/* ============================================================
   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:#7FE7D8;
  --brand-400:#2DD4BF;
  --brand-600:#178F80;
  --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:#0E6B60;

  /* ---- signal hue ---- */
  --signal-400:#2DD4BF;
  --signal-600:#178F80;
  --signal-ink:#041A17;

  --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);}
@media (hover:hover){
  .btn-primary:hover{background:var(--signal-600);color:#fff;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;}

/* ==================== 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-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);
}
@media (prefers-reduced-motion:no-preference){
  @keyframes pulse-up{
    0%{top:100%;opacity:0;}
    12%{opacity:1;}
    88%{opacity:1;}
    100%{top:-64px;opacity:0;}
  }}

/* ==================== 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;}
mark{background:color-mix(in oklab,var(--brand-400) 22%,transparent);color:var(--text-hi);
  padding:0 .2em;border-radius:3px;}

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

/* ── FishEye ─────────────────────────────────────────────────────────────
   The FishEye mark is a wordmark, so it carries the name itself: it is sized by
   height with the width left to the artwork, and the text name is hidden to
   avoid printing "FishEye" twice in the same lockup. */
.brand img{width:auto;height:30px;}
.brand-name{display:none;}
.brand-sub{margin-top:0;}

/* Product media. These are machines and drawings, not mood shots, so the box
   shows the whole thing: photographs crop from the top so a face is never cut,
   and the feeding diagrams sit on a light ground because their ink is black and
   would otherwise disappear into the card. */
.product--media .proj-media{aspect-ratio:16/10;background:var(--ink-050);}
.product--media .proj-media::after{display:none;}
.product--media .proj-media img{width:100%;height:100%;}
.product--media .proj-media[data-fit="contain"] img{object-fit:contain;padding:.5rem;}
/* The drawings need a light ground to read, but a near-white panel glares
   against this dark page: this is the palette's own cool light grey. */
.product--media .proj-media[data-fit="light"]{background:#C7CFD7;}
.product--media .proj-media[data-fit="light"] img{object-fit:contain;padding:.9rem;}
.product--media .proj-media[data-fit="top"] img{object-fit:cover;object-position:center 36%;}

/* Every client mark is padded onto the same 3:1 canvas with equal optical
   weight, so one width gives every card an identical, centred box. The <picture>
   itself is the sized grid item: with display:contents the <source> also becomes
   a grid item and pushes the image into a second row, off centre. */
.proj-media--logo picture{display:block;width:76%;}
.proj-media--logo img{width:100%;height:auto;max-width:none;max-height:none;}