/* ============================================================================
   POHJA demo7 — TUMMA MODERNI TECH "GLOW"
   Viileä, tekninen, premium. Hehkuvat gradientit + lasimaiset (glass) pinnat +
   mono-aksentti. Sopii: IT, autoala, turva, teollisuus, startupit, automaatio.
   TEEMA VAIHDETAAN VAIN TÄSTÄ :root-lohkosta. Kaikki muu perii muuttujat.
   ============================================================================ */

:root {
  --c-accent: #C12EF6;   /* 🔵 FOORLYN MAGENTA — poimittu logon liukuvärin oikeasta päästä */
  /* --- Efektivärit: teemattaessa vaihdetaan nämä, ei CSS:n sisältä käsin --- */
  --c-ok-bright: #34D399;   /* paneelin ok-arvo */
  /* Lomakkeen tilaviestit — teemattavissa kuten muutkin värit */
  --c-ok:  #34D399;
  --c-err: #F87171;
  /* ---- VÄRIT — FOORLY (brändinavy #111038 + brändicyan #32F1FF) ----
     Brändinavy on pintaväri (--c-surface), tausta viedään sitä syvemmäksi
     jotta kortit erottuvat. Cyan on hehkun kärki, kuten foorly.com:issa. */
  --c-bg:        #07071A;   /* syvin navy — sivun tausta */
  --c-bg-2:      #0B0B23;   /* osio-vaihtelu */
  --c-surface:   #111038;   /* 🔵 FOORLYN BRÄNDINAVY — kortti / glass-pohja */
  --c-surface-2: #191847;   /* nostettu pinta */
  --c-text:      #CDD0E6;   /* leipä (viileä, navy-vivahde). #B0B4D2 → #CDD0E6 2.10.2026 (Valtteri: "liian harmaita, menee aikaa lukemiseen") */
  --c-heading:   #F5F6FD;   /* otsikot (lähes valkoinen) */
  /* 🛑 Oli #6E7299 = 3,92–4,36:1 taustoja vasten eli ALLE WCAG AA:n (4,5:1).
     Koski koko sivustoa: murupolut, osioiden ingressit, FAQ-vastaukset,
     taulukoiden kuvausrivit, lomakkeen suostumusteksti. Nyt 5,29–5,89:1,
     mutta yhä selvästi haaleampi kuin --c-text (8,9–9,9:1) → hierarkia säilyy.
     Mitattu 20.9.2026. Älä tummenna takaisin. */
  --c-muted:     #9DA2C6;   /* haalea, WCAG AA. #8388B0 → #9DA2C6 2.10.2026 (vaaleampi, hierarkia --c-textin alla säilyy) */
  /* Vastausteksti, joka on luettavaa pitkissä pätkissä: UKK-vastaukset olivat
     --c-mutedilla ja näyttivät haalealta (Valtteri 21.9.2026). Tämä on lähes
     valkoinen mutta ei kirkkaampi kuin otsikko, joten hierarkia säilyy.
     🛑 21.9.2026, jälkikierros: sama koski myös .lead-ingressiä, hinnaston
     .pdesc-kuvausta, yhteystietojen puhelin/sähköposti-rivejä, tiimikorttien
     yhteystietolinkkejä ja logowall-huomautusta — kaikki sisältöä joka pitää
     PYSTYÄ lukemaan, ei pelkkää sivulabelia. Nostettu --c-text-2:ksi
     (14,66–16,06:1 mitattuna --c-bg / --c-bg-2 / --c-surface -taustoja
     vasten). Pikkulabelit (dt, placeholder, taulukon otsikkorivi, hinnan
     pienpainos, murupolku, lähialueet-etäisyys, tehtäväkortin tag) pysyvät
     --c-mutedilla (5,29–5,80:1, yhä WCAG AA) — hierarkia säilyy. */
  --c-text-2:    #E4E6F5;   /* vahva leipäteksti, 14,66–16,06:1 taustoja vasten */
  /* Tekstipalsta 720 → 900 px (Valtteri 22.9.2026: "ei noin kapeita, käy koko
     sivusto läpi, samoin blogit ja artikkelit"). Säiliö on 1160 px, josta
     täytteiden jälkeen jää 1080 px käyttöön, joten palsta on nyt 83 % siitä ja
     tekstiosiot lukeutuvat yhtä leveiksi kuin ruudukko-osiot niiden ympärillä.
     🛑 Muutos vaati myös leipätekstin nostamisen 1,06 remiin (ks. `.prose > p`):
     pelkkä leventäminen olisi vienyt rivin noin 105 merkkiin, mikä on liikaa.
     Isommalla kirjasimella rivi on noin 98 merkkiä. Älä leveennä tästä ilman
     että kasvatat samalla kirjasinkokoa. */
  --palsta:      1040px;    /* artikkelien ja tekstisivujen palstan leveys (ks. .prose). 900 → 1040 (Valtteri 2.10.2026: "leveys voisi ulottua enemmän oikealle"), sama kuin otsikoiden raja */
  --c-border:    #262552;   /* hiusviiva navylla */
  --c-primary:   #6B5CF6;   /* 🔵 FOORLYN VIOLETTI — logon liukuvärin keskikohta (#7E76F9) */
  --c-primary-2: #5A48E8;   /* hover */
  --c-on-primary:#FFFFFF;
  --c-accent2:   var(--c-accent);   /* alias */   /* violetti (gradientin toinen pää) */
  --c-accent3:   #32F1FF;   /* 🔵 FOORLYN BRÄNDICYAN — hehkun kärki */
  --c-dark:      #050514;   /* syvin (footer) */
  --c-on-dark:   #C6CAE4;

  /* ---- LIUKUVÄRIT + HEHKU ---- */
  /* Päägradientti = Foorlyn logon liukuväri: cyan -> violetti -> magenta.
     Käytössä otsikkokorostuksissa, merkeissä ja hehkuissa. */
  --grad:      linear-gradient(118deg, var(--c-accent3) 0%, var(--c-primary) 48%, var(--c-accent2) 100%);
  /* Painikkeille oma versio ilman cyania: valkoinen teksti ei erotu riittävästi
     vaalean cyanin päältä. Sama brändiliuku, tummempi pää. */
  --grad-btn:  linear-gradient(118deg, var(--c-primary) 0%, var(--c-accent2) 100%);
  --grad-soft: linear-gradient(118deg, color-mix(in srgb, var(--c-primary) 90%, #fff), var(--c-accent2));
  /* hero-orbit: isot, sumeat hehkupallot taustalle */
  --glow-cyan: radial-gradient(42% 50% at 12% 8%, color-mix(in srgb, var(--c-accent3) 30%, transparent), transparent 70%);
  --glow-vio:  radial-gradient(46% 54% at 92% 18%, color-mix(in srgb, var(--c-accent2) 32%, transparent), transparent 70%);
  --glow-blue: radial-gradient(50% 60% at 50% 120%, color-mix(in srgb, var(--c-primary) 26%, transparent), transparent 72%);
  /* hieno verkkoviiva-grid taustaksi (tech-tuntu) */
  --grid-lines: linear-gradient(var(--c-border) 1px, transparent 1px),
                linear-gradient(90deg, var(--c-border) 1px, transparent 1px);

  /* ---- MUODOT (pehmeästi pyöristetty, premium) ---- */
  --r-sm: 10px;
  --r:    16px;
  --r-lg: 24px;
  --r-pill: 999px;

  /* ---- VARJOT + HEHKU ---- */
  --shadow:    0 24px 60px -28px rgba(0,0,0,.9);
  --shadow-lg: 0 40px 90px -34px rgba(0,0,0,.95);
  --glow-primary: 0 0 0 1px color-mix(in srgb, var(--c-primary) 40%, transparent),
                  0 20px 50px -18px color-mix(in srgb, var(--c-primary) 55%, transparent);
  --ring: 0 0 0 1px color-mix(in srgb, #ffffff 8%, transparent);

  /* ---- TYPOGRAFIA (geometrinen display + selkeä leipä + mono-aksentti) ---- */
  --font-display: 'Sora', 'Sora Fallback', system-ui, -apple-system, sans-serif;
  --font-body:    'Inter', 'Inter Fallback', system-ui, -apple-system, sans-serif;
  --font-mono:    'JetBrains Mono', 'JetBrains Mono Fallback', ui-monospace, 'SFMono-Regular', Menlo, monospace;

  /* ---- MITAT ---- */
  --container: 1160px;
  --pad-x: clamp(18px, 5vw, 40px);
  --gap: clamp(18px, 2.4vw, 26px);
  --section-y: clamp(64px, 9vw, 120px);
}

*, *::before, *::after { box-sizing: border-box; }
/* overflow-x myös html-tasolle: body-tason sääntö ei estä juuritason vaakavieritystä,
   ja kapeilla näytöillä pseudo-elementti (hehku/koriste) sai sivun vierimään ~10 px.
   Mitattu 360 px:llä 20.9.2026 — DOM-kysely ei löytänyt syytä, koska ::before/::after
   eivät näy querySelectorAll'issa. */
html { -webkit-text-size-adjust: 100%; scroll-padding-top: 84px; overflow-x: hidden; }
/* 🛑 `overflow-x: hidden` POISTETTU bodyltä 22.9.2026. Se on html-tasolla (rivi
   yllä) ja siellä se riittää. Bodyllä se rikkoi kaksi asiaa kerralla:
   kun html:llä on non-visible overflow, bodyn oma overflow EI enää propagoidu
   viewportiin vaan body muuttuu omaksi vierityssäiliökseen. Silloin
   `position: sticky` navi tarttuu bodyyn, joka ei koskaan vieri, joten navi ei
   jää ruudun ylälaitaan (Valtteri: "valikko ei pysy stickynä"), ja samasta
   syystä ankkurivieritys käyttäytyi väärin. Älä palauta bodylle. */
body {
  margin: 0; background: var(--c-bg); color: var(--c-text);
  font-family: var(--font-body); line-height: 1.65; font-size: 1rem;
  -webkit-font-smoothing: antialiased;
}
a { color: inherit; text-decoration: none; -webkit-tap-highlight-color: transparent; }
img { max-width: 100%; height: auto; display: block; }
button { font: inherit; -webkit-tap-highlight-color: transparent; }
:focus-visible { outline: 2px solid var(--c-accent3); outline-offset: 3px; border-radius: 4px; }
/* Ohjelmallisen fokuksen kohteet (tabindex="-1": lomakkeen vaiheotsikot,
   hyppylinkin kohde) eivät ole painettavia, joten niille ei piirretä reunusta. */
[tabindex="-1"]:focus, [tabindex="-1"]:focus-visible { outline: none; }

.skip-link { position: absolute; left: -9999px; top: 0; z-index: 200; background: var(--c-primary); color: #fff; padding: 12px 18px; border-radius: 0 0 10px 0; font-weight: 700; }
.skip-link:focus { left: 0; }

.wrap { max-width: var(--container); margin-inline: auto; padding-inline: var(--pad-x); }
.section { padding-block: var(--section-y); position: relative; }
.section--tint { background: var(--c-bg-2); }
/* Hehkuosio (Valtteri 22.9.2026): sama tausta kuin CTA-nauhassa, mutta koko
   osion levyisenä ilman korttikehystä. Tarkoitus on rikkoa pitkän sivun rytmi
   kolmannella taustalla `--tint`in rinnalle, ei korostaa yhtä sisältöä.
   🛑 EI KOSKAAN suoraan heron alle: hero käyttää samoja hehkuja, joten kaksi
   peräkkäistä hehkukerrosta sulautuu yhdeksi eikä osioiden raja erotu.
   Väliin on jäätävä vähintään yksi tavallinen tai --tint-osio. */
.section--hehku { background: linear-gradient(135deg, var(--c-surface), var(--c-bg-2)); overflow: hidden; }
.section--hehku::before { content: ""; position: absolute; inset: 0; pointer-events: none;
  background: var(--glow-cyan), var(--glow-vio); opacity: .7; }
.section--hehku > .wrap { position: relative; z-index: 1; }
/* Hiusviivat ylä- ja alareunaan, jotta osio irtoaa naapureistaan myös silloin
   kun viereinen osio on tummempi kuin hehkun vaalein kohta. */
.section--hehku { border-block: 1px solid var(--c-border); }
.section--snug { padding-block: clamp(48px, 7vw, 88px); }

/* ======== LOMAKEOSIO = SIVUSTON TAVOITE (Valtteri 22.9.2026) ========
   🛑 Tämä osio on se, jota varten koko sivusto on olemassa, joten se saa
   erottua muista. Keinot on valittu niin, ettei muu ulkoasu muutu:
   1) LEVEYS: palsta 1320 px muiden 1160 px:n sijaan, jolloin lomake tulee
      näkyvästi ulos muun sivun linjasta ja luetaan omana asianaan.
   2) OTSIKKO yhtä astetta suurempi kuin muissa osioissa.
   3) KORTIN REUNA brändiväriin ja hehku voimakkaammaksi.
   🛑 Taustaa EI värjätä: yläpuolella on jo .section--tint, ja kaksi peräkkäistä
   värjättyä kaistaa söisi juuri sen eron jota tässä haetaan. */
.section--lomake > .wrap { max-width: 1320px; }
.section--lomake .sec-head h2 { font-size: clamp(2.05rem, 4.1vw, 3.2rem); }
.section--lomake .mvf { border-color: color-mix(in srgb, var(--c-primary) 45%, var(--c-border));
  box-shadow: var(--shadow-lg), var(--glow-primary); }

/* ---- TYPOGRAFIA-ASTEIKKO ---- */
h1, h2, h3, h4 { font-family: var(--font-display); color: var(--c-heading); line-height: 1.08; letter-spacing: -.02em; margin: 0; font-weight: 700; }
h1 { font-size: clamp(2.3rem, 5.6vw, 4rem); font-weight: 800; letter-spacing: -.035em; }
h2 { font-size: clamp(1.85rem, 3.6vw, 2.85rem); }
h3 { font-size: 1.22rem; }
p  { margin: 0 0 1rem; }
.grad-text { background: var(--grad); -webkit-background-clip: text; background-clip: text; color: transparent; }

/* ---- FOORLYN TUNNUS: logon viisto kulma ----
   Foorlyn liikemerkki on kulmikas F, jossa on 45°:n viistot leikkaukset. Sama
   viisto toistuu koko sivustolla pienenä gradienttimerkkinä: väliotsikoissa,
   listamerkeissä ja korttien yläreunassa. Se erottaa sivuston pohjasta ilman
   että mitään koristetta lisätään — merkki on brändin oma muoto.
   Mono-fontti (JetBrains Mono) poistettu väliotsikoista: se on demo7-pohjan
   "tech"-signaali eikä sovi digitoimistolle, jonka asiakkaat ovat yrittäjiä. */
.eyebrow { display: inline-flex; align-items: center; gap: 10px;
  font-family: var(--font-body); font-size: .76rem; font-weight: 700;
  letter-spacing: .14em; text-transform: uppercase; color: var(--c-accent3);
  margin-bottom: 18px; }
/* 🛑 Merkki on LEVEÄ suunnikas, ei neliö (Valtteri 22.9.2026). Syy on liukuväri:
   13 px:n levyisellä palalla cyanista magentaan ehti näkyä vain yksi sävy, joten
   merkki näytti tasavärisen violetilta laatalta. Foorlyn liikemerkissä F:n palkit
   ovat leveitä ja koko liukuväri juoksee niiden yli — sama tehdään tässä.
   Leveys 34 px, korkeus 12 px, ja gradientti on loivennettu 100°:een niin että
   se kulkee palkin pitkittäissuunnassa eikä poikki. Viistot kulmat ovat 45°
   suhteessa korkeuteen, joten `polygon`in vaakaprosentti skaalautuu leveyden
   mukana: 12 px / 34 px ≈ 35 %. */
.eyebrow::before { content: ""; width: 34px; height: 12px; flex: none;
  background: linear-gradient(100deg, var(--c-accent3) 0%, var(--c-primary) 52%, var(--c-accent2) 100%);
  clip-path: polygon(35% 0, 100% 0, 65% 100%, 0 100%); }

/* Väliotsikkolohko 640 → 780 → 1040 px (Valtteri 22.9.2026: "Kotisivut valmiina
   neljässä vaiheessa mahtuisi 1 riville tietokoneella, katso onko muitakin
   turhaan 2 rivillä").
   🛑 Mitattu, ei arvattu: 780 px:n lohkossa **29 väliotsikkoa 1440 px:n
   leveydellä katkesi kahdelle riville**, vaikka 27 niistä olisi mahtunut
   yhdelle. Otsikon tarvitsema leveys vaihteli 817 ja 1232 pikselin välillä,
   ja lohkossa oli tilaa 780. Säiliön sisäleveys on 1080 px, joten 1040 kattaa
   kaikki paitsi kaksi pisintä.
   Ingressillä on OMA kapeampi rajansa alempana: se on leipätekstiä, eikä
   1040 px:n rivi ole luettava. Otsikko ja ingressi eivät siis jaa leveyttä. */
.sec-head { max-width: 1040px; margin-bottom: clamp(34px, 5vw, 54px); }
.sec-head--center { margin-inline: auto; text-align: center; }
/* 🛑 Keskitetyn otsikkolohkon ingressi EI ollut keskellä (Valtteri 22.9.2026:
   "jotain outoa tasauksissa"). `text-align: center` keskitti vain tekstin
   rivin sisällä, mutta ingressin oma laatikko on kapeampi (max-width 900 px)
   kuin otsikon 1160 px, ja ilman automaarginia se jäi kiinni vasempaan
   reunaan. Silmä näki otsikon ja ingressin eri keskikohdilla. */
.sec-head--center .lead { margin-inline: auto; }
.lead { color: var(--c-text-2); }
/* Väli otsikon ja ingressin väliin AINA, ei vain .sec-head-kääreen sisällä.
   Ilman tätä otsikko ja teksti olivat kiinni toisissaan kaikissa osioissa,
   joissa ingressi ei ole .sec-headissä (Miksi Foorly, Ota yhteyttä …). */
h1 + .lead, h2 + .lead, h3 + .lead { font-size: 1.08rem; margin-top: 16px; max-width: var(--palsta); }
/* Ingressi 68ch → 820 px ja 1,08 → 1,12 rem (Valtteri: "leipäteksti voisi olla
   leveämpi tietokoneella"). Raja on PIKSELEINÄ eikä ch-yksikköinä samasta
   syystä kuin .prose-palstalla: ch on elementin oman fontin "0"-merkin leveys,
   joten sama ch-luku tuottaa eri levyisen laatikon otsikolle ja leipätekstille.
   820 px 17,9 px:n tekstillä on noin 92 merkkiä rivillä. */
.sec-head .lead { font-size: 1.08rem; margin-top: 16px; max-width: var(--palsta); }
/* Keskitetty ingressi kapeammaksi (QA 30.9.2026): 900 px:n palstalla keskitetty
   teksti oli noin 100 merkkiä rivillä, jolloin rivin alun löytäminen vaikeutuu.
   Vasemmalle tasatut ingressit pysyvät palstan levyisinä. */
.sec-head.sec-head--center .lead { max-width: 65ch; }

/* ============================ NAPIT ============================ */
.btn { display: inline-flex; align-items: center; justify-content: center; gap: 9px; padding: 14px 24px; border-radius: var(--r-pill); font-weight: 600; font-size: .98rem; border: 1px solid transparent; cursor: pointer; transition: transform .22s, box-shadow .3s, background .3s, border-color .25s; white-space: nowrap; min-height: 48px; }
.btn .arr { transition: transform .25s; }
.btn-primary { background: var(--grad-btn); background-size: 180% 180%; background-position: 0% 50%; background-origin: border-box; background-repeat: no-repeat; color: var(--c-on-primary); box-shadow: var(--glow-primary); }
.btn-ghost { background: color-mix(in srgb, #fff 4%, transparent); color: var(--c-heading); border-color: var(--c-border); }
.btn-call { background: transparent; color: var(--c-heading); border-color: var(--c-border); }
.btn-call svg { color: var(--c-accent3); }
/* Pitkä painiketeksti + `white-space: nowrap` levittää napin yli kapean näytön
   ja aiheuttaa koko sivun vaakaylivuodon. Mitattu 20.9.2026: 412px leveä nappi
   375px:n näytöllä. Kapealla ruudulla teksti saa rivittyä. */
@media (max-width: 640px) {
  .btn { white-space: normal; text-align: center; }
}
@media (hover: hover) {
  .btn-primary:hover { background-position: 100% 50%; transform: translateY(-2px); }
  .btn-primary:hover .arr { transform: translateX(4px); }
  /* Haamunapin hover oli pelkkä reunan sävyero, joka ei erottunut tummalla
     taustalla (Valtteri 22.9.2026). Nyt mukana taustan kirkastus ja sama
     cyan-hehku kuin muissakin nostoissa, jotta osumakohta tuntuu. */
  .btn-ghost:hover, .btn-call:hover {
    border-color: color-mix(in srgb, var(--c-primary) 75%, var(--c-border));
    background: color-mix(in srgb, var(--c-primary) 14%, transparent);
    color: var(--c-heading); transform: translateY(-2px);
    box-shadow: 0 8px 22px -14px color-mix(in srgb, var(--c-accent3) 70%, transparent);
  }
}

/* ============================ HERO ============================ */
/* Hero: matalampi yläreuna, sisältö lähemmäs navia (Valtteri 20.9.2026) */
/* Ylätäyte sisältää navin korkeuden (76 px), koska navi on bannerin päällä
   negatiivisella marginaalilla (shared.js). Näin otsikko ei jää navin alle. */
.hero { position: relative; padding-block: calc(76px + clamp(44px, 6vw, 76px)) clamp(48px, 7vw, 84px); overflow: hidden; }
/* 🛑 `pointer-events: none` ja `.hero .wrap`in pinojärjestys ovat pakollisia, eivät
   kosmetiikkaa (mitattu Playwrightilla 22.9.2026). Hehkukerros on positioitu
   (`position: absolute`), joten se maalautuu KAIKKIEN heron staattisten lasten
   päälle vaikka z-index on 0. Etusivulla `.hero-grid` nosti sisällön omaan
   kerrokseensa, mutta alasivujen heroissa sisältö on suoraan `.wrap`in sisällä:
   siellä hehku peitti murupolun ja hero-napit kokonaan. Klikki ei mennyt perille
   eikä hover laukennut — Valtterin havainto "ankkurilinkit eivät toimi eikä hover
   sano mitään" kokemuksia.html:ssä. Koski jokaista alasivun heroa. */
.hero::before { content: ""; position: absolute; inset: 0; z-index: 0; pointer-events: none; background: var(--glow-cyan), var(--glow-vio), var(--glow-blue); }
.hero > .wrap { position: relative; z-index: 1; }
/* Taustan ruudukko poistettu (Valtteri 20.9.2026) — hehkut jäävät */
.hero-grid { position: relative; z-index: 1; display: grid; grid-template-columns: 1.05fr .95fr; gap: clamp(28px, 5vw, 56px); align-items: center; }
.hero-copy { min-width: 0; }
.hero h1 { margin-bottom: 20px; }
/* 46 → 58ch ja .cta-band p 52 → 72ch (Valtteri 3.10.2026: lyhyt teksti ei turhaan kahdelle riville) */
.hero-sub { font-size: clamp(1.05rem, 1.7vw, 1.22rem); color: var(--c-on-dark); max-width: 58ch; margin-bottom: 30px; }
.hero-cta { display: flex; flex-wrap: wrap; gap: 12px; }
/* Riskinpoisto kuuluu painikkeen viereen, ei leipätekstiin: lukija lukee
   ehdon juuri silloin kun hän epäröi klikkausta (katselmus 23.9.2026, K4).
   🛑 Ei sanamuotoa "ei sitoutumista": se poistettiin 31.8.2026, koska se
   koskee vain esikatselua ja sitoutuminen alkaa sopimuksesta. */
/* Lukuväli ei katkea riville kahtia: selain tarjoaa rivinvaihtopaikan heti
   ajatusviivan jälkeen, joten "1–2 arkipäivässä" katkesi muotoon "1–" ja "2".
   Näkyi herossa isolla kirjasimella sekä ingressissä että mikrokopiossa. */
.nowrap { white-space: nowrap; }

/* Trustindexin arvostelukaruselli (kokemuksia.html). min-height varaa tilan
   ennen kuin kolmannen osapuolen skripti on latautunut, jotta sivu ei hyppää.
   🛑 Widgetin oma ulkoasu tulee Trustindexistä, ei täältä: jos se ei erotu
   tummalta taustalta, korjaus tehdään Trustindexin puolella eikä ylikirjoittamalla
   sen tyylejä täällä. */
/* min-height on CLS-lattia, ei kiinteä korkeus. Mitattu 23.9.2026: widgetin oma
   sisältö on 197 px työpöydällä ja 220 px puhelimessa, joten 320 px jätti noin
   100 px tyhjää osion alalaitaan. 230 px riittää varaukseksi ja kasvaa itsestään
   jos arvosteluja tulee lisää.
   🛑 Widgetin sarakemäärä tulee luokasta ti-col-3 eli Trustindexin omasta
   asetuksesta, eikä sitä korjata täältä: 390 px:llä kolme saraketta kutistaa
   kortin 122 px:iin ja nimet katkeavat. Muutos tehdään Trustindexin hallinnasta
   (mobiilisarakkeet 1), jolloin se pysyy voimassa myös widgetin päivittyessä. */
.ti-wrap { min-height: 230px; margin-top: 28px; }
/* 🛑 Ainoa poikkeus yllä olevaan "ei ylikirjoiteta" -sääntöön (30.9.2026):
   arvostelun päivämäärä on Trustindexin omalla #767676:lla #F4F4F4-kortissa
   4,13:1 eli alle AA:n. Pelkkä tekstiväri, joka ei riipu widgetin asettelusta,
   joten widgetin päivitys ei voi rikkoa sitä. #5F5F5F = 5,81:1 samaa pohjaa vasten. */
.ti-wrap .ti-widget .ti-date { color: #5F5F5F !important; }
.hero-note { margin: 14px 0 0; font-size: .92rem; line-height: 1.5;
  color: var(--c-text-2); max-width: 58ch; }
/* Nostorivi: tasavälinen, erottimet, selitteet luettavina (Valtteri 20.9.2026 —
   olivat --c-muted eli 3,4:1 tummalla taustalla, nyt --c-on-dark eli 11,3:1) */
.hero-metrics { display: grid; grid-template-columns: repeat(3, minmax(0, max-content));
  justify-content: start; align-items: stretch; gap: 0;
  margin-top: 34px; padding-top: 26px; border-top: 1px solid var(--c-border); }
.hero-metrics .m { padding-right: clamp(22px, 3.2vw, 42px); }
.hero-metrics .m + .m { padding-left: clamp(22px, 3.2vw, 42px); border-left: 1px solid var(--c-border); }
.hero-metrics .m b { font-family: var(--font-display); display: block;
  font-size: clamp(1.45rem, 2.3vw, 1.85rem); font-weight: 800; line-height: 1.05;
  letter-spacing: -.025em; color: var(--c-heading); }
.hero-metrics .m span { display: block; margin-top: 6px; font-size: .84rem; line-height: 1.35;
  color: var(--c-on-dark); }
/* 🛑 Kolme lukua pysyvät yhdellä rivillä myös puhelimessa (katselmus 23.9.2026,
   K16). Aiemmin ne pinottiin allekkain, jolloin hero oli 390 px:llä 1 238 px
   korkea ja ensimmäinen sosiaalinen todiste eli logonauha alkoi vasta 1,5
   ruudun päästä. Kapea sarake kestää tämän, koska luvut ovat lyhyitä
   (350 €, 1–2, 300+) ja selitteet rivittyvät kahdelle riville. */
@media (max-width: 560px) {
  .hero-metrics { grid-template-columns: repeat(3, 1fr); gap: 0; }
  .hero-metrics .m { padding-right: 10px; }
  .hero-metrics .m + .m { padding-left: 10px; }
  .hero-metrics .m b { font-size: 1.28rem; }
  .hero-metrics .m span { font-size: .78rem; margin-top: 4px; } /* 12,5 px; .74rem oli 11,8 px (QA 30.9.2026) */
}

.hero-visual { position: relative; z-index: 1; }

/* ============================ LOGO-MARQUEE ============================ */
.marquee { border-block: 1px solid var(--c-border); padding-block: 22px; overflow: hidden; position: relative; }
.marquee::before, .marquee::after { content: ""; position: absolute; top: 0; bottom: 0; width: 90px; z-index: 2; }
.marquee::before { left: 0; background: linear-gradient(90deg, var(--c-bg), transparent); }
.marquee::after { right: 0; background: linear-gradient(270deg, var(--c-bg), transparent); }
.marquee-row { display: flex; align-items: center; gap: 56px; padding-inline-end: 56px; /* = gap, jotta -50 % osuu täsmälleen kopion alkuun (sauma ilman nykäystä) */ width: max-content; animation: scroll 35s linear infinite; } /* 21 logoa 2.10.2026: puolisko 4019 px, nopeus ~115 px/s */
/* Asiakaslogot (Valtteri 21.9.2026): yhtenäinen korkeus, kevyt himmennys erottuu
   silti tummalta pohjalta täydessä värissä — ei harmaasävyä, koska osa logoista
   menettäisi luettavuutensa. */
.marquee-row img { height: 32px; width: auto; flex: none; opacity: .82; transition: opacity .25s; }
@media (hover: hover) { .marquee-row img:hover { opacity: 1; } }
/* Pitkät tekstilogot matalammiksi, jotta ne eivät hallitse nauhaa (2.10.2026). */
.marquee-row img.matala { height: 24px; }
@keyframes scroll { to { transform: translateX(-50%); } }

/* ============================ BENTO (palvelut) ============================ */
.bento { display: grid; grid-template-columns: repeat(6, 1fr); gap: var(--gap); }
.tile { position: relative; background: var(--c-surface); border: 1px solid var(--c-border); border-radius: var(--r-lg); padding: 28px; overflow: hidden; transition: border-color .25s, transform .25s, box-shadow .3s; }
.tile::after { content: ""; position: absolute; inset: 0; border-radius: inherit; background: radial-gradient(60% 80% at 100% 0%, color-mix(in srgb, var(--c-primary) 14%, transparent), transparent 60%); opacity: 0; transition: opacity .3s; pointer-events: none; }
@media (hover: hover) { .tile:hover { transform: translateY(-4px); border-color: color-mix(in srgb, var(--c-primary) 50%, var(--c-border)); box-shadow: var(--shadow); } .tile:hover::after { opacity: 1; } }
.tile .ico { width: 46px; height: 46px; border-radius: 12px; display: grid; place-items: center; background: color-mix(in srgb, var(--c-primary) 16%, transparent); color: var(--c-accent3); margin-bottom: 18px; border: 1px solid color-mix(in srgb, var(--c-primary) 30%, transparent); }
.tile h3 { color: var(--c-heading); margin-bottom: 8px; }
.tile p { font-size: .96rem; margin: 0; }
.tile .tlink { display: inline-flex; align-items: center; gap: 7px; margin-top: 16px; font-family: var(--font-body); font-size: .9rem; font-weight: 600; letter-spacing: .01em; color: var(--c-accent3); }
/* Kosketusalue 44 px (30.9.2026, oli 24 px): 11 px täyte ylös ja alas, ja
   marginaalit vähennetään saman verran, joten teksti pysyy paikallaan. */
@media (max-width: 860px) {
  .tile .tlink { padding-block: 11px; margin-block: 5px -11px; }
  /* Kortissa linkki työnnetään alas `margin-top: auto`lla, jolloin täyte
     siirtäisi tekstiä. Siellä osuma-alue laajennetaan näkymättömällä
     ::after-kerroksella, joka ei vie tilaa. */
  .card .tlink { position: relative; }
  .card .tlink::after { content: ""; position: absolute; inset: -11px 0; }
  /* QA 30.9.2026: ::after ei näy elementin laatikossa (mitattu 26 px), joten
     osuma-alue kasvatetaan lisäksi alaspäin kortin täytteeseen: 26 + 20 = 46 px.
     Negatiivinen alamarginaali kumoaa täytteen eikä margin-top: auto muutu. */
  .card .tlink { padding-bottom: 20px; margin-bottom: -20px; }
}
.tile--wide { grid-column: span 4; }
.tile--half { grid-column: span 3; }
.tile--third { grid-column: span 2; }
/* iso featured-tile sisältää mini-visualin */
.tile--feat { grid-column: span 3; background: linear-gradient(135deg, var(--c-surface), var(--c-bg-2)); }
/* Rinnakkaiset kortit ovat yhtä korkeita, mutta linkki jäi tekstin perään, joten
   lyhyemmän kortin alaosaan jäi tyhjää ja linkit olivat eri korkeuksilla
   (QA 30.9.2026, etusivun Palvelut ja palvelut.html). Nyt linkki on aina kortin
   alareunassa ja tyhjä tila jää tekstin ja linkin väliin tasaisesti.
   ≤640 px ikonilliset kortit ovat gridejä (tiedoston loppu), joten tämä ei
   koske niitä. */
.bento .tile { display: flex; flex-direction: column; }
.bento .tile > .tlink { margin-top: auto; align-self: flex-start; }
.bento .tile > p:has(+ .tlink) { margin-bottom: 16px; }
@media (max-width: 860px) { .bento .tile > p:has(+ .tlink) { margin-bottom: 5px; } }

/* ============================ STATS-BAND ============================ */
.statband { position: relative; border-radius: var(--r-lg); border: 1px solid var(--c-border); background: linear-gradient(135deg, var(--c-surface), var(--c-bg-2)); padding: clamp(34px, 5vw, 54px); overflow: hidden; }
.statband::before { content: ""; position: absolute; inset: 0; background: var(--glow-vio); opacity: .5; }
.statband-grid { position: relative; display: grid; grid-template-columns: repeat(4, 1fr); gap: 28px; text-align: center; }
.statband .s b { font-family: var(--font-display); display: block; font-size: clamp(2.2rem, 4.4vw, 3.4rem); font-weight: 800; color: var(--c-heading); letter-spacing: -.03em; }
.statband .s b .grad-text { display: inline; font-size: inherit; color: transparent; }
/* 🛑 Ilman yllä olevaa font-sizea ja coloria sääntö `.statband .s span`
   (0,3,1) voittaa `.grad-text`in (0,1,0) ja kaappaa <b>:n sisällä olevalta
   gradienttispanilta sekä koon (54px -> 14px) että läpinäkyvyyden. Luku
   renderöityi pienenä ja harmaana. Havaittu 20.9.2026 lasketuista tyyleistä. */
.statband .s span { color: var(--c-on-dark); font-size: .9rem; }

/* ============================ FEATURE-SPLIT ============================ */
.feature-split { display: grid; grid-template-columns: 1fr 1fr; gap: clamp(30px, 5vw, 64px); align-items: center; }
.feature-list { list-style: none; padding: 0; margin: 22px 0 0; display: grid; gap: 14px; }
/* Listan jälkeen tuleva kappale näytti listan viimeiseltä riviltä: väli oli 0 px
   kun listan margin-bottom ja kappaleen margin-top olivat molemmat nolla, vaikka
   listakohtien välit ovat 14–18 px. Mitattu 20.9.2026 (kotisivujen-yllapito). */
.feature-list + p, .svc-d-list + p { margin-top: 20px; }
/* Sama vika kuin kappaleella yllä, mutta painikkeella: se liimautui kiinni
   listan viimeiseen riviin (Valtteri 22.9.2026). Painike tarvitsee enemmän
   ilmaa kuin kappale, koska sillä on oma tausta ja 48 px korkeus. */
.feature-list + .btn, .svc-d-list + .btn { margin-top: 26px; }
.feature-list li { display: flex; gap: 13px; align-items: flex-start; }
.feature-list .ck { flex: none; width: 26px; height: 26px; border-radius: 8px; display: grid; place-items: center; background: color-mix(in srgb, var(--c-primary) 18%, transparent); color: var(--c-accent3); margin-top: 2px; }
.feature-list b { color: var(--c-heading); font-family: var(--font-display); font-weight: 600; }
/* CSS dashboard-mockup */
.dash { background: var(--c-surface); border: 1px solid var(--c-border); border-radius: var(--r-lg); padding: 22px; box-shadow: var(--shadow-lg); }
.dash-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 18px; }
.dash-head b { font-family: var(--font-display); color: var(--c-heading); font-size: 1rem; }
/* 🛑 Ei badgeja (Valtteri 22.9.2026). Tämä oli pillerimuotoinen "● alv 0 %"
   -merkkilappu; nyt pelkkä hillitty teksti. Sama linja kuin lomakkeen
   hintavihjeessä ja kaksisarakkeisten korttien numerossa alempana. */
.dash-head .pp { font-family: var(--font-body); font-weight: 600; font-size: .78rem; color: var(--c-text-2); }
/* 🛑 Kolmen luvun ruudukko (toteutus / kk / sivumäärä) purettiin 22.9.2026.
   Kaksi vikaa: "alkaen 350 €" ei mahtunut noin 150 px:n sarakkeeseen yhdelle
   riville vaan katkesi kolmelle, ja "4 sivua" luki hintalukuna vaikka se on
   laajuus. Nyt kortissa on kaksi hintariviä hinnaston tapaan (selite vasemmalla,
   summa oikealla) ja sivumäärä on siirretty sisältölistaan. Summat ovat
   oikeassa reunassa allekkain, jolloin ne luetaan yhdellä silmäyksellä. */
.dash-hinta { margin: 0; display: grid; }
.dash-hinta > div { display: flex; align-items: baseline; justify-content: space-between;
  gap: 14px; padding: 14px 0; border-top: 1px solid var(--c-border); }
.dash-hinta > div:first-child { border-top: 0; padding-top: 2px; }
.dash-hinta dt { font-size: .93rem; color: var(--c-on-dark); }
.dash-hinta dd { margin: 0; white-space: nowrap; font-family: var(--font-display);
  font-weight: 700; color: var(--c-heading); font-size: 1.32rem; letter-spacing: -.01em; }
/* "alkaen" ja "/ kk" ovat määreitä, eivät osa summaa: pienempinä ne eivät kilpaile
   luvun kanssa, mutta 🛑 "alkaen" ei saa kadota (hinta on aina alkaen-hinta). */
.dash-hinta dd .alk, .dash-hinta dd .per {
  font-family: var(--font-body); font-weight: 600; font-size: .78rem; color: var(--c-text-2); }
.dash-otsake { margin: 0; padding: 16px 0 0; border-top: 1px solid var(--c-border);
  font-size: .78rem; font-weight: 700; letter-spacing: .12em; text-transform: uppercase;
  color: var(--c-muted); }
/* Pohjan palkkikaavio poistettu 20.9.2026: se esitti dataa jota ei ole olemassa.
   Tilalla se mitä hintaan oikeasti kuuluu. */
.dash-lista { list-style: none; margin: 0; padding: 14px 0 2px; display: grid; gap: 11px; }
.dash-lista li { position: relative; padding-left: 26px; font-size: .92rem; color: var(--c-on-dark); line-height: 1.45; }
/* 🛑 Luettelomerkki oli vino suunnikas (clip-path: polygon), joka luki
   kuviona eikä merkkinä (Valtteri 21.9.2026: "joku ihan random kuvio").
   Nyt sama check-merkki kuin muualla sivustolla, vain CSS:llä piirrettynä,
   koska tämä lista on pelkkiä <li>-rivejä ilman ikoni-SVG:tä. */
.dash-lista li::before { content: ""; position: absolute; left: 2px; top: .30em;
  width: 6px; height: 11px; border: solid var(--c-accent3); border-width: 0 2px 2px 0;
  transform: rotate(45deg); }

/* ============================ TIMELINE (prosessi) ============================
   🛑 Vaiheet ovat kortteja (Valtteri 21.9.2026): aiemmin numero oli pelkkä pieni
   tyylittelemätön teksti otsikon yläpuolella eikä vaiheiden raja erottunut
   mitenkään, joten osio luki sekavana tekstimassana. Kortti antaa jokaiselle
   vaiheelle oman rajansa ja numero nousee liukuvärisenä kiintopisteeksi.
   Vaiheita yhdistänyt vaakaviiva poistettiin: korttien välissä se katkesi
   reunoihin eikä enää kertonut järjestyksestä mitään. */
.timeline { display: grid; grid-template-columns: repeat(4, 1fr); gap: 18px; }
.tstep {
  position: relative; text-align: left;
  background: var(--c-surface);
  border: 1px solid var(--c-border);
  border-radius: var(--r-lg);
  padding: 24px 22px;
  transition: border-color .25s, transform .25s, box-shadow .3s;
}
/* 🛑 Vaiheen numero on 1, 2, 3, 4 eikä 01, 02 (Valtteri 22.9.2026). Nollalla
   täytetty numerointi on tekninen tapa, joka lupaa kymmeniä vaiheita siellä
   missä niitä on neljä. Numero oli myös pelkkää liukuväritekstiä ilman pohjaa,
   jolloin se luki irrallisena kuin sivunumero. Nyt se on kohokuvioinen merkki:
   liukuvärilaatta, jonka yläreunassa on valo ja alareunassa varjo, eli sama
   3d-vaikutelma kuin painikkeissa. Toteutus on CSS:llä, ei kuvalla. */
.tnode {
  width: 54px; height: 54px; border-radius: 17px;
  display: grid; place-items: center; position: relative; overflow: hidden;
  font-family: var(--font-display);
  font-size: 1.55rem; font-weight: 800; line-height: 1; letter-spacing: -.02em;
  color: var(--c-on-primary);
  /* Valo ylhäältä omana kerroksenaan liukuvärin PÄÄLLÄ mutta numeron ALLA:
     taustakerroksena se ei himmennä numeroa, toisin kuin pseudoelementti. */
  /* 🛑 Liukuväri ilman cyania (30.9.2026): `--grad`in cyan-pää antoi
     valkoiselle numerolle vain 2,39–2,65:1 (isolle tekstille vaaditaan 3:1).
     Sama ratkaisu kuin painikkeissa (`--grad-btn`), ja valo on ohuempi (.3 → .14),
     koska se vaalensi juuri numeron yläpuoliskon taustaa. */
  background: linear-gradient(to bottom, rgba(255, 255, 255, .14), rgba(255, 255, 255, 0) 52%), var(--grad-btn);
  box-shadow: 0 12px 24px -12px color-mix(in srgb, var(--c-primary) 85%, transparent),
              inset 0 1px 0 rgba(255, 255, 255, .5),
              inset 0 -8px 16px -8px rgba(0, 0, 0, .38);
  margin-bottom: 16px;
}
.tstep h3 { color: var(--c-heading); margin-bottom: 7px; font-size: 1.08rem; }
.tstep p { font-size: .94rem; margin: 0; }
@media (hover: hover) {
  .tstep:hover { transform: translateY(-3px); box-shadow: var(--shadow);
    border-color: color-mix(in srgb, var(--c-primary) 45%, var(--c-border)); }
}

/* ============================ HINNOITTELU (tasot) ============================ */
.pricing { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--gap); align-items: stretch; }
.pricing--2 { grid-template-columns: repeat(2, 1fr); max-width: 820px; margin-inline: auto; }
.tier { display: flex; flex-direction: column; background: var(--c-surface); border: 1px solid var(--c-border); border-radius: var(--r-lg); padding: 30px; transition: transform .25s, box-shadow .3s, border-color .25s; }
.tier--feat { background: linear-gradient(170deg, color-mix(in srgb, var(--c-primary) 16%, var(--c-surface)), var(--c-surface)); border-color: color-mix(in srgb, var(--c-primary) 55%, var(--c-border)); box-shadow: var(--glow-primary); position: relative; }
/* Merkintä tekstinä, ei kelluvana pillerinä (Valtteri 20.9.2026) */
.tier--feat .badge { display: block; font-family: var(--font-body); font-size: .78rem; font-weight: 700; letter-spacing: .14em; text-transform: uppercase; color: var(--c-accent3); margin-bottom: 8px; }
/* Kortin nimi otsikkona (Valtteri 2.10.2026): pieni versaali-yläotsikko hukkui
   hinnan alle, joten nimi on nyt kortin selvä otsikko. */
.tier .pl { display: block; font-family: var(--font-display); font-size: 1.4rem; font-weight: 700; color: var(--c-heading); letter-spacing: -.01em; line-height: 1.2; }
.tier .price { font-family: var(--font-display); font-size: 2.6rem; font-weight: 800; color: var(--c-heading); margin: 10px 0 4px; letter-spacing: -.03em; }
.tier .price small { font-size: .9rem; font-weight: 500; color: var(--c-muted); margin-left: .35em; }
/* 🛑 Hinta on aina muodossa "alkaen 350 €", ei koskaan pelkkä luku eikä
   "kertamaksu" (talon sääntö). Sana nostetaan omalle rivilleen brändicyanilla,
   jotta se luetaan ennen lukua eikä sitä voi sivuuttaa. */
/* 🛑 30.9.2026: "alkaen" siirrettiin omalta riviltään luvun eteen samalle
   riville. Omalla rivillään se luki tunnisteen jatkona ("KOTISIVUT" / "ALKAEN")
   ja hajotti kortin yläosan kahdeksi labeliksi ennen hintaa. Sana ei kadonnut:
   se on yhä cyan ja versaali, ja se luetaan ennen lukua. */
.tier .price .alkaen { display: inline-block; font-family: var(--font-body); font-size: .8rem;
  font-weight: 700; letter-spacing: .12em; text-transform: uppercase;
  color: var(--c-accent3); }
/* "Tarjouksesta"-kortissa ei ole alkaen-riviä, joten sen hinta nousi noin 25 px
   naapureita ylemmäs ja koko kortin sisältö oli eri linjassa (mitattu 22.9.2026).
   Tyhjä rivi varataan aina, jolloin kolme hintaa ovat samalla korkeudella.
   Korkeus on kiinteä eikä line-height, koska tyhjä lohko ei muodosta riviä:
   `content: ""` + `display: block` on 0 px korkea (mitattu 22.9.2026).
   0,8 rem × 1,65 riviväli = 1,32 rem, sama kuin alkaen-rivillä. */
/* 30.9.2026: tilanvaraus poistettu, koska alkaen on nyt luvun rivillä eikä
   omallaan, joten kaikki hinnat ovat jo samalla korkeudella. */
.tier .price:not(:has(.alkaen))::before { content: none; }
/* Hintarivi joustavaksi: "alkaen", luku ja lisämääre ("+ 25 €/kk") ovat
   samalla rivillä kun ne mahtuvat, ja lisämääre siirtyy kokonaisena seuraavalle
   riville kapeassa kortissa (1024 px, mainontasivu). Rivin vähimmäiskorkeus on
   aina kaksi riviä, joten kortit ovat keskenään linjassa riippumatta siitä,
   rivittyikö jonkin kortin määre vai ei (mitattu 30.9.2026). */
.tier .price { display: flex; flex-wrap: wrap; align-items: baseline; column-gap: .35em;
  min-height: calc(2.6rem * 1.65 + .9rem * 1.65); align-content: flex-start; }
.tier .price small { margin-left: 0; font-family: var(--font-body); letter-spacing: 0; white-space: nowrap; }
/* Kuvausteksti on kahdesta neljään riviä kortista riippuen, jolloin sen alla
   olevat ruksilistat alkoivat eri korkeuksilta. Kolmen rivin vähimmäiskorkeus
   pitää listat linjassa; pidempi teksti kasvattaa laatikkoa normaalisti. */
.tier .pdesc { min-height: calc(3 * .92rem * 1.65); }
/* Hintakorttien rivit linjaan: kortissa jossa on merkintä ("Suosituin") oli yksi
   rivi enemmän, joten hinta ja kaikki sen alla putosivat ~28 px naapureita
   alemmas eikä mikään rivi ollut linjassa. Merkille varataan tila aina. */
.tier .badge-tila { display: block; min-height: 1.05rem; margin-bottom: 8px; }
.tier--feat .badge { min-height: 1.05rem; }
/* 🛑 Tässä oli 22.9.2026 asti tilanvaraus korostetun kortin merkinnälle
   (`.tier:not(.tier--feat) .pl { margin-top: calc(1.05rem + 8px) }`). Merkintää
   ei ole yhdessäkään kortissa, joten sääntö vain työnsi kahta korttia noin
   25 px alemmas kuin kolmatta eli rikkoi sen linjan jonka oli määrä korjata.
   Jos merkintä ("Suosituin") joskus palaa, palauta tilanvaraus sen kanssa.
   30.9.2026: merkintä on palannut google-ads-mainonta.html:ssä (Hakusanamainonta),
   ja naapurikorttien hinta oli 27 px ylempänä. Tilanvaraus palautettu ehdolla:
   se koskee vain ruudukkoa, jossa merkintä oikeasti on, joten virhe ei toistu. */
/* Hintaruudukko on yksisarakkeinen ≤980 px:llä (ks. 980 px -lohko), jolloin
   rinnakkaista linjattavaa ei ole. */
@media (min-width: 981px) {
  .pricing:has(.tier--feat .badge) .tier:not(.tier--feat) > .pl:first-child {
    margin-top: calc(.78rem * 1.65 + 8px); }
}
.tier .pdesc { color: var(--c-text-2); font-size: .92rem; margin-bottom: 20px; }
.tier ul { list-style: none; padding: 0; margin: 0 0 24px; display: grid; gap: 11px; }
.tier li { display: flex; gap: 10px; align-items: flex-start; font-size: .94rem; }
.tier li svg { flex: none; color: var(--c-accent3); margin-top: 3px; }
.tier .btn { margin-top: auto; width: 100%; }
/* "Tarjouksesta" ei ole hinta (QA 30.9.2026): samassa 2,6 rem:n koossa se luki
   lukuna. Pienempi koko, mutta sama rivin korkeus kuin luvuilla (2,6 rem × 1,65),
   joten kolmen kortin rivit pysyvät linjassa. */
.tier .price:not(:has(.alkaen)) { font-size: 1.7rem; line-height: 4.29rem; }
/* Yhden sarakkeen hintakorteissa ei ole naapuria, jonka kanssa linjata, joten
   kuvauksen kolmen rivin varaus jätti vain tyhjää (QA 30.9.2026). */
@media (max-width: 980px) { .tier .pdesc { min-height: 0; } }
@media (hover: hover) {
  .tier:hover { transform: translateY(-3px);
    border-color: color-mix(in srgb, var(--c-primary) 50%, var(--c-border)); }
  .tier:not(.tier--feat):hover { box-shadow: var(--shadow); }
}

/* ============================ FAQ ============================ */
/* 🛑 UKK kahdessa sarakkeessa työpöydällä (Valtteri 21.9.2026): lista oli 800 px
   levyisenä yhtenä pitkänä pinona, vaikka sen alapuolinen osio käyttää koko
   .wrapin leveyden. Nyt UKK on yhtä leveä kuin muu sisältö ja puolta lyhyempi.
   align-items: start on välttämätön: ilman sitä avattu vastaus venyttäisi myös
   viereisen sarakkeen kysymyksen saman korkuiseksi. */
.faq { display: grid; gap: 12px; }
@media (min-width: 900px) {
  .faq { grid-template-columns: repeat(2, 1fr); gap: 14px; align-items: start; }
}
.faq details { background: var(--c-surface); border: 1px solid var(--c-border); border-radius: var(--r); overflow: hidden; transition: border-color .25s; }
.faq details[open] { border-color: color-mix(in srgb, var(--c-primary) 45%, var(--c-border)); }
.faq summary { cursor: pointer; list-style: none; padding: 13px 20px; font-family: var(--font-display); font-weight: 600; color: var(--c-heading); display: flex; align-items: center; justify-content: space-between; gap: 16px; }
.faq summary::-webkit-details-marker { display: none; }
/* Rivit tasaisiksi kahdessa sarakkeessa (30.9.2026): kaksirivinen kysymys teki
   rivistä 94 px ja viereinen yksirivinen jäi 68 px:ään, joten sarakkeiden
   reunat eivät olleet linjassa. Venytys (align-items: stretch) ei käy, koska
   avattu vastaus venyttäisi naapurin (ks. yllä). Siksi suljetulle kysymykselle
   varataan aina kahden rivin korkeus, ja summaryn align-items: center pitää
   yksirivisen tekstin pystysuunnassa keskellä. */
@media (min-width: 900px) {
  .faq summary { min-height: 76px; min-height: calc(26px + 2lh); } /* matalampi 2.10.2026 (Valtteri) */
}
.faq summary::after { content: "+"; font-family: var(--font-display); font-weight: 400; font-size: 1.5rem; line-height: 1; color: var(--c-accent3); transition: transform .25s; }
.faq details[open] summary::after { transform: rotate(45deg); }
/* Hover samaan tyyliin kuin korteissa, mutta ilman nostoa: haitari liikkuisi
   kursorin alta avautuessaan (QA 30.9.2026). */
@media (hover: hover) {
  .faq details:hover { border-color: color-mix(in srgb, var(--c-primary) 45%, var(--c-border)); }
  .faq summary:hover { color: var(--c-accent3); }
}
.faq .a { padding: 0 20px 16px; color: var(--c-text-2); line-height: 1.7; }
.faq .a p { margin: 0; }

/* ============================ CTA-BAND ============================ */
.cta-band { position: relative; text-align: center; border-radius: var(--r-lg); padding: clamp(44px, 7vw, 80px) var(--pad-x); overflow: hidden; background: linear-gradient(135deg, var(--c-surface), var(--c-bg-2)); border: 1px solid var(--c-border); }
.cta-band::before { content: ""; position: absolute; inset: 0; background: var(--glow-cyan), var(--glow-vio); opacity: .7; }
.cta-band > * { position: relative; z-index: 1; }
.cta-band h2 { margin-bottom: 14px; }
.cta-band p { color: var(--c-on-dark); max-width: 72ch; margin-inline: auto; margin-bottom: 28px; }
.cta-band .hero-cta { justify-content: center; }

/* ============================ YHTEYS + LOMAKE ============================
   🛑 21.9.2026: puolikkaan levyinen lomake poistettiin (Valtteri). Yhteystiedot
   ovat nyt omana koko leveänä rivinään ja lomake omana osionaan sen alla, joten
   kahden palstan .contact-grid ei ole enää käytössä. Kortit käyttävät samoja
   .contact-info .row -tyylejä, jotta ikonit ja typografia pysyvät samoina. */
.contact-row { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--gap); }
.contact-card {
  background: var(--c-surface); border: 1px solid var(--c-border);
  border-radius: var(--r); padding: clamp(20px, 2.4vw, 28px);
}
.contact-card .row:last-child { margin-bottom: 0; }
.map--wide { margin-top: var(--gap); }
/* Kartta 320 px kiinteää korkeutta -> kuvasuhde 1080x320 eli 3,4:1, joka luki
   kaistaleena eikä karttana (Valtteri 22.9.2026). Nyt kuvasuhde on 21:9 ja
   kartta rajataan enintään 820 px:n levyiseksi ja keskitetään, jolloin se on
   kartan muotoinen myös isolla näytöllä. */
.map--wide { max-width: 820px; margin-inline: auto; }
.map--wide iframe { height: auto; aspect-ratio: 21 / 9; }
@media (max-width: 640px) { .map--wide iframe { aspect-ratio: 4 / 3; } }
@media (max-width: 860px) {
  .contact-row { grid-template-columns: 1fr; }
}
.contact-huom { margin-top: 18px; text-align: center; color: var(--c-text-2); font-size: .98rem;
  max-width: 60ch; margin-inline: auto; } /* oli 138 merkkiä rivillä 1440 px:llä (QA 30.9.2026) */
.contact-info .row { display: flex; gap: 14px; align-items: flex-start; margin-bottom: 22px; }
.contact-info .row svg { flex: none; color: var(--c-accent3); margin-top: 3px; }
.contact-info .row span, .contact-info .row a { color: var(--c-text-2); }
/* 🛑 Yhteystiedoissa oli kolme eri asiaa samalla painolla (Valtteri 22.9.2026:
   "nyt käytännössä kaikki sama leipätekstiä"): otsake, itse yhteystieto ja
   tarkennus. Numero tai osoite on se, jota kävijä etsii, joten se on suurin.
   Otsake pienenee cyaniksi väliotsikoksi (sama muoto kuin .eyebrow ja .pl
   muualla sivustolla) ja tarkennus jää haaleaksi pikkutekstiksi.
   Kolme tasoa kirjoitetaan tähän kerran: samat luokat ovat myös
   yhteystiedot.html:ssä, eikä niitä tyylitellä sivukohtaisesti. */
.contact-info .row b { display: block; font-family: var(--font-body); font-size: .78rem;
  font-weight: 700; letter-spacing: .14em; text-transform: uppercase;
  color: var(--c-accent3); margin-bottom: 7px; }
.contact-info .contact-arvo { display: block; font-family: var(--font-display); font-weight: 700;
  font-size: clamp(1.1rem, 1.5vw, 1.24rem); line-height: 1.3; letter-spacing: -.01em;
  color: var(--c-heading); overflow-wrap: anywhere; }
/* Kosketusalue 44 px (QA 30.9.2026, oli 22 px): linkki on inline, joten
   pystytäyte ei muuta riviväliä mutta kasvattaa osuma-alueen. */
.contact-info .contact-arvo a { color: inherit; text-decoration: none; padding-block: 11px; }
.contact-info .contact-lisa { display: block; margin-top: 7px; font-size: .88rem;
  line-height: 1.45; color: var(--c-muted); }
@media (hover: hover) {
  .contact-info .contact-arvo a:hover { color: var(--c-accent3); }
}
/* Kartta: iframe ei noudattanut säiliön overflow: hidden -leikkausta, joten
   kulmat jäivät teräviksi ja vasempaan alanurkkaan jäi musta neliö. Pyöristys
   myös iframeen. Säiliön taustaväri toimii paikanpitäjänä sillä aikaa kun
   Google Maps latautuu (ennen tätä paikalla oli tyhjä reunustettu laatikko). */
.map { margin-top: 8px; border-radius: var(--r); overflow: hidden; border: 1px solid var(--c-border);
  background: var(--c-surface); min-height: 240px; }
.map iframe { width: 100%; height: 240px; border: 0; display: block; border-radius: var(--r); }
/* Tummentava suodatin (invert + hue-rotate) poistettu 2.10.2026 (Valtteri: "kartta oudosti"):
   se käänsi myös Googlen paikkakortin tummaksi laatikoksi. Kartta näkyy nyt omissa väreissään. */

.form { background: var(--c-surface); border: 1px solid var(--c-border); border-radius: var(--r-lg); padding: clamp(22px, 3vw, 34px); box-shadow: var(--shadow); }
.field { margin-bottom: 16px; }
.field label { display: block; font-size: .86rem; font-weight: 600; color: var(--c-on-dark); margin-bottom: 7px; font-family: var(--font-display); }
.field input, .field textarea { width: 100%; font-size: 16px; font-family: var(--font-body); color: var(--c-heading); background: var(--c-bg); border: 1px solid var(--c-border); border-radius: var(--r-sm); padding: 13px 15px; transition: border-color .2s, box-shadow .2s; }
.field input::placeholder, .field textarea::placeholder { color: var(--c-muted); }
.field input:focus, .field textarea:focus { outline: none; border-color: var(--c-primary); box-shadow: 0 0 0 3px color-mix(in srgb, var(--c-primary) 22%, transparent); }
.field textarea { min-height: 120px; resize: vertical; }
.hp { position: absolute; left: -9999px; width: 1px; height: 1px; opacity: 0; }

/* ============================ ALASIVUJEN PIENET ELEMENTIT ============================ */
.svc-stack { display: grid; gap: clamp(26px, 4vw, 48px); }
.svc-d-list { list-style: none; padding: 0; margin: 18px 0 24px; display: grid; gap: 11px; }
.svc-d-list li { display: flex; gap: 10px; align-items: flex-start; font-size: .96rem; }
.svc-d-list li svg { flex: none; color: var(--c-accent3); margin-top: 3px; }
.values { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--gap); }
.value { background: var(--c-surface); border: 1px solid var(--c-border); border-radius: var(--r-lg); padding: 28px; }
.value .ico { width: 46px; height: 46px; border-radius: 12px; display: grid; place-items: center; background: color-mix(in srgb, var(--c-primary) 16%, transparent); color: var(--c-accent3); margin-bottom: 16px; border: 1px solid color-mix(in srgb, var(--c-primary) 30%, transparent); }
.value h3 { color: var(--c-heading); margin-bottom: 8px; }
.value p { font-size: .95rem; margin: 0; }
.legal { max-width: 760px; }
.legal h3 { color: var(--c-heading); margin-top: 32px; margin-bottom: 8px; }
.legal p { color: var(--c-muted); }

/* ============================ RESPONSIIVISUUS ============================ */
@media (max-width: 980px) {
  .hero-grid { grid-template-columns: 1fr; gap: 48px; }
  .hero-visual { max-width: 460px; }
  .bento { grid-template-columns: repeat(2, 1fr); }
  .tile--wide, .tile--tall, .tile--half, .tile--third, .tile--feat { grid-column: span 2; }
  .tile--feat { grid-template-columns: 1fr; }
  .feature-split { grid-template-columns: 1fr; }
  .statband-grid { grid-template-columns: repeat(2, 1fr); gap: 30px; }
  .timeline { grid-template-columns: repeat(2, 1fr); gap: 30px; }
  .pricing { grid-template-columns: 1fr; max-width: 460px; margin-inline: auto; }
  .values { grid-template-columns: 1fr; }
}
@media (max-width: 560px) {
  .bento { grid-template-columns: 1fr; }
  .tile--wide, .tile--tall, .tile--half, .tile--third, .tile--feat { grid-column: span 1; }
  .statband-grid { grid-template-columns: 1fr 1fr; gap: 22px; }
  .hero-metrics { gap: 20px; }
}

@media (prefers-reduced-motion: reduce) {
  * { animation: none !important; transition: none !important; }
}


/* Lomakkeen alasvetovalikko — samat mitat ja tila kuin muilla kentillä.
   Oma nuoli-ikoni, koska selaimen oletusnuoli ei noudata teemaa.
   (Lisätty pohjiin 26.8.2026: viisi asiakasta oli joutunut kirjoittamaan tämän itse.) */
.field select, .form-group select {
  width: 100%; font: inherit; font-size: 16px; color: inherit;
  appearance: none; -webkit-appearance: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='9' viewBox='0 0 14 9' fill='none' stroke='%23888' stroke-width='2'%3E%3Cpath d='m1 1 6 6 6-6'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: right 16px center;
  padding-right: 42px;
}




/* ============================================================================
   OSIOKIRJASTO — lisätty pohjiin 26.8.2026
   Nämä kolme rakennettiin aiemmin käsin lähes joka asiakkaalle (mm. A tape).
   Kaikki käyttävät pohjan omia muuttujia, joten ne teemautuvat itsestään.
   ============================================================================ */

/* --- Yhteyshenkilökortit: isot kasvokuvat, ei pikkuruisia nappikuvia ---------
   Rajaa kuvat KASVOIHIN ennen käyttöä. Koko vartalo 60 px ympyrässä tarkoittaa,
   ettei kasvoja erota — tämä oli A tapen ensimmäisen version virhe. */
.person-cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(210px, 1fr)); gap: 16px; margin: 26px 0 8px; }
.person { display: flex; flex-direction: column; background: var(--c-surface);
  border: 1px solid var(--c-border); border-radius: var(--r); overflow: hidden; box-shadow: var(--shadow-sm); }
/* 🛑 Henkilökuvissa on VALKOINEN YMPYRÄ poltettuna itse kuvaan (läpinäkyvä WebP,
   mutta läpinäkyvyys on vain ympyrän ulkopuolella). Tummalla kortilla ympyrä
   ulottui reunasta reunaan ja katkesi tekstin kohdalla, jolloin se luki
   liimatulta tarralta (Valtteri 22.9.2026: "näyttää vähän irralliselta").
   Väriä ei voi vaihtaa CSS:llä, joten ympyrästä tehdään tarkoituksellinen:
   pienempi, keskitetty medaljonki, jonka ympärillä on ilmaa, cyan-hehku ja
   ohut rengas. Sama kuvakieli kuin hero-kuvan hehkussa (.hero-sub-kuva::before)
   ja korttien cyan-varjoissa, joten kuva istuu muun sivuston kanssa yhteen. */
.person img { display: block; width: calc(100% - 84px); margin: 30px auto 10px;
  aspect-ratio: 1 / 1; object-fit: cover; border-radius: 50%; position: relative; z-index: 1;
  /* Kirkkaus 93 %: puhdas valkoinen (255) laskee 237:ään, jolloin ympyrä ei ole
     enää sivuston kirkkain pinta. Ero on pieni, mutta se riittää siihen ettei
     ympyrä hyppää tummasta kortista irti. Enempää ei lasketa: kasvot ovat
     samassa kuvassa eikä niitä saa tummentaa. */
  filter: brightness(.93) contrast(1.04);
  box-shadow: 0 0 0 1px color-mix(in srgb, var(--c-accent3) 34%, transparent),
              0 18px 36px -18px color-mix(in srgb, var(--c-accent3) 75%, transparent); }
/* 🛑 Hehku tehdään kuvan omalla varjolla, EI taustaelementillä. Ensimmäisessä
   versiossa tässä oli kortin levyinen `radial-gradient` + `blur(26px)`, joka
   levisi koko kortin yli ja jätti nimen ja yhteystiedot lukukelvottomiksi
   liukuvärin päälle (mitattu renderistä 22.9.2026). Varjo pysyy ympyrän
   ympärillä eikä voi vuotaa tekstin päälle. */
.person > div { position: relative; z-index: 1; }
.person > div { padding: 16px 18px 18px; }
.person b { display: block; font-family: var(--font-display); font-size: 1.08rem; color: var(--c-heading); margin-bottom: 4px; }
.person span { display: block; font-size: .95rem; color: var(--c-muted); }
/* Kosketusalue 44 px (QA 30.9.2026, oli 25 px). Negatiivinen marginaali kumoaa
   täytteen, joten asettelu ei muutu. Kaksi allekkaista linkkiä: 20 px:n
   päällekkäisyys kuuluu jälkimmäiselle, joten ensimmäisen tehollinen alue on
   35 px, toisen 45 px. */
.person a { display: block; font-size: .95rem; color: var(--c-text-2); padding-block: 10px; margin-block: -10px; }
/* Allekkaisten linkkien väliin 8 px (QA 30.9.2026): marginaalit -10 ja 18 px
   yhdistyvät 8 px:ksi, jolloin kummankin 44 px:n alue on kokonaan oma. */
.person a + a { margin-top: 6px; } /* väli tiivistetty 2.10.2026, alue yhä noin 40 px */
@media (hover: hover) { .person a:hover { color: var(--c-primary); } }

/* --- 2×2-korttiruudukko: neljä referenssiä tai palvelua ----------------------
   Palvelukorttien rivisääntö: 4 kpl EI KOSKAAN 3+1, vaan 2×2. */
.cards--2 { grid-template-columns: repeat(2, 1fr); }
@media (max-width: 860px) { .cards--2 { grid-template-columns: 1fr; } }
.cards--2 .card .num { display: inline-block; align-self: flex-start; font-size: .9rem;
  letter-spacing: .12em; font-weight: 700; color: var(--c-primary);
  background: color-mix(in srgb, var(--c-primary) 12%, transparent);
  padding: 5px 13px; border-radius: var(--r-pill); }

/* --- Nostolainaus: asiakassitaatti omalla kaistallaan ------------------------
   Toimii sekä vaalealla että tummalla osiolla (peri väri isännältä). */
.pull { margin: 0; max-width: 900px; margin-inline: auto; text-align: center; }
.pull p { font-family: var(--font-display); font-weight: 600;
  font-size: clamp(1.35rem, 3vw, 2.1rem); line-height: 1.35;
  max-width: none; margin: 0 0 22px; letter-spacing: -.02em; }
.pull footer { font-size: .96rem; letter-spacing: .04em; opacity: .75; }
.pull::before { content: "”"; display: block; font-family: var(--font-display);
  font-size: clamp(3.5rem, 8vw, 6rem); line-height: .6; opacity: .18; margin-bottom: 8px; }

/* ============================================================================
   SISÄLTÖSIVUJEN KOMPONENTIT
   Lisätty 20.9.2026 (Foorly). Pohjassa oli vain etusivun osiot; pitkät
   palvelu- ja artikkelisivut tarvitsevat leipätekstin, taulukot ja murupolun.
   Kaikki perii :root-muuttujat — ei kovakoodattuja värejä.
   ============================================================================ */

/* --- Alasivun kevyempi hero (ei paneelia, ei 100vh) --- */
.hero--sub { padding-block: clamp(96px, 12vw, 150px) clamp(40px, 6vw, 68px); min-height: 0; }
.hero--sub .hero-copy { max-width: 780px; }
/* 🛑 55 alasivua 65:stä on heroja ILMAN kuvaa (artikkelit, paikkakunnat,
   kokemuksia, palvelut…). Niillä .hero-sub jäi 46 merkkiin ja koko oikea
   puolisko jäi tyhjäksi, vaikka mitään ei ole täyttämässä sitä. Kun kuvaa
   ei ole, teksti saa hengittää: mitattu rivinpituus 62 ch on yhä luettava
   (suositus 45–75). Kuvalliset herot (.hero-sub-grid) pysyvät ennallaan. */
/* Kuvaton alasivun hero matalammaksi (QA 30.9.2026): paikkakunta- ja
   artikkelisivuilla hero oli 520–620 px korkea yhdellä otsikkorivillä, koska
   täyte oli mitoitettu kuvalliselle herolle. Ylätäyte sisältää navin 76 px. */
.hero--sub:not(:has(.hero-sub-grid)) { padding-block: clamp(96px, 7.5vw, 108px) clamp(24px, 2.6vw, 36px); }
/* Ilman painikkeita ingressin alamarginaali (30 px) oli vain tyhjää heron pohjalla. */
.hero--sub:not(:has(.hero-sub-grid)):not(:has(.hero-cta)) .hero-sub { margin-bottom: 0; }
.hero--sub:not(:has(.hero-sub-grid)) .hero-copy { max-width: var(--palsta); }
.hero--sub:not(:has(.hero-sub-grid)) .hero-sub { max-width: var(--palsta); }
/* Kuvaton hero (artikkelit, listaukset): otsikko ja ingressi samaan leveyteen
   kuin tekstipalsta (Valtteri 2.10.2026: "edelleen kapeita"), ja seuraava osio
   alkaa heti, jotta artikkelin "Lyhyt vastaus" ei jää liian alas. */
.hero--sub:not(:has(.hero-sub-grid)) + .section { padding-top: clamp(8px, 1.2vw, 16px); }
/* Tasapainotettu rivitys (text-wrap: balance) jakoi kuvattoman heron otsikon
   kahdelle tasapitkälle riville, jolloin se näytti kapealta vaikka tilaa oli.
   pretty täyttää rivin ja estää vain orpo-sanan. Sääntö on tiedoston lopussa,
   jotta se voittaa yleisen h1-säännön (rivi ~1313). */
.hero--sub h1 { margin-bottom: 18px; }

/* --- Murupolku --- */
/* Väri --c-muted → --c-text (QA 30.9.2026): #8388B0 oli hero-hehkun päällä
   14,4 px:n koossa 3,0-4,1:1 kaikilla 17 alasivulla, AA vaatii 4,5:1. */
.crumbs { font-size: .9rem; color: var(--c-text); margin-bottom: 22px; display: flex;
  flex-wrap: wrap; gap: 8px; align-items: center; }
.crumbs a { color: var(--c-text); text-decoration: none; }
/* Kosketusalue 44 px mobiilissa ilman että murupolun asettelu muuttuu
   (30.9.2026: linkit olivat 24 px korkeita). Täyte kasvattaa osuma-aluetta ja
   negatiivinen marginaali kumoaa sen vaikutuksen riviin. */
@media (max-width: 860px) { .crumbs a { padding-block: 11px; margin-block: -11px; } }
.crumbs span[aria-current] { color: var(--c-text); }
@media (hover: hover) { .crumbs a:hover { color: var(--c-accent3); } }

/* --- Leipätekstipalsta: keskitetty, ei vasempaan reunaan liimattu ---
   🛑 Muutettu 21.9.2026 (Valtteri): "blogeissa teksti on liian kapea, alkaa
   vasemmalta – sama monella perussivulla". Aiemmin palsta oli 62ch KIINNI
   vasemmassa reunassa, jolloin oikealle jäi ~480 px tyhjää ja sivu näytti
   keskeneräiseltä. Nyt palsta on 720 px ja KESKITETTY säiliöön, joten tyhjä tila
   jakautuu molemmille puolille ja teksti on optisesti leveämpi.

   🛑 Palsta on PIKSELEINÄ, ei ch-yksikköinä. Syy: ch on elementin oman fontin
   "0"-merkin leveys, joten 70ch tarkoittaa 45 px:n otsikolla yli kaksi kertaa
   leveämpää laatikkoa kuin 16 px:n leipätekstillä. Keskitettynä otsikot alkoivat
   silloin selvästi kappaleiden vasemmalta puolelta eivätkä linjautuneet.
   Mitattu 21.9.2026: 720 px = noin 84 merkkiä Interin 16 px:llä. */
.prose { max-width: var(--palsta); }
.wrap.prose { max-width: var(--container); }
/* 🛑 Keskitys POISTETTU 22.9.2026. Se lisättiin 21.9. jotta kapea palsta ei
   näyttäisi vasempaan reunaan liimatulta, mutta se rikkoi rivityksen: .prose-
   osion otsikko alkoi 90 px kauempaa oikealta kuin saman sivun .sec-head-
   otsikko, ja lukija näki kaksi eri tasoa. Kaikki tekstisisältö alkaa nyt
   säiliön vasemmasta reunasta, ja vain erikseen keskitetyt lohkot (.sec-head--center,
   .cta-band) ovat keskellä. */
.wrap.prose > * { margin-inline: 0; }
.wrap.prose > p,
.wrap.prose > ul,
.wrap.prose > ol,
.wrap.prose > blockquote { max-width: var(--palsta); }
/* Otsikot saavat saman väljyyden kuin .sec-head-otsikot (1040 px), jotta sama
   otsikko katkeaa samalla tavalla riippumatta siitä kummassa lohkossa se on.
   Leipäteksti pysyy 900 px:ssä, koska rivin pituus on luettavuuskysymys ja
   otsikon leveys ei ole. */
.wrap.prose > h2,
.wrap.prose > h3,
.wrap.prose > h4 { max-width: 1040px; }
/* Taulukot ja nostolaatikot saavat hieman leveämmän palstan kuin teksti, jotta
   hintasarakkeiden luvut eivät rivity. 🛑 Älä rajaa niitä 70ch:hon — se oli
   20.9.2026 korjattu vika ("alkaen 99 / €/kk" kahdella rivillä). */
.wrap.prose > table,
.wrap.prose > .tbl-wrap,
.wrap.prose > .callout { max-width: var(--palsta); }
/* 🛑 H2:lla oli margin-top: 0, joten 45 px:n otsikko sai ylöspäin saman 18 px:n
   välin kuin kappaleiden väliin jää — otsikko näytti kiinnittyvän edelliseen
   leipätekstiin eikä osioerottelua syntynyt. Mitattu 20.9.2026 artikkeleista. */
.prose > h2 { margin-top: 52px; margin-bottom: 18px; }
.prose > h2:first-child { margin-top: 0; }
.prose > h3 { margin-top: 34px; margin-bottom: 12px; }
/* 🛑 Kirjasinkoko kuuluu yhteen palstan leveyden kanssa (mitattu 22.9.2026).
   900 px:n palstalla 16 px:n teksti antaa 114 merkkiä riville, jolloin silmä
   kadottaa rivinvaihdon. 1,18 rem (noin 18,9 px) tuo rivin noin 97 merkkiin,
   mikä on sama luettavuus kuin vanhalla 720 px / 16 px -yhdistelmällä.
   Jos palsta muuttuu, muuta tätä samassa yhteydessä. */
.prose > p { color: var(--c-text); margin-bottom: 20px; line-height: 1.75; font-size: 1.08rem; }
.prose > ul, .prose > ol { font-size: 1.04rem; line-height: 1.7; }
/* Numeroitu luettelo leipätekstin seassa (lisätty 22.9.2026 palvelusivujen
   luettavuuskierroksella). Oletusnumerot ovat pieniä ja katoavat tummalla
   taustalla, joten numero nostetaan omaksi liukuvärimerkikseen kuten
   prosessiosioiden vaihenumerot. Käytetään kun kappale luettelee "ensimmäinen,
   toinen, kolmas" -tyyppisiä asioita: silloin ne kuuluvat luettelona. */
.numerolista { list-style: none; counter-reset: nro; padding: 0; margin: 22px 0; }
.numerolista > li {
  counter-increment: nro; position: relative; padding-left: 52px; margin-bottom: 18px;
  color: var(--c-text); line-height: 1.7;
}
.numerolista > li::before {
  content: counter(nro); position: absolute; left: 0; top: -2px;
  width: 34px; height: 34px; border-radius: 10px; display: grid; place-items: center;
  font-family: var(--font-display); font-weight: 800; font-size: 1.02rem;
  color: var(--c-accent3);
  background: color-mix(in srgb, var(--c-primary) 16%, transparent);
  border: 1px solid color-mix(in srgb, var(--c-primary) 30%, transparent);
}
.numerolista > li strong { color: var(--c-heading); }
.numerolista > li:last-child { margin-bottom: 0; }
.prose > p:last-child { margin-bottom: 0; }
/* Sisältölinkit. Globaali `a { color: inherit }` tekee leipätekstin linkeistä
   näkymättömiä, joten ne merkitään erikseen kaikissa tekstiä sisältävissä
   lohkoissa — myös UKK-vastauksissa ja nostolaatikoissa, jotka ovat .prose:n
   ulkopuolella. (Havaittu 20.9.2026: UKK-linkit eivät erottuneet lainkaan.) */
/* 🛑 Laajennettu 20.9.2026: aiempi lista kattoi vain .prose/.faq/.callout/.card p/
   .lead/.hero-sub, joten leipätekstin linkit NÄIDEN ULKOPUOLELLA jäivät globaalin
   `a { color: inherit }` varaan — samanvärisiksi kuin ympäröivä teksti ilman
   alleviivausta. Mitattu: näkymättömiä linkkejä 7 sivulla (mm. kotisivut-rauma 5 kpl,
   "Katso myös" -rivit, yhteystietojen tietosuojaseloste). Nyt kaikki tekstilinkit
   merkitään, ja poikkeukset luetellaan :not()-listassa. */
main p a, main li a, main td a, main th a, main dd a, main figcaption a,
.faq .a a, .callout a, .lead a, .hero-sub a {
  color: var(--c-accent3); text-decoration: underline; text-underline-offset: 3px;
  text-decoration-thickness: 1px; text-decoration-color: color-mix(in srgb, var(--c-accent3) 45%, transparent); }
/* Painikkeet, korttilinkit ja linkkiruudukot eivät ole leipätekstilinkkejä */
main a.btn, main a.tlink, main .lahialueet a, main .chips a, main .crumbs a,
main .nayte a, main .kortti a, main a.lahialueet-kaikki, main .maakunta a,
main .foot-col a {
  text-decoration: none; }
/* 🛑 Painikkeen väri asetetaan tässä MUUNNELMAN mukaan, ei `color: inherit`.
   Aiempi `main a.btn { color: inherit; }` (0,1,2) voitti `.btn-primary`n
   (0,1,0) valkoisen, joten main-elementin pääpainikkeet perivät leipätekstin
   #B0B4D2:n violetin liukuvärin päälle: 2,28–2,32:1 (mitattu 30.9.2026,
   117 painiketta 64 sivulla). Kaksi luokkaa (.btn.btn-*) voittaa myös
   leipätekstilinkkien säännöt (`.faq .a a` 0,2,1), joten linkin cyan ja
   alleviivaus eivät vuoda painikkeeseen missään lohkossa. */
main a.btn { color: inherit; }
main a.btn.btn-primary { color: var(--c-on-primary); text-decoration: none; }
main a.btn.btn-ghost, main a.btn.btn-call { color: var(--c-heading); text-decoration: none; }
@media (hover: hover) {
  main p a:hover, main li a:hover, main td a:hover, main dd a:hover,
  .faq .a a:hover, .callout a:hover, .lead a:hover, .hero-sub a:hover {
    text-decoration-color: currentColor; }
}
.prose strong { color: var(--c-heading); font-weight: 650; }

/* --- Nostolaatikko: yksi asia jonka lukijan pitää muistaa --- */
.callout { margin: 32px 0; padding: clamp(22px, 3vw, 30px); border-radius: var(--r-lg);
  background: var(--c-surface); border: 1px solid var(--c-border);
  border-left: 3px solid var(--c-accent3); box-shadow: var(--shadow); }
.callout h3 { margin: 0 0 10px; font-size: 1.16rem; }
/* 🛑 Calloutin "Lyhyt vastaus" ei ole otsikko vaan nimilappu (katselmus
   23.9.2026, B10). Se oli h3 ja tuli ennen artikkelin ensimmäistä h2:ta, joten
   otsikkohierarkiaan syntyi hyppy h1 → h3 jokaisessa artikkelissa. Ulkoasu on
   tarkoituksella sama kuin .callout h3:lla, jotta markupin vaihto ei muuta
   mitään visuaalisesti. Käytä tätä uusissa artikkeleissa h3:n sijaan. */
.callout-label { margin: 0 0 10px; font-family: var(--font-display); font-weight: 700;
  font-size: 1.16rem; line-height: 1.25; color: var(--c-heading); }
.callout p { margin: 0; color: var(--c-text); line-height: 1.7; }

/* --- Taulukko: vaakavieritys mobiilissa, ei sivun ylivuotoa --- */
/* 🛑 Taulukko vierii vaakaan kapealla ruudulla, mutta MIKÄÄN ei kertonut siitä:
   390 px:n puhelimessa 520 px:n taulukosta näkyi vain ensimmäinen sarake, joten
   hinnat jäivät kokonaan ruudun ulkopuolelle ilman vihjettä (mitattu 20.9.2026 —
   "Meidän hinnoittelumme" näytti listalta ilman yhtään hintaa). Varjot piirretään
   `background-attachment`-tekniikalla: `local`-kerrokset peittävät varjon kun
   reuna on saavutettu, `scroll`-kerrokset jäävät näkyviin kun sisältöä on lisää.
   Ei JS:ää eikä HTML-muutosta. */
.tbl-wrap { overflow-x: auto; margin: 26px 0; border: 1px solid var(--c-border);
  border-radius: var(--r); background: var(--c-surface); -webkit-overflow-scrolling: touch;
  background-image:
    linear-gradient(to right, var(--c-surface) 40%, transparent),
    linear-gradient(to left,  var(--c-surface) 40%, transparent),
    linear-gradient(to right, rgba(0,0,0,.55), transparent),
    linear-gradient(to left,  rgba(0,0,0,.55), transparent);
  background-position: left center, right center, left center, right center;
  background-size: 44px 100%, 44px 100%, 18px 100%, 18px 100%;
  background-repeat: no-repeat;
  background-attachment: local, local, scroll, scroll; }
.tbl { width: 100%; border-collapse: collapse; font-size: .96rem; min-width: 520px; }
.tbl caption { text-align: left; padding: 16px 18px 0; font-family: var(--font-body); font-weight: 700;
  font-size: .76rem; letter-spacing: .1em; text-transform: uppercase; color: var(--c-text); }
.tbl th, .tbl td { padding: 13px 18px; text-align: left; border-bottom: 1px solid var(--c-border); }
/* --c-muted → --c-text (30.9.2026): vierityskääreen reunavarjo (yllä) tummentaa
   pohjan #262552:ksi, jota vasten --c-muted oli 4,16:1. Pieni versaalilabel
   tarvitsee 4,5:1 myös varjon kohdalla. */
.tbl thead th { font-family: var(--font-body); font-weight: 700; font-size: .76rem; letter-spacing: .1em;
  text-transform: uppercase; color: var(--c-text); font-weight: 600; }
.tbl tbody th { color: var(--c-heading); font-weight: 600; }
.tbl tbody td { color: var(--c-text); }
.tbl tbody tr:last-child th, .tbl tbody tr:last-child td { border-bottom: 0; }

/* --- Kortit: peruskortti + kahden vertailu --- */
.cards { display: grid; gap: var(--gap); }
/* 🛑 margin: 0 on pakollinen: <blockquote class="card"> perii selaimen oman
   `margin: 1em 40px` -säännön, joka kavensi suosituskortteja 80 px (mitattu
   20.9.2026: kortti 271 px vaikka gridin kaista on 351 px → tekstipalsta ~30
   merkkiä ja kortit venyivät korkeiksi). Näkyi sekä mobiilissa että työpöydällä. */
.card { margin: 0; display: flex; flex-direction: column; gap: 12px; padding: clamp(22px, 3vw, 30px);
  border-radius: var(--r-lg); background: var(--c-surface); border: 1px solid var(--c-border);
  box-shadow: var(--shadow); }
.card h3 { margin: 0; font-size: 1.22rem; }
.card p { color: var(--c-text); margin: 0; line-height: 1.7; }
/* 0,88rem eikä 0,82rem: mobiilissa 0,82 = 13,1 px, mikä jää alle luettavan
   rajan (14 px) pidemmissä pätkissä. Mitattu 21.9.2026. */
.card-lead { color: var(--c-accent3) !important; font-family: var(--font-body); font-weight: 600;
  font-size: .88rem; letter-spacing: .04em; }
.card .feature-list { margin: 4px 0 0; }
.split-cards { display: grid; grid-template-columns: repeat(2, 1fr); gap: var(--gap); margin: 30px 0; }
@media (max-width: 860px) { .split-cards { grid-template-columns: 1fr; } }
/* Tasakokoinen 3-sarakkeinen ruudukko (artikkelilistaus). Bento-ruudukko jättää
   1–2 kortin ryhmän kapeaksi ja tyhjäksi; tämä pitää kortit yhtä leveinä. */
.cards--3 { grid-template-columns: repeat(3, 1fr); }
@media (max-width: 960px) { .cards--3 { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 640px) { .cards--3 { grid-template-columns: 1fr; } }
/* Kosketusalue 44 px (QA 30.9.2026, oli 25 px): inline-linkin pystytäyte ei
   muuta riviväliä. */
.card h3 a { color: inherit; padding-block: 11px; }
@media (hover: hover) { .card h3 a:hover { color: var(--c-accent3); } }
.card .tlink { margin-top: auto; }

/* --- Chip-lista: toimialat, paikkakunnat, avainsanat --- */
.chips { display: flex; flex-wrap: wrap; gap: 10px; list-style: none; padding: 0; margin: 0; }
.chips li { padding: 9px 16px; border-radius: var(--r-pill); font-size: .93rem;
  color: var(--c-text); background: var(--c-surface); border: 1px solid var(--c-border); }

/* ============================================================================
   KUVAT — lisätty 20.9.2026 (Foorly)
   Foorlyn omat isometriset kuvitukset ovat läpinäkyvätaustaisia ja samassa
   cyan–violetti–magenta-paletissa kuin teema, joten ne istuvat tummalle
   taustalle ilman kehystä tai taustalaatikkoa.
   ============================================================================ */

/* Palvelutiilet ilman kuvia (Valtteri 20.9.2026): kuvat olivat eri kokoisia, osassa
   tiilistä ei ollut kuvaa lainkaan ja isoimmat valuivat kortin reunan yli. Nyt kaikki
   tiilet ovat samanlaisia: ikoni + otsikko + teksti + linkki. */
.tile { position: relative; }

/* --- Hero-kuvitus: korvaa CSS-paneelin --- */
.hero-kuva { position: relative; display: grid; place-items: center; }
.hero-kuva img { width: 100%; max-width: 560px; height: auto;
  filter: drop-shadow(0 40px 80px color-mix(in srgb, var(--c-accent2) 30%, transparent)); }
.hero-kuva::before { content: ""; position: absolute; inset: 8% 6%; border-radius: 50%;
  background: radial-gradient(circle, color-mix(in srgb, var(--c-accent3) 22%, transparent), transparent 68%);
  filter: blur(42px); z-index: -1; }

/* --- Työnäytteet: oikeita sivustokuvia kahdessa sarakkeessa --- */
.naytteet { display: grid; grid-template-columns: repeat(2, 1fr); gap: var(--gap); }
@media (max-width: 760px) { .naytteet { grid-template-columns: 1fr; } }
/* Etusivu: 3 + 3 (Valtteri 2.10.2026: "laita 3 ja 3 allekkain"). */
.naytteet--3 { grid-template-columns: repeat(3, 1fr); }
@media (max-width: 1080px) { .naytteet--3 { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 760px)  { .naytteet--3 { grid-template-columns: 1fr; } }
/* Kolmessa sarakkeessa kortti on ~350 px leveä, ja kuvan päällä oleva teksti peitti
   koko kuvakaappauksen (mitattu 2.10.2026). Sama ratkaisu kuin puhelimessa (alla):
   teksti nousee vain kuvan alaosan päälle ja jatkuu sen alle samalla verholla. */
/* 3.10.2026 (Valtteri: "liian tumma se tummennusfiltteri ... alhaalla jotain sinistä"): kolmen sarakkeen
   korteissa teksti kokonaan kuvan alle kortin omalle pinnalle. Ei tummennusta kuvan päällä, joten
   kuvakaappaus näkyy kokonaan eikä alaosaan jää kahta sävyä (verho #050512 vs. kortti --c-surface). */
.naytteet--3 .nayte > .nayte-teksti { position: relative; inset: auto; margin-top: 0; background: none;
  padding: clamp(16px, 2vw, 22px) clamp(18px, 2.2vw, 24px) clamp(18px, 2.2vw, 24px); }
/* Työnäytteiden alle jäävä "Katso referenssimme". Oma luokka inline-tyylin
   tilalle, jotta väli on sama kuin muissa osioissa (22.9.2026). */
.naytteet-cta { margin-top: 30px; display: flex; justify-content: center; }

/* ======================= CASE-LISTA (referenssisivu) =======================
   🛑 Referenssit kertovat mitä projektissa tehtiin, eivät vain että sivut
   tehtiin (Valtteri 22.9.2026: "kuulostais vähän laajemmalta projektilta kuin
   vaa uusilta verkkosivuilta"). Siksi korttiruudukko ei riitä: pidempi kuvaus
   ei mahdu kuvan päälle luettavana.
   Muoto on KUVA + TEKSTI VIERETYSTEN, mikä on `pohjat/_ohjeet.md` §12:n toinen
   sallittu muoto (teksti kuvan päällä tai vierellä, ei koskaan alla). Joka
   toinen case on käänteinen, jotta 15 riviä ei lue yhtenä pylväänä.
   Kapealla näytöllä pinoutuminen on väistämätöntä; §12 koskee asettelua, jossa
   vierekkäin olisi tilaa. */
.caset { display: grid; gap: clamp(28px, 4vw, 52px); }
.case { display: grid; grid-template-columns: 1.05fr .95fr; gap: clamp(22px, 3vw, 44px);
  align-items: center; }
.case:nth-child(even) .case-kuva { order: 2; }
.case-kuva { display: block; position: relative; border-radius: var(--r-lg); overflow: hidden;
  border: 1px solid var(--c-border); background: var(--c-surface); box-shadow: var(--shadow);
  transition: transform .25s, border-color .25s; }
.case-kuva img { display: block; width: 100%; height: auto; }
@media (hover: hover) {
  .case-kuva:hover { transform: translateY(-3px);
    border-color: color-mix(in srgb, var(--c-primary) 45%, var(--c-border)); }
}
.case-ala { display: block; font-family: var(--font-body); font-size: .78rem; font-weight: 700;
  letter-spacing: .14em; text-transform: uppercase; color: var(--c-accent3); margin-bottom: 9px; }
.case-teksti h3 { font-size: clamp(1.25rem, 2vw, 1.55rem); margin-bottom: 12px; }
.case-teksti p { font-size: .97rem; margin: 0; }
/* Tehdyn työn erittely: lyhyet faktat omana rivinään, jotta laajuuden näkee
   lukematta kappaletta. Ei pillereitä (talon linja: ei badgeja). */
.case-tiedot { list-style: none; margin: 16px 0 0; padding: 0;
  display: flex; flex-wrap: wrap; gap: 8px 18px; }
.case-tiedot li { position: relative; padding-left: 15px; font-size: .86rem; color: var(--c-text-2); }
.case-tiedot li::before { content: ""; position: absolute; left: 0; top: .52em;
  width: 5px; height: 5px; border-radius: 50%; background: var(--c-accent3); }
.case-osoite { display: inline-flex; align-items: center; gap: 7px; margin-top: 18px;
  font-family: var(--font-body); font-size: .92rem; font-weight: 600; color: var(--c-accent3); }
@media (hover: hover) { .case-osoite:hover { text-decoration: underline; text-underline-offset: 3px; } }
/* Kosketusalue 44 px (30.9.2026, oli 24 px), teksti pysyy paikallaan. */
@media (max-width: 860px) { .case-osoite { padding-block: 11px; margin-block: 7px -11px; } }
@media (max-width: 860px) {
  .case { grid-template-columns: 1fr; gap: 20px; }
  .case:nth-child(even) .case-kuva { order: 0; }
}
/* 20.9.2026 tässä pakotettiin yksi näyte per rivi, jotta kuva ja sen vieressä
   oleva teksti mahtuivat rinnakkain. Sääntö (_ohjeet.md §12) täyttyy nyt
   toisella sallitulla tavalla, teksti kuvan päällä, joten kahden sarakkeen
   ruudukko riittää eikä tätä ohitusta enää tarvita. */
/* Työnäytteet: kaksi vierekkäin, teksti kuvan PÄÄLLÄ (Valtteri 22.9.2026).
   🛑 Muoto on valittu `pohjat/_ohjeet.md` §12:n säännön mukaan: kuvalla on
   oltava teksti joko päällään tai vierellään, eikä koskaan alla. Vierekkäinen
   teksti olisi kahdessa sarakkeessa jättänyt kuvalle vain noin 220 px, jolloin
   näytettävän sivuston otsikko ja navigaatio eivät enää erottuisi eikä kuva
   todistaisi mitään. Siksi teksti nostettiin kuvan päälle, jolloin kuva saa
   koko sarakkeen leveyden. */
.nayte { position: relative; border-radius: var(--r-lg); border: 1px solid var(--c-border);
  background: var(--c-surface); overflow: hidden; box-shadow: var(--shadow); display: block; }
.nayte img { width: 100%; max-width: none; height: auto; display: block; border: 0; border-radius: 0; }
/* Verho on kuvan päällä vain alaosassa: ylhäällä sivusto näkyy koskemattomana,
   alhaalla teksti saa tarpeeksi tumman pohjan.
   🛑 Häivytys tapahtuu TEKSTIN YLÄPUOLELLA, ei sen takana. Ensimmäinen versio
   häivytti verhon .72 → 0 välillä 82–100 %, ja tekstilaatikon yläreuna osui
   juuri siihen: vaalealla sivustolla (Länsi-Helsingin Kirjanpito) valkoinen
   teksti mitattiin **1,33:1** eli käytännössä näkymättömäksi. Siksi laatikolla
   on iso padding-top, joka työntää tekstin läpinäkyvän kaistan alapuolelle.
   🛑 Älä ohenna verhoa mittaamatta (kontrasti.py) — vika ei näy silmällä
   tummilla sivustoilla, joita näytteistä on enemmistö. */
.nayte-teksti { position: absolute; inset: auto 0 0 0;
  padding: clamp(52px, 6vw, 72px) clamp(18px, 2.4vw, 26px) clamp(18px, 2.4vw, 26px);
  /* Häivytys voimakkaammaksi 30.9.2026: vaalealla näytesivulla (Länsi-Helsinki)
     alkuperäisen sivun leipäteksti näkyi haaleana otsikon yläpuolella 74–88 %
     kaistalla. Tumma osa (≥ .95) ulottuu nyt 84 %:iin ja pehmeä reuna on
     vain ylimmät 16 %. */
  background: linear-gradient(to top, rgba(5,5,18,.98) 0%, rgba(5,5,18,.97) 60%, rgba(5,5,18,.955) 84%, rgba(5,5,18,.75) 93%, rgba(5,5,18,0) 100%); }
.nayte h3 { margin: 0 0 6px; font-size: 1.12rem; color: #FFFFFF; }
.nayte p { margin: 0; color: rgba(255,255,255,.88); font-size: .93rem; line-height: 1.55; }
/* 🛑 KOKO NÄYTE ON LINKKI (Valtteri 22.9.2026). Aiemmin sivustolle pääsi vain
   osoitetekstiä klikkaamalla, vaikka kortti nousi hoverissa ja näytti siksi
   kauttaaltaan klikattavalta: klikkaus kuvaan ei tehnyt mitään.
   🛑 Peitto on oma tyhjä <a>, EI osoitetekstin ::after (tavallinen stretched
   link). Syy: `.nayte-teksti` on absoluuttinen, joten se olisi ollut peiton
   sijoituskehys ja peitto olisi kattanut vain alareunan tekstilaatikon.
   Selitin on `.nayte > a.nayte-koko` eikä pelkkä luokka, jotta se voittaa
   rivin ~1275 `.nayte > *` -puskurisäännön järjestyksestä riippumatta. */
.nayte > a.nayte-koko { position: absolute; inset: 0; z-index: 3; border-radius: inherit; }
.nayte > a.nayte-koko:focus-visible { outline: 2px solid var(--c-accent3); outline-offset: -4px; }
/* Osoite ei ole enää linkki vaan linkin näköinen teksti: koko kortti hoitaa
   klikkauksen, ja sisäkkäinen <a> olisi sekä ylimääräinen tabulaattorikohde
   että peiton alla saavuttamaton. */
.nayte-osoite { color: var(--c-accent3); font-weight: 600; }
/* Osoite omalle rivilleen, jotta se erottuu kuvauksesta kapeassa sarakkeessa. */
.nayte-linkki { margin-top: 8px !important; font-size: .88rem; }
@media (hover: hover) {
  .nayte { transition: transform .25s, border-color .25s; }
  .nayte:hover { transform: translateY(-3px); border-color: color-mix(in srgb, var(--c-primary) 45%, var(--c-border)); }
  .nayte:hover .nayte-osoite { text-decoration: underline; text-underline-offset: 3px; }
}
.nayte-teksti { min-width: 0; }
/* 🛑 Tässä oli 22.9.2026 asti kaksi jäänneriviä poistetusta kahden sarakkeen
   asettelusta: `.nayte h3 { font-size: 1.2rem }` ja `.nayte p { color: var(--c-text) }`.
   Ne tulivat myöhemmin kaskadissa ja kumosivat kuvan päällä olevan tekstin
   valkoisen värin, eli verhon kontrastilaskelma ei pitänyt paikkaansa.
   Älä palauta niitä: teksti on tumman verhon päällä, joten se on valkoinen. */

/* --- Kuva + teksti rinnakkain (palvelusivujen osiot) --- */
.kuva-split { display: grid; grid-template-columns: 1fr 1fr; gap: clamp(28px, 5vw, 60px);
  align-items: center; }
.kuva-split img { width: 100%; max-width: 480px; height: auto; margin-inline: auto;
  filter: drop-shadow(0 30px 60px rgba(0,0,0,.55)); }
.kuva-split--kaanteinen > :first-child { order: 2; }
@media (max-width: 860px) {
  .kuva-split { grid-template-columns: 1fr; }
  .kuva-split--kaanteinen > :first-child { order: 0; }
}

/* --- Alasivun hero kahdessa palstassa: teksti + kuvitus --- */
/* Sarakesuhde 1.15/.85 → 1.35/.65 (30.9.2026): tekstisarake oli 589 px, jolloin
   h1 katkesi neljälle riville neljällä sivulla (palvelut, meistä, hakukone-
   optimointi, verkkokauppa). Nyt 691 px ja kaikki kymmenen kuvallista heroa
   ovat 2–3 rivillä sekä 1024 että 1440 px:llä (mitattu). Kuvitus kutistuu
   400 → 333 px, mikä riittää koristekuvalle. Otsikkokoko pysyy samana kuin
   muilla sivuilla. */
.hero-sub-grid { display: grid; grid-template-columns: 1.35fr .65fr; gap: clamp(24px, 4vw, 56px); align-items: center; }
.hero-sub-grid .hero-copy { max-width: none; }
.hero-sub-kuva { display: grid; place-items: center; position: relative; }
.hero-sub-kuva img { width: 100%; max-width: 400px; height: auto;
  filter: drop-shadow(0 30px 60px color-mix(in srgb, var(--c-accent2) 26%, transparent)); }
/* Kuvitus isommaksi työpöydällä ilman että tekstisarake kapenee (QA 30.9.2026).
   Sarakesuhde 1.35/.65 pidetään (h1 2–3 riviä, ks. yllä), mutta kuva saa
   ylittää sarakkeensa: 861–1199 px:llä 6 % kumpaankin suuntaan (sarakeväli ja
   reunan täyte ovat ≥ 40 px), 1200 px:stä alkaen oikealle säiliön ulkopuolelle
   niin, että ruudun reunaan jää aina vähintään 60 px. Aiemmin kuva oli
   294–333 px ja oikea puolisko tyhjä. Kapeampi tekstisarake testattiin
   (1.25/.75 ja 1.2/.8): h1 nousi 4–5 riville neljällä sivulla, joten hylätty.
   max-height pitää neliömäiset kuvitukset heron korkuisina. */
@media (min-width: 861px) {
  .hero-sub-kuva img { width: 112%; max-width: none; max-height: 380px; object-fit: contain; }
}
@media (min-width: 1200px) {
  .hero-sub-kuva { justify-items: start; }
  .hero-sub-kuva img { width: calc(100% + clamp(0px, (100vw - 1200px) / 2, 90px)); max-height: 420px; }
}
.hero-sub-kuva::before { content: ""; position: absolute; inset: 12% 8%; border-radius: 50%;
  background: radial-gradient(circle, color-mix(in srgb, var(--c-accent3) 18%, transparent), transparent 70%);
  filter: blur(38px); z-index: -1; }
@media (max-width: 860px) {
  .hero-sub-grid { grid-template-columns: 1fr; }
  .hero-sub-kuva img { max-width: 300px; }
}

/* ============================ LÄHIALUEET (sisäinen linkitys) ============================
   🛑 Miksi tämä on olemassa: 35 paikkakuntasivua sai vain YHDEN sisääntulevan
   sisältölinkin (koontisivulta), vaikka projektin päätös 6 vaatii vähintään kolme.
   Vanhan foorly.com:in ydinongelma oli juuri tämä — 371 orpoa sivua. Jokainen sivu
   linkittää nyt viiteen maantieteellisesti lähimpään, joten jokainen saa 4–6
   sisääntulevaa linkkiä ja kävijä pääsee naapurikuntaan yhdellä klikkauksella. */
.lahialueet { list-style: none; margin: 0; padding: 0;
  display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 230px), 1fr)); gap: 12px; }
.lahialueet li { position: relative; padding: 15px 18px 15px 34px;
  background: var(--c-surface); border: 1px solid var(--c-border); border-radius: var(--r-sm);
  transition: border-color .2s, transform .2s; }
.lahialueet li::before { content: ""; position: absolute; left: 16px; top: 21px;
  width: 10px; height: 10px; background: var(--grad);
  clip-path: polygon(30% 0, 100% 0, 70% 100%, 0 100%); }
.lahialueet a { display: block; font-weight: 600; color: var(--c-heading); font-size: .97rem; }
/* Koko laatta on osuma-alue (30.9.2026): linkki oli 26 px korkea 60 px:n
   laatassa. Täyte 15 px on sama kuin li:n pystytäyte ja negatiivinen marginaali
   kumoaa sen, joten linkin laatikko on koko laatan korkuinen (56 px) eikä
   asettelu muutu. (Aiempi ::after-suunnitelma jäi kommentiksi ilman sääntöä.) */
.lahialueet a { padding-block: 15px; margin-block: -15px; }
.lahialueet a::after { content: ""; position: absolute; inset: 0; border-radius: inherit; }
.lahialueet span { display: block; margin-top: 3px; font-size: .8rem; color: var(--c-muted); }
@media (hover: hover) {
  .lahialueet li:hover { border-color: color-mix(in srgb, var(--c-primary) 55%, var(--c-border)); transform: translateY(-2px); }
  .lahialueet li:hover a { color: var(--c-accent3); }
}
.lahialueet-kaikki { margin: 22px 0 0; }
.lahialueet-kaikki a { font-weight: 600; color: var(--c-accent3); }
/* Kosketusalue 44 px (QA 30.9.2026, oli 20 px): inline-linkin pystytäyte ei
   muuta riviväliä. */
.lahialueet-kaikki a { padding-block: 12px; }


/* Taulukot kapealla ruudulla: tiiviimpi typografia, jotta hintasarake tulee
   näkyviin ilman vieritystä tai heti sen alussa (20.9.2026). */
@media (max-width: 560px) {
  .tbl { font-size: .84rem; min-width: 430px; }
  .tbl th, .tbl td { padding: 10px 12px; }
  .tbl caption { padding: 13px 12px 0; }
}
/* Oikean reunan häivytys puhelimessa (QA 30.9.2026). Yllä oleva
   background-attachment-varjo on rgba(0,0,0,.55) navyn päällä eikä erotu:
   390 px:llä taulukko leikkautui keskeltä sanaa ilman vihjettä vierityksestä
   (13 sivua). Maski häivyttää viimeiset 48 px, jolloin näkyy, että sisältö
   jatkuu. Staattinen: näkyy myös kun vieritys on lopussa, koska CSS ei tunne
   vieritysasemaa ilman scroll-driven-animaatioita (tuki puutteellinen). */
@media (max-width: 640px) {
  .tbl-wrap {
    -webkit-mask-image: linear-gradient(to right, #000 calc(100% - 48px), rgba(0, 0, 0, .15));
    mask-image: linear-gradient(to right, #000 calc(100% - 48px), rgba(0, 0, 0, .15));
  }
}


/* --- Mobiilisäädöt 20.9.2026 (mitattu 390 px) --- */
@media (max-width: 560px) {
  /* Prosessiruudukko oli 2 saraketta -> 155 px tekstipalsta, 2-3 sanaa rivillä */
  .timeline { grid-template-columns: 1fr; gap: 24px; }
  /* Hintarivi on 302 px leveä: selite ja summa mahtuvat rinnakkain vain hieman
     pienemmällä (aiemmin tässä kavennettiin kolmen luvun ruudukkoa, joka
     poistettiin 22.9.2026). */
  .dash-hinta dt { font-size: .88rem; }
  .dash-hinta dd { font-size: 1.18rem; }
  /* 🛑 Nostorivin pinoaminen allekkain poistettu 23.9.2026 (K16): se teki
     herosta 1 238 px korkean 390 px:llä. Kolmen sarakkeen säädöt ovat nyt
     .hero-metricsin oman 560 px -lohkon yhteydessä, älä ylikirjoita niitä
     täältä. */
}

/* --- Paikkakuntalistaus: maakunnat sarakkeina, ei 18 erillistä osiota --- */
.maakunnat { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(100%, 220px), 1fr));
  gap: clamp(22px, 3vw, 34px); }
.maakunta h3 { font-size: .82rem; font-weight: 700; letter-spacing: .12em; text-transform: uppercase;
  color: var(--c-accent3); margin-bottom: 12px; padding-bottom: 10px;
  border-bottom: 1px solid var(--c-border); display: flex; align-items: center; gap: 9px; }
.maakunta h3::before { content: ""; width: 10px; height: 10px; flex: none; background: var(--grad);
  clip-path: polygon(30% 0, 100% 0, 70% 100%, 0 100%); }
.maakunta ul { list-style: none; margin: 0; padding: 0; display: grid; gap: 7px; }
.maakunta li a { font-size: .97rem; color: var(--c-text); }
@media (hover: hover) { .maakunta li a:hover { color: var(--c-accent3); } }


/* Hero-valokuva: teräväkulmainen suorakaide erottui sivuston pyöristyksistä
   (hakusanamainonta.html, mitattu 20.9.2026). */
.hero-visual > img, .hero-sub-kuva > img.kuva-raami { border-radius: var(--r-lg); }

/* --- Korttiruudukko sovittaa sarakemäärän korttien määrään (20.9.2026) ---
   Kiinteä kolmen sarakkeen ruudukko jätti orpoja kortteja: artikkelilistauksen
   "Hinnat ja tarjoukset" = 1 kortti kolmasosaleveänä ja ~700 × 290 px tyhjää
   vieressä, "Kenelle ja millaiselle yritykselle" = 2, neljän kortin ryhmät 3+1.
   Nyt 4 korttia asettuu 2×2:ksi, 2 vierekkäin ja yksin jäävä kortti rajataan
   luettavaan leveyteen sen sijaan että se venyisi tai jättäisi rivin tyhjäksi. */
.cards--3:has(> :nth-child(4):last-child) { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.cards--3:has(> :nth-child(2):last-child) { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.cards--3:has(> :first-child:last-child)  { grid-template-columns: minmax(0, min(540px, 100%)); }

/* Korttiotsikko: line-height 1,08 puristi kaksirivisen otsikon kiinni itseensä */
.card h3 { line-height: 1.25; margin-bottom: 4px; }

/* --- Pitkät suomen yhdyssanat eivät mahdu kapeaan palstaan (20.9.2026) ---
   "hakusanamainonta", "hakukoneoptimointi", "verkkokauppa-alusta" ym. venyttivät
   korttiotsikoita ruudun yli 390 px:n näytöllä (mitattu: kortin otsikko ulottui
   x=413 eli 23 px ruudun ulkopuolelle). Tavutus + sanan katkaisu hätävarana. */
h1, h2, h3, h4, .card h3, .tile h3, .lahialueet a, .maakunta a {
  overflow-wrap: break-word; }
/* Tasapainotettu rivitys (QA 30.9.2026): kapealla näytöllä otsikon viimeiselle
   riville jäi yksi sana ("Esimerkkejä tekemistämme / sivustoista"). Selain, joka
   ei tue ominaisuutta, rivittää kuten ennen. */
h1, h2, h3 { text-wrap: balance; }
/* 🛑 Tavutus vain kapeilla näytöillä (21.9.2026). Globaalina se tavutti myös
   työpöydän hero-otsikon ("Kotisivut yrityk-selle" 1440 px:llä), vaikka tilaa
   oli reilusti – tavuviiva isossa otsikossa näyttää virheeltä. Kapeassa
   palstassa tavutus on edelleen tarpeen pitkille yhdyssanoille. */
@media (max-width: 860px) {
  h1, .lahialueet a, .maakunta a { hyphens: auto; }
  /* 🛑 h2-h4 eivät tavutu automaattisesti (QA 30.9.2026): selaimen suomen
     sanakirja katkoi 37 otsikkoa 15 sivulla vääristä kohdista ("pieny-rityksen",
     "hakuko-neoptimointi", "kertaluon-teinen"). Pitkiin yhdyssanoihin on lisätty
     &shy; HTML:ään yhdyssanarajalle, ja overflow-wrap (yllä) on hätävara. */
  h2, h3, h4 { hyphens: manual; }
  /* 🛑 Poikkeus: heron h1 ei tavutu koskaan (katselmus 23.9.2026, K17).
     390 px:llä siitä tuli "Kotisivut yrityk-selle", ja tavutettu pääotsikko
     hidastaa juuri sen lauseen lukemista, jonka varassa koko sivu on. Muut
     otsikot tavuttuvat edelleen, ja ≤420 px:n overflow-wrap estää ylivuodon. */
  .hero h1 { hyphens: manual; }
}
html[lang="fi"] { hyphenate-limit-chars: 8 4 4; }
/* `hyphens: auto` nojaa selaimen suomen tavutussanakirjaan, jota kaikissa
   ympäristöissä ei ole. Kapeilla näytöillä varmistetaan katkaisu ilman sitä:
   mitattu 320 px:llä ylivuoto 30 px pelkän tavutuksen varassa. */
@media (max-width: 420px) {
  h1, h2, h3, h4, .card h3, .tile h3 { overflow-wrap: anywhere; }
  .card h3 { font-size: 1.08rem; }
}
/* 🛑 320 px (iPhone SE 2016): hintakortit vuotivat ruudun yli (21.9.2026).
   Mitattu juuritason vaakavieritys 25 px sekä etusivulla että
   edulliset-kotisivut.html:ssä. Syy EI ollut taulukoissa — .tbl-wrap
   vierittää ne omassa laatikossaan — vaan hintakorteissa: grid-lapsen
   oletusarvo `min-width: auto` estää kutistumisen min-contentin alle, joten
   284 px:n sarakkeessa istui 327 px:n kortti. minmax(0,1fr) purkaa rajan ja
   pienempi sisennys antaa tekstille tilaa rivittyä. */
@media (max-width: 400px) {
  .pricing { grid-template-columns: minmax(0, 1fr); }
  .tier { min-width: 0; padding: 22px; }
}

/* ============================ REFERENSSILOGOT ============================
   Logoseinä kokemuksia.html:ssä (80 asiakaslogoa vanhalta foorly.com:ilta,
   21.9.2026). Tummalla taustalla logot eivät erotu, joten jokainen saa oman
   vaalean laatan — sama ratkaisu kuin painetussa referenssilistassa.
   🛑 Logot on esikäsitelty: 33 vaaleaa (tummalle taustalle tarkoitettua)
   logoa on invertoitu tummiksi, jotta ne näkyvät vaalealla laatalla.
   Alkuperäiset osoitteet purettiin ShortPixelin data-u-attribuutista. */
.logowall { display: grid; grid-template-columns: repeat(6, 1fr); gap: 14px; }
.logowall .logo {
  display: grid; place-items: center; min-height: 92px; padding: 16px 18px;
  /* 🛑 Ei vaaleaa laattaa (Valtteri 2.10.2026: "poista kaikki valkoiset taustat").
     Logot ovat *-seina.webp: alkuperäinen, valkoinen tausta poistettu ja tummalle
     katoavat osat (kontrasti #0B0B23:a vasten < 3:1) vaihdettu valkoisiksi suoraan
     tiedostoon. Poikkeukset: _logot-alkuperaiset/seina-poikkeukset.json. */
  /* Liukuväri sama kuin .cta-band (Valtteri 2.10.2026: "pelkkä raja vähän irrallinen"). */
  background: linear-gradient(135deg, var(--c-surface), var(--c-bg-2)); border: 1px solid var(--c-border); border-radius: var(--r);
  transition: transform .2s, box-shadow .25s, background .2s;
}
.logowall .logo img { max-width: 100%; max-height: 44px; width: auto; height: auto; display: block; }
/* 🛑 YKSI LAATTAVÄRI KAIKILLE LOGOILLE (Valtteri 22.9.2026: "onko vähän outo
   että osa valkoinen ja osa sininen tausta ... vaalealla ja sinisellä joka sopii
   Foorlyn väreihin").

   Välivaihe, jossa vaaleat logot saivat tumman laatan, hylättiin: seinä näytti
   sekavalta kun joka kolmas laatta oli eri värinen. Nyt laatta on yksi vaalea
   sinertävä sävy (#EEF0FA), joka on Foorlyn violetin suuntaan taitettu valkoinen.

   🛑 Logot ovat käsittelemättömiä alkuperäisiä (Valtteri 2.10.2026: "palauta
   joka ikinen logo originaaliksi"). 22.9. tehty tummennus (19 vaaleaa logoa,
   HSL-lightness alas) on peruttu, joten valkoiset logot eivät erotu laatalta.
   Käsittelemättömät alkuperäiset: `asiakkaat/foorly/_logot-alkuperaiset/`
   (sivusto-kansion ULKOPUOLELLA, ei deployata). Skripti: scratchpadin
   lopullinen_logo.py, logiikka kuvattu tässä. */
@media (hover: hover) {
  .logowall .logo:hover { transform: translateY(-3px); background: linear-gradient(135deg, var(--c-surface-2), var(--c-surface)); box-shadow: 0 14px 34px rgba(0,0,0,.35); }
}
.logowall .logo:focus-visible { outline: 2px solid var(--c-accent3); outline-offset: 3px; }
@media (max-width: 1080px) { .logowall { grid-template-columns: repeat(4, 1fr); } }
@media (max-width: 720px)  { .logowall { grid-template-columns: repeat(3, 1fr); gap: 10px; }
                             .logowall .logo { min-height: 76px; padding: 12px; }
                             .logowall .logo img { max-height: 34px; } }
/* 🛑 Kolme saraketta pidetään 360 px:ään asti: 79 logoa kahdessa sarakkeessa
   venytti sivun 40 riviin mobiilissa. Mitattu 21.9.2026. */
@media (max-width: 360px)  { .logowall { grid-template-columns: repeat(2, 1fr); } }

/* ============================ CLS-ESTO: NAV JA FOOTER ============================
   🛑 Nav ja footer renderöidään shared.js:llä, joten ennen skriptin ajoa
   #site-nav on tyhjä ja koko sivu hyppää 76 px alas kun navi ilmestyy.
   Mitattu 21.9.2026: CLS 0,127 (Googlen raja 0,1). Tyhjälle kääreelle
   varataan sama korkeus kuin valmiille navigaatiolle; kun shared.js on
   täyttänyt sen, :empty ei enää päde ja display:contents ottaa vallan. */
/* 🛑 Varaus on nyt 0, EI 76 px. Navi ei enää vie omaa tilaansa vaan kelluu
   bannerin päällä, joten 76 px:n varaus työntäisi sivua alas ennen JS:ää ja
   aiheuttaisi juuri sen CLS-hypyn, joka 21.9. korjattiin. Tilan varaa .hero. */
#site-nav:empty { display: block; height: 0; }
#site-footer:empty { display: block; min-height: 520px; }
@media (max-width: 740px) { #site-footer:empty { min-height: 760px; } }

/* ============ KOTISIVUT PLUS — FOORLY-KOHTAINEN YLIKIRJOITUS ============
   🛑 kp/kotisivut-plus.css ja kp/kotisivut-plus.js ovat kopio jaetusta
   _yhteinen/widgets/-kirjastosta. Kirjastoa EI muokata täällä, koska sama
   koodi ajetaan muidenkin asiakkaiden sivuilla. Kaikki Foorlyn oma ulkoasu
   tehdään tästä lohkosta ja widgetin omilla --kp-*-muuttujilla.

   🛑 Kirjaston tyylitiedosto ladataan <head>issä styles.css:n JÄLKEEN, joten
   jokainen sääntö tarvitsee yhden luokan enemmän kuin kirjaston vastaava:
   kirjasto .kp-avaus (0,1,0) → tässä .kp-root.kp-avaus (0,2,0). Elementeillä
   on molemmat luokat, joten ylimääräinen luokka ei muuta markupia.

   Miksi: avausnappi oli --c-dark (#050514) lähes mustalla sivutaustalla
   (#07071A) eikä erottunut lainkaan, ja avattu paneeli oli brändinavya
   tummalla sivulla (Valtteri 21.9.2026). Nappi ja paneeli nostetaan irti
   taustasta vaalealla syaaninsinisellä liukuvärillä.

   Kontrastit mitattu (WCAG 2.1, sRGB-suhde) — luvut sääntöjen kohdalla.
   Liikkeen vähennyksen (prefers-reduced-motion) kirjastosääntö käyttää
   !important-lippua, joten se pysäyttää myös nämä animaatiot. */

/* ---- 1. Kelluva avausnappi ---- */

/* NAPPI NÄKYY HETI (Valtteri 2.10.2026: "foorlyllä se ei ainakaan ole näkyvissä").
   🛑 Kumoaa K23:n (23.9.2026), jossa nappi piilotettiin kunnes hero oli
   ohitettu (`body.heron-ohi`). Mitattu demossa 2.10.: työpöydällä nappi näkyi
   vasta evästevalinnan JA vierityksen jälkeen, puhelimessa ei koskaan.
   Foorly myy Kotisivut Plussaa, joten oman sivun on näytettävä se.
   Evästepalkin aikainen piilotus (alempana) jää voimaan. */

.kp-root.kp-avaus {
  /* 🛑 Tummennettu 21.9.2026 (Valtteri): *"muuta sävyä hieman tummemman siniseen
     suuntaan? nyt erottuu vähän liikaakin se"*. Aiempi liuku oli hyvin vaalea
     (#7BEFFF → #A9D8FF → #C3C9FF) ja hyppäsi tummalta sivulta silmille kuin
     valo. Nyt syvä teräksensininen, joka on yhä brändin cyan–violetti-akselilla
     mutta istuu taustaan. Valkoinen teksti: 6,93:1 · 8,40:1 · 9,27:1. */
  /* 🛑 2.10.2026 (Valtteri): "painike ns se lilan pinkki". Sivuston painikkeiden
     liukuväri tummennettuna, jotta valkoinen teksti läpäisee AA:n: 5,95:1 · 5,45:1. */
  background: linear-gradient(118deg, #5A48E8 0%, #A81FD8 100%);
  color: #FFFFFF;
  font-weight: 700;
  /* Nostetaan 26 px:ään reunasta, jotta aaltorengas mahtuu kasvamaan
     leikkautumatta ruudun laitaan (ks. foorly-kp-aalto alla). */
  right: 26px; bottom: 26px;
  /* Hehku hillitty samasta syystä: kirkas cyan-kehä teki napista lampun. */
  box-shadow:
    0 0 0 1px color-mix(in srgb, var(--c-accent3) 26%, transparent),
    0 10px 28px -10px rgba(5, 5, 20, .7),
    0 22px 52px -18px rgba(5, 5, 20, .9);
}
/* Pallo käännettiin vaaleaksi, kun nappi tummeni: tumma pallo katosi tummaa
   nappia vasten. Vaalea cyan erottuu napista (5,16:1) ja tumma ikoni pallosta
   (12,96:1 · 11,57:1). */
.kp-root.kp-avaus .kp-avaus__pallo {
  background: rgba(255, 255, 255, .2);
  color: #FFFFFF;
}
@media (hover: hover) {
  .kp-root.kp-avaus:hover {
    box-shadow:
      0 0 0 1px color-mix(in srgb, var(--c-accent3) 45%, transparent),
      0 16px 38px -12px rgba(5, 5, 20, .8),
      0 28px 64px -18px rgba(5, 5, 20, .92);
  }
}

/* ---- 2. Aaltoefekti näkyvämmäksi ----
   Kirjasto käynnistää aaltosarjan 15 s latauksen jälkeen (`huomio()`, kun
   paneeli ei avaudu itsestään) ja uudelleen kun itse avautunut paneeli
   suljetaan. Rengas oli 2 px violettia ja kasvoi -17 px:ään. Tässä se on 3 px
   kirkasta cyania, hehkuu ja kasvaa -24 px:ään.
   🛑 **Kierrosmäärä 5 → 1 (Valtteri 21.9.2026):** *"widget välkkyy monta kertaa
   alussa, eli ei niin. vain kerran ehkä 15 tai 20 sec"*. Viisi kierrosta tarkoitti
   **12 sekuntia yhtäjaksoista välkkymistä**, ja koska tämä sääntö on kirjastoa
   spesifisempi (`.kp-root.kp-avaus--pulssi`), se ohitti kirjaston oman rajauksen.
   Nyt kaksi rengasta lähtee 1,2 s välein yhden kerran, eli huomio kestää 3,6 s.
   Älä nosta kierrosmäärää takaisin.
   Renkaan kasvuvara on syy siihen, miksi nappi siirrettiin 22 px:stä 26 px:ään
   ruudun reunasta: -24 px ilman siirtoa leikkaisi renkaan laitaan.
   🛑 Liikkeen vähennys hoidetaan omalla säännöllä alempana: kirjaston
   !important-lippu koskee vain `--pulssi`-luokkaa ja itse elementtejä, ei
   näitä pseudo-elementtejä. */
.kp-root.kp-avaus::before,
.kp-root.kp-avaus::after {
  border-width: 3px;
  border-color: color-mix(in srgb, var(--c-accent3) 85%, #FFFFFF);
  box-shadow: 0 0 20px color-mix(in srgb, var(--c-accent3) 55%, transparent);
}
/* Toinen rengas lähtee puolen kierroksen jäljessä, jolloin ulospäin leviää
   aaltosarja eikä yksittäinen sykähdys. */
.kp-root.kp-avaus--pulssi::after  { animation: foorly-kp-aalto 2.4s cubic-bezier(.22, .7, .3, 1) 1; }
/* Yksi aalto per muistutus (Valtteri 5.10.2026: "vain kerran esim 10 sec välein"). */
.kp-root.kp-avaus--pulssi::before { animation: none; }
@keyframes foorly-kp-aalto {
  0%   { inset: -3px;  opacity: 1; border-width: 3px; }
  10%  { opacity: 1; }
  70%  { inset: -24px; opacity: 0; border-width: 1px; }
  100% { inset: -24px; opacity: 0; border-width: 1px; }
}
@media (prefers-reduced-motion: reduce) {
  .kp-root.kp-avaus::before,
  .kp-root.kp-avaus::after,
  .kp-root.kp-avaus--pulssi::before,
  .kp-root.kp-avaus--pulssi::after { animation: none !important; opacity: 0 !important; }
}

/* ---- 3. Avattu paneeli: vaalea pinta tummalla sivulla ----
   Koko paneelin paletti käännetään vaaleaksi widgetin omilla muuttujilla,
   jolloin kirjaston kaikki osat (ylä- ja alapalkki, toimintorivit, kentät,
   kortit) seuraavat mukana ilman että kirjastoa kosketaan. */
.kp-root.kp-paneeli {
  /* 🛑 TUMMA PANEELI 2.10.2026 (Valtteri: "laita tausta tummemmaksi vaikka sama
     tausta kuin Katsotaanko miltä sivusi voisivat näyttää?"). Tausta on sama kuin
     .cta-band: navy-liuku + cyan- ja violettihehku. Aiempi vaalea paletti
     (21.9.) korvattu. Kontrastit #111038-pohjalla: otsikko #FFF 17,9:1,
     teksti #D9DCF5 13,6:1, himmeä #AEB3E0 9,0:1. */
  --kp-surface:    #16154C;   /* rivit, kentät, alapalkki */
  --kp-surface-2:  #201E62;   /* hover */
  --kp-heading:    #FFFFFF;
  --kp-text:       #D9DCF5;
  --kp-muted:      #AEB3E0;
  --kp-on-dark:    #FFFFFF;
  --kp-dark:       #050514;
  --kp-border:     #2E2C6E;
  --kp-primary:    #5A48E8;   /* painikkeen valkoinen teksti 5,95:1 */
  --kp-primary-2:  #A81FD8;   /* painikkeen valkoinen teksti 5,45:1 */
  --kp-shadow-sm:  0 8px 22px -10px rgba(0, 0, 0, .6);
  background:
    radial-gradient(42% 50% at 12% 8%, color-mix(in srgb, var(--c-accent3) 21%, transparent), transparent 70%),
    radial-gradient(46% 54% at 92% 18%, color-mix(in srgb, var(--c-accent2) 22%, transparent), transparent 70%),
    linear-gradient(135deg, var(--c-surface), var(--c-bg-2));
  color: var(--kp-text);
  border-color: color-mix(in srgb, var(--c-accent3) 30%, var(--c-border));
  box-shadow:
    0 0 0 1px color-mix(in srgb, var(--c-accent3) 22%, transparent),
    0 0 72px -4px color-mix(in srgb, var(--c-accent3) 26%, transparent),
    0 34px 84px -22px rgba(0, 0, 0, .95);
}
/* Yläpalkki yhtenäiseksi ja Foorlyn logo nimen tilalle (Valtteri 2.10.2026) on
   kirjaston oletus 0.13:sta alkaen (logo haetaan .brand img:stä). Aiempi
   taustakuvakikka poistettu, muuten logo näkyisi kahdesti. */
.kp-root.kp-paneeli .kp-paneeli__ylapalkki { background: transparent; border-bottom: 0; }
/* Rajaton yläpalkki ei tarvitse rungon 20 px yläreunusta: otsikko nousee 18 px
   (Valtteri 2.10.2026: "Kuinka voimme auttaa? mahtuu ylemmäs nyt"). Koskee
   kaikkia näkymiä, jotta otsikot ovat samalla korkeudella. */
.kp-root.kp-paneeli .kp-paneeli__runko { padding-top: 2px; }
.kp-root.kp-paneeli .kp-paneeli__nimi {
  height: 30px; flex: 0 0 105px; margin-right: auto;
  background: url("kuvat/logo-foorly.webp") left center / contain no-repeat;
  color: transparent; font-size: 0; overflow: hidden;
}
/* Kirjasto antaa lomakkeelle ja lähetysriville paneelin pintavärin, joka erottui
   tumman liukuvärin päällä omana laatikkonaan (mitattu kuvasta 2.10.2026). */
.kp-root.kp-paneeli .kp-lomake { background: transparent; }
.kp-root.kp-paneeli .kp-toiminnot-rivi {
  background: linear-gradient(to bottom, transparent, var(--c-bg-2) 16px);
}
/* Ikonit pääväri tummalla olisivat liian himmeitä: cyan 12,9:1. */
.kp-root.kp-paneeli .kp-tiedot__ikoni,
.kp-root.kp-paneeli .kp-toiminto__ikoni { color: var(--c-accent3); }
/* Kirjaston virhepuna #DC2626 jää vaalealla pinnalla 4,02–4,48:1 eli alle
   AA:n. Tummennetaan: #B91C1C = 6,47:1 valkoisella, 5,39:1 liu'un tummimmalla. */
/* Kampanjanauha (kirjasto 0.6+, sijainti: 'ylos'). 🛑 Yllä --kp-on-dark on
   tumma (#111038) vaaleaa paneelia varten, joten kirjaston tumma kortti jäi
   tummaa tummalla (mitattu 2.10.2026, teksti ei erottunut). Kortti saa napin
   liukuvärin ja oman vaalean tekstivärinsä. Valkoinen teksti 5,95:1 · 5,45:1. */
.kp-root.kp-paneeli .kp-kampanja {
  --kp-on-dark: #FFFFFF;
  background: linear-gradient(118deg, #5A48E8 0%, #A81FD8 100%);
  color: #FFFFFF;
}
.kp-root.kp-paneeli .kp-kampanja p { opacity: 1; }
.kp-root.kp-paneeli .kp-kampanja .kp-btn { background: #FFFFFF; color: #2B1A8A; }   /* 10,9:1 */
.kp-root.kp-paneeli .kp-virhe { color: #FCA5A5; }   /* tummalla 9,6:1 */
.kp-root.kp-paneeli .kp-kentta--virhe input,
.kp-root.kp-paneeli .kp-kentta--virhe textarea { border-color: #F87171; }

/* ---- 4. Puhelin ----
   Kirjasto kutistaa napin pyöreäksi ikoniksi ja kasvattaa pallon täyteen
   kokoon, jolloin tumma pallo peittäisi koko vaalean liukuvärin. Pallo
   läpinäkyväksi ja ikoni tummaksi: vaalea nappi säilyy myös puhelimessa
   (ikoni #111038 liu'un päällä 13,53:1 · 12,08:1 · 11,36:1).
   Rengas kasvaa vähemmän, koska nappi on lähempänä ruudun reunaa. */
@media (max-width: 640px) {
  .kp-root.kp-avaus {
    right: calc(18px + env(safe-area-inset-right, 0px));
    bottom: calc(18px + env(safe-area-inset-bottom, 0px));
  }
  /* Kirjasto kutistaa napin puhelimessa pyöreäksi ikoniksi. Pallo pidetään
     vaaleana myös silloin, koska nappi on nyt tumma: aiempi `background: none`
     oli tehty vaaleaa nappia varten ja jättäisi ikonin näkymättömiin. */
  .kp-root.kp-avaus .kp-avaus__pallo {
    background: transparent;
    color: #FFFFFF;
  }
  @keyframes foorly-kp-aalto {
    0%   { inset: -3px;  opacity: 1; border-width: 3px; }
    10%  { opacity: 1; }
    70%  { inset: -16px; opacity: 0; border-width: 1px; }
    100% { inset: -16px; opacity: 0; border-width: 1px; }
  }
}

/* ============ HOVER-NOSTON VILKKUMISSILMUKKA (Valtteri 22.9.2026) ============
   🛑 Kun elementti nousee hoverissa (translateY), sen alareuna liikkuu pois
   kursorin alta. Reunalla tämä tuottaa silmukan: hover päättyy, elementti
   palaa, hover alkaa uudelleen — käyttäjälle se näkyy pomppimisena ja
   edestakaisena zoomina. Korjaus on läpinäkyvä puskuri, joka pitää elementin
   osumapinnan paikallaan noston ajan. Puskuri on nostoa suurempi (5 px > 4 px).
   Älä poista: ilman tätä sama vika palaa jokaiseen nostavaan korttiin. */
.tile, .tstep, .nayte, .logowall .logo { position: relative; }
/* .lahialueet li jätettiin pois: sen ::before on jo luettelomerkki (rivi ~785),
   ja sen nosto on vain 2 px pienessä elementissä. */
.tile::before, .tstep::before, .nayte::before,
.logowall .logo::before {
  content: ""; position: absolute; inset: -5px; border-radius: inherit;
  pointer-events: auto; z-index: 0;
}
/* Puskuri ei saa peittää sisältöä eikä linkkejä. */
.tile > *, .tstep > *, .nayte > *, .logowall .logo > * { position: relative; z-index: 1; }
/* 🛑 Poikkeus työnäytteen tekstilohkolle (löytyi mitatessa 22.9.2026). Yllä oleva
   sääntö pakotti `position: relative` KAIKILLE .nayten lapsille, myös liu'ulle,
   jonka `.nayte-teksti` oli määritelty absoluuttiseksi 50 riviä aiemmin. Sääntö
   tuli kaskadissa myöhemmin ja voitti, joten teksti ei ollut koskaan kuvan
   päällä vaan sen ALLA — eli juuri se asettelu, jonka `pohjat/_ohjeet.md` §12
   kieltää, ja jonka takia korttiin jäi musta kaista kuvan ja tekstin väliin.
   Absoluuttinen sijoittelu riittää pinoamiseen yksinään, joten puskurin yli
   pääsemiseen tarvitaan vain z-index. */
.nayte > .nayte-teksti { position: absolute; z-index: 1; }

/* ===================================================================
   Kotisivut Plus -widget piiloon mobiilissa (Valtteri 23.9.2026, K7)
   ===================================================================
   🛑 Kiinteä CTA-palkki ja widgetin kelluva nappi asuvat molemmat ruudun
   alalaidassa, joten ne peittäisivät toisensa. Mobiilissa palkki voittaa:
   siinä on näkyvä teksti ja klikattava puhelinnumero, kun widgetin nappi on
   pelkkä ikoni ja lisäksi väistyy alaspäin vieritettäessä.
   Työpöydällä widget jää ennalleen, koska siellä palkkia ei ole.
   🛑 Kirjaston omia tiedostoja (kp/kotisivut-plus.css/.js) ei muokata. Tämä on
   sivustokohtainen ohitus, ja valitsin on yhtä luokkaa tarkempi kuin kirjaston
   oma, koska kirjasto ladataan headissä tämän tiedoston jälkeen. */
/* 🛑 Kumottu 2.10.2026 (Valtteri, ks. yllä): nappi näkyy myös puhelimessa.
   Kun kiinteä CTA-palkki on esillä, nappi nousee sen yläpuolelle, jotta
   kumpikaan ei peitä toista. Palkin korkeus mitattu: --kp-palkki. */
@media (max-width: 768px) {
  body:has(.sticky-cta.show) .kp-root.kp-avaus {
    bottom: calc(var(--kp-palkki, 76px) + 12px + env(safe-area-inset-bottom, 0px));
    transition: bottom .3s cubic-bezier(.2, .7, .2, 1), transform .3s, opacity .3s;
  }
}

/* ============================ EVÄSTEPALKKI ============================
   Näkyy kerran, kunnes kävijä valitsee. Valinta muistetaan localStoragessa,
   ks. shared.js "MITTAUS JA SUOSTUMUS".
   🛑 Palkki on alalaidassa, jossa ovat myös mobiilin CTA-palkki ja työpöydän
   widget-nappi. Molemmat piilotetaan niin kauan kuin valinta on tekemättä,
   jotta alalaidassa on kerrallaan yksi asia. Piilotus on lyhyt: palkki katoaa
   ensimmäisestä valinnasta. */
.evastepalkki {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 120;
  display: flex; flex-wrap: wrap; align-items: center; justify-content: center;
  gap: 14px 22px;
  padding: 16px var(--pad-x) calc(16px + env(safe-area-inset-bottom));
  background: var(--c-surface);
  border-top: 1px solid var(--c-border);
  box-shadow: 0 -18px 40px -24px rgba(0, 0, 0, .8);
  transform: translateY(100%);
  transition: transform .3s cubic-bezier(.2, .7, .2, 1);
}
.evastepalkki.nakyy { transform: none; }
.evastepalkki p { margin: 0; max-width: 62ch; font-size: .92rem; color: var(--c-text-2); }
.evastepalkki a { color: var(--c-accent3); text-decoration: underline; text-underline-offset: 2px; }
.evastepalkki-napit { display: flex; gap: 10px; flex: none; align-items: center; }
.evastepalkki-lue { font-size: .92rem; padding: 10px 6px; white-space: nowrap; }
.evastepalkki .btn { padding: 10px 18px; font-size: .92rem; }
body.evasteet-auki .sticky-cta { display: none !important; }
/* 🛑 Kotisivut Plus -nappi EI piiloudu evästepalkin ajaksi (Valtteri 2.10.2026:
   "en näe sitä vaikka olen kirjautunut"). Demossa palkki on auki jokaisella
   uudella kävijällä, joten piilotus teki napista näkymättömän. Nappi nousee
   palkin yläpuolelle. Palkin korkeus mitattu 2.10.: 81 px (≥1024), 119 px
   (800), 177 px (390). Valitsin voittaa mobiilin CTA-palkkisäännön (:has). */
body.evasteet-auki:has(.evastepalkki.nakyy) .kp-root.kp-avaus { bottom: 95px; }
@media (max-width: 1000px) {
  body.evasteet-auki:has(.evastepalkki.nakyy) .kp-root.kp-avaus { bottom: 133px; }
}
/* Itse auennut paneeli (työpöytä) nousee samoin palkin yläpuolelle, jotta
   evästepalkin napit jäävät painettaviksi (2.10.2026). */
@media (min-width: 641px) {
  body.evasteet-auki:has(.evastepalkki.nakyy) .kp-root.kp-paneeli {
    bottom: 95px; max-height: min(730px, calc(100vh - 117px));
  }
}
@media (min-width: 641px) and (max-width: 1000px) {
  body.evasteet-auki:has(.evastepalkki.nakyy) .kp-root.kp-paneeli {
    bottom: 133px; max-height: min(730px, calc(100vh - 155px));
  }
}
@media (max-width: 640px) {
  body.evasteet-auki:has(.evastepalkki.nakyy) .kp-root.kp-avaus { bottom: calc(191px + env(safe-area-inset-bottom, 0px)); }
}
@media (prefers-reduced-motion: reduce) { .evastepalkki { transition: none; } }
@media (max-width: 640px) {
  .evastepalkki { justify-content: stretch; gap: 12px;
    padding: 12px var(--pad-x) calc(12px + env(safe-area-inset-bottom)); }
  .evastepalkki p { font-size: .86rem; }
  .evastepalkki-napit { width: 100%; flex-wrap: wrap; }
  /* Lue lisää rivittyy napien alle vain jos se ei mahdu samalle riville. */
  .evastepalkki-lue { padding: 6px 4px; }
  /* QA 30.9.2026: flex: 1 jakoi napit tasan (170 px), jolloin "Vain
     välttämättömät" rivittyi kahdelle riville ja palkki oli 206 px (26 %
     ruudusta). Nyt napit jakavat tilan sisällön mukaan ja teksti ei rivity. */
  .evastepalkki-napit .btn { flex: 1 1 auto; justify-content: center; white-space: nowrap; padding: 10px 14px; }
}

/* ============ VISUAALINEN QA-KIERROS 30.9.2026: mobiilin tiivistys ja kosketusalueet ============
   Nämä säännöt ovat tiedoston lopussa tarkoituksella: niiden on voitettava
   aiemmat mobiilisäännöt (.timeline 560 px -lohko) kaskadissa. */

/* ---- Footerin kosketusalueet 44 px ----
   shared.js antaa footerin linkeille 9 px täytteen (43 px) ja alalaidan
   linkeille 8 px (39 px). Alalaidan linkit ovat vierekkäin, joten niiden täyte
   kasvatetaan 44 px:ään ja negatiivinen marginaali kumoaa lisäyksen.
   🛑 Valitsimessa on footer.site, koska shared.js lisää oman tyylinsä headiin
   tämän tiedoston jälkeen: saman tarkkuuden sääntö hävisi järjestyksessä. */
/* Raja 740 → 1023 px (QA 30.9.2026): tabletti (768 px) on kosketuslaite,
   mutta sai työpöydän 16-25 px:n linkit. Sama raja kuin navin valikkonapilla. */
@media (max-width: 1023px) {
  /* Allekkaiset linkit: niiden väli on 43 px, joten 44 px:n osuma-alue ei
     mahdu ilman että väli kasvaa. Täyte 9 → 9,5 px eli +1 px per linkki.
     Negatiivinen marginaali olisi vain siirtänyt seuraavan linkin edellisen
     täytteen päälle, jolloin todellinen osuma-alue olisi jäänyt 43 px:ään. */
  footer.site .foot-col a { padding-block: 9.5px; }
  footer.site .foot-bottom a { padding-block: 11px; margin-block: -3px; }
}

/* ---- Prosessi pystyaikajanaksi puhelimessa ----
   Neljä täysleveää korttia vei 390 px:llä noin 900 px (etusivu oli yhteensä
   16 230 px). Nyt numero on vasemmalla, teksti oikealla ja vaiheet yhdistää
   ohut pystyviiva, eli järjestys luetaan viivasta eikä korttien rajoista.
   Numero on 44 px, jolloin viivan x on 21 px. */
@media (max-width: 640px) {
  .timeline { grid-template-columns: 1fr; gap: 0; }
  .tstep { display: grid; grid-template-columns: 44px minmax(0, 1fr); column-gap: 16px;
    background: none; border: 0; border-radius: 0; padding: 0 0 26px; box-shadow: none; }
  .tstep:last-child { padding-bottom: 0; }
  .tstep .tnode { grid-row: 1 / span 2; width: 44px; height: 44px; border-radius: 14px;
    font-size: 1.28rem; margin: 0; }
  .tstep h3 { grid-column: 2; margin: 10px 0 6px; }
  .tstep p { grid-column: 2; }
  .tstep:not(:last-child)::after { content: ""; position: absolute; left: 21px; top: 52px; bottom: 6px;
    width: 2px; border-radius: 2px;
    background: linear-gradient(to bottom, color-mix(in srgb, var(--c-primary) 70%, transparent), color-mix(in srgb, var(--c-primary) 12%, transparent)); }
  /* Hover-nosto on korttia varten; ilman korttia nousu näyttäisi hyppäykseltä. */
  .tstep:hover { transform: none; box-shadow: none; }
}

/* ---- Palvelukortit tiiviimmiksi puhelimessa ----
   Ikoni otsikon vasemmalle puolelle samalle riville: säästää ikonin oman
   rivin (46 px + 18 px väli) jokaisesta kortista. Kuvaus ja linkki jatkuvat
   koko leveydeltä. Rakenne (.ico, h3, p, .tlink) ei muutu. */
/* 🛑 Vain ikonillisille korteille (QA 30.9.2026). Ensimmäinen versio koski
   kaikkia .tile-kortteja, jolloin ikonittomissa korteissa (etusivun Oppaat,
   paikkakuntasivut, "Katso myös" -kortit: 37 korttia 11 sivulla) h3 putosi
   40 px:n ikonisarakkeeseen ja katkesi kirjain kerrallaan 8-12 riville. */
@media (max-width: 640px) {
  .tile:has(.ico) { display: grid; grid-template-columns: 40px minmax(0, 1fr); column-gap: 14px;
    align-items: center; padding: 22px; }
  .tile .ico { width: 40px; height: 40px; border-radius: 11px; margin: 0; }
  .tile:has(.ico) h3 { margin: 0; }
  .tile:has(.ico) > p, .tile:has(.ico) > .tlink { grid-column: 1 / -1; }
  .tile:has(.ico) > p { margin-top: 12px; }
  .tile:has(.ico) > .tlink { justify-self: start; }
}

/* ============ QA-KIERROS 2, 30.9.2026: parannukset ============ */

/* ---- Työnäytteet puhelimessa: sivusto näkyviin ----
   ≤760 px:llä kortti on noin 350 × 225 px, ja kuvan päällä oleva teksti (otsikko,
   kuvaus, osoite) vei lähes koko kuvan: näytetystä sivustosta näkyi vain navin
   kaista. 🛑 Teksti pysyy KUVAN PÄÄLLÄ (`pohjat/_ohjeet.md` "Ei irrallisia
   kuvia": teksti kuvan päällä tai vieressä, ei alla, ks. myös .nayte-kommentti
   ylempänä), mutta tekstilohko nousee vain kuvan alaosan päälle ja jatkuu sen
   alle samalla verholla. Kuvan ja tekstin väliin ei synny kaistaa, koska
   verhon läpinäkyvä yläreuna on kuvan päällä. Verhon tumma osa alkaa ennen
   tekstiä (padding-top > läpinäkyvä kaista), joten kontrastilaskelma pätee. */
@media (max-width: 760px) {
  .nayte > .nayte-teksti { position: relative; inset: auto; margin-top: -84px; }
}

/* ---- "Näytä kaikki" -painike (shared.js, data-nayta-lisaa) ----
   Luokka lisätään vain JS:llä ja vain puhelimessa, joten ilman JS:ää kaikki
   kohteet näkyvät. Piilotetut kohteet pysyvät DOMissa (FAQ-schema, haku). */
.nl-piilossa { display: none !important; }
.nayta-lisaa { display: flex; justify-content: center; margin-top: 20px; }
.nayta-lisaa[hidden] { display: none; }

/* ---- Kartan varasisältö (yhteystiedot) ----
   Kartta ladataan vasta sivun load-tapahtuman jälkeen (shared.js). Siihen asti,
   ja jos upotus estetään, laatikossa näkyy osoite ja linkki karttaan. Varasisältö
   on iframen alla: tyhjä iframe on läpinäkyvä ja ladattu kartta peittää sen.
   Iframe ei ota klikkauksia ennen latausta, jotta linkki toimii. */
.map { position: relative; }
.map-varalla { position: absolute; inset: 0; display: grid; place-content: center; gap: 10px;
  padding: 20px; text-align: center; color: var(--c-text-2); }
.map-varalla p { margin: 0; }
.map-varalla a { justify-self: center; display: inline-flex; align-items: center; min-height: 44px;
  font-weight: 600; color: var(--c-accent3); }
.map iframe { position: relative; z-index: 1; }
.map iframe:not(.ladattu) { pointer-events: none; }
/* Iframen fokusrengas leikkautui .map-laatikon overflow: hidden -rajaukseen
   (QA 30.9.2026): rengas piirretään laatikkoon. */
.map:focus-within { outline: 2px solid var(--c-accent3); outline-offset: 3px; }
/* Ladatun kartan alle jäänyt varalinkki pysyi tab-järjestyksessä näkymättömänä
   (QA 30.9.2026). Latauksen jälkeen varasisältö poistuu kokonaan. */
.map:has(iframe.ladattu) .map-varalla { visibility: hidden; }

/* ======== JAKSOTETTU PIDEMPI TEKSTI (Valtteri 2.10.2026) ========
   Palvelusivujen alas siirretty pidempi teksti lohkoina koko säiliön
   leveydellä, kaksi saraketta. Pariton viimeinen lohko ja .lohko--leve
   (taulukot, kuvalliset lohkot) täyttävät koko rivin. */
.lohkot { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--gap); align-items: start; }
/* Koko rivin lohko merkitään HTML:ssä (.lohko--leve), koska :nth-child ei
   osaa laskea parittomuutta leveän lohkon jälkeen (2.10.2026). */
.lohkot > .lohko--leve { grid-column: 1 / -1; }
.lohko { background: var(--c-surface); border: 1px solid var(--c-border); border-radius: var(--r-lg);
  padding: clamp(22px, 3vw, 34px); min-width: 0; }
.lohko > h3 { color: var(--c-heading); margin: 0 0 14px; }
.lohko > p { color: var(--c-text); line-height: 1.72; margin: 0 0 14px; }
.lohko > p:last-child, .lohko > :last-child { margin-bottom: 0; }
.lohko > ul, .lohko > ol { padding-left: 1.25em; margin: 0 0 14px; line-height: 1.7; display: grid; gap: 6px; }
.lohko > .tbl-wrap { margin: 6px 0 14px; }
.lohko strong { color: var(--c-heading); font-weight: 650; }
.lohko a:not(.btn) { text-decoration: underline; text-underline-offset: 3px; }
@media (max-width: 860px) { .lohkot { grid-template-columns: 1fr; } }

.hero--sub:not(:has(.hero-sub-grid)) h1 { text-wrap: pretty; }
/* Artikkelin ensimmäinen laatikko ("Lyhyt vastaus") ei tarvitse yläväliä heron jälkeen. */
.wrap.prose > .callout:first-child { margin-top: 0; }
/* Neljän kortin vahvuusrivi (etusivu 2.10.2026). */
.values--4 { grid-template-columns: repeat(4, 1fr); }
@media (max-width: 980px) { .values--4 { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 560px) { .values--4 { grid-template-columns: 1fr; } }
/* Kotisivujen peruspaketti -kortti etusivulla (2.10.2026): yksi hinta ja
   kuusi lyhyttä kohtaa, korvaa epäselvän kahden hintarivin dash-kortin. */
.paketti { background: linear-gradient(170deg, color-mix(in srgb, var(--c-primary) 16%, var(--c-surface)), var(--c-surface));
  border: 1px solid color-mix(in srgb, var(--c-primary) 45%, var(--c-border)); border-radius: var(--r-lg);
  padding: clamp(24px, 3.4vw, 36px); box-shadow: var(--glow-primary); }
.paketti-nimi { margin: 0; font-family: var(--font-display); font-weight: 700; font-size: 1.2rem; color: var(--c-heading); }
.paketti-hinta { margin: 10px 0 2px; font-family: var(--font-display); font-weight: 800; font-size: clamp(2.4rem, 4vw, 3rem); line-height: 1; color: var(--c-heading); letter-spacing: -.03em; }
.paketti-hinta .alk { font-family: var(--font-body); font-size: .85rem; font-weight: 700; letter-spacing: .12em; text-transform: uppercase; color: var(--c-accent3); vertical-align: middle; margin-right: 6px; }
.paketti-lisa { margin: 8px 0 22px; color: var(--c-text-2); font-size: .95rem; }
.paketti-lista { list-style: none; margin: 0; padding: 20px 0 0; border-top: 1px solid var(--c-border);
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px 18px; }
.paketti-lista li { display: flex; gap: 9px; align-items: flex-start; color: var(--c-heading); font-size: .96rem; line-height: 1.4; }
.paketti-lista svg { flex: none; color: var(--c-accent3); margin-top: 2px; }
@media (max-width: 480px) { .paketti-lista { grid-template-columns: 1fr; } }
/* Evästeasetukset-linkki palkin ollessa jo auki: lyhyt välähdys (2.10.2026). */
@keyframes evaste-huomio { 0%, 100% { box-shadow: none; } 40% { box-shadow: 0 0 0 3px var(--c-accent3), 0 -10px 40px color-mix(in srgb, var(--c-accent3) 35%, transparent); } }
.evastepalkki.huomio { animation: evaste-huomio 1.1s ease 2; }
@media (prefers-reduced-motion: reduce) { .evastepalkki.huomio { animation: none; outline: 3px solid var(--c-accent3); } }
/* Painikkeet lukurivin alla (meista.html): ilman väliä napit osuivat lukuriviin (2.10.2026). */
.hero-metrics + .hero-cta { margin-top: 30px; }
/* ======== YHTEYSTIEDOT-SIVU (2.10.2026) ========
   Yhteystiedot ja kartta rinnakkain, henkilöt alla. Kartta täyttää
   tietosarakkeen korkeuden eikä jää kapeaksi nauhaksi. */
.yht-grid { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1.25fr); gap: var(--gap); align-items: stretch; }
.yht-tiedot { display: grid; gap: 14px; align-content: start; }
.yht-rivi { display: flex; gap: 16px; align-items: flex-start; background: var(--c-surface); border: 1px solid var(--c-border);
  border-radius: var(--r-lg); padding: 20px 22px; }
.yht-rivi > svg { flex: none; color: var(--c-accent3); margin-top: 2px; }
.yht-rivi b { display: block; font-family: var(--font-display); font-size: 1.1rem; color: var(--c-heading); margin-bottom: 6px; }
.yht-rivi a, .yht-rivi span { display: block; color: var(--c-heading); font-size: 1.02rem; line-height: 1.5; }
.yht-rivi a { padding-block: 3px; }
.yht-rivi .yht-pieni, .yht-rivi .yht-pieni a { display: inline; color: var(--c-text-2); font-size: .9rem; }
.yht-rivi .yht-pieni { display: block; margin-top: 4px; }
@media (hover: hover) { .yht-rivi a:hover { color: var(--c-accent3); } }
.yht-kartta { margin: 0; min-height: 320px; }
.yht-kartta iframe { height: 100%; min-height: 320px; }
.yht-grid + .person-cards { margin-top: calc(var(--gap) * 1.5); }
@media (max-width: 860px) { .yht-grid { grid-template-columns: 1fr; } .yht-kartta, .yht-kartta iframe { min-height: 260px; } }
/* Yhteystiedot rivinä ilman karttaa (2.10.2026): kolme korttia rinnakkain, kartta omana osionaan sivun lopussa. */
.yht-grid--rivi { grid-template-columns: 1fr; }
.yht-grid--rivi .yht-tiedot { grid-template-columns: repeat(3, minmax(0, 1fr)); }
@media (max-width: 860px) { .yht-grid--rivi .yht-tiedot { grid-template-columns: 1fr; } }
.yht-kartta--leve, .yht-kartta--leve iframe { min-height: 360px; }
@media (max-width: 860px) { .yht-kartta--leve, .yht-kartta--leve iframe { min-height: 260px; } }
/* Otsikon ja painikkeiden väli, kun nauhassa ei ole kappaletta (2.10.2026). */
.cta-band h2 + .hero-cta { margin-top: 26px; }
/* Lomakemoduuli heti heron jälkeen (yhteystiedot): hieman enemmän ilmaa yläpuolelle (Valtteri 2.10.2026). */
.hero--sub:not(:has(.hero-sub-grid)) + #yhteys { padding-top: clamp(36px, 4.5vw, 60px); }
/* Yhteystiedot 2.10.2026: ylhäällä Myynti + Asiakastuki, henkilöiden alla Toimisto + kartta. */
.yht-grid--kaksi .yht-tiedot { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.yht-grid--toimisto { margin-top: calc(var(--gap) * 1.5); grid-template-columns: minmax(0, 1fr) minmax(0, 2fr); }
.person-cards + .yht-grid--toimisto { margin-top: calc(var(--gap) * 1.5); }
@media (max-width: 860px) { .yht-grid--kaksi .yht-tiedot, .yht-grid--toimisto { grid-template-columns: 1fr; } }

/* Lomakkeen tekstivärit ovat lomake/lomake.css:n lopussa (ladataan tämän jälkeen). */
/* Yhteystiedot-sivu tiiviimmäksi (Valtteri 2.10.2026: "liikaa väliä eri osioiden välillä").
   Yleinen --section-y on 64–120 px per reuna, tällä sivulla 32–56 px. */
.sivu-yhteystiedot .section { padding-block: clamp(32px, 4.2vw, 56px); }
.sivu-yhteystiedot .section--snug { padding-block: clamp(24px, 3vw, 40px); }
.sivu-yhteystiedot .hero--sub + #yhteys { padding-top: clamp(28px, 3.4vw, 44px); }

/* Etusivu ja palvelusivut (.sivu-tiivis) hieman tiiviimmiksi (Valtteri 2.10.2026, palvelusivut samoin: "osa etusivun osioista voisiko olla
   lähempänä toisiaan ... ei liian lähelle mutta ihan vähän"). Mitattu ennen: 120 px per
   reuna (240 px sisältöjen välissä), osion lopun kappaleesta +16 px, ja saman taustan
   osioiden välissä lopun kehotteeseen 224 px. */
.sivu-etusivu, .sivu-tiivis { --section-y: clamp(56px, 7.5vw, 96px); }
.sivu-etusivu .section > .wrap > p:last-child, .sivu-tiivis .section > .wrap > p:last-child { margin-bottom: 0; }
.sivu-etusivu .section:not(.section--tint):not(.section--hehku) + .section--snug:last-child,
.sivu-tiivis .section:not(.section--tint):not(.section--hehku) + .section--snug:last-child { padding-top: clamp(8px, 1.5vw, 16px); }
/* Lukurivi Miksi Foorly -osiossa (siirretty herosta 2.10.2026). */
.hero-metrics--osio { justify-content: center; margin: clamp(32px, 4vw, 48px) auto 0; text-align: center; }
.lukurivi { position: relative; z-index: 1; padding-block: clamp(28px, 3.4vw, 44px) 0; } /* heron sisällä, jotta tausta on sama (Valtteri 2.10.2026) */
.lukurivi .hero-metrics--osio { margin-top: 0; border-top: 0; padding-top: 0; }

/* Välikehote palvelusivujen keskivaiheilla (Valtteri 2.10.2026). Osion sisällä, ei omana osiona,
   jotta taustojen vuorottelu säilyy. */
.valikehote { margin-top: clamp(32px, 4vw, 48px); display: flex; flex-wrap: wrap; align-items: center; justify-content: center;
  gap: 14px 22px; padding: clamp(18px, 2.4vw, 26px) clamp(18px, 3vw, 32px); text-align: center;
  border: 1px solid var(--c-border); border-radius: var(--r-lg); background: linear-gradient(135deg, var(--c-surface), var(--c-bg-2)); }
.valikehote p { margin: 0; color: var(--c-heading); font-weight: 600; font-size: 1.05rem; }
.valikehote-napit { display: flex; flex-wrap: wrap; gap: 12px; justify-content: center; }

/* Isot otsikot: rivit tasan, ei yksittäistä sanaa toiselle riville (Valtteri 3.10.2026). */
main h1 { text-wrap: balance; }

/* ===================== KONSEPTIDEMO 5.10.2026 =====================
   Etusivun palvelukortit (.pricing/.tier) saavat napin alle tekstilinkin
   kokonaisuuden omalle sivulle. Nappi on kortin alareunassa (margin-top: auto),
   linkki sen alla keskitettynä. */
.tier .tlink { display: inline-flex; align-items: center; gap: 7px; margin-top: 14px; align-self: center;
  font-family: var(--font-body); font-size: .9rem; font-weight: 600; letter-spacing: .01em; color: var(--c-accent3); }
@media (hover: hover) { .tier .tlink:hover { color: var(--c-heading); } }
/* Kysy tarjous -hinta ilman alkaen-sanaa: sama korkeus kuin hintariveillä. */
.tier .price:not(:has(.alkaen)) { white-space: nowrap; }

/* Hero suuremmaksi pöytäkoneella ja ilmaa ensimmäisten osioiden väliin (Miikka 5.10.2026:
   "jottei näytöllä ole niin paljon tekstiä samaan aikaan"). Vain etusivu, vain ≥ 1024 px:
   hero ja lukurivi täyttävät ensimmäisen ruudun, logonauha alkaa taitteen kohdalta ja
   palvelukortit tulevat vasta vierittämällä. */
@media (min-width: 1024px) {
  .sivu-etusivu .hero { padding-top: calc(76px + clamp(84px, 9vw, 128px)); padding-bottom: clamp(76px, 7.5vw, 108px); }
  .sivu-etusivu .lukurivi { padding-top: clamp(60px, 6vw, 92px); }
  .sivu-etusivu .marquee { padding-block: 30px; }
  .sivu-etusivu .marquee + .section { padding-top: clamp(120px, 12vw, 168px); }
}

/* Mobiilioptimointi 6.10.2026: pitkä katkeamaton merkkijono (URL, koodi, ?text=…) saa
   katketa, ettei se levitä sivua vaakasuunnassa. Löytyi artikkelista
   chat-whatsapp-vai-soittopainike 360 px:ssä (393 px leveä lista). `anywhere` vaikuttaa
   myös min-content-leveyteen, joten se toimii flex-listojen sisällä, toisin kuin break-word. */
main :is(p, li, dd, blockquote, figcaption) { overflow-wrap: anywhere; }

/* ===================== ETUSIVU, KONVERSIOKIERROS 6.10.2026 =====================
   Valtterin hyväksymät muutokset tutkimusnäytön perusteella (kehitysideat.md). */

/* Hero-kuva: oikea asiakassivusto kuvituksen tilalla. Kuvakaappaus tarvitsee kehyksen
   ja varjon, läpinäkyvän kuvituksen drop-shadow ei sovi suorakaiteelle. */
.hero-kuva--nayte img { border-radius: var(--r-lg); border: 1px solid var(--c-border);
  filter: none; box-shadow: var(--shadow-lg), var(--glow-primary); }

/* Palvelukorttien sisältölista: oletuksena auki (ilman JS:ää kaikki näkyy).
   index.html:n skripti sulkee listat puhelimella, jolloin lomake nousee noin
   tuhat pikseliä ylemmäs. Työpöydällä summary piilotetaan ja lista on aina auki. */
.tier-sis { margin: 0 0 24px; }
.tier-sis > ul { margin-bottom: 0; }
.tier-sis > summary { display: none; }
@media (max-width: 640px) {
  .tier-sis > summary { display: flex; align-items: center; gap: 8px; cursor: pointer;
    list-style: none; padding-block: 11px; font-size: .92rem; font-weight: 600;
    color: var(--c-accent3); }
  .tier-sis > summary::-webkit-details-marker { display: none; }
  .tier-sis > summary::after { content: ""; width: 8px; height: 8px; margin-top: -3px;
    border-right: 2px solid currentColor; border-bottom: 2px solid currentColor;
    transform: rotate(45deg); transition: transform .2s; }
  .tier-sis[open] > summary::after { transform: rotate(225deg); margin-top: 3px; }
  .tier-sis[open] > ul { margin-top: 6px; }
  .tier-sis { margin-bottom: 18px; }
  .tier .pdesc:has(+ .tier-sis) { margin-bottom: 4px; }
}

/* Logonauha pysähtyy hoverissa, näppäimistöfokuksessa (tabindex="0"), napautuksella ja
   Enter/välilyönnillä (WCAG 2.2.2).
   prefers-reduced-motion pysäyttää sen jo yleissäännöllä (animation: none). */
@media (hover: hover) { .marquee:hover .marquee-row { animation-play-state: paused; } }
.marquee:focus-within .marquee-row, .marquee.pysaytetty .marquee-row { animation-play-state: paused; }

/* Hero ei täytä koko puhelimen ruutua: logonauhan yläreuna näkyy 390 × 844:llä. */
@media (max-width: 560px) {
  .hero:has(> .hero-grid) { padding-block: calc(76px + 24px) 24px; }
  .hero-grid { gap: 20px; }
  .hero:has(> .hero-grid) .hero-metrics { margin-top: 18px; padding-top: 14px; }
  .hero-kuva--nayte img { max-width: 280px; }
  .hero:has(> .hero-grid) .hero-sub { margin-bottom: 22px; }
}

/* Etusivun H1: "Google-mainonta" ei saa katketa yhdysmerkistä (.nowrap), joten sanan on
   mahduttava heron palstaan. 64 px:llä se oli 24 px palstaa leveämpi työpöydällä ja 39 px
   320 px:llä. Mitattu 6.10.2026: 3 riviä kaikilla leveyksillä 320–1440 px. */
.hero:has(> .hero-grid) h1 { font-size: clamp(2.3rem, 4.5vw, 3.55rem); }
@media (max-width: 360px) { .hero:has(> .hero-grid) h1 { font-size: 1.95rem; } }

/* Etusivun osiovälit tiiviimmiksi (Valtteri 6.10.2026: näkyvä väli osioiden tekstien välillä oli
   työpöydällä 150–300 px, puhelimella 90–205 px). Ei konversiotutkimusta osioväleistä,
   ulkoasupäätös. Koskee vain etusivua (.sivu-etusivu). */
.sivu-etusivu { --section-y: clamp(40px, 5vw, 64px); }
.sivu-etusivu .section--snug { padding-block: clamp(32px, 4.5vw, 56px); }
.sivu-etusivu .section .sec-head { margin-bottom: clamp(24px, 3.5vw, 36px); }

/* Lomakeosioiden johdantorivi yhdelle riville työpöydällä (Valtteri 6.10.2026):
   "Vastaamme arkisin, yleensä saman tai seuraavan arkipäivän aikana. Yhteydenotto ei sido
   mihinkään." on 95 merkkiä, palstan 62ch katkaisi sen kahdelle riville. */
.section--lomake .sec-head--center .lead { max-width: 60rem; }

/* ===================== KONSEPTIDEMO 6.10.2026, KIERROS 4: TAUSTAKUVAHERO =====================
   Miikka: heron taustalla valokuva (BreakpointHubin Keilaniemi esimerkkinä), heron nykyinen
   tausta (hehkut) kuvan päällä peittävyydellä 70–80 %, otsikko keskitetty, koska oikean puolen
   kuvaelementti poistui, ja 5.10. lisätty väljyys ylä- ja alapuolelle takaisin.
   Peittävyys: kuva piirretään hehkujen PÄÄLLE läpinäkyvyydellä 1 − peitto, mikä on sama asia kuin
   tausta kuvan päällä peitolla (0,75 × tausta + 0,25 × kuva). Säädetään yhdestä muuttujasta. */
.hero--kuva { --hero-kuva-peitto: .8; }   /* 75 → 80 % (Miikka 6.10.2026, kierros 5) */
.hero-tausta { position: absolute; inset: 0; z-index: 0; overflow: hidden; pointer-events: none; }
.hero-tausta img { display: block; width: 100%; height: 100%; object-fit: cover; object-position: center 62%;
  opacity: calc(1 - var(--hero-kuva-peitto)); }
/* Alareuna häivytetään sivun taustaan, ettei kuvan reuna näy logonauhan yläpuolella viivana. */
.hero-tausta::after { content: ""; position: absolute; inset: auto 0 0 0; height: 28%;
  background: linear-gradient(to bottom, transparent, var(--c-bg)); }
.hero--kuva .hero-keski { display: grid; justify-items: center; text-align: center; }
.hero--kuva .hero-copy { max-width: 900px; }
.hero--kuva h1 { margin-bottom: 22px; }
.hero--kuva .hero-sub { max-width: 56ch; margin-inline: auto; }
.hero--kuva .hero-cta { justify-content: center; }
/* Väljyys: hero ja lukurivi täyttävät ensimmäisen ruudun työpöydällä (5.10. tavoite). 6.10.
   tiivistys koski vain kuvallista .hero-grid-heroa, joten puhelimella pätee heron perustäyte. */
@media (min-width: 1024px) {
  .sivu-etusivu .hero--kuva { padding-top: calc(76px + clamp(104px, 11vw, 168px)); padding-bottom: clamp(88px, 9vw, 128px); }
}
@media (max-width: 560px) {
  .hero--kuva { padding-top: calc(76px + 48px); padding-bottom: 44px; }
  .hero--kuva .hero-sub { margin-bottom: 26px; }
}

/* ===================== KONSEPTIDEMO 6.10.2026, KIERROS 5 =====================
   Miikka: hero vähintään 75 vh työpöydällä, väli logonauhan ja Palvelut-osion väliin,
   Kysy tarjous alkaen-sanan värillä. */
@media (min-width: 1024px) {
  /* Sisältö ja lukurivi keskitetään pystysuunnassa, kun 75 vh on sisältöä korkeampi. */
  .sivu-etusivu .hero--kuva { min-height: 75vh; display: flex; flex-direction: column; justify-content: center; }
}
/* 5.10. välisääntö `.marquee + .section` lakkasi osumasta 6.10., kun logonauhan perään tuli
   pysäytysskripti (<script> on elementtisisar, joten + ei osu). Sama väli tunnisteella ja
   pienempi väli puhelimelle. */
.sivu-etusivu #palvelut { padding-top: clamp(64px, 9vw, 96px); }
@media (min-width: 1024px) { .sivu-etusivu #palvelut { padding-top: clamp(120px, 12vw, 168px); } }
/* Kysy tarjous samalla vaaleansinisellä kuin alkaen-sana (Miikka 6.10.2026). */
.tier .price:not(:has(.alkaen)) { color: var(--c-accent3); }

/* ===================== KONSEPTIDEMO 6.10.2026, KIERROS 10 =====================
   1) Hero-H1 "Kotisivut, / näkyvyys ja kasvu" on pakotettu kahdelle riville (<br>). Alarivi (17 merkkiä)
      ei mahdu 390 px:n palstaan h1:n perusasteikolla (2,3 rem), joten puhelimella fontti on pienempi.
      Mitattu renderistä 390, 360 ja 320 px: kaksi riviä. */
@media (max-width: 560px) {
  .hero--kuva h1 { font-size: 2.05rem; }
}
@media (max-width: 380px) {
  .hero--kuva h1 { font-size: 1.85rem; }
}
/* 2) Osion taustakuva (kierros 11, Miikka 6.10.2026): kuvanauha ei ole oma lohkonsa vaan Mistä aloitetaan
      -osion taustakuva, ja häivytys jatkuu osion loppuun eli Asiakkaiden arviot -osioon asti. Kuva vaimennettuna,
      yläreuna häivytetään edellisen osion taustaan ja alaosa seuraavan osion taustaan, jotta saumaa ei näy.
      Värit muuttujista: etusivulla ylhäällä sivun tausta ja alhaalla tint (arviot), Kotisivut-sivulla
      päinvastoin (kierroksesta 13 alkaen edellä Näin projekti etenee, joka on tint, ja perässä Mitä
      sisältyy, joka ei ole; body-luokka .sivu-kotisivut). Kerros on absoluuttinen ja
      osion kokoinen, joten osio ei tarvitse overflow: hiddeniä eikä lomakekortin hehku leikkaudu. Otsikot ja
      lomake ovat kerroksen päällä. Kierroksella 10 sama kuva oli erillinen nauha osioiden välissä.
      Peitto .55 → .78 ja häivytykset 32/58 → 42/62 % (Miikka 6.10.2026: taustasta riittää, että siitä näkyy
      hämärästi Aurajoen rantaa; osio ja tekstit pysyvät selkeinä). */
.section--kuvatausta { position: relative; }
.section--kuvatausta > .wrap { position: relative; z-index: 1; }
.osio-tausta { --tausta-yla: var(--c-bg); --tausta-ala: var(--c-bg-2); --tausta-kohta: 50% 42%; --tausta-peitto: .78;
  position: absolute; inset: 0; z-index: 0; overflow: hidden; pointer-events: none; }
.osio-tausta img { display: block; width: 100%; height: 100%; object-fit: cover; object-position: var(--tausta-kohta);
  opacity: calc(1 - var(--tausta-peitto)); }
.osio-tausta::before, .osio-tausta::after { content: ""; position: absolute; left: 0; right: 0; }
.osio-tausta::before { top: 0; height: 42%; background: linear-gradient(to bottom, var(--tausta-yla), transparent); }
.osio-tausta::after { bottom: 0; height: 62%; background: linear-gradient(to top, var(--tausta-ala), transparent); }
.sivu-kotisivut .osio-tausta { --tausta-yla: var(--c-bg-2); --tausta-ala: var(--c-bg); }

/* ===================== KONSEPTIDEMO 6.10.2026, KIERROS 13: KOTISIVUT-SIVU =====================
   Miikka: (1) heroon BreakpointHubin kuvakaappaus kuvituksen tilalle, (2) osioiden väliin lisää tilaa,
   erityisesti sivun alkuun, (3) järjestys hero · vahvuudet · logonauha · hinnat · arviot · työnäytteet,
   loppukehote pois. Koskee vain Kotisivut-sivua (body .sivu-kotisivut). */
/* 1) Kuvakaappaus tarvitsee kehyksen ja varjon kuten etusivun .hero-kuva--nayte; kuvituksen drop-shadow
      ja 380/420 px:n korkeusraja eivät sovi suorakaiteelle. Tekstipalsta 1.35/.65 → 1.2/.8 vain leveällä
      näytöllä (puhelimella yksi palsta kuten muilla alasivuilla), koska tämän sivun H1 on lyhyt. */
@media (min-width: 861px) { .sivu-kotisivut .hero-sub-grid { grid-template-columns: 1.2fr .8fr; } }
.hero-sub-kuva--nayte img { max-height: none; border-radius: var(--r-lg); border: 1px solid var(--c-border);
  filter: none; box-shadow: var(--shadow-lg), var(--glow-primary); }
@media (max-width: 860px) { .hero-sub-kuva--nayte img { max-width: 420px; } }
/* 2) Osiovälit: .sivu-tiivis antaa 56–96 px per reuna, tällä sivulla 72–120 px. Heron alle ja logonauhan
      ympärille vielä enemmän, koska sivun alku oli tiivein (hero → vahvuudet → nauha → hinnat). */
/* 🛑 Kumottu 7.10.2026 (Valtteri: "ei liikaa väliä osioiden välillä, vertaa etusivu vs muut sivut"):
   Kotisivut-sivu käyttää samaa osioväliä kuin etusivu ja muut palvelusivut, ks. OSIOVÄLIT 7.10.2026
   tiedoston lopussa. Ennen: --section-y 72–120 px, heron alla 64–104 px, logonauhan jälkeen 96–152 px. */

/* ===================== KONSEPTIDEMO 6.10.2026, KIERROS 14 =====================
   Miikka: (1) Kasvu-sivulle valokuva tekstin viereen (sama kehys kuin Kotisivut-sivun kuvakaappauksella),
   (2) Referenssit-sivun heroon tilaa ylä- ja alapuolelle, (3) Yhteystiedot-sivulla lomake heron sisällä
   otsikon vieressä ja tuomiokirkko osion taustakuvana. */
/* 1) Kasvu: H1 on 41 merkkiä, joten tekstipalsta 1.3/.7 (Kotisivut-sivulla 1.2/.8), kuva 390–460 px. */
/* Kierros 15: Kasvu-sivun palstasääntö 1.4fr .6fr pystykuvalle; poistettu kierroksella 18, kun hero muuttui taustakuvaheroksi. */
/* 2) Referenssit: tekstihero oli 20–32 px navin alla ja 24–36 px ennen seuraavaa osiota (yleinen tekstiheron
      tiivistys), ja seuraava osio alkoi 8–16 px:n päästä. Tällä sivulla väljempi ylä ja ala sekä otsikon
      ja ingressin välit. */
.sivu-referenssit .hero--sub:not(:has(.hero-sub-grid)) { padding-block: calc(76px + clamp(64px, 8vw, 112px)) clamp(56px, 7vw, 96px); }
.sivu-referenssit .hero--sub:not(:has(.hero-sub-grid)) + .section { padding-top: clamp(64px, 8vw, 112px); }
.sivu-referenssit .hero--sub h1 { margin-bottom: 24px; }
.sivu-referenssit .hero--sub .hero-sub { margin-bottom: 36px; }
/* 3) Yhteystiedot: hero on lomakeosio. Taustakuva samalla peittomallilla kuin etusivun taustakuvahero
      (.hero-tausta, kuva hehkujen päällä läpinäkyvyydellä 1 − peitto); alareuna häivytetään seuraavan osion
      tint-taustaan. Teksti ja lomakekortti rinnakkain puoliksi vähintään 861 px:llä, puhelimella allekkain.
      Lomakekortti saa saman korostetun reunan ja hehkun kuin .section--lomake-osioissa. */
.hero--lomake { --hero-kuva-peitto: .76; }
.hero--lomake .hero-tausta img { object-position: center 42%; }
.hero--lomake .hero-tausta::after { background: linear-gradient(to bottom, transparent, var(--c-bg-2)); }
.hero-lomake-grid { grid-template-columns: 1fr; gap: clamp(28px, 4vw, 56px); }
@media (min-width: 861px) { .hero-lomake-grid { grid-template-columns: 1fr 1fr; } }
.hero--lomake .hero-sub { margin-bottom: 26px; }
.hero--lomake .mvf { border-color: color-mix(in srgb, var(--c-primary) 45%, var(--c-border));
  box-shadow: var(--shadow-lg), var(--glow-primary); }
.sivu-yhteystiedot .hero--lomake { padding-bottom: clamp(56px, 7vw, 88px); }

/* ===================== KONSEPTIDEMO 6.10.2026, KIERROS 15: KASVU-SIVUN PYSTYKUVA =====================
   Miikka: Kasvu-sivun kuva pystymalliseksi, Turku-aiheiseksi. Kuva rajataan 3:4:ään ja sen korkeus rajataan,
   jotta hero ei kasva tekstipalstaa paljon korkeammaksi; kuva täyttää palstan leveyden ja rajautuu object-fitillä
   (rajauskohta muuttujasta --pysty-kohta). Kehys ja varjo tulevat .hero-sub-kuva--nayte:stä. Puhelimella kuva on
   enintään 360 × 480 px, ettei se vie koko ruutua ennen sisältöä. */
.hero-sub-kuva--pysty img { aspect-ratio: 3 / 4; object-fit: cover; object-position: var(--pysty-kohta, 50% 50%);
  max-height: min(600px, 72vh); }
@media (max-width: 860px) { .hero-sub-kuva--pysty img { max-width: 360px; max-height: 480px; } }

/* ===================== KONSEPTIDEMO 6.10.2026, KIERROS 18: KASVU-SIVUN TAUSTAKUVAHERO =====================
   Miikka: Föri taustakuvana kuten etusivulla ja Yhteystiedot-sivulla, lautta tekstin oikealla puolella ja kuva
   jatkuu tekstin takana vasemmalla. Sama .hero-tausta-malli (kuva hehkujen päällä läpinäkyvyydellä 1 − peitto),
   mutta peitto on kevyempi (42 %) ja vasen puoli tummennetaan erikseen liukuvärillä tekstin alta, jotta lautta
   näkyy oikealla ja teksti pysyy luettavana. Kuva kohdistetaan oikealle (lautta) ja hieman alas (vesi).
   Tekstihero (.hero--sub ilman .hero-sub-grid) saisi muuten tiiviin täytteen ja seuraava osio alkaisi 8–16 px:n
   päästä; taustakuvan vuoksi täyte on suurempi ja hero vähintään 640 px työpöydällä. Puhelimella peitto kuten
   muilla taustakuvaheroilla ja tummennus ylhäältä, koska teksti on kuvan päällä koko leveydeltä.
   Kierros 17:n 4:5-kuvasuhde (.sivu-kasvu .hero-sub-kuva--pysty img) poistettu tarpeettomana. */
.hero--fori { --hero-kuva-peitto: .9; }   /* 42 → 80 → 90 % (Miikka 6.10.2026, kierros 18 jatko): teksti erottuu taloista paremmin */
.hero--fori .hero-tausta img { object-position: 100% 58%; }   /* oikea reuna: lautta kokonaan näkyviin leveällä näytöllä */
.hero--fori .hero-tausta::before { content: ""; position: absolute; inset: 0;
  background: linear-gradient(to right, color-mix(in srgb, var(--c-bg) 60%, transparent) 0%, color-mix(in srgb, var(--c-bg) 40%, transparent) 30%,
    color-mix(in srgb, var(--c-bg) 15%, transparent) 55%, transparent 80%); }  /* kevennetty, kun peitto nousi 80 %:iin */
.hero--fori .hero-tausta::after { background: linear-gradient(to bottom, transparent, var(--c-bg-2)); }
.sivu-kasvu .hero--fori { padding-block: calc(76px + clamp(48px, 6vw, 88px)) clamp(56px, 7vw, 88px); }
/* Flex-säiliössä .wrap kutistuisi sisältönsä levyiseksi ja keskittyisi (margin-inline: auto), joten leveys pakotetaan. */
.sivu-kasvu .hero--fori > .wrap { width: 100%; }
.sivu-kasvu .hero--fori + .section { padding-top: var(--section-y); }
.sivu-kasvu .hero--fori .hero-copy { max-width: 640px; }
@media (min-width: 1024px) {
  .sivu-kasvu .hero--fori { min-height: 640px; display: flex; flex-direction: column; justify-content: center; }
}
@media (max-width: 860px) {
  .hero--fori { --hero-kuva-peitto: .9; }
  .hero--fori .hero-tausta img { object-position: 70% 50%; }
  .hero--fori .hero-tausta::before { background: linear-gradient(to bottom, color-mix(in srgb, var(--c-bg) 60%, transparent), transparent 70%); }
}

/* ============ 7.10.2026 (Valtteri): etusivun neljä palvelukorttia ============
   Mainonta ja hakukoneoptimointi/GEO erotettiin omiksi korteikseen, joten korttia on neljä.
   Työpöydällä 2 × 2 (neljä rinnakkain kavensi hinnat ja listat liikaa); alle 981 px
   yksi palsta kuten ennen (RESPONSIIVISUUS, .pricing). */
@media (min-width: 981px) {
  .pricing--4 { grid-template-columns: repeat(2, 1fr); max-width: 920px; margin-inline: auto; }
}

/* ===================== OSIOVÄLIT 7.10.2026 (Valtteri) =====================
   Palvelusivut samaan osioväliin kuin etusivu (.sivu-etusivu 40–64 px per reuna, päätös 34).
   Mitattu livenä ennen muutosta: sisällöstä sisältöön etusivulla 161–186 px (1440) ja 113–121 px (390),
   palvelusivuilla 233–281 px ja 153–185 px, Kotisivut-sivulla enimmillään 311 px. */
.sivu-tiivis { --section-y: clamp(40px, 5vw, 64px); }
