:root {
  --dv-color-primary: #a98bff;
  --dv-color-primary-contrast: #08070c;
  --dv-color-secondary: #7a5eef;
  --dv-color-accent: #cbb8ff;
  --dv-color-bg: #08070c;
  --dv-color-surface: rgba(169,139,255,0.05);
  --dv-color-text: #ded9ec;
  --dv-color-muted: rgba(222,217,236,0.46);
  --dv-color-border: rgba(169,139,255,0.22);
  --dv-color-success: #5fd39b;
  --dv-color-warning: #f5b544;
  --dv-color-danger: #ff7b7b;
  --dv-font-heading: "Syne", "IBM Plex Sans", sans-serif;
  --dv-font-body: "IBM Plex Sans", sans-serif;
  --dv-font-mono: "IBM Plex Mono", ui-monospace, monospace;
  --dv-font-size-base: 16px;
  --dv-line-height: 1.6;
  --dv-font-size-h1: calc(var(--dv-font-size-base) * 3.052);
  --dv-font-size-h2: calc(var(--dv-font-size-base) * 2.441);
  --dv-font-size-h3: calc(var(--dv-font-size-base) * 1.953);
  --dv-font-size-h4: calc(var(--dv-font-size-base) * 1.563);
  --dv-font-size-h5: calc(var(--dv-font-size-base) * 1.250);
  --dv-font-size-h6: calc(var(--dv-font-size-base) * 1.000);
  --dv-radius: 2px;
  --dv-spacing: 1rem;
  --dv-container-max: 1240px;
  --dv-container-gutter: clamp(1.25rem, 4.5vw, 3.75rem);
  --dv-shadow: 0 1px 2px rgba(5, 4, 9, 0.6), 0 24px 60px -24px rgba(169, 139, 255, 0.3);
}

*,*::before,*::after{box-sizing:border-box}
body{margin:0;background:var(--dv-color-bg);color:var(--dv-color-text);font-family:var(--dv-font-body);font-size:var(--dv-font-size-base);line-height:var(--dv-line-height)}
h1,h2,h3,h4,h5,h6{font-family:var(--dv-font-heading);line-height:1.2;margin:0 0 .5em}
h1{font-size:var(--dv-font-size-h1)}h2{font-size:var(--dv-font-size-h2)}h3{font-size:var(--dv-font-size-h3)}
a{color:var(--dv-color-primary)}
img,video{max-width:100%;height:auto}
.dv-main{max-width:var(--dv-container-max);margin-inline:auto;padding-inline:var(--dv-container-gutter)}
[hidden]{display:none!important}
.dv-consent-launcher{position:fixed;left:max(1rem,env(safe-area-inset-left));bottom:max(1rem,env(safe-area-inset-bottom));z-index:2147482999;display:grid;place-items:center;width:3rem;height:3rem;padding:0;border:1px solid var(--dv-color-border);border-radius:50%;background:var(--dv-color-surface);color:var(--dv-color-text);box-shadow:var(--dv-shadow,0 2px 12px rgba(0,0,0,.2));cursor:pointer}
.dv-consent-launcher:focus-visible,.dv-consent__close:focus-visible{outline:2px solid var(--dv-color-primary);outline-offset:3px}
.dv-consent__close{justify-self:end;border:0;background:transparent;color:inherit;min-width:2.75rem;min-height:2.75rem;font-size:1.5rem;cursor:pointer}
.dv-consent{position:fixed;z-index:2147483000;box-sizing:border-box;display:grid;grid-template-columns:minmax(0,1fr);gap:calc(var(--dv-spacing) * .75);padding:var(--dv-spacing);max-height:calc(100vh - 2 * var(--dv-spacing));max-height:calc(100dvh - 2 * var(--dv-spacing));overflow:auto;background:var(--dv-color-surface);color:var(--dv-color-text);border:1px solid var(--dv-color-border);border-radius:var(--dv-radius);box-shadow:var(--dv-shadow, 0 -2px 24px rgba(0,0,0,.18));font-family:var(--dv-font-body);font-size:.95rem;line-height:var(--dv-line-height)}
.dv-consent.dv-consent--bottom{inset:auto 0 0 0;transform:none;width:100%;border-radius:0;border-inline-width:0;border-block-end-width:0}
.dv-consent.dv-consent--bottom-left{inset:auto auto var(--dv-spacing) var(--dv-spacing);transform:none;width:min(28rem, calc(100vw - 2 * var(--dv-spacing)))}
.dv-consent.dv-consent--bottom-right{inset:auto var(--dv-spacing) var(--dv-spacing) auto;transform:none;width:min(28rem, calc(100vw - 2 * var(--dv-spacing)))}
.dv-consent.dv-consent--center{inset:50% auto auto 50%;width:min(32rem, calc(100vw - 2 * var(--dv-spacing)));transform:translate(-50%,-50%)}
.dv-consent__body{display:flex;flex-direction:column;gap:.35rem}
.dv-consent__title{margin:0;font-family:var(--dv-font-heading);font-size:1.05rem;font-weight:600}
.dv-consent__text{margin:0;color:var(--dv-color-muted)}
.dv-consent__link{align-self:flex-start;color:var(--dv-color-primary)}
.dv-consent__cats{display:flex;flex-direction:column;gap:.5rem;max-height:40vh;overflow:auto;padding-block-start:.5rem;border-block-start:1px solid var(--dv-color-border)}
.dv-consent__cat{display:flex;gap:.6rem;align-items:flex-start;cursor:pointer}
.dv-consent__cat-input{flex:none;margin-block-start:.2rem;accent-color:var(--dv-color-primary)}
.dv-consent__cat-input:disabled{cursor:not-allowed}
.dv-consent__cat-text{display:flex;flex-direction:column;gap:.15rem}
.dv-consent__cat-label{font-weight:600}
.dv-consent__cat-desc{color:var(--dv-color-muted);font-size:.85em}
.dv-consent__actions{display:flex;flex-wrap:wrap;align-items:center;gap:.5rem}
.dv-consent__btn{font:inherit;cursor:pointer;padding:.55rem 1.1rem;border:1px solid var(--dv-color-border);border-radius:var(--dv-radius);background:var(--dv-color-bg);color:var(--dv-color-text)}
.dv-consent__btn:focus-visible{outline:2px solid var(--dv-color-primary);outline-offset:2px}
.dv-consent__btn--primary{background:var(--dv-color-primary);color:var(--dv-color-primary-contrast);border-color:var(--dv-color-primary)}
.dv-consent__btn--ghost{margin-inline-end:auto;background:transparent;border-color:transparent;text-decoration:underline;color:var(--dv-color-text)}
@media (max-width:480px){.dv-consent__actions{flex-direction:column;align-items:stretch}.dv-consent__btn--ghost{margin-inline-end:0}}
.dv-booking{display:flex;flex-direction:column;gap:.6rem;font-family:var(--dv-font-body);color:var(--dv-color-text)}
.dv-booking--busy{opacity:.6}
.dv-booking__bar{display:flex;align-items:center;justify-content:space-between;gap:.5rem}
.dv-booking__month{margin:0;font-family:var(--dv-font-heading);font-size:1.05rem;font-weight:600;text-align:center;flex:1 1 auto}
.dv-booking__nav{font:inherit;cursor:pointer;min-width:40px;min-height:40px;padding:.4rem .7rem;border:1px solid var(--dv-color-border);border-radius:var(--dv-radius);background:var(--dv-color-surface);color:var(--dv-color-text)}
.dv-booking__nav:focus-visible{outline:2px solid var(--dv-color-primary);outline-offset:2px}
.dv-booking__nav:disabled{opacity:.5;cursor:not-allowed}
.dv-booking__grid{display:grid;grid-template-columns:repeat(7,1fr);gap:2px}
.dv-booking__weekdays{display:contents}
.dv-booking__week{display:contents}
.dv-booking__weekday{padding:.25rem 0;text-align:center;font-size:.8em;color:var(--dv-color-muted)}
.dv-booking__cell{display:block}
.dv-booking__cell--empty{min-height:40px}
.dv-booking__day{font:inherit;cursor:pointer;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:2px;width:100%;min-width:40px;min-height:40px;padding:.3rem 0;border:1px solid transparent;border-radius:var(--dv-radius);background:var(--dv-color-surface);color:var(--dv-color-text)}
.dv-booking__day:focus-visible{outline:2px solid var(--dv-color-primary);outline-offset:2px}
.dv-booking__daynum{line-height:1}
.dv-booking__dot{display:block;width:6px;height:6px;border-radius:50%;background:transparent}
.dv-booking__day--free .dv-booking__dot{background:var(--dv-color-primary)}
.dv-booking__day--free{border-color:var(--dv-color-border)}
.dv-booking__day--today{box-shadow:inset 0 0 0 1px var(--dv-color-accent)}
.dv-booking__day--none{color:var(--dv-color-muted);cursor:default}
.dv-booking__day--selected{background:var(--dv-color-primary);color:var(--dv-color-primary-contrast);border-color:var(--dv-color-primary);font-weight:700}
.dv-booking__day--selected .dv-booking__dot{background:var(--dv-color-primary-contrast)}
.dv-booking__day[aria-disabled="true"]{opacity:.65;cursor:default}
.dv-booking__hint{margin:0;color:var(--dv-color-muted);font-size:.85em}
.dv-booking__status{margin:0;color:var(--dv-color-muted);font-size:.9em}
.dv-booking__status--error{color:var(--dv-color-danger);font-weight:600}
.dv-booking__times{display:flex;flex-wrap:wrap;gap:.4rem;align-items:center}
.dv-booking__times-title{flex:1 0 100%;margin:0;font-weight:600;font-size:.9em}
.dv-booking__slot{font:inherit;cursor:pointer;min-height:40px;padding:.5rem .9rem;border:1px solid var(--dv-color-border);border-radius:var(--dv-radius);background:var(--dv-color-surface);color:var(--dv-color-text)}
.dv-booking__slot:focus-visible{outline:2px solid var(--dv-color-primary);outline-offset:2px}
.dv-booking__slot--selected{background:var(--dv-color-primary);color:var(--dv-color-primary-contrast);border-color:var(--dv-color-primary);font-weight:700}
.dv-booking__selected{margin:0;font-weight:600}
.dv-booking__empty{margin:0;color:var(--dv-color-muted)}
.dv-booking__retry{font:inherit;cursor:pointer;align-self:flex-start;min-height:40px;padding:.5rem 1.1rem;border:1px solid var(--dv-color-primary);border-radius:var(--dv-radius);background:var(--dv-color-primary);color:var(--dv-color-primary-contrast)}
.dv-booking__retry:focus-visible{outline:2px solid var(--dv-color-primary);outline-offset:2px}
@media (max-width:480px){.dv-booking__grid{gap:1px}.dv-booking__slot{flex:1 1 40%}.dv-booking__month{font-size:1rem}}
.dv-form__step{border:0;margin:0;padding:0;min-inline-size:0}
.dv-form__step-nav{display:flex;gap:.5rem;flex-wrap:wrap}
.dv-form__step-prev,.dv-form__step-next{font:inherit;cursor:pointer}
.dv-form__step-prev[disabled]{opacity:.5;cursor:default}
.dv-form__step-progress{margin:.25rem 0;font-size:.875rem;color:var(--dv-color-muted)}
.dv-form__step--current{display:block}

/* --- customCss --- */
/* Fontit ladataan tokens.fonts.externalUrl -kentasta. ALA lisaa @import
   tanne: alusta liittaa taman CSS:n omien perussaantojensa PERAAN, joten
   @import ei olisi enaa stylesheetin ensimmainen saanto ja se ohitettaisiin. */
/* ==========================================================================
   Devella — tumma teema.
   Lahde: hdwp/src/customers/devella/assets/{_tokens,style,navbar}.scss
   Luokkanimet on etuliitetty dvf-:lla, arvot ovat lahteesta.
   Tietoiset poikkeamat on merkitty kommentilla "POIKKEAMA".
   ========================================================================== */

:root{
  /* Pinnat ja tekstit */
  --dvf-dark: var(--dv-color-bg, #08070c);
  --dvf-darker: #050409;
  --dvf-light: var(--dv-color-text, #ded9ec);
  --dvf-header: #f3effd;
  --dvf-primary: var(--dv-color-primary, #a98bff);
  --dvf-primary-contrast: var(--dv-color-primary-contrast, #08070c);
  --dvf-brand: var(--dv-color-secondary, #7a5eef); /* ei tekstivarina: 4.47:1 */
  --dvf-accent: var(--dv-color-accent, #cbb8ff);
  --dvf-overlay: rgba(8,7,12,.72);
  --dvf-overlay-strong: rgba(8,7,12,.94);

  /* Saatavuustilan merkkivari (_tokens.scss: --status-busy). */
  --dvf-warning: var(--dv-color-warning, #f5b544);
  --dvf-warning-soft: rgba(245,181,68,.09);

  /* Lapinakyvat savyt.
     POIKKEAMA: --dvf-ink-faint on lahteen .46 sijaan .55, jotta pieni
     mono-teksti yltaa WCAG AA:han (4.82:1 taustaa --dvf-dark vasten). */
  --dvf-ink-soft: rgba(222,217,236,.72);
  --dvf-ink-faint: rgba(222,217,236,.55);
  --dvf-surface: var(--dv-color-surface, rgba(169,139,255,.05));
  --dvf-surface-strong: rgba(169,139,255,.1);
  --dvf-line: var(--dv-color-border, rgba(169,139,255,.22));
  --dvf-line-soft: rgba(169,139,255,.13);
  --dvf-glow: rgba(169,139,255,.3);
  --dvf-window-bg: rgba(5,4,9,.7);
  /* Liikennevalopisteet ovat aria-hidden-koristeita: ei kontrastivaatimusta. */
  --dvf-dot-red: #ff5f57;
  --dvf-dot-amber: #febc2e;
  --dvf-dot-green: #28c840;
  --dvf-grid-line: rgba(169,139,255,.05);

  /* Koodivarit */
  --dvf-code-key: var(--dvf-primary);
  --dvf-code-str: #8fd6b4;
  --dvf-code-prop: var(--dvf-light);

  /* Typografia */
  --dvf-font-display: var(--dv-font-heading, "Syne", "IBM Plex Sans", sans-serif);
  --dvf-font-body: var(--dv-font-body, "IBM Plex Sans", sans-serif);
  --dvf-font-mono: var(--dv-font-mono, "IBM Plex Mono", ui-monospace, monospace);
  --dvf-mono-size: .92rem;
  --dvf-mono-track: .04em;

  /* Mitat */
  --dvf-radius: var(--dv-radius, 2px);
  --dvf-shell-max: var(--dv-container-max, 1240px);
  --dvf-gutter: var(--dv-container-gutter, clamp(1.25rem, 4.5vw, 3.75rem));
  --dvf-section-pad: clamp(3.5rem, 8vw, 7rem);
  --dvf-ease-out: cubic-bezier(.22, 1, .36, 1);
  --dvf-nav-h: 4.6rem;
  --dvf-grid: 48px;

  /* Alustan omien selektorien vanhat aliakset */
  --dv-max: var(--dvf-shell-max);
  --dv-gutter: var(--dvf-gutter);
}

/* _tokens.scss: navi madaltuu mobiilissa. Vaikuttaa myos ankkurien
   scroll-margin-topiin ja heron min-heightiin. */
@media screen and (max-width:768px){
  :root{--dvf-nav-h:4rem}
}

/* --------------------------------------------------------------- Perusta */

*,*::before,*::after{box-sizing:border-box}

/* Lahteen style.scss asettaa html-fontin 16px:aan ja pudottaa sen 15px:aan
   mobiilissa. Alusta asettaa vain bodyn (--dv-font-size-base), joten ilman
   tata koko rem-asteikko — leipateksti, mono, clamp()-alarajat — oli
   mobiilissa 6,7 % lahdetta isompi. */
html{
  color-scheme:dark;
  font-size:16px;
  overflow-x:clip; /* varmistin: sivu ei vuoda vaakasuunnassa */
  scroll-behavior:smooth;
}

@media screen and (max-width:768px){
  html{font-size:15px}
}

body{
  margin:0;
  font-family:var(--dvf-font-body);
  font-size:1.05rem;
  line-height:var(--dv-line-height, 1.6);
  color:var(--dvf-light);
  background:var(--dvf-dark);
  -webkit-font-smoothing:antialiased;
  overflow-x:clip;
}

/* Taustaruudukko: 48px viivasto, jonka maski haivyttaa reunoja kohti,
   ja hidas kirkkauspulssi (style.scss body::before + dvGridPulse). */
body::before{
  content:"";
  position:fixed;
  inset:0;
  z-index:-1;
  pointer-events:none;
  /* Pelkka viivasto, ei hehkua. Lahteessa body::before EI sisalla
     radial-gradienttia — sivun ylaosaan lisatty violetti hehku varjasi
     paaotsikon taustan. Hero saa oman pehmean valonsa .dvf-grid-fadesta,
     kuten lahteessakin. Maski ja sen pysahdyskohta ovat lahteen arvot. */
  background-image:
    linear-gradient(var(--dvf-grid-line) 1px, transparent 1px),
    linear-gradient(90deg, var(--dvf-grid-line) 1px, transparent 1px);
  background-size:var(--dvf-grid) var(--dvf-grid);
  -webkit-mask-image:radial-gradient(ellipse 80% 70% at 50% 20%, #000 40%, transparent 100%);
  mask-image:radial-gradient(ellipse 80% 70% at 50% 20%, #000 40%, transparent 100%);
  animation:dvfGridPulse 12s ease-in-out infinite;
}

/* CRT-juovasto.
   POIKKEAMA: lahteessa tama on z-index:1 eli SISALLON PAALLA. Tassa se on
   taustakerroksessa (-1). Sisallon paalla oleva tummennuskalvo pienentaisi
   kaiken tekstin kontrastia, ja --dvf-ink-faint on juuri nostettu AA-rajalle
   — kalvo soisi sen marginaalin pois. */
body::after{
  content:"";
  position:fixed;
  inset:0;
  z-index:-1;
  pointer-events:none;
  opacity:.45;
  background:repeating-linear-gradient(
    0deg,
    transparent,
    transparent 2px,
    rgba(0,0,0,.07) 2px,
    rgba(0,0,0,.07) 4px
  );
}

@keyframes dvfGridPulse{
  0%,100%{opacity:.35}
  50%{opacity:.55}
}

/* Otsikot. Pitkat suomen yhdyssanat ("ohjelmistokehitysta") eivat saa vuotaa
   palstasta millaan leveydella: break-word (ei anywhere) sailyttaa
   min-content-koon, hyphens tuottaa kapealla ruudulla siistin tavuviivan. */
h1,h2,h3,h4,h5,h6{
  font-family:var(--dvf-font-display);
  color:var(--dvf-header);
  letter-spacing:-.035em;
  font-weight:700;
  margin:0 0 .5em;
  overflow-wrap:break-word;
  -webkit-hyphens:auto;
  hyphens:auto;
}
h1{font-size:clamp(2.1rem, 6vw, 4.6rem);line-height:1;font-weight:800}
h2{font-size:clamp(1.6rem, 4vw, 3rem);line-height:1.1}
h3{font-size:clamp(1.2rem, 2vw, 1.6rem);line-height:1.25}
h4{font-size:1.15rem;font-weight:600;letter-spacing:-.01em}

p,li,dd,dt,figcaption{overflow-wrap:break-word}
p{margin:0 0 1em}
p:last-child{margin-bottom:0}

a{color:var(--dvf-primary);text-underline-offset:.22em}
a:hover{color:var(--dvf-accent)}

img{max-width:100%;height:auto}

code,kbd,pre,samp{font-family:var(--dvf-font-mono)}

hr{border:0;border-top:1px solid var(--dvf-line-soft);margin:2rem 0}

::selection{background:var(--dvf-primary);color:var(--dvf-primary-contrast)}

::placeholder{font-family:var(--dvf-font-mono);color:var(--dvf-ink-faint);opacity:1}

.dvf-mono{
  font-family:var(--dvf-font-mono);
  font-weight:400;
  letter-spacing:.02em;
}

/* ------------------------------------------------------- Reveal-animaatio */
/* Lahde: _animations.scss .rv / .rv-init / .rv-in + components/Reveal.
   Merkitty elementti (.dvf-rv) liukuu paikalleen kun se tulee nakyviin.
   Sisalto on aina oletuksena NAKYVAA: .dvf-rv--init lisataan vain
   animaatiot/component.js:sta, ja vain jos IntersectionObserver on
   kaytettavissa, liike sallittu eika kyseessa ole botti. Ilman JS:aa
   luokka jaa lisaamatta ja sivu nakyy tavalliseen tapaan. */

.dvf-rv{will-change:auto}

.dvf-rv--init{
  opacity:0;
  transform:translateY(22px);
  transition:
    opacity .65s var(--dvf-ease-out),
    transform .65s var(--dvf-ease-out);
}

.dvf-rv--init.dvf-rv--in{
  opacity:1;
  transform:translateY(0);
}

@media (prefers-reduced-motion: reduce){
  .dvf-rv--init{
    opacity:1!important;
    transform:none!important;
    transition:none!important;
  }
}

/* EI CSS-VARATIETA REVEALILLE — kokeiltu ja hylatty tietoisesti.
   `animation-timeline: view()` tuottaisi saman liukuman ilman skriptia,
   mutta scroll-aikajana EI ole kertaluontoinen: se seuraa vierityskohtaa
   jatkuvasti, joten kerran paljastunut elementti palaa lahtotilaan
   (opacity 0) heti kun se jaa ruudun ulkopuolelle. Kokosivun renderissa —
   tulostus, PDF, kuvakaappaus — puolet sivusta oli silloin tyhjaa.
   Lahteen Reveal on kertaluontoinen (unobserve ensimmaisen osuman jalkeen),
   eika sita voi jaljitella CSS:lla. Ilman JS:aa sisalto nakyy siis
   sellaisenaan, mika on oikea lopputulos. */

/* ------------------------------------------------------- Saavutettavuus */

/* Ankkurikohteet jaisivat muuten sticky-headerin alle. Tama kattaa myos
   ankkurikohteet. Lahteen .anchor-alias -spanit on poistettu markupista,
   joten niille ei tarvita omaa saantoa. */
[id]{scroll-margin-top:calc(var(--dvf-nav-h) + 8px)}

/* POIKKEAMA (lisays): lahteessa focus-visible on vain navilinkeilla ja
   -napilla. Tassa kaikilla interaktiivisilla. */
:focus-visible{
  outline:2px solid var(--dvf-primary);
  outline-offset:2px;
  border-radius:var(--dvf-radius);
}

.dv-skip{position:absolute;left:-9999px}
.dv-skip:focus{
  left:1rem;top:1rem;z-index:200;
  background:var(--dvf-overlay-strong);
  color:var(--dvf-header);
  border:1px solid var(--dvf-line);
  font-family:var(--dvf-font-mono);
  font-size:var(--dvf-mono-size);
  padding:.75rem 1rem;
  border-radius:var(--dvf-radius);
  text-decoration:none;
}

.dv-hp{position:absolute!important;left:-9999px!important}

/* Lahteen body.nav-open: avoin mobiilivalikko lukitsee sivun vierityksen. */
body.dv-nav-open{overflow:hidden}


/* ---------------------------------------------------------- Ylatunniste */
/* navbar.scss .site-nav / .nav-bar / .nav-link */

/* Navi on lahtokohtaisesti LAPINAKYVA ja rajaton — se sulautuu heroon.
   Tausta, blur ja alarajaviiva ilmestyvat vasta kun sivua on vieritetty
   (.is-scrolled), ja navi liukuu pois alaspain vierittaessa (.is-hidden).
   Tilaluokat asettaa paavalikko/component.js. */
div:has(>.dv-header){
  position:sticky;
  top:0;
  z-index:200;
  width:100%;
  background:transparent;
  border-bottom:1px solid transparent;
  transition:
    transform .3s var(--dvf-ease-out),
    background .3s ease,
    border-color .3s ease;
}

.dv-header {
  width:100%;
}

div.is-scrolled:has(>.dv-header){
  background:rgba(8,7,12,.88);
  -webkit-backdrop-filter:blur(12px);
  backdrop-filter:blur(12px);
  border-bottom-color:var(--dvf-line);
}

div:has(>.dv-header).is-hidden{transform:translateY(-100%)}

/* Avoin mobiilivalikko ei koskaan piiloudu, ja se saa umpinaisen taustan. */
div:has(>.dv-header).is-open{
  transform:none;
  background:var(--dvf-dark);
  border-bottom-color:var(--dvf-line);
}

div:has(>.dv-header):focus-within{
  background:rgba(8,7,12,.88);
  -webkit-backdrop-filter:blur(12px);
  backdrop-filter:blur(12px);
}
/* Avoin, koko näytön valikko on fixed-näkymäporttiin: kumotaan
   is-scrolledin ja focus-withinin backdrop-filter, ettei siitä tule
   fixed-paneelin containing blockia (navi putoaisi headerin sisään). */
div:has(>.dv-header).is-open{
  -webkit-backdrop-filter:none;
  backdrop-filter:none;
}

/* JS:TON VARATIE, sama syy kuin revealissa: jos komponentin skripti ei paase
   ajoon CSP:n takia, .is-scrolled jaa lisaamatta ja navi olisi lapinakyva
   koko sivun mitalta. Scroll-aikajana tuo taustan ja rajaviivan samassa
   kohdassa kuin JS (12px). Suunnan tunnistusta eli .is-hiddenia ei voi
   tehda CSS:lla — se jaa JS-polun varaan. */
@supports (animation-timeline: scroll()){
  div:has(>.dv-header):not(.is-scrolled):not(.is-open){
    animation:dvfNavSolid linear both;
    animation-timeline:scroll(root block);
    animation-range:12px 64px;
  }
}

@keyframes dvfNavSolid{
  from{background:transparent;border-bottom-color:transparent}
  to{background:rgba(8,7,12,.88);border-bottom-color:var(--dvf-line)}
}

.dv-header__inner{
  position:relative;
  max-width:var(--dvf-shell-max);
  margin-inline:auto;
  padding:.4rem var(--dvf-gutter);
  min-height:var(--dvf-nav-h);
  display:flex;
  align-items:center;
  gap:1rem;
}

.dv-header__logo{
  font-family:var(--dvf-font-display);
  font-weight:700;
  font-size:1.15rem;
  letter-spacing:-.02em;
  color:var(--dvf-header);
  text-decoration:none;
  display:inline-flex;
  align-items:center;
  min-width:0;
}
.dv-header__logo:hover{color:var(--dvf-primary)}
.dv-header__logo img{display:block;height:2.4rem;max-width:100%;width:auto}

.dv-header__nav{margin-left:auto}
.dv-header__nav ul{
  display:flex;
  align-items:center;
  gap:.15rem;
  list-style:none;
  margin:0;
  padding:0;
  flex-wrap:wrap;
}

/* {% menu 'main' %} tuottaa .dv-header__nav > nav.dv-nav > ul.dv-menu > li > a.
   Labelit ovat pelkkaa tekstia ("Teknologiat"), joten lahteen violetti
   jarjestysnumero (.nav-link .n) on tuotettava CSS-laskurilla.
   decimal-leading-zero antaa 01/02/03. */
.dv-header__nav .dv-menu{
  counter-reset:dvf-nav;
  display:flex;
  align-items:center;
  gap:.15rem;
  list-style:none;
  margin:0;
  padding:0;
  flex-wrap:wrap;
}
.dv-header__nav .dv-menu>li{counter-increment:dvf-nav}

.dv-header__nav a{
  display:inline-flex;
  align-items:baseline;
  gap:.45rem;
  padding:.45rem .7rem;
  font-family:var(--dvf-font-mono);
  font-size:.92rem;
  letter-spacing:var(--dvf-mono-track);
  text-transform:lowercase;
  white-space:nowrap;
  color:var(--dvf-light);
  text-decoration:none;
  border-bottom:1px solid transparent;
  transition:color .2s ease, border-color .2s ease;
}
/* Lahteen <span class="mono n">01</span>. */
.dv-header__nav .dv-menu>li>a::before{
  content:counter(dvf-nav, decimal-leading-zero);
  color:var(--dvf-primary);
  font-size:.72rem;
}
/* is-active tulee <li>:lle, aria-current <a>:lle — katetaan molemmat. */
.dv-header__nav a:hover,
.dv-header__nav a:focus,
.dv-header__nav a[aria-current],
.dv-header__nav .is-active>a{
  color:var(--dvf-primary);
  border-bottom-color:var(--dvf-primary);
}

.dv-header__toggle{display:none}

/* ------------------------------------------------------------- Rungot */

.dv-page,.dv-article,.dv-archive{
  max-width:var(--dvf-shell-max);
  margin-inline:auto;
  padding-inline:var(--dvf-gutter);
}

/* Etusivun dvf-osiot ovat taysleveita: niiden ylarajaviivan on ulotuttava
   reunasta reunaan, ja keskityksen hoitaa .dvf-shell. */
.dv-page:has(.dvf-section),
.dv-page:has(.dvf-hero){
  max-width:none;
  padding-inline:0;
}

/* Lahde: style.scss .page-title (rivit 1287-1295). Ilman tata sivun otsikko
   peri globaalin h1:n (jopa 4.6rem) ja oli sisasivuilla selvasti lahdetta
   isompi — .dv-article__title sai saman kohtelun jo aiemmin. */
.dv-page__title{
  margin-top:clamp(2rem,5vw,3.5rem);
  margin-bottom:clamp(1.2rem, 3vw, 1.8rem);
  font-size:clamp(1.6rem, 5vw, 3.2rem);
  line-height:1.1;
  font-weight:800;
}

.dv-page__content{padding-block:0 clamp(2rem,5vw,4rem)}
.dv-page:has(.dvf-section) .dv-page__content{padding-block:0}
.dv-page__content>p,
.dv-page__content>ul,
.dv-page__content>ol{color:var(--dvf-ink-soft);max-width:46rem}
.dv-page__content>ul,.dv-page__content>ol{padding-left:1.25rem}
.dv-page__content li{margin-bottom:.4rem}

/* Lahde: style.scss .page-content (rivit 1298-1364) — sama kohtelu kuin
   .post-contentilla. Puuttui kokonaan, joten esim. tietosuojaselosteen
   valiotsikot kayttivat globaalia h2-kokoa (jopa 3rem) ja olivat lahes
   yhta isoja kuin sivun otsikko.

   RAJAUS ON PAKOLLINEN: nama ovat leipatekstisaantoja, ja `.dv-page__content h2`
   voittaa spesifisyydessa yhden luokan `.dvf-section__title`in. Ilman
   :not(:has(...))-vartta etusivun osioiden otsikot kutistuivat leipatekstin
   valiotsikkokokoon. Etusivu tunnistetaan sen omista dvf-lohkoista. */
.dv-page:not(:has(.dvf-hero)):not(:has(.dvf-section)) .dv-page__content{color:var(--dvf-ink-soft)}
.dv-page:not(:has(.dvf-hero)):not(:has(.dvf-section)) .dv-page__content>*{max-width:46rem}
.dv-page:not(:has(.dvf-hero)):not(:has(.dvf-section)) .dv-page__content>*:first-child{margin-top:0}
.dv-page:not(:has(.dvf-hero)):not(:has(.dvf-section)) .dv-page__content h2,
.dv-page:not(:has(.dvf-hero)):not(:has(.dvf-section)) .dv-page__content h3,
.dv-page:not(:has(.dvf-hero)):not(:has(.dvf-section)) .dv-page__content h4,
.dv-page:not(:has(.dvf-hero)):not(:has(.dvf-section)) .dv-page__content h5{
  margin:1.8rem 0 .8rem;
  color:var(--dvf-header);
}
.dv-page:not(:has(.dvf-hero)):not(:has(.dvf-section)) .dv-page__content h2{font-size:clamp(1.35rem, 2.6vw, 1.9rem)}
.dv-page:not(:has(.dvf-hero)):not(:has(.dvf-section)) .dv-page__content h3{font-size:clamp(1.15rem, 2vw, 1.4rem)}
.dv-page:not(:has(.dvf-hero)):not(:has(.dvf-section)) .dv-page__content h4,
.dv-page:not(:has(.dvf-hero)):not(:has(.dvf-section)) .dv-page__content h5{font-size:clamp(1.05rem, 1.6vw, 1.2rem)}
.dv-page:not(:has(.dvf-hero)):not(:has(.dvf-section)) .dv-page__content p,
.dv-page:not(:has(.dvf-hero)):not(:has(.dvf-section)) .dv-page__content li{font-size:1.02rem;-webkit-hyphens:auto;hyphens:auto}
.dv-page:not(:has(.dvf-hero)):not(:has(.dvf-section)) .dv-page__content a{color:var(--dvf-primary)}
.dv-page:not(:has(.dvf-hero)):not(:has(.dvf-section)) .dv-page__content img{border:1px solid var(--dvf-line-soft)}
.dv-page:not(:has(.dvf-hero)):not(:has(.dvf-section)) .dv-page__content pre{max-width:100%;overflow-x:auto;white-space:pre-wrap;overflow-wrap:anywhere}
.dv-page:not(:has(.dvf-hero)):not(:has(.dvf-section)) .dv-page__content table{max-width:100%}

.dv-breadcrumbs ol{
  display:flex;gap:.5rem;list-style:none;margin:1.5rem 0 1rem;padding:0;
  font-family:var(--dvf-font-mono);font-size:.8rem;
  letter-spacing:var(--dvf-mono-track);
  color:var(--dvf-ink-faint);flex-wrap:wrap;
}
.dv-breadcrumbs a{color:var(--dvf-ink-faint);text-decoration:none}
.dv-breadcrumbs a:hover{color:var(--dvf-primary)}

/* ------------------------------------------------- Artikkeli & arkisto */
/* Lahde: style.scss .post-container / .post-content / .post-featured-image
   (rivit 1266-1390). Alustan vastineet ovat .dv-article__* ja .dv-archive__*
   — sama rooli, eri luokkanimi. */

.dv-article,
.dv-archive{padding-block:clamp(1.5rem, 4vw, 2.75rem) clamp(2.5rem, 6vw, 4rem)}

.dv-article__header{margin-bottom:clamp(1.2rem, 3vw, 1.8rem)}

.dv-article__title,
.dv-archive__title{
  font-family:var(--dvf-font-display);
  font-size:clamp(1.6rem, 5vw, 3.2rem);
  line-height:1.1;
  font-weight:800;
  letter-spacing:-.035em;
  color:var(--dvf-header);
  margin:0 0 clamp(1.2rem, 3vw, 1.8rem);
}
.dv-article__title{margin-bottom:.6rem}

.dv-article__meta{
  font-family:var(--dvf-font-mono);
  font-size:.82rem;
  letter-spacing:var(--dvf-mono-track);
  color:var(--dvf-ink-faint);
  margin:0;
}

.dv-article__hero{
  margin:clamp(1.5rem, 4vw, 2.5rem) 0 0;
  padding-bottom:clamp(1.5rem, 4vw, 2.5rem);
  border-bottom:1px solid var(--dvf-line-soft);
}
.dv-article__hero img{display:block;width:100%;height:auto;border:1px solid var(--dvf-line-soft)}

.dv-article__content{color:var(--dvf-ink-soft)}
/* Lukuleveys rajataan sisallon lapsille, ei kaareelle. */
.dv-article__content>*{max-width:46rem}
.dv-article__content>*:first-child{margin-top:0}
/* Globaali h2-koko (jopa 3rem) on artikkelisisallolle liian iso:
   valiotsikot olivat lahes yhta isoja kuin sivun otsikko. */
.dv-article__content h2,
.dv-article__content h3,
.dv-article__content h4,
.dv-article__content h5{
  margin:1.8rem 0 .8rem;
  color:var(--dvf-header);
}
.dv-article__content h2{font-size:clamp(1.35rem, 2.6vw, 1.9rem)}
.dv-article__content h3{font-size:clamp(1.15rem, 2vw, 1.4rem)}
.dv-article__content h4,
.dv-article__content h5{font-size:clamp(1.05rem, 1.6vw, 1.2rem)}
.dv-article__content p,
.dv-article__content li{font-size:1.02rem;hyphens:auto}
.dv-article__content ul,
.dv-article__content ol{padding-left:1.25rem}
.dv-article__content li{margin-bottom:.4rem}
.dv-article__content a{color:var(--dvf-primary)}
.dv-article__content img,
.dv-article__content iframe,
.dv-article__content video{max-width:100%;height:auto}
.dv-article__content img{border:1px solid var(--dvf-line-soft)}
/* Leveat taulukot ja koodilohkot vieritetaan omassa laatikossaan sen
   sijaan etta ne venyttaisivat koko sivua vaakasuunnassa. */
.dv-article__content table{max-width:100%}
.dv-article__content .table-responsive,
.dv-article__content figure{max-width:100%;overflow-x:auto}
.dv-article__content pre{
  max-width:100%;
  overflow-x:auto;
  white-space:pre-wrap;
  overflow-wrap:anywhere;
}

.dv-article__related{
  margin-top:clamp(2.5rem, 6vw, 4rem);
  padding-top:clamp(1.5rem, 4vw, 2.5rem);
  border-top:1px solid var(--dvf-line-soft);
}
.dv-article__related>h2{
  font-size:clamp(1.35rem, 2.6vw, 1.9rem);
  margin:0 0 1.25rem;
}

.dv-cards{display:grid;gap:1.5rem;grid-template-columns:repeat(auto-fill,minmax(16rem,1fr))}
.dv-card{
  background:var(--dvf-surface);
  border:1px solid var(--dvf-line-soft);
  border-radius:var(--dvf-radius);
  overflow:hidden;
  display:flex;
  flex-direction:column;
  min-width:0;
}
.dv-card:hover{border-color:var(--dvf-line)}
.dv-card__body{padding:1.25rem;display:flex;flex-direction:column;gap:.5rem}
.dv-card__title{margin:0;font-size:1.125rem}
.dv-card__title a{text-decoration:none;color:var(--dvf-header)}
.dv-card__title a:hover{color:var(--dvf-primary)}
.dv-card__date{
  font-family:var(--dvf-font-mono);font-size:.8rem;
  letter-spacing:var(--dvf-mono-track);color:var(--dvf-ink-faint);
}
.dv-empty{color:var(--dvf-ink-faint);font-style:italic}

/* -------------------------------------------------------------- Lomake */
/* style.scss input.form-control / textarea.form-control */

.dv-form{display:grid;gap:1.1rem;max-width:36rem}
.dv-form label{
  display:grid;gap:.4rem;
  font-family:var(--dvf-font-mono);
  font-size:.82rem;
  letter-spacing:.06em;
  color:var(--dvf-ink-soft);
}
.dv-form input,
.dv-form textarea,
.dv-form select{
  font-family:var(--dvf-font-mono);
  font-size:1rem;
  padding:10px 14px;
  border:1px solid var(--dvf-line);
  border-radius:var(--dvf-radius);
  background:rgba(0,0,0,.35);
  color:var(--dvf-light);
  width:100%;
  transition:border-color .2s ease, box-shadow .2s ease;
}
.dv-form input{min-height:3.2rem}
.dv-form textarea{min-height:8rem;resize:vertical}

/* Alustan generoima lomakemarkup (forms.ts): .dv-form__field kaaree
   labelin ja kontrollin SISARUKSINA, joten labelin on oltava block —
   muuten pakollisen kentan tahti valahtaa omalle rivilleen. */
.dv-form__field{display:grid;gap:.4rem;min-width:0}
.dv-form label.dv-form__label{display:block}
.dv-form__label{
  font-family:var(--dvf-font-mono);
  font-size:.82rem;
  letter-spacing:.06em;
  color:var(--dvf-ink-soft);
}
.dv-form__label span{color:var(--dvf-primary)}
.dv-form__control{
  font-family:var(--dvf-font-mono);
  font-size:1rem;
  padding:10px 14px;
  border:1px solid var(--dvf-line);
  border-radius:var(--dvf-radius);
  background:rgba(0,0,0,.35);
  color:var(--dvf-light);
  width:100%;
  transition:border-color .2s ease, box-shadow .2s ease;
}
input.dv-form__control{min-height:3.2rem}
.dv-form__control:hover{border-color:var(--dvf-primary)}
.dv-form__control:focus{
  outline:none;
  background:rgba(0,0,0,.5);
  border-color:var(--dvf-primary);
  box-shadow:0 0 0 1px var(--dvf-primary);
}
textarea.dv-form__control{min-height:8rem;resize:vertical}
.dv-form__check{width:auto;min-height:0;margin:0;accent-color:var(--dvf-primary);cursor:pointer}
.dv-form__field--radio{border:0;margin:0;padding:0;display:grid;gap:.5rem;min-width:0}
.dv-form__field--radio legend{
  padding:0;
  font-family:var(--dvf-font-mono);
  font-size:.82rem;
  letter-spacing:.06em;
  color:var(--dvf-ink-soft);
}
.dv-form__radio{
  display:flex;
  align-items:center;
  gap:.5rem;
  font-family:var(--dvf-font-mono);
  font-size:.85rem;
  color:var(--dvf-light);
  cursor:pointer;
}
.dv-form__radio input{accent-color:var(--dvf-primary);margin:0;min-height:0;width:auto}
.dv-form__status{
  font-family:var(--dvf-font-mono);
  font-size:.85rem;
  line-height:1.5;
  padding:.75rem .9rem;
  border:1px solid var(--dvf-line);
  border-radius:var(--dvf-radius);
  background:var(--dvf-surface);
  color:var(--dvf-light);
}
.dv-form__status[hidden]{display:none}
.dv-form input[type="checkbox"],
.dv-form input[type="radio"]{width:auto;min-height:0;accent-color:var(--dvf-primary)}
.dv-form input:hover,
.dv-form textarea:hover,
.dv-form select:hover{border-color:var(--dvf-primary)}
.dv-form input:focus,
.dv-form textarea:focus,
.dv-form select:focus{
  outline:none;
  background:rgba(0,0,0,.5);
  border-color:var(--dvf-primary);
  box-shadow:0 0 0 1px var(--dvf-primary);
}
.dv-form select{appearance:none;background-image:none}
.dv-form select option{background:var(--dvf-darker);color:var(--dvf-light)}

/* Lahetysnappi = .contact-form button: lapinakyva, violetti reunus,
   taytto vasta hoverilla. */
.dv-form__submit,
.dv-form button{
  justify-self:start;
  display:inline-flex;
  align-items:center;
  justify-content:center;
  gap:.55rem;
  font-family:var(--dvf-font-mono);
  font-weight:500;
  font-size:.92rem;
  letter-spacing:var(--dvf-mono-track);
  line-height:1.3;
  text-transform:lowercase;
  padding:.75rem 1.4rem;
  min-height:44px;
  border:1px solid var(--dvf-primary);
  border-radius:var(--dvf-radius);
  background:transparent;
  color:var(--dvf-light);
  cursor:pointer;
  transition:background .2s ease, color .2s ease, border-color .2s ease, box-shadow .2s ease;
}
.dv-form__submit:hover,
.dv-form__submit:focus,
.dv-form button:hover,
.dv-form button:focus{
  background:var(--dvf-primary);
  color:var(--dvf-primary-contrast);
  border-color:var(--dvf-primary);
  box-shadow:0 0 24px var(--dvf-glow);
}

/* --------------------------------------------------------- Alatunniste */
/* style.scss .footer / .footer-row */

.dv-footer{
  margin-top:0;
  background:var(--dvf-darker);
  border-top:1px solid var(--dvf-line-soft);
  color:var(--dvf-ink-faint);
}
.dv-footer__inner{
  max-width:var(--dvf-shell-max);
  margin-inline:auto;
  padding:1.4rem var(--dvf-gutter);
  display:flex;
  flex-wrap:wrap;
  align-items:center;
  justify-content:space-between;
  gap:.6rem 1rem;
  font-size:.82rem;
}
.dv-footer p{margin:0}
.dv-footer__legal{
  font-family:var(--dvf-font-mono);
  letter-spacing:.02em;
  color:var(--dvf-ink-faint);
  margin:0;
}
/* Tietosuojalinkilla ei ole omaa luokkaa (shell: DOM-sopimus). */
.dv-footer a{
  color:var(--dvf-light);
  text-decoration:underline;
  text-underline-offset:3px;
}
.dv-footer a:hover{color:var(--dvf-primary)}
.dv-footer ul{list-style:none;margin:0;padding:0;display:flex;flex-wrap:wrap;gap:.4rem 1rem}

/* ==================================================== dvf — Runko ====== */

/* .container */
.dvf-shell{
  width:100%;
  max-width:var(--dvf-shell-max);
  padding-left:var(--dvf-gutter);
  padding-right:var(--dvf-gutter);
  margin-left:auto;
  margin-right:auto;
}

.dvf-section{
  padding-block:var(--dvf-section-pad);
  border-top:1px solid var(--dvf-line-soft);
}
.dvf-section:first-child{border-top:0}

/* .section-head */
.dvf-section__head{
  margin-bottom:clamp(1.8rem, 4vw, 2.6rem);
  max-width:42rem;
}

/* .kicker — koko kicker on violetti, ja SPAN (pelkka numero) on himmea.
   Katetaan seka paljas span etta .dvf-section__num; jos markupissa on
   viela erillinen .dvf-section__name, se pysyy violettina. */
.dvf-section__eyebrow{
  display:inline-flex;
  align-items:center;
  flex-wrap:wrap;
  gap:.55rem;
  font-family:var(--dvf-font-mono);
  font-size:.78rem;
  letter-spacing:.16em;
  text-transform:uppercase;
  color:var(--dvf-primary);
  margin:0 0 .9rem;
  overflow-wrap:anywhere;
}
/* Span ymparoi VAIN numeron; " / Teknologiat" on paljasta tekstia. */
.dvf-section__num{color:var(--dvf-ink-faint)}

.dvf-section__title{
  font-family:var(--dvf-font-display);
  font-size:clamp(1.6rem, 4vw, 3rem);
  font-weight:700;
  letter-spacing:-.035em;
  line-height:1.1;
  color:var(--dvf-header);
  /* Otsikon ja leadin vali .8rem kuten lahteessa. Arvo on otsikon
     alamarginaalissa eika leadin ylamarginaalissa, jottei tulos riipu
     marginaalien yhteensulautumisesta — .dvf-section__head voi olla grid. */
  margin:0 0 .8rem;
}

/* .section-lead */
.dvf-section__lead{
  color:var(--dvf-ink-soft);
  font-size:1.05rem;
  margin:0 0 clamp(1.8rem, 4vw, 2.6rem);
}
/* Kun kicker+otsikko+lead on kaaritty .dvf-section__headiin, valistys tulee
   kaareelta eika leadilta — muuten se laskettaisiin kahteen kertaan. */
.dvf-section__head .dvf-section__lead{margin-bottom:0}

/* ====================================================== dvf — Hero ===== */

.dvf-hero{
  position:relative;
  /* isolation: pitaa alla olevan ::before-hehkun heron sisalla eika
     paasta sita karkaamaan sivun taustakerrokseen. */
  isolation:isolate;
  min-height:calc(100vh - var(--dvf-nav-h));
  display:flex;
  flex-direction:column;
  justify-content:center;
  padding:clamp(2rem, 6vw, 3.5rem) 0 clamp(3.5rem, 8vw, 5.5rem);
  border-top:0;
  overflow:hidden;
}

/* .grid-fade — koristediv heron alussa (aria-hidden). */
.dvf-grid-fade{
  position:absolute;
  inset:0;
  z-index:-1;
  pointer-events:none;
  background:
    radial-gradient(ellipse 50% 40% at 80% 20%, rgba(203,184,255,.09), transparent 60%),
    radial-gradient(ellipse 40% 30% at 10% 80%, rgba(169,139,255,.08), transparent 60%);
}

/* Otsikko vie koko shellin leveyden; lead + CTA ja koodipaneeli jakavat
   alemman rivin. Nain pitka otsikkosana ei tormaa koodipaneeliin. */
.dvf-hero__grid{
  display:grid;
  grid-template-columns:minmax(0,1fr) minmax(0,1fr);
  column-gap:3rem;
  row-gap:clamp(1.6rem, 4vw, 2.6rem);
  align-items:start;
  position:relative;
}
.dvf-hero__grid>*{min-width:0}

.dvf-hero__head{grid-column:1 / -1}

.dvf-hero__intro{align-self:center;min-width:0}

/* .hero-panel — koodi-ikkuna on koriste (aria-hidden). */
.dvf-hero__panel{position:relative;align-self:center;min-width:0}

/* .ty — terminaaliruutu. HUOM: lahteen .ty-line{opacity:0} on
   typewriter-JS:n varassa; ilman JS:aa se piilottaisi tekstin kokonaan,
   joten sita EI tuoda tanne. */
.dvf-hero__term{
  font-family:var(--dvf-font-mono);
  font-size:.85rem;
  line-height:1.75;
  /* Lahteen .ty: kiintea korkeus varaa tilan kirjoittuvalle tekstille,
     jotta otsikko ei hyppaa animaation aikana. */
  min-height:5.2rem;
  color:var(--dvf-ink-soft);
  margin:0 0 1.4rem;
  overflow-wrap:anywhere;
}
.dvf-term__cmd{color:var(--dvf-ink-soft);margin:0;min-height:1.35em}
/* .ty-prompt — pelkka "$" on violetti, komento itse tavallista tekstia. */
.dvf-term__prompt{color:var(--dvf-primary)}
.dvf-term__out{color:var(--dvf-ink-soft);margin:0;min-height:1.35em}

/* Konekirjoitusanimaatio (lahde: TypeLines + .ty-caret).
   PERIAATE: teksti on aina DOMissa ja nakyvissa. Rivit piilotetaan vasta
   kun animaatiot/component.js on paattanyt ajaa animaation ja lisannyt
   .is-typing-luokan — ilman JS:aa tai reduced motionilla teksti nakyy
   sellaisenaan. Lahteen .ty-line{opacity:0} olisi piilottanut sen. */
.dvf-hero__term.is-typing .dvf-term__cmd,
.dvf-hero__term.is-typing .dvf-term__out{opacity:0}
.dvf-hero__term.is-typing .is-on{opacity:1}

.dvf-term__caret{
  display:inline-block;
  width:.55em;
  height:1em;
  margin-left:2px;
  background:var(--dvf-primary);
  vertical-align:text-bottom;
  animation:dvfCaret 1s steps(1) infinite;
}

@keyframes dvfCaret{
  0%,49%{opacity:1}
  50%,100%{opacity:0}
}

.dvf-hero__title{
  font-family:var(--dvf-font-display);
  font-size:clamp(2.1rem, 6vw, 4.6rem);
  font-weight:800;
  letter-spacing:-.035em;
  line-height:1;
  color:var(--dvf-header);
  margin:0 0 1.5rem;
  /* "Ohjelmistokehitysta" on yksi 19-merkkinen sana eika mahdu 390px:n
     naytolle. lang="fi" -> selain tavuttaa suomen saantojen mukaan;
     overflow-wrap on varalla jos tavutussanakirjaa ei ole. ALA POISTA. */
  -webkit-hyphens:auto;
  hyphens:auto;
  overflow-wrap:break-word;
}
/* .hero-line / .hero-accent */
.dvf-hero__line{display:block}
.dvf-hero__accent{
  display:block;
  color:var(--dvf-ink-soft);
  font-weight:600;
}

.dvf-hero__lead{
  max-width:36rem;
  color:var(--dvf-ink-soft);
  font-size:1.12rem;
  margin:0 0 1.6rem;
}

/* .availability-badge — herossa pelkka piste + teksti ilman laatikkoa.
   Piste on ::before-pseudo, ei markup-elementti.
   Lahteessa pisteessa ei ole animaatiota. */
/* .availability-badge — herossa pelkka piste + teksti ilman laatikkoa.
   Piste on ::before-pseudo, ei markup-elementti (lahteessakaan ei ole).
   Lahteessa pisteessa ei ole animaatiota. */
.dvf-badge{
  display:inline-flex;
  align-items:center;
  gap:.55rem;
  margin:0 0 1.1rem;
  font-family:var(--dvf-font-mono);
  color:var(--dvf-warning);
  font-size:.76rem;
  letter-spacing:.14em;
  text-transform:uppercase;
}
.dvf-badge::before{
  content:"";
  width:.5rem;
  height:.5rem;
  border-radius:50%;
  background:currentColor;
  flex:0 0 auto;
}


/* .hero-cta */
.dvf-hero__actions{
  display:flex;
  flex-wrap:wrap;
  gap:.7rem;
}

/* .hero-status — "devella.fi · Kokkola, FI" omassa .dvf-shellissaan
   gridin alla. */
.dvf-hero__meta{
  font-family:var(--dvf-font-mono);
  margin:clamp(2rem, 5vw, 3rem) 0 0;
  color:var(--dvf-ink-faint);
  font-size:.75rem;
  letter-spacing:.12em;
  text-transform:uppercase;
  overflow-wrap:anywhere;
}

/* .scroll-cue + .chevron */
.dvf-scroll-cue{
  position:absolute;
  left:50%;
  bottom:1.4rem;
  transform:translateX(-50%);
  color:var(--dvf-light);
  opacity:.7;
  /* Kosketuskohde: pelkka 0.7rem chevron on liian pieni osoitinlaitteelle. */
  display:grid;
  place-items:center;
  width:44px;
  height:44px;
}
.dvf-scroll-cue:hover,
.dvf-scroll-cue:focus{color:var(--dvf-primary);opacity:1}
.dvf-chevron{
  display:block;
  width:.7rem;
  height:.7rem;
  border-right:1px solid currentColor;
  border-bottom:1px solid currentColor;
  transform:rotate(45deg);
  animation:dvfScrollPulse 2s ease-in-out infinite;
}
@keyframes dvfScrollPulse{
  0%,100%{transform:rotate(45deg) translate(0,0);opacity:.9}
  50%{transform:rotate(45deg) translate(5.6px,5.6px);opacity:.35}
}

/* ===================================================== dvf — Napit ===== */
/* .btn — gemenat tulevat tasta, eivat markupista. */

.dvf-btn{
  display:inline-flex;
  align-items:center;
  justify-content:center;
  gap:.55rem;
  position:relative;
  font-family:var(--dvf-font-mono);
  font-weight:500;
  font-size:.92rem;
  letter-spacing:var(--dvf-mono-track);
  line-height:1.3;
  text-transform:lowercase;
  text-decoration:none;
  padding:.75rem 1.4rem;
  /* Kosketuskohteen vahimmaiskoko. */
  min-height:44px;
  max-width:100%;
  white-space:normal;
  border:1px solid transparent;
  border-radius:var(--dvf-radius);
  background:transparent;
  color:var(--dvf-light);
  cursor:pointer;
  transition:background .2s ease, color .2s ease, border-color .2s ease, box-shadow .2s ease;
}

/* Heron paa-CTA = lahteen .btn-secondary: taytetty violetti, joka
   TYHJENEE hoverilla. */
.dvf-btn--secondary{
  background:var(--dvf-primary);
  color:var(--dvf-primary-contrast);
  border-color:var(--dvf-primary);
}
.dvf-btn--secondary:hover,
.dvf-btn--secondary:focus{
  background:transparent;
  color:var(--dvf-primary);
  border-color:var(--dvf-primary);
}

/* .btn-ghost */
.dvf-btn--ghost{
  background:transparent;
  color:var(--dvf-light);
  border-color:var(--dvf-line);
}
.dvf-btn--ghost:hover,
.dvf-btn--ghost:focus{
  color:var(--dvf-primary);
  border-color:var(--dvf-primary);
}

/* .arr — nuoli omana spaninaan, liukuu oikealle hoverilla. */
.dvf-arr{
  font-family:var(--dvf-font-mono);
  transition:transform .2s var(--dvf-ease-out);
}
.dvf-btn:hover .dvf-arr,
.dvf-btn:focus .dvf-arr,
a:hover .dvf-arr{transform:translateX(3px)}

/* ============================================== dvf — Koodi-ikkuna ===== */
/* .code-frame / .code-chrome. Sama kehys myos lomakkeelle (.form-frame). */

.dvf-code-frame,
.dvf-form-frame{
  border:1px solid var(--dvf-line);
  background:var(--dvf-window-bg);
  box-shadow:0 0 0 1px rgba(169,139,255,.05), 0 24px 60px rgba(0,0,0,.35);
  min-width:0;
}

.dvf-code-chrome{
  display:flex;
  align-items:center;
  gap:.4rem;
  padding:.65rem .9rem;
  border-bottom:1px solid var(--dvf-line);
}

/* Liikennevalot: kolme paljasta spania palkissa. Tiedostonimi on <em>,
   joten se ei osu naihin. */
.dvf-code-chrome>span{
  width:.55rem;
  height:.55rem;
  border-radius:50%;
  background:var(--dvf-line);
  display:inline-block;
  flex:0 0 auto;
}
.dvf-code-chrome>span:nth-child(1){background:var(--dvf-dot-red)}
.dvf-code-chrome>span:nth-child(2){background:var(--dvf-dot-amber)}
.dvf-code-chrome>span:nth-child(3){background:var(--dvf-dot-green)}

/* Tiedostonimi oikeaan reunaan.
   POIKKEAMA: lahteessa vari on --ink-faint (.46); tassa .55 (ks. yllä). */
.dvf-code-chrome__name{
  margin-left:auto;
  font-family:var(--dvf-font-mono);
  font-style:normal;
  color:var(--dvf-ink-faint);
  font-size:.72rem;
  white-space:nowrap;
}


/* .code-frame pre — rivittyy, ei vierity: sivu ei saa vuotaa vaakaan. */
.dvf-code{
  margin:0;
  padding:1.2rem 1.1rem 1.4rem;
  font-family:var(--dvf-font-mono);
  color:var(--dvf-light);
  font-size:.82rem;
  line-height:1.55;
  background:transparent;
  white-space:pre-wrap;
  overflow-wrap:anywhere;
  max-width:100%;
  tab-size:2;
}
.dvf-code__k{color:var(--dvf-code-key)}
.dvf-code__s{color:var(--dvf-code-str)}
.dvf-code__p{color:var(--dvf-code-prop)}

/* ============================================ dvf — Teknologiakortit == */
/* .skill-grid / .skill-card / .skill-index / .skill-label / .skill-note */

.dvf-grid{
  display:grid;
  grid-template-columns:repeat(auto-fit, minmax(15rem,1fr));
  gap:1rem;
}
.dvf-grid>*{min-width:0}

.dvf-card{
  display:flex;
  flex-direction:column;
  min-width:0;
  padding:1.3rem 1.3rem 1.5rem;
  border:1px solid var(--dvf-line-soft);
  border-radius:0;
  background:var(--dvf-surface);
  transition:border-color .25s ease, background .25s ease;
}
.dvf-card:hover{border-color:var(--dvf-line);background:var(--dvf-surface-strong)}
.dvf-card__num{
  font-family:var(--dvf-font-mono);
  color:var(--dvf-ink-faint);
  font-size:.72rem;
  letter-spacing:.02em;
  margin:0 0 .6rem;
}
.dvf-card__title{
  font-family:var(--dvf-font-display);
  font-size:1.15rem;
  font-weight:700;
  letter-spacing:-.035em;
  line-height:1.25;
  color:var(--dvf-header);
  margin:0 0 .3rem;
}
.dvf-card__desc{
  color:var(--dvf-ink-soft);
  font-size:.92rem;
  margin:0 0 1rem;
}

/* .skill-items */
.dvf-tags{
  display:flex;
  flex-wrap:wrap;
  gap:.4rem;
  list-style:none;
  padding:0;
  margin:0;
}
.dvf-tag{
  font-family:var(--dvf-font-mono);
  font-size:.72rem;
  letter-spacing:.02em;
  color:var(--dvf-ink-soft);
  background:transparent;
  border:1px solid var(--dvf-line-soft);
  border-radius:0;
  margin:0;
  padding:.2rem .5rem;
}

/* ================================================== dvf — Palvelut ==== */
/* .stack-list / .stack-row / .stack-index / .stack-title / .stack-copy */

.dvf-svc-list{
  display:flex;
  flex-direction:column;
  border-top:1px solid var(--dvf-line-soft);
  margin:0;
  padding:0;
  list-style:none;
}

.dvf-svc{
  display:grid;
  grid-template-columns:5.5rem minmax(0,1fr);
  gap:1.2rem;
  padding:1.8rem 0;
  border-bottom:1px solid var(--dvf-line-soft);
  transition:background .25s ease;
}
.dvf-svc>*{min-width:0}
.dvf-svc:hover{background:var(--dvf-surface)}
.dvf-svc:hover .dvf-svc__num{color:var(--dvf-primary)}

.dvf-svc__num{
  font-family:var(--dvf-font-mono);
  font-size:1.1rem;
  letter-spacing:.02em;
  color:var(--dvf-ink-faint);
  margin:.2rem 0 0;
  transition:color .25s ease;
}
.dvf-svc__body{min-width:0}
.dvf-svc__title{
  font-family:var(--dvf-font-display);
  font-size:clamp(1.2rem, 2vw, 1.6rem);
  font-weight:700;
  letter-spacing:-.035em;
  line-height:1.25;
  color:var(--dvf-header);
  margin:0 0 .5rem;
}
.dvf-svc__desc{
  color:var(--dvf-ink-soft);
  max-width:46rem;
  margin:0;
}

/* .stack-points */
.dvf-svc__list{
  list-style:none;
  padding:0;
  margin:.9rem 0 0;
  color:var(--dvf-ink-soft);
  max-width:46rem;
}
.dvf-svc__list li{
  position:relative;
  padding-left:1.2rem;
  margin-bottom:.25rem;
  font-size:.98rem;
}
.dvf-svc__list li::before{
  content:"\2192";
  position:absolute;
  left:0;
  color:var(--dvf-primary);
  font-family:var(--dvf-font-mono);
}

/* .stack-cta */
.dvf-svc__cta{margin-top:1.1rem}

/* .stack-row.is-secondary — kotisivut ja verkkokaupat ovat sivujuonne:
   sama rakenne, pienempi visuaalinen paino. */
.dvf-svc--secondary .dvf-svc__title{
  font-size:1.05rem;
  color:var(--dvf-ink-soft);
}
.dvf-svc--secondary .dvf-svc__desc{font-size:.95rem}

/* =================================================== dvf — Yhteys ===== */

/* .contact-section — osio tummenee alaspain. */
.dvf-section--contact{
  background:linear-gradient(180deg, transparent, rgba(0,0,0,.28));
}

/* .availability-note — YKSI kappale: label-span + paljas huomautusteksti.
   Reunus on taysi --dvf-warning, ei pehmennetty. Tekstille ei ole omaa
   elementtia, joten se tyylitellaan kappaleella itsellaan. */
.dvf-notice{
  margin:0 0 clamp(1.5rem, 4vw, 2.2rem);
  padding:.9rem 1.1rem;
  border:1px solid var(--dvf-warning);
  border-radius:0;
  background:var(--dvf-warning-soft);
  color:var(--dvf-ink-soft);
  font-size:.95rem;
}
.dvf-notice__label{
  display:block;
  margin:0 0 .3rem;
  font-family:var(--dvf-font-mono);
  color:var(--dvf-warning);
  font-size:.76rem;
  letter-spacing:.14em;
  text-transform:uppercase;
}

.dvf-contact{
  display:grid;
  grid-template-columns:minmax(0,.9fr) minmax(0,1.1fr);
  gap:clamp(1.5rem, 4vw, 2.4rem);
  align-items:start;
}
.dvf-contact>*{min-width:0}

.dvf-contact__info{min-width:0}

/* .company-card / .company-name / .company-address */
.dvf-company{color:var(--dvf-ink-soft);margin-bottom:1.6rem}
.dvf-company p{margin-bottom:.1rem}
.dvf-company__name{
  font-family:var(--dvf-font-display);
  font-size:1.45rem;
  font-weight:700;
  letter-spacing:-.035em;
  color:var(--dvf-header);
  margin:0 0 .35rem;
}
.dvf-company__address,
.dvf-company__businessid{
  font-family:var(--dvf-font-mono);
  font-size:.88rem;
  letter-spacing:.02em;
  color:var(--dvf-ink-soft);
  margin:0 0 .1rem;
}

/* .contact-person-label */
.dvf-label{
  font-family:var(--dvf-font-mono);
  color:var(--dvf-primary);
  font-size:.72rem;
  letter-spacing:.16em;
  text-transform:uppercase;
  margin:0 0 .9rem;
  padding-top:1.2rem;
  border-top:1px solid var(--dvf-line-soft);
}

/* .person-card / .person-photo / .person-meta */
.dvf-person{
  display:flex;
  flex-wrap:wrap;
  gap:1rem;
  align-items:center;
  margin-bottom:0;
  min-width:0;
}
/* Valokuvaa ei ole (mediakirjasto tyhja) -> nimikirjainlaatikko samassa
   koossa kuin lahteen .person-photo. */
.dvf-person__avatar{
  flex:0 0 auto;
  width:5.2rem;
  height:5.2rem;
  display:grid;
  place-items:center;
  border:1px solid var(--dvf-line);
  border-radius:2px;
  background:var(--dvf-surface-strong);
  font-family:var(--dvf-font-display);
  font-weight:700;
  font-size:1.35rem;
  letter-spacing:.04em;
  color:var(--dvf-primary);
}
.dvf-person__body{min-width:0}
.dvf-person__body p{margin-bottom:.1rem}
.dvf-person__name{
  font-family:var(--dvf-font-display);
  font-size:1.25rem;
  font-weight:700;
  letter-spacing:-.035em;
  color:var(--dvf-header);
  margin:0 0 .15rem;
}
/* .contact-link */
.dvf-person__row{
  font-family:var(--dvf-font-mono);
  font-size:.88rem;
  letter-spacing:.02em;
  margin:0 0 .1rem;
  overflow-wrap:anywhere;
}
/* .contact-link */
.dvf-contact__link{color:var(--dvf-light);text-decoration:none}
.dvf-contact__link:hover{color:var(--dvf-primary)}

/* .form-frame — yksi sisainen padding otsikosta lomakkeen loppuun.
   .dvf-code-chrome, .dvf-form-title, .dvf-form-lead ja lomakelohko ovat
   kehyksen SUORIA lapsia (kehyksen reunat tulevat .dvf-code-frame-saannosta
   osion alussa). .dvf-form-col on pelkka sarakekaare. */
.dvf-form-col{min-width:0}

.dvf-form-frame{
  --dvf-frame-pad:clamp(1rem, 3vw, 1.5rem);
  padding:0 0 var(--dvf-frame-pad);
}
.dvf-form-title{
  font-family:var(--dvf-font-display);
  font-size:clamp(1.2rem, 2vw, 1.6rem);
  font-weight:700;
  letter-spacing:-.035em;
  line-height:1.25;
  color:var(--dvf-header);
  margin:var(--dvf-frame-pad) var(--dvf-frame-pad) .3rem;
}
.dvf-form-lead{
  margin:0 var(--dvf-frame-pad) 1rem;
  color:var(--dvf-ink-soft);
  font-size:.95rem;
}
.dvf-form-frame .dv-form{
  padding:0 var(--dvf-frame-pad);
  max-width:none;
}

/* =============================================== Evastebanneri ======== */
/* Luokat haettu get_style_targets-tyokalulla. */

.dv-consent{
  position:fixed;
  z-index:120;
  inset:auto 1rem 1rem 1rem;
  margin-inline:auto;
  max-width:min(40rem, calc(100vw - 2rem));
  display:grid;
  gap:1rem;
  padding:1.25rem;
  background:var(--dvf-overlay-strong);
  -webkit-backdrop-filter:blur(14px);
  backdrop-filter:blur(14px);
  border:1px solid var(--dvf-line);
  border-radius:var(--dvf-radius);
  color:var(--dvf-light);
  box-shadow:0 0 0 1px rgba(8,7,12,.6), 0 24px 60px -24px rgba(169,139,255,.3);
}
.dv-consent--bottom{max-width:none;left:1rem;right:1rem}
.dv-consent--bottom-left{margin-inline:0;right:auto;max-width:min(40rem,calc(100vw - 2rem))}
.dv-consent--bottom-right{margin-inline:0;left:auto;max-width:min(40rem,calc(100vw - 2rem))}
.dv-consent--center{inset:1rem;margin:auto;height:fit-content}

.dv-consent__body{display:grid;gap:.5rem}
.dv-consent__title{
  font-family:var(--dvf-font-display);
  font-weight:700;
  font-size:1.05rem;
  letter-spacing:-.035em;
  color:var(--dvf-header);
  margin:0;
}
.dv-consent__text{font-size:.92rem;color:var(--dvf-ink-soft);margin:0}
.dv-consent__link{
  font-family:var(--dvf-font-mono);
  font-size:.8rem;
  letter-spacing:var(--dvf-mono-track);
  color:var(--dvf-primary);
  justify-self:start;
}

.dv-consent__cats{
  display:grid;
  gap:.75rem;
  padding-top:.85rem;
  border-top:1px solid var(--dvf-line-soft);
}
.dv-consent__cats[hidden]{display:none}
.dv-consent__cat{display:flex;align-items:flex-start;gap:.65rem;cursor:pointer}
.dv-consent__cat-input{
  flex:none;
  margin:.2rem 0 0;
  width:1rem;height:1rem;
  accent-color:var(--dvf-primary);
  cursor:pointer;
}
.dv-consent__cat-text{display:grid;gap:.15rem;min-width:0}
.dv-consent__cat-label{
  font-family:var(--dvf-font-mono);
  font-size:.85rem;
  letter-spacing:var(--dvf-mono-track);
  color:var(--dvf-light);
}
.dv-consent__cat-desc{font-size:.82rem;color:var(--dvf-ink-faint)}

.dv-consent__actions{
  display:flex;
  flex-wrap:wrap;
  gap:.6rem;
  justify-content:flex-end;
  align-items:center;
}
/* Napit pitavat luontaisen leveytensa. flex-grow venyttaisi eri mittaiset
   napit riville ja tyontaisi viimeisen omalle rivilleen. */
.dv-consent__actions .dv-consent__btn{flex:0 0 auto}
.dv-consent__btn{
  font-family:var(--dvf-font-mono);
  font-size:.82rem;
  letter-spacing:var(--dvf-mono-track);
  line-height:1;
  text-transform:lowercase;
  padding:.65rem 1.05rem;
  border:1px solid var(--dvf-line);
  border-radius:var(--dvf-radius);
  background:transparent;
  color:var(--dvf-light);
  cursor:pointer;
  transition:background-color .18s ease, color .18s ease, border-color .18s ease;
}
.dv-consent__btn:hover{border-color:var(--dvf-primary);color:var(--dvf-primary)}
.dv-consent__btn[hidden]{display:none}
.dv-consent__btn--primary{
  background:var(--dvf-primary);
  border-color:var(--dvf-primary);
  color:var(--dvf-primary-contrast);
}
.dv-consent__btn--primary:hover{
  background:var(--dvf-accent);
  border-color:var(--dvf-accent);
  color:var(--dvf-primary-contrast);
}
/* Ghost-nappi ("muokkaa asetuksia") vasempaan reunaan, hyvaksynta oikeaan —
   kuten devella.fi:ssa. Kaareytyneena tama on vaaraton. */
.dv-consent__actions .dv-consent__btn--ghost{margin-right:auto}
.dv-consent__btn--ghost{
  background:transparent;
  border-color:var(--dvf-line-soft);
  color:var(--dvf-ink-soft);
}
.dv-consent__btn--ghost:hover{border-color:var(--dvf-primary);color:var(--dvf-primary)}

/* ================================================== Responsiivisuus === */

/* Koodipaneeli piiloon ennen kuin siita tulee liian kapea luettavaksi
   (style.scss: 991.98px). Se on aria-hidden-koriste, joten mitaan
   sisaltoa ei katoa. */
@media screen and (max-width:991.98px){
  .dvf-hero__grid{grid-template-columns:minmax(0,1fr)}
  .dvf-hero__panel{display:none}
}

@media screen and (max-width:768px){
  .dvf-hero{min-height:0}
  .dvf-scroll-cue{display:none}
  .dvf-contact{grid-template-columns:minmax(0,1fr)}

  /* Mobiilivalikko: alustan toggle + pudotuspaneeli. */
  .dv-header__toggle{
    display:block;
    position:relative;
    flex:0 0 auto;
    margin-left:auto;
    width:2.6rem;
    height:2.6rem;
    padding:0;
    border:1px solid var(--dvf-line);
    border-radius:var(--dvf-radius);
    background:transparent;
    color:var(--dvf-light);
    cursor:pointer;
  }
  .dv-header__toggle span{
    display:block;
    width:1.5rem;
    height:1px;
    margin:5px auto;
    background:currentColor;
    transition:transform .25s var(--dvf-ease-out), opacity .2s ease;
  }
  .dv-header__toggle:hover,
  .dv-header__toggle[aria-expanded="true"]{border-color:var(--dvf-primary);color:var(--dvf-primary)}

  /* Hampurilainen ristiksi (lahde: navbar.scss .site-nav.is-open
     .nav-toggle span). Palkkien pystyaskel on 1px korkeus + 5px marginaali
     ylos ja alas = 11px, joten ylin ja alin siirtyvat keskelle sen verran. */
  .dv-header__toggle[aria-expanded="true"] span:nth-child(1){transform:translateY(6px) rotate(45deg)}
  .dv-header__toggle[aria-expanded="true"] span:nth-child(2){opacity:0}
  .dv-header__toggle[aria-expanded="true"] span:nth-child(3){transform:translateY(-6px) rotate(-45deg)}

  /* Scrim paneelin alle. Ilman sita avoin valikko nayttaa silta kuin sivu
     olisi jumissa: body on lukittu mutta sisalto nakyy kirkkaana. */
  .dv-header.is-open::after{
    content:"";
    position:fixed;
    inset:var(--dvf-nav-h) 0 0 0;
    background:rgba(5,4,9,.72);
    z-index:-1;
    pointer-events:none;
  }

  .dv-header__nav{
    display:none;
    width:100%;
    position:absolute;
    top:100%;
    left:0;
    right:0;
    margin-left:0;
    max-height:calc(100vh - var(--dvf-nav-h));
    overflow-y:auto;
    -webkit-overflow-scrolling:touch;
    padding:.5rem var(--dvf-gutter) 1.5rem;
    background:var(--dvf-dark);
    border-bottom:1px solid var(--dvf-line);
  }
  /* component.js asettaa data-open TYHJANA merkkijonona (setAttribute
     'data-open',''), joten [data-open="true"] EI osuisi koskaan. */
  .dv-header__nav[data-open],
  .dv-header__toggle[aria-expanded="true"]+.dv-header__nav{display:block}

  .dv-header__nav .dv-menu{
    flex-direction:column;
    align-items:stretch;
    flex-wrap:nowrap;
    gap:0;
  }
  .dv-header__nav a{
    display:flex;
    align-items:baseline;
    font-size:1.35rem;
    padding:.9rem 0;
    border-bottom:1px solid var(--dvf-line-soft);
    white-space:normal;
  }
  .dv-header__nav .dv-menu>li>a::before{font-size:.85rem;min-width:2.2rem}
  .dv-header__nav .dv-menu>li:last-child>a{border-bottom:none}
}

@media screen and (max-width:576px){
  .dvf-svc{
    grid-template-columns:minmax(0,1fr);
    gap:.4rem;
    padding:1.4rem 0;
  }
  .dvf-svc__num{margin-top:0}
  .dvf-grid{grid-template-columns:minmax(0,1fr)}
  .dv-footer__inner{justify-content:flex-start}
}

@media screen and (max-width:30rem){
  .dvf-hero__actions .dvf-btn{width:100%}
  .dvf-person{align-items:flex-start}
}

/* Lahteen _animations.scss tekee taman samoin. Kaikki tuodut animaatiot
   (dvfGridPulse, dvfScrollPulse) ja siirtymat nollataan. */
@media (prefers-reduced-motion: reduce){
  html{scroll-behavior:auto}
  *,*::before,*::after{
    animation-duration:.01ms!important;
    animation-iteration-count:1!important;
    transition-duration:.01ms!important;
    scroll-behavior:auto!important;
  }
}


/* Yhteystietojen valokuva: sama 5.2rem-laatikko kuin nimikirjaimilla, kuva täyttää sen. */
img.dvf-person__avatar{
  display:block;
  object-fit:cover;
  object-position:center;
  padding:0;
  font-size:0;
  letter-spacing:normal;
  text-transform:none;
}

/* Korjaus: body::before (ruudukko) ja body::after (CRT-juovat) katosivat,
   koska z-index:-1 ilman pinoamiskontekstia maalaa bodyn peittävän taustan
   TAAKSE. isolation: isolate pitää kerrokset taustan päällä, sisällön alla. */
body {
  isolation: isolate;
}

/* Vahvistus body::before / ::after -taustakerroksille.
   1) Taustavari html:aan, body läpinäkyväksi — ruudukko ei jää
      peittävän body-taustan taakse.
   2) z-index: 0 (ei -1) — kerrokset pysyvät bodyn pinossa taustan päällä.
   3) Suorat lapset z-index: 1 — sisältö, navi ja lomakkeet ruudukon yllä. */
html {
  background-color: var(--dvf-dark);
}
body {
  background-color: transparent;
}
body::before,
body::after {
  z-index: 0;
}
body > * {
  position: relative;
  z-index: 0;
}

/* Laskeutumissivu (esim. /devella-web): sivupohja piirtaa murupolun ja
   h1.dv-page__title:n, mutta hero-sivulla oikea h1 on jo herossa. Lisaksi
   .dv-page:has(.dvf-section) nollaa reunuksen tayslevean osion vuoksi, joten
   nama kaksi liimautuisivat ruudun vasempaan reunaan. Piilotetaan molemmat
   vain nailta sivuilta; etusivu on jo ennestaan ilman niita (page.isHome). */
.dv-page:has(.dvf-hero) > .dv-breadcrumbs,
.dv-page:has(.dvf-hero) > .dv-page__title{
  display:none;
}

/* Kuvakaappausruudukko (esitesivu): sama ikkunakehys kuin koodilaatikolla,
   joten yllapidon kuva istuu samaan visuaaliseen kieleen kuin .dvf-code-frame.
   Kuva on kehyksen suora lapsi, jolloin chrome-palkki jaa sen ylapuolelle. */
.dvf-shots{
  display:grid;
  grid-template-columns:repeat(auto-fit, minmax(24rem,1fr));
  gap:1.5rem;
}
.dvf-shots>*{min-width:0}
.dvf-shot{margin:0}
/* Kuva tulee media-gallery-lohkosta (.dv-gallery > figure > picture > img),
   koska se on ainoa lohko joka renderoi responsiivisen kuvan. Yksi kuva per
   lohko, joten gallerian oma ruudukko puretaan. */
.dvf-shot .dv-gallery{display:block}
.dvf-shot .dv-gallery__item{margin:0}
.dvf-shot .dvf-code-frame img{display:block;width:100%;height:auto}
.dvf-shot__cap{
  margin:.8rem 0 0;
  color:var(--dvf-ink-soft);
  font-size:.95rem;
}


#ominaisuudet .dvf-shots{
  padding-top: calc(var(--dv-space) * 2.5);
}

#ominaisuudet .dvf-shots{
  padding-top: var(--dvf-gutter);
}

/* --- Koko näytön mobiilivalikko ---
   Yliajetaan aiempi pudotuspaneeli: paneeli peittää nyt koko näkymäportin.
   Logo ja hampurilainen nostetaan paneelin päälle, jotta valikon saa suljettua.
   Erillinen scrim piilotetaan, koska paneeli itse on koko ruudun tumma tausta. */
@media screen and (max-width:768px){
  .dv-header__logo,
  .dv-header__toggle{position:relative;z-index:2}
  .dv-header__nav{
    position:fixed;
    top:0;
    left:0;
    right:0;
    z-index:1;
    width:100%;
    max-height:none;
    height:100vh;
    height:100dvh;
    padding:calc(var(--dvf-nav-h) + 1.25rem) var(--dvf-gutter) 2rem;
    border-bottom:none;
  }
  .dv-header.is-open::after{display:none}
}

/* --- Konekirjoitustekstin tilanvaraus (ei layout-hyppyä) ---
   Hero-terminaalin rivit kirjoitetaan merkki kerrallaan. Pitkä output
   (esim. teknologialista) rivittyy kapealla näytöllä usealle riville,
   jolloin hero:n korkeus kasvoi kesken animaation ja sivu hyppäsi.
   Varataan output-riveille kahden rivin tila jo ennen kirjoitusta,
   jotta säiliön korkeus pysyy vakaana alusta loppuun. */
@media screen and (max-width:768px){
  .dvf-hero__term .dvf-term__out{min-height:3.5em}
}
