/* ============================================================
   DYNAMIC ENGINEERING AS — design system
   Ground: low-chroma cool neutral. Structural hue: brand cyan
   #24A2DA (sampled from the corporate mark). Signal: safety amber,
   used only where something is actionable or live.
   ============================================================ */
:root {
  color-scheme: dark;

  /* ---- ground ---- */
  --ink-000:#0B0D11;   /* page */
  --ink-050:#12151B;   /* header, raised band */
  --ink-100:#171B22;   /* card */
  --ink-150:#1E232B;   /* card hover */
  --ink-200:#252A33;   /* hairline */
  --ink-300:#343A45;   /* strong border */

  /* ---- text ---- */
  --text-hi:#F2F3F6;
  --text-mid:#CBD1D9;
  --text-dim:#98A1AD;

  /* ---- structural hue: the brand mark's cyan ---- */
  --brand-300:#6FC2EC;
  --brand-400:#24A2DA;
  --brand-600:#1B6E96;
  --brand-glow:color-mix(in oklab, var(--brand-400) 14%, transparent);
  /* the one blue that is legible as a foreground on white */
  --brand-on-light:#0F5F86;

  /* ---- signal hue ---- */
  --signal-400:#24A2DA;
  --signal-600:#1B6E96;
  --signal-ink:#04121A;

  --ok:#3FCB8E;
  --crit:#F2836F;

  /* ---- type ---- */
  --font-display:"Segoe UI Variable Display",-apple-system,BlinkMacSystemFont,ui-sans-serif,system-ui,"Segoe UI",Roboto,"Helvetica Neue","Noto Sans",Arial,sans-serif;
  --font-text:-apple-system,BlinkMacSystemFont,ui-sans-serif,system-ui,"Segoe UI Variable Text","Segoe UI",Roboto,"Helvetica Neue","Noto Sans",Arial,sans-serif;
  --font-mono:ui-monospace,"SF Mono",SFMono-Regular,"Cascadia Mono","Segoe UI Mono","Roboto Mono",Menlo,Consolas,"Liberation Mono",monospace;

  --fs-2xs:clamp(0.75rem,0.729rem + 0.093vw,0.8125rem);
  --fs-xs:clamp(0.8125rem,0.792rem + 0.093vw,0.875rem);
  --fs-sm:clamp(0.875rem,0.854rem + 0.093vw,0.9375rem);
  --fs-base:clamp(1rem,0.979rem + 0.093vw,1.0625rem);
  --fs-md:clamp(1.0625rem,1.021rem + 0.185vw,1.1875rem);
  --fs-lg:clamp(1.1875rem,1.125rem + 0.278vw,1.375rem);
  --fs-xl:clamp(1.375rem,1.292rem + 0.370vw,1.625rem);
  --fs-2xl:clamp(1.625rem,1.5rem + 0.556vw,2rem);
  --fs-3xl:clamp(1.875rem,1.625rem + 1.111vw,2.625rem);
  --fs-4xl:clamp(2.25rem,1.75rem + 2.222vw,3.75rem);
  --fs-5xl:clamp(2.5rem,1.7rem + 3.556vw,4.75rem);

  --tr-display:-0.033em;
  --tr-h2:-0.026em;
  --tr-h3:-0.019em;
  --tr-h4:-0.011em;
  --tr-label:0.12em;
  --tr-tag:0.04em;

  --lh-display:1.03;
  --lh-h2:1.12;
  --lh-h3:1.22;
  --lh-lead:1.5;
  --lh-body:1.62;

  /* ---- space / shape ---- */
  --section-y:clamp(3rem,2.25rem + 3.5vw,5.5rem);
  --gutter:clamp(1.25rem,0.85rem + 1.85vw,2.5rem);
  --maxw:1240px;
  --maxw-text:70ch;
  --r-card:6px;
  --r-ctl:4px;

  --dur:.42s;
  --ease:cubic-bezier(.16,1,.3,1);
}

*,*::before,*::after{box-sizing:border-box;}
*{margin:0;}

html{
  font-family:var(--font-text);
  font-optical-sizing:auto;
  font-synthesis:none;
  -webkit-text-size-adjust:100%;
  scrollbar-color:var(--ink-300) var(--ink-000);
  overflow-x:clip;
}
body{
  background:var(--ink-000);
  color:var(--text-mid);
  font-size:var(--fs-base);
  line-height:var(--lh-body);
  -webkit-font-smoothing:antialiased;
}
img,video,svg{display:block;max-width:100%;}
img,video{height:auto;}
a{color:inherit;text-decoration:none;}
button{font:inherit;color:inherit;background:none;border:0;cursor:pointer;}
h1,h2,h3,h4{
  font-family:var(--font-display);
  color:var(--text-hi);
  font-weight:600;
  text-wrap:balance;
}
p{text-wrap:pretty;}

::selection{background:var(--signal-400);color:var(--signal-ink);}
:root{accent-color:var(--brand-400);caret-color:var(--signal-400);}

:focus-visible{
  outline:2px solid var(--brand-300);
  outline-offset:3px;
  border-radius:var(--r-ctl);
}

.skip-link{
  position:absolute;left:var(--gutter);top:-100px;z-index:200;
  background:var(--signal-400);color:var(--signal-ink);
  padding:.75rem 1.15rem;border-radius:var(--r-ctl);
  font-weight:600;font-size:var(--fs-sm);
  transition:top .18s ease;
}
.skip-link:focus{top:1rem;}

.wrap{width:100%;max-width:var(--maxw);margin-inline:auto;padding-inline:var(--gutter);}
section{padding-block:var(--section-y);position:relative;}
.band{background:var(--ink-050);border-block:1px solid var(--ink-200);}

/* ---- the instrument label: mono, uppercase, indexed ---- */
.eyebrow{
  display:flex;align-items:center;gap:.7rem;
  font-family:var(--font-mono);
  font-size:var(--fs-2xs);
  font-weight:500;
  letter-spacing:var(--tr-label);
  text-transform:uppercase;
  color:var(--text-dim);
  margin-bottom:1.25rem;
}
.eyebrow::before{
  content:attr(data-index);
  color:var(--brand-400);
  font-weight:700;
}
.eyebrow::after{
  content:"";flex:1;height:1px;background:var(--ink-200);
  max-width:120px;
}

.s-head{max-width:var(--maxw-text);margin-bottom:clamp(1.75rem,1.25rem + 1.6vw,2.75rem);}
.s-title{
  font-size:var(--fs-3xl);
  line-height:var(--lh-h2);
  letter-spacing:var(--tr-h2);
  margin-left:-0.02em;
}
.s-deck{
  font-size:var(--fs-lg);
  line-height:var(--lh-lead);
  color:var(--text-dim);
  margin-top:1rem;
  max-width:52ch;
}

.mono{font-family:var(--font-mono);font-variant-numeric:tabular-nums slashed-zero;}

/* ---- reveal: default state is FINAL. Motion is opt-in. ----
   The hidden state is scoped to html.js: only script can restore it, so without
   script (or if site.js fails to load) the page renders fully instead of blank. */
.reveal{opacity:1;transform:none;}
@media (prefers-reduced-motion:no-preference){
  .js .reveal{
    opacity:0;transform:translateY(14px);
    transition:opacity var(--dur) var(--ease),transform var(--dur) var(--ease);
  }
  .js .reveal.is-in{opacity:1;transform:none;}
  html{scroll-behavior:smooth;}}
html{scroll-padding-top:96px;}

/* ---- buttons ---- */
.btn{
  display:inline-flex;align-items:center;justify-content:center;gap:.55rem;
  min-height:48px;padding:.8rem 1.5rem;
  border-radius:var(--r-ctl);
  font-size:var(--fs-sm);font-weight:600;
  letter-spacing:.005em;
  white-space:nowrap;
  border:1px solid transparent;
  transition:background .15s ease,border-color .15s ease,color .15s ease,transform .15s ease;
}
.btn-primary{background:var(--signal-400);color:var(--signal-ink);}
.btn-ghost{border-color:var(--ink-300);color:var(--text-hi);}
@media (hover:hover){
  .btn-primary:hover{background:var(--signal-600);color:#fff;transform:translateY(-1px);}
  .btn-ghost:hover{border-color:var(--brand-400);color:var(--brand-300);background:var(--brand-glow);transform:translateY(-1px);}}
/* ============================ NAV ============================ */
.nav{
  position:fixed;inset-block-start:0;inset-inline:0;z-index:100;
  border-bottom:1px solid transparent;
  transition:background .2s ease,border-color .2s ease,backdrop-filter .2s ease;
}
.nav.is-stuck{
  background:color-mix(in oklab,var(--ink-050) 88%,transparent);
  backdrop-filter:blur(14px) saturate(150%);
  border-bottom-color:var(--ink-200);
}
@media (prefers-reduced-transparency:reduce){
  .nav.is-stuck{background:var(--ink-050);backdrop-filter:none;}}
.nav-inner{display:flex;align-items:center;justify-content:space-between;gap:1rem;min-height:72px;}
.brand{display:flex;align-items:center;gap:.7rem;min-height:44px;}
.brand img{width:34px;height:36px;}
.brand-name{
  display:block;
  font-family:var(--font-display);
  font-size:var(--fs-sm);font-weight:600;
  letter-spacing:.1em;text-transform:uppercase;
  color:var(--text-hi);line-height:1.15;
}
.brand-sub{
  display:block;
  font-family:var(--font-mono);
  font-size:10px;letter-spacing:.14em;text-transform:uppercase;
  color:var(--text-dim);margin-top:3px;
}
/* On the narrowest phones the tagline wraps to two lines and doubles the header
   height for no gain; the wordmark alone carries it. */
@media (max-width:359px){
  .brand-sub{display:none;}
}
.nav-links{display:flex;align-items:center;gap:.35rem;}
.nav-links a{
  position:relative;
  white-space:nowrap;
  display:inline-flex;align-items:center;min-height:44px;
  padding:.35rem .7rem;
  font-size:var(--fs-sm);color:var(--text-mid);
  border-radius:var(--r-ctl);
  transition:color .15s ease,background .15s ease;
}
@media (hover:hover){.nav-links a:hover{color:var(--text-hi);background:var(--ink-100);}}
.nav-links a::after{
  content:"";position:absolute;left:.7rem;right:.7rem;bottom:.3rem;height:1.5px;
  background:var(--brand-400);border-radius:2px;
  transform:scaleX(0);transform-origin:left;
  transition:transform .2s var(--ease);
}
.nav-links a:hover::after,.nav-links a:focus-visible::after{transform:scaleX(1);}
.nav-right-group{display:flex;align-items:center;gap:.9rem;}
.lang-switch{
  display:inline-flex;align-items:center;justify-content:center;
  min-width:44px;min-height:44px;
  font-family:var(--font-mono);font-size:var(--fs-2xs);font-weight:600;
  letter-spacing:var(--tr-tag);color:var(--text-dim);
  border:1px solid var(--ink-300);border-radius:var(--r-ctl);
  transition:color .15s ease,border-color .15s ease;
}
@media (hover:hover){.lang-switch:hover{color:var(--brand-300);border-color:var(--brand-400);}}
.nav-toggle{
  display:none;
  width:48px;height:48px;align-items:center;justify-content:center;
  border:1px solid var(--ink-300);border-radius:var(--r-ctl);
  color:var(--text-hi);
}
.nav-toggle svg{width:20px;height:20px;}
.nav-toggle .ico-close{display:none;}
.nav-toggle[aria-expanded="true"] .ico-open{display:none;}
.nav-toggle[aria-expanded="true"] .ico-close{display:block;}

/* mobile drawer */
.drawer{
  position:fixed;inset:0;z-index:99;
  background:var(--ink-000);
  padding:96px var(--gutter) 2rem;
  display:flex;flex-direction:column;gap:.25rem;
  overflow-y:auto;
  visibility:hidden;opacity:0;
  transition:opacity .22s ease,visibility .22s ease;
}
.drawer.is-open{visibility:visible;opacity:1;}
.drawer a{
  display:flex;align-items:center;min-height:56px;
  font-family:var(--font-display);
  font-size:var(--fs-xl);font-weight:600;letter-spacing:var(--tr-h4);
  color:var(--text-hi);
  border-bottom:1px solid var(--ink-200);
}
.drawer a .mono{color:var(--brand-400);font-size:var(--fs-2xs);margin-right:1rem;min-width:2.2em;}
.drawer-meta{margin-top:auto;padding-top:2rem;font-size:var(--fs-sm);color:var(--text-dim);}
.drawer-meta a{display:inline-block;font-size:inherit;font-family:var(--font-text);font-weight:400;border:0;padding-block:.4rem;color:var(--brand-300);}

@media (max-width:920px){
  .nav-links{display:none;}
  .nav-toggle{display:inline-flex;}}

/* ============================ HERO ============================ */
.hero{
  position:relative;
  min-height:clamp(600px,92svh,940px);
  display:flex;align-items:flex-end;
  padding-block:clamp(7rem,6rem + 8vw,10rem) clamp(2.5rem,1.5rem + 4vw,4rem);
  overflow:hidden;
  border-bottom:1px solid var(--ink-200);
}
.hero-media{position:absolute;inset:0;z-index:0;background:var(--ink-000);}
.hero-media img,.hero-media video{
  width:100%;height:100%;object-fit:cover;object-position:center 55%;
}
.hero-media video{position:absolute;inset:0;opacity:0;transition:opacity .8s ease;}
.hero-media video.is-playing{opacity:1;}
.hero-media img{position:absolute;inset:0;}

/* scrim: guarantees AA contrast for the headline over moving footage */
.hero-scrim{
  position:absolute;inset:0;z-index:1;pointer-events:none;
  background:
    linear-gradient(to top,var(--ink-000) 0%,color-mix(in oklab,var(--ink-000) 92%,transparent) 22%,color-mix(in oklab,var(--ink-000) 55%,transparent) 52%,color-mix(in oklab,var(--ink-000) 30%,transparent) 100%),
    linear-gradient(to right,color-mix(in oklab,var(--ink-000) 88%,transparent) 0%,color-mix(in oklab,var(--ink-000) 45%,transparent) 55%,transparent 100%);
}
.hero-grid{
  position:absolute;inset:0;z-index:1;pointer-events:none;
  background-image:
    linear-gradient(to right,rgba(255,255,255,.055) 1px,transparent 1px),
    linear-gradient(to bottom,rgba(255,255,255,.055) 1px,transparent 1px);
  background-size:72px 72px;
  -webkit-mask-image:radial-gradient(ellipse 70% 70% at 20% 80%,#000 0%,transparent 70%);
  mask-image:radial-gradient(ellipse 70% 70% at 20% 80%,#000 0%,transparent 70%);
}
.hero .wrap{position:relative;z-index:2;width:100%;}
.hero h1{
  font-size:var(--fs-5xl);
  line-height:var(--lh-display);
  letter-spacing:var(--tr-display);
  margin-left:-0.02em;
  max-width:25ch;
  text-shadow:0 2px 30px rgba(0,0,0,.5);
}
.hero h1 em{font-style:normal;color:var(--brand-300);}
@media (max-width:560px){
  .hero h1{font-size:clamp(1.85rem,1.15rem + 3vw,2.5rem);max-width:18ch;}}
/* The rotator cell is sized by its longest word, and the Norwegian
   "programvareintegrasjon" is a single unbreakable token: on the narrowest
   phones the headline needs one more step down to stay inside the viewport. */
@media (max-width:359px){
  .hero h1{font-size:1.62rem;}
}
.hero-lead{
  font-size:var(--fs-lg);
  line-height:var(--lh-lead);
  color:var(--text-mid);
  max-width:56ch;margin-top:1.4rem;
  text-shadow:0 1px 20px rgba(0,0,0,.6);
}
.hero-actions{display:flex;flex-wrap:wrap;gap:.85rem;margin-top:2.2rem;}

/* ==================== SECTORS ==================== */
.sector-grid{display:grid;grid-template-columns:repeat(2,1fr);gap:1px;background:var(--ink-200);border:1px solid var(--ink-200);border-radius:var(--r-card);overflow:hidden;}
@media (max-width:760px){.sector-grid{grid-template-columns:1fr;}}
.sector{background:var(--ink-000);padding:1.9rem 1.7rem;display:flex;flex-direction:column;gap:.7rem;}
.sector p{margin-bottom:.2rem;}
.sector h3{font-size:var(--fs-lg);letter-spacing:var(--tr-h4);}
.sector p{font-size:var(--fs-sm);color:var(--text-dim);}
.sector-logos{
  display:flex;flex-wrap:wrap;align-items:center;
  gap:1.2rem 1.6rem;
  margin-top:.5rem;padding-top:1.2rem;
  border-top:1px solid var(--ink-200);
  min-height:64px;
}
/* Every mark is flattened to a single white silhouette. Hover raises opacity
   only — it must NOT restore the original colours: most client logos are dark
   navy or black for white paper, so on this background they go muddy or vanish
   entirely. Uniform white is also the fairer treatment across a client wall. */
.sector-logos img{
  max-height:22px;max-width:126px;width:auto;height:auto;
  opacity:.62;
  filter:grayscale(1) brightness(0) invert(1);
  transition:opacity .22s var(--ease),transform .22s var(--ease);
}
.sector-logos img.lg-md{max-height:30px;max-width:92px;}
.sector-logos img.lg-sq{max-height:44px;max-width:56px;}
/* moelven is already a light wordmark — inverting it doubles the letterforms */
.sector-logos img.lg-light{filter:grayscale(1) brightness(1.1) contrast(1.05);}
.sector-logos img.lg-md{max-height:34px;max-width:88px;}
.sector-logos img.lg-sq{max-height:46px;max-width:58px;}
@media (hover:hover){
  .sector-logos img:hover{opacity:1;transform:translateY(-1px);}
  /* hovering one mark dims its neighbours, so the pointer picks one out
     without any of them changing colour */
  .sector-logos:hover img{opacity:.34;}
  .sector-logos:hover img:hover{opacity:1;}}
@media (prefers-reduced-motion:reduce){.sector-logos img{transition:opacity .22s ease;}
  .sector-logos img:hover{transform:none;}}

/* ==================== PROJECT REGISTER ==================== */
.proj-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:1.25rem;}
@media (max-width:1000px){.proj-grid{grid-template-columns:repeat(2,1fr);}}
@media (max-width:640px){.proj-grid{grid-template-columns:1fr;}}
.proj{
  background:var(--ink-100);border:1px solid var(--ink-200);border-radius:var(--r-card);
  overflow:hidden;display:flex;flex-direction:column;
  box-shadow:inset 0 1px 0 rgba(255,255,255,.045);
  transition:border-color .16s ease,transform .16s ease;
}
@media (hover:hover){.proj:hover{border-color:color-mix(in oklab,var(--brand-400) 40%,var(--ink-200));transform:translateY(-1px);}}
.proj-media{position:relative;aspect-ratio:16/10;background:var(--ink-050);overflow:hidden;}
.proj-media img{width:100%;height:100%;object-fit:cover;transition:transform .5s var(--ease);}
@media (hover:hover){.proj:hover .proj-media img{transform:scale(1.03);}}
.proj-media::after{content:"";position:absolute;inset:0;background:linear-gradient(to top,color-mix(in oklab,var(--ink-100) 85%,transparent),transparent 55%);}
/* A <picture> is only a source switcher, so it must not become the layout
   box: without this the logo sizes against the picture instead of the cell. */
.brand picture,.person picture{display:contents;}
.proj-media--logo{display:grid;place-items:center;}
.proj-media--logo img{max-width:52%;max-height:38%;width:auto;height:auto;opacity:.45;filter:grayscale(1) brightness(0) invert(1);}
.proj-body{padding:1.4rem 1.4rem 1.5rem;display:flex;flex-direction:column;gap:.7rem;flex:1;}
.proj-client{font-family:var(--font-mono);font-size:var(--fs-2xs);letter-spacing:var(--tr-label);text-transform:uppercase;color:var(--brand-400);}
.proj h3{font-size:var(--fs-lg);letter-spacing:var(--tr-h4);line-height:1.25;}
.proj p{font-size:var(--fs-sm);color:var(--text-dim);}
.proj-tags{display:flex;flex-wrap:wrap;gap:.35rem;margin-top:auto;padding-top:.9rem;}
.tag{font-family:var(--font-mono);font-size:11px;letter-spacing:var(--tr-tag);text-transform:uppercase;color:var(--text-dim);border:1px solid var(--ink-300);border-radius:999px;padding:.25rem .65rem;white-space:nowrap;}

/* ==================== STATS ==================== */
.stats{display:grid;grid-template-columns:repeat(5,1fr);gap:1px;background:var(--ink-200);border:1px solid var(--ink-200);border-radius:var(--r-card);overflow:hidden;}
@media (max-width:900px){.stats{grid-template-columns:repeat(3,1fr);}}
@media (max-width:520px){.stats{grid-template-columns:repeat(2,1fr);}}
.stat{background:var(--ink-050);padding:1.5rem 1.2rem;}
.stat-v{
  font-family:var(--font-display);font-weight:600;
  font-variant-numeric:tabular-nums slashed-zero;
  font-size:var(--fs-2xl);letter-spacing:var(--tr-h3);
  color:var(--text-hi);line-height:1.1;
}
.stat-l{font-family:var(--font-mono);font-size:11px;letter-spacing:var(--tr-tag);text-transform:uppercase;color:var(--text-dim);margin-top:.5rem;}

/* ==================== COMPANY ==================== */
.co-split{display:grid;grid-template-columns:1.05fr .95fr;gap:clamp(2rem,1rem + 4vw,4rem);align-items:center;}
@media (max-width:860px){.co-split{grid-template-columns:1fr;}}
.co-body p+p{margin-top:1.1rem;}
.co-body p{font-size:var(--fs-md);}
.co-figure{border:1px solid var(--ink-200);border-radius:var(--r-card);overflow:hidden;background:var(--ink-100);}
.co-figure img{width:100%;aspect-ratio:16/10;object-fit:cover;}
.co-figure figcaption{padding:.9rem 1.1rem;font-family:var(--font-mono);font-size:11px;letter-spacing:var(--tr-tag);text-transform:uppercase;color:var(--text-dim);border-top:1px solid var(--ink-200);}

/* ==================== CONTACT ==================== */
.contact-solo{max-width:640px;}
.contact-card{
  background:var(--ink-100);border:1px solid var(--ink-200);border-radius:var(--r-card);
  padding:1.75rem 1.6rem;box-shadow:inset 0 1px 0 rgba(255,255,255,.045);
}
.person{display:flex;align-items:center;gap:1rem;padding-bottom:1.25rem;margin-bottom:1.25rem;border-bottom:1px solid var(--ink-200);}
.person img{width:64px;height:64px;border-radius:50%;object-fit:cover;border:1px solid var(--ink-300);flex:none;}
.person h3{font-size:var(--fs-md);letter-spacing:var(--tr-h4);}
.person .role{font-family:var(--font-mono);font-size:11px;letter-spacing:var(--tr-label);text-transform:uppercase;color:var(--brand-400);margin-top:.25rem;}
.c-rows{display:flex;flex-direction:column;}
.c-row{display:grid;grid-template-columns:110px 1fr;gap:1rem;align-items:baseline;padding:.75rem 0;border-bottom:1px solid var(--ink-200);}
.c-row:last-child{border-bottom:0;}
.c-row dt{font-family:var(--font-mono);font-size:11px;letter-spacing:var(--tr-tag);text-transform:uppercase;color:var(--text-dim);}
.c-row dd a{
  display:inline-flex;align-items:center;min-height:32px;
  color:var(--brand-300);border-bottom:1px solid transparent;
  transition:border-color .15s ease;
}
@media (hover:hover){.c-row dd a:hover{border-bottom-color:var(--brand-300);}}
.c-row dd{color:var(--text-mid);font-variant-numeric:tabular-nums;}
@media (max-width:420px){.c-row{grid-template-columns:1fr;gap:.15rem;}}

/* ==================== FOOTER ==================== */
footer{border-top:1px solid var(--ink-200);background:var(--ink-050);padding-block:1.5rem;}
.foot-bar{
  display:flex;flex-wrap:wrap;gap:.5rem 1.5rem;justify-content:space-between;
  font-family:var(--font-mono);font-size:11px;letter-spacing:var(--tr-tag);
  text-transform:uppercase;color:var(--text-dim);
}
/* ==================== THE STACK DIAGRAM ==================== */
.stack-diagram{position:relative;display:flex;flex-direction:column;}
.stack-spine{
  position:absolute;left:calc(3.25rem + 5px);top:1.6rem;bottom:1.6rem;width:2px;
  background:linear-gradient(to bottom,var(--brand-400),color-mix(in oklab,var(--brand-400) 30%,transparent));
  border-radius:2px;
}
@media (prefers-reduced-motion:no-preference){
  .stack-spine::after{
    content:"";position:absolute;left:-2px;width:6px;height:64px;border-radius:3px;
    background:linear-gradient(to bottom,transparent,var(--brand-300),transparent);
    animation:pulse-up 5.5s cubic-bezier(.4,0,.6,1) infinite;
  }
  @keyframes pulse-up{
    0%{top:100%;opacity:0;}
    12%{opacity:1;}
    88%{opacity:1;}
    100%{top:-64px;opacity:0;}
  }}
.layer{
  position:relative;
  display:grid;
  grid-template-columns:3.25rem 12px 1fr auto;
  align-items:start;
  gap:0 1.1rem;
  padding:1.35rem 1.25rem 1.35rem 0;
  border-bottom:1px solid var(--ink-200);
  transition:background .16s ease;
}
.layer:first-child{border-top:1px solid var(--ink-200);}
@media (hover:hover){.layer:hover{background:color-mix(in oklab,var(--ink-100) 60%,transparent);}}
.layer-lv{
  font-family:var(--font-mono);font-variant-numeric:tabular-nums;
  font-size:var(--fs-2xs);letter-spacing:var(--tr-tag);
  color:var(--text-dim);padding-top:.35rem;
}
.layer-node{
  width:12px;height:12px;border-radius:50%;margin-top:.45rem;
  background:var(--ink-050);
  border:2px solid var(--brand-400);
  position:relative;z-index:1;
  box-shadow:0 0 0 4px var(--ink-050);
}
.layer:first-child .layer-node{background:var(--brand-400);}
.layer-body h3{font-size:var(--fs-lg);letter-spacing:var(--tr-h4);margin-bottom:.3rem;}
.layer-body p{font-size:var(--fs-sm);color:var(--text-dim);max-width:62ch;}
.layer-dom{
  font-family:var(--font-mono);font-size:11px;
  letter-spacing:var(--tr-tag);text-transform:uppercase;
  color:var(--brand-300);
  border:1px solid color-mix(in oklab,var(--brand-400) 35%,var(--ink-300));
  border-radius:999px;padding:.25rem .65rem;
  white-space:nowrap;margin-top:.3rem;
}
@media (max-width:720px){
  .layer{grid-template-columns:2.4rem 12px 1fr;gap:0 .85rem;padding-right:0;}
  .stack-spine{left:calc(2.4rem + 5px);}
  .layer-dom{grid-column:3;justify-self:start;margin-top:.7rem;}}

/* ==================== MULTI-PAGE CHROME ==================== */
.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0;}
.nav-links a.is-current{color:var(--text-hi);}
.nav-links a.is-current::after{transform:scaleX(1);}

/* first section on a sub-page clears the fixed nav */
.first-section{padding-top:clamp(7rem,6rem + 5vw,9.5rem);}
.page-h1{font-size:var(--fs-4xl);line-height:var(--lh-h2);letter-spacing:var(--tr-h2);margin-left:-0.02em;}

/* teasers to services / projects */
.teaser-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:1.25rem;}
@media (max-width:1040px){.teaser-grid{grid-template-columns:repeat(2,1fr);}}
@media (max-width:680px){.teaser-grid{grid-template-columns:1fr;}}
.teaser{display:flex;flex-direction:column;gap:.6rem;background:var(--ink-100);
  border:1px solid var(--ink-200);border-radius:var(--r-card);padding:1.9rem 1.7rem;
  box-shadow:inset 0 1px 0 rgba(255,255,255,.045);
  transition:border-color .16s ease,background .16s ease,transform .16s ease;}
@media (hover:hover){.teaser:hover{border-color:color-mix(in oklab,var(--brand-400) 40%,var(--ink-200));
  background:var(--ink-150);transform:translateY(-1px);}}
.teaser h3{font-size:var(--fs-xl);letter-spacing:var(--tr-h4);line-height:1.25;}
.teaser-p{font-size:var(--fs-sm);color:var(--text-dim);}
.teaser-chips{display:flex;flex-wrap:wrap;gap:.35rem;margin-top:.35rem;}
.teaser-go{margin-top:auto;padding-top:.9rem;font-family:var(--font-mono);font-size:var(--fs-2xs);
  letter-spacing:var(--tr-label);text-transform:uppercase;color:var(--brand-300);}

/* products */
/* Cards stretch to match within their row (grid default). Rows are NOT forced to a
   single height: with image bands on some cards and not others, that would stretch
   the text-only cards into tall empty boxes. */
/* four products: two columns, so the grid is a clean 2x2 rather than 3 + orphan */
.product-grid{display:grid;grid-template-columns:repeat(2,1fr);gap:1.25rem;}
@media (max-width:680px){.product-grid{grid-template-columns:1fr;}}
.product{background:var(--ink-100);border:1px solid var(--ink-200);border-radius:var(--r-card);
  padding:1.75rem 1.6rem;display:flex;flex-direction:column;gap:.9rem;
  box-shadow:inset 0 1px 0 rgba(255,255,255,.045);
  transition:border-color .16s ease,transform .16s ease;}
@media (hover:hover){.product:hover{border-color:color-mix(in oklab,var(--brand-400) 40%,var(--ink-200));transform:translateY(-1px);}}
.product h2{font-size:var(--fs-lg);letter-spacing:var(--tr-h4);line-height:1.25;}
.product p{font-size:var(--fs-sm);color:var(--text-dim);}
/* base tag row: must wrap wherever it is used, not only inside a product card.
   Scoping the flex to .product left a bare .cap-tags on the Talk With SCADA page
   running 541px wide inside a 348px column on a phone. */
.cap-tags{display:flex;flex-wrap:wrap;gap:.4rem;}
.product .cap-tags{margin-top:auto;padding-top:.4rem;}

/* ==================== PRODUCT DETAIL: TALK WITH SCADA ==================== */

/* -- breadcrumb back to the parent page -- */
.crumb{font-family:var(--font-mono);font-size:var(--fs-2xs);letter-spacing:var(--tr-tag);
  text-transform:uppercase;color:var(--text-dim);margin-bottom:1.1rem;}
.crumb a{color:var(--text-dim);text-decoration:none;border-bottom:1px solid var(--ink-300);
  padding-bottom:1px;transition:color .16s ease,border-color .16s ease;}
@media (hover:hover){.crumb a:hover{color:var(--brand-300);border-color:var(--brand-400);}}

.note{font-size:var(--fs-xs);line-height:1.55;color:var(--text-dim);}

/* -- hero: copy beside a live-looking assistant panel -- */
/* Stacked, not side by side: the screenshot is a wide UI capture and its text
   is only legible when the image gets the full column width. */
.s-head + .reveal > .shot{margin-top:.5rem;}
mark{background:color-mix(in oklab,var(--brand-400) 22%,transparent);color:var(--text-hi);
  padding:0 .2em;border-radius:3px;}

/* -- constraints -- */
.rules{display:grid;grid-template-columns:1fr 1fr;gap:1.6rem 2.5rem;}
@media (max-width:760px){.rules{grid-template-columns:1fr;gap:1.35rem;}}
.rule{display:flex;flex-direction:column;gap:.3rem;padding-left:1rem;
  border-left:2px solid var(--brand-600);}
.rule h3{font-size:var(--fs-base);letter-spacing:var(--tr-h4);line-height:1.3;color:var(--text-hi);}
.rule p{font-size:var(--fs-sm);color:var(--text-dim);}
.rule code{font-family:var(--font-mono);font-size:.86em;color:var(--brand-300);
  background:var(--ink-050);border:1px solid var(--ink-200);border-radius:3px;padding:.05em .3em;}

/* -- a product card that leads to its own detail page -- */
.product--link{text-decoration:none;color:inherit;}
.product--link .teaser-go{margin-top:.35rem;}
@media (hover:hover){
  .product--link:hover{border-color:color-mix(in oklab,var(--brand-400) 55%,var(--ink-200));}}

/* ==================== MEDIA HANDLING ====================
   Deterrents against casual saving: no drag-to-desktop, no iOS long-press
   "Save Image", no text-selection handles over media. The context menu is
   suppressed in site.js. None of this stops anyone using developer tools —
   nothing client-side can — it only removes the one-gesture paths. */
img,video{
  -webkit-user-drag:none;user-select:none;-webkit-user-select:none;
  -webkit-touch-callout:none;
}
/* the hero video is decorative: nothing to aim a context menu at */
.hero-media img,.hero-media video{pointer-events:none;}

/* -- product screenshot: framed like the panels it replaces -- */
.shot{margin:0;border:1px solid var(--ink-200);border-radius:var(--r-card);overflow:hidden;
  background:var(--ink-050);box-shadow:0 22px 48px -30px rgba(0,0,0,.9);}
/* aspect-ratio per breakpoint: the two sources have different shapes, so this
   reserves the right box and prevents a layout shift either side of the swap */
.shot{max-width:730px;}   /* the capture is 1464px wide: past this it goes soft */
.shot img{display:block;width:100%;height:auto;aspect-ratio:1464/796;}
.shot-note{max-width:730px;margin-top:1rem;}

/* ==================== PRODUCT CARD IMAGERY ====================
   A shallow letterbox band, desaturated and dimmed, fading into the card
   beneath it. The photograph is texture and context; the heading is still
   the loudest thing on the card. Cards without a band simply start at the
   heading, and the two that have no apt photograph sit together in the
   last row so the grid still reads as deliberate. */
/* A card with no photograph sizes to its own content instead of stretching to
   the height of the photo card beside it, which would leave a dead gap. */
.product-grid > .product:not(.product--media){align-self:start;}
.product{overflow:hidden;}
.product--media{padding-top:0;}
.product-media{
  display:block;position:relative;overflow:hidden;
  margin:0 -1.6rem 1.35rem;
  background:var(--ink-050);
}
.product-media img{
  display:block;width:100%;height:100%;
  aspect-ratio:16/6;object-fit:cover;object-position:center;
  filter:saturate(.75) brightness(.68) contrast(1.02);
  transition:filter .45s var(--ease),transform .6s var(--ease);
}
.product-media[data-focus="top"] img{object-position:top center;}
/* melt the bottom edge into the card so it reads as one surface */
.product-media::after{
  content:"";position:absolute;inset:0;pointer-events:none;
  background:linear-gradient(to bottom,
    color-mix(in oklab,var(--ink-100) 25%,transparent) 0%,
    transparent 42%,
    color-mix(in oklab,var(--ink-100) 92%,transparent) 100%);
}
@media (hover:hover){
  .product--link:hover .product-media img{filter:saturate(.95) brightness(.85);transform:scale(1.03);}}
@media (prefers-reduced-motion:reduce){
  .product-media img{transition:none;}
  .product--link:hover .product-media img{transform:none;}}

/* ==================== HERO STACK ROTATOR ====================
   The destination cycles up the stack so the headline states the full span
   rather than stopping at the control room. inline-grid stacks every item in
   one cell, so the box is already as wide as the longest word and the line
   never reflows as it changes. The visible rotator is aria-hidden; the h1
   carries the whole sentence in an .sr-only span for assistive tech. */
.roll{display:inline-grid;vertical-align:baseline;}
.roll-item{
  grid-area:1/1;
  opacity:0;transform:translateY(.3em);
  /* leaving: go first and go quickly */
  transition:opacity .18s ease,transform .18s ease;
}
/* arriving: wait until the outgoing word has actually gone, so two words are
   never both half-visible on top of each other */
.roll-item.is-on{
  opacity:1;transform:none;
  transition:opacity .34s var(--ease) .18s,transform .34s var(--ease) .18s;
}
@media (prefers-reduced-motion:reduce){
  .roll-item{transition:none;transform:none;}}

/* Consultancy is not a separate business beside the stack: it spans it. This
   note closes the stack section rather than opening one of its own. */
.span-note{
  margin-top:2.25rem;padding-top:1.75rem;
  border-top:1px solid var(--ink-200);
  max-width:72ch;
}
/* a second note follows immediately: keep them a pair, not two dividers */
.span-note + .span-note{margin-top:1.6rem;padding-top:1.35rem;}
.span-note h3{font-size:var(--fs-lg);letter-spacing:var(--tr-h3);color:var(--text-hi);}
.span-note p{margin-top:.6rem;color:var(--text-mid);font-size:var(--fs-base);line-height:var(--lh-body);}
.span-note .cap-tags{margin-top:1rem;}