/* Sitepilot — Modern design system (Linear/Attio-inspired) */
/* Inter-font ladataan HTML <head>:istä omasta static/fonts-kansiosta (_fontti_head.html) */

:root {
    /* Brand */
    /* HallaLink-sininen oletus (18.10.2026), yrityksen teema korvaa base.html:ssä */
    --primary: #1E3A8A;
    --primary-hover: #182F70;
    --primary-light: #E8EDF7;
    --primary-border: #B4C2E3;
    --linkki: #2563eb;
    /* huomio = violetti korostus (kirjanpito, hyvitys) */
    --tila-huomio-tausta: #f3e8ff;
    --tila-huomio-teksti: #6b21a8;

    /* Accent (sekundääri) — lämmin teal, hienostunut */
    --accent: #0d9488;            /* teal-600 */
    --accent-hover: #0f766e;      /* teal-700 */
    --accent-light: #ccfbf1;      /* teal-100 */
    --accent-border: #5eead4;     /* teal-300 */

    /* Semantic */
    --success: #16a34a;           /* green-600 */
    --success-light: #dcfce7;     /* green-100 */
    --danger: #dc2626;            /* red-600 */
    --danger-light: #fee2e2;
    --warning: #f59e0b;           /* amber-500 */
    --warning-light: #fef3c7;
    --info: #0ea5e9;              /* sky-500 */
    --info-light: #e0f2fe;

    /* Neutraalit — lämmin luonnonvalkoinen aiemman sinertävän harmaan tilalle.
       Väripalettityö 8/2026: sinertävä pohja sai ruudun näyttämään kylmältä ja
       hieman halvalta, ja sen vaihtaminen oli koko setin halvin laatuloikka.
       Sävyt on johdettu vanhoista säilyttämällä VAALEUS (havaittu kirkkaus
       muuttuu alle yhden yksikön) ja kääntämällä sävy sinisestä lämpimään:
       ennen sininen oli vahvin kanava, nyt punainen. Kontrastisuhteet eivät
       siis muutu eikä yhdenkään tekstin luettavuus heikkene.
       --gray-50 osui tasan palettityön suosittelemaan Porcelain-sävyyn.
       Tenant-neutraali muutos: yhdenkään asiakkaan brändiväriin ei kosketa,
       eikä tilaväreihin (vihreä maksettu, punainen erääntynyt) kosketa. */
    --gray-25: #FDFCF9;
    --gray-50: #F8F7F4;           /* Porcelain, sivun pohja */
    --gray-100: #F2F1EE;
    --gray-200: #E7E6E3;
    --gray-300: #D7D6D3;
    --gray-400: #A3A29F;
    --gray-500: #73726F;
    --gray-600: #555451;
    --gray-700: #41403D;
    --gray-800: #292825;
    --gray-900: #272623;

    /* Sivun pohja — hienovarainen gradient ankeuden poistoksi */
    /* HallaLink-pohja (18.10.2026): viileä vaalea #F3F6F5 */
    --body-bg: linear-gradient(180deg, #F3F6F5 0%, #EDF1F0 100%);

    /* Rakenne */
    --radius-sm: 6px;
    --radius: 10px;                /* hieman isompi kuin 8 */
    --radius-lg: 14px;

    /* Varjot — modernit, hienovaraiset */
    --shadow-xs: 0 1px 2px rgba(16, 24, 40, 0.05);
    --shadow-sm: 0 1px 3px rgba(16, 24, 40, 0.08), 0 1px 2px rgba(16, 24, 40, 0.04);
    --shadow: 0 4px 8px -2px rgba(16, 24, 40, 0.08), 0 2px 4px -2px rgba(16, 24, 40, 0.04);
    --shadow-lg: 0 12px 24px -6px rgba(16, 24, 40, 0.10), 0 4px 8px -3px rgba(16, 24, 40, 0.05);
    --shadow-xl: 0 20px 32px -8px rgba(16, 24, 40, 0.12);

    /* Fokus-rengas */
    --focus-ring: 0 0 0 3px rgba(30, 58, 138, 0.16);

    /* Välistys — 196 eri paddingia templaateissa oli oire siitä ettei
       skaalaa ollut olemassa. Uusi koodi käyttää näitä. */
    --space-1: 4px;
    --space-2: 8px;
    --space-3: 12px;
    --space-4: 16px;
    --space-5: 24px;
    --space-6: 32px;

    /* Tekstikoot. Arvot ovat ne jotka templaateissa oikeasti esiintyvät,
       eivät kaunis teoreettinen skaala: tokenin tehtävä on korvata inline-
       tyyli muuttamatta ulkoasua. Aiemmat arvot (13.5 / 15 / 18 / 22) eivät
       vastanneet mitään käytössä olevaa kokoa, eikä niihin viitattu
       yhdestäkään templaatista. */
    --text-2xs: 10px;
    --text-xs: 11px;
    --text-sm: 12px;
    --text-md: 13px;
    --text-base: 14px;
    --text-lg: 16px;
    --text-xl: 18px;
    --text-2xl: 22px;

    /* Tyypografia */
    --font-sans: 'Inter', 'Inter Vara', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;

    /* ─────────────────────────────────────────────────────────────────
       BRÄNDIVÄRIN ASTEIKKO

       Yksi hex ei riitä. Kun käytössä on vain "pääväri", jokainen
       vaaleampi tai tummempi sävy keksitään uudelleen silmämääräisesti,
       ja lopputulos on 284 eri väriarvoa templaateissa.

       Asteikko johdetaan tenantin omasta väristä (--primary), joten se
       seuraa jokaisen yrityksen brändiä ilman käännösvaihetta.
       50 = vaalein tausta, 900 = tummin teksti.
       ───────────────────────────────────────────────────────────────── */
    --brand-50:  color-mix(in srgb, var(--primary)  6%, white);
    --brand-100: color-mix(in srgb, var(--primary) 12%, white);
    --brand-200: color-mix(in srgb, var(--primary) 25%, white);
    --brand-300: color-mix(in srgb, var(--primary) 40%, white);
    --brand-400: color-mix(in srgb, var(--primary) 65%, white);
    --brand-500: var(--primary);
    --brand-600: color-mix(in srgb, var(--primary) 88%, black);
    --brand-700: color-mix(in srgb, var(--primary) 76%, black);
    --brand-800: color-mix(in srgb, var(--primary) 62%, black);
    --brand-900: color-mix(in srgb, var(--primary) 48%, black);

    /* ─────────────────────────────────────────────────────────────────
       TEHTÄVÄN MUKAAN NIMETYT VÄRIT

       --gray-500 kertoo mikä väri on, ei mihin se kuuluu. Siksi sama
       harmaa päätyy sekä himmeään tekstiin että reunaviivaan, ja
       teemaa vaihdettaessa molemmat pitäisi muuttaa eri suuntiin.

       Uusi koodi käyttää näitä. Silloin tumma teema on pelkkä uusi
       arvojoukko näille tokeneille, ei tuhannen rivin muutos.
       ───────────────────────────────────────────────────────────────── */
    --tausta: var(--gray-50);            /* sivun pohja */
    --pinta: #ffffff;                    /* kortti, taulukko, paneeli */
    --pinta-korostettu: var(--gray-50);  /* hover, zebra, otsikkorivi */
    --pinta-upotettu: var(--gray-100);   /* syvennys, koodilohko */

    --teksti: var(--gray-900);           /* leipäteksti ja otsikot */
    --teksti-toissijainen: var(--gray-600);  /* selitteet */
    --teksti-himmea: var(--gray-500);    /* metatieto, yksiköt */
    --teksti-pinnalla: #ffffff;          /* teksti värillisen päällä */
    --palkki-teksti: #ffffff;            /* teksti yläpalkissa, joka on tumma molemmissa tiloissa */

    --reuna: var(--gray-200);            /* kortin ja kentän reuna */
    --reuna-vahva: var(--gray-300);      /* korostettu reuna, erotin */

    /* Tilavärit pareittain: tausta + sen päällä toimiva teksti. Pari
       pitää kontrastin kunnossa myös silloin kun joku vaihtaa toisen. */
    --tila-ok-tausta: var(--success-light);
    --tila-ok-teksti: #166534;
    --tila-virhe-tausta: var(--danger-light);
    --tila-virhe-teksti: #991b1b;
    --tila-varoitus-tausta: var(--warning-light);
    --tila-varoitus-teksti: #92400e;
    --tila-varoitus-reuna: #f0c27b;
    --tila-tieto-tausta: var(--info-light);
    --tila-tieto-teksti: #075985;
}

/* ── TUMMA TEEMA (dark mode) ──────────────────────────────────────────────
   "Tumma teema on pelkkä uusi arvojoukko näille tokeneille" (yllä). Ei kosketa
   brändivärejä (--primary/--secondary tulevat base.html:sta teemasta ja toimivat
   täyttöinä molemmissa; --primary-ink pitää tekstikontrastin). Vaihdetaan vain
   neutraalit + --gray-* + tilavärien sävyt. data-theme-mode="dark" juuressa. */
:root[data-theme-mode="dark"]:not(.tyotila) {
    /* Selaimen omat säätimet (päivämäärän kalenterikuvake, vierityspalkit, valintaruudut)
       tummina, muuten kuvake piirtyi mustana tummalle pohjalle (Petteri 3.11.2026). */
    color-scheme: dark;
    /* HallaLink-tumma (18.10.2026): pohja #101b25, teksti #F3F6F5 */
    --body-bg: linear-gradient(180deg, #101b25 0%, #0c151d 100%);
    --tausta: #101b25;
    --pinta: #172633;
    --pinta-korostettu: #1d3041;
    --pinta-upotettu: #0c151d;
    --teksti: #F3F6F5;
    --teksti-toissijainen: #b6bdc7;
    --teksti-himmea: #8b93a0;
    --reuna: #2a3d4f;
    --reuna-vahva: #3a4f63;
    /* --gray-* raakakäyttäjille: vaaleus käännetty (50 tummin, 900 vaalein) */
    --gray-25: #13202b;  --gray-50: #172633;  --gray-100: #1d3041;
    --gray-200: #2a3d4f; --gray-300: #3a4f63; --gray-400: #727c89;
    --gray-500: #8b93a0; --gray-600: #aab2bd; --gray-700: #c7cdd6;
    --gray-800: #e2e6ec; --gray-900: #eef1f5;
    /* tilavärit tummaan: läpikuultava tausta + kirkas teksti */
    --tila-ok-tausta: rgba(34,197,94,.16);   --tila-ok-teksti: #4ade80;
    --tila-virhe-tausta: rgba(220,38,38,.18); --tila-virhe-teksti: #f87171;
    --tila-varoitus-tausta: rgba(245,158,11,.16); --tila-varoitus-teksti: #fbbf24;
    --tila-varoitus-reuna: rgba(245,158,11,.42);
    --tila-tieto-tausta: rgba(59,130,246,.16); --tila-tieto-teksti: #7cb0ff;
    --success-light: rgba(34,197,94,.16);
    --danger-light: rgba(220,38,38,.18);
    --warning-light: rgba(245,158,11,.16);
    --info-light: rgba(59,130,246,.16);
    --linkki: #7cb0ff;
    --tila-huomio-tausta: rgba(168,85,247,.2);
    --tila-huomio-teksti: #d8b4fe;
    /* teksti varillisen paalla: --primary on tummassa vaalea, joten valkoinen ei erotu */
    --teksti-pinnalla: var(--primary-ink, #0f1a24);
}
/* Tummassa kentat ilman omaa taustaa (selaimen oletus valkoinen) seuraavat pintaa.
   Sivukohtainen tai inline-tyyli voittaa edelleen. */
:root[data-theme-mode="dark"]:not(.tyotila) :is(input:not([type="checkbox"]):not([type="radio"]):not([type="range"]):not([type="submit"]):not([type="button"]):not([type="file"]), select, textarea) {
    background-color: var(--pinta-upotettu);
    color: var(--teksti);
    border-color: var(--reuna);
}

* { box-sizing: border-box; margin: 0; padding: 0; }
html { -webkit-text-size-adjust: 100%; }

/* Mobiili-overflow-turvaverkko (KaPe työmaalla ~360px). Pitkät katkeamattomat
   merkkijonot (sähköpostit, IBANit, viitenumerot, koodit, tiedostonimet) +
   taulukkosolut katkeavat ruudun rajoissa eivätkä työnnä sivua vaakaan. Median
   kuvat/canvasit skaalautuvat alas. Leveät monisarakkeiset taulut kannattaa
   silti kääriä .table-responsive:iin (overflow-x:auto). */
img, canvas, video { max-width: 100%; }
td, th { overflow-wrap: anywhere; }
body {
    /* iPhonen kotivalikkosovellus: vieritysvara, ks. base.html */
    padding-bottom: var(--ios-vieritysvara, 0px);
    font-family: 'Inter', 'Inter Vara', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
    background: var(--body-bg);
    background-attachment: fixed;
    color: var(--teksti);
    font-size: 14px;
    line-height: 1.5;
    -webkit-font-smoothing: antialiased;
    font-feature-settings: 'cv11', 'ss01';
}

/* Otsikot — yhtenäistetty koot + tiukempi line-height */
h1, h2, h3, h4, h5, h6 {
    line-height: 1.3;
    color: var(--teksti);
    font-weight: 600;
    letter-spacing: -0.01em;
}
h1 { font-size: 24px; }
h2 { font-size: 20px; }
h3 { font-size: 16px; }
h4 { font-size: 14px; }
h5 { font-size: 13px; }
h6 { font-size: 12px; text-transform: uppercase; letter-spacing: 0.05em; color: var(--gray-500); }

/* Linkit — sinivärit + focus-ring saavutettavuussyistä
   HUOM: Navigaatio-, nappi- ja kortti-linkit pitävät omat värinsä
   (specificity: .nav-link, .btn, .mobile-card, .nav-brand a, .nav-add-menu a
   yms. voittavat nämä säännöt). Underline poissa em. komponenteista. */
/* Navbar on kiinnitetty ja 56 px korkea, joten sivun sisainen ankkurilinkki
   vei kohteen otsikon sen alle piiloon. Yksi rivi korjaa kaikki ankkurit. */
html { scroll-padding-top: 64px; }
/* Sivu ei saa liikkua sivuttain (Villen löydös 3.11.2026: iPhonella Työkohteet- ja
   kiinteistösivun sai vedettyä vinoon, samoin aiemmin asetusten välilehtirivin).
   clip leikkaa vaakaylivuodon luomatta vierityssäiliötä, joten position: sticky
   toimii yhä. Vaakavierittävät rivit ja taulukot vierivät omassa säiliössään. */
@supports (overflow: clip) {
    html:not(.tyotila), html:not(.tyotila) body { overflow-x: clip; }
}

a { color: var(--linkki, #2563eb); text-decoration: none; }
/* Vain hiirella. Kosketusnaytolla hover jaa paalle klikkauksen jalkeen, ja
   koko KPI-tiili jai alleviivatuksi kunnes johonkin muuhun kosketti. */
@media (hover: hover) { a:hover { text-decoration: underline; } }
/* Estä underline komponenteissa, joissa se aiheuttaa häiriötä */
.nav-link:hover,
.nav-brand a:hover,
.btn:hover,
.btn-outline:hover,
.mobile-card:hover,
.nav-add-menu a:hover,
.bottom-bar-item:hover,
.stat-card:hover,
.navbar .nav-user-info:hover,
.card a.card-title:hover { text-decoration: none; }

a:focus-visible {
    outline: 2px solid rgba(37, 99, 235, 0.4);
    outline-offset: 2px;
    border-radius: 2px;
}

/* Code-tagit — monospace + pehmeä tausta */
code, kbd, samp {
    font-family: 'JetBrains Mono', 'Fira Code', 'Consolas', 'Courier New', monospace;
    /* Teeman pinta: kiinteä #F5F4F1 jäi tummassa tilassa vaaleaksi laatikoksi (3.11.2026). */
    background: var(--pinta-korostettu);
    padding: 1px 5px;
    border-radius: 3px;
    font-size: 90%;
    color: var(--gray-800);
}
pre {
    font-family: 'JetBrains Mono', 'Fira Code', 'Consolas', 'Courier New', monospace;
    background: #F5F4F1;
    padding: 12px 14px;
    border-radius: 6px;
    font-size: 13px;
    overflow-x: auto;
    line-height: 1.5;
}
pre code { background: transparent; padding: 0; font-size: inherit; }
/* Napin sisällä lihavointi seuraa napin tekstiväriä (tumma tila: strong jäi näkymättömäksi primary-napissa). */
.btn strong, .btn b { color: inherit; }

/* Navbar — moderni, valkoinen ja hienovarainen */
/* Yläpalkin korkeus + iPhonen tilarivin turva-alue. viewport-fit=cover ja
   black-translucent (base.html) vievät sivun tilarivin alle, joten palkki
   kasvaa turva-alueen verran eikä yläreunaan jää valkoista kaistaa
   (laatukatsaus I1, 2.11.2026). Muualla turva-alue on 0. */
:root { --ylapalkki: calc(56px + env(safe-area-inset-top, 0px)); }
.navbar {
    background: linear-gradient(135deg, var(--primary) 0%, var(--primary) 50%, var(--primary-hover) 100%);
    background-color: var(--primary);
    color: #ffffff;
    padding: env(safe-area-inset-top, 0px) max(20px, env(safe-area-inset-right, 0px)) 0 max(20px, env(safe-area-inset-left, 0px));
    height: var(--ylapalkki);
    display: flex;
    align-items: center;
    justify-content: space-between;
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    z-index: 1000;
}
.navbar .nav-brand a { color: #ffffff; }
.navbar .menu-toggle { color: rgba(255,255,255,0.9); }
.navbar .menu-toggle:hover { background: rgba(255,255,255,0.15); }
.navbar .nav-user-info { color: #ffffff; }
.navbar .nav-user-info:hover { background: rgba(255,255,255,0.15); }
.navbar .nav-user-role { color: rgba(255,255,255,0.75); }
.navbar .nav-logout-btn { background: rgba(255,255,255,0.15); color: #ffffff; }
.navbar .nav-logout-btn:hover { background: rgba(255,255,255,0.25); color: #ffffff; }
.navbar .nav-search-input {
    background: rgba(255,255,255,0.15);
    border: 1px solid rgba(255,255,255,0.25);
    color: #ffffff;
}
.navbar .nav-search-input::placeholder { color: rgba(255,255,255,0.65); }
.navbar .nav-search-input:focus {
    background: #fff;
    border-color: var(--primary);
    box-shadow: var(--focus-ring);
}
.navbar .nav-add-btn {
    background: var(--primary);
    border: 1px solid var(--primary);
    color: var(--primary-ink, #fff);
}
.navbar .nav-add-btn:hover { background: var(--primary-hover); border-color: var(--primary-hover); }
.navbar a[title="Ilmoitukset"] { color: var(--gray-600) !important; }
.navbar a[title="Ilmoitukset"]:hover { color: var(--gray-900) !important; }
.nav-brand { display: flex; align-items: center; gap: 12px; }
.nav-brand a { color: white; text-decoration: none; font-weight: 700; font-size: 18px; display: flex; align-items: center; gap: 10px; }
.nav-logo-img {
    width: 28px;
    height: 28px;
    border-radius: 6px;
    flex-shrink: 0;
}
.nav-logo-text { font-weight: 700; font-size: 18px; letter-spacing: -0.3px; }
/* HallaLink-logo 32 px korkeana, kapealla ruudulla pelkkä merkki. */
.nav-logo-hallalink { display: block; height: 32px; width: auto; }
.nav-logo-merkki { display: none; height: 32px; width: 32px; }
.menu-toggle {
    background: none;
    border: none;
    color: white;
    font-size: 22px;
    cursor: pointer;
    padding: 6px 8px;
    border-radius: 6px;
    transition: background 0.15s;
}
.menu-toggle:hover { background: rgba(255,255,255,0.15); }
.nav-user { display: flex; align-items: center; gap: 10px; }
.nav-user-info {
    display: none;
    flex-direction: column;
    align-items: flex-end;
    text-decoration: none;
    color: white;
    line-height: 1.2;
    padding: 4px 8px;
    border-radius: 6px;
    transition: background 0.15s;
}
.nav-user-info:hover { background: rgba(255,255,255,0.12); }
.nav-user-name { font-size: 14px; font-weight: 600; }
.nav-user-role { font-size: 11px; opacity: 0.75; font-weight: 400; }
.nav-logout-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    background: rgba(255,255,255,0.12);
    border: none;
    color: white;
    width: 36px;
    height: 36px;
    border-radius: 8px;
    cursor: pointer;
    transition: background 0.15s;
}
.nav-logout-btn:hover { background: rgba(255,255,255,0.25); }
@media (min-width: 768px) {
    .nav-user-info { display: flex; }
}

/* Sidebar — vaalea, tiivis, moderni */
.sidebar {
    position: fixed;
    top: var(--ylapalkki);
    left: -260px;
    width: 256px;
    height: calc(100vh - var(--ylapalkki));
    background: var(--gray-25);
    border-right: 1px solid var(--gray-200);
    transition: left 0.25s ease;
    z-index: 999;
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
    overscroll-behavior: contain;
    padding: 12px 8px 16px;
}
.sidebar.open { left: 0; box-shadow: var(--shadow-lg); }
.nav-link {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 8px 12px;
    margin: 1px 4px;
    color: var(--gray-700);
    text-decoration: none;
    font-size: 13.5px;
    font-weight: 500;
    border-radius: var(--radius-sm);
    transition: all 0.12s;
    border-left: none;
}
.nav-link:hover {
    background: var(--gray-100);
    color: var(--gray-900);
}
.nav-link.active {
    background: var(--primary-light);
    color: var(--primary);
    font-weight: 600;
    border-left: 3px solid var(--primary);
    padding-left: 9px;
}
/* Sivupalkin kuvake on SVG (ks. app/static/icons.svg). Emoji vaati
   himmennyksen ja saturaation laskun, koska se toi omat varinsa; SVG ottaa
   rivin varin, joten aktiivinen kohta varjaa kuvakkeen itsestaan. */
.nav-link .ikoni {
    width: 18px;
    height: 18px;
    opacity: 0.75;
    flex: 0 0 auto;
}
.nav-link:hover .ikoni { opacity: 1; }
.nav-link.active .ikoni { opacity: 1; }
/* Alias jos joku käyttää .nav-item-luokkaa */
.nav-item:hover { background: #F5F4F1; }
.nav-section {
    padding: 14px 16px 6px;
    font-size: 10px;
    font-weight: 700;
    text-transform: uppercase;
    color: var(--gray-500);
    letter-spacing: 0.06em;
}

@media (min-width: 1024px) {
    .sidebar { left: 0; }
    .content { margin-left: 256px; }
    .menu-toggle { display: none; }
}

/* Content */
.content, .content-full {
    margin-top: var(--ylapalkki);
    padding: 24px;
    min-height: calc(100vh - var(--ylapalkki));
    /* Max-width estää sisällön venymisen koko ultraleveän näytön levyiseksi.
       Ilman tätä vasen-oikea-rivit (nimi vasemmalla, arvo oikealla reunalla)
       saavat valtavan tyhjän välin keskelle. */
    max-width: 1400px;
}
/* Keskelle: ilman auto-marginaaleja laatikko jäi yli 1400 px näytöllä
   vasempaan reunaan ja 2FA-kortti sen mukana (2.11.2026). */
.content-full { margin-left: auto; margin-right: auto; max-width: 1400px; }
@media (max-width: 768px) {
    .content, .content-full { padding: 16px; }
}

/* Kaksisarakkeinen korttiryhmä — kapeat tieto-osiot vierekkäin leveällä
   näytöllä (vähemmän tyhjää, vähemmän pystyskrollia). 1 sarake mobiilissa. */
.detail-2col {
    display: grid;
    /* auto-fit: pari vierekkäin, mutta YKSINÄINEN kortti (kun parin toinen
       puuttuu ehdolla) venyy täysleveäksi — ei tyhjää puolikasta. */
    grid-template-columns: repeat(auto-fit, minmax(340px, 1fr));
    gap: 12px;
    align-items: start;
}
.detail-2col > .card { margin-bottom: 0; }
@media (max-width: 900px) {
    /* minmax(0, 1fr): pelkkä 1fr ei kutistu alle sisältönsä, ja työmaan
       kortit levisivät 650 px:iin 390 px:n ruudulla (27.9.2026). */
    .detail-2col { grid-template-columns: minmax(0, 1fr); }
}

/* Listojen 2-sarakkeinen ruudukko (kapeat listakortit vierekkäin). */
.list-2col {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 8px;
}
.list-2col > * { margin-bottom: 0 !important; }
@media (max-width: 1100px) {
    .list-2col { grid-template-columns: 1fr; }
}

/* Korttiruudukko valikko-/yhteenvetokorteille (auto-fit). */
.card-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
    gap: 12px;
}
.card-grid > * { margin-bottom: 0 !important; }

/* Cards — modernisti tiivistetty (spec: padding 16px, radius 8px) */
.card {
    /* Teeman muuttujat (30.9.2026): vaaleassa samat arvot kuin ennen
       (#ffffff, #E7E6E3), tummassa kortti ei jää valkoiseksi. */
    background: var(--pinta);
    border-radius: 10px;
    border: 1px solid var(--gray-200);
    box-shadow: 0 1px 3px rgba(79, 70, 229, 0.05), 0 1px 2px rgba(16, 24, 40, 0.04);
    padding: 16px;
    margin-bottom: 16px;
}
.card-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin: 0 0 12px;
    flex-wrap: wrap;
    gap: 8px;
}
.card-title {
    font-size: 15px;
    font-weight: 600;
    color: var(--teksti);
    margin: 0 0 8px;
    letter-spacing: -0.01em;
}
.card-header .card-title { margin: 0; }

/* Buttons — moderni, sivistynyt */
.btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 6px;
    padding: 8px 14px;
    min-height: 36px;               /* touch-target säilytys */
    border: 1px solid transparent;
    border-radius: var(--radius-sm);
    font-family: var(--font-sans);
    font-size: 13.5px;
    font-weight: 500;               /* kevennetty 600 → 500, sivistyneempi */
    letter-spacing: -0.005em;
    cursor: pointer;
    text-decoration: none;
    transition: background-color 0.15s ease, border-color 0.15s ease, color 0.15s ease, box-shadow 0.15s ease, transform 0.05s ease;
    white-space: nowrap;
    box-shadow: var(--shadow-xs);
}
.btn:active { transform: translateY(1px); }
.btn-primary { background: linear-gradient(135deg, var(--primary), var(--primary)); color: var(--primary-ink, #fff); border-color: var(--primary); box-shadow: 0 1px 3px rgba(0, 0, 0, 0.15); }
.btn-primary:hover { background: linear-gradient(135deg, var(--primary), var(--primary-hover)); border-color: var(--primary-hover); }
.btn-success { background: linear-gradient(135deg, #22c55e, #16a34a); color: #fff; border-color: #16a34a; box-shadow: 0 1px 3px rgba(22, 163, 74, 0.25); }
.btn-success:hover { background: linear-gradient(135deg, #16a34a, #15803d); border-color: #15803d; }
.btn-danger { background: linear-gradient(135deg, #ef4444, #dc2626); color: #fff; border-color: #dc2626; box-shadow: 0 1px 3px rgba(220, 38, 38, 0.25); }
.btn-danger:hover { background: linear-gradient(135deg, #dc2626, #991b1b); border-color: #991b1b; }
.btn-warning { background: var(--warning); color: #1f2937; border-color: var(--warning); }
.btn-warning:hover { background: #d97706; border-color: #d97706; }
.btn-outline {
    background: transparent;
    border: 1px solid #D6D5D2;
    color: var(--gray-700);
    box-shadow: none;               /* outline pysyy kevyenä */
}
.btn-outline:hover { background: var(--pinta-korostettu); border-color: var(--gray-400); color: var(--gray-900); }
.btn-sm { padding: 5px 10px; font-size: 12px; min-height: 28px; }
.btn-lg { padding: 12px 22px; font-size: 15px; width: 100%; }
.btn-block { width: 100%; }
/* Fokus-rengas saavutettavuussyistä — näkyy sekä hiirelle että näppäimistölle */
.btn:focus { outline: 2px solid rgba(37, 99, 235, 0.4); outline-offset: 2px; }
.btn:focus:not(:focus-visible) { outline: none; }
.btn:focus-visible { outline: 2px solid rgba(37, 99, 235, 0.4); outline-offset: 2px; box-shadow: var(--shadow-xs); }

/* Forms — hienovaraiset labelit, sininen fokus (spec 2563eb) */
.form-group { margin-bottom: 14px; }
.form-label {
    display: block;
    font-weight: 500;
    margin-bottom: 4px;
    font-size: 12px;
    color: var(--gray-700);  /* teemamuuttuja: tummassa tilassa vaalea (1.10.2026) */
    letter-spacing: -0.005em;
}
.form-control {
    width: 100%;
    padding: 8px 10px;
    /* Teeman muuttujat (1.10.2026): kovakoodattu #fff jäi tummassa tilassa
       valkoiseksi kentäksi. */
    border: 1px solid var(--reuna-vahva);
    border-radius: 5px;
    font-family: var(--font-sans);
    font-size: 14px;
    background: var(--pinta);
    color: var(--teksti);
    transition: border-color 0.15s ease, box-shadow 0.15s ease;
    -webkit-appearance: none;
}
.form-control:hover { border-color: var(--gray-400); }
.form-control:focus {
    outline: none;
    border-color: var(--primary);
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--primary) 18%, transparent);
}
.form-control::placeholder { color: var(--gray-400); }
select.form-control { height: 36px; cursor: pointer; }
textarea.form-control { min-height: 72px; resize: vertical; line-height: 1.55; }

.form-row {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 12px;
}
@media (max-width: 480px) {
    .form-row { grid-template-columns: 1fr; }
}

/* Kuvakkeet: SVG spritesta, ei emojia.
   currentColor tarkoittaa etta kuvake ottaa tekstin varin, joten aktiivinen
   valikkorivi varjaa myos kuvakkeen eika kuvake tuo omia vareja mukanaan.
   vertical-align pitaa kuvakkeen tekstin perusviivalla. */
.ikoni {
    display: inline-block;
    vertical-align: -0.18em;
    flex: 0 0 auto;
    color: currentColor;
    fill: currentColor;
}

/* Reitti-kehys (tyomaa-ajo, materiaalin nouto).
   Tyopoydalla kehys ryhmittaa reitin kentat. Puhelimessa kehyksen reunus ja
   sisennys siirsivat sisalla olevat kentat eri linjaan kuin lomakkeen muut
   kentat, jolloin syottolaatikot nayttivat porrastuvan satunnaisesti.
   Kapealla naytolla kehys puretaan: jaljelle jaa otsikko, ja kaikki kentat
   jakavat saman vasemman reunan. */
.route-fieldset {
    border: 1px solid var(--gray-200, #E8E7E4);
    border-radius: 6px;
    padding: 12px;
    margin-bottom: 14px;
}
.route-fieldset > legend {
    font-size: 11px;
    text-transform: uppercase;
    letter-spacing: 0.5px;
    color: var(--gray-500);
    font-weight: 700;
    padding: 0 6px;
}
@media (max-width: 640px) {
    .route-fieldset { border: 0; padding: 0; }
    .route-fieldset > legend { padding: 0; margin-bottom: 6px; }
}

/* Tables — modernit, puhtaat */
.table-responsive { overflow-x: auto; margin: 0 -20px; padding: 0 20px; }
table { width: 100%; border-collapse: separate; border-spacing: 0; }
th, td {
    padding: 10px 14px;
    text-align: left;
    border-bottom: 1px solid var(--gray-100);
    font-size: 13.5px;
    color: var(--gray-800);
}
th {
    font-weight: 600;
    color: var(--gray-500);
    font-size: 11px;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    background: var(--gray-50);
    border-bottom: 1px solid var(--gray-200);
}
tbody tr:hover { background: var(--gray-50); }

/* .table — eksplisiittinen variantti (spec: border-collapse, hover, 14px) */
.table {
    width: 100%;
    border-collapse: collapse;
    border-spacing: 0;
    font-size: 14px;
}
.table th {
    padding: 10px 12px;
    text-align: left;
    font-weight: 600;
    color: var(--gray-700);
    background: var(--pinta-korostettu);
    border-bottom: 2px solid var(--reuna);
    text-transform: none;               /* ei caps .table-luokassa */
    letter-spacing: normal;
    font-size: 14px;
}
.table td {
    padding: 10px 12px;
    border-bottom: 1px solid var(--gray-100);
    color: var(--teksti);
    font-size: 14px;
}
.table tr:hover td { background: var(--pinta-korostettu); }

/* .table-sm — tiiviimpi variantti */
.table-sm th,
.table-sm td { padding: 6px 8px; }

/* .table-striped — zebra-raidat */
.table-striped tbody tr:nth-child(even) td { background: #FCFBF8; }
.table-striped tbody tr:hover td { background: #FBFAF7; }      /* hover voittaa zebran */

/* Mobile cards for table data */
/* HR-dokumentit: henkilörivi ja uusimmat asiakirjat (Petteri 5.11.2026) */
.hr-henkilo { display: flex; justify-content: space-between; align-items: center; gap: 8px; flex-wrap: wrap; min-height: 44px; padding: 6px 0; border-bottom: 1px solid var(--reuna); color: inherit; text-decoration: none; }
.hr-henkilo:last-child { border-bottom: 0; }

.mobile-card {
    display: none;
    background: var(--pinta);
    border-radius: var(--radius);
    border: 1px solid var(--gray-200);
    padding: 14px 16px;
    margin-bottom: 8px;
    box-shadow: var(--shadow-xs);
    text-decoration: none;
    color: inherit;
    transition: all 0.12s;
}
.mobile-card:hover { box-shadow: var(--shadow); border-color: var(--gray-300); }
.mobile-card-title { font-weight: 600; font-size: 14.5px; color: var(--gray-900); margin-bottom: 3px; letter-spacing: -0.005em; }
.mobile-card-sub { font-size: 12.5px; color: var(--gray-500); }
.mobile-card-row { display: flex; justify-content: space-between; gap: 12px; margin-top: 8px; font-size: 13.5px; }
/* Kirjausrivi: kuvaus rivittyy omalla alueellaan, oikean reunan luku ei
   kutistu eikä liimaudu kuvaukseen kiinni (ChatGPT 15.9.2026, työmaakortin
   Viimeisimmät kirjaukset). */
.mobile-card-row.kirjausrivi > span:first-child { flex: 1 1 auto; min-width: 0; overflow-wrap: anywhere; }
.mobile-card-row.kirjausrivi > span:last-child { flex: 0 0 auto; white-space: nowrap; }
@media (max-width: 768px) {
    .desktop-table { display: none; }
    .mobile-card { display: block; }
}

/* Alerts */
.alert {
    padding: 12px 16px;
    border-radius: var(--radius);
    margin-bottom: 16px;
    font-size: 14px;
}
.alert-success { background: var(--tila-ok-tausta, #d1fae5); color: var(--tila-ok-teksti, #065f46); }
.alert-error, .alert-danger { background: var(--tila-virhe-tausta, #fee2e2); color: var(--tila-virhe-teksti, #991b1b); }
.alert-warning { background: var(--tila-varoitus-tausta, #fef3c7); color: var(--tila-varoitus-teksti, #92400e); }
.alert-info { background: var(--tila-tieto-tausta, #dbeafe); color: var(--tila-tieto-teksti, #1e40af); }

/* Badges — pehmeät pilleri, selkeät värit (spec 2/N: 12px, 4px radius, fw 500) */
.badge {
    display: inline-flex;
    align-items: center;
    padding: 2px 8px;
    border-radius: 4px;
    font-size: 12px;
    font-weight: 500;
    letter-spacing: -0.005em;
    border: 1px solid transparent;
    line-height: 1.5;
    white-space: nowrap;
}
.badge-primary { background: linear-gradient(135deg, var(--primary), var(--primary-hover)); color: #ffffff; border: none; }
.badge-success { background: linear-gradient(135deg, #22c55e, #16a34a); color: #ffffff; border: none; }
.badge-warning { background: linear-gradient(135deg, #fbbf24, #f59e0b); color: #78350f; border: none; }
.badge-danger  { background: linear-gradient(135deg, #ef4444, #dc2626); color: #ffffff; border: none; }
.badge-neutral { background: var(--pinta-upotettu); color: var(--teksti-toissijainen); border: 1px solid var(--reuna-vahva); }
/* Taaksepäin yhteensopivat aliakset — älä riko olemassa olevia HTML:iä */
.badge-info { background: linear-gradient(135deg, #38bdf8, #0ea5e9); color: #ffffff; border: none; }
.badge-gray { background: var(--pinta-upotettu); color: var(--gray-700); border: 1px solid var(--gray-200); }

/* Stats grid */
.stats-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
    gap: 14px;
    margin-bottom: 20px;
}
.stat-card {
    background: var(--pinta);
    border-radius: var(--radius);
    border: 1px solid var(--gray-200);
    padding: 18px 20px;
    box-shadow: var(--shadow-xs);
    text-align: left;
    transition: all 0.15s;
    border-top: 3px solid var(--gray-200);
}
.stat-card:hover { box-shadow: var(--shadow); border-color: var(--gray-300); transform: translateY(-1px); }
.stat-card.stat-success { border-top-color: var(--success); }
.stat-card.stat-warning { border-top-color: var(--warning); }
.stat-card.stat-danger { border-top-color: var(--danger); }
.stat-card.stat-info { border-top-color: var(--info); }
.stat-value { font-size: 26px; font-weight: 700; color: var(--gray-900); letter-spacing: -0.02em; line-height: 1.1; }
.stat-label { font-size: 12px; color: var(--gray-500); margin-top: 4px; font-weight: 500; }

/* Image grid */
.image-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
    gap: 8px;
}
.image-grid img {
    width: 100%;
    height: 120px;
    object-fit: cover;
    border-radius: var(--radius);
    cursor: pointer;
}

/* Login */
.login-container {
    max-width: 420px;
    margin: 40px auto;
    padding: 0 16px;
}
.login-logo {
    text-align: center;
    margin-bottom: 32px;
}
.login-logo img { display: block; margin: 0 auto; }

/* Setup wizard */
.setup-step {
    text-align: center;
    margin-bottom: 24px;
}
.setup-steps {
    display: flex;
    justify-content: center;
    gap: 8px;
    margin-bottom: 24px;
}
.step-dot {
    width: 12px;
    height: 12px;
    border-radius: 50%;
    background: var(--gray-300);
}
.step-dot.active { background: var(--primary); }
.step-dot.done { background: var(--success); }

/* PWA Install banner */
.install-banner {
    display: none;
    background: var(--primary);
    color: white;
    padding: 16px;
    text-align: center;
    position: fixed;
    bottom: 0;
    left: 0;
    right: 0;
    z-index: 1001;
    box-shadow: 0 -2px 8px rgba(0,0,0,0.2);
}
.install-banner.show { display: block; }
.install-banner .btn { margin-top: 8px; background: white; color: var(--primary); }

/* Utilities */
.text-right { text-align: right; }
.text-center { text-align: center; }
/* .table th asettaa text-align: left ja voittaa paljaan .text-right -luokan
   tarkkuudellaan, jolloin numerosarakkeen OTSIKKO jäi vasemmalle vaikka luvut
   olivat oikealla: otsikko ei osoittanut omaa saraketta (käyttäjä 2026-08-17).
   Tämä koskee kaikkia .table-taulukoita, joten korjaus on täällä. */
.table th.text-right, .table td.text-right { text-align: right; }
.table th.text-center, .table td.text-center { text-align: center; }
.text-muted { color: var(--gray-500); }
.text-success { color: var(--success); }
.text-danger { color: var(--danger); }
.mt-1 { margin-top: 8px; }
.mt-2 { margin-top: 16px; }
.mb-1 { margin-bottom: 8px; }
.mb-2 { margin-bottom: 16px; }
.d-flex { display: flex; }
.gap-1 { gap: 8px; }
.gap-2 { gap: 16px; }
.flex-wrap { flex-wrap: wrap; }
.justify-between { justify-content: space-between; }
.align-center { align-items: center; }
.hidden { display: none; }

/* Page header — spec: flex baseline, border-bottom, margin 20px */
.page-header {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    padding: 0 0 16px 0;
    border-bottom: 2px solid var(--primary-border);
    margin-bottom: 20px;
    flex-wrap: wrap;
    gap: 12px;
}
.page-title {
    font-size: 22px;
    font-weight: 600;
    color: var(--teksti);
    margin: 0;
    letter-spacing: -0.02em;
    line-height: 1.2;
}
.page-subtitle {
    font-size: 13px;
    color: var(--teksti-himmea);
    margin-top: 4px;
}

/* Empty state — pehmeä tausta, keskitetty */
.empty-state {
    text-align: center;
    padding: 40px 20px;
    color: var(--teksti-himmea);
    background: var(--pinta-korostettu);
    border-radius: 8px;
}
.empty-state-icon { font-size: 36px; margin-bottom: 10px; opacity: 0.6; }
/* Alias lyhyemmälle .empty-nimelle */
.empty {
    padding: 40px 20px;
    text-align: center;
    color: var(--teksti-himmea);
    background: var(--pinta-korostettu);
    border-radius: 8px;
}

/* Row delete button */
.row-delete {
    width: 28px;
    height: 28px;
    min-width: 28px;
    padding: 0;
    font-size: 14px;
    line-height: 28px;
    border-radius: 50%;
    background: var(--gray-200);
    color: var(--gray-500);
    border: none;
    cursor: pointer;
    flex-shrink: 0;
    text-align: center;
}
.row-delete:hover { background: var(--danger); color: white; }

/* Nav badges */
.nav-badge {
    /* Numero keskelle palloa (Petteri 3.11.2026): flex + kiinteä korkeus
       eikä tekstirivin korkeuden varassa. */
    display: inline-flex;
    align-items: center;
    justify-content: center;
    box-sizing: border-box;
    min-width: 18px;
    height: 18px;
    line-height: 1;
    background: var(--danger);
    color: white;
    font-size: 11px;
    font-weight: 700;
    /* 5/4 px: numero puoli pikseliä oikealle. Petterin näytöllä (Windows,
       ClearType) ykkösen jalka piirtyi 3 px leveänä ja jäi 0,5 px vasemmalle
       parillisessa 18 px pallossa (mitattu kuvakaappauksesta 6.11.2026). */
    padding: 0 4px 0 5px;
    border-radius: 9px;
    margin-left: 4px;
    vertical-align: middle;
}

/* Kellon ilmoituspallo yläpalkissa. Sama keskitys kuin .nav-badge: aiemmin
   inline-tyyli line-height 16px, jolloin numero istui alhaalla vasemmalla
   selaimen skaalauksesta riippuen (Petteri 5.11.2026). */
.notif-badge {
    position: absolute;
    top: 0;
    right: 0;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    box-sizing: border-box;
    min-width: 16px;
    height: 16px;
    padding: 0 3px 0 4px;  /* ks. .nav-badge: puolen pikselin optinen korjaus */
    border-radius: 8px;
    background: var(--danger);
    color: #fff;
    font-size: 10px;
    font-weight: 700;
    line-height: 1;
}
.notif-badge[hidden] { display: none; }

/* Nav search */
.nav-search { display: none; }
.nav-search-input {
    padding: 6px 12px;
    border: 1px solid rgba(255,255,255,0.2);
    border-radius: 8px;
    background: rgba(255,255,255,0.1);
    color: white;
    font-size: 14px;
    width: 180px;
    transition: all 0.2s;
}
.nav-search-input::placeholder { color: rgba(255,255,255,0.5); }
.nav-search-input:focus { outline: none; background: rgba(255,255,255,0.2); border-color: rgba(255,255,255,0.4); width: 220px; }
@media (min-width: 600px) { .nav-search { display: block; } }

/* Globaali "+ Uusi" -nappi navbarissa */
.nav-add-btn {
    display: flex;
    align-items: center;
    gap: 4px;
    padding: 6px 12px;
    background: rgba(255,255,255,0.15);
    border: 1px solid rgba(255,255,255,0.3);
    border-radius: 8px;
    color: #fff;
    font-size: 14px;
    font-weight: 600;
    cursor: pointer;
    transition: all 0.15s;
}
.nav-add-btn:hover { background: rgba(255,255,255,0.25); border-color: rgba(255,255,255,0.5); }
.nav-add-plus { font-size: 18px; line-height: 1; }
.nav-add-label { }
/* Mobiilissa (<=768px, missa alapalkki nakyy) ylapalkin "+ Uusi" on
   paallekkainen alapalkin FABin kanssa -> piilotetaan VAIN nappi. Valikko
   (#nav-add-menu) jaa DOM:iin, koska alapalkin FAB (avaaLisaysvalikko) kayttaa
   samaa elementtia. Tyopoydalla nappi jaa (ei alapalkkia). */
@media (max-width: 768px) { .navbar .nav-add-btn { display: none !important; } }
/* Koodit-linkki ylapalkissa vain tyopoydalla: puhelimessa sama on alapalkissa (27.10.2026). */
@media (max-width: 768px) { #koodit-ylapalkki { display: none !important; } }
.nav-add-menu {
    position: absolute;
    top: calc(100% + 4px);
    right: 0;
    background: var(--pinta);
    border: 1px solid var(--reuna);
    border-radius: 8px;
    box-shadow: 0 8px 24px rgba(0,0,0,0.15);
    min-width: 220px;
    max-height: 80vh;
    overflow-y: auto;
    z-index: 2000;
    padding: 6px 0;
}
.nav-add-menu a {
    display: block;
    padding: 10px 16px;
    font-size: 14px;
    color: var(--teksti);
    text-decoration: none;
    white-space: nowrap;
}
.nav-add-menu a:hover { background: var(--gray-50); color: var(--primary); }
.nav-add-menu.hidden { display: none; }

/* Sama valikko pohjalevyna kun se avataan alapalkin +-napista: puhelimessa
   ylareunaan ankkuroitu pudotusvalikko on peukalon ulottumattomissa. */
.nav-add-menu.nav-add-sheet {
    position: fixed;
    top: auto;
    left: 0;
    right: 0;
    bottom: 0;
    min-width: 0;
    max-height: 70vh;
    border-radius: 14px 14px 0 0;
    border: none;
    box-shadow: 0 -8px 28px rgba(0,0,0,0.25);
    /* Alareunaan alapalkin (66 px) verran tilaa, muuten viimeinen rivi jaa
       sen alle piiloon. */
    padding: 10px 0 calc(76px + env(safe-area-inset-bottom, 8px));
    z-index: 1050;
}
.nav-add-menu.nav-add-sheet a {
    padding: 14px 20px;
    font-size: 16px;
}
.nav-add-menu.nav-add-sheet::before {
    content: '';
    display: block;
    width: 40px;
    height: 4px;
    border-radius: 2px;
    background: var(--gray-300);
    margin: 0 auto 8px;
}

/* Alapalkin keskinappi. Nostettu palkin ylapuolelle jotta se erottuu
   navigaatiokohteista: se ei ole sivu vaan toiminto. */
.bottom-bar-fab {
    flex: 0 0 auto;
    justify-self: center;
    width: 52px;
    height: 52px;
    margin: -18px 0 0;
    border: none;
    border-radius: 50%;
    background: var(--primary);
    /* Teeman tekstiväri: tummassa tilassa korostus on vaalea, jolloin
       valkoinen plus katosi (19.10.2026). */
    color: var(--primary-ink, #fff);
    font-size: 30px;
    line-height: 1;
    font-weight: 300;
    cursor: pointer;
    box-shadow: 0 4px 12px rgba(0,0,0,0.25);
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 0;
}
.bottom-bar-fab:active { transform: scale(0.94); }

/* Lightbox */
.lightbox {
    display: none;
    position: fixed;
    top: 0; left: 0; right: 0; bottom: 0;
    background: rgba(0,0,0,0.9);
    z-index: 2000;
    align-items: center;
    justify-content: center;
    cursor: pointer;
}
.lightbox.active { display: flex; }
.lightbox img {
    max-width: 95vw;
    max-height: 90vh;
    object-fit: contain;
    border-radius: 4px;
}
.lightbox-close {
    position: absolute;
    top: 12px; right: 16px;
    background: none;
    border: none;
    color: white;
    font-size: 36px;
    cursor: pointer;
    line-height: 1;
    padding: 4px 12px;
}

/* Bottom bar — mobile navigation */
/* Alapalkki: nelja linkkia + lisaysnappi keskella.
   Viisi tasalevyista saraketta, joten nappi on TASAN keskella ja
   kuvakkeiden valit ovat yhta suuret. Kuitit siirtyi pois palkista
   (lisaysvalikko ja sivupalkki), jotta keskitys onnistuu: parillinen maara
   linkkeja on ainoa tapa saada nappi keskelle. */
.bottom-bar {
    display: none;
    grid-template-columns: repeat(5, 1fr);
    align-items: start;
    position: fixed;
    /* --ios-palkki: iPhonen kotivalikkosovelluksen korjaus, ks. base.html */
    bottom: var(--ios-palkki, 0px);
    left: 0;
    right: 0;
    background: var(--pinta);
    border-top: 1px solid var(--gray-200);
    z-index: 1000;
    padding: 4px 0 env(safe-area-inset-bottom, 4px);
    box-shadow: 0 -2px 8px rgba(0,0,0,0.08);
    /* iOS Safari: fixed-palkki "seilaa" momentum-scrollissa ellei sita
       promotoida omaksi kompositointitasokseen. translateZ(0) pitaa sen
       paikallaan ruudun alareunassa myos inertia-rullauksen aikana.
       (translateZ palkkiin ITSEENSA on turvallista: sen oma fixed-positio
       sailyy suhteessa ruutuun; vain sen fixed-LAPSET saisivat uuden
       ankkurin, eika sellaisia ole.) */
    transform: translateZ(0);
    -webkit-transform: translateZ(0);
    backface-visibility: hidden;
    -webkit-backface-visibility: hidden;
}
.bottom-bar-item {
    flex: 1;
    min-width: 0;
    display: flex;
    flex-direction: column;
    align-items: center;
    padding: 6px 0;
    font-size: 11px;
    color: var(--gray-500);
    text-decoration: none;
    gap: 2px;
}
/* Kuvake on nyt SVG eika emoji: se ottaa rivin varin, joten aktiivinen
   kohta varjaa myos kuvakkeen. */
.bottom-bar-item .ikoni { vertical-align: 0; margin-bottom: 1px; }
/* Teksti ei saa katketa kahdelle riville: kuusi solua on kapea, ja
   rivittynyt nimi nostaisi palkin korkeutta epatasaisesti. */
.bottom-bar-item span:last-child {
    white-space: nowrap;
    max-width: 100%;
    overflow: hidden;
    text-overflow: ellipsis;
}
@media (max-width: 360px) {
    .bottom-bar-item { font-size: 10px; }
    .bottom-bar-item .ikoni { width: 20px; height: 20px; }
}
.bottom-bar-item.active { color: var(--primary); font-weight: 600; }
.bottom-badge {
    position: absolute;
    top: 2px; right: calc(50% - 18px);
    background: var(--danger);
    color: white;
    font-size: 10px;
    font-weight: 700;
    min-width: 16px;
    height: 16px;
    border-radius: 8px;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 0 4px;
}

@media (max-width: 768px) {
    .bottom-bar { display: grid; }
    /* Alapalkki 64 px + kelluva nappi joka nousee 18 px sen ylapuolelle.
       env(safe-area-inset-bottom) palauttaa 0 ilman viewport-fit=cover:ia,
       joten fallbackiin ei voi luottaa: varataan tila kiintein luvuin.
       Myos .content-full, joka jai aiemmin kokonaan ilman alapaddingia. */
    .content, .content-full {
        padding-bottom: calc(110px + env(safe-area-inset-bottom, 0px));
    }
    .sidebar { bottom: 0; height: auto; z-index: 1001; padding-bottom: 20px; }
    /* EI position:fixed. Se romahduttaa dokumentin korkeuden, jolloin selain
       pakottaa scrollY:n nollaan, eika sulkeminen palauta paikkaa: kayttaja
       oli asetussivun puolivalissa, avasi valikon, ja loysi itsensa sivun
       alusta (auditti 28.8.2026). Vierityksen lukitus tehdaan nyt bodylle
       JS:sta, joka muistaa ja palauttaa kohdan. */
    .sidebar.open ~ .content { overflow: hidden; }
}

/* =====================================================================
   KÄYNNISSÄ OLEVA KELLO — näkyy joka sivulla (2026-09-18)
   Petteri: "se ei sitä kellotusta näytä missään ... vaan siellä etusivulla
   'jossain' jää helposti huomaamatta tai pysäyttämättä". Palkki on kiinni
   ruudussa, ei sivun sisällössä, joten se ei jää rullauksen taakse.
   ===================================================================== */
.kellopalkki {
    position: fixed;
    z-index: 1002; /* alapalkin (1000) ylapuolella, sivupalkin (1001) kanssa */
    left: 0;
    right: 0;
    bottom: calc(66px + env(safe-area-inset-bottom, 0px) + var(--ios-palkki, 0px));
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 9px 14px;
    background: var(--tila-varoitus-tausta, #FFF4E0);
    border-top: 1px solid var(--tila-varoitus-reuna, #F0C27B);
    border-bottom: 1px solid var(--tila-varoitus-reuna, #F0C27B);
    color: var(--tila-varoitus-teksti, #7A4A00);
    box-shadow: 0 -2px 10px rgba(0, 0, 0, 0.10);
    transform: translateZ(0);
}
.kellopalkki-piste {
    flex: 0 0 auto;
    width: 9px;
    height: 9px;
    border-radius: 50%;
    background: var(--danger, #C0392B);
    animation: kellopalkki-syke 1.6s ease-in-out infinite;
}
@keyframes kellopalkki-syke {
    0%, 100% { opacity: 1; transform: scale(1); }
    50% { opacity: .35; transform: scale(.8); }
}
@media (prefers-reduced-motion: reduce) {
    .kellopalkki-piste { animation: none; }
}
.kellopalkki-aika {
    font-variant-numeric: tabular-nums;
    font-weight: 700;
    font-size: 16px;
    letter-spacing: .01em;
    white-space: nowrap;
}
.kellopalkki-kohde {
    flex: 1 1 auto;
    min-width: 0;
    font-size: 13px;
    line-height: 1.3;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.kellopalkki-nappi {
    flex: 0 0 auto;
    padding: 7px 14px;
    border: none;
    border-radius: 6px;
    background: var(--danger, #C0392B);
    color: #fff;
    font-size: 13px;
    font-weight: 600;
    text-decoration: none;
    white-space: nowrap;
}
.kellopalkki-nappi:hover { color: #fff; text-decoration: none; opacity: .9; }
/* Sisällön alapadding kasvaa palkin verran, jottei viimeinen rivi jää alle. */
body.kello-kay .content,
body.kello-kay .content-full {
    padding-bottom: calc(154px + env(safe-area-inset-bottom, 0px));
}
@media (min-width: 769px) {
    /* Työpöydällä ei ole alapalkkia: kompakti pilleri oikeaan alanurkkaan,
       jottei se peitä sivun koko leveyttä. */
    .kellopalkki {
        left: auto;
        right: 18px;
        bottom: 18px;
        max-width: 420px;
        border: 1px solid var(--tila-varoitus-reuna, #F0C27B);
        border-radius: 10px;
        box-shadow: 0 6px 20px rgba(0, 0, 0, 0.16);
    }
    body.kello-kay .content,
    body.kello-kay .content-full { padding-bottom: 88px; }
}
@media print {
    .kellopalkki { display: none !important; }
}

/* =====================================================================
   MOD 3/N — Lomakkeet, dialogit, mobiili (2026-04-18)
   Lisäykset, EI korvaa olemassa olevia luokkia.
   ===================================================================== */

/* --- Lomakeparannukset --- */

/* .form-help — pieni apuvihje kentän alle */
.form-help {
    font-size: 12px;
    color: var(--gray-500);
    margin-top: 4px;
    line-height: 1.4;
}

/* .form-error — validointivirhe kentän alle */
.form-error {
    font-size: 12px;
    color: var(--danger);
    margin-top: 4px;
    line-height: 1.4;
    display: block;
}

/* Required-merkki labelille */
.form-label .required,
.form-label.required::after {
    color: var(--danger);
    font-weight: 600;
}
.form-label.required::after { content: ' *'; }

/* .form-row-auto — taipuva flex-rivi (vaihtoehto grid 1fr 1fr -muodolle).
   Käytä kun haluat 3+ kenttää tai automaattista mitoitusta. */
.form-row-auto {
    display: flex;
    gap: 12px;
    flex-wrap: wrap;
}
.form-row-auto > .form-group {
    flex: 1 1 200px;
    min-width: 200px;
}

/* Kenttien koko-variantit */
.form-control-sm {
    padding: 6px 10px;
    font-size: 13px;
    min-height: 30px;
}
.form-control-lg {
    padding: 12px 16px;
    font-size: 15px;
    min-height: 44px;
}

/* Virhetila (2.10.2026): selaimen muotovirhe kirjoittamisen jälkeen sekä
   palvelimen kenttävirhe (aria-invalid, kenttavirheet.js). Teemamuuttujilla,
   jotta tumma tila ei näytä vaaleanpunaista laatikkoa. */
/* Vain kirjoitettavat kentät: valikolla, tiedosto- ja päiväkentällä ei ole
   placeholder-tilaa, joten koskematon pakollinen kenttä ("Valitse työmaa",
   tiedoston valinta) näkyi heti punaisena. */
input.form-control:is([type=text], [type=email], [type=tel], [type=url], [type=number], [type=search], [type=password], :not([type])):user-invalid:not(:focus),
textarea.form-control:user-invalid:not(:focus) {
    border-color: var(--danger);
    background-color: var(--tila-virhe-tausta);
}
/* Palvelimen kenttävirhe kaikille kenttätyypeille. !important, koska osa
   kentistä (esim. litteroinnin rivisumma) asettaa reunansa inline-tyylillä. */
input[aria-invalid="true"],
select[aria-invalid="true"],
textarea[aria-invalid="true"] {
    border: 1px solid var(--danger) !important;
    background-color: var(--tila-virhe-tausta) !important;
}
input[aria-invalid="true"]:focus,
select[aria-invalid="true"]:focus,
textarea[aria-invalid="true"]:focus {
    outline: none;
    box-shadow: 0 0 0 3px var(--tila-virhe-tausta);
}
.kentta-virheviesti {
    margin-top: 4px;
    font-size: 13px;
    color: var(--tila-virhe-teksti);
}

/* Pois käytöstä: ei voi muuttaa eikä tieto ole tässä olennainen. */
.form-control:disabled {
    background: var(--pinta-upotettu);
    color: var(--teksti-himmea);
    cursor: not-allowed;
    opacity: 0.75;
}
/* Vain luku: tieto on olennainen ja luettava, sitä ei vain muokata tässä
   (esim. laskennasta tuleva summa, lukittu kirjaus). Ei himmennystä eikä
   kielletty-kursoria, teksti valittavissa ja kopioitavissa. */
.form-control[readonly] {
    background: var(--pinta-korostettu);
    color: var(--teksti);
    border-style: dashed;
    cursor: text;
}
.form-control:disabled:hover { border-color: var(--gray-300); }

/* Date/time/select/number — sama .form-control-ulkoasu jo paikallaan, varmistetaan tyyppikohtaiset.
   iOS-fix: input[type="date"]/[type="time"] turhat alimitat */
input[type="date"].form-control,
input[type="time"].form-control,
input[type="datetime-local"].form-control,
input[type="number"].form-control {
    -webkit-appearance: none;
    appearance: none;
    background-color: var(--pinta);
    line-height: 1.4;
}
/* Selectille kustom-nuoli (chevron), Inter-yhteensopiva */
select.form-control {
    background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%236b7280' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'><polyline points='6 9 12 15 18 9'/></svg>");
    background-repeat: no-repeat;
    background-position: right 10px center;
    padding-right: 32px;
    appearance: none;
    -webkit-appearance: none;
}

/* Checkbox & radio — brandatut värit + isompi klikkausalue */
input[type="checkbox"],
input[type="radio"] {
    accent-color: var(--primary);
    width: 16px;
    height: 16px;
    cursor: pointer;
    vertical-align: middle;
}
.check-row,
.radio-row {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    cursor: pointer;
    padding: 6px 0;
    min-height: 36px;     /* touch target */
    user-select: none;
}
.check-row input,
.radio-row input { margin: 0; }

/* File-input — siistimpi look */
input[type="file"] {
    font-family: var(--font-sans);
    font-size: 13px;
    color: var(--gray-700);
}
input[type="file"]::file-selector-button {
    background: var(--gray-100);
    border: 1px solid var(--gray-300);
    border-radius: var(--radius-sm);
    color: var(--gray-700);
    padding: 6px 12px;
    margin-right: 10px;
    font-family: var(--font-sans);
    font-size: 13px;
    font-weight: 500;
    cursor: pointer;
    transition: background 0.12s, border-color 0.12s;
}
input[type="file"]::file-selector-button:hover {
    background: var(--gray-200);
    border-color: var(--gray-400);
}

/* Toggle-button -ryhmä (esim. status-suodatin .d-flex.gap-1 mb-2 — apuluokka) */
.btn-group {
    display: inline-flex;
    flex-wrap: wrap;
    gap: 4px;
}

/* --- Btn-ghost — läpinäkyvä tekstinappi --- */
.btn-ghost {
    background: transparent;
    border-color: transparent;
    color: var(--gray-700);
    box-shadow: none;
}
.btn-ghost:hover {
    background: var(--gray-100);
    color: var(--gray-900);
}

/* --- Typografia-apulaiset ---
   Korvaavat inline-tyylin `style="font-size: Npx;"`, jota oli templaateissa
   toista tuhatta kertaa. Luokan on voitettava taulukko- ja lomakesäännöt
   (esim. `.table td`, `.form-control`) joiden spesifisyys on korkeampi kuin
   pelkän luokan — muuten vaihto muuttaisi ulkoasua juuri siellä missä koko
   on tarkoituksella pieni. Siksi valitsin on `html .text-*`, jolloin spesifisyys on
   sama (0,0,1,1) ja myöhempi sijainti tiedostossa ratkaisee. Vanhat
   .text-sm (13px) ja .text-xs (12px) eivät olleet käytössä yhdessäkään
   templaatissa, joten arvot voitiin yhtenäistää tokenien kanssa. */
.text-strong { font-weight: 600; }
html .text-2xs { font-size: var(--text-2xs); }
html .text-xs { font-size: var(--text-xs); }
html .text-sm { font-size: var(--text-sm); }
html .text-md { font-size: var(--text-md); }
html .text-base { font-size: var(--text-base); }
html .text-lg { font-size: var(--text-lg); }
html .text-xl { font-size: var(--text-xl); }
html .text-2xl { font-size: var(--text-2xl); }
.text-mono {
    font-family: 'JetBrains Mono', 'Fira Code', 'Consolas', monospace;
    font-size: 0.92em;
}
.truncate {
    text-overflow: ellipsis;
    overflow: hidden;
    white-space: nowrap;
    min-width: 0;     /* salli flex-childille kutistuminen */
}

/* --- Yleiskäyttöiset layout-apulaiset (toistuvat inline-tyylit) --- */
.inline-stack {
    display: inline-flex;
    align-items: center;
    gap: 8px;
}
.row-stack {
    display: flex;
    align-items: center;
    gap: 8px;
    flex-wrap: wrap;
}
.col-stack {
    display: flex;
    flex-direction: column;
    gap: 8px;
}

/* --- Mobiiliresponsiivisuus --- */

/* Page-header: action-napit alle pienillä näytöillä */
@media (max-width: 640px) {
    .page-header {
        align-items: stretch;
        flex-direction: column;
    }
    .page-header > * { width: 100%; }
    .page-title { font-size: 19px; }
    /* form-row 2-col → 1-col mobiilissa myös 640px (oli vain 480px) */
    .form-row { grid-template-columns: 1fr; }
    .form-row-auto { flex-direction: column; gap: 10px; }
    .form-row-auto > .form-group {
        flex-basis: auto;
        min-width: 0;
    }
    /* Kortit hieman tiiviimmiksi */
    .card { padding: 14px; }
}

/* Taulukon reagointi: jos templaatti käärii .table-responsive, vyörytetään */
.table-responsive {
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
}

/* Mobiili: taulukko vieritetään sivusuunnassa, EI puristeta sarakkeita.
   Ilman tätä selain kutistaa sarakkeet mahtumaan ja katkoo sisällön
   kirjain kerrallaan ("B u d j e t t i", "1 8 4 2 0 €"). Numerosarakkeet
   eivät katkea lainkaan; ensimmäinen sarake (nimi) rivittyy sanoittain
   järkevän levyisenä. */
@media (max-width: 768px) {
    .table-responsive > table th,
    .table-responsive > table td {
        white-space: nowrap;
    }
    .table-responsive > table th:first-child,
    .table-responsive > table td:first-child {
        white-space: normal;
        min-width: 150px;
        max-width: 230px;
    }
}

/* Reduced motion — sammuta animaatiot */
@media (prefers-reduced-motion: reduce) {
    *, *::before, *::after {
        animation-duration: 0.001ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: 0.001ms !important;
        scroll-behavior: auto !important;
    }
}

/* Mobiili: pakota inline-yliajatut stats-gridit (repeat(3)/repeat(2)) 2 sarakkeeseen
   kapealla — muuten stat-kortit puristuvat ~110px:iin ja luvut eivät mahdu. */
@media (max-width: 480px) {
    .stats-grid { grid-template-columns: repeat(2, 1fr) !important; }
}

/* Tabletti pystyssa (~800 px): neljalle kortille auto-fit antaa kolme
   sarakkeetta ja neljas jaa orvoksi omalle rivilleen. Tasan nelja korttia
   menee 2 x 2:een, kolme ja muut maarat pysyvat ennallaan. */
@media (min-width: 481px) and (max-width: 1023px) {
    .stats-grid:has(> :nth-child(4):last-child) { grid-template-columns: repeat(2, 1fr); }
}

/* Print styles */
@media print {
    .navbar, .sidebar, .bottom-bar, .btn, form, .nav-search,
    .page-header a, .d-flex.gap-1.flex-wrap.mb-2 { display: none !important; }
    .content { margin: 0; padding: 10px; }
    .card { box-shadow: none; border: 1px solid #ddd; page-break-inside: avoid; }
    .stats-grid { grid-template-columns: repeat(4, 1fr); }
    .stat-card { box-shadow: none; border: 1px solid #ddd; }
    body { font-size: 12px; background: white; }
    .page-header { margin-bottom: 10px; }
    .page-header .page-title { font-size: 18px; }
}

/* =====================================================================
   Design-auditointi 2026-08-03: kolme verifioitua korjausta.
   Lisäyksiä, ei korvaa olemassa olevia luokkia.
   ===================================================================== */

/* 1. .btn-secondary oli käytössä 33 kertaa ilman määrittelyä. .btn ei
      aseta taustaa lainkaan, joten nappi renderöityi läpinäkyvänä ja
      reunattomana eli näkymättömänä. Sidotaan .btn-outlineen, joka on
      jo vakiintunut toissijainen nappi (913 käyttöä), jotta samalle
      asialle ei jää kahta eri ulkoasua. */
.btn-secondary {
    background: transparent;
    border: 1px solid #D6D5D2;
    color: var(--gray-700);
    box-shadow: none;
}
.btn-secondary:hover {
    background: #FBFAF7;
    border-color: var(--gray-400);
    color: var(--gray-900);
}

@media (max-width: 768px) {
    /* 2. .table-responsive vetää -20px marginaalin, mutta .content-padding
          on mobiilissa 16px. Erotus vuoti 4px yli molemmilta reunoilta eli
          koko sivu sai 8px vaakavierityksen jokaisella taulukkosivulla. */
    .table-responsive { margin: 0 -16px; padding: 0 16px; }
    /* Negatiivinen marginaali on tarkoitettu .content:in suoraksi lapseksi
       (taulukko reunasta reunaan). Kortin sisalla se venytti kaareen kortin
       oman reunaviivan paalle ja kortti nayttti rikkinaiselta. Koskee 25
       sivupohjaa. */
    .card > .table-responsive { margin: 0; padding: 0; }

    /* 3. Alle 16px kenttä pakottaa iOS Safarin zoomaamaan sisään
          fokusoitaessa. Työmaalla se tarkoittaa nipistystä takaisin joka
          kentän jälkeen, hanskat kädessä käytännössä mahdotonta.
          16px on Applen raja, ei makuasia. */
    .form-control,
    input[type="text"], input[type="number"], input[type="email"],
    input[type="tel"], input[type="password"], input[type="date"],
    input[type="time"], input[type="search"], select, textarea {
        font-size: 16px;
    }
}

/* ===================================================================
   Kosketuskohteet ja lomakkeet mobiilissa (design-audit 2026-08-03)
   =================================================================== */

@media (max-width: 768px) {
    /* Kosketuskohde 44 px: sitä pienempi kenttä tai nappi on hanskat
       kädessä osumapeliä. 36 px riitti hiirelle, ei sormelle. */
    .btn { min-height: 44px; padding: 10px 16px; }
    /* 5.10.2026: myös pieni nappi 44 px (UI-sääntö 7, Talouden läpikäynti). */
    .btn-sm { min-height: 44px; padding: 8px 12px; }

    .form-control,
    input[type="text"], input[type="number"], input[type="email"],
    input[type="tel"], input[type="password"], input[type="date"],
    input[type="time"], input[type="search"], select {
        min-height: 44px;
    }

    /* Valintaruutu 16 px on sormelle liian pieni; kasvatetaan ruutu ja
       annetaan sen ympärille väljyyttä ilman että rivi hyppää. */
    input[type="checkbox"], input[type="radio"] {
        width: 22px;
        height: 22px;
        min-height: 22px;
    }

    /* Sääntö yllä koskee vain .btn-luokkaa ja lomakekenttiä, joten paljaat
       <button>-elementit jäivät ulkopuolelle. Mobiiliauditti 28.8.2026 löysi
       niitä kahdesta paikasta joissa niihin oikeasti kosketaan:
       yläpalkin napit (tiiveyskytkin 24 px) ja kartan zoom (30 px).
       Ne eivät kasva itsestään, koska niillä ei ole .btn-luokkaa.

       Näitä ei laajenneta kaikkiin <button>-elementteihin: taulukoiden
       rivinsiirtonuolet on tarkoituksella pieniä ja pinottu päällekkäin,
       ja 44 px hajottaisi rivin. */
    /* .nav-user-info on kayttajan nimilinkki, ei kosketuskohde: se piiloutuu
       kapealla ruudulla. Ilman poissulkua tama saanto (spesifisyys 0,2,0)
       voitti sen oman display:none -saannon (0,1,0) ja palautti nimen
       nakyviin, jolloin navbar vuoti taas yli. Oma korjaus rikkoi toisen. */
    .navbar button,
    .navbar > a:not(.nav-user-info),
    .nav-user > a:not(.nav-user-info) {
        min-height: 44px;
        min-width: 44px;
        display: inline-flex;
        align-items: center;
        justify-content: center;
    }
    .leaflet-control-zoom a,
    .leaflet-bar a {
        width: 44px !important;
        height: 44px !important;
        line-height: 44px !important;
        font-size: 20px !important;
    }

    /* Vaakarivi rivittyy kapealla naytolla. Rivi joka ei mahdu tyontaa
       sisaltoa ruudun ulkopuolelle, eika sinne paase kasiksi — se on aina
       vika. Rivi joka mahtuu ei muutu miksikaan. */
    .d-flex { flex-wrap: wrap; }

    /* Valinnan koko rivi klikattavaksi: label on iso kosketuskohde. */
    .check-row, .radio-row {
        display: flex;
        align-items: center;
        gap: 10px;
        min-height: 44px;
        cursor: pointer;
    }
    .check-row input, .radio-row input { flex: none; }
}

/* Pitkien lomakkeiden tallennuspalkki pysyy näkyvissä. Ilman tätä
   käyttäjä vierittää 70 kentän lomakkeen läpi etsimässä nappia, eikä
   näe onko tallennus vielä tekemättä. */
.form-sticky-actions {
    position: sticky;
    bottom: 0;
    z-index: 20;
    display: flex;
    gap: 8px;
    align-items: center;
    flex-wrap: wrap;
    padding: 10px 0;
    margin-top: 12px;
    /* Kortin pinta, ei #fff: tummassa tilassa palkki oli valkoinen (Petteri 6.11.2026).
       Ei --tausta: vaaleassa tilassa se on harmaa ja laskulomakkeen palkin alle
       jäi harmaa raita (Petteri 7.11.2026). */
    background: linear-gradient(180deg, transparent 0%, var(--pinta) 28%);
    border-top: 1px solid var(--reuna);
}
.form-sticky-actions .btn { flex: 1 1 auto; }
/* Mobiilissa alapalkki (.bottom-bar, 66 px) on kiinteä ruudun alalaidassa.
   Ilman siirtoa tallennusnappi jäisi sen alle piiloon. */
@media (max-width: 768px) {
    .form-sticky-actions { bottom: 66px; }
}
@supports (padding: env(safe-area-inset-bottom)) {
    .form-sticky-actions { padding-bottom: calc(10px + env(safe-area-inset-bottom)); }
}

/* Neutraali vierityskääre leveille taulukoille. Toisin kuin
   .table-responsive tässä ei ole negatiivista marginaalia, joten kääreen
   voi lisätä mihin tahansa ilman että ulkoasu siirtyy. */
.table-scroll {
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
}

/* Kapealla naytolla taulukko saa levita luonnolliseen leveyteensa ja
   vierita. Ilman tata `table { width: 100% }` puristaa sarakkeet niin
   kapeiksi etta otsikot katkeavat kirjain kerrallaan pystyyn. */
@media (max-width: 768px) {
    .table-scroll > table { width: auto; min-width: 100%; }
    .table-scroll > table th,
    .table-scroll > table td { overflow-wrap: normal; }
}

/* ── Lukutaulukko korteiksi kapealla naytolla (yhteinen, 14.9.2026) ──────
   table.taulukko-korteiksi: tyopoydalla tavallinen taulukko, puhelimella
   jokainen rivi on kortti ja solut otsikko+arvo-pareja. Otsikot base.html
   kopioi thead:n th-teksteista td:n data-label-attribuuttiin, joten
   templatteen riittaa yksi luokka. Ensimmainen solu on kortin otsikko
   (ei labelia), tyhjaotsikkoinen solu (toiminnot) asettuu oikeaan laitaan.
   Inventaario 14.9.: 10 finance/docs-sivun sarakkeet katkesivat puhelimella. */
@media (max-width: 768px) {
    .taulukko-korteiksi,
    .taulukko-korteiksi tbody,
    .taulukko-korteiksi tfoot,
    .taulukko-korteiksi tr,
    .taulukko-korteiksi td { display: block; width: 100% !important; min-width: 0 !important; box-sizing: border-box; }
    .taulukko-korteiksi thead, .taulukko-korteiksi tr.korteiksi-otsikkorivi { display: none; }
    .taulukko-korteiksi tr {
        border: 1px solid var(--gray-200, #E8E7E4);
        border-radius: 8px;
        padding: 8px 12px;
        margin-bottom: 8px;
        background: var(--pinta, #fff);
    }
    .taulukko-korteiksi td {
        display: flex;
        justify-content: space-between;
        align-items: baseline;
        gap: 10px;
        padding: 3px 0 !important;
        border: 0 !important;
        text-align: right !important;
        white-space: normal !important;
        font-size: 13px;
    }
    .taulukko-korteiksi td::before {
        content: attr(data-label);
        flex: 0 0 38%;
        text-align: left;
        color: var(--gray-500, #73726F);
        font-size: 12px;
        font-weight: 600;
    }
    /* Otsikkosolu on block, ei flex: solun oma sisältö (nimi + <br> +
       yhteyshenkilö) säilyttää pystyjärjestyksensä. ChatGPT 15.9.2026:
       laskutuskontaktin nimi ja yhteyshenkilö pakkautuivat samalle riville. */
    .taulukko-korteiksi td.korteiksi-otsikko { display: block; font-weight: 600; font-size: 14px; text-align: left !important; padding-bottom: 6px !important; overflow-wrap: anywhere; }
    .taulukko-korteiksi td.korteiksi-otsikko::before,
    .taulukko-korteiksi td.korteiksi-toiminnot::before,
    .taulukko-korteiksi td:not([data-label])::before { display: none; }
    .taulukko-korteiksi td.korteiksi-toiminnot { justify-content: flex-end; padding-top: 8px !important; }
    .taulukko-korteiksi td.korteiksi-tyhja { display: none; }
    /* Pitkä arvo (sähköposti, verkkolaskuosoite) katkeaa sanan sisältä eikä
       venytä korttia; jos arvo ei mahdu otsikon viereen se putoaa omalle
       riville otsikon alle. */
    .taulukko-korteiksi td { flex-wrap: wrap; overflow-wrap: anywhere; }
    /* Yhteenvetorivi (tfoot) on sekin kortti, mutta korostettu. */
    .taulukko-korteiksi tfoot tr { background: var(--gray-50, #F7F6F3); border-color: var(--gray-300, #D6D5D2); }
    .taulukko-korteiksi tfoot td { font-weight: 600; }
    .taulukko-korteiksi tfoot td.korteiksi-otsikko { text-transform: none; color: var(--gray-700, #41403D); }
    .table-scroll:has(> .taulukko-korteiksi),
    .table-responsive:has(> .taulukko-korteiksi) { overflow: visible; }
    .table-scroll > table.taulukko-korteiksi { width: 100%; }
}

/* Toissijainen sarake pois kapealta naytolta. Vaihtoehto olisi vaakavieritys,
   mutta silloin rivin tarkein ohjain (esim. maarakentta) jaa ruudun taakse. */
@media (max-width: 768px) {
    .piilota-kapealla { display: none; }
}

/* ── Syottotaulukko korteiksi kapealla naytolla ─────────────────────────
   Kosteusmittauksen lukemataulukossa on 8 saraketta syottokenttia ja se on
   750 px levea. Puhelimessa (360 px) se mahtui vain vaakavieritykseen: RH-,
   lampotila- ja poistokentta jaivat ruudun oikealle puolelle, ja lomake
   taytetaan nimenomaan puhelimella mittauspaikalla.

   Rivi muuttuu kortiksi ja solut label+kentta-pareiksi. Otsikko luetaan
   solun data-label-attribuutista, joten markupista on vain yksi versio ja
   sama muotoilu patee myos JS:n lisaamiin riveihin. */
@media (max-width: 768px) {
    .syottotaulukko-kortiksi thead { display: none; }
    .syottotaulukko-kortiksi,
    .syottotaulukko-kortiksi tbody,
    .syottotaulukko-kortiksi tr,
    .syottotaulukko-kortiksi td {
        display: block;
        width: 100%;
    }
    .syottotaulukko-kortiksi tr {
        border: 1px solid var(--gray-200);
        border-radius: 8px;
        padding: 8px 10px;
        margin-bottom: 10px;
        background: var(--pinta);
    }
    .syottotaulukko-kortiksi td {
        display: flex;
        align-items: center;
        gap: 10px;
        border: 0;
        padding: 4px 0;
    }
    .syottotaulukko-kortiksi td::before {
        content: attr(data-label);
        flex: 0 0 84px;
        font-size: 12px;
        font-weight: 600;
        color: var(--gray-600);
    }
    /* Kentta otsikon VIEREEN, ei alle: .form-control on width:100%, joka
       tyontaisi sen omalle rivilleen. */
    .syottotaulukko-kortiksi td > .form-control {
        flex: 1 1 120px;
        width: auto;
        min-width: 0;
    }
    /* Poistonappi omalle rivilleen oikeaan laitaan, ilman tyhjaa otsikkoa */
    .syottotaulukko-kortiksi td.reading-poista { justify-content: flex-end; }
    .syottotaulukko-kortiksi td.reading-poista::before { display: none; }
    /* Kaare ei saa vierittaa kun taulukko ei ole enaa levea */
    .syottotaulukko-kortiksi-kaare { overflow-x: visible; }
    /* Solu jossa on monta kenttaa (syvyys: mm + selite) rivittyy, ja kentat
       kutistuvat. Kiinteat leveydet tyonsivat sisallon ruudun ulkopuolelle. */
    .syottotaulukko-kortiksi td { flex-wrap: wrap; }
    .syottotaulukko-kortiksi .syvyys-mm,
    .syottotaulukko-kortiksi .syvyys-selite { width: auto; min-width: 0; flex: 1 1 70px; }
    /* Mittapaa-solussa on laitevalinta JA reian numero: numero omalle
       rivilleen otsikon alle, jotta se ei kutistu lukukelvottomaksi. */
    .syottotaulukko-kortiksi .reika-nro { flex: 1 1 100%; }
}

/* Syvyyssolun kentat: tyopoydalla kiinteat leveydet kuten ennen. */
.syvyys-mm { width: 70px; display: inline-block; }
.syvyys-selite { width: 120px; display: inline-block; }
/* Reian / mittapaan numero laitevalinnan alla (Petteri 14.9.2026: porareiat
   kirjataan numeroineen lahtotiedoiksi ennen mittauksia). */
.reika-nro { width: 130px; display: block; margin-top: 3px; }


/* ===================================================================
   Apuluokat toistuville inline-kuvioille (design-audit 2026-08-03)

   Templaateissa oli 7183 inline-tyyliä mutta vain parikymmentä eri
   kuviota. Nämä luokat korvaavat yleisimmät. Uusi koodi käyttää näitä
   inline-tyylin sijaan.
   =================================================================== */

/* Otsikkorivi: nimi vasemmalla, toiminnot oikealla, rivittyy kapealla. */
.row-between {
    display: flex;
    justify-content: space-between;
    align-items: center;
    flex-wrap: wrap;
    gap: var(--space-2);
}

/* Vaakarivi jossa osat samalla korkeudella. */
.row-center {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: var(--space-3);
}

/* ─────────────────────────────────────────────────────────────
   Näkymätiheys: Kompakti (html.density-compact)
   Kytkin navbarissa (base.html), valinta localStorage 'sp_density'.
   Classic = oletus ilman luokkaa. Kompakti tiivistää taulukot,
   kortit, napit ja lomakkeet noin 25 %: enemmän rivejä ruudulle.
   Mobiilissa (alle 768px) tiivistys on maltillisempi, jotta
   touch-targetit eivät kutistu liikaa.
   ───────────────────────────────────────────────────────────── */
html.density-compact {
    --space-1: 3px; --space-2: 6px; --space-3: 9px;
    --space-4: 12px; --space-5: 17px; --space-6: 24px;
}
html.density-compact .card { padding: 11px 13px; margin-bottom: 10px; }
html.density-compact th, html.density-compact td { padding: 5px 10px; font-size: 12.5px; }
html.density-compact th { white-space: nowrap; }
html.density-compact .table th, html.density-compact .table td { padding: 5px 9px; font-size: 13px; }
html.density-compact .table-sm th, html.density-compact .table-sm td { padding: 3px 6px; }
html.density-compact .btn { padding: 5px 11px; min-height: 30px; font-size: 12.5px; }
html.density-compact .form-control { padding: 5px 8px; font-size: 13px; }
html.density-compact .form-group { margin-bottom: 9px; }
html.density-compact .page-header { margin-bottom: 10px; }
html.density-compact .page-title { font-size: 19px; }
html.density-compact h2 { font-size: 17px; }
html.density-compact h3 { font-size: 14.5px; }
html.density-compact .nav-link { padding: 5px 12px; font-size: 12.5px; }
html.density-compact .nav-section { padding-top: 10px; padding-bottom: 6px; }
@media (max-width: 768px) {
    /* Touch-target: napit ja lomakekentät eivät saa kutistua kännykässä */
    html.density-compact .btn { min-height: 36px; padding: 7px 12px; }
    html.density-compact .form-control { padding: 8px 10px; font-size: 14px; }
}

/* Paivamaaran tarkistus (base.html): kertoo kun kirjaus ei mene talle
   paivalle. Sijoittuu kentan alle eika vie tilaa kun se on piilossa. */
.pvm-huomio {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 6px;
    margin-top: 4px;
    padding: 4px 8px;
    border-radius: 4px;
    font-size: 12px;
    color: var(--tila-varoitus-teksti);
    background: var(--tila-varoitus-tausta);
}
.pvm-huomio.hidden { display: none; }
.pvm-tanaan {
    background: none;
    border: none;
    padding: 0;
    font: inherit;
    color: inherit;
    text-decoration: underline;
    cursor: pointer;
}
/* Puhelimella linkkinappi kosketuskokoon (inventaario 16.9.2026: 18 px). */
@media (max-width: 768px) {
    .pvm-tanaan { min-height: 44px; padding: 6px 0; display: inline-flex; align-items: center; }
}


/* Kapea puhelin (iPhone SE / 13 mini, 375 px): navbarin oikea nappiryhma
   ei mahtunut ja uloskirjautuminen leikkaantui ruudun ulkopuolelle.
   Tiiveyskytkin ja ohje loytyvat sivupalkista, joten ne piilotetaan
   navbarista ensin. Auditti 28.8.2026. */
@media (max-width: 480px) {
    .navbar { padding: env(safe-area-inset-top, 0px) 8px 0; }
    .nav-user { gap: 2px; }
    .navbar #density-toggle,
    .navbar .nav-help { display: none; }
    /* Uloskirjautuminen on Oman profiilin lopussa (AS6, 3.11.2026). !important,
       koska napilla on base.html:ssä oma inline-display. Tumma tila pysyy
       yläpalkissa (Petteri 4.11.2026: "mobiilissa ei ole yötila nappia"). */
    .navbar .nav-logout-form { display: none !important; }
    .navbar #theme-mode-toggle { min-width: 44px; min-height: 44px; justify-content: center; }
    /* Kayttajan nimi ja rooli ovat puhelimessa koristetta: han tietaa kuka
       on kirjautunut. Teksti vie 76 px, ja se on juuri se tila jonka
       44 pikselin kosketuskohteet tarvitsevat. Nimi loytyy sivupalkista ja
       profiilista. */
    .nav-user-info { display: none; }
}

/* Kapea puhelin (iPhone SE 320 px, moni Android 360 px): ylapalkin oikea
   reuna vuoti ulos ruudusta ja ULOSKIRJAUTUMISNAPPI jai kokonaan nakymattomiin.
   Mitattu 17.9.2026 sivulla 320 px: .nav-brand 164 px + .nav-user 184 px
   + reunus 8 px = 356 px, eli nappi alkoi kohdasta 312 ja paattyi 356.
   Logon teksti "Sitepilot" vie 70 px eika kerro mitaan mita logo ei jo kerro,
   joten se piiloutuu ensin. Kosketuskohteet sailyvat 44 pikselisina. */
/* 3.11.2026 (asetusehdotus AS6): tekstilogo ei enää piiloudu alle 380 px, koska
   logo on ainoa Etusivu-linkki. Tilaa tehtiin piilottamalla puhelimella
   uloskirjautuminen yläpalkista; se on Oman profiilin lopussa. */
@media (max-width: 380px) {
    .nav-brand { gap: 6px; }
    .nav-brand a { gap: 6px; }
}

/* KPI-tiilet: nimeton flex-lapsi peri min-width:auto, joten pitka otsikko
   ("TARJOUSVAIHEESSA") tyonsi itsensa kortin reunan yli sen sijaan etta
   olisi rivittynyt. Koskee 15 sivupohjaa jotka kayttavat samaa kuviota. */
/* Attribuuttivalitsin, koska sama kuvio on seitsemalla etuliitteella
   (ws-, inv-, cust-, qt-, op-, mp-, sub-) ja uusia tulee lisaa.
   min-width:0 estaa ylivuodon: ilman sita flex-lapsi peri min-width:auto ja
   pitka otsikko tyonsi itsensa kortin reunan yli. */
[class*="hero-tile"] > * { min-width: 0; }

/* Kapea puhelin: kaksi saraketta jattaa otsikolle noin 94 px, kun
   "TARJOUSVAIHEESSA" vaatii 123 px. Pelkka rivitys korjaisi ylivuodon mutta
   katkaisisi sanan keskelta, koska otsikko on yksi sana. Yksi sarake antaa
   koko leveyden ja otsikko mahtuu riville. Sivupohjien oma
   minmax(160px, 1fr) yliajetaan tassa, koska sama luku toistuu 15
   tiedostossa. Auditti 28.8.2026. */
@media (max-width: 430px) {
    [class$="-hero"], .hero { grid-template-columns: 1fr !important; }
}

/* ── Pyörivä latausilmaisin (esim. tositepaketin kokoaminen) ─────────────
   Käyttö: <span class="sp-pyorija" aria-hidden="true"></span> tekstin edessä.
   Perii tekstin värin (currentColor), joten toimii napissa ja tummassa tilassa. */
.sp-pyorija {
    display: inline-block;
    width: 0.9em;
    height: 0.9em;
    vertical-align: -0.15em;
    border: 2px solid currentColor;
    border-right-color: transparent;
    border-radius: 50%;
    animation: sp-pyorija-kierto 0.8s linear infinite;
}
@keyframes sp-pyorija-kierto { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) {
    .sp-pyorija { animation-duration: 2s; }
}

/* ---------------------------------------------------------------------
   Kortin toiminnot ja toimintovalikko (yhteinen komponentti, 13.9.2026).
   Kortilla näkyy päätoiminto ja tila, loput <details class="toimintovalikko">
   -valikossa. Natiivi details toimii ilman JS:ää; toimintovalikko.js lisää
   sulkemisen ulkopuolelta ja Escapella. Ensimmäinen käyttäjä: ostolaskut.
   --------------------------------------------------------------------- */
.kortin-toiminnot {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    align-items: center;
    margin-top: 8px;
}
.kortin-toiminnot form { margin: 0; display: inline-flex; }
.toimintovalikko {
    position: relative;
    display: inline-block;
}
.toimintovalikko > summary {
    list-style: none;
    cursor: pointer;
    user-select: none;
}
.toimintovalikko > summary::-webkit-details-marker { display: none; }
.toimintovalikko > summary::marker { content: ''; }
.toimintovalikko-lista {
    position: absolute;
    right: 0;
    top: calc(100% + 4px);
    z-index: 60;
    min-width: 240px;
    max-width: min(320px, calc(100vw - 32px));
    background: var(--pinta, #fff);
    color: var(--teksti, #111827);
    border: 1px solid var(--gray-200, #E8E7E4);
    border-radius: 10px;
    box-shadow: var(--shadow-xl, 0 12px 28px -6px rgba(0,0,0,0.22));
    padding: 6px;
    display: flex;
    flex-direction: column;
    gap: 2px;
}
.toimintovalikko-lista.toimintovalikko-vasen { right: auto; left: 0; }
.toimintovalikko-lista form { margin: 0; display: block; }
.toimintovalikko-lista > a,
.toimintovalikko-lista > button,
.toimintovalikko-lista > form > button {
    display: flex;
    align-items: center;
    gap: 8px;
    width: 100%;
    min-height: 44px;
    padding: 10px 12px;
    border: none;
    border-radius: 6px;
    background: none;
    color: var(--teksti, #111827);
    font: inherit;
    font-size: 14px;
    text-align: left;
    text-decoration: none;
    cursor: pointer;
}
.toimintovalikko-lista > a:hover,
.toimintovalikko-lista > button:hover,
.toimintovalikko-lista > form > button:hover {
    background: var(--gray-100, #F5F4F1);
}
.toimintovalikko-lista .vaara { color: var(--danger, #dc2626); }
.toimintovalikko-lista > button:disabled { opacity: .6; cursor: default; }
/* Kapealla näytöllä otsikkorivin valikko avautuu rivin levyisenä. Kortin
   valikko (.kortin-valikko) on kortin oikeassa yläkulmassa ja käyttää
   oletusleveyttä. */
@media (max-width: 768px) {
    /* Sama koskee kortin toimintoriviä: sen Lisää-nappi jää ruudukossa
       vasempaan reunaan, ja nappiin ankkuroitu 240 px:n paneeli valuisi
       ruudun vasemman reunan yli (ChatGPT 14.9.2026, tarjouskortti). */
    .otsikon-toiminnot, .kortin-toiminnot { position: relative; }
    .otsikon-toiminnot .toimintovalikko,
    .kortin-toiminnot .toimintovalikko { position: static; }
    .otsikon-toiminnot .toimintovalikko-lista,
    .kortin-toiminnot .toimintovalikko-lista { left: 0; right: 0; max-width: none; min-width: 0; }
    .toimintovalikko-lista { max-height: 60vh; overflow-y: auto; }
}

/* ---------------------------------------------------------------------
   KENTTÄKÄÄRE (yhteinen komponentti, 13.9.2026)
   <div class="kentta"><label>Nimi</label><select|input></div>
   Label on AINA kentän yläpuolella samassa kääreessä, joten rivittyminen
   ei koskaan erota niitä toisistaan. .kenttarivi asettelee kääreet
   ruudukkoon: työpöydällä rinnakkain, puhelimella yhteen sarakkeeseen.
   Ensimmäinen käyttäjä: ostolaskun litterointi.
   --------------------------------------------------------------------- */
.kenttarivi {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
    gap: 10px 12px;
    align-items: end;
}
.kentta {
    display: flex;
    flex-direction: column;
    gap: 4px;
    min-width: 0;
}
.kentta > label {
    font-size: 12px;
    font-weight: 600;
    color: var(--gray-700, #41403D);
    line-height: 1.2;
}
.kentta > select,
.kentta > input:not([type="hidden"]),
.kentta > textarea {
    width: 100%;
    max-width: 100%;
    min-height: 36px;
    padding: 6px 28px 6px 10px;
    font-size: 14px;
    border: 1px solid var(--gray-300, #D6D5D2);
    border-radius: 6px;
    background: var(--pinta, #fff);
    color: var(--teksti, #111827);
    text-overflow: ellipsis;
}
.kentta > input:not([type="hidden"]),
.kentta > textarea { padding-right: 10px; }
.kentta > .tilihaku {
    width: 100%;
    flex: none;
    height: auto;
    min-height: 36px;
    margin-bottom: 4px;
}
.kentta .vihje { font-size: 11px; color: var(--gray-500, #73726F); }
@media (max-width: 768px) {
    .kenttarivi { grid-template-columns: 1fr; gap: 14px; }
    .kentta > label { font-size: 13px; }
    .kentta > select,
    .kentta > input:not([type="hidden"]),
    .kentta > textarea,
    .kentta > .tilihaku { min-height: 48px; font-size: 16px; }
}

/* ---------------------------------------------------------------------
   TYÖTILAN TOIMINTOPALKKI (yhteinen komponentti, 13.9.2026)
   Sivu merkitsee <body class="tyotila"> (block body_class). Puhelimella
   yleinen alapalkki ja plusnappi väistyvät tehtävän ajaksi ja tilalla on
   yksi kiinteä palkki: yhteenveto + päätoiminto. Työpöydällä palkki on
   sticky sisällön alareunassa kuten ennenkin.
   --------------------------------------------------------------------- */
.tyotila-palkki {
    position: sticky;
    bottom: 0;
    z-index: 30;
    display: flex;
    justify-content: space-between;
    align-items: center;
    flex-wrap: wrap;
    gap: 8px 12px;
    padding: 10px 12px;
    margin-top: 10px;
    background: var(--pinta, #fff);
    border-top: 2px solid var(--primary, #4f46e5);
}
.tyotila-palkki .yhteenveto { font-size: 12px; color: var(--gray-600, #55544F); line-height: 1.4; }
.tyotila-palkki .yhteenveto strong { color: var(--teksti, #111827); }
.tyotila-palkki .ero { color: #b45309; font-weight: 600; }
.tyotila-palkki .toiminnot { display: flex; gap: 8px; align-items: center; margin-left: auto; }
@media (max-width: 768px) {
    body.tyotila .bottom-bar { display: none !important; }
    body.tyotila .content,
    body.tyotila .content-full { padding-bottom: calc(120px + env(safe-area-inset-bottom, 0px)); }
    .tyotila-palkki {
        position: fixed;
        left: 0;
        right: 0;
        bottom: 0;
        margin: 0;
        padding: 10px 12px calc(10px + env(safe-area-inset-bottom, 0px));
        box-shadow: 0 -4px 16px rgba(0,0,0,0.10);
        transform: translateZ(0);
    }
    .tyotila-palkki .toiminnot { width: 100%; margin-left: 0; }
    .tyotila-palkki .toiminnot .btn { flex: 1 1 auto; min-height: 46px; font-size: 15px; }
}

/* ---------------------------------------------------------------------
   DOKUMENTTIKATSELIN (yhteinen komponentti, 13.9.2026)
   Koko ruudun tila sovelluksen sisällä: sivut kuvina allekkain, oma
   vieritys, zoomaus. Ei iframea eikä uutta välilehteä (kotinäytön PWA
   hyppäisi ulos Safariin). Sulkeminen ei vaihda sivua, joten lomakkeen
   tila säilyy.
   --------------------------------------------------------------------- */
.dokumenttikatselin {
    display: none;
    position: fixed;
    inset: 0;
    z-index: 1200;
    background: #2b2a28;
    color: #fff;
    flex-direction: column;
}
.dokumenttikatselin.auki { display: flex; }
body.katselin-auki { overflow: hidden; }
.dokumenttikatselin-palkki {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 8px 10px;
    padding-top: calc(8px + env(safe-area-inset-top, 0px));
    background: #1c1b1a;
    flex: none;
}
.dokumenttikatselin-palkki .otsikko { flex: 1; font-size: 14px; font-weight: 600; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* Puhelimella otsikko omalle rivilleen: viisi nappia ja otsikko eivät
   mahdu 360-390 px leveyteen samalle riville (Tallenna lisätty 28.9.2026). */
@media (max-width: 480px) {
    /* Rivi 1: otsikko ja Sulje, rivi 2: zoom ja Tallenna. */
    .dokumenttikatselin-palkki { flex-wrap: wrap; row-gap: 6px; }
    .dokumenttikatselin-palkki .otsikko { flex: 1 1 0; order: 0; }
    .dokumenttikatselin-palkki [data-katselin-sulje] { order: 1; }
    .dokumenttikatselin-palkki button { order: 2; padding: 0 10px; }
    .dokumenttikatselin-palkki [data-katselin-miinus] { margin-left: 0; }
    .dokumenttikatselin-palkki::after { content: ''; order: 1; flex-basis: 100%; }
}
.dokumenttikatselin-palkki button {
    min-width: 44px; min-height: 44px; padding: 0 12px;
    border: 1px solid rgba(255,255,255,0.25); border-radius: 8px;
    background: rgba(255,255,255,0.08); color: #fff; font-size: 15px; cursor: pointer;
}
/* Sivulle upotettu PDF sivukuvina (katselin.js upota, 2.11.2026). */
.pdf-upotus {
    display: flex; flex-direction: column; width: 100%;
    background: var(--gray-100); border-radius: 6px; overflow: hidden;
}
.pdf-upotus-palkki { display: flex; justify-content: flex-end; padding: 6px 8px; background: var(--pinta); border-bottom: 1px solid var(--reuna); }
.pdf-upotus-palkki .btn { min-height: 44px; }
.pdf-upotus-sivut { flex: 1; overflow: auto; -webkit-overflow-scrolling: touch; padding: 8px; color: var(--teksti-toissijainen); font-size: 14px; }
.pdf-upotus-sivut img { display: block; width: 100%; max-width: 900px; margin: 0 auto 8px; background: #fff; box-shadow: 0 1px 3px rgba(0,0,0,.15); }
.pdf-upotus-huom { margin: 0 0 8px; }
.dokumenttikatselin-sisalto {
    flex: 1;
    overflow: auto;
    -webkit-overflow-scrolling: touch;
    padding: 10px;
    padding-bottom: calc(24px + env(safe-area-inset-bottom, 0px));
    --katselin-zoom: 100%;
}
.dokumenttikatselin-sisalto h3 { font-size: 13px; font-weight: 600; color: #d6d5d2; margin: 10px 0 6px; }
.dokumenttikatselin-sisalto img {
    display: block;
    /* 100 % = lukuleveys (enintään 900 px), zoom skaalaa siitä. Aiemmin 100 %
       oli koko ikkunan leveys, jolloin tietokoneella A4-sivu oli valtava
       (Petteri 28.9.2026: "kosteusmittausraportin avaa niin sekin on VITUN
       ISOLLA"). Puhelimella min() valitsee ruudun leveyden kuten ennen. */
    width: calc(min(100%, 900px) * var(--katselin-kerroin, 1));
    max-width: none;
    margin: 0 auto 10px;
    background: #fff;
    box-shadow: 0 2px 10px rgba(0,0,0,0.4);
}
.dokumenttikatselin-sisalto .tila { padding: 20px; text-align: center; color: #d6d5d2; font-size: 14px; }
.dokumenttikatselin-sisalto .tila a { color: #93c5fd; }

/* ---------------------------------------------------------------------
   LISTAN TYÖKALURIVI (yhteinen komponentti, 13.9.2026)
   Yksi rivi listan yläpuolella: suodatin + listaa koskeva toiminto +
   lyhyt vihje. Valintarivi (.listatyokalut-valinta) erikseen: Valitse
   kaikki + joukkotoiminnot, jotka näkyvät vasta valinnasta. Kaikilla
   elementeillä sama korkeus, ei omia kortteja. Ensimmäinen käyttäjä:
   ostolaskut.
   --------------------------------------------------------------------- */
.listatyokalut {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    align-items: center;
    margin: 0 0 10px;
}
.listatyokalut-suodatin {
    display: flex;
    gap: 6px;
    align-items: center;
    flex: 0 1 280px;
    min-width: 0;
    margin: 0;
}
.listatyokalut-suodatin select.form-control { min-height: 36px; padding-top: 6px; padding-bottom: 6px; }
.listatyokalut .btn { min-height: 36px; }
.listatyokalut-vihje {
    flex: 1 1 100%;
    font-size: 12px;
    line-height: 1.45;
    color: var(--gray-500, #73726F);
}
.listatyokalut-vihje.varoitus {
    color: var(--tila-varoitus-teksti, #92400e);
    background: var(--warning-light, #fffbeb);
    border-left: 3px solid var(--warning, #f59e0b);
    border-radius: 6px;
    padding: 8px 10px;
}
.listatyokalut-valinta {
    padding: 6px 0;
    border-top: 1px solid var(--gray-200, #E8E7E4);
    border-bottom: 1px solid var(--gray-200, #E8E7E4);
}
.listatyokalut-valitse {
    display: flex;
    align-items: center;
    gap: 8px;
    font-size: 13px;
    min-height: 36px;
    cursor: pointer;
}
.listatyokalut-valitse input { width: 20px; height: 20px; }
@media (max-width: 768px) {
    .listatyokalut-suodatin { flex: 1 1 100%; }
    .listatyokalut-suodatin select.form-control { min-height: 44px; font-size: 16px; }
    .listatyokalut .btn { min-height: 44px; }
    .listatyokalut-valitse { min-height: 44px; }
}

/* Kortin toimintorivi puhelimella: päätoiminto koko leveydellä, toissijaiset
   tasalevyisinä kahdessa sarakkeessa, kaikki 44 px korkeita. Yksinäinen
   toissijainen venyy koko leveydelle (flex-grow). Valikko on kortin
   oikeassa yläkulmassa (.kortin-valikko), ei toimintorivillä. */
.kortin-valikko { display: inline-block; margin-bottom: 2px; }
.kortin-valikko > summary {
    min-width: 40px; min-height: 32px; padding: 2px 10px;
    font-size: 20px; line-height: 1; letter-spacing: 1px;
}
.kortin-tosite { margin-top: 4px; }
.kortin-tosite .badge { font-size: 11px; }
@media (max-width: 768px) {
    .kortin-toiminnot { gap: 8px; }
    .kortin-toiminnot > .btn,
    .kortin-toiminnot > a,
    .kortin-toiminnot > form { flex: 1 1 calc(50% - 4px); min-width: calc(50% - 4px); }
    /* Pitkä nappiteksti (esim. "Pikatarjous ilman laskentaa") rivittyy
       puolikkaassa ruudukossa sen sijaan että leikkautuisi. */
    .kortin-toiminnot > .btn,
    .kortin-toiminnot > a.btn,
    .kortin-toiminnot > form > .btn { min-height: 44px; justify-content: center; width: 100%; white-space: normal; line-height: 1.25; text-align: center; }
    .kortin-toiminnot > form > .btn { flex: 1 1 auto; }
    .kortin-toiminnot > .btn-success,
    .kortin-toiminnot > .btn-primary,
    .kortin-toiminnot > form:has(> .btn-success),
    .kortin-toiminnot > form:has(> .btn-primary) { flex-basis: 100%; min-width: 100%; }
    .kortin-toiminnot > .badge { flex: 0 0 auto; min-width: 0; }
}

/* ---------------------------------------------------------------------
   VÄLILEHDET (yhteinen komponentti, 13.9.2026). Työpöydällä rivi
   alaviivalla, puhelimella tasainen 2 sarakkeen ruudukko: kaikki
   välilehdet samankokoisia, mikään ei jää irralleen oikealle.
   --------------------------------------------------------------------- */
.valilehdet {
    display: flex;
    gap: 0;
    border-bottom: 2px solid var(--gray-200, #E8E7E4);
    margin-bottom: 14px;
    flex-wrap: wrap;
}
/* Välilehti on linkki (näkymän vaihto) tai nappi (saman sivun vaihe, esim.
   tarjouslomakkeen viisi vaihetta). Sama ulkoasu molemmille. */
.valilehdet a,
.valilehdet button {
    padding: 9px 14px;
    text-decoration: none;
    color: var(--gray-500, #73726F);
    font-size: 13px;
    font-family: inherit;
    font-weight: 500;
    background: none;
    border: 0;
    border-bottom: 2px solid transparent;
    border-radius: 0;
    margin-bottom: -2px;
    display: flex;
    align-items: center;
    gap: 6px;
    cursor: pointer;
    white-space: nowrap;
}
.valilehdet a.active,
.valilehdet button.active { color: var(--primary, #4f46e5); border-bottom-color: var(--primary, #4f46e5); font-weight: 700; }
.valilehdet a:hover:not(.active),
.valilehdet button:hover:not(.active) { color: var(--gray-700, #41403D); }
.valilehdet .count {
    /* Sama keskitys kuin .nav-badge (Petteri 6.11.2026): välilehtien laskuri
       oli tekstirivin varassa (line-height 16,5 px, padding 2 px), joten
       numero istui alhaalla. Nyt flex + kiinteä korkeus. */
    display: inline-flex; align-items: center; justify-content: center;
    box-sizing: border-box; min-width: 20px; height: 20px; line-height: 1;
    background: var(--gray-200, #E8E7E4); color: var(--gray-700, #41403D);
    font-size: 11px; font-weight: 700; padding: 0 6px 0 7px; border-radius: 999px;
}
.valilehdet a.active .count { background: var(--primary, #4f46e5); color: white; }
.valilehdet a.urgent .count { background: var(--danger, #ef4444); color: white; }
.valilehdet .right { margin-left: auto; }
@media (max-width: 768px) {
    .valilehdet {
        display: grid;
        grid-template-columns: 1fr 1fr;
        gap: 6px;
        border-bottom: none;
        margin-bottom: 12px;
    }
    .valilehdet a,
    .valilehdet button {
        min-height: 44px;
        padding: 6px 10px;
        margin: 0;
        font-size: 14px;
        border: 1px solid var(--gray-200, #E8E7E4);
        border-radius: 8px;
        background: var(--pinta, #fff);
        white-space: normal;
        text-align: left;
    }
    .valilehdet a.active,
    .valilehdet button.active { background: var(--primary-light, #eef2ff); border-color: var(--primary, #4f46e5); }
    .valilehdet .right { margin-left: 0; }
    .valilehdet .count { margin-left: auto; }
    /* Muunnelma: vieritettävä palkki (ostolaskut 29.9.2026, vrt. Tunnit).
       Kun välilehtiä on paljon ja lista on sivun pääasia, ruudukko vie
       koko ensimmäisen ruudun. */
    .valilehdet.vierittyva { display: flex; flex-wrap: nowrap; overflow-x: auto; gap: 2px;
        border-bottom: 2px solid var(--gray-200, #E8E7E4); scrollbar-width: none;
        -webkit-overflow-scrolling: touch; }
    .valilehdet.vierittyva::-webkit-scrollbar { display: none; }
    .valilehdet.vierittyva a { flex: 0 0 auto; min-height: 44px; border: 0; border-radius: 0;
        background: none; border-bottom: 2px solid transparent; margin-bottom: -2px;
        white-space: nowrap; padding: 6px 10px; }
    .valilehdet.vierittyva a.active { background: none; border-bottom-color: var(--primary, #4f46e5); }
    .valilehdet.vierittyva .count { margin-left: 4px; }
}

/* ---------------------------------------------------------------------
   KORTIN TEKSTIASTEIKKO (yhteinen, 13.9.2026). Kolme kokoa: summa, meta,
   pienteksti. Templatet eivät kirjoita inline-pikselikokoja.
   --------------------------------------------------------------------- */
.kortin-summa { font-size: 18px; font-weight: 700; line-height: 1.2; color: var(--gray-900, #111827); }
.kortin-meta { font-size: 13px; line-height: 1.45; color: var(--gray-700, #41403D); margin-top: 2px; }
.kortin-pienteksti { font-size: 12px; line-height: 1.4; color: var(--gray-500, #73726F); }
.kortin-meta.tabular, .kortin-summa { font-variant-numeric: tabular-nums; }
.kortin-meta.yliviivattu { color: var(--gray-500, #73726F); text-decoration: line-through; }
.kortin-summa.hyvitys, .kortin-meta.hyvitys, .kortin-pienteksti.hyvitys { color: var(--success, #16a34a); }
.kortin-pienteksti.hyvitys { font-weight: 600; }
.kortin-summa.hyvitetty { color: #065f46; }
.kortin-pienteksti.ok { color: #065f46; }
.kortin-pienteksti.varoitus { color: #b45309; }
.kortin-pienteksti.virhe { color: #b91c1c; font-weight: 700; }
@media (max-width: 768px) {
    .mobile-card-title { font-size: 15px; }
    .mobile-card-sub { font-size: 13px; }
    .kortin-summa { font-size: 20px; }
    .kortin-meta { font-size: 14px; }
    .kortin-pienteksti { font-size: 12px; }
}

/* Otsikkorivin toiminnot (yhteinen): päätoiminto + valikko + ohje rinnakkaan,
   ei kortin toimintorivin ruudukkoa. */
.otsikon-toiminnot { display: flex; gap: 6px; flex-wrap: wrap; align-items: center; }

/* TYÖKALURIVI (yhteinen, 16.9.2026): kalenterin ja aikataulun ohjaimet
   (edellinen/tänään/seuraava, näkymä, zoom) yhdellä rivittyvällä rivillä.
   Puhelimella napit ja kentät kosketuskokoon, tekstierottimet pois.
   Inventaario 390 px: aikataulun 13 pientä nappia ja "|"-merkit. */
.tyokalurivi { display: flex; gap: 6px; flex-wrap: wrap; align-items: center; font-size: 13px; }
.tyokalurivi form { margin: 0; display: flex; gap: 4px; align-items: center; flex-wrap: wrap; }
.tyokalurivi .erotin { color: var(--gray-400, #A8A7A3); margin: 0 4px; }
@media (max-width: 768px) {
    .tyokalurivi .btn, .tyokalurivi .gantt-zoom-btn, .tyokalurivi summary { min-height: 40px !important; min-width: 40px; }
    .tyokalurivi .form-control { min-height: 40px; height: auto !important; font-size: 14px !important; }
    .tyokalurivi .erotin { display: none; }
}
/* Lukutaulukon kortissa kenttä otsikon viereen (inventaarion laskettu-sarake). */
@media (max-width: 768px) {
    .taulukko-korteiksi td > .form-control { flex: 1 1 120px; width: auto; min-width: 0; }
}
@media (max-width: 768px) {
    .otsikon-toiminnot .btn { min-height: 44px; }
    .kortin-oikea { flex: 0 0 auto; }
    /* Yksi kosketuskoko kortin valikolle: 44 x 44 px. */
    .kortin-valikko > summary { min-height: 44px; min-width: 44px; padding: 0 10px; font-size: 18px; letter-spacing: 0; }
}
@media (max-width: 768px) {
    .kortin-oikea { max-width: 56%; }
    .kortin-oikea .kortin-pienteksti { white-space: nowrap; }
    .mobile-card > .d-flex.justify-between { align-items: flex-start; }
}

/* ---------------------------------------------------------------------
   SUODATINPILLIT (yhteinen komponentti, 14.9.2026). Listan tilasuodatin
   yhtenä rivinä: työpöydällä rivittyy, puhelimella vieritettävä rivi ilman
   sivun vaakavieritystä. Aktiivinen pilli täytetty.
   --------------------------------------------------------------------- */
.suodatinpillit {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    margin: 0 0 10px;
}
.suodatinpillit a {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    min-height: 36px;
    padding: 6px 14px;
    border: 1px solid var(--gray-300, #D6D5D2);
    border-radius: 999px;
    background: var(--pinta, #fff);
    color: var(--gray-700, #41403D);
    font-size: 13px;
    font-weight: 500;
    text-decoration: none;
    white-space: nowrap;
}
.suodatinpillit a:hover { background: var(--gray-100, #F5F4F1); }
.suodatinpillit a.active {
    background: var(--primary, #4f46e5);
    border-color: var(--primary, #4f46e5);
    color: var(--primary-ink, #fff);
    font-weight: 600;
}
@media (max-width: 768px) {
    .suodatinpillit {
        flex-wrap: nowrap;
        overflow-x: auto;
        -webkit-overflow-scrolling: touch;
        scrollbar-width: none;
        margin-left: -16px;
        margin-right: -16px;
        padding: 2px 16px 6px;
    }
    .suodatinpillit::-webkit-scrollbar { display: none; }
    .suodatinpillit a { min-height: 40px; font-size: 14px; }
    .listatyokalut-suodatin input.form-control { min-height: 44px; font-size: 16px; }
}

/* Maksukeskuksen taulukot puhelimella lohkoina, ei vaakavieritystä.
   Kaksi eri rakennetta, kaksi luokkaa (ChatGPT 14.9.2026): .mk-erat on
   6-sarakkeinen maksuerätaulukko, .mk-lisatyot 3-sarakkeinen lisätyötaulukko.
   nth-child-säännöt eivät saa vuotaa toiseen. */
@media (max-width: 760px) {
    .mk-taulu.mk-erat, .mk-taulu.mk-lisatyot { min-width: 0; width: 100%; display: block; }
    .mk-taulu.mk-erat thead, .mk-taulu.mk-lisatyot thead { display: none; }
    .mk-taulu.mk-erat tbody, .mk-taulu.mk-lisatyot tbody { display: block; width: 100%; }
    .mk-taulu.mk-erat td, .mk-taulu.mk-lisatyot td { display: block; border: none !important; padding: 0 !important; min-width: 0; }

    .mk-taulu.mk-erat tr {
        display: grid;
        grid-template-columns: 24px minmax(0, 1fr) auto;
        grid-template-areas: "nro selite summa" "nro tila tila";
        gap: 4px 8px;
        padding: 10px 0;
        border-bottom: 1px solid var(--gray-200, #E8E7E4);
    }
    /* Rastisarake poistui 14.10.2026, napit ovat rivillä tilan alla. */
    .mk-taulu.mk-erat td:nth-child(1) { grid-area: nro; }
    .mk-taulu.mk-erat td:nth-child(2) { grid-area: selite; font-weight: 600; overflow-wrap: anywhere; }
    .mk-taulu.mk-erat td:nth-child(3) { grid-area: summa; text-align: right; font-weight: 700; white-space: nowrap; }
    .mk-taulu.mk-erat td:nth-child(4) { display: none; }
    .mk-taulu.mk-erat td:nth-child(5) { grid-area: tila; }
    .mk-taulu.mk-erat .mk-vihje { font-weight: 400; }

    .mk-taulu.mk-lisatyot tr {
        display: grid;
        grid-template-columns: minmax(0, 1fr) auto;
        grid-template-areas: "tyo summa" "tila tila";
        gap: 4px 8px;
        padding: 10px 0;
        border-bottom: 1px solid var(--gray-200, #E8E7E4);
    }
    .mk-taulu.mk-lisatyot td:nth-child(1) { grid-area: tyo; overflow-wrap: anywhere; }
    .mk-taulu.mk-lisatyot td:nth-child(2) { grid-area: summa; text-align: right; font-weight: 700; white-space: nowrap; }
    .mk-taulu.mk-lisatyot td:nth-child(3) { grid-area: tila; }
}

/* ---------------------------------------------------------------------
   PIKALINKIT (yhteinen, 14.9.2026): kortin toimintolinkit tasaisena
   ruudukkona, ei eri levyisinä pillinä. Puhelimella kaksi saraketta.
   --------------------------------------------------------------------- */
.pikalinkit {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
    gap: 6px;
}
/* Nimi saa rivittyä kahdelle riville, ei leikkaudu (ChatGPT 15.9.2026:
   Muutostyötarjous ja Dokumenttipohjat katkesivat 390 px:llä). Kuvake ei
   kutistu; yhden rivin painikkeet pysyvät yhtä korkeina ruudukon ansiosta. */
.pikalinkit > .btn { justify-content: flex-start; text-align: left; min-height: 38px; white-space: normal; hyphens: auto; overflow-wrap: anywhere; line-height: 1.25; padding-top: 6px; padding-bottom: 6px; }
.pikalinkit > .btn > svg { flex: 0 0 auto; }
@media (max-width: 768px) {
    .pikalinkit { grid-template-columns: repeat(2, minmax(0, 1fr)); }
    /* 390 px: sarake on 160 px, ja pisin yksisanainen nimi (Muutostyötarjous,
       Dokumenttipohjat, Käyntikirjaukset) mahtuu yhdelle riville vain kun
       reunus on 8 px, kuvakkeen väli 4 px ja fontti 13,5 px. */
    .pikalinkit > .btn { min-height: 44px; font-size: 13.5px; gap: 4px; padding-left: 8px; padding-right: 8px; }
}
/* Kapeimmat puhelimet (320 px): yksi sarake, jotta pitkät nimet eivät
   katkea sanan sisältä. */
@media (max-width: 340px) {
    .pikalinkit { grid-template-columns: minmax(0, 1fr); }
}

/* KELLON ALOITUSLOMAKE (yhteinen include timesheets/_kello_aloitus.html):
   työpöydällä kentät rivissä, puhelimella pinossa täysleveinä. */
.kello-lomake { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; }
.kello-lomake > select, .kello-lomake > input[type="text"] { flex: 1 1 200px; max-width: 260px; min-width: 0; }
.kello-lomake > .btn { white-space: nowrap; }
@media (max-width: 768px) {
    .kello-lomake > select, .kello-lomake > input[type="text"] { flex-basis: 100%; max-width: none; min-height: 44px; font-size: 16px; }
    .kello-lomake > .btn { width: 100%; min-height: 44px; justify-content: center; }
}

/* RIVILOMAKE (yhteinen, 14.9.2026): yksi kenttä + yksi nappi samalla
   rivillä, ei koskaan rivity (Muistilista, Työntekijät). */
.rivilomake { display: flex; gap: 6px; align-items: center; flex-wrap: nowrap; }
.rivilomake > .form-control { flex: 1 1 auto; min-width: 0; }
.rivilomake > .btn { flex: 0 0 auto; white-space: nowrap; }
@media (max-width: 768px) {
    .rivilomake > .form-control, .rivilomake > .btn { min-height: 44px; }
}

/* LISÄOSIO (yhteinen, 14.9.2026): kortin sisällä oleva harvoin käytetty
   lomake (lisää pöytäkirja, muokkaa päivämääriä, lisää vakuus). Avaaja
   näyttää samalta kuin muut toissijaiset napit, ei oranssilta linkiltä. */
details.lisaosio { margin-top: 8px; }
details.lisaosio > summary {
    display: inline-flex; align-items: center; gap: 6px;
    min-height: 36px; padding: 6px 12px;
    border: 1px solid var(--gray-300, #D3D1CC); border-radius: 6px;
    background: var(--white, #fff); color: var(--gray-800, #2F2E2B);
    font-size: 13px; font-weight: 600; cursor: pointer; list-style: none;
}
details.lisaosio > summary::-webkit-details-marker { display: none; }
details.lisaosio > summary::marker { content: ''; }
details.lisaosio[open] > summary { background: var(--gray-100, #F3F2EF); }
details.lisaosio > form { margin-top: 8px; }
@media (max-width: 768px) {
    details.lisaosio > summary { min-height: 44px; font-size: 14px; }
}

/* KORTTIOSIO (yhteinen, 14.9.2026): harvoin käytetty hallintolomake
   suljettuna korttina; avautuu otsikosta ja on auki kun sisältöä on. */
details.kortti-osio > summary {
    cursor: pointer;
    list-style: none;
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 8px;
}
details.kortti-osio > summary::-webkit-details-marker { display: none; }
details.kortti-osio > summary::after { content: '\25BE'; color: var(--gray-500, #73726F); font-size: 14px; }
details.kortti-osio[open] > summary::after { content: '\25B4'; }
details.kortti-osio:not([open]) > summary { margin-bottom: 0 !important; }

/* ── Sivun sisäiset välilehdet (työkohde 30.9.2026) ─────────────────────
   Sama ulkoasu kuin Tuntien välilehdillä, mutta sivun sisällä: kaikki osiot
   renderöidään, JS näyttää yhden. Ilman JS:ää osiot näkyvät allekkain.
   Vain teeman muuttujia, jotta käyttäjän väri ja tumma tila toimivat. */
.sivun-lehdet {
    display: flex;
    gap: 2px;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    border-bottom: 1px solid var(--gray-200);
    margin: 4px 0 16px;
    scrollbar-width: none;
}
.sivun-lehdet::-webkit-scrollbar { display: none; }
.sivun-lehdet button {
    flex: 0 0 auto;
    min-height: 44px;
    padding: 9px 14px;
    border: 0;
    border-bottom: 2px solid transparent;
    margin-bottom: -1px;
    background: none;
    font: inherit;
    font-size: 14px;
    font-weight: 500;
    color: var(--teksti-toissijainen);
    cursor: pointer;
    white-space: nowrap;
}
.sivun-lehdet button:hover { color: var(--teksti); }
/* Painallus näkyy heti, ennen kuin välilehti vaihtuu (19.10.2026). */
.sivun-lehdet button:active { background: var(--pinta-korostettu); color: var(--teksti); }

/* iPhone odottaa napautuksen jälkeen ~0,3 s mahdollista kaksoisnapautusta
   (suurennus). Napeissa ja linkeissä se tuntui hitautena, esim. työkohteen
   välilehdissä (19.10.2026). Sormilla suurentaminen toimii edelleen. */
a, button, summary, label, select, input, textarea, [role="tab"], [role="button"] {
    touch-action: manipulation;
}
.sivun-lehdet button.auki {
    color: var(--primary);
    border-bottom-color: var(--primary);
    font-weight: 600;
}
.lehdet-js .sivun-lehti:not(.auki) { display: none; }
@media (max-width: 420px) {
    .sivun-lehdet button { padding: 9px 10px; font-size: 13px; }
}

/* ── Työkohteen yläosa, tilanneruudut ja huomiot (30.9.2026) ──────────── */
.tk-otsikko { align-items: flex-start; gap: 12px; }
.tk-otsikko-teksti { display: flex; flex-direction: column; gap: 6px; min-width: 0; }
.tk-takaisin { font-size: 13px; color: var(--teksti-toissijainen); text-decoration: none; }
.tk-takaisin:hover { color: var(--teksti); }
.tk-otsikko .page-title { margin: 0; }
.tk-viite {
    display: inline-block; margin-left: 6px; padding: 2px 8px; border-radius: var(--radius-sm);
    background: var(--primary-light); color: var(--primary); font: 700 14px 'JetBrains Mono', monospace;
    vertical-align: middle;
}
.tk-merkit { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; }
.tk-meta { font-size: 14px; color: var(--teksti-toissijainen); line-height: 1.5; overflow-wrap: anywhere; }
.tk-tila { display: flex; flex-wrap: wrap; gap: 8px; align-items: flex-start; margin: 0 0 12px; }
.tk-tila:empty { display: none; }
.tk-ruudut {
    display: grid; grid-template-columns: repeat(4, minmax(0, 1fr));
    background: var(--pinta); border: 1px solid var(--gray-200); border-radius: var(--radius);
    overflow: hidden; margin-bottom: 12px; box-shadow: var(--shadow-xs);
}
.tk-ruutu {
    display: flex; flex-direction: column; gap: 2px; min-width: 0; text-align: left;
    padding: 14px 18px; border: 0; border-right: 1px solid var(--gray-100); background: none;
    font: inherit; color: var(--teksti); cursor: pointer;
}
.tk-ruutu:last-child { border-right: 0; }
.tk-ruutu:hover { background: var(--pinta-korostettu); }
.tk-ruutu-nimi { font-size: 12px; font-weight: 500; color: var(--teksti-toissijainen); }
.tk-ruutu-luku { font-size: 22px; font-weight: 700; letter-spacing: -.3px; font-variant-numeric: tabular-nums; }
.tk-ruutu-selite { font-size: 12px; color: var(--teksti-toissijainen); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tk-korostus { color: var(--primary); }
.tk-heikko { color: var(--tila-virhe-teksti); }
.tk-huomiot { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 12px; }
.tk-huomio {
    display: inline-flex; align-items: center; min-height: 36px; padding: 0 12px;
    border-radius: var(--radius-sm); font: inherit; font-size: 13px; font-weight: 500;
    cursor: pointer; text-decoration: none; border: 1px solid transparent;
}
.tk-huomio-varoitus { background: var(--tila-varoitus-tausta); color: var(--tila-varoitus-teksti); border-color: var(--tila-varoitus-reuna); }
.tk-huomio-tieto { background: var(--tila-tieto-tausta); color: var(--tila-tieto-teksti); }
.tk-huomio:hover { text-decoration: none; filter: brightness(.97); }
@media (max-width: 768px) {
    .tk-ruudut { grid-template-columns: repeat(2, minmax(0, 1fr)); }
    .tk-ruutu:nth-child(2n) { border-right: 0; }
    .tk-ruutu:nth-child(-n+2) { border-bottom: 1px solid var(--gray-100); }
    .tk-ruutu-luku { font-size: 19px; }
    .tk-huomio { min-height: 44px; width: 100%; }
}

/* ── Uusi päävalikko, kokeilu (1.10.2026, app/shared/navigaatio.py) ────────
   Tiheä (32 px rivit pöytäkoneella), ei varjoja, korostusväri vain valitussa.
   Alasivut: pöytäkoneella hiiren alle sivuun, puhelimella napautuksesta alle. */
.sidebar:has(.vu-valikko) { display: flex; flex-direction: column; overflow: visible; }
.vu-haku { display: flex; align-items: center; gap: 8px; width: calc(100% - 8px); height: 32px; margin: 0 4px 8px; padding: 0 8px;
    border: 1px solid var(--gray-300); border-radius: var(--radius-sm); background: var(--pinta); color: var(--teksti-toissijainen);
    font: inherit; font-size: 13px; cursor: pointer; text-align: left; }
.vu-haku span { flex: 1; }
.vu-haku kbd { font: inherit; font-size: 11px; border: 1px solid var(--gray-200); border-radius: 4px; padding: 0 4px; background: transparent; color: var(--teksti-toissijainen); }
.vu-valikko { display: flex; flex-direction: column; gap: 1px; }
.vu-kohta { position: relative; display: flex; align-items: center; }
.vu-paa { flex: 1 1 0; min-width: 0; display: flex; align-items: center; gap: 8px; height: 32px; padding: 0 8px; margin: 0 4px; border: 1px solid transparent;
    border-radius: var(--radius-sm); color: var(--teksti); text-decoration: none; font-size: 13px; font-weight: 500; }
.vu-paa:hover { background: var(--pinta-korostettu); text-decoration: none; }
.vu-paa .ikoni { width: 16px; height: 16px; opacity: .7; }
.vu-nimi { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.vu-kohta.aktiivinen > .vu-paa { background: var(--pinta); border-color: var(--gray-200); color: var(--primary); font-weight: 600; }
.vu-kohta.aktiivinen > .vu-paa .ikoni { opacity: 1; }
.vu-avaa { display: none; }
.vu-alas { display: none; position: absolute; left: calc(100% - 4px); top: -7px; z-index: 1001; min-width: 210px; padding: 6px;
    background: var(--pinta); border: 1px solid var(--gray-200); border-radius: var(--radius); flex-direction: column; }
.vu-alas-ylos { top: auto; bottom: -7px; }
/* Otsikkoa ei näytetä: valikko aukeaa pääkohdan viereen, joten nimi toistui ja
   työnsi ensimmäisen osion pykälän alemmas kuin pääkohta (Petteri 19.10.2026).
   Ruudunlukija saa nimen valikon aria-labelista. */
.vu-alas-otsikko { display: none; }
.vu-alas a { display: flex; align-items: center; gap: 8px; justify-content: space-between; height: 32px; padding: 0 8px;
    border-radius: var(--radius-sm); color: var(--teksti); text-decoration: none; font-size: 13px; }
.vu-alas a:hover { background: var(--pinta-korostettu); }
.vu-alas a.aktiivinen { color: var(--primary); font-weight: 600; }
.vu-kohta:hover > .vu-alas, .vu-kohta:focus-within > .vu-alas, .vu-kohta.auki > .vu-alas { display: flex; }
.vu-ala { margin-top: auto; display: flex; flex-direction: column; gap: 1px; padding-top: 8px; border-top: 1px solid var(--gray-200); }
.vu-kytkin { margin: 4px; }
.vu-kytkin button, .vu-kokeile button { width: 100%; height: 32px; border: 1px dashed var(--gray-300); border-radius: var(--radius-sm);
    background: none; color: var(--teksti-toissijainen); font: inherit; font-size: 12px; cursor: pointer; }
.vu-kokeile { margin: 12px 8px 0; }
.vu-lehdet { display: flex; gap: 2px; overflow-x: auto; scrollbar-width: none; margin: 0 0 14px; border-bottom: 1px solid var(--gray-200); }
.vu-lehdet::-webkit-scrollbar { display: none; }
.vu-lehdet a { flex: 0 0 auto; display: inline-flex; align-items: center; gap: 6px; min-height: 40px; padding: 0 10px; margin-bottom: -1px;
    border-bottom: 2px solid transparent; color: var(--teksti-toissijainen); font-size: 13px; font-weight: 500; text-decoration: none; white-space: nowrap; }
.vu-lehdet a:hover { color: var(--teksti); text-decoration: none; }
.vu-lehdet a.aktiivinen { color: var(--teksti); border-bottom-color: var(--primary); font-weight: 600; }
.vu-hakuikkuna { width: min(560px, calc(100vw - 32px)); padding: 0; border: 1px solid var(--gray-300); border-radius: var(--radius);
    background: var(--pinta); color: var(--teksti); margin-top: 12vh; }
.vu-hakuikkuna::backdrop { background: rgba(20, 20, 19, .3); }
.vu-hakuikkuna input { width: 100%; height: 48px; border: 0; border-bottom: 1px solid var(--gray-200); padding: 0 16px; font: inherit;
    font-size: 15px; background: transparent; color: var(--teksti); outline: none; box-sizing: border-box; }
.vu-hakuikkuna ul { list-style: none; margin: 0; padding: 6px; max-height: 50vh; overflow-y: auto; }
.vu-hakuikkuna li a { display: flex; justify-content: space-between; gap: 12px; height: 36px; align-items: center; padding: 0 10px;
    border-radius: var(--radius-sm); color: var(--teksti); text-decoration: none; font-size: 14px; }
.vu-hakuikkuna li a.valittu, .vu-hakuikkuna li a:hover { background: var(--primary-light); }
.vu-osio { color: var(--teksti-toissijainen); font-size: 12px; }
.vu-ei { padding: 10px; color: var(--teksti-toissijainen); }
.vu-hakuohje { padding: 8px 16px; border-top: 1px solid var(--gray-200); font-size: 12px; color: var(--teksti-toissijainen); }
@media (max-width: 1023px), (pointer: coarse) {
    .sidebar:has(.vu-valikko) { overflow-y: auto; }
    .vu-kohta { flex-wrap: wrap; }
    .vu-paa { height: auto; min-height: 44px; font-size: 15px; }
    .vu-nimi { white-space: normal; }
    .vu-avaa { display: inline-flex; align-items: center; justify-content: center; width: 44px; height: 44px; border: 0; background: none;
        color: var(--teksti-toissijainen); cursor: pointer; }
    .vu-kohta.auki > .vu-avaa .ikoni { transform: rotate(180deg); }
    .vu-kohta:hover > .vu-alas, .vu-kohta:focus-within > .vu-alas { display: none; }
    .vu-alas, .vu-alas-ylos { position: static; flex-basis: 100%; width: 100%; min-width: 0; border: 0; padding: 0 0 6px 28px; background: none; }
    .vu-kohta.auki > .vu-alas { display: flex; }
    .vu-alas-otsikko { display: none; }
    .vu-alas a { height: 44px; font-size: 15px; }
    .vu-lehdet a { min-height: 44px; font-size: 14px; }
    .vu-kytkin button, .vu-kokeile button { height: 44px; font-size: 13px; }
    .vu-haku { height: 44px; font-size: 15px; }
    .vu-haku kbd { display: none; }
}
/* Puhelimella pääkohdan nimi mahtuu yhdelle riville avausnuolen viereen. */
@media (max-width: 1023px) {
    .sidebar:has(.vu-valikko) { width: 288px; left: -292px; }
    .sidebar.open:has(.vu-valikko) { left: 0; }
}

/* ── Tarjousten ja tarjouspyyntöjen pipeline (30.9.2026) ─────────────────
   Sivu ei rullaa pitkäksi: jokainen sarake vierii omana laatikkonaan, Ohi on
   kapea palkki oikealla. Puhelimella yksi vaihe kerrallaan (.tp-vaiheet). */
.tp-haku { width: min(260px, 100%); height: 36px; background: var(--pinta); color: var(--teksti); border-color: var(--gray-300); }
@media (min-width: 761px) { .otsikon-toiminnot:has(.tp-haku) { flex-wrap: nowrap; } }
.tp-jakso { display: flex; flex-wrap: wrap; align-items: center; gap: 4px 12px; margin-bottom: 10px; }
.tp-jakso .suodatinpillit { margin: 0; }
.tp-pieni { font-size: 12px; color: var(--teksti-toissijainen); }
.tp-katkaise { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tp-varoitus { color: var(--tila-varoitus-teksti); }
.tk-ruutu[aria-pressed="true"] { background: var(--primary-light); box-shadow: inset 0 -2px 0 var(--primary); }
.tk-ruutu:disabled { cursor: default; }
a.tk-ruutu { text-decoration: none; }
.tp-arkistoi { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin-bottom: 10px; }
.tp-arkistoi > span { flex: 1; min-width: 220px; }
.tp-vaiheet { display: none; }
.tp-putki-alue { overflow-x: auto; }
.tp-putki { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)) 150px; gap: 12px; align-items: start; }
.tp-putki.ohi-auki { grid-template-columns: repeat(4, minmax(0, 1fr)); }
.tp-putki.tp-5 { grid-template-columns: repeat(5, minmax(0, 1fr)) 120px; min-width: 1020px; }
.tp-putki.tp-5.ohi-auki { grid-template-columns: repeat(6, minmax(0, 1fr)); min-width: 1200px; }
.tp-putki.js .tp-sarake[data-sarake-ohi] { display: none; }
.tp-putki.js.ohi-auki .tp-sarake[data-sarake-ohi] { display: flex; }
.tp-putki:not(.js) .tp-taitettu, .tp-putki.ohi-auki .tp-taitettu, .tp-putki:not(.js) .tp-sulje { display: none; }
.tp-sarake { display: flex; flex-direction: column; min-width: 0; background: var(--pinta-korostettu); border: 1px solid var(--gray-200); border-radius: var(--radius); }
.tp-sarake-yla { display: flex; align-items: baseline; gap: 8px; padding: 12px 12px 8px; }
.tp-sarake-yla h2 { margin: 0; font-size: 14px; font-weight: 700; }
.tp-sulje { margin-left: auto; }
.tp-sarake-lista { display: flex; flex-direction: column; gap: 8px; padding: 0 8px 8px; overflow-y: auto; max-height: max(320px, calc(100vh - 360px)); }
.tp-sarake-ala { margin: 0; padding: 0 12px 10px; }
.tp-tyhja { margin: 0; padding: 18px 0; text-align: center; font-size: 12px; color: var(--teksti-toissijainen); }
.tp-kortti { display: flex; flex-direction: column; gap: 3px; padding: 10px 12px; background: var(--pinta); border: 1px solid var(--gray-200); border-radius: var(--radius-sm); font-size: 13px; }
.tp-kortti:hover { border-color: var(--primary); }
.tp-kortti[hidden] { display: none; }
.tp-kortti-nimi { font-weight: 600; font-size: 14px; color: var(--teksti); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; text-decoration: none; }
.tp-kortti-nimi:hover { color: var(--primary); }
.tp-kortti-ala { display: flex; justify-content: space-between; align-items: center; gap: 8px; }
.tp-summa { font-variant-numeric: tabular-nums; }
.tp-siru { display: inline-block; padding: 2px 8px; border-radius: 999px; font-size: 11px; font-weight: 600; white-space: nowrap; background: var(--pinta-korostettu); color: var(--teksti-toissijainen); }
.tp-siru-varoitus { background: var(--tila-varoitus-tausta); color: var(--tila-varoitus-teksti); }
.tp-siru-ok { background: var(--tila-ok-tausta); color: var(--tila-ok-teksti); }
.tp-siru-virhe { background: var(--tila-virhe-tausta); color: var(--tila-virhe-teksti); }
.tp-siru-tieto { background: var(--tila-tieto-tausta); color: var(--tila-tieto-teksti); }
.tp-kortti-linkit { display: flex; gap: 14px; margin-top: 4px; padding-top: 6px; border-top: 1px solid var(--gray-100); font-size: 12px; font-weight: 500; }
.tp-kortti-lomake { margin: 2px 0 0; }
.tp-kortti-toiminnot { display: flex; flex-wrap: wrap; gap: 4px 14px; }
.tp-linkkinappi { background: none; border: 0; padding: 0; font: inherit; font-size: 12px; color: var(--teksti-toissijainen); cursor: pointer; text-decoration: underline; text-underline-offset: 2px; }
.tp-linkkinappi:hover { color: var(--teksti); }
.tp-taitettu { display: flex; flex-direction: column; align-items: flex-start; gap: 4px; padding: 12px; text-align: left; border: 1px solid var(--gray-200); border-radius: var(--radius); background: var(--pinta-korostettu); color: var(--teksti-toissijainen); font: inherit; cursor: pointer; }
.tp-taitettu:hover { background: var(--gray-100); color: var(--teksti); }
.tp-taitettu-luku { font-size: 16px; font-weight: 700; color: var(--teksti); }
/* Teksti vaakaan: pystyyn käännettyä ei lue kukaan (Petteri 30.9.2026). */
.tp-taitettu-nimi { font-size: 14px; font-weight: 700; color: var(--teksti); }
.tp-taitettu-nayta { margin-top: 6px; font-size: 13px; font-weight: 500; color: var(--primary); }
@media (max-width: 760px) {
    .tp-haku { width: 100%; height: 44px; order: 3; }
    .tp-jakso { margin-bottom: 4px; }
    .tp-jakso > .tp-pieni { display: none; }
    .tp-vaiheet { display: flex; gap: 8px; overflow-x: auto; margin-bottom: 10px; scrollbar-width: none; }
    .tp-vaiheet::-webkit-scrollbar { display: none; }
    .tp-vaiheet button { flex: 0 0 auto; min-height: 44px; padding: 0 14px; border-radius: 999px; border: 1px solid var(--gray-300); background: var(--pinta); color: var(--teksti-toissijainen); font: inherit; font-size: 14px; cursor: pointer; }
    .tp-vaiheet button[aria-pressed="true"] { background: var(--primary); border-color: var(--primary); color: var(--primary-ink, #fff); }
    .tp-putki, .tp-putki.ohi-auki, .tp-putki.tp-5, .tp-putki.tp-5.ohi-auki { display: flex; flex-direction: column; align-items: stretch; min-width: 0; }
    .tp-putki.js .tp-sarake:not(.valittu), .tp-putki.js.ohi-auki .tp-sarake[data-sarake-ohi]:not(.valittu) { display: none; }
    .tp-putki.js .tp-sarake.valittu { display: flex; }
    .tp-putki .tp-taitettu, .tp-putki .tp-sulje { display: none; }
    .tp-sarake { background: none; border: 0; }
    .tp-sarake-yla { padding: 0 0 8px; }
    .tp-sarake-lista { max-height: none; overflow: visible; padding: 0; }
    .tp-kortti { padding: 12px 14px; }
    .tp-kortti-linkit a, .tp-linkkinappi { min-height: 44px; display: inline-flex; align-items: center; }
}

/* ── Työkohteen Kirjaukset-välilehti (30.9.2026) ───────────────────────── */
.tk-kirjaukset { display: grid; grid-template-columns: minmax(0, 1fr); gap: 16px; align-items: start; }
.tk-kirjaukset:has(> .tk-laskutettavaa) { grid-template-columns: minmax(0, 1fr) 300px; }
.tk-kirjaukset-kortit { min-width: 0; }
.tk-kortin-toiminnot { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; justify-content: flex-end; }
.tk-pieni { font-size: 12px; color: var(--teksti-toissijainen); margin: 4px 0; }
.tk-nowrap { white-space: nowrap; }
.tk-taulu { width: 100%; border-collapse: collapse; font-size: 13px; }
.tk-taulu th { text-align: left; font-size: 11px; font-weight: 600; text-transform: uppercase; letter-spacing: .3px;
    color: var(--teksti-toissijainen); padding: 6px 8px; border-bottom: 1px solid var(--gray-200); }
.tk-taulu td { padding: 8px; border-bottom: 1px solid var(--gray-100); vertical-align: middle; }
.tk-taulu .oik { text-align: right; }
.tk-osat { margin: -8px 0 16px; }
.tk-osat a { min-height: 44px; display: inline-flex; align-items: center; }
.tk-laskutetut { margin-top: 10px; font-size: 13px; }
.tk-laskutetut > summary { cursor: pointer; color: var(--teksti-toissijainen); padding: 6px 0; }
.tk-rivilomake { display: inline-flex; gap: 6px; align-items: center; margin: 0; flex-wrap: wrap; justify-content: flex-end; }
.tk-rivilomake .form-control { width: auto; min-width: 110px; max-width: 180px; height: 34px; padding: 4px 8px; font-size: 13px; }
.tk-puuttuu { border-color: var(--tila-varoitus-reuna); background: var(--tila-varoitus-tausta); }
.tk-puuttuu-teksti { color: var(--tila-virhe-teksti); font-weight: 600; }
.tk-lisa > summary { list-style: none; }
.tk-lisa > summary::-webkit-details-marker { display: none; }
.tk-lisa-lomake { padding: 14px !important; width: 300px; display: flex; flex-direction: column; gap: 8px; }
.tk-lisa-rivi { display: flex; align-items: center; gap: 6px; font-size: 13px; }
.tk-laskutettavaa { position: sticky; top: 12px; display: flex; flex-direction: column; gap: 8px; margin-bottom: 0; }
.tk-summarivi { display: flex; justify-content: space-between; gap: 12px; font-size: 14px; font-variant-numeric: tabular-nums; }
.tk-summarivi-yht { font-weight: 700; font-size: 16px; border-top: 1px solid var(--gray-200); padding-top: 8px; }
.tk-laskutettavaa .btn-link { background: none; border: 0; color: var(--primary); cursor: pointer; font: inherit; font-size: 13px; }
@media (max-width: 1100px) {
    .tk-kirjaukset:has(> .tk-laskutettavaa) { grid-template-columns: minmax(0, 1fr); }
    .tk-laskutettavaa { position: static; }
}
@media (max-width: 768px) {
    .tk-rivilomake { width: 100%; }
    .tk-rivilomake .form-control { flex: 1; max-width: none; height: 44px; font-size: 15px; }
    .tk-rivilomake .btn { min-height: 44px; }
    .tk-lisa-lomake { width: min(300px, 86vw); }
}

/* Dokumentin lähetys (1.10.2026) */
.lah-lomake { max-width: 760px; }
.lah-liite { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 10px 12px; margin-bottom: 16px; border: 1px solid var(--reuna, var(--gray-200)); border-radius: 8px; background: var(--pinta-korostettu); }
.lah-liite-nimi { font-weight: 600; overflow-wrap: anywhere; }
.lah-varoitus { padding: 10px 12px; margin-bottom: 16px; border-radius: 8px; background: var(--tila-varoitus-tausta); color: var(--tila-varoitus-teksti); }
.lah-vastaanottajat { display: grid; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); gap: 8px; margin-bottom: 8px; }
.lah-rasti { display: flex; align-items: flex-start; gap: 10px; min-height: 44px; padding: 8px 10px; border: 1px solid var(--reuna, var(--gray-200)); border-radius: 8px; cursor: pointer; overflow-wrap: anywhere; }
.lah-rasti input { margin-top: 3px; width: 18px; height: 18px; flex: 0 0 auto; }
.lah-varoitus .lah-rasti { border-color: currentColor; margin-top: 8px; }
.lah-pieni { font-size: 13px; color: var(--teksti-toissijainen); }
.lah-napit { display: flex; justify-content: flex-end; gap: 8px; flex-wrap: wrap; }
.lah-napit .btn { min-height: 44px; min-width: 120px; }
.osapuoli-lomake { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)) auto; gap: 8px; align-items: end; margin-top: 12px; }
@media (max-width: 700px) { .osapuoli-lomake { grid-template-columns: 1fr; } }

/* Kosteusmittauskalusto (1.10.2026) */
.kal-huomio { padding: 10px 12px; margin-bottom: 12px; border-radius: 8px; background: var(--tila-varoitus-tausta); color: var(--tila-varoitus-teksti); }
.kal-massa { display: flex; flex-wrap: wrap; align-items: center; gap: 10px 16px; margin-bottom: 12px; }
.kal-rasti, .kal-pvm { display: inline-flex; align-items: center; gap: 8px; min-height: 44px; }
.kal-pvm .form-control { width: auto; }
.kal-pieni { font-size: 13px; color: var(--teksti-toissijainen); }
.kal-taulu .kal-valinta { width: 44px; }
.kal-taulu input[type=checkbox], .kal-rasti input { width: 20px; height: 20px; }
.kal-taulu td, .kal-taulu th { vertical-align: middle; }
@media (max-width: 768px) {
    .taulukko-korteiksi.kal-taulu td.kal-valinta { display: flex; justify-content: flex-start; text-align: left; min-height: 44px; align-items: center; }
    .taulukko-korteiksi.kal-taulu td.kal-valinta::before { display: none; }
    .kal-massa .btn { width: 100%; min-height: 44px; }
}

/* Työkohteen tunnit: rooli riveille (1.10.2026) */
/* Työkohteen tunnit: laskutettavan roolin vaihto rivin alla (1.10./3.10.2026) */
.tunti-rooli { display: flex; gap: 6px; margin: 4px 0 2px; max-width: 340px; }
.tunti-rooli .form-control { height: 32px; min-height: 0; padding: 4px 8px; font-size: 13px; }
.tunti-rooli .btn { height: 32px; }
.tr-rivi.tr-rivi-tekija { grid-template-columns: 0 minmax(0, 1fr) auto; column-gap: 0; }
.tr-rivi-tekija .tr-nimi { font-weight: 500; }
.tunti-toimet { display: flex; flex-wrap: wrap; align-items: flex-start; gap: 4px 16px; }
.tunti-rooli-d summary { cursor: pointer; color: var(--primary); font-size: 12px; font-weight: 600; list-style: none; padding: 2px 0; }
.tunti-rooli-d summary::-webkit-details-marker { display: none; }
.tunti-rooli-d[open] summary { color: var(--teksti-toissijainen); }
@media (max-width: 640px) {
    .tunti-rooli { max-width: none; }
    .tunti-rooli .form-control, .tunti-rooli .btn { height: 44px; }
    .tr-rivi.tr-rivi-tekija { grid-template-columns: 0 minmax(0, 1fr) auto; }
    .tunti-rooli-d summary { min-height: 44px; display: flex; align-items: center; }
}

/* Konekortti: työmaalta haetut koneet (1.10.2026) */
.kone-historia { margin-top: 8px; }
.kone-historia > summary { cursor: pointer; min-height: 44px; display: flex; align-items: center; }
.kone-historia .kirjausrivi { gap: 8px; align-items: center; }

.tk-ikoninappi { display: inline-flex; align-items: center; justify-content: center; min-width: 44px; min-height: 44px;
    border: none; background: none; color: var(--teksti-toissijainen); cursor: pointer; border-radius: 6px; text-decoration: none; }
.tk-ikoninappi:hover { background: var(--pinta-korostettu); color: var(--teksti); }

/* Yksi takaisin-nappi sivulla (1.10.2026): sivun oma takaisin_linkki() korvaa yläpalkin napin. */
body:has(.takaisin-linkki) .paluupalkki-takaisin { display: none; }

/* Tilitapahtumat: viimeisin onnistunut haku (1.10.2026) */
.tx-haettu { margin: 4px 0 0; font-size: 13px; font-weight: 600; color: var(--teksti); }
.tx-haettu-vanha { color: var(--tila-varoitus-teksti); }

/* Laskulomake, uusi rakenne 2.10.2026: yläpalkki, Vastaanottaja- ja
   Laskun tiedot -kortit rinnakkain, laskurivit omana korttinaan. */
.lasku-ylapalkki { display: flex; align-items: center; justify-content: flex-start; gap: 12px; flex-wrap: wrap; }
.lasku-ylapalkki .page-title { margin: 0; }
.lasku-tiedot .lasku-tiedot-koko { grid-column: 1 / -1; margin: -8px 0 12px; }
.lasku-lomake .form-sticky-actions { justify-content: flex-end; }
.lasku-lomake .form-sticky-actions .btn { flex: 0 0 auto; width: auto; }
.lasku-kortit { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 16px; margin-bottom: 16px; align-items: start; }
.lasku-kortti { margin: 0; }
.lasku-kortin-otsikko { font-size: 13px; font-weight: 600; color: var(--teksti-toissijainen); text-transform: uppercase; letter-spacing: .04em; margin: 0 0 12px; }
.lasku-valinnat { display: flex; gap: 16px; flex-wrap: wrap; margin-bottom: 4px; }
.lasku-valinta { display: inline-flex; align-items: center; gap: 6px; min-height: 44px; font-size: 14px; cursor: pointer; margin: 0; }
.lasku-tiedot { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 0 16px; }
.lasku-tiedot > * { min-width: 0; }
.lasku-tiedot .lasku-tiedot-levea { grid-column: span 2; }
.lasku-tiedot-valinta { display: flex; align-items: flex-end; }
.lasku-tiedot .d-flex > * { flex: 1 1 0; min-width: 0; max-width: none !important; }
/* Maksuehdon numero kapeaksi, jotta pikavalinta mahtuu (5.10.2026: "Pika-v" katkesi). */
.lasku-tiedot .d-flex > input[type=number] { flex: 0 0 64px; }
.lasku-erapaiva { min-height: 38px; display: flex; align-items: center; font-variant-numeric: tabular-nums; }
.lasku-rivit { margin-bottom: 16px; }
.lasku-rivit-loppu { display: flex; gap: 24px; align-items: flex-start; margin-top: 16px; }
.lasku-tekstit { flex: 1 1 auto; min-width: 0; }
.lasku-rivit-loppu #invoice-live-summary { flex: 0 0 340px; }
@media (max-width: 1000px) {
    .lasku-kortit { grid-template-columns: 1fr; }
}
@media (max-width: 768px) {
    .lasku-lomake .form-sticky-actions .btn { flex: 1 1 auto; width: 100%; }
    .lasku-rivit-loppu { flex-direction: column-reverse; align-items: stretch; }
    .lasku-rivit-loppu #invoice-live-summary { flex: none; }
}
@media (max-width: 600px) {
    .lasku-tiedot { grid-template-columns: repeat(2, minmax(0, 1fr)); }
    .lasku-tiedot .lasku-tiedot-levea { grid-column: 1 / -1; }
}

/* Lomakerivit joissa kentät ovat eri levyisiä (2.10.2026): .form-row on
   kaksi yhtä leveää saraketta, joten kolmas kenttä (postitoimipaikka)
   putosi yksin omalle rivilleen. */
.form-row.lomakerivi-tili { grid-template-columns: minmax(0, 1fr) 160px; }
.form-row.lomakerivi-osoite { grid-template-columns: minmax(0, 2fr) 130px minmax(0, 1fr); }
@media (max-width: 600px) {
    .form-row.lomakerivi-tili { grid-template-columns: minmax(0, 1fr) 120px; }
    .form-row.lomakerivi-osoite { grid-template-columns: 120px minmax(0, 1fr); }
    .form-row.lomakerivi-osoite > :first-child { grid-column: 1 / -1; }
}

/* Tuntien rivimalli (3.10.2026, tunnit-moduulin uudistus): päivä ohut
   otsikkorivi, kirjaus yksi rivi, väri vain poikkeamille, tunnit oikealla.
   Pohja: templates/timesheets/_rivimalli.html */
.tr-lista { background: var(--pinta); border: 1px solid var(--reuna); border-radius: 10px; overflow: hidden; }
.tr-viikko span:last-child { text-transform: none; }
.tr-viikko { display: flex; justify-content: space-between; padding: 12px 14px 4px; font-size: 11px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase; color: var(--teksti-himmea); font-variant-numeric: tabular-nums; }
.tr-paiva { display: flex; justify-content: space-between; align-items: center; gap: 8px; min-height: 32px; padding: 4px 14px; font-size: 13px; font-weight: 700; color: var(--teksti); border-top: 1px solid var(--reuna); font-variant-numeric: tabular-nums; }
.tr-lista > .tr-paiva:first-child, .tr-viikko + .tr-paiva { border-top: 0; }
.tr-paiva.poikkeus .tr-paiva-h { color: var(--tila-varoitus-teksti); }
.tr-paiva-oikea { display: inline-flex; align-items: center; gap: 8px; }
.tr-rivi { display: grid; grid-template-columns: 64px minmax(0, 1fr) auto; column-gap: 8px; align-items: baseline; padding: 4px 14px 6px; font-size: 14px; }
.tr-rivi.js-entry-row, .tr-rivi.klikattava { cursor: pointer; }
.tr-rivi.js-entry-row:hover, .tr-rivi.klikattava:hover { background: var(--pinta-korostettu); }
.tr-koodi { font-family: ui-monospace, "SF Mono", "Cascadia Mono", Consolas, monospace; font-size: 12px; color: var(--teksti-toissijainen); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.tr-nimi { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; color: var(--teksti); }
.tr-h { text-align: right; min-width: 34px; font-variant-numeric: tabular-nums; color: var(--teksti); }
.tr-lisa { grid-column: 2 / -1; display: flex; flex-direction: column; gap: 1px; font-size: 12.5px; line-height: 1.4; color: var(--teksti-toissijainen); min-width: 0; }
.tr-lisa:empty { display: none; }
.tr-kuvaus { color: var(--teksti-toissijainen); overflow-wrap: anywhere; }
.tr-puuttuu { color: var(--tila-varoitus-teksti); }
.tr-meta { color: var(--teksti-himmea); font-size: 12px; }
.tr-meta .poikkeus { color: var(--tila-varoitus-teksti); font-weight: 600; }
.tr-tyhja { display: flex; justify-content: space-between; align-items: center; gap: 8px; min-height: 32px; padding: 2px 14px; font-size: 13px; color: var(--teksti-himmea); border-top: 1px solid var(--reuna); }
.tr-tyhja.poikkeus { color: var(--tila-varoitus-teksti); }
.tr-poissa { display: flex; justify-content: space-between; gap: 8px; margin: 4px 14px 6px; padding: 6px 10px; border-radius: 8px; background: var(--pinta-korostettu); font-size: 13px; }
.tr-poissa b { font-weight: 600; }
.tr-tila { white-space: nowrap; font-weight: 600; color: var(--teksti-toissijainen); }
.tr-tila.ok { color: var(--tila-ok-teksti); }
.tr-tila.varoitus { color: var(--tila-varoitus-teksti); }
.tr-plus { display: inline-grid; place-items: center; width: 32px; height: 28px; border: 1px dashed var(--reuna-vahva); border-radius: 7px; background: none; color: var(--teksti-toissijainen); font-size: 16px; line-height: 1; cursor: pointer; text-decoration: none; padding: 0; }
.tr-plus:hover { color: var(--primary); border-color: var(--primary); text-decoration: none; }
@media (max-width: 640px) {
    .tr-rivi { grid-template-columns: 56px minmax(0, 1fr) auto; }
    .tr-plus { width: 44px; height: 44px; }
}

/* Henkilön tuntijakso (3.10.2026) */
.jk-huomio { display: flex; justify-content: space-between; align-items: center; gap: 10px; flex-wrap: wrap; padding: 8px 12px; margin-bottom: 10px; border: 1px solid var(--warning); border-radius: 10px; background: var(--tila-varoitus-tausta); color: var(--tila-varoitus-teksti); font-size: 13px; }
.jk-huomio-napit { display: inline-flex; gap: 6px; }
.jk-huomio-napit form { margin: 0; }
.jk-palkki { display: grid; grid-template-columns: auto minmax(0, 1fr) auto; align-items: center; gap: 12px; padding: 10px 14px; margin-bottom: 10px; background: var(--pinta); border: 1px solid var(--reuna); border-radius: 10px; }
.jk-jakso { display: grid; grid-template-columns: 36px auto 36px; align-items: center; gap: 8px; }
.jk-nuoli { width: 36px; justify-content: center; }
.jk-jakso-teksti { text-align: center; line-height: 1.25; }
.jk-jakso-teksti strong { display: block; font-size: 15px; }
.jk-jakso-teksti span { font-size: 12px; color: var(--teksti-toissijainen); }
.jk-summa { font-variant-numeric: tabular-nums; min-width: 0; }
.jk-summa b { font-size: 18px; margin-right: 6px; }
.jk-summa span { font-size: 12.5px; color: var(--teksti-toissijainen); }
.jk-napit { display: inline-flex; gap: 6px; align-items: center; }
.jk-lista { margin-bottom: 14px; }
.jk-poisto { display: inline; margin: 0 0 0 8px; }
.jk-poisto button { background: none; border: 0; padding: 0; color: var(--danger); font: inherit; font-size: 12px; cursor: pointer; }
.tr-rivi[role="button"]:focus-visible, .tr-rivi[role="link"]:focus-visible { outline: 2px solid var(--primary); outline-offset: -2px; }
@media (max-width: 640px) {
    .jk-palkki { grid-template-columns: minmax(0, 1fr) auto; }
    .jk-jakso { grid-column: 1 / -1; grid-template-columns: 44px minmax(0, 1fr) 44px; }
    .jk-nuoli { width: 44px; height: 40px; }
    .jk-napit .btn { min-height: 44px; }
}

/* Kuka missä (3.10.2026): henkilö x päivä palkkajaksolta */
.km-otsake { display: flex; justify-content: space-between; align-items: center; gap: 10px; flex-wrap: wrap; }
.km-toimet { margin-bottom: 10px; }
.km-toimi { display: flex; justify-content: space-between; align-items: center; gap: 10px; flex-wrap: wrap; padding: 6px 14px; border-top: 1px solid var(--reuna); font-size: 13.5px; }
.tr-viikko + .km-toimi { border-top: 0; }
.km-toimi-napit { display: inline-flex; gap: 6px; }
.km-toimi-napit form { margin: 0; }
.tr-meta.poikkeus, .km-tila .poikkeus { color: var(--tila-varoitus-teksti); }
.km-kaari { overflow-x: auto; background: var(--pinta); border: 1px solid var(--reuna); border-radius: 10px; }
.km-taulu { border-collapse: collapse; width: 100%; font-size: 13px; font-variant-numeric: tabular-nums; }
.km-taulu th, .km-taulu td { border-bottom: 1px solid var(--reuna); padding: 0 6px; height: 40px; text-align: left; white-space: nowrap; }
.km-taulu tbody tr:last-child th, .km-taulu tbody tr:last-child td { border-bottom: 0; }
.km-taulu thead th { height: 32px; font-size: 11px; font-weight: 700; color: var(--teksti-himmea); text-transform: uppercase; letter-spacing: .03em; }
.km-taulu thead th.tanaan { color: var(--primary); }
.km-taulu .vk-alku { border-left: 1px solid var(--reuna-vahva); }
.km-nimi-s { position: sticky; left: 0; z-index: 1; background: var(--pinta); min-width: 150px; max-width: 190px; padding-left: 12px !important; font-weight: 500; }
.km-nimi-s a { color: var(--teksti); display: block; overflow: hidden; text-overflow: ellipsis; }
.km-tila { display: block; font-size: 11px; font-weight: 400; color: var(--teksti-himmea); line-height: 1.2; }
.km-solu { min-width: 64px; }
.km-solu a { display: flex; flex-direction: column; justify-content: center; height: 40px; color: var(--teksti); text-decoration: none; line-height: 1.15; }
.km-solu a:hover { background: var(--pinta-korostettu); }
.km-koodi { font-family: ui-monospace, "SF Mono", "Cascadia Mono", Consolas, monospace; font-size: 11px; color: var(--teksti-toissijainen); max-width: 96px; overflow: hidden; text-overflow: ellipsis; }
.km-h { font-weight: 600; }
.km-poissa { font-size: 12px; color: var(--teksti-toissijainen); }
.km-solu.puuttuu a.km-tyhja { color: var(--tila-varoitus-teksti); font-size: 12px; }
.km-yht { text-align: right !important; font-weight: 700; padding-right: 12px !important; }
.km-selite { font-size: 12px; color: var(--teksti-himmea); margin: 8px 2px 0; }
.km-taulu tbody th { text-transform: none; letter-spacing: 0; font-size: 13px; color: var(--teksti); }
@media (max-width: 640px) {
    .km-toimi { display: grid; grid-template-columns: minmax(0, 1fr) auto; }
    .km-toimi-napit .btn { min-height: 44px; }
}
.jk-tarkista { display: inline; margin: 0; }
.jk-tarkista button { background: none; border: 0; padding: 2px 0; color: var(--primary); font: inherit; font-size: 12px; font-weight: 600; cursor: pointer; text-align: left; }
.jk-tarkistus { background: var(--pinta); color: var(--teksti); border-color: var(--reuna); }
@media (max-width: 640px) { .jk-tarkista button { min-height: 44px; padding: 0 2px; } }
.jk-tarkistus { flex-wrap: nowrap; padding: 6px 8px 6px 12px; }

/* Palkka-aineisto: henkilörivit (3.10.2026) */
.pa-lista { border-top: 1px solid var(--reuna); margin-bottom: 10px; }
.pa-kaikki { display: inline-flex; align-items: center; gap: 6px; font-size: 12px; color: var(--teksti-toissijainen); padding: 6px 0; }
.pa-rivi { display: grid; grid-template-columns: 24px minmax(0, 1fr) 72px 150px auto; gap: 10px; align-items: center; min-height: 40px; padding: 4px 0; border-bottom: 1px solid var(--reuna); font-size: 13.5px; }
.pa-nimi { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.pa-nimi a { color: var(--teksti); font-weight: 600; text-decoration: none; }
.pa-nimi a:hover { text-decoration: underline; }
.pa-h { text-align: right; font-variant-numeric: tabular-nums; }
.pa-tila { font-size: 12.5px; color: var(--teksti-toissijainen); }
.pa-tila .ok { color: var(--tila-ok-teksti); }
.pa-tila .poikkeus { color: var(--tila-varoitus-teksti); font-weight: 600; }
.pa-toimet { display: inline-flex; gap: 6px; justify-content: flex-end; }
.pa-toimet form { margin: 0; }
@media (max-width: 640px) {
    .pa-rivi { grid-template-columns: 24px minmax(0, 1fr) auto; grid-template-areas: "v n h" ". t o"; row-gap: 4px; padding: 6px 0; }
    .pa-valinta { grid-area: v; } .pa-nimi { grid-area: n; } .pa-h { grid-area: h; }
    .pa-tila { grid-area: t; } .pa-toimet { grid-area: o; }
    .pa-toimet .btn { min-height: 44px; }
}
.pa-valinta input, .pa-kaikki input { width: 20px; height: 20px; }
a.tr-linkki { color: inherit; text-decoration: none; }
a.tr-linkki:hover { text-decoration: none; }
.jk-kello { padding: 8px 14px; margin-bottom: 10px; }
.jk-kello summary { cursor: pointer; font-size: 13.5px; font-weight: 600; min-height: 28px; display: flex; align-items: center; }
.pa-arkisto { margin: 0 0 12px; border: 1px solid var(--reuna); border-radius: 8px; }
.pa-arkisto-rivi { padding: 4px 14px; border-top: 1px solid var(--reuna); font-size: 13px; }
.pa-arkisto .tr-viikko + .pa-arkisto-rivi { border-top: 0; }
.pa-arkisto-rivi summary { cursor: pointer; min-height: 32px; display: flex; align-items: center; gap: 4px; flex-wrap: wrap; }
.pa-arkisto-rivi ul { margin: 2px 0 6px 18px; padding: 0; }
.pa-arkisto-rivi li { margin: 2px 0; }

/* Ilmoituksen sulkuruksi: kosketusalue 44 px puhelimella (Tunnit-läpikäynti 6.10.2026). */
.alert-close { background: none; border: none; cursor: pointer; font-size: 18px; line-height: 1;
    padding: 0 4px; color: inherit; opacity: 0.6; min-width: 32px; min-height: 32px;
    display: inline-flex; align-items: center; justify-content: center; }
@media (max-width: 768px) { .alert-close { min-width: 44px; min-height: 44px; } }

/* ── Tarjous- ja tarjouspyyntölista (11.10.2026) ──────────────────────
   Tiivis rivi per asiakirja, tilat valintaruutuina (static/js/tarjouslista.js).
   .tl-pyynnot vaihtaa sarakkeet tarjouspyynnöille. */
.tl-ylä { display: flex; flex-wrap: wrap; gap: 8px 16px; align-items: center; margin: 4px 0 10px; }
.tl-rastit { display: flex; flex-wrap: wrap; gap: 4px 14px; align-items: center; }
.tl-rasti { display: inline-flex; align-items: center; gap: 6px; min-height: 44px; font-size: 14px; color: var(--teksti); cursor: pointer; user-select: none; }
.tl-rasti input { width: 18px; height: 18px; margin: 0; accent-color: var(--primary); }
.tl-rasti .tl-lkm { color: var(--teksti-toissijainen); font-size: 12.5px; }
.tl-haku { flex: 1 1 220px; max-width: 340px; margin-left: auto; }
.tl-taulu { background: var(--pinta); border: 1px solid var(--reuna); border-radius: var(--radius); overflow: hidden; }
.tl-rivi, .tl-otsake {
    display: grid;
    grid-template-columns: 74px 104px minmax(0, 1fr) minmax(0, 1.3fr) 104px 210px;
    gap: 12px; align-items: center; padding: 0 14px; min-height: 44px; font-size: 13.5px;
}
.tl-otsake { min-height: 36px; background: var(--pinta-korostettu); color: var(--teksti-toissijainen); font-size: 12px; font-weight: 600; border-bottom: 1px solid var(--reuna); }
.tl-otsake button { all: unset; cursor: pointer; display: inline-flex; gap: 4px; align-items: center; min-height: 36px; }
.tl-otsake button[aria-sort="ascending"]::after { content: '\2191'; }
.tl-otsake button[aria-sort="descending"]::after { content: '\2193'; }
.tl-otsake .tl-oik, .tl-rivi .tl-oik { text-align: right; justify-content: flex-end; }
.tl-rivi { position: relative; border-bottom: 1px solid var(--reuna); color: var(--teksti); }
.tl-rivi:last-child { border-bottom: 0; }
.tl-rivi:hover { background: var(--pinta-korostettu); }
.tl-rivi > span { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tl-avaa { color: inherit; text-decoration: none; }
.tl-avaa::after { content: ''; position: absolute; inset: 0; }
.tl-avaa:focus-visible { outline: none; }
.tl-rivi:has(.tl-avaa:focus-visible) { outline: 2px solid var(--primary); outline-offset: -2px; }
.tl-pieni { color: var(--teksti-toissijainen); font-size: 12px; }
.tl-summa { font-variant-numeric: tabular-nums; font-weight: 600; }
.tl-tila { display: flex; align-items: center; gap: 8px; }
.tl-merkki { display: inline-block; padding: 2px 8px; border-radius: 999px; font-size: 11.5px; font-weight: 600; white-space: nowrap; }
.tl-merkki.laskennassa, .tl-merkki.draft { background: var(--pinta-upotettu); color: var(--teksti-toissijainen); border: 1px solid var(--reuna); }
.tl-merkki.sent { background: var(--tila-varoitus-tausta); color: var(--tila-varoitus-teksti); }
.tl-merkki.accepted { background: var(--tila-ok-tausta); color: var(--tila-ok-teksti); }
.tl-merkki.ohi { background: var(--pinta-upotettu); color: var(--teksti-himmea); }
.tl-rivi.ohi { color: var(--teksti-toissijainen); }
.tl-rivi .tl-linkki { position: relative; z-index: 1; font-size: 12.5px; }
.tl-tyhja { padding: 24px 14px; color: var(--teksti-toissijainen); text-align: center; }
/* Lappu nimen edessä: nimen lopun katkaisu leikkasi perässä olleen lapun
   puhelimella pelkäksi reunaksi (12.10.2026). */
.tl-mt { font-size: 11px; color: var(--teksti-toissijainen); border: 1px solid var(--reuna); border-radius: 4px; padding: 0 4px; margin-right: 6px; }
@media (max-width: 760px) {
    .tl-haku { max-width: none; margin-left: 0; flex-basis: 100%; }
    .tl-otsake { display: none; }
    .tl-rivi {
        grid-template-columns: minmax(0, 1fr) auto;
        grid-template-areas: "asiakas summa" "kohde tila";
        gap: 2px 10px; padding: 8px 12px; min-height: 56px;
    }
    .tl-rivi .tl-pvm, .tl-rivi .tl-nro { display: none; }
    .tl-rivi .tl-asiakas { grid-area: asiakas; font-weight: 600; }
    .tl-rivi .tl-kohde { grid-area: kohde; color: var(--teksti-toissijainen); font-size: 12.5px; }
    .tl-rivi .tl-summa { grid-area: summa; }
    .tl-rivi .tl-tila { grid-area: tila; justify-content: flex-end; }
    .tl-rivi .tl-linkki { display: none; }
    .tl-alirivi { padding-left: 24px; min-height: 44px; }
}
/* Pyyntö ja sen tarjoukset yhtenä ryhmänä. Useamman tarjouksen pyynnöllä jokainen
   tarjous on oma, sisennetty rivinsä päivämäärineen (Petteri 23.10.2026: linkit
   samassa solussa olivat sekavat, ja tarjoukset pitää nähdä erillisinä). */
.tl-ryhma { border-bottom: 1px solid var(--reuna); }
.tl-ryhma:last-child { border-bottom: 0; }
.tl-ryhma .tl-rivi { border-bottom: 0; }
.tl-ryhma .tl-rivi + .tl-rivi { border-top: 1px dashed var(--reuna); }
.tl-alirivi { background: var(--pinta-korostettu); font-size: 12.5px; min-height: 36px; }
.tl-alirivi .tl-asiakas { padding-left: 18px; }
.tl-alirivi .tl-summa { font-weight: 500; }
/* Tarjouspyynnöt: tila ja linkit erillisiin sarakkeisiin. Yhdessä 210 px:n
   solussa merkki, "vastaamatta 3 pv" ja linkit olivat sekava sekamelska, joka ei
   mahtunut ruutuun, vaikka pyytäjän ja kohteen perässä oli tilaa (23.10.2026). */
.tl-pyynnot .tl-rivi, .tl-pyynnot .tl-otsake { grid-template-columns: 74px minmax(0, 1fr) minmax(0, 1.25fr) 100px 110px 170px 140px; }
.tl-pyynnot .tl-rivi .tl-tila { white-space: normal; flex-wrap: wrap; gap: 3px 8px; line-height: 1.3; }
.tl-pyynnot .tl-rivi .tl-avaukset { display: flex; gap: 12px; }
@media (max-width: 760px) {
    .tl-pyynnot .tl-rivi { grid-template-columns: minmax(0, 1fr) auto; }
    .tl-rivi .tl-paikka, .tl-pyynnot .tl-rivi .tl-avaukset { display: none; }
}

/* Odotusilmaisin (js/odotus.js): ylareunan palkki ja odottava nappi. */
#sp-odotuspalkki { position: fixed; top: 0; left: 0; right: 0; height: 4px; z-index: 10000;
    overflow: hidden; pointer-events: none; opacity: 0; transition: opacity .15s; }
#sp-odotuspalkki.paalla { opacity: 1; }
#sp-odotuspalkki::before { content: ""; position: absolute; top: 0; bottom: 0; left: -40%; width: 40%;
    background: var(--accent, var(--primary)); animation: sp-odotus 1.1s ease-in-out infinite; }
@keyframes sp-odotus { to { left: 100%; } }
html.sp-odottaa-sivu, html.sp-odottaa-sivu body { cursor: progress; }
/* Koko ruudun odotusnäkymä (27.10.2026): mitä tehdään, kulunut aika, ohje jos kestää. */
#sp-odotuspeite { position: fixed; inset: 0; z-index: 10001; display: none; align-items: center;
    justify-content: center; padding: 16px; background: rgba(15, 23, 42, .55); }
#sp-odotuspeite.paalla { display: flex; }
.sp-odotuskortti { background: var(--pinta); color: var(--teksti); border-radius: 12px; padding: 24px 28px;
    width: 100%; max-width: 420px; text-align: center; box-shadow: 0 10px 30px rgba(0, 0, 0, .35); }
.sp-odotuskortti .sp-pyora-iso { width: 44px; height: 44px; margin: 0 auto 14px; border: 4px solid var(--gray-300);
    border-top-color: var(--primary); border-radius: 50%; animation: sp-pyori .9s linear infinite; }
.sp-odotuskortti h3 { margin: 0 0 6px; font-size: 17px; line-height: 1.3; }
.sp-odotuskortti p { margin: 4px 0; font-size: 14px; color: var(--teksti-toissijainen); font-variant-numeric: tabular-nums; }
.sp-odotuskortti .sp-odotus-vinkki { color: var(--tila-varoitus-teksti); }
.sp-odottaa { cursor: progress !important; opacity: .85; }
.sp-pyora { display: inline-block; width: 14px; height: 14px; margin-right: 8px; vertical-align: -2px;
    border: 2px solid currentColor; border-right-color: transparent; border-radius: 50%;
    animation: sp-pyori .7s linear infinite; }
@keyframes sp-pyori { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) {
    #sp-odotuspalkki::before { animation-duration: 2.5s; }
    .sp-pyora { animation-duration: 1.6s; }
}

/* Kiinteistönhoito: isot kosketusalueet hanskakädelle ja yhdellä kädellä
   (Villen käyttäjätesti 13.10.2026 M3). Käyttäjät ovat talonmiehiä. */
.kh-sivu .btn, .kh-sivu .valilehti { min-height: 44px; display: inline-flex; align-items: center; justify-content: center; }
.kh-sivu .form-control, .kh-sivu select { min-height: 44px; }

/* =====================================================================
   KELLUVA ALAPALKKI (19.10.2026, Hallan väriteemapaketti + Petteri:
   "ainakin mun iphonella se leikkaa nurkat niistä teksteistä").
   Reunasta reunaan ulottuva palkki jäi iPhonen pyöristettyjen nurkkien
   alle: Etusivu- ja Viestit-tekstit leikkautuivat. Kelluva palkki on
   12 px irti reunoista ja nostettu alareunasta. Kotivalikkosovelluksessa
   (html.ios-standalone, base.html) nosto on isompi, koska ilman
   viewport-fit=coveria env(safe-area-inset-bottom) palauttaa nollan.
   Käytössä body.alapalkki-kelluva -luokalla.
   ===================================================================== */
:root { --alapalkki-ala: 8px; --alapalkki-korkeus: 62px;
    /* Palkin etäisyys näytön alareunasta. Turva-alueen (kotipalkki, 34 px)
       päälle lisätty väli nosti palkin liian ylös (palaute 2.11.2026).
       Palkki on yhtä kaukana näytön alareunasta kuin sivureunoista (12 px,
       iPhonen 34 px turva-alue miinus 22 px), jolloin kotipalkin viiva
       piirtyy palkin päälle; ilman turva-aluetta (Android, tietokone) väli
       on 8 px. */
    --alapalkki-pohja: max(var(--alapalkki-ala), calc(env(safe-area-inset-bottom, 0px) - 22px)); }
/* viewport-fit=cover (2.11.2026) antaa env(safe-area-inset-bottom):lle oikean
   arvon, joten kotivalikkosovelluksen erillinen 22 px:n nosto jäisi tuplaksi. */
html.ios-standalone { --alapalkki-ala: 8px; }
/* Ei kumivenytystä sivun reunoilla kotivalikkosovelluksessa. Mitattu
   19.10.2026 Petterin iPhonesta: Tunnit-sivu on vain ~100 px ruutua
   pidempi, joten pyyhkäisy osui heti loppuun, sivu venyi yli (scrollY 147,
   max 96) ja kiinteä alapalkki pomppasi venytyksen mukana 50 px ylös ja
   takaisin. Sama tapahtui jokaisen sivun ylä- ja alareunassa. */
html.ios-standalone,
html.ios-standalone body { overscroll-behavior-y: none; }
@media (max-width: 768px) {
    body.alapalkki-kelluva .bottom-bar {
        left: 12px;
        right: 12px;
        bottom: calc(var(--alapalkki-pohja) + var(--ios-palkki, 0px));
        height: var(--alapalkki-korkeus);
        /* SportLinkin mitat (seura-demo sovellus.css): palkki 62 px, kohta
           50 px ja säde 25 px, sama 6 px väli kohdasta palkin reunaan joka
           suuntaan (palaute 2.11.2026: valitun kohdan väli oli sivuilla 4 px).
           5 px + 1 px reuna = 6 px. */
        padding: 5px;
        align-items: center;
        border: 1px solid color-mix(in srgb, var(--reuna) 90%, transparent);
        /* Kapseli (palaute 2.11.2026). Soikeat ja erisuuret kulmat näyttivät
           huonoilta; 64 px korkeaan palkkiin ei mahdu näytön kulman kanssa
           samankeskistä 43 px:n sädettä. */
        border-radius: 999px;
        background: color-mix(in srgb, var(--pinta) 82%, transparent);
        -webkit-backdrop-filter: blur(20px) saturate(1.4);
        backdrop-filter: blur(20px) saturate(1.4);
        box-shadow: 0 6px 24px rgba(0, 0, 0, 0.14);
    }
    :root[data-theme-mode="dark"]:not(.tyotila) body.alapalkki-kelluva .bottom-bar {
        background: color-mix(in srgb, var(--pinta) 88%, transparent);
        -webkit-backdrop-filter: blur(20px) saturate(1.2) brightness(.85);
        backdrop-filter: blur(20px) saturate(1.2) brightness(.85);
        box-shadow: 0 6px 24px rgba(0, 0, 0, 0.45);
    }
    body.alapalkki-kelluva .bottom-bar-item {
        height: 50px;
        padding: 0;
        justify-content: center;
        border-radius: 999px;
        color: var(--teksti-toissijainen);
    }
    body.alapalkki-kelluva .bottom-bar-item.active {
        color: var(--primary);
        background: color-mix(in srgb, var(--primary) 12%, transparent);
    }
    /* Plus palkin sisään samaan riviin, ei nostettuna palkin yläpuolelle:
       nostettu nappi ei ollut linjassa palkin eikä kohtien kanssa
       (palaute 2.11.2026). */
    body.alapalkki-kelluva .bottom-bar-fab {
        margin: 0;
        width: 48px;
        height: 48px;
        align-self: center;
        box-shadow: none;
    }
    /* Sivun loppu jää palkin yläpuolelle: palkki + nosto + napin nousu. */
    body.alapalkki-kelluva .content,
    body.alapalkki-kelluva .content-full {
        padding-bottom: calc(var(--alapalkki-korkeus) + var(--alapalkki-pohja) + 48px);
    }
    body.alapalkki-kelluva .kellopalkki {
        left: 12px;
        right: 12px;
        border: 1px solid var(--reuna);
        border-radius: 14px;
        bottom: calc(var(--alapalkki-korkeus) + var(--alapalkki-pohja) + 8px + var(--ios-palkki, 0px));
    }
    body.alapalkki-kelluva .settings-sticky-save {
        bottom: calc(var(--alapalkki-korkeus) + var(--alapalkki-pohja) + 6px + var(--ios-palkki, 0px));
    }
    /* Tallennuspalkki ulottuu kelluvan alapalkin taakse ruudun alareunaan asti:
       palkin ja alapalkin välistä näkyivät lomakkeen rivit, ja iPhonen turva-alueen
       34 px:n täyte teki palkin alle tyhjän raidan (Petteri 7.11.2026). */
    body.alapalkki-kelluva .form-sticky-actions {
        bottom: 0;
        padding-bottom: calc(var(--alapalkki-korkeus) + var(--alapalkki-pohja) + 10px + var(--ios-palkki, 0px));
        background: var(--pinta);
    }
}
/* Läpinäkyvyys pois, jos käyttäjä on sen laitteesta kieltänyt tai selain
   ei osaa sumentaa: umpinainen palkki on aina luettava. */
@media (max-width: 768px) and (prefers-reduced-transparency: reduce) {
    body.alapalkki-kelluva .bottom-bar {
        background: var(--pinta);
        -webkit-backdrop-filter: none;
        backdrop-filter: none;
        border-color: var(--reuna);
    }
}
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
    @media (max-width: 768px) {
        body.alapalkki-kelluva .bottom-bar { background: var(--pinta); border-color: var(--reuna); }
    }
}

/* Kartta omaksi pinontatasokseen: Leafletin tasot (z-index 400-1000)
   nousivat muuten sivun kiinteiden palkkien päälle. Asetussivulla kartta
   peitti Tallenna-palkin vierittäessä (Ville 19.10.2026: "tallenna painike
   leijuilee tai jää datan alle"). */
.leaflet-container { z-index: 0; isolation: isolate; }

/* ── KATERAPORTTI (26.10.2026) ────────────────────────────────────────────
   Yksi rauhallinen sommittelu hallabit.fi:n hengessä: pieni yläotsikko,
   isot luvut, yksi taulukko jossa luvut ovat samoissa sarakkeissa.
   Vain teemamuuttujia; puhelimella taulukko on kortteja (taulukko-korteiksi). */
.kate-ylaotsikko {
    font-size: 11px; font-weight: 700; letter-spacing: 0.12em; text-transform: uppercase;
    color: var(--teksti-himmea); margin-bottom: 2px;
}
.kate-selite { margin: 4px 0 0; color: var(--teksti-toissijainen); font-size: 14px; }

.kate-kpit {
    display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 12px;
    margin: 4px 0 16px;
}
.kate-kpi {
    background: var(--pinta); border: 1px solid var(--reuna); border-radius: 12px;
    padding: 16px 18px; min-width: 0;
    animation: kate-nousu .45s cubic-bezier(.2,.7,.2,1) both;
    animation-delay: calc(var(--i, 0) * 60ms);
}
.kate-kpi-nimi {
    font-size: 11px; font-weight: 700; letter-spacing: 0.08em; text-transform: uppercase;
    color: var(--teksti-himmea);
}
.kate-kpi-luku {
    font-size: 28px; font-weight: 700; line-height: 1.15; margin-top: 6px;
    color: var(--teksti); font-variant-numeric: tabular-nums; white-space: nowrap;
    overflow: hidden; text-overflow: ellipsis;
}
.kate-kpi-selite { font-size: 12px; color: var(--teksti-toissijainen); margin-top: 4px; }
.kate-kpi.kate-plus .kate-kpi-luku { color: var(--tila-ok-teksti); }
.kate-kpi.kate-miinus .kate-kpi-luku { color: var(--tila-virhe-teksti); }

.kate-suodatin { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 12px; }
.kate-nappi {
    display: inline-flex; align-items: center; min-height: 36px; padding: 0 14px;
    border-radius: 999px; border: 1px solid var(--reuna); background: var(--pinta);
    color: var(--teksti-toissijainen); font-size: 13px; font-weight: 600; text-decoration: none;
    transition: background .15s, color .15s, border-color .15s;
}
.kate-nappi:hover { color: var(--teksti); border-color: var(--reuna-vahva); }
.kate-nappi.on { background: var(--primary); color: var(--teksti-pinnalla); border-color: var(--primary); }

.kate-taulu-kehys {
    background: var(--pinta); border: 1px solid var(--reuna); border-radius: 12px;
    overflow: hidden;
}
.kate-taulu th { white-space: nowrap; font-size: 12px; letter-spacing: 0.04em; text-transform: uppercase; color: var(--teksti-himmea); }
.kate-taulu th.lajiteltu { color: var(--teksti); }
.kate-lajittelu { color: inherit; text-decoration: none; display: inline-flex; align-items: center; gap: 4px; min-height: 24px; }
.kate-lajittelu:hover { color: var(--teksti); }
.kate-nuoli { font-size: 9px; }
.kate-rivi { cursor: pointer; animation: kate-nousu .4s cubic-bezier(.2,.7,.2,1) both; animation-delay: calc(140ms + var(--i, 0) * 25ms); }
.kate-rivi td { vertical-align: top; }
.kate-kohde { font-weight: 600; color: var(--teksti); text-decoration: none; }
.kate-rivi:hover .kate-kohde { color: var(--primary); }
.kate-asiakas { font-size: 12px; color: var(--teksti-toissijainen); margin-top: 2px; }
.kate-tarjous { font-size: 12px; color: var(--teksti-toissijainen); margin-top: 4px; }
.kate-tila {
    display: inline-block; padding: 1px 8px; border-radius: 999px; font-size: 11px; font-weight: 600;
    background: var(--pinta-upotettu); color: var(--teksti-toissijainen); border: 1px solid var(--reuna);
}
.kate-tila-active { background: var(--tila-tieto-tausta); color: var(--tila-tieto-teksti); border-color: transparent; }
.kate-tila-warranty { background: var(--tila-varoitus-tausta); color: var(--tila-varoitus-teksti); border-color: transparent; }
.kate-luku { font-variant-numeric: tabular-nums; white-space: nowrap; }
.kate-himmea { color: var(--teksti-himmea); }
.kate-kate { font-weight: 700; }
.kate-plus-teksti { color: var(--tila-ok-teksti); }
.kate-miinus-teksti { color: var(--tila-virhe-teksti); }
.kate-varoitus-teksti { color: var(--tila-varoitus-teksti); }
.kate-mittari {
    display: block; height: 4px; width: 72px; margin: 5px 0 0 auto; border-radius: 999px;
    background: var(--pinta-upotettu); overflow: hidden;
}
.kate-mittari-palkki { display: block; height: 100%; background: var(--tila-ok-teksti); border-radius: 999px; transform-origin: left; animation: kate-kasvu .6s ease-out both; animation-delay: calc(200ms + var(--i, 0) * 25ms); }
.kate-mittari-palkki.varoitus { background: var(--tila-varoitus-teksti); }
.kate-mittari-palkki.miinus { background: var(--tila-virhe-teksti); width: 0 !important; }
.kate-taulu tfoot td { font-weight: 700; background: var(--pinta-korostettu); border-top: 2px solid var(--reuna); border-bottom: 0; }

@keyframes kate-nousu { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: none; } }
@keyframes kate-kasvu { from { transform: scaleX(0); } to { transform: none; } }
@media (prefers-reduced-motion: reduce) {
    .kate-kpi, .kate-rivi, .kate-mittari-palkki { animation: none; }
}
@media (max-width: 900px) {
    .kate-kpit { grid-template-columns: repeat(2, minmax(0, 1fr)); }
    .kate-kpi { padding: 12px 14px; }
    .kate-kpi-luku { font-size: 22px; }
}
@media (max-width: 480px) {
    /* Kapealla luku ei saa katketa kolmeen pisteeseen: pienempi fontti ja
       rivitys sallittu. */
    .kate-kpit { gap: 8px; }
    .kate-kpi { padding: 10px 12px; }
    .kate-kpi-luku { font-size: 19px; white-space: normal; overflow: visible; }
}
@media (max-width: 768px) {
    .kate-taulu-kehys { background: transparent; border: 0; border-radius: 0; overflow: visible; }
    .kate-taulu.taulukko-korteiksi tr { background: var(--pinta); border-radius: 12px; }
    .kate-rivi td.korteiksi-otsikko { min-height: 44px; }
    .kate-mittari { display: none; }
    .kate-nappi { min-height: 44px; }
    .kate-kohde { display: inline-block; min-height: 24px; }
}

/* Raportit-sivun kohdelista: sama tyyli, kohteet tilan mukaan ryhmiteltynä. */
.rpt-kohteet .kate-ryhma { margin: 18px 0 8px; font-size: 12px; font-weight: 700; letter-spacing: 0.08em; text-transform: uppercase; color: var(--teksti-himmea); }
.rpt-kohteet .kate-rivi { animation: none; cursor: default; }
.rpt-kohteet .kate-toiminnot { display: flex; gap: 6px; flex-wrap: wrap; justify-content: flex-end; }
.rpt-kohteet .kate-toiminnot .btn { min-height: 36px; }
@media (max-width: 768px) {
    .rpt-kohteet .kate-toiminnot .btn { min-height: 44px; flex: 1 1 auto; justify-content: center; }
}

/* Työmaan muistilista: näkyvä valintaruutu, kuvat ja muokkaus (2.11.2026).
   Ennen ruutu oli merkki &#9744;, joka ei näkynyt iPhonella, joten 0/9 jäi
   selittämättä. */
.muisti-rivi { display: flex; align-items: flex-start; gap: 8px; padding: 6px 0; border-bottom: 1px solid var(--gray-200); }
.muisti-rivi:last-of-type { border-bottom: 0; }
.muisti-ruutu { flex: none; width: 44px; height: 44px; display: inline-flex; align-items: center; justify-content: center; background: none; border: 0; cursor: pointer; padding: 0; }
.muisti-ruutu span { width: 22px; height: 22px; border: 2px solid var(--gray-400); border-radius: 5px; display: inline-flex; align-items: center; justify-content: center; color: var(--pinta); background: var(--pinta); }
.muisti-rivi.tehty .muisti-ruutu span { background: var(--primary); border-color: var(--primary); }
.muisti-sisalto { flex: 1; min-width: 0; padding-top: 10px; }
.muisti-teksti { font-size: 14px; color: var(--teksti); white-space: pre-line; overflow-wrap: anywhere; }
.muisti-rivi.tehty .muisti-teksti { text-decoration: line-through; color: var(--teksti-toissijainen); }
.muisti-kuvat { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 6px; }
.muisti-kuvat img { width: 72px; height: 72px; object-fit: cover; border-radius: 6px; cursor: pointer; border: 1px solid var(--gray-200); }
.muisti-muokkaus summary { display: inline-flex; align-items: center; gap: 4px; min-height: 44px; cursor: pointer; font-size: 13px; color: var(--teksti-toissijainen); list-style: none; }
.muisti-muokkaus summary::-webkit-details-marker { display: none; }
.muisti-muokkaus form { display: flex; flex-direction: column; gap: 8px; margin-bottom: 6px; }
.muisti-muokkaus form .btn { align-self: flex-start; }
.muisti-poistot { display: flex; flex-wrap: wrap; gap: 8px; }
.muisti-poistot label { display: inline-flex; align-items: center; gap: 6px; min-height: 44px; font-size: 13px; }
.muisti-poistot img { width: 44px; height: 44px; object-fit: cover; border-radius: 4px; }
.muisti-kuvanappi { min-height: 44px; display: inline-flex; align-items: center; gap: 6px; cursor: pointer; }
.muisti-kuvanappi.valittu { border-color: var(--primary); color: var(--primary); }
.muisti-tilaaja { display: inline-flex; align-items: center; gap: 4px; font-size: 12px; color: var(--teksti-toissijainen); margin-top: 4px; }
.muisti-valinta { display: inline-flex; align-items: center; gap: 8px; min-height: 44px; font-size: 14px; color: var(--teksti); }
.muisti-valinta input { width: 20px; height: 20px; flex-shrink: 0; }

/* Vaakasuunnassa vieritettävät välilehti- ja pillipalkit eivät saa vierittyä
   pystyyn. overflow-x: auto tekee selaimessa myös overflow-y:stä auton, ja
   aktiivisen välilehden alaviivan negatiivinen marginaali jäi palkin alle,
   joten iPhonella palkin sai vedettyä piiloon (laatukatsaus #14, 2.11.2026). */
.vu-lehdet, .tuntilehdet, .valilehdet.vierittyva, .sivun-lehdet, .suodatinpillit, .tp-vaiheet {
    overflow-y: hidden;
    overscroll-behavior-x: contain;
}

/* Plussan kaari ja alapalkki ilman tekstejä (Villen ehdotukset 2.11.2026,
   valinnat Profiilissa). */
.plussa-kaari { position: fixed; inset: 0; z-index: 1049; background: rgba(11, 22, 40, .62); opacity: 0; transition: opacity .15s; }
.plussa-kaari.nakyy { opacity: 1; }
.plussa-kaari-kohta { position: fixed; width: 96px; margin: -28px 0 0 -48px; display: flex; flex-direction: column; align-items: center; gap: 4px;
    text-decoration: none; background: none; border: 0; padding: 0; cursor: pointer; font: inherit;
    transform: scale(.4) translateY(40px); opacity: 0; transition: transform .18s ease-out, opacity .18s; }
.plussa-kaari.nakyy .plussa-kaari-kohta { transform: none; opacity: 1; }
.plussa-kaari-ympyra { width: 56px; height: 56px; border-radius: 50%; display: inline-flex; align-items: center; justify-content: center;
    background: var(--pinta); color: var(--primary); box-shadow: 0 4px 14px color-mix(in srgb, var(--teksti) 22%, transparent); font-size: 20px; font-weight: 700; }
.plussa-kaari-ympyra svg { width: 24px; height: 24px; }
.plussa-kaari-teksti { font-size: 12px; font-weight: 600; line-height: 1.2; color: var(--pinta); text-align: center;
    color: #fff; background: rgba(11, 22, 40, .88); padding: 2px 7px; border-radius: 8px; white-space: nowrap; }
.bottom-bar-fab { transition: transform .18s; }
.bottom-bar-fab.auki { transform: rotate(45deg); position: relative; z-index: 1050; }
@media (prefers-reduced-motion: reduce) { .plussa-kaari, .plussa-kaari-kohta, .bottom-bar-fab { transition: none; } }
html.alapalkki-ikonit body.alapalkki-kelluva .bottom-bar-item span:last-child { display: none; }
html.alapalkki-ikonit body.alapalkki-kelluva .bottom-bar-item .ikoni { width: 28px; height: 28px; margin: 0; }
.profiili-valinta { display: flex; flex-wrap: wrap; gap: 8px; margin: 4px 0 12px; }
.profiili-valinta label { display: inline-flex; align-items: center; gap: 8px; min-height: 44px; padding: 0 14px; border: 1px solid var(--reuna); border-radius: 999px; cursor: pointer; color: var(--teksti); }
.profiili-valinta input { width: 18px; height: 18px; }

/* Paluupalkki ilman nappia ei vie tilaa (Etusivu-nappi poistettu 2.11.2026). */
.paluupalkki:not(:has(a, button)) { display: none !important; }

/* Rulla: piilossa olevat kohdat ja vedon aikana ei siirtymäanimaatiota. */
.plussa-kaari.nakyy .plussa-kaari-kohta.piilossa { opacity: 0; transform: scale(.6); pointer-events: none; }
.plussa-kaari.pyorii .plussa-kaari-kohta { transition: none; }
.plussa-kaari { touch-action: none; }
.plussa-kaari-vihje { position: fixed; left: 0; right: 0; text-align: center; color: #fff; font-size: 12px; opacity: .85; }
.plussa-kaari-teksti { font-size: 12px; }
.bottom-bar-fab .ikoni { width: 26px; height: 26px; display: block; }
.plussa-kaari-teksti { white-space: normal; max-width: 84px; text-align: center; }
/* Pitkä yksi sana ("Työmaapäiväkirja", "Muutostyötarjous") ei mahtunut 84 px:iin,
   vaan valui tumman taustan yli (Petteri 4.11.2026). Teksti on aina yhdellä
   rivillä ja tausta sen levyinen: naapurikohdat ovat kaarella vähintään 31 px
   eri korkeudella, joten yksirivinen teksti ei osu niihin. Reunimmaiset
   pidetään ruudulla base.html:ssä. */
.plussa-kaari-kohta .plussa-kaari-teksti { width: max-content; max-width: none; white-space: nowrap; }
.plussa-kaari-kohta { width: 84px; margin: -26px 0 0 -42px; }
.plussa-kaari-ympyra { width: 52px; height: 52px; }

/* Asetukset-keskus ja kategoriasivut (Villen asetusehdotus 3.11.2026, vaihe 2).
   Puhelimella ryhmitelty lista, rivi 48 px ja ikoniruutu 30 px. Tietokoneella
   kategoriasivulla vasemmalla sama lista tiiviinä (rivi 44 px), lomake enintään 640 px. */
.asetus-keskus { max-width: 640px; }
.asetus-lista ul { list-style: none; margin: 0 0 16px; padding: 0; background: var(--pinta); border: 1px solid var(--reuna); border-radius: var(--radius-sm); overflow: hidden; }
.asetus-lista li + li { border-top: 1px solid var(--reuna); }
.asetus-ryhma { margin: 0 0 6px 4px; font-size: 12px; font-weight: 700; letter-spacing: .04em; text-transform: uppercase; color: var(--teksti-toissijainen); }
.asetus-rivi { display: flex; align-items: center; gap: 12px; min-height: 48px; padding: 6px 12px; color: var(--teksti); text-decoration: none; }
.asetus-rivi:hover { background: var(--pinta-korostettu, var(--pinta-upotettu)); }
.asetus-rivi.is-active { background: var(--primary-light); }
.asetus-ikoni { flex: none; display: inline-flex; align-items: center; justify-content: center; width: 30px; height: 30px; border-radius: 8px; background: var(--primary); color: var(--primary-ink, #fff); }
.asetus-nimi { flex: 1 1 auto; min-width: 0; font-size: 16px; }
.asetus-arvo { flex: none; font-size: 14px; color: var(--teksti-toissijainen); white-space: nowrap; }
.asetus-nuoli { flex: none; display: inline-flex; color: var(--teksti-toissijainen); }
.asetus-sivu { display: block; }
.asetus-palsta { display: none; }
.asetus-lomake { max-width: 640px; }
.asetus-kapea { max-width: 160px; }
.asetus-huomio { margin-top: 12px; }
@media (min-width: 1024px) {
    .asetus-sivu { display: grid; grid-template-columns: 260px minmax(0, 640px); gap: 24px; align-items: start; }
    .asetus-palsta { display: block; position: sticky; top: 72px; }
    .asetus-lista-tiivis .asetus-rivi { min-height: 44px; border-radius: 8px; }
    .asetus-lista-tiivis .asetus-nimi { font-size: 14px; }
    .asetus-lista-tiivis .asetus-ikoni { width: 26px; height: 26px; }
    .asetus-lista-tiivis .asetus-nuoli { display: none; }
}
.asetus-alaotsikko { margin: 20px 0 8px; font-size: 15px; font-weight: 700; color: var(--teksti); }
.asetus-kortti > .asetus-alaotsikko:first-child { margin-top: 0; }

/* Sivunvaihto (Villen nopeusehdotus K1, 3.11.2026): uusi sivu häivytetään sisään,
   ylä- ja alapalkki pysyvät paikallaan. Toimii iOS 18.2+ ja Chrome 126+, muilla ei
   animaatiota. Luuranko näkyy, jos sivu ei tullut 150 ms:ssa (js/siirtyma.js). */
@view-transition { navigation: auto; }
.navbar { view-transition-name: hl-yla; }
.bottom-bar { view-transition-name: hl-ala; }
::view-transition-old(root) { animation: 90ms ease-out both hl-ulos; }
::view-transition-new(root) { animation: 180ms ease-out both hl-sisaan; }
/* iPhonen tilarivi (kello) välähti valkoisena joka sivunvaihdossa (Petteri 4.11.2026):
   Safari värittää tilarivin yläreunan kiinteän palkin taustasta, mutta siirtymän
   aikana palkki on kuvana eikä elementtinä, jolloin väri tulee sivun taustasta.
   iOS:llä ei siirtymäanimaatiota; napautuksen korostus ja luuranko pysyvät. */
@supports (-webkit-touch-callout: none) { @view-transition { navigation: none; } }
/* Ilman siirtymää sivu vaihtui iPhonella töksähtäen (Petteri 4.11.2026): vain
   sisältö häivytetään sisään, palkit pysyvät paikallaan eikä tilarivi välähdä. */
@supports (-webkit-touch-callout: none) {
    .content, .content-full { animation: hl-haivy 160ms ease-out backwards; }
}
/* Vain läpinäkyvyys: siirto (transform) tekisi sisällöstä kiinteiden palkkien
   (tallennus, kello) sijoituspohjan. */
@keyframes hl-haivy { from { opacity: 0; } }
@media (prefers-reduced-motion: reduce) { .content, .content-full { animation: none !important; } }
@keyframes hl-ulos { to { opacity: 0; } }
@keyframes hl-sisaan { from { opacity: 0; transform: translateY(8px); } }
.bottom-bar-item { transition: transform .12s ease-out, background-color .15s, color .15s; }
.bottom-bar-item.hl-painettu { transform: scale(.9); }
.hl-luuranko { position: fixed; left: 0; right: 0; bottom: 0; z-index: 900; padding: 16px; display: grid; gap: 12px;
    align-content: start; background: var(--tausta); opacity: 0; transition: opacity .15s; pointer-events: none; }
html.hl-siirtyy .hl-luuranko { opacity: 1; }
.hl-luuranko .o { height: 24px; width: 45%; border-radius: 8px; }
.hl-luuranko .n { height: 44px; border-radius: 10px; }
.hl-luuranko .k { display: grid; gap: 10px; padding: 14px; border-radius: 12px; background: var(--pinta); border: 1px solid var(--reuna); }
.hl-luuranko .k i:nth-child(1) { width: 60%; }
.hl-luuranko .k i:nth-child(3) { width: 40%; }
.hl-luuranko .o, .hl-luuranko .n, .hl-luuranko .k i { display: block; min-height: 12px;
    background-color: color-mix(in srgb, var(--teksti) 9%, var(--pinta));
    background-image: linear-gradient(90deg, transparent, color-mix(in srgb, var(--pinta) 80%, transparent), transparent);
    background-size: 200% 100%; animation: hl-kiilto 1.1s linear infinite; }
.hl-luuranko .k i { border-radius: 6px; }
@keyframes hl-kiilto { from { background-position: 150% 0; } to { background-position: -50% 0; } }
@media (prefers-reduced-motion: reduce) {
    ::view-transition-group(*), ::view-transition-old(*), ::view-transition-new(*) { animation: none !important; }
    .hl-luuranko * { animation: none !important; }
}

/* Kuura painalluksiin (Petteri 4.11.2026), js/kuura.js. Kuvio kasvaa kulmista,
   pysyy hetken ja sulaa; ulompi kerros harventaa sen keskeltä, jotta teksti
   pysyy luettavana. Kuura on painikkeen sisällä tekstin alla. */
@property --kuura-k { syntax: '<percentage>'; inherits: false; initial-value: 0%; }
:root { --kuura-hohde: #7CC0FF; }
/* Kuvio valmiina kuvana, välimuistista (ennen piirrettiin joka sivulla). */
.kuura > i { background-image: url('../img/kuura.webp'); }
.kuura-kohde { isolation: isolate; }
.kuura-suhteellinen { position: relative; }
.kuura { position: absolute; inset: 0; z-index: -1; pointer-events: none; border-radius: inherit; overflow: hidden;
    -webkit-mask: radial-gradient(farthest-side, rgba(0,0,0,.18) 30%, #000 92%);
            mask: radial-gradient(farthest-side, rgba(0,0,0,.18) 30%, #000 92%); }
.kuura > i { position: absolute; inset: 0; border-radius: inherit;
    background-size: 200px 200px;
    -webkit-mask: radial-gradient(circle at 0 0, #000 calc(var(--kuura-k) * .55), transparent var(--kuura-k)),
                  radial-gradient(circle at 100% 100%, #000 calc(var(--kuura-k) * .55), transparent var(--kuura-k)),
                  radial-gradient(circle at 100% 0, #000 calc(var(--kuura-k) * .35), transparent calc(var(--kuura-k) * .7)),
                  radial-gradient(circle at 0 100%, #000 calc(var(--kuura-k) * .35), transparent calc(var(--kuura-k) * .7));
            mask: radial-gradient(circle at 0 0, #000 calc(var(--kuura-k) * .55), transparent var(--kuura-k)),
                  radial-gradient(circle at 100% 100%, #000 calc(var(--kuura-k) * .55), transparent var(--kuura-k)),
                  radial-gradient(circle at 100% 0, #000 calc(var(--kuura-k) * .35), transparent calc(var(--kuura-k) * .7)),
                  radial-gradient(circle at 0 100%, #000 calc(var(--kuura-k) * .35), transparent calc(var(--kuura-k) * .7));
    box-shadow: inset 0 0 10px color-mix(in srgb, var(--kuura-hohde) 45%, transparent);
    animation: kuura-jaatyy 1.9s ease-out forwards; }
@keyframes kuura-jaatyy {
    0% { --kuura-k: 0%; opacity: 0; }
    12% { opacity: .6; }
    40% { --kuura-k: 85%; opacity: .6; }
    65% { --kuura-k: 95%; opacity: .6; }
    100% { --kuura-k: 95%; opacity: 0; } }
/* Valittu kohta (alapalkki, suodatin, välilehti) jää kuuraan: kasvaa kuten
   painallus, mutta ei sula. */
.kuura.kuura-jaa > i { animation: kuura-jaa 1.2s ease-out forwards; }
@keyframes kuura-jaa {
    0% { --kuura-k: 0%; opacity: 0; }
    20% { opacity: .6; }
    100% { --kuura-k: 95%; opacity: .6; } }
.kuura-pysyva > i { animation: none; --kuura-k: 95%; opacity: .6; }
/* Plussan rullan päädyt ovat aina hieman huurteessa ja sumeita, kuin jäisen
   lasin takana: kertoo, että rulla jatkuu (Petteri 4.11.2026). */
.plussa-kaari-ympyra { position: relative; overflow: hidden; isolation: isolate; }
.plussa-kaari-ympyra .kuura-pysyva { opacity: var(--huurre, 0); transition: opacity .25s; }
.plussa-kaari-ympyra .kuura-pysyva > i { animation: none; --kuura-k: 95%; opacity: .7; }
.plussa-kaari-ympyra .kuura-pysyva { -webkit-mask: radial-gradient(farthest-side, rgba(0,0,0,.35) 35%, #000 95%);
            mask: radial-gradient(farthest-side, rgba(0,0,0,.35) 35%, #000 95%); }
/* Sumennus vain kuvakkeeseen, kuura pysyy terävänä (Petteri 4.11.2026:
   "älä sitä kuuraa sumenna"). --sumeus ja --huurre tulevat rullan skriptistä. */
.plussa-kaari-ympyra svg { filter: blur(var(--sumeus, 0px)); transition: filter .25s; }
/* --huurre asetetaan rullan skriptissä: ylin 0, viereiset noin 0,3, alimmat 1. */
.plussa-kaari-ympyra { color: color-mix(in srgb, var(--primary) calc(100% - 30% * var(--huurre, 0)), var(--pinta)); }
@media (prefers-reduced-motion: reduce) { .kuura > i { animation: none; opacity: 0; } }

/* Muistilistan kirjoitus ponnahdusikkunassa (Petteri 4.11.2026). Puhelimella
   alhaalta nouseva paneeli, tietokoneella keskellä. Tekstikenttä 16 px, ettei
   iPhone suurenna sivua, ja tarpeeksi korkea muokattavaksi. */
.muistio-ikkuna { border: 0; padding: 0; border-radius: 16px; width: min(560px, calc(100vw - 32px));
    background: var(--pinta); color: var(--teksti); box-shadow: 0 12px 40px rgba(0, 0, 0, .3); }
.muistio-ikkuna::backdrop { background: rgba(11, 22, 40, .55); }
.muistio-lomake { display: grid; gap: 12px; padding: 16px; }
.muistio-otsikko { display: flex; align-items: center; justify-content: space-between; gap: 8px; font-size: 16px; }
.muistio-sulje { min-width: 44px; min-height: 44px; border: 0; background: none; color: var(--teksti-toissijainen); cursor: pointer; }
.muistio-lomake .muistio-teksti { font-size: 16px; line-height: 1.5; height: auto; min-height: 34vh; resize: vertical; }
.muistio-napit { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.muistio-napit .btn-primary { margin-left: auto; min-height: 44px; }
.muistio-laskuri { font-size: 12px; color: var(--teksti-toissijainen); }
.muisti-muokkaa-nappi { border: 0; background: none; padding: 8px 0; min-height: 44px; color: var(--teksti-toissijainen);
    font: inherit; font-size: 13px; cursor: pointer; display: inline-flex; align-items: center; gap: 4px; }
.muisti-lisaa { min-height: 44px; }
@media (max-width: 768px) {
    .muistio-ikkuna { margin: auto 0 0; width: 100vw; max-width: 100vw; border-radius: 16px 16px 0 0;
        padding-bottom: env(safe-area-inset-bottom, 0px); }
    .muistio-lomake .muistio-teksti { min-height: 38vh; }
}

/* Muistilistan järjestys raahaamalla kahvasta (4.11.2026). Kahva 44 px, ei vieritä
   sivua raahatessa. */
.muisti-kahva { width: 44px; min-height: 44px; flex-shrink: 0; border: 0; border-radius: 8px; cursor: grab;
    touch-action: none; background-color: transparent;
    background-image: radial-gradient(circle, var(--teksti-toissijainen) 1.6px, transparent 2px);
    background-size: 7px 7px; background-repeat: repeat; background-position: center;
    -webkit-mask: linear-gradient(#000, #000) center / 14px 20px no-repeat; mask: linear-gradient(#000, #000) center / 14px 20px no-repeat; }
.muisti-kahva:focus-visible { outline: 3px solid var(--primary); outline-offset: 2px; }
.muisti-rivi.raahataan { background: var(--pinta-korostettu); border-radius: 10px; box-shadow: 0 6px 18px color-mix(in srgb, var(--teksti) 18%, transparent); }
.muisti-rivi.raahataan .muisti-kahva { cursor: grabbing; }

/* Muistilistan näkyvyys värikoodattuna (4.11.2026): tilaaja vihreä, aliurakoitsijat sininen. */
.muisti-nakyvyys { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 4px; }
.muisti-merkki { display: inline-flex; align-items: center; gap: 4px; font-size: 12px; font-weight: 600;
    padding: 2px 8px; border-radius: 999px; }
.muisti-merkki.tilaaja { background: var(--tila-ok-tausta); color: var(--tila-ok-teksti); }
.muisti-merkki.au { background: var(--tila-tieto-tausta); color: var(--tila-tieto-teksti); }
.muisti-nakyvyysnappi { margin: 0 0 8px; min-height: 44px; }
.muisti-valintalista { display: grid; gap: 2px; max-height: 45vh; overflow-y: auto; border: 1px solid var(--reuna);
    border-radius: 10px; padding: 4px 10px; }
.muisti-valintalista .muisti-merkki { margin-left: 6px; }
.muisti-nakyvyystoimet .btn { min-height: 44px; }

/* Materiaalikilpailutuksen toimittajavalinta (4.11.2026): kaikki valittavissa, posti valitulle. */
.rfq-toimittajahaku { max-width: 360px; margin-bottom: 8px; }
.rfq-toimittajat { display: grid; grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); gap: 4px 12px; max-height: 420px; overflow-y: auto; }
.rfq-toimittaja-valinta { display: flex; align-items: center; gap: 8px; font-size: 13px; min-height: 44px; cursor: pointer; }
.rfq-toimittaja-valinta input { flex-shrink: 0; }
.rfq-toimittaja-posti { font-size: 13px; padding: 5px 8px; margin: 0 0 6px 24px; width: calc(100% - 24px); }

/* Materiaalikilpailutuksen rivit (4.11.2026): nimi rivittyy, puhelimella kortit. */
.rfq-valintapalkki { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; margin: 4px 0 6px; }
.rfq-kaikki { display: none; align-items: center; gap: 8px; min-height: 44px; font-weight: 600; cursor: pointer; }
.rfq-kaikki input { width: 22px; height: 22px; }
.rfq-rivit tr.rfq-pois .rfq-nimi, .rfq-rivit tr.rfq-pois input[type=text] { opacity: 0.55; }
.rfq-toimittajavihje { margin: -2px 0 8px; }
.rfq-rivit .rfq-nimi { font-size: 13px; padding: 5px 8px; resize: none; overflow: hidden; min-height: 34px; line-height: 1.35; }
@media (max-width: 640px) {
    .rfq-rivit thead { display: none; }
    .rfq-rivit tr { display: grid; grid-template-columns: 34px 96px 84px 1fr; gap: 4px 8px; padding: 10px 0; border-bottom: 1px solid var(--reuna); }
    .rfq-rivit td { padding: 0 !important; border: 0 !important; background: none !important; }
    .rfq-rivit td:nth-child(1) { grid-row: 1 / 3; display: flex; align-items: flex-start; padding-top: 2px !important; }
    .rfq-rivit td:nth-child(1) input { width: 22px; height: 22px; }
    .rfq-rivit td:nth-child(2) { grid-column: 2 / 5; }
    .rfq-rivit td:nth-child(3) { grid-column: 2; }
    .rfq-rivit td:nth-child(4) { grid-column: 3; }
    /* Nimi luetaan tekstinä, muokattavissa napauttamalla (16 px estää iOS:n zoomauksen). */
    .rfq-rivit .rfq-nimi { border: 0; background: transparent; padding: 0; min-height: 0; font-size: 16px; font-weight: 600; line-height: 1.3; color: var(--teksti); box-shadow: none; }
    .rfq-rivit .rfq-nimi:focus { background: var(--pinta); outline: 2px solid var(--primary); outline-offset: 2px; border-radius: 4px; }
    .rfq-rivit input[type=text] { font-size: 16px; min-height: 40px; padding: 6px 8px; }
    .rfq-rivit td div { margin-top: 2px !important; }
    .rfq-kaikki { display: inline-flex; }
    .rfq-toiminnot .btn { flex: 1 1 100%; min-height: 48px; justify-content: center; }
    .rfq-toimittajat { grid-template-columns: 1fr; max-height: none; }
}
