/* 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;
  --sovittu-tausta: #F1FAF6;
  --sovittu-viiva:  #CBE7DA;
  /* 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;
  --sovittu-tausta: rgba(63,191,143,.12);
  --sovittu-viiva:  rgba(63,191,143,.30);
  --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, sovitut hinnat, hankinnan lohko-otsikot — ja ne
   jaivat vaaleiksi kun teksti kaantyi vaaleaksi. Mitattu selaimesta:
   hankinnan 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 .hlohko.avoin   .hlohko-otsikko { background: rgba(229,100,90,.14); }
html.yo .hlohko.ostettu .hlohko-otsikko { background: rgba(63,191,143,.13); }
html.yo .hlohko.avoin   .hlohko-otsikko .tilapallo { background: #E5645A; }
html.yo .hlohko.ostettu .hlohko-otsikko .tilapallo { background: #3FBF8F; }
html.yo .hlohko.avoin   { border-top-color: #E5645A; }
html.yo .hlohko.ostettu { border-top-color: #3FBF8F; }

html.yo table.ruudukko tr:hover > td,
html.yo tr.lataus td,
html.yo tr.kaupparivi:hover > td { background: #202730; }
html.yo tr.panosvalinta > td { background: #1D2229; }
html.yo tr.panosvalinta:hover > td { background: #232A33; }

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.erittelypalkki > td { background: rgba(242,132,30,.10); }
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 .merkintapallo.sininen   { background: #5C9CFF; }
html.yo .merkintapallo.keltainen { background: #E0A93A; }
html.yo .merkintapallo.vihrea    { background: #3FBF8F; }

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); }
html.yo .ailokirivi.tyhja { background: rgba(224,169,58,.12); }

* { 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: "hankinta 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 hankintanä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; }

/* Suodatinpainikkeet yhtena ryhmana: omat / mallilaskelmat / kaikki.
   Yhteen liitettyina ne lukevat valintana eivatka kolmena toimintona. */
.valintapainikkeet { display: inline-flex; }
.valintapainikkeet .painike { border-radius: 0; margin-left: -1px; }
.valintapainikkeet .painike:first-child {
  border-radius: 6px 0 0 6px; margin-left: 0;
}
.valintapainikkeet .painike:last-child { border-radius: 0 6px 6px 0; }
.valintapainikkeet .painike.vahva { position: relative; z-index: 1; }

.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, .kpirivi {
  display: grid; gap: 10px; margin-bottom: 16px;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
}
.kpirivi { padding: 12px 0 16px; margin-bottom: 0; }

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

/* Korostus on ylalinja, ja se oli ohjelmassa jo ennen tata tyota
   `.mittari`-korteissa. Sita ei korvata omalla raidalla vaan
   laajennetaan koskemaan myos hankinnan kortteja, jotta korostus
   tarkoittaa samaa molemmilla sivuilla.

   Vari tarkoittaa samaa joka sivulla: oranssi on se luku josta tarjous
   muodostuu, vihrea saatu hyoty, punainen tappio. Varitetaan vain yksi
   kortti riviaa kohti — jos jokainen olisi varillinen, vari lakkaisi
   tarkoittamasta mitaan. */
.kpikortti { border-top: 3px solid var(--viiva); }
.kpikortti.saasto { border-top-color: var(--vihrea); }
.kpikortti.tappio { border-top-color: var(--punainen); }
.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-kaikki-otsikko { margin: 0 0 8px; font-size: 15px; font-weight: 700; }
.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); }

table.kokohanke { flex: 1; min-width: 640px; border-collapse: collapse; font-size: 12px; }
table.kokohanke th {
  font-size: 12px; font-weight: 600; color: var(--teksti-himmea); text-align: left;
  padding: 2px 7px; white-space: nowrap;
}
/* Otsikko tasataan kuten sen alla oleva luku. Ilman tätä riviä
   "table.kokohanke th" (tarkkuus 0,1,2) voitti .oik:n (0,1,0), jolloin
   otsikot jäivät vasempaan ja luvut olivat oikeassa — sama sarake, kaksi
   eri reunaa, eikä kummankaan alta nähnyt kumpi luku oli kenen.
   Asko 23.8.2026: "laita nää rivit niin että on kohdakkain esim
   materiaalin alla on summa suoraan alla". Sama täyte molemmilla, jotta
   oikea reuna osuu pikselilleen. */
table.kokohanke th.oik { text-align: right; }
table.kokohanke td {
  padding: 4px 7px; background: var(--tausta); border-top: 1px solid var(--viiva);
  border-bottom: 1px solid var(--viiva); white-space: nowrap;
}
table.kokohanke th.oik, table.kokohanke td.oik { padding-right: 7px; }

/* 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; }
.sisenna { display: inline-block; width: 18px; }

.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; }
/* KETJUTAULUKOT OVAT SAMAN LEVYISET. Mitattu 24.8.2026: säätimet
   olivat kahdessa linjassa (382 ja 419 px), koska "Tarjoussumman
   muodostuminen" ja "Käytetyt kertoimet" ovat kaksi eri taulukkoa ja
   automaattinen sarakejako mitoitti kummankin oman sisältönsä mukaan.
   Kiinteä jako tekee niistä yhden linjan riippumatta nimien pituudesta. */

.muokkain { border-top: 2px solid var(--oranssi); margin-top: 22px; padding-top: 4px; }
.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; }

/* rivin sisällön muokkaus */
tr.erittelypalkki > td { background: #FFF7EF; padding: 5px 8px !important;
                         border-top: 1px solid rgba(232,115,12,.35); }
.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; }

/* panostason oma otsikkorivi */
tr.panosotsikko > td {
  background: #ECEFF3; color: var(--teksti-himmea); font-size: 12px;
  font-weight: 600; letter-spacing: .02em; padding: 3px 7px !important;
  border-bottom: 1px solid var(--viiva-tumma); white-space: nowrap;
}
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; }
.sivutus { display: flex; gap: 4px; margin-right: auto; }
.sivunappi {
  min-width: 26px; border: 1px solid var(--viiva-tumma); background: var(--tausta);
  border-radius: 5px; padding: 2px 6px; cursor: pointer; font-size: 12px;
}
.sivunappi:hover { background: var(--tausta-2); }
.sivunappi.nyt { background: var(--oranssi); border-color: var(--oranssi);
                 color: #fff; font-weight: 600; }
.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; }

/* ------------------------------------------------- oman kirjaston merkinnät */

.merkintapalkki { display: flex; align-items: center; gap: 6px; margin-bottom: 10px; }

.merkintapallo {
  display: inline-block; width: 10px; height: 10px; border-radius: 50%;
  vertical-align: -1px; margin-right: 5px;
}
.merkintapallo.sininen  { background: #1B6EF3; }
.merkintapallo.keltainen{ background: #E8A80C; }
.merkintapallo.vihrea   { background: #12805C; }

/* 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; }

/* Lisaasetukset ovat auki napsautuksen takana: perusrivit nakyvat heti,
   eika pitka lomake saa jattamaan tietoja tayttamatta. */
.lisaasetukset.piilossa { display: none; }
.lisaasetukset { margin-top: 10px; border-top: 1px solid var(--viiva); padding-top: 12px; }
.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; }
  .hankepalkki table.kokohanke { display: block; overflow-x: auto; margin-top: 8px; }
  /* 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; }

/* ------------------------------------------ sovittu kokonaishinta

   Aliurakkahinta ei ole yksikköhinta vaan koko rivin hinta, eikä sillä
   ole panoksia: se on neuvoteltu luku eikä osiensa summa. Kun se on
   voimassa, rivin muut luvut eivät summaudu — mutta ne EIVÄT HÄVIÄ
   eivätkä himmene lukukelvottomiksi. Ne ovat tarkistus sille onko
   sovittu hinta järkevä: 18 tuntia ja 900 € materiaalia vastaan
   4 500 € on 200 €/h, ja sen näkee vain jos tunnit voi lukea.

   Ei-laske merkitään katkoviivalla eikä haalistamalla. Himmeä väri
   tarkoittaa tässä ohjelmassa lukittua, ja nämä eivät ole lukittuja —
   ne vain eivät ole rivin hinta. */
td.rahasolu.eilaske { background: repeating-linear-gradient(
    -45deg, transparent, transparent 5px,
    rgba(20,25,35,.035) 5px, rgba(20,25,35,.035) 10px); }
td.rahasolu.eilaske .summa { text-decoration: line-through;
                             text-decoration-color: var(--viiva-tumma); }
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; }

/* ------------------------------------------- sovittu hinta alirivinä

   Alirivi on hinta jonka aliurakoitsija antoi, ja se on rivin kustannus.
   Se ei ole paketti: paketilla on panokset, alirivillä ei ole — se on
   neuvoteltu luku eikä osiensa summa. Siksi se erottuu paketeista, ja
   erottuu vihreällä: TYYLI.md antaa vihreän vahvistetulle ja saadulle
   tarjoukselle, ja tämä on juuri se.

   Alirivejä voi olla useita samalla rivillä, joten vasen reunaviiva
   sitoo ne yhdeksi ryhmäksi ilman että kukin niistä huutaa erikseen. */
/* Taulukkoetuliite on pakollinen taustasäännössä: yleinen
   "table.ruudukko tr:hover > td" (0,2,3) on tarkempi kuin
   "tr.sovittu > td" (0,1,2), joten ilman etuliitettä väri katoaa hiiren
   alla. Sama vika kuin päärivillä 22.8.2026 — löytyi pääikkunan
   vartijasta, joka tarkisti vain kaksi rivityyppiä. */
table.ruudukko tr.sovittu > td,
table.ruudukko tr.sovittu:hover > td { background: var(--sovittu-tausta);
                                       border-top: 1px solid var(--sovittu-viiva); }
table.ruudukko tr.sovittu > td:first-child {
  box-shadow: inset 3px 0 0 var(--vihrea); }
.aumerkki { background: var(--vihrea); color: #fff; border-radius: 3px;
            padding: 1px 5px; font-weight: 700; letter-spacing: .3px; }
.sovittunimi { display: flex; gap: 6px; align-items: center; min-width: 0; }
.sovittunimi .solukentta { min-width: 0; }
/* Toimittaja EI ole himmeä, vaikka se on toissijainen tieto. Himmeä väri
   lukee tässä ohjelmassa "lukittu", ja tämä kenttä on muokattava — sama
   virhe on tehty jo kahdesti aiemmin (TYYLI.md). Toissijaisuus näkyy
   kapeutena ja paikkatekstinä, ei värinä. */
.sovittunimi .toimittaja { max-width: 150px; }

/* Hinta ja muoto ovat samassa solussa, koska ne ovat sama tieto: 1 500 ei
   tarkoita mitään ennen kuin tietää onko se könttä vai yksikköhinta.
   Erilleen sijoitettuna toinen luetaan ilman toista, ja sadan yksikön
   rivillä se on satakertainen virhe. */
td.sovittuhinta { white-space: nowrap; }
.sovittuluku { display: inline-block; min-width: 62px; padding: 3px 6px;
               border: 1px solid var(--viiva); border-radius: 4px;
               background: var(--tausta); text-align: right;
               font-variant-numeric: tabular-nums; font-weight: 600; }
.sovittuluku:focus { outline: 2px solid var(--oranssi); outline-offset: -1px; }
.muotonappi { display: block; margin: 2px 0 0 auto; min-height: 20px;
              padding: 1px 6px;
              border: 1px solid var(--vihrea); border-radius: 4px;
              background: var(--tausta); color: var(--vihrea); cursor: pointer;
              font-size: 11px; font-weight: 600; }
.muotonappi:hover { background: var(--vihrea); color: #fff; }

/* Vain osan kattava hinta on KELTAINEN eikä punainen. Punainen tarkoittaa
   arviota jota ei ole hyväksytty; tämä on lähteellinen luku, joka vain ei
   kata koko riviä. Ero on TYYLI.md:n, ja jos molemmat olisivat punaisia,
   punaisesta tulisi yleinen "tässä on jotain" eikä se tarkoittaisi enää
   mitään. */
table.ruudukko tr.sovittu.vajaa > td:first-child {
  box-shadow: inset 3px 0 0 var(--keltainen); }
.vajaamerkki { border: 1px solid var(--keltainen); color: var(--keltainen);
               border-radius: 3px; padding: 0 5px; font-size: 11px;
               font-weight: 600; white-space: nowrap; }

/* Vertailu on se luku jonka takia laskettu sisältö kannattaa säilyttää.
   Se on oma rivinsä eikä solu: 4 500 € vastaan 3 200 € ei mahdu soluun
   niin että molemmat luvut näkyisivät, ja pelkkä erotus ilman kumpaakaan
   ei kerro kummasta se tulee. */
table.ruudukko tr.sovittuvertailu > td,
table.ruudukko tr.sovittuvertailu:hover > td {
  background: var(--sovittu-tausta); border-bottom: 1px solid var(--sovittu-viiva);
  padding: 4px 10px 6px; }
.vertailu { display: flex; flex-wrap: wrap; gap: 4px 16px; align-items: baseline;
            padding-left: 44px; font-size: 12.5px;
            font-variant-numeric: tabular-nums; }
.vertailu .ero { font-weight: 700; }
.vertailu .ero.yli { color: var(--punainen); }
.vertailu .ero.ali { color: var(--vihrea); }

/* 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ä. */
@media (max-width: 1000px) {
  table.ruudukko.puu tr.sovittu > td { display: none; }
  table.ruudukko.puu tr.sovittu > td:nth-child(1),
  table.ruudukko.puu tr.sovittu > td:nth-child(3),
  table.ruudukko.puu tr.sovittu > td:nth-child(4),
  table.ruudukko.puu tr.sovittu > td:nth-child(8),
  table.ruudukko.puu tr.sovittu > td:nth-child(11) { display: block; }
  table.ruudukko.puu tr.sovittu > td:nth-child(1)  { grid-area: 1 / 1 / 4 / 2; }
  table.ruudukko.puu tr.sovittu > td:nth-child(3)  { grid-area: 1 / 2 / 2 / 3; }
  table.ruudukko.puu tr.sovittu > td:nth-child(11) { grid-area: 1 / 3 / 2 / 4;
                                                     text-align: right; }
  table.ruudukko.puu tr.sovittu > td:nth-child(4)  { grid-area: 2 / 2 / 3 / 4;
                                                     padding-left: 0 !important; }
  table.ruudukko.puu tr.sovittu > td:nth-child(8)  { grid-area: 3 / 2 / 4 / 4;
                                                     display: flex; gap: 8px;
                                                     align-items: center; }
  table.ruudukko.puu tr.sovittu > td:nth-child(8) .sovittuluku { flex: 1 1 auto; }
  .sovittunimi { flex-wrap: wrap; }
  .sovittunimi .toimittaja { max-width: none; }
  /* Muoto nappina numeron viereen: pystysuunnassa on tilaa vähemmän kuin
     leveyssuunnassa, ja kosketuskoko 44 px on tässä se mitta joka ratkaisee. */
  .muotonappi { display: inline-block; margin: 0; flex: 0 0 auto;
                min-height: 34px; padding: 4px 12px; font-size: 12.5px; }
  .sovittuluku { min-height: 34px; padding: 6px 8px; }
  .vertailu { padding-left: 0; gap: 2px 12px; }

}

/* ==================================================== hankintanäkymä

   Laskenta on tehty rakenteittain, koska niin kustannus syntyy. Ostaminen
   ei tapahdu rakenteittain: yksi soitto laattamiehelle, yksi tilaus
   rautakauppaan. Näkymä kääntää saman aineiston ostajan jakoon.

   Se on lukunäkymä. Ainoa ulostulo on PDF, eikä täältä voi rikkoa
   laskentaa — siksi se saa olla näin suoraviivainen. */
.hankintapalkki { display: flex; flex-wrap: wrap; gap: 10px 26px;
                  align-items: flex-start; padding: 10px 16px;
                  background: var(--tausta-2);
                  border-bottom: 1px solid var(--viiva); }
.hmittari .otsake { font-size: 11px; text-transform: uppercase;
                    letter-spacing: .5px; color: var(--teksti-himmea); }
.hmittari .arvo { font-size: 17px; font-weight: 700;
                  font-variant-numeric: tabular-nums; }
.hmittari .lisa { font-size: 11.5px; color: var(--teksti-himmea); }
/* Sijoittamaton raha on keltainen eikä punainen: se ei ole arvio vaan
   päätös jota ei ole tehty. Luku on oikein, sen osoite puuttuu. */
.hmittari.varoitus .arvo { color: var(--keltainen); }
.hvalinnat { margin-left: auto; display: flex; flex-direction: column;
             gap: 4px; font-size: 12.5px; }
.hvalinnat label { display: flex; gap: 6px; align-items: center;
                   min-height: 24px; cursor: pointer; }

.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; }

table.hankintataulu tr.hankintarivi > td { padding-top: 6px;
                                           padding-bottom: 6px; }
table.hankintataulu tr.hankintarivi.valittu > td { background: var(--oranssi-vaalea); }
table.hankintataulu tr.sijoittamaton > td:first-child {
  box-shadow: inset 3px 0 0 var(--keltainen); }
table.hankintataulu .vajaamerkki { margin-left: 0; }
/* Nuoli ja nimi pysyvät yhdessä, merkki rivittyy. Kapeassa solussa
   pelkkä nuoli omalla rivillään näyttää katkenneelta rivitykseltä eikä
   avausnuolelta. */
table.hankintataulu .pakettinimi { display: flex; flex-wrap: wrap;
                                   gap: 2px 6px; align-items: baseline;
                                   white-space: normal; }
table.hankintataulu .pakettinimi .nuoli.avaus { flex: 0 0 auto; }
table.hankintataulu .pakettinimi .nimi { flex: 1 1 auto; min-width: 0; }
/* Rivi jolla on vain omaa työtä ei lähde tarjouspyyntöön. Se näkyy
   luettelossa, jottei paketin sisältö näytä pienemmältä kuin se on —
   mutta se erottuu, koska sitä ei tilata keneltäkään. */
table.hankintataulu tr.eitilattava > td { background: var(--tausta-2); }
.osamerkki { margin-left: 6px; padding: 0 5px; font-size: 11px;
             border: 1px solid var(--viiva-tumma);
             color: var(--teksti-himmea); border-radius: 3px;
             white-space: nowrap; }

.hvalinta { display: flex; gap: 8px; align-items: flex-start;
            margin-top: 10px; font-size: 13px; cursor: pointer; }
.hvalinta.varo { background: var(--oranssi-vaalea);
                 border-left: 3px solid var(--oranssi);
                 padding: 8px 10px; border-radius: 0 5px 5px 0; }

@media (max-width: 1000px) {
  /* Hankintataulu ei ole .puu, joten korttisäännöt eivät koske sitä.
     Yhdeksän saraketta ei mahdu 390 pikseliin, ja tässä taulukossa
     vieritys on oikea vastaus: luvut ovat vertailtavia keskenään, eikä
     korttijako säilytä sarakkeiden rinnakkaisuutta. */
  .hankintapalkki { padding: 8px 10px; gap: 8px 16px; }
  .hvalinnat { margin-left: 0; flex: 1 1 100%; }
  .hvalinnat label { min-height: 44px; }
  .hvaroitus { margin: 8px 10px 0; }
  .padded > table.hankintataulu { display: block; overflow-x: auto;
                                  max-width: 100%; }
}

/* --------------------------------------------- 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; }

/* KAIKKI TÄHDET OVAT SAMANNÄKÖISIÄ. Asko 24.8.2026: "kaikki logot
   saman näköisiä, en pyytänyt vihreää logoa."

   Tässä oli päivän mittaan kaksi eri väritystä: punainen/vihreä
   täyttönä (panoksella ei ole hintaa / on) ja sitä ennen sama asia
   renkaana. Molemmat poistettiin tällä päätöksellä — tunnus on tunnus
   eikä mittari, ja kolme erinäköistä tähteä samassa sarakkeessa
   näyttää kolmelta eri toiminnolta.

   MITÄ TÄSTÄ KATOSI, ja se on sanottava: rivistä ei enää näe napin
   väristä onko panoksella hinta. Tieto on yhä olemassa kahdessa
   paikassa — panoksen oma hintasolu on tyhjä, ja rakennerivin summan
   vieressä lukee "hinta puuttuu (n)" — mutta ne ovat eri paikassa kuin
   nappi. Jos tila halutaan takaisin, se tehdään jollain muulla kuin
   tähden värillä. Ks. SOPIMUS.md.

   Tilaluokka jää nappiin (puuttuu/haettu): se on tosi tieto ja
   vihjeteksti käyttää sitä. Väriä sillä ei ole. */


.aihinta { display: flex; flex-wrap: wrap; gap: 10px 26px;
           padding: 10px 12px; margin-bottom: 10px;
           background: var(--tausta-2); border-radius: 6px; }
.aihinta .hmittari.ilmainen .arvo { color: var(--vihrea); }

.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; }

/* Edistyminen. Palkki kertoo montako, teksti mitä se on maksanut tähän
   mennessä — juokseva kulu on se luku jonka takia keskeytysnappi on
   olemassa. */
.aipalkki { height: 8px; background: var(--tausta-3); border-radius: 4px;
            overflow: hidden; }
.aitaytto { height: 100%; width: 0; background: var(--oranssi);
            transition: width .25s ease; }
.aiteksti { margin: 6px 0 10px; font-size: 13px; font-weight: 600;
            font-variant-numeric: tabular-nums; }
.ailoki { max-height: 260px; overflow-y: auto; border: 1px solid var(--viiva);
          border-radius: 6px; }
.ailokirivi { display: flex; gap: 10px; align-items: baseline;
              padding: 4px 10px; font-size: 12.5px;
              border-bottom: 1px solid var(--viiva); }
.ailokirivi:last-child { border-bottom: 0; }
.ailokirivi .nimi { flex: 1 1 auto; min-width: 0; overflow-wrap: anywhere; }
.ailokirivi .tila { white-space: nowrap; color: var(--teksti-himmea); }
.ailokirivi.kesken .tila { color: var(--oranssi); }
/* 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; }
}
.ailokirivi.loytyi .tila { color: var(--vihrea); }
/* Tyhjäksi jäänyt on KELTAINEN eikä punainen: luku ei ole väärä vaan
   puuttuva, ja se on TYYLI.md:n ero. */
.ailokirivi.tyhja { background: #FDF6E7; }
.ailokirivi.tyhja .tila { color: var(--keltainen); }
.ailokirivi.vika .tila { color: var(--punainen); font-weight: 600; }

.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; }
/* Säästölause luetaan juuri kun käyttäjä on maksanut. Se on ainoa kohta
   jossa hinta muuttuu hyväksyttäväksi, joten se ei ole alaviite. */
.aisaasto { margin: 8px 0; padding: 8px 10px; background: var(--tausta);
            border: 1px solid #CBE7DA; border-radius: 5px; line-height: 1.45; }

@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; }
  .aihinta { gap: 8px 16px; }
  .aikohteet, .ailoki { 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; }
.ailahde { font-size: 11px; color: var(--sininen); white-space: nowrap;
           border-bottom: 1px dotted var(--sininen); cursor: help; }

/* ------------------------------------------- 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; }
.odotus .kehra {
  width: 54px; height: 54px; display: block; margin: 0 auto;
}
/* .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; }

/* Sovittu aliurakkahinta korvaa rivin sisällön. Asko 23.8.2026: "pitäs
   yliviivata kaikki lasketut rakenteet."

   Ero .pois-luokkaan on merkityksellinen: .pois on KÄYTTÄJÄN valinta,
   tämä on SEURAUS sovitusta hinnasta. Siksi eri merkintä — yliviivaus
   sanoo "korvattu", himmennys sanoo "jätetty pois". */
/* YLIVIIVAUS POISTETTU 27.8.2026. Asko: *"talla nakyy yliviivattuja
   asioita, yliviivaus pois."* Merkinta jaa himmennykseksi: se kertoo
   yha etta luku on korvattu kaupan hinnalla, mutta rivi pysyy
   luettavana. Yliviivattu numero on vaikea lukea juuri silloin kun
   sita halutaan verrata kaupan hintaan — ja vertailu on koko syy
   siihen etta sisalto jatetaan nakyviin. */
tr.sovittupois td { opacity: .6; }

/* Aliurakan ero. Plusmerkkinen tarkoittaa että sovittu on laskettua
   kalliimpi — se on tappio, ei säästö. Väri on siksi eri kuin
   miinusmerkkisellä, ja etumerkki kirjoitetaan auki. */
.saasto { color: var(--vihrea, #12805C); }
.tappio { color: var(--punainen, #b3372c); }

/* 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; }
}

/* Sovittu hinta korvasi rivin oman luvun. Yliviivattu luku kertoo mikä
   korvattiin; säästö kertoo kannattiko se. Vihreä = sovittu halvempi. */
.rahasolu .korvattu {
  font-size: 11px; line-height: 1.2; color: var(--himmea, #7a7a7a);
  margin-top: 1px;
}
.rahasolu .saasto {
  font-size: 11px; line-height: 1.2; font-weight: 600; margin-top: 1px;
  font-variant-numeric: tabular-nums;
}
.rahasolu .saasto.plus { color: var(--vihrea, #12805C); }
.rahasolu .saasto.miinus { color: var(--punainen, #C0392B); }

/* 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; }
.odotus .sekunnit { min-width: 70px; display: inline-block; }

/* 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; }

/* PYÖRIVÄ KEHRÄ. Asko 23.8.2026: "muutetaan se tiimalasi sellaseen
   Applen pyörivään shittiin."

   Merkki ⧗ oli kirjasinmerkki jota kierrätettiin CSS:llä, ja se nykii:
   kirjasimen piirto ei ole symmetrinen keskipisteen ympäri. Kehrä on
   piirretty reunuksesta, joten se pyörii tasan.

   Kolme neljäsosaa himmeää, yksi neljäsosa korostusväriä — sama ele
   kuin järjestelmän omissa odotusmerkeissä, mutta ohjelman väreissä. */
.kehra {
  display: inline-block; width: 18px; height: 18px; vertical-align: -3px;
  border: 2px solid var(--reuna, #d9dde3);
  border-top-color: var(--korostus, #d2601a);
  border-radius: 50%;
  animation: kehra 0.8s linear infinite;
}
.kehra.pieni { width: 13px; height: 13px; border-width: 2px; }
.odotus .kehra { border-width: 4px; margin-bottom: 4px; }
@keyframes kehra { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) {
  .kehra { animation-duration: 2.4s; }
}

/* 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);
}

/* Aliurakan kate: laskettu vastaan sovittu. Vihreä kun urakointi
   säästi, punainen kun se maksoi enemmän kuin oma työ. */
.aukate {
  margin-left: 10px; padding: 1px 8px; border-radius: 10px;
  font-size: 11.5px; font-weight: 600; white-space: nowrap;
  font-variant-numeric: tabular-nums;
}
.aukate.plus { background: rgba(18,128,92,.12); color: var(--vihrea, #12805C); }
.aukate.miinus { background: rgba(192,57,43,.12); color: var(--punainen, #C0392B); }

/* Könttähinnan valintalaatikko: rivilista, jako ja yhteenveto. */
.konttalista { max-height: 34vh; overflow: auto; border: 1px solid var(--reuna);
  border-radius: 8px; }
.konttalista table.ruudukko { font-size: 13px; }
.konttayhteenveto { margin-top: 10px; font-size: 13px;
  display: flex; flex-direction: column; gap: 8px; }
.konttayhteenveto table.jako { width: 100%; }
.konttayhteenveto table.jako td { padding: 2px 8px; }

/* Aliurakan vertailurivit laskennan summarivin alla. Vaaleampi kuin
   summarivi: nämä eivät ole osa laskentaa vaan sen tulkinta. */
tr.aurivi > td { font-weight: 400; }
tr.aurivi.aupohja > td { color: var(--teksti-himmea); }
tr.auerotus > td { font-weight: 700; }
tr.auerotus.plus > td { color: var(--vihrea, #12805C); }
tr.auerotus.miinus > td { color: var(--punainen, #C0392B); }
.konttayhteenveto tr.kiinteaosa td { background: rgba(210,96,26,.06); }

/* Könttäkauppojen luettelo: yksi kauppa, sen rivit ja purkunappi. */
.kauppalista { display: flex; flex-direction: column; gap: 14px; }
.kauppa { border: 1px solid var(--reuna); border-radius: 8px; padding: 10px 12px; }
/* Tehty kauppa saa ostajansa värin vasempaan reunaan ja otsikkoonsa.
   Reuna eikä koko laatikko: laatikossa on taulukko lukuja, ja täysi
   väripohja tekee niistä vaikealukuisia. Sama sävy kuin rivillä. */
.kauppa.uvari { border-left: 5px solid var(--uvari-h); }
.kauppa.uvari > .kauppaotsikko { background: var(--uvari);
  margin: -10px -12px 8px; padding: 8px 12px;
  border-radius: 7px 7px 0 0; }
.kauppalista .kauppa.uvari > .kauppaotsikko { margin: -8px -10px 6px;
  padding: 7px 10px; }
.kauppaotsikko { display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  margin-bottom: 6px; }
.kauppasumma { margin-left: auto; font-variant-numeric: tabular-nums; }
.kauppa table.jako { width: 100%; font-size: 13px; }
.kauppa table.jako td { padding: 2px 6px; }
/* Olemassa olevat kaupat könttälaatikon yläosassa, erotettuna
   uuden kaupan lomakkeesta. */
.konttalista + .selite, .kauppalista:not(:empty) {
  border-bottom: 1px solid var(--reuna); padding-bottom: 12px;
  margin-bottom: 12px;
}
.kauppalista .kauppa { padding: 8px 10px; }
.kauppalista .painike.pikku { padding: 3px 10px; font-size: 12px; }

/* Kauppanäkymä: hinta ja valinta samaan silmäykseen. */
.kauppapalkki {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  padding: 10px 12px; margin-bottom: 10px; border-radius: 8px;
  background: var(--pohja2, #f2f4f7); border: 1px solid var(--reuna);
}
.kauppapalkki label { font-size: 12px; color: var(--teksti-himmea); }
.kauppapalkki input[type=number] { width: 130px; text-align: right; }
/* Toimittajakenttä oli leveydetön ja venyi omalle riviltään, jolloin
   "Keneltä" näkyi kahdesti: nimenä ja kentän vihjetekstinä. */
.kauppapalkki input[type=text] { width: 220px; }
.kauppapalkki .alvvalinta { gap: 4px; }
tr.kaupparivi { cursor: pointer; }
tr.kaupparivi:hover > td { background: #FAFBFC; }
tr.kaupparivi.valittu > td { background: var(--korostus-vaalea, #FDF1E7); }

/* JO MYYTY RIVI ON VIHREÄ JA LUKOSSA. Asko 25.8.2026: "kun tekee
   uudestaan kaupan on jo tehdyt litterat vihreällä… niin pitkään kun on
   litteroita muulla värillä kun vihreä voi tehdä jäljelle oleville
   kauppaa."

   Sama vihreä kuin hankintamatriisin ostetuilla riveillä
   (tr.mrivi.mostettu) — myyty tarkoittaa samaa asiaa kummassakin
   näkymässä, joten se ei saa näyttää eri väriltä.

   Kursori on default eikä pointer: rivi ei reagoi napsautukseen, ja
   osoitin joka lupaa toimintoa jota ei ole on oma pieni valheensa. */
tr.kaupparivi.myyty > td { background: var(--vihrea-oma); }
tr.kaupparivi.myyty:hover > td { background: var(--vihrea-oma-2); }
tr.kaupparivi.myyty { cursor: default; }
tr.kaupparivi.myyty input[type=checkbox] { opacity: .45; }

/* Merkki kertoo kenelle myytiin. Se on vihreä eikä oranssi, koska
   oranssi merkki (.merkki.H) tarkoittaa muualla arviota — ja tämä on
   päinvastoin: sovittu luku. */
.merkki.myyty {
  background: var(--vihrea-oma-2); color: #14532D;
  border: 1px solid rgba(20, 83, 45, .25);
}

.vapaatluku { margin-left: auto; font-size: 12.5px; white-space: nowrap; }
.hvalinta.tiivis { margin: 0; font-size: 12.5px; white-space: nowrap; }

/* Kaupan yhteenveto alapalkissa, vahvistusnapin vieressä: päätösluku
   on siinä missä päätös tehdään. */
.kauppatiivis { font-size: 13px; display: flex; align-items: center;
  gap: 6px; flex-wrap: wrap; }
.hakurivi.tiivis { margin: 0 0 8px; gap: 6px; }
.hakurivi.tiivis input[type=text] { flex: 1; }

/* Panosvalinta kauppalaatikossa: rivin materiaalit ja työt alirivein. */
tr.panosvalinta { cursor: pointer; }
tr.panosvalinta > td { background: #FBFCFD; font-size: 12.5px; }
tr.panosvalinta:hover > td { background: #F4F6F8; }
tr.panosvalinta.valittu > td { background: var(--korostus-vaalea, #FDF1E7); }
tr.panosvalinta.myyty { opacity: .55; cursor: default; }
tr.panosvalinta.myyty > td { text-decoration: line-through;
  text-decoration-color: var(--viiva-tumma); }
tr.panosvalinta .panosnimi .merkki { margin-left: 6px; }

/* 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; }

/* ------------------------------------------------- hankintamatriisi

   Asko 24.8.2026: "tuossa on miltä haluun näyttävän." Ulkoasu on toisen
   tekoälyn hahmotelmasta: tumma mittaripalkki, suodatinrivi ja yksi
   taulukko jossa littera aukeaa panosriveiksi.

   Värit ovat meidän. Hahmotelma käytti Tailwindin oletuksia (#1e293b,
   siniset napit, pastellibadget); tämä ohjelma on oranssi ja valkoinen.
   Tumman palkin pidin, koska se erottaa yhteenvedon taulukosta — mutta
   sen sävy tulee omasta tekstivärista eikä vieraasta paletista. */

/* Matriisin mittarit ovat samaa .hmittari-perhettä kuin hankinnan oma
   palkki; tässä vain väli ja etumerkin väri. Hahmotelman tumma palkki
   jäi pois — Asko 24.8.2026: "meidän on ihan hyvä." */
.mpalkki { border-bottom: 0; padding-bottom: 4px; }
.hmittari.msaasto .arvo { color: var(--vihrea); }
.hmittari.mtappio .arvo { color: var(--punainen); }

.mtyokalut {
  display: flex; gap: 8px; align-items: center;
  margin: 0 14px 10px; flex-wrap: wrap;
}
/* TARKKUUS: `input.mhaku` eikä `.mhaku`. Rivin 346 yleissääntö
   `input[type=text], … { width: 100% }` on tarkkuudeltaan (0,1,1),
   koska attribuuttivalitsin painaa saman kuin luokka — pelkkä `.mhaku`
   on (0,1,0) ja hävisi sille. Leveys 340 px ei siis ollut koskaan
   voimassa: mitattu 31.8.2026, kenttä oli 1 214 px ja työnsi
   tilavalikon ja Avaa kaikki -napin omille riveilleen. Kolme riviä,
   113 px korkeutta.

   Sama tarkkuusvika kuin `th` vastaan `.oik` sarakeotsikoissa. */
input.mhaku {
  width: 340px; max-width: 100%; padding: 6px 10px;
  border: 1px solid var(--viiva); border-radius: 5px; font-size: 13px;
}
/* Tilavalikko samoin: ilman elementtiä se häviää samalle säännölle. */
select.mvalikko { width: 200px; }
.mvalikko {
  padding: 6px 8px; border: 1px solid var(--viiva); border-radius: 5px;
  font-size: 13px; background: var(--tausta);
}
.mtyhja {
  margin: 0 14px; padding: 36px; text-align: center;
  color: var(--teksti-himmea);
  border: 1px dashed var(--viiva); border-radius: 10px;
}

table.mmatriisi { font-size: 12px; }
table.mmatriisi .mlittera    { width: 74px; white-space: nowrap; }
table.mmatriisi .mtyyppi     { width: 92px; }
table.mmatriisi .mtoimittaja { width: 168px; }
table.mmatriisi .mtila       { width: 132px; }
table.mmatriisi td, table.mmatriisi th { padding: 5px 8px; }
tr.mrivi > td { background: var(--tausta-2); font-weight: 600; }
tr.mrivi.mostettu > td { background: var(--vihrea-oma); }
tr.mlapsi > td {
  background: var(--tausta); font-weight: 400; color: var(--teksti-himmea);
}
.mlapsinimi { padding-left: 26px !important; }
.kursiivi { font-style: italic; }

.mnuoli {
  border: 0; background: none; cursor: pointer; padding: 0 6px 0 0;
  color: var(--viiva-tumma); font-size: 11px;
}
.mnuoli.tyhja { cursor: default; display: inline-block; width: 17px; }
.mnuoli:hover { color: var(--teksti); }

.mbadge {
  display: inline-block; padding: 1px 6px; border-radius: 3px;
  font-size: 10.5px; font-weight: 600; white-space: nowrap;
  border: 1px solid var(--viiva); color: var(--teksti-himmea);
  background: var(--tausta);
}
.mbadge.materiaali { border-color: #A8C8EE; color: #1F5FA9; background: #F2F7FD; }
.mbadge.tyo        { border-color: #E3C58A; color: #8A6114; background: #FDF8EE; }
.mbadge.kokonaisuus { border-color: var(--viiva); }

.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);
}
.mtilamerkki.mostettu {
  border-color: var(--vihrea); color: var(--vihrea); background: #F1FAF6;
}
/* 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. */
button.mtilamerkki.vaihe { cursor: pointer; }
button.mtilamerkki.vaihe:hover { filter: brightness(.97); }
.mtilamerkki.laskettu {
  border-color: var(--viiva); color: var(--teksti-himmea); background: var(--tausta);
}
.mtilamerkki.tarjouspyynto {
  border-color: #B9D3B0; color: #3F6B33; background: #F4F9F2;
}
.mtilamerkki.neuvottelu {
  border-color: #E3C58A; color: #8A6114; background: #FDF8EE;
}
.mtilamerkki.ostettu,
.mtilamerkki.toimitettu {
  border-color: var(--vihrea); color: var(--vihrea); background: #F1FAF6;
}
.mtilamerkki.toimitettu::before { content: '✓ '; }

@media (max-width: 900px) {
  .mhaku { width: 100%; }
}
.mbadge.hankinta { border-color: #B9D3B0; color: #3F6B33; background: #F4F9F2; }


/* 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; }

/* SÄÄDINSARAKE. Asko 24.8.2026: "laitetaan nuo laatikot nättiin riviin."
   Kiinteä leveys, jotta laatikko on samassa kohdassa joka rivillä eikä
   siirry nimen pituuden mukana. Tyhjäkin solu varaa saman tilan —
   muuten summasarake hyppisi rivien välillä. */

/* TAVARALUETTELON TARKISTUSTAULUKKO. Hukka-% muokataan tässä eikä
   laskelmapuussa: hukka ei ole laskennan luku vaan tilauksen, ja se
   pitää nähdä nettomäärän ja tilausmäärän vieressä. Vierekkäin näkyy
   se mitä erikseen luettuna ei näy — tuplaantunut hukka. */
.tavarataulu { width: 100%; border-collapse: collapse; font-size: 12.5px; }
.tavarataulu th { text-align: left; padding: 5px 8px; font-weight: 600;
                  color: var(--teksti-himmea); font-size: 11.5px;
                  text-transform: uppercase; letter-spacing: .4px;
                  border-bottom: 1.5px solid var(--viiva);
                  position: sticky; top: 0; background: var(--tausta-2); }
.tavarataulu td { padding: 5px 8px; border-bottom: 1px solid var(--viiva);
                  vertical-align: top; }
.tavarataulu .oik { text-align: right; font-variant-numeric: tabular-nums;
                    white-space: nowrap; }
.tavarataulu tr:hover td { background: var(--tausta-2); }

/* 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; }
/* Poimittava toimittajakortti: koko kortti on napsautettava. */
.kauppa.poimittava { cursor: pointer; }
.kauppa.poimittava:hover { background: var(--tausta-2); }
.lohko-otsikko { font-size: 13px; margin: 14px 0 4px; }

/* Suodatin ei saa puristua napeista. "Vain vapaat" ja vapaana-laskuri
   lisättiin samalle riville 25.8.2026, ja kenttä kutistui niin että
   vihjetekstistä näkyi "Rajaa ni". */
.hakurivi.tiivis > input { flex: 1 1 220px; min-width: 180px; }

/* KOE 25.8.2026: liitoslajin valinta ja tarjoustulos.
   Peruminen = tämän lohkon poisto, ks. palvelin/tarjousliitos.py. */
.liitoslajit { border-bottom: 1px solid var(--viiva); padding-bottom: 8px;
               margin-bottom: 6px; }
.liitoslajiselite { margin-top: 0; margin-bottom: 10px; }
.tarjousrivi { border: 1px solid var(--viiva); border-radius: 8px;
               padding: 10px 12px; margin: 8px 0;
               background: var(--tausta-2); }
.tarjousrivi > div { margin-bottom: 4px; }

/* 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 .dialogi:has(.konttasisus) { height: 100vh; }
body.irrotettu #peite { background: var(--tausta); }

/* ================================================================
   URAKOITSIJAN VÄRI HANKINTAMATRIISISSA

   Asko 25.8.2026: "vie kursorin päälle niin häviää värit, laitetaan
   eri väreillä eri urakat, saman firman urakat jos useita niin samalla
   värilla."

   VÄRI HÄVISI HIIREN ALLA, ja se oli tarkkuusvika: yleinen sääntö
   `table.ruudukko tr:hover > td` (0,2,3) on tarkempi kuin
   `tr.mrivi.mostettu > td` (0,2,1), joten hover maalasi vihreän yli
   harmaalla. Rivi näytti myydyltä kunnes sitä katsoi.

   Kahdeksan väriä riittää: enemmän ja ne alkavat muistuttaa toisiaan
   niin ettei kahta vierekkäistä erota. Jos urakoitsijoita on yli
   kahdeksan, kaksi jakaa värin — ja nimi lukee silti rivillä.

   Värit ovat vaaleita ja vähän kylläisiä: rivillä on lukuja, ja
   voimakas tausta tekee niistä vaikealukuisia. Hover tummentaa samaa
   sävyä eikä vaihda sitä toiseen.
   ================================================================ */
/* VÄRI MÄÄRITELLÄÄN KERRAN, muuttujana luokalle. Näkymiä on kaksi —
   hankintamatriisi ja Sovi hinta -laatikko — ja kahdeksan sävyä
   kahteen kertaan kirjoitettuna ajautuisi erilleen ensimmäisessä
   muutoksessa. `uvari` kertoo että rivillä on urakoitsijan väri,
   `uvN` mikä se on. */
.uv0 { --uvari: hsl(150 45% 90%); --uvari-h: hsl(150 45% 84%); }
.uv1 { --uvari: hsl(205 55% 91%); --uvari-h: hsl(205 55% 85%); }
.uv2 { --uvari: hsl( 35 70% 90%); --uvari-h: hsl( 35 70% 84%); }
.uv3 { --uvari: hsl(280 40% 92%); --uvari-h: hsl(280 40% 86%); }
.uv4 { --uvari: hsl( 95 45% 90%); --uvari-h: hsl( 95 45% 84%); }
.uv5 { --uvari: hsl(  0 55% 93%); --uvari-h: hsl(  0 55% 87%); }
.uv6 { --uvari: hsl(185 45% 90%); --uvari-h: hsl(185 45% 84%); }
.uv7 { --uvari: hsl( 50 60% 89%); --uvari-h: hsl( 50 60% 83%); }

/* TARKKUUS ON SE MIKSI NÄMÄ OVAT KAHDELLA SELEKTORILLA eikä yhdellä
   `tr.uvari`-säännöllä: yleinen vihreä (tr.mrivi.mostettu > td,
   tr.kaupparivi.myyty > td) on kahden luokan tarkkuudella, ja
   yhden luokan sääntö häviäisi sille hiljaa. */
tr.mrivi.uvari > td,
tr.kaupparivi.myyty.uvari > td { background: var(--uvari); }

/* Ostettu ilman toimittajan nimeä pitää vanhan vihreänsä — ja sekin
   säilyy nyt hiiren alla.

   TÄMÄ ON ENNEN URAKKAVÄREJÄ, ei jälkeen. Sääntöjen tarkkuus on sama
   (0,4,3), joten myöhempi voittaa — ja jälkeen kirjoitettuna tämä
   maalasi urakoitsijan värin yli yleisellä vihreällä. Mitattu
   25.8.2026: uv7-rivi näytti hiiren alla samalta kuin nimetön kauppa. */
table.ruudukko tr.mrivi.mostettu:hover > td { background: var(--vihrea-oma-2); }

/* HOVER TUMMENTAA SAMAA VÄRIÄ. Taulukkoetuliite on pakollinen, jotta
   tämä voittaa yleisen `table.ruudukko tr:hover > td` -säännön. */
table.ruudukko tr.mrivi.uvari:hover > td,
table.ruudukko tr.kaupparivi.myyty.uvari:hover > td {
  background: var(--uvari-h);
}

/* Tilamerkki napiksi kun rivi on ostettu: siitä pääsee urakoitsijan
   tietoihin. Ulkonäkö on sama kuin merkillä — vain kursori ja hover
   kertovat että se on painettava. */
.mtilamerkki.painettava { cursor: pointer; border-width: 1px; }
.mtilamerkki.painettava:hover { filter: brightness(0.94); }
.kentat.tiivis { gap: 4px 10px; margin: 8px 0; }

/* ================================================================
   SOVI HINTA: TILA TYÖLLE, EI VIITTEELLE

   Asko 25.8.2026: "tälle alapuoliselle työlle jää liian vähän tilaa,
   työ tehdään siellä nyt kökkö. --> haluan selvän näkymän. ratkaise."

   Mitattu ennen korjausta, ikkunan sisus 779 px:

       tehdyt kaupat (viite)   376 px   48 %
       selite                   61 px
       hinta ja kattavuus       83 px
       suodatin                 45 px
       TYÖLISTA                318 px   41 %

   Rivilista sai vähemmän tilaa kuin jo tehtyjen kauppojen luettelo.
   Nyt sisus on pystysuora flex ja lista venyy siihen mitä jää — ja
   viite on kiinni kunnes sitä tarvitaan.
   ================================================================ */
.dialogi .keho > .konttasisus {
  display: flex; flex-direction: column; height: 100%; min-height: 0;
}
/* Lista vie kaiken jäljelle jäävän JA VIERITTYY SISÄLLÄÄN.

   `flex: 1 1 0` ja `min-height: 0` yhdessä: ilman jälkimmäistä flex-
   kohde ei kutistu sisältöään pienemmäksi, ja lista venyi ikkunan yli
   — mitattu 25.8.2026, keho 779 px ja lista 756 px vaikka muuta
   sisältöä oli 202 px. Silloin koko ikkuna vierittyy ja hinta- sekä
   suodatinpalkki katoavat ylös, mikä on huonompi kuin liian pieni
   lista. */
/* IKKUNA TARVITSEE MÄÄRÄTYN KORKEUDEN. `kasvaa`-lippu tekee ikkunasta
   sisältönsä korkuisen, ja venyvälle listalle ei silloin ole mitään
   jaettavaa: mitattu 25.8.2026, ikkuna kutistui 270 px:iin ja lista
   nollaan. Määrätty korkeus antaa flexille sen mitä se jakaa.

   88 vh eikä 100: ikkunan ympärille jää reuna, josta näkee että
   takana on laskenta. Yläraja 940 px, jottei se venyisi järjettömäksi
   pystynäytöllä. Ikkuna on yhä vedettävissä kulmasta. */
.dialogi:has(.konttasisus) { height: min(88vh, 940px); }
.dialogi .keho:has(> .konttasisus) { overflow: hidden; }
.konttasisus > .konttalista {
  /* `min-height: 0` EIKÄ POHJAA. Kokeilin 29.8.2026 antaa työlistalle
     300 px:n pohjan, jotta se voittaisi viitelohkon. Vartija
     tarkista_yksikkovalikko punastui, ja se oli oikeassa: mitattuna
     laatikon korkeuksilla 560, 480 ja 400 px sisältö LEIKKAANTUI joka
     kerta, koska keho on `overflow: hidden` eikä ylivuodolle ole
     vieritystä. Laatikkoa voi vetää kulmasta juuri niin pieneksi.

     Työlistan tila hoidetaan sen sijaan viitelohkon KATOLLA, alempana.
     Katto antaa periksi pienessä ikkunassa, pohja ei. */
  flex: 1 1 0; max-height: none; min-height: 0;
}

/* VIITE EI SAA MITATA ITSEÄÄN NÄYTÖSTÄ, VAAN LAATIKOSTA.

   Asko 29.8.2026, kuva irrotetusta ikkunasta jossa oli kolme tehtyä
   kauppaa: "tuolla alimmaisessa tehdään työ ja ei oo tilaa. sille pitää
   saada kunnon näkymä missä litteroita valitaan."

   Syy oli yksikkö. Viitelohkon katto oli `max-height: 40vh` eli 40 %
   NÄYTTÖIKKUNAN korkeudesta, mutta laatikko itse on 88vh. Viite varasi
   siis noin 45 % laatikosta — ja koska se oli `flex: 0 0 auto`, se ei
   suostunut kutistumaan vaikka työlistalla ei ollut tilaa. Mitattu
   kuvasta: viite 417 px, työlista noin 70 px.

   Kaksi korjausta samaan asiaan:
   1. katto on nyt OSUUS LAATIKOSTA (%), jolloin se pienenee kun laatikko
      pienenee sen sijaan että söisi koko laatikon,
   2. lohko on kutistuva (`flex: 0 1 auto`), joten työlistan pohja voittaa
      kun tila loppuu.

   Lohko ei katoa eikä sen sisältö muutu: se vierittyy sisällään. */
.konttasisus > .kauppalista:not(:empty) {
  /* Katto on pienempi kahdesta: osuus laatikosta pitää viitteen
     kohtuullisena pienessä ikkunassa, kiinteä 160 px estää sitä
     kasvamasta suurella näytöllä sitä mukaa kuin ikkuna kasvaa. Viite
     on luettelo jota vieritetään joka tapauksessa; sen kasvattaminen ei
     tuo lisää tietoa, työlistan kasvattaminen tuo.

     MIKSI KATTO EIKÄ TYÖLISTAN POHJA. Työlistalla on `flex-basis: 0`,
     joten se saa vain sen mitä yli jää — se on aina se joka luovuttaa
     ensin. Ainoa tapa antaa sille tilaa ilman pohjaa (joka leikkaisi
     sisältöä pienessä laatikossa) on rajoittaa sitä mikä on sen
     yläpuolella. Mitattu 29.8.2026: katolla 16 % työlista sai
     oletuskoossa 50 % laatikosta, katolla 26 % vain 40 %. */
  flex: 0 1 auto; min-height: 0; max-height: min(16%, 160px);
}
/* TEHDYT KAUPAT EIVÄT SAA LITISTYÄ. Asko 31.8.2026 ruutukuvaan:
   punainen kauppalohko oli leikkautunut kesken rivin, näkyvissä puolet
   yhdestä rivistä ja vieressä oma vierityspalkki.

   Syy: `flex: 1 1 auto` yhdessä `min-height: 0`:n kanssa antaa tämän
   kutistua nollaan asti kun sisarelementti (rivitaulukko) vie tilan.
   Katto 40vh ei auta, koska ongelma on pohjassa eikä katossa.

   Pohja on kaksi riviä: kutistuminen sallitaan, muttei sen alle.
   Yhden rivin puolikas ei kerro mitään ja näyttää rikkinäiseltä. */
.konttasisus > .kauppalista > .kauppalista-sisus {
  flex: 1 1 auto; min-height: 96px; max-height: 40vh; overflow: auto;
}

/* Kokoontaitettava otsikko: ohje ja tehdyt kaupat. Näyttää tekstiltä
   eikä napilta — se on avaus, ei toiminto. */
.ohjenappi {
  display: flex; align-items: center; gap: 10px; width: 100%;
  border: 0; background: none; cursor: pointer;
  padding: 6px 2px; margin: 0; text-align: left;
  font-size: 13px; color: var(--teksti-himmea);
}
.ohjenappi:hover { color: var(--oranssi); }
.ohjenappi b { margin-left: auto; color: var(--teksti); }
.lyhennettava { flex: 0 0 auto; }
.lyhennettava .selite { margin-top: 0; }
.kauppalista-sisus { max-height: 40vh; overflow: auto; }

/* 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. */
/* Toimittaja joka tulee kaupasta eika panoksen omasta kentasta.
   Sama muoto kuin kysytylla toimittajalla, mutta oma savy: kysytty
   hinta ja tehty kauppa eivat ole sama asia. */
.panostoimittaja.kaupasta {
  background: var(--vihrea-oma); color: #14532D;
  box-shadow: inset 0 0 0 1px #14532D33; font-weight: 600;
}

.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 hankintamatriisin
   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 myytya
   litteraa (tr.kaupparivi.myyty), ja kysytty hinta on eri asia kuin
   tehty kauppa. 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; }

/* HANKINTAMATRIISIN LITTERARYHMÄT. Asko 28.8.2026: "tänne littera jako
   kuin kansilehdellä. saman näköinen ilme, puumallin avaus."

   Ulkoasu on kansilehden väliotsikko sellaisenaan (`tr.valiotsikko`),
   koska taulukolla on myös luokka `ruudukko` — samannäköisyys ei ole
   kopioitu vaan sama sääntö. Kaksi asiaa on eri: nimisarake on tässä
   toinen eikä neljäs, joten toisen tason sisennys osoitetaan siihen,
   ja neljännestä sarakkeesta (Laskettu) sisennys otetaan pois — muuten
   yleinen sääntö työntäisi euromäärän sivuun. */
table.mmatriisi tr.valiotsikko.taso2 > td.mryhmanimi { padding-left: 22px; }
table.mmatriisi tr.valiotsikko.taso2 > td:nth-child(4) { padding-left: 8px; }
table.mmatriisi tr.valiotsikko > td { padding: 6px 8px; }

/* SOVI HINTA -LAATIKON LITTERARYHMÄT. Asko 28.8.2026: "sinne kanssa
   sama litterapuu."

   Sama väliotsikkotyyli kuin kansilehdellä ja hankinnassa. Nimisarake
   on tässä kolmas, joten toisen tason sisennys osoitetaan siihen ja
   yleisen säännön neljännen sarakkeen sisennys perutaan — muuten
   työsarakkeen euromäärä siirtyisi sivuun. */
.konttalista tr.valiotsikko.taso2 > td.kryhmanimi { padding-left: 22px; }
.konttalista tr.valiotsikko.taso2 > td:nth-child(4) { padding-left: 6px; }
.konttalista tr.valiotsikko > td { padding: 5px 6px; }
.konttalista tr.valiotsikko > td .merkki { margin-left: 6px; }
/* Ryhmän valintaruutu on samankokoinen kuin rivin oma: kaksi eri
   kokoista ruutua samassa sarakkeessa näyttäisi kahdelta eri
   toiminnolta. */
.konttalista .ryhmaruutu { margin: 0; }/* AI MODE -nappi: kytkin nayttaa kytkimelta molemmissa tiloissa.

   VIOLETTI POIS 29.8.2026. Asko: "pakitetaan väri maailmasta, ei toimi,
   poista myös liila, pakitetaan siihen mikä oli alunperin." Nappi on nyt
   tavallinen ylapalkin nappi; kytkimeksi sen erottaa harvennus ja
   lihavointi, ei oma varimaailma. Ohjelma on oranssi ja valkoinen. */
.ypainike.aimode-nappi { font-weight: 600; letter-spacing: .04em; }/* ---------------------------------------------- automaatin kansiopuu */
.kansiopuu { border: 1px solid var(--viiva); border-radius: 8px;
             overflow: hidden; margin: 10px 0; }
.kansiopuu .kansio {
  display: flex; align-items: center; gap: 6px; cursor: pointer;
  padding: 8px 10px; background: var(--tausta-3);
  border-top: 1px solid var(--viiva); user-select: none;
}
.kansiopuu .kansio:first-child { border-top: 0; }
.kansiopuu .kansio:hover { background: var(--tausta-2); }
/* Pudotuskohde on kerrottava ENNEN pudotusta: ilman korostusta
   käyttäjä ei tiedä mihin kansioon tiedosto on menossa. */
.kansiopuu .kansio.pudotus {
  background: var(--oranssi-vaalea); outline: 2px dashed var(--oranssi);
  outline-offset: -2px;
}
.kansiopuu .kansio.luokittelematon { background: #FEF3C7; }
.kansiopuu .kansiokuvake { font-size: 15px; }
.kansiopuu .tiedosto {
  display: flex; align-items: center; gap: 8px;
  padding: 5px 10px 5px 34px; border-top: 1px solid var(--tausta-3);
  background: var(--tausta);
}
.kansiopuu .tiedosto:hover { background: var(--tausta-2); }
.kansiopuu .tiedostonimi { flex: 1; min-width: 0; overflow: hidden;
                           text-overflow: ellipsis; white-space: nowrap; }
.kansiopuu .tiedostonapit { display: flex; gap: 4px; flex: 0 0 auto; }/* ================================================ 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; } }
.kansiopuu .tiedostokuvake.esikatselu { position: relative; cursor: zoom-in; }
.kansiopuu .esikatselukuva {
  position: absolute; left: 22px; top: -6px; z-index: 40;
  max-width: 320px; max-height: 240px; background: var(--tausta);
  border: 1px solid var(--viiva); border-radius: 6px;
  box-shadow: var(--varjo); padding: 3px; display: none;
}
.kansiopuu .tiedostokuvake.esikatselu:hover .esikatselukuva { display: block; }
.kansiopuu .kansio .kansionappi { margin-left: 8px; visibility: hidden; }
.kansiopuu .kansio:hover .kansionappi { visibility: visible; }
.kansiopuu .kansio.taso1 { padding-left: 26px; }
.kansiopuu .kansio.taso2 { padding-left: 42px; }
.kansiopuu .kansio.tyhja { background: var(--tausta); }
.kansiopuu .kansio.tyhja .kansionimi { color: var(--teksti-himmea); }
.kansiopuu .kansionimi { flex: 0 1 auto; }
/* Pampula: sama merkki kuin Finderin sininen tunniste. */
.kansiopuu .pampula {
  width: 9px; height: 9px; border-radius: 50%;
  background: #2F7CF6; flex: 0 0 auto; margin-left: 6px;
}/* ============================================== hankintanäkymän KPI-kortit
   Asko 29.8.2026: "ylälaidan yhteenvetokortit" ja "hankintanäkymän
   visuaalinen ilme on sekava". */
/* Kortin ulkoasu on yhteinen `.mittari`:n kanssa, ks. sen lohko
   ylempana. Tanne jaa vain se mika on hankinnalle omaa: arvon vari
   saasto- ja tappiotapauksessa. Raita tulee yhteisesta lohkosta. */
.kpikortti.saasto .arvo { color: var(--vihrea); }
.kpikortti.tappio .arvo { color: var(--punainen); }/* Vuorotteluväri ja väljyys: numerot eivät saa puuroutua. */
table.mmatriisi tbody tr.mrivi:nth-child(even) > td { background: var(--tausta-2); }
table.mmatriisi tbody tr.mrivi > td { padding-top: 7px; padding-bottom: 7px; }
table.mmatriisi td.oik { font-variant-numeric: tabular-nums; }

/* Hankintanäkymän tilalohkot: avoimet ja ostetut. */
table.mmatriisi tr.mtilaryhma > td {
  background: var(--tausta-3); padding: 10px 12px;
  border-top: 2px solid var(--viiva-tumma); font-size: 13px;
}
table.mmatriisi tr.mtilaryhma.avoin > td { border-top-color: #C0392B; }
table.mmatriisi tr.mtilaryhma.ostettu > td { border-top-color: var(--vihrea); }
table.mmatriisi tr.mtilaryhma .tilapallo { margin-right: 6px; }
table.mmatriisi tr.mtilaryhma .tilasumma {
  float: right; font-weight: 700; font-variant-numeric: tabular-nums;
}
/* Ostohinta budjettia vasten. Väri VAIN kun vertailu on olemassa —
   `laskettu = null` tarkoittaa ettei riviä voi verrata, ei ylitystä. */
table.mmatriisi td.ostoalle { color: var(--vihrea); font-weight: 600; }
table.mmatriisi td.ostoyli  { color: var(--punainen); font-weight: 600; }

/* ============================================ oikealta liukuva paneeli */
.paneelitausta { position: fixed; inset: 0; background: rgba(20,25,35,.35);
                 z-index: 60; }
.sivupaneeli {
  position: fixed; top: 0; right: 0; bottom: 0; width: min(880px, 92vw);
  background: var(--tausta); border-left: 1px solid var(--viiva);
  box-shadow: -8px 0 30px rgba(20,25,35,.18); z-index: 61;
  display: flex; flex-direction: column;
  transform: translateX(100%); transition: transform .18s ease-out;
}
.sivupaneeli.auki { transform: translateX(0); }
.paneeliylapalkki { display: flex; align-items: center; gap: 10px;
  padding: 12px 16px; border-bottom: 1px solid var(--viiva);
  background: var(--tausta-2); }
.paneeliylapalkki h2 { margin: 0; font-size: 16px; }
.paneeliylapalkki .painike { margin-left: auto; }
.infokuvake { cursor: help; font-size: 14px; }
.paneelisisus { flex: 1; overflow: auto; padding: 14px 16px 24px; }

.sopimuskortti { border: 1px solid var(--viiva); border-radius: 12px;
  padding: 12px 14px; margin-bottom: 14px; box-shadow: var(--varjo);
  border-left: 4px solid var(--vihrea); background: var(--tausta); }
.sopimusotsikko { display: flex; align-items: center; gap: 8px;
                  margin-bottom: 10px; }
.sopimuskpi { display: grid; gap: 12px; margin-bottom: 10px;
              grid-template-columns: repeat(3, minmax(0, 1fr)); }
.sopimuskpi .otsake { font-size: 10px; letter-spacing: .05em;
  text-transform: uppercase; color: var(--teksti-himmea); font-weight: 700; }
.sopimuskpi .arvo { font-size: 18px; font-weight: 700;
                    font-variant-numeric: tabular-nums; }
.sopimuskpi .saasto .arvo { color: var(--vihrea); }
.sopimuskpi .tappio .arvo { color: var(--punainen); }

/* Kelluva toimintopalkki hankintanäkymässä. */
.mtoiminnot {
  position: sticky; top: 0; z-index: 5;
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  padding: 10px 12px; margin-bottom: 8px;
  background: var(--oranssi-vaalea); border: 1px solid var(--oranssi);
  border-radius: 10px; box-shadow: var(--varjo);
}table.mmatriisi .mvalinta { width: 34px; text-align: center; }
table.mmatriisi tr.mvalittu > td { background: var(--sininen-rivi); }

/* ══════════════════════════════════════════════ HANKINTANÄKYMÄN ILME
   Asko 29.8.2026: "toiminnallisuus toimii, mutta visuaalinen ilme on
   sekava, raskas ja vanhahtava. Nyt keskitytään 100 % ulkoasuun."

   Osui: edellinen kierros lisäsi raidoituksen ja napit mutta jätti
   rakenteen ennalleen. Tämä on ulkoasupassi, ei toiminnallisuutta. */

/* ---- 1. Taulukko kapseliksi ---- */
.taulukkokehys:has(table.mmatriisi) {
  background: var(--tausta); border: 1px solid var(--viiva);
  border-radius: 14px; box-shadow: var(--varjo); overflow: hidden;
  margin-bottom: 18px;
}
table.mmatriisi { border-collapse: separate; border-spacing: 0; width: 100%; }

/* ---- 2. Tilalohkojen otsikot: iso, ilmava, tagi ---- */
table.mmatriisi tr.mtilaryhma > td {
  background: linear-gradient(180deg, var(--tausta-2), var(--tausta-3));
  padding: 16px 18px; font-size: 15px;
  border-top: 1px solid var(--viiva); border-bottom: 1px solid var(--viiva);
  letter-spacing: .01em;
}
table.mmatriisi tr.mtilaryhma:first-child > td { border-top: 0; }
table.mmatriisi tr.mtilaryhma b { font-size: 16px; }
table.mmatriisi tr.mtilaryhma .tilapallo {
  display: inline-block; width: 10px; height: 10px; border-radius: 50%;
  margin-right: 10px; font-size: 0; vertical-align: middle;
}
table.mmatriisi tr.mtilaryhma.avoin   .tilapallo { background: #C0392B; }
table.mmatriisi tr.mtilaryhma.ostettu .tilapallo { background: #12805C; }
table.mmatriisi tr.mtilaryhma.avoin   > td { border-left: 4px solid #C0392B; }
table.mmatriisi tr.mtilaryhma.ostettu > td { border-left: 4px solid #12805C; }
table.mmatriisi tr.mtilaryhma .himmea {
  font-size: 12px; margin-left: 10px; font-weight: 400;
}
table.mmatriisi tr.mtilaryhma .tilasumma { font-size: 17px; }

/* ---- 3. Väljyys ja typografia ---- */
table.mmatriisi thead th {
  padding: 11px 12px; font-size: 11px; letter-spacing: .06em;
  text-transform: uppercase; color: var(--teksti-himmea);
  background: var(--tausta-2); border-bottom: 1px solid var(--viiva);
  font-weight: 700;
}
table.mmatriisi tbody td { padding: 11px 12px; font-size: 13.5px;
                           border-bottom: 1px solid var(--tausta-3); }
table.mmatriisi tbody tr.mrivi:nth-child(even) > td { background: transparent; }
table.mmatriisi tbody tr.mrivi:hover > td { background: var(--tausta-2); }
/* Rahasarakkeet oikealle ja tasalevyisin numeroin, nimet vasemmalle. */
table.mmatriisi td.oik, table.mmatriisi th.oik {
  text-align: right; font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum"; }

/* ---- 4. Litterarivit erottuvat, alarivit sisennetään ---- */
table.mmatriisi tr.mryhma > td {
  background: var(--tausta-3); font-weight: 600; padding: 10px 12px;
  border-bottom: 1px solid var(--viiva);
}
table.mmatriisi tr.mryhma.taso1 > td.mryhmanimi { padding-left: 16px; }
table.mmatriisi tr.mryhma.taso2 > td.mryhmanimi { padding-left: 34px; }
table.mmatriisi tr.mrivi > td:nth-child(3) { padding-left: 30px; }
table.mmatriisi tr.mlapsi > td:nth-child(3) { padding-left: 48px;
                                              color: var(--teksti-himmea); }

/* ---- 5. Nykyaikaiset valintaruudut ---- */
table.mmatriisi .mvalinta input[type=checkbox],
.mtoiminnot input[type=checkbox] {
  width: 17px; height: 17px; accent-color: var(--oranssi);
  cursor: pointer; margin: 0;
}table.mmatriisi tr.mvalittu > td {
  background: var(--oranssi-vaalea) !important;
  box-shadow: inset 3px 0 0 var(--oranssi);
}/* ---- 6. Kelluva toimintokortti ---- */
/* YÖTILALOHKO POISTETTU 31.8.2026. Asko: "tänne oli jäänyt jotain
   mustaa ja ei oikein toimi — muuttui valkoiseksi kun teki kaupan."

   Tämä oli 29.8. pakitetun värimaailmakokeilun jäänne: se ylikirjoitti
   vaalean palkin tummalla (#1D2430, kentät #2A3340, teksti #F3F5F8) ja
   jäi voimaan kun muu kokeilu poistettiin. Valkoiseksi se "muuttui"
   siksi, että palkki piirretään vain kun rivejä on valittuna — kaupan
   jälkeen valinta tyhjenee, palkki katoaa ja alta näkyy tavallinen
   vaalea tausta.

   Asko 29.8.2026: "pakitetaan väri maailmasta." Oranssi ja valkoinen.
   Palkin oma tyyli on ylempänä (`--oranssi-vaalea` ja oranssi reunus)
   eikä sitä tarvitse toistaa täällä.

   Valittujen rivien korostus jää: se on tieto eikä väriteema. */
.mtoiminnot > b { font-size: 15px; }
.mtoiminnot > .himmea { margin-right: auto; font-size: 13px; }
/* KENTÄT EIVÄT VENY. Asko 31.8.2026: "laatikon koko taas liian leveä,
   1/3 osaa riittää." Kattaa-valikko oli ainoa ilman leveyttä, joten se
   venyi rivin yli ja työnsi muut omalle rivilleen. */
.mtoiminnot input, .mtoiminnot select { flex: 0 0 auto; }
.mtoiminnot select.solukentta { width: 170px; }/* ---- 7. KPI-kortit ilmavammiksi ---- */

/* Tarjouspyynnön oma litterapuu. Matriisin alkuvalintaa voi täydentää
   avaamalla pää- ja alalitterat ennen esikatselua. Native details/summary
   säilyttää näppäimistökäytön eikä tarvitse omaa avauslogiikkaa. */
/* Luku ja sita muuttavat painikkeet ovat samassa rivissa, ohje omalla
   rivillaan. Aiemmin painikkeet olivat vastakkaisessa reunassa, ja
   levealla dialogilla niiden ja luvun valiin jai koko puun leveys. */
/* Puu, sen otsikko ja alalitteravalinta kuuluvat koko dialogin leveydelle.
   Ilman tata .kentat-ruudukko ahtaa ne 190 pikselin label-sarakkeeseen:
   painikkeet menivat allekkain ja puu jai omaan puolikkaaseensa. */
.tarjouspyynto-puuotsikko,
.tarjouspyynto-rivipuu,
.tarjouspyynto-valinta { grid-column: 1 / -1; }
.tarjouspyynto-puuotsikko { margin-bottom: 6px; }
.tarjouspyynto-luku {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  margin-bottom: 2px;
}
.tarjouspyynto-luku > b { margin-right: 4px; }

/* 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); }
.tarjouspyynto-rivipuu {
  max-height: 360px; overflow: auto; border: 1px solid var(--viiva);
  border-radius: 8px; background: var(--tausta); margin-bottom: 8px;
}
.tarjouspyynto-ryhma { border-bottom: 1px solid var(--viiva); }
.tarjouspyynto-ryhma:last-child { border-bottom: 0; }
.tarjouspyynto-ryhma > summary {
  cursor: pointer; padding: 9px 12px; background: var(--tausta-2);
  list-style-position: inside;
}
.tarjouspyynto-ryhma.taso2 {
  margin-left: 20px; border-left: 2px solid var(--viiva);
}
.tarjouspyynto-ryhma.taso2 > summary { background: var(--tausta-3); }
.tarjouspyynto-rivi {
  display: grid; grid-template-columns: 22px 72px minmax(220px, 1fr) 110px;
  align-items: center; gap: 8px; min-height: 40px; padding: 5px 12px 5px 28px;
  border-top: 1px solid var(--tausta-3); cursor: pointer;
}
.tarjouspyynto-rivi:hover { background: var(--oranssi-vaalea); }
.tarjouspyynto-rivi input {
  width: 17px; height: 17px; accent-color: var(--oranssi);
}
.tarjouspyynto-rivi > :last-child { text-align: right; }
.tarjouspyynto-valinta {
  display: flex !important; flex-direction: row !important; align-items: center;
  gap: 9px; min-height: 38px; padding: 7px 10px;
  border: 1px solid var(--viiva); border-radius: 7px; background: var(--tausta-2);
}
.tarjouspyynto-valinta input {
  width: 17px; height: 17px; flex: 0 0 auto; accent-color: var(--oranssi);
}

.kpikortti { padding: 16px 18px; }
.kpikortti .arvo { font-size: 26px; }

/* ---- Avoimet ja ostetut omina kortteinaan ---- */
.hlohkot { display: flex; flex-direction: column; gap: 20px; }
.hlohko {
  background: var(--tausta); border: 1px solid var(--viiva);
  border-radius: 14px; box-shadow: var(--varjo); overflow: hidden;
}
.hlohko-otsikko {
  display: flex; align-items: center; gap: 12px;
  padding: 16px 20px; border-bottom: 1px solid var(--viiva);
}
.hlohko-otsikko h3 { margin: 0; font-size: 17px; font-weight: 700; }
.hlohko-otsikko .maara { color: var(--teksti-himmea); font-size: 13px; }
.hlohko-otsikko .summa {
  margin-left: auto; font-size: 19px; font-weight: 700;
  font-variant-numeric: tabular-nums;
}
.hlohko-otsikko .tilapallo {
  width: 11px; height: 11px; border-radius: 50%; flex: 0 0 auto;
}
.hlohko.avoin   .hlohko-otsikko { background: #FDF2F0; }
.hlohko.avoin   .hlohko-otsikko .tilapallo { background: #C0392B; }
.hlohko.avoin   { border-top: 3px solid #C0392B; }
.hlohko.ostettu .hlohko-otsikko { background: #EFF8F4; }
.hlohko.ostettu .hlohko-otsikko .tilapallo { background: #12805C; }
.hlohko.ostettu { border-top: 3px solid #12805C; }
.hlohko-taulu { overflow-x: auto; }/* Vanha yhden taulukon kehys pois käytöstä näissä lohkoissa. */
.hlohko .taulukkokehys { border: 0; box-shadow: none; border-radius: 0; }
/* Kahden lohkon taulukoissa TARTTUVA OTSIKKO ON VIKA, ei apu.

   Mitattu kuvasta 29.8.2026: "Ostetut ja lukitut" -lohkon sarakeotsikko
   jäi kellumaan ruudun keskelle ja peitti oman lohkonsa ensimmäisen
   rivin. Syy on että tarttuva otsikko on kirjoitettu YHDELLE
   taulukolle; kahdella taulukolla molemmat tarttuvat samaan ruudun
   ylälaitaan, ja alempi peittää omat rivinsä.

   Lohkon oma otsikkokortti kertoo jo mistä lohkosta on kyse ja mikä sen
   summa on, joten sarakeotsikon ei tarvitse seurata mukana.

   TÄMÄ EI OLE VÄRIÄ. Kun väriteema pakitettiin 29.8.2026, tämä lähti
   samassa checkoutissa mukana ja otsikko alkoi taas kellua. Palautettu
   erikseen. */
.hlohko table.mmatriisi thead th { position: static; top: auto; z-index: auto; }
.hlohko table.mmatriisi tr.litteraotsikko > td { position: static; }
/* Lohkon otsikkokortti sen sijaan saa tarttua — se on se joka kertoo
   kummassa lohkossa ollaan pitkää listaa vierittäessä. */
.hlohko-otsikko { position: sticky; top: 0; z-index: 6; }

/* ---------------------------------------------------- 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 `.dialogi:has(.konttasisus)`, 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); }

/* Sivupalkin laskentapuun tyylit poistettu 30.8.2026 puun mukana. */

/* ------------------------------------------- automaatin vaiheistus */
.vaiheotsikko { color: var(--teksti-himmea); font-size: 12.5px;
                margin: -4px 0 10px; }
/* 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;
}
.vanhatvaiheet { border-top: 1px solid var(--viiva); margin-top: 6px;
                 padding-top: 10px; }

/* Referaatin osiot. Otsikko on pieni ja harvennettu, kohdat lyhyitä —
   lukija silmäilee otsikot ja pysähtyy siihen jota etsii. */
.referaatti .rkohde { font-weight: 700; font-size: 14.5px; margin-bottom: 10px; }
.referaatti .rosio { margin: 9px 0 0; }
.referaatti .rotsikko {
  font-size: 11px; font-weight: 700; letter-spacing: .07em;
  text-transform: uppercase; color: var(--oranssi-tumma); margin-bottom: 2px;
}
.referaatti ul { margin: 0; padding-left: 20px; }
.referaatti li { margin: 1px 0; }

/* Epäselvät ja hylätyt: listoja joita silmäillään, ei kappaleita. */
.varoitus.epaselvat ul, .varoitus.rikkiasiakirja ul { margin: 6px 0 0; padding-left: 20px; }
.varoitus.epaselvat li, .varoitus.rikkiasiakirja li { margin: 3px 0; }
.varoitus.rikkiasiakirja { background: #FDECEA; border-color: #E7A9A0; }
.varoitus.rikkiasiakirja .pieni { margin-top: 6px; }
.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; }

/* -------------------------------------------- kuittaus ja projektitiedot */
.kuittaus {
  display: flex; align-items: center; gap: 16px; flex-wrap: wrap;
  margin: 16px 0 6px; padding: 14px 16px;
  border: 2px solid var(--oranssi); border-radius: 10px;
  background: var(--oranssi-vaalea);
}
.kuittaus > div { flex: 1; min-width: 260px; }
table.projektimatriisi { width: 100%; }
table.projektimatriisi .ptieto { width: 48%; }
table.projektimatriisi .parvo { width: 36%; }
table.projektimatriisi .parvo input { width: 100%; }
table.projektimatriisi .ptilasolu { width: 110px; white-space: nowrap; }
.pmerkki {
  display: inline-block; padding: 1px 8px; border-radius: 999px;
  font-size: 11px; font-weight: 700; letter-spacing: .03em;
  border: 1px solid var(--viiva);
}
.pmerkki.tiedossa      { background: #E4F5EE; color: #14532D; border-color: #A8D9C4; }
.pmerkki.puuttuu       { background: #FDECEA; color: #8A2A1E; border-color: #E7A9A0; }
.pmerkki.varmistettava { background: #FFF7E6; color: #8A6114; border-color: #E3C58A; }
tr.ptila-puuttuu > td       { background: rgba(200,70,60,.05); }
tr.ptila-varmistettava > td { background: rgba(200,150,20,.06); }
/* Vaihe 3 erottuu kuittauksesta: tehtävä eikä kysymys. */
.kuittaus.vaihe3 { border-color: var(--sininen); background: #F2F7FF; }

/* --------------------------------------------------------- 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 .vnro {
  display: inline-flex; align-items: center; justify-content: center;
  width: 19px; height: 19px; border-radius: 50%; font-size: 11px;
  font-weight: 700; background: var(--viiva); color: var(--tausta);
}
.vaihenauha .vaihe.tehty { border-color: #A8D9C4; background: #E4F5EE; color: #14532D; }
.vaihenauha .vaihe.tehty .vnro { background: var(--vihrea); color: #fff; }
.vaihenauha .vaihe.nyt {
  border-color: var(--oranssi); background: var(--oranssi-vaalea);
  color: var(--teksti); font-weight: 600;
}
.vaihenauha .vaihe.nyt .vnro { background: var(--oranssi); color: #fff; }
.kuittaus.vaihe4 { border-color: var(--vihrea); background: #EFF8F4; }
tr.mittalehti td { vertical-align: top; }
tr.mittalehti.t-vahvistettu td { background: #F1FAF6; }
tr.mittalehti.t-ristiriita td { background: #FDECEA; }
tr.mittalehti.t-ei-vahvistettavissa td { background: #FDF6E7; }

/* --------------------------------------------- edistymispalkki ja kello

   Asko 30.8.2026: "kello ja keksitty prosentti, arvioi asiakirjojen
   määrästä." Palkki liikkuu myös silloin kun prosentti ei muutu —
   kiiltoanimaatio kertoo että työ käy, luku kertoo arvion. Ne ovat eri
   asioita ja siksi molemmat. */
.edistyspalkki {
  height: 10px; border-radius: 999px; overflow: hidden;
  background: var(--tausta-3); margin: 6px 0 14px; max-width: 520px;
}
.edistystayte {
  height: 100%; width: 0; border-radius: 999px;
  background: linear-gradient(90deg,
    var(--oranssi) 0%, var(--oranssi-tumma) 40%, var(--oranssi) 80%);
  background-size: 240% 100%;
  animation: kiilto 1.6s linear infinite;
  transition: width .8s ease-out;
}
@keyframes kiilto { from { background-position: 100% 0; }
                    to   { background-position: -140% 0; } }
.kelloluku { font-variant-numeric: tabular-nums; }
/* Virhetilassa palkki punaiseksi ja liike pois: pysähtynyt punainen
   kertoo että työ ei jatku. */
.automaatti .varoitus + .edistyspalkki .edistystayte,
.edistyspalkki.virhe .edistystayte {
  background: var(--punainen); animation: none;
}

/* ------------------------------------------- tiedostohallinta (File Explorer)

   Asko 30.8.2026: "kansiorakenteen nykyinen ilme on liian tiivis, harmaa
   ja sekava (liikaa toistuvia pikkunappeja jokaisella rivillä)."

   Kolme muutosta ja jokainen poistaa yhden syyn siihen:
   napit piiloon hiiren alle, kansiot korteiksi, rivit väljemmiksi. */
.kansiopuu { border: 0; border-radius: 0; overflow: visible; }
.kansiopuu .kansio {
  border: 1px solid var(--viiva); border-radius: 10px;
  background: var(--tausta); margin: 8px 0 0; padding: 11px 14px;
  gap: 9px; box-shadow: 0 1px 2px rgba(20,25,35,.05);
}
.kansiopuu .kansio:first-child { border-top: 1px solid var(--viiva); }
.kansiopuu .kansio:hover { background: var(--tausta-2); }
.kansiopuu .kansionimi { font-weight: 650; font-size: 13.5px; }
/* Tiedostomäärä tila-tagina, ei irrallisena numerona rivin lopussa. */
.kansiopuu .kansio .pampula { display: none; }
.kansiopuu .kansio .himmea.pieni {
  background: var(--tausta-3); border-radius: 999px;
  padding: 1px 9px; font-size: 11.5px; color: var(--teksti-himmea);
}
.kansiopuu .tiedosto {
  display: flex; align-items: center; gap: 10px;
  padding: 9px 14px 9px 40px; border: 0; background: transparent;
  border-radius: 8px; margin: 1px 0;
}
.kansiopuu .tiedosto:hover { background: var(--tausta-2); }
button.tiedostonimi {
  flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis;
  white-space: nowrap; text-align: left; border: 0; background: none;
  padding: 0; font: inherit; color: var(--teksti); cursor: pointer;
}
button.tiedostonimi:hover { color: var(--oranssi); text-decoration: underline; }
/* Piirustuksen tagi: ensisijainen toiminto näkyy ilman että sitä etsii. */
.mittatagi {
  font-size: 11px; font-weight: 650; white-space: nowrap;
  padding: 2px 9px; border-radius: 999px;
  background: var(--oranssi-vaalea); color: var(--oranssi-tumma);
  border: 1px solid var(--oranssi);
}
/* ⋮ vasta hiiren alla — mutta AINA näppäimistöllä tarkennettaessa,
   muuten toiminnot olisivat tavoittamattomissa ilman hiirtä. */
.rivivalikko {
  border: 0; background: none; cursor: pointer; font-size: 16px;
  line-height: 1; padding: 2px 6px; border-radius: 6px;
  color: var(--teksti-himmea); opacity: 0; transition: opacity .12s;
}
.kansiopuu .tiedosto:hover .rivivalikko,
.rivivalikko:focus { opacity: 1; }
.rivivalikko:hover { background: var(--tausta-3); color: var(--teksti); }
/* Liitenumero: pieni ja hiljainen, mutta luettavissa. Se ei erota
   lehtiä toisistaan — nimi erottaa — joten se ei saa viedä huomiota. */
.liitemerkki {
  font-size: 10.5px; font-weight: 700; letter-spacing: .02em;
  color: var(--teksti-himmea); background: var(--tausta-3);
  border-radius: 4px; padding: 1px 5px; min-width: 30px;
  text-align: center; white-space: nowrap; font-variant-numeric: tabular-nums;
}

/* ------------------------------------------------------ hinnoittelu (vaihe 5)

   Varmuus on rivin tärkein sarake, ei koriste. Epävarma osuma on se
   kohta jossa väärä hinta menisi läpi näyttäen käyttäjän omalta
   tarkistetulta luvulta — siksi se erottuu myös rivin taustasta eikä
   vain merkistä. */
.kuittaus.vaihe5 { border-color: var(--sininen); background: #F2F7FF; }
.hinnoittelu { margin-top: 18px; }
table.hinnoittelutaulu { width: 100%; font-size: 12.5px; }
table.hinnoittelutaulu td { vertical-align: top; padding: 6px 8px; }
tr.hrivi.epavarma > td { background: rgba(200,150,20,.07); }
tr.hrivi.varma > td { background: rgba(30,140,100,.05); }
tr.mittalehti.t-aliurakka td { background: var(--tausta-2); color: var(--teksti-himmea); }

/* ==================================================================
   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, ja .merkki.myyty pitää oman vihreänsä. 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; }

/* ------------------------------------------ aineiston pudotuslaatikko
   Asko 30.8.2026: "joku laatikko tuohon niinku kuuluu että vedä tähän
   tai tuo, jolloin se ilmestyy siihen ja lähtee purkamaan."

   Katkoviiva kertoo että tähän saa pudottaa; yhtenäinen viiva
   lähetyksen ajan, jolloin laatikko ei enää ota vastaan vaan kertoo
   olevansa varattu. */
.pudotusalue {
  border: 2px dashed var(--viiva); border-radius: 10px;
  padding: 26px 18px; margin: 4px 0 10px; text-align: center;
  background: var(--tausta-2); cursor: pointer;
  transition: border-color .12s, background .12s;
}
.pudotusalue:hover { border-color: var(--oranssi); background: var(--tausta-3); }
/* Korostus ENNEN pudotusta: ilman sitä ei tiedä osuuko oikeaan. */
.pudotusalue.yli { border-color: var(--oranssi); background: var(--oranssi-vaalea); }
.pudotusalue.kesken {
  cursor: progress; border-style: solid; border-color: var(--oranssi);
  background: var(--tausta-2);
}
.pudotusalue .otsake { font-weight: 600; margin-bottom: 3px; }
.pudotusalue .pieni { font-size: 12px; margin-top: 6px; }
.pudotusalue .aitahti.miettii {
  width: 40px; height: 40px; display: block; margin: 0 auto 10px;
}

/* ------------------------------------------ aineisto ei mahtunut
   Asko 30.8.2026: aineistosta luettiin 8 % ja loput katosivat
   äänettömästi, jolloin referaatti väitti ettei tarjouspyyntöä ole.
   Tämä laatikko on se ääni. Keltainen eikä punainen: ajo onnistui,
   mutta sen tulos koskee vain osaa aineistosta. Ks. TODO.md luku 60. */
.aineistovaroitus {
  background: #FEF6E0; border: 1px solid #E8C46A; border-radius: 8px;
  padding: 11px 13px; margin: 8px 0; line-height: 1.45;
}
.aineistovaroitus b { display: block; margin-bottom: 3px; }

/* 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);
}

/* Kauppasarakkeet piiloon kun niissä ei ole mitään. Asko 31.8.2026:
   "neljä saraketta yhdeksästä on tyhjiä." Mitattu Prisman hankinnasta:
   Toimittaja 0/22, Ostohinta 0/22, Erotus 0/22 — ne täyttyvät vasta
   kaupan jälkeen mutta veivät kolmanneksen taulukon leveydestä heti.

   Luokka tulee datasta: `ilman-kauppaa` asetetaan vain jos yhdelläkään
   lohkon rivillä ei ole toimittajaa, kysyttyä hintaa eikä ostohintaa.
   Sovittu mutta ostamaton rivi pitää sarakkeet näkyvissä. */
table.mmatriisi.ilman-kauppaa .mtoimittaja,
table.mmatriisi.ilman-kauppaa .mostohinta,
table.mmatriisi.ilman-kauppaa .merotus { display: none; }

/* ==================================================================
   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, ei vain hankintaan. */
.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, hankinta ja lomakkeet saavat enemmän hengitystilaa. */
.kortti, .mittari, .kpikortti, .hlohko {
  border-color: var(--viiva); border-radius: 14px; box-shadow: var(--varjo);
}
.kortti { padding: 16px; }
.mittari, .kpikortti { padding: 16px 18px 15px; }
.mittari .arvo, .kpikortti .arvo { font-size: 25px; letter-spacing: -.025em; }
.mittarit, .kpirivi { 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; }

/* Hankinta on päätösnäkymä. Korttien otsikoista nostetaan seuraava työ,
   ja tilapainikkeet ovat samalla korkeudella muiden toimintojen kanssa. */
.hlohkot { gap: 24px; }
.hlohko { border-top-width: 4px; }
.hlohko-otsikko { padding: 18px 20px; }
.hlohko-otsikko h3 { font-size: 18px; letter-spacing: -.015em; }
.hlohko-otsikko .summa { font-size: 21px; }
.mtoiminnot { border-radius: 12px; padding: 12px 14px; }
.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, .kpikortti .arvo { font-size: 22px; }
}

/* ------------------------------------------------------ väri-identiteetti
   Ilmeelle oma tunniste ilman uutta "värimaailmaa":
   oranssi = tee päätös, sininen = tieto / tarjousvaihe, 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 tai tarjousvaiheeseen,
   ei koskaan onnistuneeseen kauppaan. */
.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; }
.mtilamerkki.tarjouspyynto {
  background: var(--sininen-vaalea); border-color: #A9C8F0; color: var(--sininen-info);
}
.mtilamerkki.neuvottelu {
  background: var(--keltainen-vaalea); border-color: #EAC66E; color: #8A6114;
}
.mtilamerkki.ostettu, .mtilamerkki.toimitettu {
  background: var(--vihrea-vaalea); border-color: #9DD5BB; color: #0D7250;
}

/* 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,
html.yo tr.kaupparivi:hover > 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 .ailokirivi.tyhja,
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;
}

/* ------------------------------------------------ hankinnan poimintatila

   Hankinta on pitkän listan työtä. Yhteenveto ei saa syödä pöydän
   pystysuuntaista tilaa: samat luvut ovat tässä yhdellä, nopeasti
   silmäiltävällä rivillä ja litterataulukko alkaa lähempää yläreunaa. */
.hankintaotsikko { min-height: 56px; padding-top: 10px; padding-bottom: 10px; }
.hankinta-yhteenveto {
  display: grid; grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 0; margin: 12px 0 10px; overflow: hidden;
  border: 1px solid var(--viiva); border-radius: 10px;
  background: var(--tausta); box-shadow: var(--varjo);
}
.hankinta-mittari {
  display: grid; grid-template-columns: auto 1fr auto; align-items: baseline;
  gap: 8px; min-width: 0; padding: 10px 14px;
  border-right: 1px solid var(--viiva);
}
.hankinta-mittari:last-child { border-right: 0; }
.hankinta-mittari .otsake {
  font-size: 10px; font-weight: 700; letter-spacing: .08em;
  text-transform: uppercase; color: var(--teksti-himmea); white-space: nowrap;
}
.hankinta-mittari strong {
  min-width: 0; font-size: 19px; font-variant-numeric: tabular-nums;
  letter-spacing: -.02em; white-space: nowrap;
}
.hankinta-mittari .selite {
  overflow: hidden; color: var(--teksti-himmea); font-size: 11px;
  text-align: right; text-overflow: ellipsis; white-space: nowrap;
}
.hankinta-mittari.saasto strong { color: var(--vihrea); }
.hankinta-mittari.tappio strong { color: var(--punainen); }

/* Suodatus ja ensimmäinen littera pysyvät lähellä toisiaan: käyttäjä tekee
   tällä sivulla poimintaa, ei seuraa erillistä raporttinäkymää. */
#matriisi .mtyokalut { margin: 0 0 10px; }
#matriisi .hlohkot { gap: 14px; }
#matriisi .hlohko { border-radius: 10px; border-top-width: 3px; }
#matriisi .hlohko-otsikko { padding: 12px 16px; }
#matriisi .hlohko-otsikko h3 { font-size: 16px; }
#matriisi .hlohko-otsikko .summa { font-size: 19px; }
#matriisi table.mmatriisi tbody tr.mrivi > td { padding-top: 5px; padding-bottom: 5px; }

/* Panospampula on tarkoituksella pieni: se kertoo poimintatilasta ilman
   että jokainen alarivi muuttuu väripalkiksi. Vihreä rengas = kysytty
   aiemmin; oranssi täyttö + merkki = juuri nyt ostoskorissa. Kysyttyä ei
   poisteta käytöstä, koska vertailutarjous on normaali hankintatoimi. */
.poimintapampula {
  appearance: none; width: 19px; height: 19px; margin: 0; cursor: pointer;
  border: 2px solid var(--viiva-tumma); border-radius: 50%; background: var(--tausta);
  vertical-align: middle; transition: background .12s ease, border-color .12s ease;
}
.poimintapampula:hover { border-color: var(--oranssi); }
.poimintapampula.kysytty { border-color: var(--vihrea); box-shadow: inset 0 0 0 3px var(--tausta), inset 0 0 0 7px var(--vihrea); }
.poimintapampula:checked { border-color: var(--oranssi); background: var(--oranssi); box-shadow: inset 0 0 0 3px var(--tausta); }
.poimintapampula:disabled { cursor: not-allowed; opacity: .42; }
.mlapsi.poimittava { cursor: pointer; }
.mlapsi.poimittava:hover > td { background: color-mix(in srgb, var(--oranssi-vaalea) 55%, var(--tausta)); }

.poimintakori {
  margin: 0 0 10px; padding: 10px 12px; display: grid;
  grid-template-columns: minmax(260px, 1fr) repeat(2, auto); gap: 8px 14px;
  align-items: center; border: 1px solid color-mix(in srgb, var(--oranssi) 40%, var(--viiva));
  border-left: 4px solid var(--oranssi); border-radius: 10px;
  background: var(--oranssi-vaalea); box-shadow: var(--varjo);
}
.poimintakori.tyhja {
  display: flex; min-height: 38px; padding: 8px 12px; gap: 7px;
  border-left-color: var(--viiva-tumma); background: var(--tausta);
  box-shadow: none; color: var(--teksti-himmea);
}
/* 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. */
@media (min-width: 1101px) {
  .poimintakori:not(.tyhja) {
    position: fixed; right: 20px; bottom: 20px; z-index: 45;
    width: 360px; min-width: 300px; min-height: 240px; max-width: min(720px, calc(100vw - 24px));
    max-height: calc(100vh - 24px); resize: both; overflow: auto; grid-template-columns: 1fr;
    box-shadow: 0 12px 36px rgba(0,0,0,.25);
  }
  .poimintakori:not(.tyhja) .poimintakori-otsikko { grid-column: 1 / -1; }
  .poimintakori:not(.tyhja) .poimintakori-ryhma { grid-column: 1; }
}
.poimintakori-otsikko { display: flex; align-items: center; gap: 7px; min-width: 0; cursor: grab; }
.poimintakori-otsikko:active { cursor: grabbing; }
.poimintakori-kahva { flex: none; color: var(--teksti-himmea); font-size: 18px;
  letter-spacing: -3px; user-select: none; }
.poimintakori-merkki { color: var(--oranssi); font-size: 15px; }
.poimintakori .linkkipainike { justify-self: end; }
.poimintakori-ryhma { display: grid; grid-template-columns: 1fr; gap: 6px; }
.poimintakori-ryhma .painike { justify-self: start; padding: 6px 9px; font-size: 12px; }
.poimintakori-tuotteet { display: grid; gap: 3px; max-height: 180px; overflow: auto;
  padding: 7px 8px; border: 1px solid var(--viiva); border-radius: 7px; background: var(--tausta); }
.poimintakori-tuote { display: grid; grid-template-columns: auto minmax(0, 1fr) auto auto;
  gap: 6px; align-items: center; font-size: 12px; }
.poimintakori-tuote > span:nth-child(2) { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.poimintakori-tuote strong { font-variant-numeric: tabular-nums; font-size: 12px; }
.poimintakori-poista { border: 0; background: transparent; color: var(--teksti-himmea);
  font-size: 12px; line-height: 1; cursor: pointer; padding: 4px 5px; }
.poimintakori-poista:hover { color: var(--punainen); }
.poimintakori-ostohinta { grid-column: 1 / -1; display: flex; align-items: center;
  justify-content: space-between; gap: 10px; padding-top: 7px; border-top: 1px solid var(--viiva); }
.poimintakori-huomio { grid-column: 1 / -1; color: var(--teksti-himmea); font-size: 12px; }

@media (max-width: 1100px) {
  .hankinta-yhteenveto { grid-template-columns: 1fr; }
  .hankinta-mittari { border-right: 0; border-bottom: 1px solid var(--viiva); }
  .hankinta-mittari:last-child { border-bottom: 0; }
  .poimintakori { grid-template-columns: 1fr; }
  .poimintakori .linkkipainike { justify-self: start; }
}

/* ------------------------------------------- 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; }
