/* ==========================================================================
   Phaseform — design system
   Palette anchored on the logo blues (#1c4a74 → #2e77ad) with the laser-red
   accent from the hero photography. Space Grotesk = display, Inter = body.
   ========================================================================== */

:root {
  --navy-950: #071220;
  --navy-900: #0b1d31;
  --navy-800: #123049;
  --navy-700: #1c4a74;
  --brand-600: #24608f;
  --brand-500: #2e77ad;
  --brand-400: #4f97cc;
  --brand-100: #dcebf6;
  --brand-050: #eef5fa;
  --accent: #e6483d;
  --accent-text: #c23227;   /* red for small text on light ground: 5.5:1 on white, 4.9:1 on paper. --accent stays for lines and dots */
  --ink: #12263a;
  --slate: #43607c;
  --muted: #58718c;         /* 4.7:1 on paper — captions, meta lines and small print pass AA at 13 px */
  --line: #dfe8f0;
  --paper: #f4f7fa;
  --bg: #ffffff;
  --radius: 16px;
  --radius-sm: 10px;
  --shadow-sm: 0 1px 2px rgba(11, 29, 49, .06), 0 1px 6px rgba(11, 29, 49, .05);
  --shadow-md: 0 6px 24px rgba(11, 29, 49, .10);
  --shadow-lg: 0 18px 48px rgba(11, 29, 49, .16);
  --font-body: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
  --font-display: 'Space Grotesk', 'Inter', sans-serif;
  --wrap: 1180px;
}

* { margin: 0; padding: 0; box-sizing: border-box; }
html { scroll-behavior: smooth; -webkit-text-size-adjust: 100%; }
/* the quotation form's submit bar is sticky to the bottom of the viewport, so a field scrolled into
   view on focus could land underneath it — this keeps the browser's own scroll clear of the bar */
html { scroll-padding-bottom: 96px; }
[id] { scroll-margin-top: 92px; }
body {
  font-family: var(--font-body);
  font-size: 1.0625rem;
  line-height: 1.65;
  color: var(--ink);
  background: var(--bg);
}
img { max-width: 100%; height: auto; display: block; }
img[width][height] { width: auto; }   /* attributes only reserve layout space; CSS decides the size */
a { color: var(--brand-600); text-decoration: none; }
a:hover { color: var(--brand-500); }
:focus-visible { outline: 3px solid var(--brand-400); outline-offset: 2px; border-radius: 4px; }
::selection { background: var(--brand-100); }

h1, h2, h3, h4 { font-family: var(--font-display); line-height: 1.15; color: var(--ink); letter-spacing: -0.01em; }
h1 { font-size: clamp(2.1rem, 4.6vw, 3.4rem); font-weight: 700; }
h2 { font-size: clamp(1.6rem, 3vw, 2.3rem); font-weight: 700; }
h3 { font-size: clamp(1.2rem, 2vw, 1.5rem); font-weight: 600; }
h4 { font-size: 1.125rem; font-weight: 600; }

.wrap { max-width: var(--wrap); margin: 0 auto; padding: 0 clamp(1.1rem, 4vw, 2rem); }
.section { padding: clamp(3.2rem, 7vw, 5.5rem) 0; }
.section.tight { padding: clamp(2.2rem, 5vw, 3.4rem) 0; }
.section.paper { background: var(--paper); }
.section.dark { background: linear-gradient(180deg, var(--navy-950), var(--navy-900)); color: #d9e6f2; }
.section.dark h2, .section.dark h3 { color: #fff; }

/* Seam between two sections that belong together — on the home page, Latest news and
   Next events. Stacked full section padding put a ~140px hole between the two headings;
   these trim only the boundary, leaving each section's own rhythm alone. Declared after
   .section.tight so they win the equal-specificity tie. */
.section.seam-top { padding-top: clamp(2rem, 3.6vw, 2.8rem); }
.section.seam-bottom { padding-bottom: clamp(1.6rem, 3vw, 2.4rem); }

.eyebrow {
  display: inline-block; font-family: var(--font-display); font-size: .8rem; font-weight: 600;
  letter-spacing: .18em; text-transform: uppercase; color: var(--brand-600); margin-bottom: .9rem;
}
.eyebrow::before { content: ""; display: inline-block; width: 26px; height: 2px; background: var(--accent); vertical-align: middle; margin-right: .6rem; }
.lede { font-size: clamp(1.2rem, 1.6vw, 1.25rem); color: var(--slate); max-width: 46em; }
.section-head { max-width: 780px; margin-bottom: clamp(1.8rem, 4vw, 3rem); }
.section-head.center { margin-left: auto; margin-right: auto; text-align: center; }
.section-head.center .lede { margin-left: auto; margin-right: auto; }
.section-head h2 { margin-bottom: .7rem; }
/* Headings set their own line breaks: balance the lines instead of letting the last word
   drop alone onto a line of its own. Ignored by browsers that don't support it, which
   then wrap as before. */
.section-head h1, .section-head h2, .section-head h3, .hero h1, .hero h2 { text-wrap: balance; }
.section-head .lede, .hero .lede { text-wrap: pretty; }

/* --------------------------------------------------------------- buttons */
.btn {
  display: inline-flex; align-items: center; gap: .55rem;
  font-family: var(--font-display); font-weight: 600; font-size: .98rem;
  padding: .78rem 1.5rem; border-radius: 999px; border: 1.5px solid transparent;
  background: var(--brand-600); color: #fff; cursor: pointer;
  transition: transform .15s ease, box-shadow .15s ease, background .15s ease;
}
.btn:hover { background: var(--navy-700); color: #fff; transform: translateY(-1px); box-shadow: var(--shadow-md); }
.btn.ghost { background: transparent; border-color: var(--brand-500); color: var(--brand-600); }
.btn.ghost:hover { background: var(--brand-050); box-shadow: none; }
.btn.light { background: #fff; color: var(--navy-800); }
.btn.light:hover { background: var(--brand-100); color: var(--navy-900); }
.btn.ghost-light { background: transparent; border-color: rgba(255,255,255,.55); color: #fff; }
.btn.ghost-light:hover { background: rgba(255,255,255,.12); }
.btn .arr { transition: transform .15s ease; }
.btn:hover .arr { transform: translateX(3px); }
/* The arrow must never wrap onto a line of its own, but `white-space: nowrap` on the whole link kept
   long labels ("Discuss your microscope with an engineer") on one 369 px line that ran off a 320 px
   screen. A no-break space glues the arrow to the last word and lets the label wrap anywhere else. */
.textlink { font-weight: 600; font-family: var(--font-display); }
.textlink::after { content: "\00a0→"; }

/* ---------------------------------------------------------------- header */
.site-header {
  position: sticky; top: 0; z-index: 60;
  background: rgba(255, 255, 255, .86); backdrop-filter: blur(14px); -webkit-backdrop-filter: blur(14px);
  border-bottom: 1px solid rgba(20, 50, 78, .08);
}
.site-header .bar { display: flex; align-items: center; gap: 1.4rem; height: 72px; }
.logo { display: flex; align-items: center; flex: 0 0 auto; }
.logo img { height: 46px; width: auto; }
.nav { display: flex; align-items: center; gap: .2rem; margin-left: auto; }
.nav > a, .nav .navgroup > button {
  font-family: var(--font-display); font-size: .95rem; font-weight: 500; color: var(--ink);
  padding: .55rem .8rem; border-radius: 8px; background: none; border: 0; cursor: pointer;
  display: flex; align-items: center; gap: .3rem;
}
.nav > a:hover, .nav .navgroup > button:hover { background: var(--brand-050); color: var(--navy-700); }
.nav > a.active { color: var(--brand-600); }
.nav > a.active { box-shadow: inset 0 -2px 0 var(--accent); border-radius: 8px 8px 0 0; }
.navgroup { position: relative; }
.navgroup .caret { width: 9px; height: 9px; border-right: 1.7px solid currentColor; border-bottom: 1.7px solid currentColor; transform: rotate(45deg) translateY(-2px); transition: transform .15s; }
.navgroup.open .caret { transform: rotate(225deg) translateY(-1px); }
.navgroup > button.active { color: var(--brand-600); box-shadow: inset 0 -2px 0 var(--accent); border-radius: 8px 8px 0 0; }
.dropdown {
  position: absolute; top: calc(100% + 10px); left: 50%;
  background: #fff; border: 1px solid var(--line); border-radius: 14px; box-shadow: var(--shadow-lg);
  padding: .9rem; min-width: 300px; z-index: 70;
  opacity: 0; visibility: hidden; transform: translate(-50%, 8px);
  transition: opacity .16s ease, transform .16s ease, visibility .16s;
}
/* invisible bridge so the pointer never leaves the menu while crossing the gap */
.dropdown::before { content: ""; position: absolute; left: 0; right: 0; top: -14px; height: 14px; }
.navgroup.open .dropdown,
.navgroup:focus-within .dropdown { opacity: 1; visibility: visible; transform: translate(-50%, 0); }
/* Escape closes the panel but returns focus to the button, which is inside .navgroup — without this the
   :focus-within rule above would show the panel again while aria-expanded says "false". site.js sets .esc
   on Escape and clears it as soon as focus leaves the group. */
.navgroup.esc:focus-within .dropdown { opacity: 0; visibility: hidden; transform: translate(-50%, 8px); }
.dropdown .grouplabel { font-size: .72rem; font-weight: 600; letter-spacing: .14em; text-transform: uppercase; color: var(--muted); padding: .5rem .65rem .2rem; }
.dropdown a { display: block; padding: .5rem .65rem; border-radius: 8px; color: var(--ink); font-size: .95rem; }
.dropdown a:hover { background: var(--brand-050); color: var(--navy-700); }
.dropdown a small { display: block; color: var(--muted); font-size: .8rem; line-height: 1.35; }
/* The CTA is a .btn sitting inside .nav, and `.nav > a` above (0,1,1) outranks `.btn` (0,1,0) — it was
   stripping the fill, the border and the pill radius, so the site's primary call to action rendered as
   plain nav text at every width. Two classes (0,2,0) put the button back. */
.nav .cta { margin-left: .5rem; padding: .6rem 1.2rem; font-size: .9rem;
  background: var(--brand-600); color: #fff; border: 1.5px solid transparent; border-radius: 999px; }
.nav .cta:hover { background: var(--navy-700); color: #fff; }
/* .65rem of padding around the 24px bars clears 44x44, the minimum comfortable thumb target;
   the negative margin keeps the right edge optically aligned with the wrap. */
.nav-toggle { display: none; margin-left: auto; background: none; border: 0; cursor: pointer; padding: .65rem; margin-right: -.15rem; }
.nav-toggle span { display: block; width: 24px; height: 2px; background: var(--ink); margin: 5px 0; border-radius: 2px; transition: .2s; }

@media (max-width: 1024px) {
  .nav-toggle { display: block; }
  .nav {
    display: none; position: absolute; top: 72px; left: 0; right: 0;
    background: #fff; border-bottom: 1px solid var(--line); box-shadow: var(--shadow-lg);
    flex-direction: column; align-items: stretch; padding: 1rem 1.2rem 1.4rem; gap: .1rem;
    max-height: calc(100vh - 72px); max-height: calc(100dvh - 72px); overflow: auto;
  }
  /* The panel is a dropdown, not a full-screen drawer, so without a lock the page scrolled behind it
     and the content edge showed through mid-sentence underneath. The scrim also gives the tap-outside
     close (site.js) something to aim at. Two rules, not a `:has()` selector list — an unsupported
     `:has()` would invalidate the whole list and take the body lock down with it. */
  body.nav-open { overflow: hidden; }
  html:has(body.nav-open) { overflow: hidden; }
  body.nav-open::after { content: ""; position: fixed; inset: 72px 0 0; background: rgba(11, 29, 49, .38); z-index: 50; }
  .nav.open { display: flex; }
  .nav > a, .nav .navgroup > button { width: 100%; justify-content: space-between; font-size: 1.05rem; padding: .8rem .6rem; }
  .dropdown {
    position: static; box-shadow: none; border: 0; padding: 0 0 .4rem .9rem; min-width: 0;
    display: none; opacity: 1; visibility: visible; transform: none; transition: none;
  }
  .dropdown::before { display: none; }
  .navgroup.open .dropdown, .navgroup:focus-within .dropdown { display: block; transform: none; }
  .navgroup.esc:focus-within .dropdown { display: none; }
  .navgroup > button.active { box-shadow: inset 3px 0 0 var(--accent); border-radius: 0 8px 8px 0; }
  .nav .cta { margin: .8rem 0 0; justify-content: center; }
  body.nav-open .nav-toggle span:nth-child(1) { transform: translateY(7px) rotate(45deg); }
  body.nav-open .nav-toggle span:nth-child(2) { opacity: 0; }
  body.nav-open .nav-toggle span:nth-child(3) { transform: translateY(-7px) rotate(-45deg); }
}

/* ------------------------------------------------------------------ hero */
.hero {
  position: relative; color: #eaf2f9; overflow: hidden;
  background: var(--navy-950);
}
.hero .bg {
  position: absolute; inset: 0; background-size: cover; background-position: center;
  opacity: .55;
}
.hero .bg::after {
  content: ""; position: absolute; inset: 0;
  background: linear-gradient(100deg, rgba(7, 18, 32, .96) 22%, rgba(7, 18, 32, .55) 58%, rgba(7, 18, 32, .25));
}
.hero .wrap { display: flex; align-items: center; gap: clamp(1.5rem, 4vw, 4rem); }
.hero .inner { position: relative; padding: clamp(4rem, 10vw, 8rem) 0; max-width: 640px; flex: 1 1 auto; min-width: 0; }
.hero h1 { color: #fff; margin-bottom: 1.1rem; }
.hero .lede { color: #b9cddd; margin-bottom: 2rem; }
.hero .eyebrow { color: var(--brand-400); }
.hero .actions { display: flex; flex-wrap: wrap; gap: .8rem; }
.hero.slim .inner { padding: clamp(2.6rem, 6vw, 4.2rem) 0; max-width: 760px; }
.hero .laser { position: absolute; left: 0; right: 0; bottom: 0; height: 3px;
  background: linear-gradient(90deg, transparent, var(--accent) 30%, #ff8d7e 50%, var(--accent) 70%, transparent); opacity: .85; }
/* product render beside the copy — hero macro `render=` or a hand-written <div class="hero-render"> — a real column, not a background */
.hero:where(.render) { background: radial-gradient(55% 130% at 84% 50%, rgba(46, 119, 173, .30), transparent 70%), var(--navy-950); }   /* :where() keeps specificity at .hero so a design variant's hero override still wins */
.hero-render { position: relative; flex: 0 1 40%; display: flex; justify-content: center; align-items: center; padding: 1.5rem 0; }
.hero-render img { width: 100%; height: auto; max-height: 380px; object-fit: contain; filter: drop-shadow(0 28px 48px rgba(0, 0, 0, .5)); }
.hero-render.shot img { border-radius: 10px; box-shadow: 0 24px 60px rgba(0, 0, 0, .55); filter: none; }
@media (max-width: 900px) {
  .hero .wrap { flex-direction: column; align-items: stretch; gap: 0; }
  .hero-render { flex: none; justify-content: flex-start; padding: 0 0 2.2rem; }
  .hero-render img { width: auto; max-width: 100%; max-height: 210px; }
  /* A product render is landscape, so a height cap sizes it sensibly. A screenshot is not:
     Phinden's window is 708 x 990, and the same 210 px cap left it 150 px wide — narrower
     than the buttons above it, left-aligned against a wide empty margin, and far too small
     to read. Screenshots get their own cap and are centred under the copy. */
  .hero-render.shot { justify-content: center; }
  .hero-render.shot img { max-height: 320px; }
  .hero:where(.render) { background: radial-gradient(75% 40% at 35% 100%, rgba(46, 119, 173, .34), transparent 70%), var(--navy-950); }   /* glow follows the render to the bottom */
  .hero.render .inner { padding-bottom: 1.2rem; }
}
/* phones: the photo is a backdrop only — a top-down shade keeps copy off the bright or busy parts (laser line, product edges) */
@media (max-width: 720px) {
  .hero .lede { margin-bottom: 1.5rem; }
  .hero-render { padding-bottom: 1.6rem; }
  .hero .bg { opacity: .42; }
  .hero .bg::after { background: linear-gradient(180deg, rgba(7, 18, 32, .9), rgba(7, 18, 32, .72) 55%, rgba(7, 18, 32, .5)); }
}
/* home "showcase" (specificity kept at .hero .x via :where so the design variants still win): the photograph is the subject — full opacity, Delta 7 and laser on the left, copy in a glass panel on the right */
.hero:where(.showcase) .bg { opacity: 1; background-position: 30% 50%; }
.hero:where(.showcase) .bg::after { background: linear-gradient(90deg, rgba(7, 18, 32, .35), rgba(7, 18, 32, 0) 28%, rgba(7, 18, 32, 0) 55%, rgba(7, 18, 32, .5)); }
.hero:where(.showcase) .inner {
  max-width: 620px; margin: clamp(2.4rem, 4.5vw, 3.2rem) 0 clamp(2.4rem, 4.5vw, 3.2rem) auto;   /* keeps the hero ≈ 630 px tall on desktop = the photo's native height, so it is not upscaled at 1× */
  padding: clamp(2rem, 4vw, 3rem);
  background: rgba(7, 18, 32, .68); backdrop-filter: blur(14px); -webkit-backdrop-filter: blur(14px);
  border: 1px solid rgba(255, 255, 255, .09); border-radius: var(--radius); box-shadow: var(--shadow-lg);
}
/* drifting fog (home): two seamless noise layers from an inline SVG turbulence filter, screen-blended over the photo and
   moved with slow transforms — no asset, no JavaScript. Markup: <div class="fog" aria-hidden="true"> inside .bg */
.hero .bg { overflow: hidden; }
.hero .fog { position: absolute; inset: 0; pointer-events: none; mix-blend-mode: screen;
  /* keep the haze off the modulator so its edges stay crisp: soft elliptical hole over the device */
  -webkit-mask-image: radial-gradient(ellipse 16% 48% at 34% 56%, transparent 0 40%, #000 100%);
          mask-image: radial-gradient(ellipse 16% 48% at 34% 56%, transparent 0 40%, #000 100%); }
@media (max-width: 720px) { .hero .fog { -webkit-mask-image: radial-gradient(ellipse 34% 60% at 50% 50%, transparent 0 40%, #000 100%); mask-image: radial-gradient(ellipse 34% 60% at 50% 50%, transparent 0 40%, #000 100%); } }
.hero .fog::before, .hero .fog::after {
  content: ""; position: absolute; inset: -25%;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='900' height='600'%3E%3Cfilter id='f' x='0' y='0' width='100%25' height='100%25'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.0028 0.0045' numOctaves='3' seed='4' stitchTiles='stitch'/%3E%3CfeColorMatrix values='0 0 0 0 0.82  0 0 0 0 0.9  0 0 0 0 1  0 0 0 1.6 -0.55'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23f)'/%3E%3C/svg%3E");
  background-size: 900px 600px; background-repeat: repeat;
  opacity: .42; will-change: transform, opacity;
  animation: fog-drift-a 14s ease-in-out infinite alternate;
}
.hero .fog::after { background-position: 37% 61%; opacity: .3; animation: fog-drift-b 19s ease-in-out infinite alternate; }
/* ~15 px/s of lateral drift plus a slow breathing of the density — clearly alive, still calm */
@keyframes fog-drift-a { from { transform: translate3d(-9%, -3%, 0) scale(1.06); opacity: .32; } 50% { opacity: .5; } to { transform: translate3d(8%, 3%, 0) scale(1.22); opacity: .36; } }
@keyframes fog-drift-b { from { transform: translate3d(8%, 4%, 0) scale(1.2); opacity: .22; } 50% { opacity: .38; } to { transform: translate3d(-9%, -4%, 0) scale(1.04); opacity: .26; } }
@media (prefers-reduced-motion: reduce) { .hero .fog::before, .hero .fog::after { animation: none; } }
/* the same drift on the product heroes, which carry no photo: the fog moves over the gradient itself.
   Weighted to the right (behind the render, which paints above it) and faded out across the copy column so the
   headline and lede keep full contrast. Higher specificity than the .hero .fog masks above, at every width. */
.hero.render > .fog {
  opacity: .62;
  -webkit-mask-image: linear-gradient(100deg, transparent 0 22%, rgba(0, 0, 0, .28) 52%, #000 88%);
          mask-image: linear-gradient(100deg, transparent 0 22%, rgba(0, 0, 0, .28) 52%, #000 88%);
}
@media (max-width: 900px) {
  /* stacked layout: the render sits below the copy, so the fade runs top-to-bottom instead */
  .hero.render > .fog {
    opacity: .5;
    -webkit-mask-image: linear-gradient(180deg, transparent 0 12%, rgba(0, 0, 0, .45) 45%, #000 80%);
            mask-image: linear-gradient(180deg, transparent 0 12%, rgba(0, 0, 0, .45) 45%, #000 80%);
  }
}
@media (max-width: 720px) {
  /* phones: the photo becomes a strip above the copy (device centred), the copy sits on plain navy below it */
  .hero:where(.showcase) .bg { position: relative; height: 250px; opacity: 1; background-position: 28% 50%; }
  .hero:where(.showcase) .bg::after { background: linear-gradient(180deg, transparent 55%, var(--navy-950)); }
  .hero:where(.showcase) .inner { margin: 0; padding: 1.4rem 0 2.6rem; background: none; backdrop-filter: none; -webkit-backdrop-filter: none; border: 0; box-shadow: none; border-radius: 0; }
}

.crumbs { font-size: .85rem; color: #8fa9bf; margin-bottom: 1.2rem; }
.crumbs a { color: #b9cddd; }
.crumbs a:hover { color: #fff; }

/* ----------------------------------------------------------------- cards */
.grid { display: grid; gap: 1.5rem; }
.grid.cols-3 { grid-template-columns: repeat(3, 1fr); }
.grid.cols-2 { grid-template-columns: repeat(2, 1fr); }
.grid.cols-4 { grid-template-columns: repeat(4, 1fr); }
@media (max-width: 900px) { .grid.cols-3, .grid.cols-4 { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 620px) { .grid.cols-2, .grid.cols-3, .grid.cols-4 { grid-template-columns: 1fr; } }

.card {
  background: #fff; border: 1px solid var(--line); border-radius: var(--radius);
  box-shadow: var(--shadow-sm); overflow: hidden; display: flex; flex-direction: column;
  transition: transform .18s ease, box-shadow .18s ease;
}
a.card { color: inherit; }
.card:hover { transform: translateY(-4px); box-shadow: var(--shadow-md); }
.card .media { background: #fff; border-bottom: 1px solid var(--line); padding: 1.6rem; display: flex; align-items: center; justify-content: center; aspect-ratio: 16 / 10; overflow: hidden; min-height: 0; }   /* white ground: product photos shot on white sit in it without a visible rectangle */   /* min-height: 0 — else a portrait cover grows the box past its ratio and the card row goes uneven */
/* the build wraps <img> in <picture>; pin the wrapper (or a bare img) to the padded box so max-height has something to resolve against */
.card .media { position: relative; }
.card .media > picture, .card .media > img { position: absolute; inset: 1.6rem; margin: auto; }
.card .media > picture { display: flex; align-items: center; justify-content: center; }
.card .media img { max-width: 100%; max-height: 100%; width: auto; height: auto; object-fit: contain; }
.card .media.cover { padding: 0; }
.card .media.cover > picture, .card .media.cover > img { inset: 0; }
.card .media.cover img { width: 100%; height: 100%; object-fit: cover; }
.card .media.cover.contain img { object-fit: contain; }   /* diagrams and wide graphics: show whole, never crop */
/* two product renders in one card well — a family whose members differ by a number, so each
   render carries that number rather than leaving the reader to guess which is which */
.card .media.duo { display: grid; grid-template-columns: 1fr 1fr; gap: .8rem; }
.card .media.duo > figure { position: absolute; display: flex; flex-direction: column; align-items: center; gap: .4rem; margin: 0; inset: 1.6rem auto 1.6rem 1.6rem; width: calc(50% - 2rem); }
.card .media.duo > figure:last-child { inset: 1.6rem 1.6rem 1.6rem auto; }
/* the picture takes the flexible middle so the two captions land on one baseline — the renders
   have different aspect ratios and centring each figure independently staggered them.
   The flex child is <picture>, not <img>: the build wraps every image in one. */
.card .media.duo > figure > picture,
.card .media.duo > figure > img { position: static; inset: auto; flex: 1; min-height: 0; width: 100%; display: flex; align-items: center; justify-content: center; }
.card .media.duo > figure img { position: static; max-width: 100%; max-height: 100%; width: auto; height: auto; object-fit: contain; }
.card .media.duo figcaption { font-size: .68rem; font-weight: 600; letter-spacing: .08em; text-transform: uppercase; color: var(--muted); }
.card .body { padding: 1.4rem 1.5rem 1.6rem; display: flex; flex-direction: column; gap: .6rem; flex: 1; }
.card .body h3 { color: var(--navy-800); }
.card .body p { color: var(--slate); font-size: .97rem; flex: 1; }
.card .body .textlink { margin-top: .4rem; }

/* application tiles */
.tile { position: relative; border-radius: var(--radius); overflow: hidden; aspect-ratio: 4 / 3; display: block; }
.tile img { width: 100%; height: 100%; object-fit: cover; transition: transform .35s ease; }
.tile:hover img { transform: scale(1.05); }
.tile .cap {
  position: absolute; inset: auto 0 0 0; padding: 2.4rem 1.3rem 1.1rem;
  background: linear-gradient(180deg, transparent, rgba(7, 18, 32, .88));
  color: #fff;
}
.tile .cap h3, .tile .cap h4 { color: #fff; margin-bottom: .15rem; font-size: 1.125rem; font-weight: 600; }
.tile .cap p { font-size: .87rem; color: #c9d9e6; line-height: 1.45; max-height: 0; opacity: 0; overflow: hidden; transition: max-height .3s ease, opacity .3s ease; }
.tile:hover .cap p, .tile:focus-visible .cap p { max-height: 6em; opacity: 1; }

/* ------------------------------------------------------------ spec lists */
.speclist { display: grid; grid-template-columns: repeat(2, 1fr); gap: .9rem 2rem; list-style: none; }
@media (max-width: 720px) { .speclist { grid-template-columns: 1fr; } }
.speclist li { display: flex; gap: .8rem; align-items: flex-start; color: var(--slate); }
.speclist li strong { color: var(--ink); font-weight: 600; }
.speclist li::before {
  content: "✓"; flex: 0 0 auto; width: 22px; height: 22px; margin-top: .18rem; border-radius: 50%;
  background: var(--brand-050); color: var(--brand-600); font-size: .8rem; font-weight: 700;
  display: inline-flex; align-items: center; justify-content: center;
}

/* two-column feature rows */
.split { display: grid; grid-template-columns: 1fr 1fr; gap: clamp(1.8rem, 5vw, 4rem); align-items: center; }
@media (max-width: 860px) { .split { grid-template-columns: 1fr; } }
.split .visual { border-radius: var(--radius); overflow: hidden; box-shadow: var(--shadow-md); }
.split .visual.plain { box-shadow: none; }
/* a cutout figure that should read as an illustration, not a hero: capped and centred in its column */
.split .visual.compact { max-width: 430px; margin-inline: auto; }
.split .visual img { width: 100%; }
.figcap { font-size: .84rem; color: var(--muted); margin-top: .6rem; }

/* numbered feature grid (technology 01–04) */
.numfeat { counter-reset: nf; display: grid; grid-template-columns: repeat(2, 1fr); gap: 1.5rem; }
@media (max-width: 720px) { .numfeat { grid-template-columns: 1fr; } }
.numfeat.cols-3 { grid-template-columns: repeat(3, 1fr); }   /* applications: six machine-vision correction scenarios */
@media (max-width: 900px) { .numfeat.cols-3 { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 620px) { .numfeat.cols-3 { grid-template-columns: 1fr; } }
/* technology: the four DPP properties on one row. The column is narrow, so the card sheds a
   little padding and the body text drops a step; below 1080 px four columns stop fitting and
   it falls back to the 2x2 the grid used before. */
.numfeat.cols-4 { grid-template-columns: repeat(4, 1fr); gap: 1.1rem; }
.numfeat.cols-4 .nf { padding: 1.4rem 1.35rem 1.5rem; }
.numfeat.cols-4 .nf .nf-ico { width: 46px; height: 46px; }
.numfeat.cols-4 .nf p { font-size: .93rem; }
@media (max-width: 1080px) { .numfeat.cols-4 { grid-template-columns: repeat(2, 1fr); gap: 1.5rem; } }
@media (max-width: 620px) { .numfeat.cols-4 { grid-template-columns: 1fr; } }
.evidence .split { align-items: start; }   /* a figure beside a long text column: top-aligned, not centred in a tall gap */
.evidence .split .fig { margin: 0; position: sticky; top: 140px; }   /* the figure stays in view while the reader scrolls the steps (header 72 + jump bar 48 + air) */
@media (max-width: 860px) { .evidence .split .fig { position: static; } }
.numfeat .nf {
  background: #fff; border: 1px solid var(--line); border-radius: var(--radius);
  padding: 1.7rem 1.7rem 1.8rem; position: relative;
}
.numfeat .nf .num { font-family: var(--font-display); font-weight: 700; font-size: .95rem; color: var(--accent-text); letter-spacing: .1em; }
.numfeat .nf .nf-ico { display: block; width: 52px; height: 52px; color: var(--navy-800); margin-bottom: .7rem; }
@media (max-width: 480px) { .numfeat .nf .nf-ico { width: 44px; height: 44px; } }
.numfeat .nf .nf-head { display: flex; align-items: center; gap: .85rem; margin-bottom: .7rem; }   /* icon beside its step number: the fovea four are a sequence, the technology-page four are not */
.numfeat .nf .nf-head .nf-ico { margin-bottom: 0; }
.numfeat .nf .nf-head + h3 { margin-top: 0; }
.numfeat .nf h3 { margin: .45rem 0 .55rem; color: var(--navy-800); }
.numfeat .nf p { color: var(--slate); font-size: .97rem; }

/* applications: the closing "do you need this?" grid is a questionnaire put to the reader,
   not a feature list — same cards, one step quieter, so four questions do not shout as loudly
   as the claims above them. .quiz-close is the single question the block ends on. */
.numfeat.quiz .nf { padding: 1.25rem 1.25rem 1.35rem; }
.numfeat.quiz .nf .nf-head { gap: .7rem; margin-bottom: .55rem; }
.numfeat.quiz .nf .nf-ico { width: 38px; height: 38px; }
.numfeat.quiz .nf .num { font-size: .85rem; }
.numfeat.quiz .nf h3 { font-size: clamp(1rem, 1.4vw, 1.12rem); line-height: 1.3; margin: .35rem 0 .5rem; }
.numfeat.quiz .nf p { font-size: .87rem; line-height: 1.6; }
@media (max-width: 1080px) { .numfeat.quiz .nf .nf-ico { width: 42px; height: 42px; } }
.quiz-close {
  font-family: var(--font-display); font-weight: 600; color: var(--navy-800);
  font-size: clamp(1.05rem, 2vw, 1.3rem); line-height: 1.35; text-wrap: balance;
  max-width: 40em; margin-top: clamp(1.5rem, 3vw, 2.1rem);
}

/* the honest-boundary card: in the applications correction-mode grid, and standalone beside the technology
   "More than focus" argument, where the claim and its limit have to be readable in one glance */
.numfeat .nf.nf-limit { background: #fff6f4; border-color: #f3c7c0; }
.limitcard {
  background: #fff6f4; border: 1px solid #f3c7c0; border-radius: var(--radius);
  padding: 1.7rem 1.7rem 1.8rem;
}
.numfeat .nf.nf-limit h3, .limitcard h3 { color: var(--accent-text); }
.limitcard h3 { margin-bottom: .55rem; }
.limitcard p { color: var(--slate); font-size: .97rem; }
.limitcard p + p { margin-top: .7rem; }

/* stat / chip row */
.chips { display: flex; flex-wrap: wrap; gap: .6rem; }
.chip {
  font-family: var(--font-display); font-size: .85rem; font-weight: 600; color: var(--navy-700);
  background: var(--brand-050); border: 1px solid var(--brand-100); padding: .35rem .9rem; border-radius: 999px;
}

/* logo strip */
.logostrip { --logo-h: 52px; display: flex; flex-wrap: wrap; gap: clamp(1.2rem, 3.5vw, 2.8rem); align-items: center; justify-content: center; }
.logostrip.compact { --logo-h: 40px; }   /* customer wall: many logos, sits quieter than the funder strip */
.logostrip.left { justify-content: flex-start; }   /* in a two-column band the strip sits under a left-aligned heading, so centring strands it mid-column */
.logostrip img { height: var(--logo-h); width: auto; filter: grayscale(1); opacity: .65; transition: filter .2s, opacity .2s; }
.logostrip img:hover { filter: none; opacity: 1; }
.partner-logo { display: flex; align-items: center; height: 88px; margin-bottom: 1rem; }
.partner-logo img { max-height: 88px; max-width: 100%; width: auto; height: auto; object-fit: contain; object-position: left center; }

.logostrip-label {
  text-align: center; font-family: var(--font-display); font-size: .8rem; font-weight: 600;
  letter-spacing: .14em; text-transform: uppercase; color: var(--muted);
  margin: clamp(2.2rem, 4.5vw, 3.2rem) 0 1.5rem;
}

/* ------------------------------------------------------------------ team */
/* minmax(0, …) rather than a bare 1fr: a track's automatic minimum is its min-content width, and one
   unbreakable role ("Semiconductor/AR/Photonics") is 195 px wide — enough to push the second column
   off a 320 px screen and leave the pair visibly uneven on every phone. */
.team-grid { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 2rem 1.4rem; }
@media (max-width: 980px) { .team-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
@media (max-width: 640px) { .team-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
/* Flex column + `margin-top: auto` on the icon: grid stretches every card to the row height, so the
   profile links line up even where a name or a role wraps to two lines (at 2-up on a phone, most do). */
.person { text-align: center; display: flex; flex-direction: column; }
/* the attribute selector matches the build's width/height attributes, so this outranks the global
   `img[width][height] { width: auto }` — without it the avatar takes its width from the photo's aspect
   ratio and every non-square portrait renders as an ellipse instead of a circle. */
.person img, .person img[width][height] {
  width: 132px; max-width: 100%; aspect-ratio: 1; height: auto;
  border-radius: 50%; object-fit: cover; margin: 0 auto .8rem;
  border: 3px solid #fff; box-shadow: var(--shadow-md);
}
.person .name { font-family: var(--font-display); font-weight: 600; color: var(--ink); line-height: 1.3; }
.person .role { font-size: .88rem; color: var(--muted); margin-bottom: .55rem; }   /* was the icon's margin-top, now that the icon is pushed down */
.person .plink {
  display: inline-flex; width: 32px; height: 32px; border-radius: 9px; margin-top: auto; align-self: center;
  background: var(--brand-050); color: var(--brand-600); align-items: center; justify-content: center;
  transition: background .15s, color .15s;
}
.person .plink:hover { background: var(--brand-600); color: #fff; }
.person .plink svg { width: 15px; height: 15px; }

/* ------------------------------------------------------------------ news */
.filterbar { display: flex; flex-wrap: wrap; gap: .5rem; margin-bottom: 2rem; }
.filterbar button {
  font-family: var(--font-display); font-size: .9rem; font-weight: 600; cursor: pointer;
  padding: .5rem 1.2rem; border-radius: 999px; border: 1.5px solid var(--line); background: #fff; color: var(--slate);
}
.filterbar button:hover { border-color: var(--brand-400); color: var(--brand-600); }
.filterbar button.active { background: var(--navy-800); border-color: var(--navy-800); color: #fff; }
.newscard .media { aspect-ratio: 16 / 9; }
/* the caption block stays quiet next to the thumbnail: tighter box, smaller type — the image leads, the text labels it */
.newscard .body { padding: 1rem 1.15rem 1.15rem; gap: .35rem; }
.newscard .meta { display: flex; align-items: center; gap: .55rem; font-size: .76rem; color: var(--muted); }
.newscard .meta .cat { color: var(--brand-600); font-weight: 600; text-transform: capitalize; }
.newscard h3 { font-size: 1rem; line-height: 1.32; }

/* news page: one entry per post in two columns per year (adopted 2026-09-09 from the "Editorial rows" design variant).
   Fixed 4:3 thumbnail, kind · date, two-line title and two-line summary — every entry the same height, so a long
   title never makes a row ragged. The year heading is a slim divider; a year emptied by the category filter hides. */
.news-year { padding: 1rem 0 .6rem; }
.news-year > h2 { display: flex; align-items: center; gap: 1rem; font-family: var(--font-display); font-size: .95rem; font-weight: 600; letter-spacing: .08em; text-transform: uppercase; color: var(--muted); margin-bottom: 1.1rem; }
.news-year > h2::after { content: ""; flex: 1; height: 1px; background: var(--line); }
.news-list { list-style: none; display: grid; grid-template-columns: 1fr 1fr; gap: 1rem 1.6rem; margin-bottom: 1.4rem; }
.news-row a { display: grid; grid-template-columns: 8.5rem 1fr; column-gap: 1.1rem; padding: .8rem; height: 100%; border: 1px solid var(--line); border-radius: var(--radius-sm); background: #fff; color: inherit; text-decoration: none; transition: border-color .15s; }
.news-row a:hover { border-color: var(--brand-400); }
.news-row .thumb { display: block; aspect-ratio: 4 / 3; border-radius: 6px; overflow: hidden; background: var(--paper); }
.news-row .thumb picture, .news-row .thumb img { display: block; width: 100%; height: 100%; }
.news-row .thumb img { object-fit: cover; }
.news-row .thumb.contain img { object-fit: contain; padding: .4rem; }   /* diagrams and wide graphics: show whole, never crop */
.news-row .text { display: flex; flex-wrap: wrap; align-content: start; column-gap: .5rem; row-gap: .25rem; }
.news-row .kind { color: var(--brand-600); font-size: .8rem; font-weight: 600; text-transform: capitalize; }
.news-row .when { color: var(--muted); font-size: .82rem; }
.news-row .when::before { content: "\00b7"; margin-right: .5rem; }
.news-row .head { flex-basis: 100%; font-weight: 600; color: var(--navy-800); font-size: 1.02rem; line-height: 1.35; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
.news-row .excerpt { flex-basis: 100%; color: var(--slate); font-size: .9rem; line-height: 1.45; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
.news-year:not(:has(.news-row:not([style*="none"]))) { display: none; }
@media (max-width: 900px) { .news-list { grid-template-columns: 1fr; } }
@media (max-width: 480px) { .news-row a { grid-template-columns: 6.5rem 1fr; } .news-row .excerpt { display: none; } }

/* ---------------------------------------------------------------- article */
.article { max-width: 760px; margin: 0 auto; }
.article .meta { display: flex; flex-wrap: wrap; gap: .8rem; align-items: center; color: var(--muted); font-size: .9rem; margin: 1rem 0 2rem; }
.article .meta .cat { background: var(--brand-050); color: var(--brand-600); font-weight: 600; padding: .15rem .8rem; border-radius: 999px; font-size: .8rem; text-transform: capitalize; }
.article h1 { font-size: clamp(1.8rem, 3.6vw, 2.6rem); }
/* --iw is the cover's intrinsic width: never blow a small source up to the column width.
   Two 2020 journal-cover thumbnails are 179 px wide and were the blurriest images on the site. */
.article .cover { border-radius: var(--radius); overflow: hidden; margin: 0 auto 2.2rem; max-width: min(100%, var(--iw, 100%)); box-shadow: var(--shadow-md); }
.article .cover img { width: 100%; }
.article-body > * + * { margin-top: 1.1rem; }
.article-body h2, .article-body h3 { margin-top: 2rem; }
/* Posts carry raw DOI/journal URLs as link text. A URL has no space to break at, so on a phone it runs
   past the right edge and drags the whole page with it — break inside the word when nothing else fits. */
.article-body a { overflow-wrap: anywhere; }
.article .cover img { width: 100%; max-height: 380px; object-fit: cover; }
.article .cover.contain img { max-height: none; object-fit: contain; }   /* cover_fit: contain — journal headers, diagrams: never crop */
/* cover_caption: a hero that carries its own caption (scale bar, photo credit, who-is-who).
   The figure owns the bottom margin and the --iw clamp so the caption tracks the image width,
   not the column width; the inner .cover keeps the rounded/clipped frame. */
.article .cover-fig { margin: 0 auto 2.2rem; max-width: min(100%, var(--iw, 100%)); }
.article .cover-fig .cover { margin-bottom: 0; }
.article .cover-fig figcaption { color: var(--muted); font-size: .88rem; line-height: 1.5; margin-top: .9rem; text-align: center; }
/* post-body images: one shape for photos and transparent product renders alike.
   Without a cap a portrait render (900x1152) fills 760x973 and swamps the article;
   a box-shadow on a transparent PNG draws a shadow round empty space, not the object. */
.article-body .post-figure { --mh: 420px; width: fit-content; max-width: 100%; margin: 2rem auto; padding: 1.25rem; background: var(--bg); border: 1px solid var(--line); border-radius: var(--radius); display: flex; flex-direction: column; align-items: center; }
/* --iw / --ar (intrinsic width, aspect ratio) are written by the build. A definite width lets the browser reserve
   the box from the aspect ratio before a lazy image loads; with width:auto the unloaded image is 0x0, every figure
   collapses to its padding and the page jumps as they arrive. The portrait cap --mh is applied on the width axis
   (cap x ratio) rather than as max-height, so a capped image keeps its aspect ratio instead of being squashed. */
.article-body .post-figure img { width: min(var(--iw), calc(var(--mh) * var(--ar))); max-width: 100%; height: auto; margin: 0; border-radius: var(--radius-sm); box-shadow: none; }
.article-body .post-figure--logo { --mh: 96px; padding: 1rem 1.4rem; }
.article-body .post-figure--logo img { border-radius: 0; }
/* a run of funder / partner logos is one credit strip: the old EXIST-era posts end on five or six
   of them, and stacked full-width figures dwarf the article they belong to. */
.article-body .logo-row { display: flex; flex-wrap: wrap; align-items: center; justify-content: center; gap: .8rem; margin: 2rem 0; }
.article-body .logo-row .post-figure { --mh: 72px; margin: 0; }
/* width:0 + min-width:100% keeps a long caption from widening the fit-content box past its image: the caption
   contributes nothing to the figure's width, then stretches to whatever width the image set. */
.article-body .post-figure figcaption { width: 0; min-width: 100%; color: var(--muted); font-size: .88rem; line-height: 1.5; margin-top: .9rem; text-align: center; }
@media (max-width: 700px) { .article-body .post-figure { --mh: 300px; padding: .8rem; } }
.article-body blockquote { margin: 1.6rem 0; padding: .2rem 0 .2rem 1.25rem; border-left: 3px solid var(--brand-400); color: var(--ink); font-size: 1.08rem; line-height: 1.55; }
.article-body blockquote p { margin: 0; }
.article-body ul { padding-left: 1.4rem; }
.article-body li { margin: .45rem 0; }
.article-body .updated { color: var(--muted); font-size: .88rem; font-style: italic; }
.backlink { display: inline-flex; align-items: center; gap: .4rem; font-weight: 600; font-family: var(--font-display); margin-bottom: 1.6rem; }
.backlink::before { content: "←"; }

/* ------------------------------------------------------------ distributor */
.repcard { display: flex; flex-direction: column; gap: .3rem; position: relative; }
/* territory flags, top right. Fixed height, free width: the ratios run from 1:2 (UK) to square (the worldwide
   globe), so a common height is what lines them up — and the row is right-anchored, so a second flag grows leftwards. */
.repcard .flags { position: absolute; top: .7rem; right: .7rem; z-index: 1; display: flex; gap: 4px; }
.repcard .flags img { display: block; height: 16px; width: auto; max-width: 34px;
  border-radius: 2px; border: 1px solid rgba(11, 29, 49, .16); box-shadow: 0 1px 3px rgba(11, 29, 49, .18); }
.repcard .logo-box { height: 84px; display: flex; align-items: center; padding: 0 1.5rem; background: linear-gradient(180deg, var(--brand-050), #e9f0f6); border-bottom: 1px solid var(--line); }
.repcard .logo-box img { max-height: 56px; width: auto; max-width: 200px; object-fit: contain; }
.repcard .body { gap: .35rem; }
.repcard .region { font-size: .8rem; font-weight: 600; letter-spacing: .1em; text-transform: uppercase; color: var(--accent-text); }
.repcard .contact { font-size: .93rem; color: var(--slate); display: grid; gap: .3rem; }
.repcard .contact a { word-break: break-all; }
.repcard .contact .addr { color: var(--muted); font-size: .88rem; }

/* -------------------------------------------------------- distributor map */
.mapbox { position: relative; width: 100%; max-width: 1020px; margin: 0 auto; }
.mapbox .worldmap { width: 100%; height: auto; display: block; }
.mapbox .worldmap path { fill: #c6d8e9; stroke: #fff; stroke-width: .8; stroke-linejoin: round; }   /* country outlines: continents read as continents */
/* leader lines + logo chips (desktop): every partner is visible without hovering, like the old site */
.mapbox .leaders { position: absolute; inset: 0; width: 100%; height: 100%; pointer-events: none; z-index: 1; }
.mapbox .leaders line { stroke: var(--navy-700); stroke-width: 1.2px; vector-effect: non-scaling-stroke; opacity: .55; }
.maplabel { position: absolute; left: var(--x); top: var(--y); transform: translate(-50%, -50%); z-index: 3;
  display: flex; align-items: center; justify-content: center; height: 40px; padding: 0 .7rem; background: #fff; border: 1px solid var(--line);
  border-radius: 10px; box-shadow: var(--shadow-sm); transition: transform .15s ease, box-shadow .15s ease; }
.maplabel img { height: 26px; width: auto; max-width: 132px; object-fit: contain; }
.maplabel:hover, .maplabel:focus-visible { transform: translate(-50%, -50%) scale(1.06); box-shadow: var(--shadow-md); }
.pin { position: absolute; left: var(--x); top: var(--y); transform: translate(-50%, -50%); z-index: 2; padding: 8px; }   /* padding = hit area; the translate keeps the dot centred on the coordinate */
.pin-dot {
  display: block; width: 15px; height: 15px; border-radius: 50%;
  background: var(--accent); border: 2.5px solid #fff;
  box-shadow: 0 1px 6px rgba(11, 29, 49, .35); position: relative; transition: transform .15s ease, background .15s ease;
}
.pin.hq .pin-dot { background: var(--navy-700); }
.pin-dot::after {
  content: ""; position: absolute; inset: -7px; border-radius: 50%;
  border: 2px solid var(--accent); opacity: .55; animation: pinpulse 2.4s ease-out infinite;
}
@keyframes pinpulse {
  0% { transform: scale(.5); opacity: .6; }
  70% { transform: scale(1.5); opacity: 0; }
  100% { transform: scale(1.5); opacity: 0; }
}
.pin:hover .pin-dot, .pin:focus-visible .pin-dot { transform: scale(1.35); background: var(--navy-900); }
.pin-card {
  position: absolute; bottom: calc(100% + 12px); left: 50%;
  transform: translateX(-50%) translateY(4px);
  background: #fff; border: 1px solid var(--line); border-radius: 12px; box-shadow: var(--shadow-lg);
  padding: .8rem 1rem; width: max-content; max-width: 240px; text-align: center;
  opacity: 0; visibility: hidden; pointer-events: none; z-index: 3;
  transition: opacity .18s ease, transform .18s ease, visibility .18s;
}
.pin-card img { height: 30px; width: auto; max-width: 170px; object-fit: contain; margin: 0 auto .45rem; }
.pin-card strong { display: block; font-family: var(--font-display); font-size: .92rem; color: var(--ink); line-height: 1.25; }
.pin-card strong + span + strong { margin-top: .45rem; }
.pin-card span { display: block; font-size: .78rem; color: var(--muted); }
.pin-card::after {
  content: ""; position: absolute; top: 100%; left: 50%; transform: translateX(-50%);
  border: 7px solid transparent; border-top-color: #fff;
}
.pin:hover .pin-card, .pin:focus-visible .pin-card { opacity: 1; visibility: visible; transform: translateX(-50%) translateY(0); }
.pin.e .pin-card { left: auto; right: -14px; transform: translateY(4px); }
.pin.e:hover .pin-card, .pin.e:focus-visible .pin-card { transform: translateY(0); }
.pin.e .pin-card::after { left: auto; right: 16px; transform: none; }
.map-hint { text-align: center; font-size: .85rem; color: var(--muted); margin-top: 1rem; }
@media (prefers-reduced-motion: reduce) { .pin-dot::after { animation: none; opacity: 0; } }
@media (max-width: 640px) {
  .mapbox { margin: 0 calc(-1 * clamp(1.1rem, 4vw, 2rem)); }   /* bleed to the screen edge — every pixel of map width counts */
  .pin-card { display: none; }
  .pin { padding: 10px; }
  .pin-dot { width: 12px; height: 12px; border-width: 2px; }
  .maplabel, .mapbox .leaders { display: none; }   /* phones: dots only — the contact cards follow right below the map */
  /* Eight dots on a 355px-wide map put the three Asian pins 4px apart: no thumb can pick one, and
     hitting the neighbour jumps silently to the wrong partner. The chips, leader lines and hover
     cards are already off at this width, so the dots stay as a picture of the footprint and the rep
     cards right below do the navigating. */
  .pin { pointer-events: none; }
  .pin-dot::after { inset: -5px; }
  .map-hint { display: none; }
}

/* ------------------------------------------------------------- downloads */
.dl-list { display: grid; gap: .8rem; }
.dl-item {
  display: flex; align-items: center; gap: 1.1rem; padding: 1.05rem 1.3rem;
  background: #fff; border: 1px solid var(--line); border-radius: var(--radius-sm);
  color: var(--ink); font-weight: 500; transition: border-color .15s, box-shadow .15s;
}
.dl-item:hover { border-color: var(--brand-400); box-shadow: var(--shadow-sm); color: var(--ink); }
.dl-item .ic { flex: 0 0 auto; width: 40px; height: 40px; border-radius: 10px; background: var(--brand-050); color: var(--brand-600); display: flex; align-items: center; justify-content: center; }
.dl-item .ic svg { width: 20px; height: 20px; }
.dl-item small { display: block; color: var(--muted); font-weight: 400; }
.dl-item .go { margin-left: auto; color: var(--brand-500); font-weight: 700; }

/* ---------------------------------------------------------------- bands */
.cta-band { border-radius: calc(var(--radius) + 6px); background:
    radial-gradient(1200px 400px at 85% -20%, rgba(79, 151, 204, .35), transparent),
    linear-gradient(120deg, var(--navy-900), var(--navy-800));
  color: #d9e6f2; padding: clamp(2.2rem, 5vw, 3.6rem); position: relative; overflow: hidden; }
.cta-band::after { content: ""; position: absolute; left: 0; right: 0; bottom: 0; height: 3px;
  background: linear-gradient(90deg, transparent, var(--accent), transparent); }
.cta-band h2, .cta-band h3 { color: #fff; margin-bottom: .6rem; }
.cta-band p { max-width: 56em; color: #b9cddd; }
.cta-band .actions { margin-top: 1.6rem; display: flex; flex-wrap: wrap; gap: .8rem; }
/* Body links inherit --brand-600, which is 2.0:1 on this navy — unreadable. Buttons bring their own colours;
   everything else takes a light tint (7.9:1 on navy-800) and an underline, so it still reads as a link
   against the #b9cddd body text. */
.cta-band a:not(.btn) { color: #9fcbec; text-decoration: underline; text-underline-offset: 3px; }
.cta-band a:not(.btn):hover { color: #fff; }
/* several upcoming events: side-by-side boxes (one event keeps the full-width band) */
.event-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 320px), 1fr)); gap: 1.5rem; }
.cta-band.card { padding: clamp(1.8rem, 3.5vw, 2.6rem); display: flex; flex-direction: column; }
.cta-band.card h2, .cta-band.card h3 { color: #fff; margin: .2rem 0 .5rem; font-size: 1.35rem; line-height: 1.25; }
.cta-band.card p { flex: 1; }
.cta-band.card .actions { margin-top: 1.2rem; }
/* the event's own mark sits beside the heading. Both marks we carry are dark ink drawn for white
   paper, so each gets its own white chip rather than sitting on the navy directly. */
.event-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 1.2rem; }
.event-head > div { min-width: 0; }
.event-logo { --chip-w: 128px; --chip-h: 74px; --chip-pad: .55rem;
  flex: 0 0 auto; display: grid; place-items: center; width: var(--chip-w); height: var(--chip-h);
  padding: var(--chip-pad) calc(var(--chip-pad) + .2rem);
  border-radius: 12px; background: #fff; box-shadow: 0 6px 18px rgba(0, 0, 0, .22); }
.event-logo picture { display: contents; }   /* the build's <picture> wrapper must not become the grid item */
/* a tall mark is capped off the chip's own height: a percentage max-height has no definite grid row to
   resolve against here, and a portrait logo then spills past the white ground */
.event-logo img { width: auto; height: auto; max-width: 100%;
  max-height: calc(var(--chip-h) - 2 * var(--chip-pad)); object-fit: contain; }
.cta-band:not(.card) .event-logo { --chip-w: 156px; --chip-h: 90px; --chip-pad: .7rem; }
/* narrow: the chip beside the text squeezes the eyebrow onto two lines, so give it its own row above */
@media (max-width: 560px) {
  .event-head { flex-direction: column-reverse; align-items: flex-start; gap: .1rem; }
  .event-logo, .cta-band:not(.card) .event-logo { --chip-w: 104px; --chip-h: 60px; --chip-pad: .45rem; margin-bottom: .9rem; }
}

/* ---------------------------------------------------------------- footer */
.site-footer { background: var(--navy-950); color: #a9becf; font-size: .95rem; }
.site-footer .cols { display: grid; grid-template-columns: 1.7fr 1fr 1fr 1fr 1.3fr; gap: 2.4rem; padding: clamp(2.6rem, 6vw, 4rem) 0 2.4rem; }
@media (max-width: 900px) { .site-footer .cols { grid-template-columns: 1fr 1fr; } }
@media (max-width: 560px) { .site-footer .cols { grid-template-columns: 1fr; } }
.site-footer .brandcol img { height: 56px; width: auto; margin-bottom: 1rem; }
.site-footer .brandcol p { max-width: 30em; font-size: .9rem; }
.site-footer .fh { font-family: var(--font-display); font-size: .8rem; font-weight: 600; letter-spacing: .16em; text-transform: uppercase; color: #6f8aa3; margin-bottom: 1rem; }
.site-footer ul { list-style: none; display: grid; gap: .45rem; }
.site-footer a { color: #c6d7e4; }
.site-footer a:hover { color: #fff; }
.site-footer .legal { border-top: 1px solid rgba(255, 255, 255, .09); padding: 1.3rem 0; display: flex; flex-wrap: wrap; gap: 1rem; justify-content: space-between; font-size: .85rem; color: #6f8aa3; }
.site-footer .legal a { color: #8fa9bf; }
.site-footer .soc { display: flex; gap: .7rem; margin-top: 1.2rem; }
.site-footer .soc a { display: flex; width: 38px; height: 38px; border-radius: 10px; background: rgba(255,255,255,.08); align-items: center; justify-content: center; }
.site-footer .soc a:hover { background: var(--brand-600); }
.site-footer .soc svg { width: 18px; height: 18px; fill: #fff; }
/* The footer button carries the page-wide pill, but not the page-wide size: next to 0.95rem link columns a
   1.5rem-padded button reads as a stray call to action from another page. Footer scale, and the label centred
   so that a narrow window wraps it symmetrically instead of leaving a ragged second line. */
.site-footer .btn { margin-top: 1.3rem; font-size: .9rem; padding: .62rem 1.25rem; justify-content: center; text-align: center; }

/* --------------------------------------------------------------- helpers */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
}
.center { text-align: center; }
.skip {
  position: absolute; left: -999px; top: 0; background: #fff; color: var(--navy-800);
  padding: .6rem 1rem; z-index: 100; border-radius: 0 0 8px 0; font-weight: 600;
}
.skip:focus { left: 0; }
.badge-soon { display: inline-block; font-size: .72rem; font-weight: 700; letter-spacing: .08em; text-transform: uppercase; background: #fdeeec; color: var(--accent-text); border-radius: 999px; padding: .2rem .7rem; vertical-align: middle; margin-left: .5rem; }

/* ── added 2026-09-05: spec tables + titled feature lists (live-site re-sync) ── */
.spectable{width:100%;border-collapse:collapse;font-size:.95rem;margin-top:1rem}
.spectable th,.spectable td{text-align:left;vertical-align:top;padding:.7rem .9rem;border-bottom:1px solid var(--line,#e5e9ef)}
.spectable th{font-weight:600;color:var(--ink,#0f1b2d);width:34%;background:rgba(15,27,45,.03)}
.spectable thead th{background:none;color:var(--muted,#5b6b7f);font-size:.8rem;letter-spacing:.06em;text-transform:uppercase}
.spectable td .soon{color:var(--muted,#5b6b7f)}   /* announced, not shipping yet — greyed so the line reads as a plan, not a capability */
@media (max-width:640px){
  /* phones: each row becomes a label/value pair — no grey band, one hairline per row, room to breathe */
  .spectable,.spectable tbody,.spectable tr,.spectable th,.spectable td{display:block;width:auto}
  .spectable thead{display:none}
  .spectable tr{padding:.75rem 0;border-bottom:1px solid var(--line)}
  .spectable th,.spectable td{padding:0;border:0;background:none}
  .spectable th{font-size:.82rem;font-weight:600;letter-spacing:.02em;color:var(--brand-600);margin-bottom:.2rem}
  .spectable td{font-size:1rem;line-height:1.5}
}
/* logo variant of .spectable: the label column carries each package's own mark beside its name,
   on the same white plate the integration tiles up-page use — so one product reads the same in
   both places. A row flagged `next` is not shipping yet: dashed plate + a "Next:" kicker. */
.swspectable th .swcell { display: flex; align-items: center; gap: .7rem; }
.swspectable .plate { display: grid; place-items: center; flex: none; width: 116px; height: 44px; padding: .3rem .4rem;
  background: #fff; border: 1px solid var(--line); border-radius: var(--radius-sm); }
/* wide wordmarks (Thorlabs, ScanImage, µManager) are width-bound, not height-bound: the plate has
   to be wide enough that they still read at table size */
.swspectable .plate img { max-height: 26px; max-width: 100%; width: auto; object-fit: contain; }
.swspectable .plate.dark { background: #1c1f26; border-color: #1c1f26; }   /* Nikon publishes the NIS-Elements mark white-on-dark only */
.swspectable .plate.text { font-family: var(--font-display); font-size: .78rem; font-weight: 600; color: var(--navy-700); }
.swspectable .swname { font-weight: 600; }
.swspectable .swname i { display: block; font-style: normal; font-size: .68rem; font-weight: 700; letter-spacing: .07em;
  text-transform: uppercase; color: var(--brand-600); }
.swspectable tr.soon .plate { border-style: dashed; border-color: var(--brand-400); }
@media (max-width:640px){
  /* phone rows stack label over value; the plate shrinks but stays on the name's line */
  .swspectable th .swcell { gap: .55rem; margin-bottom: .35rem; }
  .swspectable .plate { width: 96px; height: 38px; }
  .swspectable .plate img { max-height: 22px; }
  .swspectable .swname { font-size: 1.05rem; font-weight: 700; }
  .swspectable .swname i { font-size: .72rem; }
  .swspectable td { font-size: .95rem; color: var(--slate); }
}
/* comparison variant of .spectable: label column + one column per product variant.
   Desktop splits the value columns evenly; the phone rules above stack every cell, so
   each value announces its own column via data-col. (Design/Mobile lanes: restyle freely.) */
.comparetable td{width:33%}
@media (max-width:640px){
  .comparetable td::before{content:attr(data-col) " — ";font-weight:600;color:var(--brand-600)}
  .comparetable td{margin-top:.25rem}
}
/* ── company timeline (added 2026-09-09) ──────────────────────────────────
   Chapters, not a log: each row is a picture and a short read, alternating
   sides. The rail behind them keeps the chronology readable at a glance. */
.timeline{list-style:none;margin:0;padding:0;display:grid;gap:clamp(2.2rem,4.5vw,3.8rem)}
.tl-chapter{display:grid;grid-template-columns:minmax(0,7fr) minmax(0,9fr);gap:clamp(1.5rem,4vw,3.5rem);align-items:center}
.tl-chapter.flip .tl-media{order:2}
.tl-media{border-radius:var(--radius);overflow:hidden;box-shadow:var(--shadow-md);background:var(--paper);
  max-width:400px;width:100%}
.tl-chapter:not(.flip) .tl-media{margin-left:auto}   /* hug the text column, not the page edge */
.tl-media img{display:block;width:100%;height:100%;aspect-ratio:4/3;object-fit:cover}
/* logos, renders and infographics letterbox instead of cropping. The box owns the
   ratio and the padding; the <picture> is transparent to layout so the image can
   fill what is left. Padding on the <img> itself collapses it — it is a replaced
   element carrying its own aspect-ratio. */
.tl-media.contain{background:var(--paper);aspect-ratio:4/3;display:grid;place-items:center;
  grid-template:100%/100%;   /* a definite track, so the image's height:100% has something to resolve against */
  padding:clamp(1rem,3vw,2.2rem)}
.tl-media.contain picture{display:contents}
/* size from the intrinsic ratio, capped by the box: height:100% does not resolve against a
   centred grid area, so a PORTRAIT image used to overflow the 4/3 box and get cropped by
   overflow:hidden. Landscape images letterbox exactly as before. */
/* pin to the single explicit cell: with <picture display:contents> the img is auto-placed
   into an implicit row, where height:100% has nothing to resolve against and a portrait
   image overflows the 4/3 box */
.tl-media.contain img{grid-area:1/1;width:100%;height:100%;max-width:100%;max-height:100%;aspect-ratio:auto;object-fit:contain}
/* a PORTRAIT render in a 4/3 box is height-limited: it fills barely half the card width and the
   rest reads as empty. `box: tall` in timeline.json squares the box and eases the padding, so the
   subject sizes to the box rather than to the leftover height. */
.tl-media.contain.tall{aspect-ratio:1/1;padding:clamp(.6rem,1.6vw,1.2rem)}
.tl-year{font-family:var(--font-display);font-size:clamp(1.9rem,3.6vw,2.6rem);font-weight:600;line-height:1;
  color:var(--brand-500);letter-spacing:-.02em;margin-bottom:.4rem}   /* 4.8:1 — was a tint at 1.2:1 */
.tl-chapter.tl-funding .tl-year,.tl-chapter.tl-company .tl-year{color:var(--accent-text)}
.tl-chapter.tl-vision  .tl-year{color:var(--navy-700)}
.tl-body h2{font-size:clamp(1.4rem,2.6vw,1.85rem);margin-bottom:.6rem}
.tl-text{color:var(--slate)}
.tl-links{margin-top:1rem;font-size:.92rem;display:flex;flex-wrap:wrap;gap:.5rem;align-items:baseline}
.tl-links a{color:var(--brand-600);font-weight:500}
.tl-links a:hover{text-decoration:underline}
.tl-links span{color:var(--line)}
@media (max-width:760px){
  .tl-chapter{grid-template-columns:1fr;gap:1.2rem}
  .tl-media{max-width:none}
  .tl-chapter:not(.flip) .tl-media{margin-left:0}
  .tl-chapter.flip .tl-media{order:0}   /* picture always leads on phones */
}
.featlist{list-style:none;margin:1.2rem 0 0;padding:0;display:grid;gap:1rem}
.featlist li{padding-left:1.6rem;position:relative}
.featlist li::before{content:"";position:absolute;left:0;top:.55em;width:.7rem;height:.7rem;border-radius:50%;background:var(--accent,#e2493b)}
.featlist strong{display:block;color:var(--ink,#0f1b2d);margin-bottom:.15rem}
.featlist span{color:var(--slate,#3d4b5c)}
.figpair{display:grid;grid-template-columns:1fr 1fr;gap:1rem}
@media (max-width:560px){.figpair{grid-template-columns:1fr}}
/* application screenshots: square corners (a rounded window chrome reads as a rendering fault)
   and never upscaled past their native width — a 357 px panel blown up to a half column was
   soft, and the padding around it read as an empty white field */
.shots .visual.shot{border-radius:0;box-shadow:none;overflow:visible;display:flex;justify-content:center;align-items:flex-start}
.shots .visual.shot img{width:auto;max-width:100%;height:auto;border:1px solid var(--line)}
.shots .figpair{align-items:start}
/* the screenshots no longer fill their column, so a centred .split left the prose floating
   half a column down from the first window — both columns start at the top instead */
.split:has(> .shots){align-items:start}


/* ── video (added 2026-09-07) ─────────────────────────────────────────── */
video { max-width: 100%; display: block; }
.video { position: relative; border-radius: var(--radius); overflow: hidden; box-shadow: var(--shadow-md); background: var(--navy-950); }
.video video { width: 100%; height: auto; }
.video.loop video { object-fit: cover; }
.video .badge { position: absolute; left: .9rem; top: .9rem; font-family: var(--font-display); font-size: .74rem; font-weight: 600; letter-spacing: .12em; text-transform: uppercase; color: #fff; background: rgba(7,18,32,.55); padding: .3rem .7rem; border-radius: 999px; pointer-events: none; }
@media (max-width: 640px) { .video .badge { font-size: .8rem; } }


/* ── download gate (added 2026-09-08) ─────────────────────────────────── */
.dl-gate[hidden] { display: none; }
.dl-gate { position: fixed; inset: 0; z-index: 900; background: rgba(7,18,32,.62); backdrop-filter: blur(4px); display: flex; align-items: center; justify-content: center; padding: 1rem; }
.dl-gate-card { position: relative; background: #fff; color: var(--ink); border-radius: var(--radius); box-shadow: var(--shadow-lg); padding: 2rem 2rem 1.6rem; width: min(520px, 100%); }
.dl-gate-close { position: absolute; right: .8rem; top: .6rem; border: 0; background: none; font-size: 1.6rem; line-height: 1; color: var(--muted); cursor: pointer; }
.dl-gate-text { color: var(--slate); margin: .6rem 0 1.1rem; font-size: .97rem; }
.dl-gate-form label { display: block; font-weight: 600; font-size: .9rem; margin-bottom: .8rem; }
/* font-size stays at 1rem: `font: inherit` alone would take the label's .9rem, and iOS Safari zooms the
   page in whenever a focused field is under 16 px — the same reason .form-grid sets it explicitly. */
.dl-gate-form input[type=email] { display: block; width: 100%; margin-top: .35rem; padding: .7rem .9rem; font: inherit; font-size: 1rem; border: 1.5px solid var(--line); border-radius: var(--radius-sm); }
.dl-gate-form input[type=email]:focus { border-color: var(--brand-500); outline: none; }
.dl-gate-check { font-weight: 500 !important; color: var(--slate); display: flex !important; gap: .55rem; align-items: flex-start; }
.dl-gate-check input { margin-top: .3rem; }
.dl-gate-legal { font-size: .8rem; color: var(--muted); margin-bottom: 1rem; }
.dl-gate-error { color: var(--accent-text); font-size: .9rem; margin-top: .6rem; }


/* ── RFQ form (added 2026-09-08) ─────────────────────────────────────── */
.rfq-card { width: min(860px, 100%); max-height: calc(100vh - 2rem); max-height: calc(100dvh - 2rem); overflow: auto; }
.rfq-form fieldset { border: 0; padding: 0; margin: 0 0 1.2rem; }
.rfq-form legend { font-family: var(--font-display); font-weight: 600; margin-bottom: .6rem; }
.rfq-form .checks { display: grid; grid-template-columns: repeat(auto-fill, minmax(190px, 1fr)); gap: .5rem .9rem; }
.rfq-form .check { display: flex; gap: .55rem; align-items: flex-start; font-weight: 500; font-size: .93rem; cursor: pointer; }
/* WCAG 2.2 target size (2.5.8): a default checkbox renders ~13px, well under the 24px minimum and awkward to
   hit on a phone. Scaling the box itself keeps the native control — and its focus ring — rather than faking
   one out of a pseudo-element. The consent ticks are the last step before submitting; they must not be fiddly. */
.rfq-form .check input { margin-top: .15rem; width: 24px; height: 24px; min-width: 24px; accent-color: var(--brand-600); }
.rfq-form .check + .check { margin-top: .5rem; }                       /* spacing between adjacent targets counts too */
.rfq-form .check small { display: block; color: var(--muted); font-weight: 400; font-size: .8rem; }
.rfq-form .check.consent { margin-top: 1rem; }
.form-grid { display: grid; grid-template-columns: 1fr 1fr; gap: .8rem 1rem; }
.form-grid label, .rfq-form > label:not(.check) { display: block; font-size: .88rem; font-weight: 600; color: var(--ink); }
.form-grid .span2 { grid-column: 1 / -1; }
.form-grid input, .form-grid textarea { display: block; width: 100%; margin-top: .3rem; padding: .62rem .8rem; font: inherit; font-size: 1rem; border: 1.5px solid var(--line); border-radius: var(--radius-sm); background: #fff; color: var(--ink); }
.form-grid input:focus, .form-grid textarea:focus { border-color: var(--brand-500); outline: none; }
.form-grid input:user-invalid { border-color: var(--accent); }
/* optional fields step back: a grey label and a lighter border, so the required path reads first.
   Contrast stays above 4.5:1 — de-emphasised, never unreadable. */
.form-grid label.opt { color: var(--slate); font-weight: 500; }
.form-grid label.opt small { font-weight: 400; color: var(--muted); font-size: .82em; }
.form-grid label.opt input { border-color: color-mix(in srgb, var(--line) 60%, #fff); }
.form-grid label.opt input:focus { border-color: var(--brand-500); }
.rfq-form .actions { display: flex; align-items: center; gap: 1rem; flex-wrap: wrap; }
.rfq-status { font-size: .92rem; color: var(--slate); }
.rfq-error { color: var(--accent-text); font-size: .92rem; margin-top: .6rem; }
.rfq-form.inline { background: #fff; border: 1px solid var(--line); border-radius: var(--radius); padding: clamp(1.2rem, 3vw, 2rem); }
.rfq-done { text-align: center; padding: 1rem 0; }
.rfq-done h3 { margin-bottom: .5rem; }
@media (max-width: 640px) {
  .form-grid { grid-template-columns: 1fr; }
  /* phones: both dialogs become bottom sheets — the close button and the submit row stay pinned while the form scrolls */
  .dl-gate { padding: 0; align-items: flex-end; }
  .dl-gate-card { width: 100%; max-height: 100dvh; overflow: auto; border-radius: var(--radius) var(--radius) 0 0; padding: .6rem 1.1rem calc(1rem + env(safe-area-inset-bottom)); }
  .dl-gate-close { position: sticky; top: .4rem; float: right; margin: 0 -.4rem 0 0; width: 40px; height: 40px; border-radius: 50%; background: #fff; box-shadow: var(--shadow-md); z-index: 2; }
  .dl-gate-card > .eyebrow { margin-top: .9rem; }
  .dl-gate-card h3 { clear: right; }
  .rfq-form .actions { position: sticky; bottom: 0; background: #fff; padding: .8rem 0 calc(.4rem + env(safe-area-inset-bottom)); margin-top: 1.4rem; border-top: 1px solid var(--line); }
  .rfq-form .actions .btn { flex: 1; justify-content: center; }
  .rfq-form .actions .rfq-status:empty { display: none; }
}


/* ── aperture switch (2026-09-11) ──────────────────────────────────────────────
   A segmented control that filters everything carrying data-v to one variant.
   "Compare both" is the default and the no-JavaScript state, so a reader who never
   gets the script still sees every figure — the switch subtracts, never adds. */
.vswitch { display: inline-flex; flex-wrap: wrap; gap: .25rem; margin-top: 1.2rem; padding: .25rem; background: var(--paper); border: 1px solid var(--line); border-radius: 999px; }
.vsw { appearance: none; border: 0; background: transparent; cursor: pointer; font: inherit; font-size: .9rem; font-weight: 600; color: var(--slate); padding: .5rem 1.1rem; border-radius: 999px; transition: background .15s, color .15s; }
.vsw:hover { color: var(--navy-800); }
.vsw.is-on { background: #fff; color: var(--navy-800); box-shadow: var(--shadow-sm); }
.vsw:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
/* one rule does all the filtering: inside a scope pinned to a variant, anything keyed to a
   different one is gone. `revert` on the match keeps table cells as table cells. */
.vscope[data-variant] [data-v] { display: revert; }
.vscope[data-variant="delta-7-10"] [data-v="delta-7-20"],
.vscope[data-variant="delta-7-20"] [data-v="delta-7-10"] { display: none; }
/* a single-variant comparison table is a two-column table; let the value column breathe */
.vscope:not([data-variant="all"]) .comparetable th:first-child { width: 52%; }
/* A key feature the two apertures state differently is one bullet, not two: the shared claim
   sits on the bullet line and the difference follows it. As a red badge beside a duplicated
   title it was the loudest token on the row while being the least important one.
   Default state = one aperture picked: the difference reads inline, as the tail of the shared
   sentence, and the label is redundant. Only the comparison state escalates to labelled lines —
   deliberately that way round, because `:not([data-variant="all"])` would outrank the
   `[data-v]` hide rule above and un-hide the aperture the switch just dropped.
   (Assumes a .vscope wrapper; product.html hard-codes data-variant="all" so the server-rendered
   default is the labelled one, with or without JavaScript.) */
.featlist .feat-split, .featlist .fv { display: inline; }
.featlist .fv em { display: none; }
.vscope[data-variant="all"] .featlist .feat-split { display: grid; gap: .4rem; margin-top: .5rem; padding-left: .9rem; border-left: 1px solid var(--line); }
.vscope[data-variant="all"] .featlist .fv { display: grid; grid-template-columns: 2.9rem 1fr; gap: .5rem; align-items: baseline; }
.vscope[data-variant="all"] .featlist .fv em { display: inline; font-style: normal; font-size: .69rem; font-weight: 700; letter-spacing: .07em; text-transform: uppercase; color: var(--slate, #3d4b5c); opacity: .65; }
/* the two states data-v cannot express on its own: .both belongs to the comparison,
   .solo only appears once a single aperture is picked */
.vscope[data-variant="all"] .solo,
.vscope:not([data-variant="all"]) .both { display: none; }
/* a one-aperture table is a key/value list: its column head would only repeat the band head
   above it, and the variant line over the mounting notes the same */
.vscope:not([data-variant="all"]) .comparetable thead,
.vscope:not([data-variant="all"]) .specnote-var { display: none; }
@media (max-width: 640px) { .vscope:not([data-variant="all"]) .comparetable td::before { content: none; } }
/* the part itself, in the column the second aperture's mounting notes leave empty */
.specphoto { align-self: center; text-align: center; }
.specphoto picture { display: inline-block; }
.specphoto img { max-width: 100%; max-height: 300px; width: auto; height: auto; }
/* the 35-mode table: folded away, because it is datasheet material that the technology
   page none the less sends people here to read */
.zt-details { border-top: 1px solid var(--line); padding-top: 1rem; }
.zt-details > summary { cursor: pointer; font-size: .92rem; font-weight: 600; color: var(--navy-800); list-style-position: outside; padding: .3rem 0; }
.zt-details > summary:hover { color: var(--accent); }
.zt-details > summary:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }
.zt-details[open] > summary { margin-bottom: .4rem; }

/* ── product spec sheet (2026-09-09): specs and their figures share one panel ─ */
.specsheet { background: #fff; border: 1px solid var(--line); border-radius: var(--radius); box-shadow: var(--shadow-sm); padding: clamp(1.4rem, 3.5vw, 2.6rem); }
.specsheet .grid { gap: 1.5rem clamp(1.5rem, 4vw, 3rem); }
.specsheet h4 { font-size: 1.05rem; letter-spacing: .01em; color: var(--navy-800); padding-bottom: .55rem; border-bottom: 2px solid var(--brand-100); }
/* the sheet reads as four chapters — differences, commonalities, evidence, datasheet —
   separated by rules, so no band inherits the weight of the one above it */
.specband + .specband { margin-top: clamp(1.8rem, 3.5vw, 2.8rem); padding-top: clamp(1.5rem, 3vw, 2.2rem); border-top: 1px solid var(--line); }
.specsheet h3.bandhead { font-size: .78rem; font-weight: 600; letter-spacing: .09em; text-transform: uppercase; color: var(--muted); border-bottom: 0; padding-bottom: 0; margin-bottom: 1rem; }
/* a band whose head needs the headline number with it — the label stays the small uppercase
   marker, the qualifier runs beside it in sentence case */
.specsheet h3.bandhead span { margin-left: .7rem; font-size: .84rem; font-weight: 400; letter-spacing: 0; text-transform: none; color: var(--muted); }
/* a box inside the sheet: the long form of one row of the table above it, recessed onto paper so
   it reads as belonging to that row rather than as the next band */
.specinset { margin-top: clamp(1.4rem, 3vw, 2.1rem); background: var(--paper); border: 1px solid var(--line); border-radius: var(--radius-sm); padding: clamp(1.1rem, 2.5vw, 1.7rem); }
.specinset .inset-head { display: flex; align-items: baseline; gap: .9rem; flex-wrap: wrap; margin-bottom: .8rem; }
.specsheet .specinset h4 { font-size: 1.05rem; border-bottom: 0; padding-bottom: 0; }   /* the box edge already separates it — no rule under the head */
.specinset .inset-head span { font-size: .84rem; color: var(--muted); }
.specinset > p { max-width: 68ch; color: var(--slate); font-size: .97rem; }
.specinset .objwrap { background: var(--bg); border-radius: var(--radius-sm); }   /* the lookup itself sits back on white inside the recess */
.specinset .bandnote { margin-bottom: 0; }
/* a value's condition, under the value: the column still scans as numbers */
.bandnote { margin: -.4rem 0 1.2rem; max-width: 62ch; color: var(--slate); font-size: .95rem; }
.bandnote.centred { margin: 1.1rem auto 0; text-align: center; }   /* a note under both trees, not a caption for the left one */
.comparetable td small { display: block; margin-top: .2rem; font-size: .84em; line-height: 1.4; color: var(--muted); }
/* mounting: paragraphs, kept in the table's two columns so the reader holds their aperture */
.specnotes { margin-top: clamp(1.3rem, 2.5vw, 1.9rem); }
.specnote-var { font-weight: 600; font-size: .92rem; color: var(--brand-600); }
.specnote-list { margin: 0; }
.specnote-list dt { margin-top: .75rem; font-size: .74rem; font-weight: 600; letter-spacing: .07em; text-transform: uppercase; color: var(--muted); }
@media (max-width: 640px) { .specnote-list dt { font-size: .8rem; letter-spacing: .05em; } }   /* 11.8px is under the phone floor */
.specnote-list dd { margin: .2rem 0 0; font-size: .93rem; line-height: 1.55; color: var(--slate); }
@media (min-width: 721px) { .dl-list.two { grid-template-columns: 1fr 1fr; } }
.specsheet .speclist { margin-top: 1rem; }
/* one row per measurement: the two Zernike trees side by side so the apertures compare
   across the row, then the shared transmission curve spanning both columns */
/* auto-fit rather than a fixed 2: between 721 and 900 px the two fixed columns were ~307 px
   each and the trees' painted labels fell to ~9 px. Below 400 px of column the pair stacks. */
.specsheet-figs { display: grid; grid-template-columns: repeat(auto-fit, minmax(400px, 1fr)); gap: 1.8rem clamp(1.4rem, 3vw, 2.4rem); align-items: start; }
.specfig { text-align: center; min-width: 0; }
.specfig .visual { box-shadow: none; display: block; max-width: 100%; }
/* the row labels n = 1 … n = 7 are painted into the bitmap, so they shrink with it: capped at
   300 px tall the tree rendered 398 px wide in a 496 px column and the labels landed at ~11 px,
   below the caption beneath them and visibly cramped. Let the tree take the column it already
   has — the source is 1400 px wide, so this is still a downscale. */
.specfig .visual img { width: 100%; height: auto; max-width: 560px; margin: 0 auto; }
.specfig .figcap { margin: .7rem auto 0; max-width: 34em; font-size: .8rem; }
.specfig.wide { grid-column: 1 / -1; }
.specfig.wide .visual { display: block; }
/* spanning both columns is about giving the curve a single continuous x-axis, not about size:
   at the full 1180 px wrap the plot dwarfs the table it belongs to. Cap it at a plate width and
   centre it — the chart carries five decades of wavelength perfectly well at 820 px. */
.specfig.wide .visual img { width: 100%; max-width: 656px; max-height: none; }   /* 20% down from 820px: the curve was outsizing the trees above it */
@media (max-width: 720px) {
  .specsheet-figs { grid-template-columns: 1fr; }
  /* figure flag `wide`: swipeable at its legible size instead of shrinking to 80 px tall */
  .specfig.wide .visual { overflow-x: auto; -webkit-overflow-scrolling: touch; scrollbar-width: thin; }   /* starts on the axis, legend and visible band; the infrared tail is one swipe away */
  .specfig.wide .visual img { min-width: 640px; }
  .specfig.wide .figcap::after { content: " Swipe to see the full chart."; }
}

/* the identical-specs band: one column per spec group, so a third group does not
   land under the first with an empty half-row beside it */
.specgroups { grid-template-columns: repeat(3, minmax(0, 1fr)); }
@media (max-width: 1000px) { .specgroups { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 620px)  { .specgroups { grid-template-columns: 1fr; } }

/* ── generated Zernike modes: 35 rows x two apertures ────────────────────
   Recipe 2026-09-10 — layout:wide · naming:exact · label:inline · band:none ·
   purity:number · split:panels · rows:plain · density:dense · numerals:sans ·
   align:right · plate:wide.
   A lookup table, not a chart: 35 rows that all carry meaning read better as numbers than
   as colour. Purity is the number alone — the old meter was 76 px wide and right-aligned
   under a right-aligned number, so no two bars shared a left edge and the column could not
   be compared, which is the only thing a bar is for. The split between the two apertures is
   now the heaviest line in the table (a tinted panel each, divided by a real gutter), because
   it is the highest-level division; every finer one is drawn lighter. */
.zt-lede { margin-top: .9rem; }
.zerntable-scroll { position: relative;   /* contains the .sr-only column labels: absolutely positioned, they would otherwise escape this scroller and widen the page */
  margin-top: 1rem; overflow-x: auto; -webkit-overflow-scrolling: touch; scrollbar-width: thin; }
.zerntable-scroll:focus-visible { outline: 2px solid var(--brand-500); outline-offset: 3px; border-radius: var(--radius-sm); }
/* fixed layout + one colgroup: the two apertures get identical column widths, so a value
   sits at the same offset in both halves and the eye can compare straight across */
.zerntable { width: 100%; min-width: 700px; table-layout: fixed; border-collapse: collapse; font-size: .81rem; }
.zt-cmode { width: 28%; }
.zerntable th, .zerntable td { padding: .2rem .55rem; text-align: right; vertical-align: baseline; }

/* header: aperture over its three measures, so a column is never ambiguous.
   No text-transform on the column head — uppercasing turns [µm] into [MM]. */
.zerntable thead th { font-weight: 600; background: none; }
.zt-variants th { padding-bottom: .3rem; color: var(--brand-600); font-size: .92rem; text-align: center; border-bottom: 2px solid var(--brand-100); }
.zt-variants th:first-child { border-bottom: 0; text-align: left; vertical-align: bottom; color: var(--muted); font-size: .72rem; letter-spacing: .04em; }
.zt-cols th { padding-top: .45rem; color: var(--muted); font-size: .72rem; font-weight: 600; letter-spacing: .04em; border-bottom: 1px solid var(--line); }

/* split:panels — one tint per aperture, carried from the header down through the last row,
   with a real gutter between them. The gutter is a transparent border and the cells clip
   their fill to the padding box, so the section ground shows through whatever colour it is. */
.zerntable thead th:nth-child(2), .zerntable .zt-cols th:nth-child(-n+3),
.zerntable tbody td:nth-child(n+2):nth-child(-n+4) { background: var(--paper); background-clip: padding-box; }
.zerntable thead th:nth-child(3), .zerntable .zt-cols th:nth-child(n+4),
.zerntable tbody td:nth-child(n+5) { background: var(--brand-050); background-clip: padding-box; }
.zt-variants th:nth-child(2), .zt-variants th:nth-child(3) { border-radius: var(--radius-sm) var(--radius-sm) 0 0; }
.zerntable tbody td:nth-child(5),
.zt-cols th:nth-child(4),        /* this row has no Mode cell, so the aperture break is the 4th */
.zt-variants th:nth-child(3) { border-left: 14px solid transparent; }

/* the mode column: left-aligned, and pinned while the numbers scroll under the thumb.
   The symbol never breaks; the name after it may wrap rather than widen the column. */
.zerntable tbody th[scope="row"] { position: sticky; left: 0; z-index: 1; text-align: left; font-weight: 500; color: var(--ink); background: var(--bg); font-variant-numeric: tabular-nums; }
.zt-sym { white-space: nowrap; }
/* naming:exact, label:inline — orientation + family, muted, on the same line as the symbol */
.zt-name { margin-left: .5rem; color: var(--muted); font-weight: 400; }
.zerntable tbody tr:hover td, .zerntable tbody tr:hover th[scope="row"] { background: var(--brand-050); }

.zerntable td.num { color: var(--slate); font-variant-numeric: tabular-nums; }
.zerntable td.pur { color: var(--ink); }
.purval { font-variant-numeric: tabular-nums; }
.purval small { margin-left: .1em; color: var(--muted); font-size: .82em; }

.zt-notes { margin: .9rem 0 0; padding-left: 1.1rem; max-width: 78ch; color: var(--muted); font-size: .8rem; line-height: 1.55; }
.zt-notes li + li { margin-top: .3rem; }
@media (max-width: 720px) {
  .zerntable { font-size: .78rem; }
  .zt-notes::after { content: "Swipe the table sideways to reach the second aperture."; display: block; margin-top: .45rem; font-style: italic; }
}
@media (prefers-reduced-motion: no-preference) { .zerntable tbody tr { transition: background .12s ease; } }

/* ── sticky quotation bar on phones (2026-09-09) ─────────────────────── */
.stickybar { display: none; }
@media (max-width: 720px) {
  .stickybar {
    display: flex; gap: .6rem; position: fixed; left: 0; right: 0; bottom: 0; z-index: 50;
    padding: .6rem clamp(1.1rem, 4vw, 2rem) calc(.6rem + env(safe-area-inset-bottom));
    background: rgba(255, 255, 255, .92); backdrop-filter: blur(12px); -webkit-backdrop-filter: blur(12px);
    border-top: 1px solid var(--line); box-shadow: 0 -6px 24px rgba(11, 29, 49, .08);
  }
  .stickybar .btn { flex: 1; justify-content: center; padding: .7rem .6rem; font-size: .92rem; white-space: nowrap; }
  body:has(.stickybar) .site-footer .legal { padding-bottom: calc(1.3rem + 66px + env(safe-area-inset-bottom)); }
  html[data-variant] .stickybar { bottom: 52px; }   /* above the design-variant switcher bar */
}


/* ── visual audit fixes (2026-09-09) ─────────────────────────────────── */
/* 9  slim heroes on the site-wide photo: zoom past the Delta 7 silhouette so only fog and beam remain behind the title */
.hero.slim .bg[style*="laser-fog"] { background-size: auto 170%; background-position: 100% 45%; }
/* 10 application tiles: two across on phones instead of a six-tile column */
@media (max-width: 620px) {
  .grid.tiles { grid-template-columns: 1fr 1fr; gap: .7rem; }
  .grid.tiles .tile .cap { padding: 1.5rem .7rem .6rem; }
  .grid.tiles .tile .cap h3, .grid.tiles .tile .cap h4 { font-size: .92rem; line-height: 1.25; }
}
/* 5  figure pairs: every visual in a .figgrid sits in the same 4:3 frame, image contained — captions line up */
.figgrid .visual { aspect-ratio: 4 / 3; display: grid; place-items: center; background: var(--paper); border-radius: var(--radius); overflow: hidden; box-shadow: var(--shadow-md); }
.figgrid .visual picture { display: contents; }
.figgrid .visual img { width: 100%; height: 100%; max-height: 100%; object-fit: contain; }
/* a technical drawing paired with a photo keeps its white sheet, but takes the same 4:3 frame so both captions sit on one line */
.figgrid .drawing.fit43 { aspect-ratio: 4 / 3; display: grid; place-items: center; }
.figgrid .drawing.fit43 picture { display: contents; }
.figgrid .drawing.fit43 img { width: auto; max-width: 100%; max-height: 100%; }
/* 5b  two portrait figures as a diptych — a photo of the instrument beside the result it produced.
   Distinct from the older .figpair (equal halves, product pages): here the columns are sized in the
   ratio of the two aspect ratios (445x523 next to 412x520), so both frames come out the same height
   at their native pixel size and the two captions start on the same line, and .figgrid's 4:3 frame
   would crop both. Set the ratio per use with --duo. */
.figduo { display: grid; grid-template-columns: var(--duo, 1fr) 1fr; gap: 1.5rem; align-items: start; max-width: 900px; margin-inline: auto; }
.figduo > figure { margin: 0; }
@media (max-width: 700px) { .figduo { grid-template-columns: 1fr; gap: 1.8rem; } }
/* 3  home: secondary products as compact horizontal cards in a two-across row */
.grid.secondary { grid-template-columns: 1fr 1fr; }
.card.compact { flex-direction: row; }
.card.compact .media { flex: 0 0 36%; aspect-ratio: auto; padding: 1rem; border-bottom: 0; border-right: 1px solid var(--line); }
.card.compact .media > picture, .card.compact .media > img { inset: 1rem; }
.card.compact .body { padding: 1.1rem 1.3rem 1.2rem; gap: .4rem; }
.card.compact .body h3 { font-size: 1.05rem; }
.card.compact .body p { font-size: .9rem; }
@media (max-width: 620px) { .grid.secondary { grid-template-columns: 1fr; } .card.compact .media { flex-basis: 40%; } }
/* 4  screen-reader-only heading (heading order on list pages) */
.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; }

/* ── standalone figures under a split (applications, 2026-09-09) ────────
   Outside .split a .visual carries no width rule, so the image sized itself from the
   srcset slot (720 px at 1440) and sat left-aligned in the wrap; a <video> did the same. */
.fig { margin: 0 auto; }
.fig .visual { border-radius: var(--radius); overflow: hidden; }
.fig .visual img, .fig video { width: 100%; }
.fig.narrow { max-width: 760px; }   /* a square before/after comparison at full wrap width would run 1100 px tall */
.fig figcaption { margin-top: .7rem; }

/* ── applications: domain template (2026-09-09) ─────────────────────────
   A sticky jump bar under the hero; each domain reads intro, evidence, action;
   the integration cases are four captioned tiles instead of one collage. */
.jumpbar { position: sticky; top: 72px; z-index: 40; background: rgba(255, 255, 255, .92); backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px); border-bottom: 1px solid var(--line); }
.jumpbar .wrap { display: flex; gap: .2rem; overflow-x: auto; scrollbar-width: none; }
.jumpbar .wrap::-webkit-scrollbar { display: none; }
.jumpbar a { font-family: var(--font-display); font-weight: 600; font-size: .88rem; color: var(--slate); padding: .85rem .9rem; white-space: nowrap; border-bottom: 3px solid transparent; margin-bottom: -1px; }
.jumpbar a:hover { color: var(--brand-600); }
.jumpbar a.on { color: var(--brand-600); border-bottom-color: var(--accent); }
.jumpbar .sep { width: 1px; flex: 0 0 auto; background: var(--line); margin: .7rem .5rem; }
.jumpbar ~ .section[id], .jumpbar ~ .section .tile[id] { scroll-margin-top: 130px; }   /* header 72 + jump bar 48 + air */
.domain-lede { font-size: clamp(1.2rem, 1.6vw, 1.25rem); color: var(--ink); margin: .2rem 0 1rem; max-width: 36em; text-wrap: pretty; }
.split.fill > .visual { align-self: stretch; }               /* the photo fills the text column's height */
.split.fill > .visual picture { display: contents; }
.split.fill > .visual img { height: 100%; object-fit: cover; }
.evidence { margin-top: clamp(2.4rem, 5vw, 3.6rem); }
/* the section's own top padding already opened the gap — an .evidence that opens a
   section must not add its inter-block margin on top of it */
.domain > .wrap > .evidence:first-child { margin-top: 0; }
.evidence-head { display: flex; align-items: baseline; gap: .9rem; flex-wrap: wrap; margin-bottom: 1.1rem; }
.evidence-head h3 { font-size: 1.15rem; }
.evidence-head span { font-size: .84rem; color: var(--muted); }
/* institutional logos in an evidence head, in place of a naming line: they sit on
   the heading's baseline, so the row reads as one line rather than two blocks. */
.evidence-head .ev-logos { display: flex; align-items: baseline; gap: 1.1rem; }
/* height only, no max-width: a width cap on a fixed-height logo squashes the wordmark
   instead of scaling it. The head wraps, so a wide logo row costs a line, not an overflow. */
.evidence-head .ev-logos img { height: 28px; width: auto; flex: none; }
@media (max-width: 620px) { .evidence-head .ev-logos { gap: .8rem; } .evidence-head .ev-logos img { height: 22px; } }
.actions-row { margin-top: clamp(1.6rem, 3vw, 2.2rem); padding-top: 1.4rem; border-top: 1px solid var(--line); display: flex; flex-wrap: wrap; gap: .8rem 1.6rem; align-items: center; }
.actions-row .textlink { color: var(--brand-600); }
.actions-row .textlink:hover { text-decoration: underline; }
.cases { display: grid; grid-template-columns: repeat(4, 1fr); gap: 1.5rem; }
.cases.five { grid-template-columns: repeat(5, 1fr); gap: 1.1rem; }   /* five installations, one row — a 4+1 orphan reads as an afterthought */
.case { margin: 0; }
.case .ph { aspect-ratio: 3 / 4; border-radius: var(--radius); overflow: hidden; box-shadow: var(--shadow-sm); background: var(--paper); }
.case .ph picture { display: contents; }
.case .ph img { width: 100%; height: 100%; object-fit: cover; }
.case figcaption { margin-top: .9rem; }
.case .lg { height: 34px; display: flex; align-items: center; margin-bottom: .6rem; }
.case .lg img, .case .lg picture img { max-height: 34px; width: auto; max-width: 150px; }
.case strong { display: block; font-family: var(--font-display); font-weight: 600; font-size: 1.02rem; line-height: 1.3; color: var(--ink); }
.case .who { display: block; font-size: .86rem; color: var(--muted); margin-top: .25rem; line-height: 1.4; }
.grid.tiles.show-cap .tile .cap p { max-height: none; opacity: 1; }   /* text visible without hovering (touch has no hover) */
@media (max-width: 860px) {
  .split.fill > .visual { align-self: auto; }
  .split.fill > .visual img { height: auto; }
  .cases, .cases.five { grid-template-columns: 1fr 1fr; gap: .9rem; }
  .case strong { font-size: .92rem; }
  .case .who { font-size: .8rem; }
  .case .lg { height: 26px; }
  .case .lg img { max-height: 26px; max-width: 110px; }
  .grid.tiles.show-cap .tile .cap p { display: none; }
}

/* ── applications: one boxed grid per page (2026-09-10) ──────────────────
   Two bordered card grids stacked read as one wall, and the column count
   changing between them (3 then 4) makes the break look accidental rather
   than meant. The reference grid — "Where this shows up" — keeps the cards,
   because it is scanned rather than read. The case-study steps are the same
   numbered sequence with the card taken off: hairline rules between the
   columns, one rule above and below the band. Same markup, same .nf, so a
   step can move between the two treatments by changing one class. */
.numfeat.steps { gap: 0; border-top: 1px solid var(--line); border-bottom: 1px solid var(--line); }
.numfeat.steps .nf {
  background: none; border: 0; border-radius: 0;
  border-left: 1px solid var(--line); padding: 1.6rem 1.7rem 1.7rem;
}
/* the column that starts a row sits flush with the text above it; the one that
   ends a row sits flush with the right edge of the wrap */
.numfeat.steps.cols-3 .nf:nth-child(3n+1),
.numfeat.steps.cols-4 .nf:nth-child(4n+1) { border-left: 0; padding-left: 0; }
.numfeat.steps.cols-3 .nf:nth-child(3n),
.numfeat.steps.cols-4 .nf:nth-child(4n) { padding-right: 0; }

/* two columns — at the same widths .numfeat.cols-4 / .cols-3 fold to two, so the
   rules have to be re-dealt: a left rule on the right-hand column, a top rule on
   every item that is no longer in the first row */
@media (max-width: 1080px) {
  .numfeat.steps.cols-4 { gap: 0; }
  .numfeat.steps.cols-4 .nf { border-left: 1px solid var(--line); padding-left: 1.7rem; padding-right: 1.7rem; }
  .numfeat.steps.cols-4 .nf:nth-child(2n+1) { border-left: 0; padding-left: 0; }
  .numfeat.steps.cols-4 .nf:nth-child(2n) { padding-right: 0; }
  .numfeat.steps.cols-4 .nf:nth-child(n+3) { border-top: 1px solid var(--line); }
}
@media (max-width: 900px) {
  .numfeat.steps.cols-3 { gap: 0; }
  .numfeat.steps.cols-3 .nf { border-left: 1px solid var(--line); padding-left: 1.7rem; padding-right: 1.7rem; }
  .numfeat.steps.cols-3 .nf:nth-child(2n+1) { border-left: 0; padding-left: 0; }
  .numfeat.steps.cols-3 .nf:nth-child(2n) { padding-right: 0; }
  .numfeat.steps.cols-3 .nf:nth-child(n+3) { border-top: 1px solid var(--line); }
}
/* one column: the vertical rules go, a rule between consecutive steps stays */
@media (max-width: 620px) {
  .numfeat.steps .nf,
  .numfeat.steps.cols-3 .nf, .numfeat.steps.cols-4 .nf {
    border-left: 0; border-top: 0; padding-left: 0; padding-right: 0;
  }
  .numfeat.steps .nf + .nf { border-top: 1px solid var(--line); }
}

/* the line under a section heading that names the source rather than making a claim —
   institution, journal, year. Quieter than .domain-lede, which is still a sentence. */
.section-head .meta { font-size: .88rem; color: var(--muted); margin-top: .45rem; }

/* prose that sits directly in a domain section, outside a .split column, has no
   column to borrow a measure from — 1180 px of wrap is about twice a readable line */
.domain > .wrap > p { max-width: 72ch; }

/* ── phinden (2026-09-09): the same domain / evidence / actions grammar as applications ──── */
.fig.medium { max-width: 960px; }   /* 4:3 screen recording: UI text stays legible, the block does not run 800 px tall */
.case .sw { display: block; font-size: .8rem; font-weight: 700; letter-spacing: .04em; color: var(--brand-600); margin-bottom: .25rem; }   /* which control software the installation runs on — product names keep their own case (uppercasing turns µManager into MMANAGER) */
.figgrid figure.fig { margin: 0; }
.video.light { background: #fff; }   /* a diagram loop on white: the navy plate behind a video would rim it in dark */
.chips-label { font-family: var(--font-display); font-size: .76rem; font-weight: 600; letter-spacing: .09em; text-transform: uppercase; color: var(--muted); margin-bottom: .7rem; }
.chip.soon { background: #f4f5f7; border-color: #dfe3e8; color: var(--muted); }   /* not shipping yet — neutral, not an alert */

/* ── phinden integration diagram (2026-09-10) ─────────────────────────────────────────
   Replaces the clipart drawing. Three nodes on one row — Phinden · your control software ·
   your microscope — with the two paths the body text describes drawn separately: the
   double arrow (Phinden asks the native software for frames) and the rail underneath
   (Phinden drives the DPP itself, without going back through the software). Under 860 px
   the grid collapses to one column and every connector turns to point down. */
.swflow { margin: 0; display: grid; gap: .8rem 1.1rem; align-items: stretch;
  grid-template-columns: minmax(0, 1fr) auto minmax(0, 1.5fr) auto minmax(0, 1fr); }
.swflow .swyou { grid-column: 3; justify-self: center; display: flex; flex-direction: column; align-items: center; gap: .15rem;
  font-family: var(--font-display); font-size: .76rem; font-weight: 600; letter-spacing: .09em; text-transform: uppercase; color: var(--muted); }
.swflow .swyou svg { width: 12px; height: 20px; color: var(--brand-400); }
.swnode { grid-row: 2; background: var(--bg); border: 1px solid var(--line); border-radius: var(--radius);
  padding: 1.1rem 1.15rem; box-shadow: var(--shadow-sm); display: flex; flex-direction: column; gap: .45rem; }
.swnode.sw-phinden { grid-column: 1; }
.swnode.sw-host { grid-column: 3; border-color: var(--brand-100); background: var(--brand-050); }
.swnode.sw-scope { grid-column: 5; }
.swnode .ic { width: 30px; height: 30px; color: var(--brand-500); }
.swnode strong { font-family: var(--font-display); font-weight: 600; font-size: 1.05rem; color: var(--ink); }
.swnode > span { font-size: .95rem; color: var(--muted); line-height: 1.5; }
.swnode .swdpp { margin-top: auto; align-self: flex-start; font-family: var(--font-display); font-size: .8rem; font-weight: 600;
  letter-spacing: .04em; color: var(--navy-700); background: var(--brand-050); border: 1px solid var(--brand-100); padding: .3rem .7rem; border-radius: 999px; }
.swarrow { grid-row: 2; align-self: center; display: flex; flex-direction: column; align-items: center; gap: .2rem; min-width: 92px;
  font-size: .8rem; line-height: 1.35; color: var(--muted); text-align: center; }
.swarrow.sw-link-a { grid-column: 2; }
.swarrow.sw-link-b { grid-column: 4; }
.swarrow svg { width: 84px; height: 12px; color: var(--brand-400); }
.swarrow .ar.v { display: none; }   /* the column layout's connector, shown under 860 px */
/* one tile per control software: the mark on its own ground, the product name spelled out under it —
   several of these wordmarks (ZEISS, Thorlabs) name the company, not the program */
.swlogos { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: .55rem; margin-top: .45rem; }
.swlogo { display: flex; flex-direction: column; align-items: center; gap: .3rem; }
.swlogo .plate { display: grid; place-items: center; width: 100%; height: 48px; padding: .4rem .55rem;
  background: #fff; border: 1px solid var(--line); border-radius: var(--radius-sm); }
.swlogo .plate img { max-height: 28px; max-width: 100%; width: auto; object-fit: contain; }
.swlogo .plate.dark { background: #1c1f26; border-color: #1c1f26; }   /* Nikon publishes the NIS-Elements mark white-on-dark only */
.swlogo .plate.text { font-family: var(--font-display); font-size: .82rem; font-weight: 600; color: var(--navy-700); }
.swlogo b { font-size: .82rem; font-weight: 600; color: var(--slate); letter-spacing: .01em; }
.swlogo i { display: block; font-style: normal; font-size: .72rem; color: var(--muted); }
.swlogo.soon .plate { border-style: dashed; border-color: var(--brand-400); }   /* not shipping yet — a dashed edge says it; dimming the mark only made it look broken */
.swrail { grid-column: 1 / -1; grid-row: 3; display: flex; align-items: center; gap: .8rem; margin-top: .2rem; }
.swrail::before, .swrail::after { content: ""; height: 1px; background: var(--brand-100); flex: 1; }
.swrail::after { background: linear-gradient(90deg, var(--brand-100), var(--brand-400)); }
.swrail span { font-size: .84rem; color: var(--slate); text-align: center; }
.swrail svg { width: 14px; height: 12px; color: var(--brand-400); flex: none; }
@media (max-width: 860px) {
  /* the compound selectors match the placement rules above one for one — a bare .swnode
     loses to .swnode.sw-host and the cards stay in their desktop columns */
  .swflow { grid-template-columns: 1fr; gap: .7rem; }
  .swflow .swyou, .swflow .swrail,
  .swnode.sw-phinden, .swnode.sw-host, .swnode.sw-scope,
  .swarrow.sw-link-a, .swarrow.sw-link-b { grid-column: 1; grid-row: auto; }
  /* stacked, "you work here" has to sit on top of the software card it points at, not on top of the first card in the source */
  .swnode.sw-phinden { order: 1; } .swarrow.sw-link-a { order: 2; } .swflow .swyou { order: 3; }
  .swnode.sw-host { order: 4; } .swarrow.sw-link-b { order: 5; } .swnode.sw-scope { order: 6; }
  .swflow .swrail { order: 7; }
  .swarrow { min-width: 0; flex-direction: row; flex-wrap: nowrap; align-items: center; gap: .55rem; justify-content: center; }
  .swarrow .ar.h { display: none; }
  .swarrow .ar.v { display: block; width: 12px; height: 46px; }
  .swarrow span { flex: 1 1 0; }
  .swarrow span:first-child { text-align: right; }
  .swarrow span:last-child { text-align: left; }
  .swlogos { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .swrail::before, .swrail::after { display: none; }
  .swrail { border-left: 2px solid var(--brand-100); padding-left: .9rem; }
  .swrail span { text-align: left; }
  /* the arrow pointed right, along a row that no longer exists once the diagram is a column —
     the rule it belongs to is the brand-coloured rail on the left */
  .swrail svg { display: none; }
}

/* ── before/after comparison with a magnification switch (applications › ophthalmology, 2026-09-09) ──
   figure.cmp > .cmp-frame[data-mag] > .pair.full + .pair.zoom (each: the "with" <img> underneath, .before > "without" <img>
   clipped to the left of --x), .handle, .lbl, .level, .sbar, input[type=range] covering the frame. The range drives --x, the
   .mag switch flips data-mag and swaps [data-on]/[data-off] texts. Script: site.js "comparison". */
.cmp { max-width: 760px; margin: 0 auto; }
.cmp-frame { position: relative; aspect-ratio: var(--ar, 687 / 545); border-radius: var(--radius); overflow: hidden; background: #000; box-shadow: var(--shadow-md); user-select: none; -webkit-user-select: none; touch-action: pan-y; }
.cmp-frame .pair { position: absolute; inset: 0; transition: opacity .28s ease; }
.cmp-frame .pair picture { display: contents; }
.cmp-frame .pair img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.cmp-frame .pair .before { position: absolute; inset: 0; clip-path: inset(0 calc(100% - var(--x, 50%)) 0 0); }
.cmp-frame[data-mag="on"] .pair.full, .cmp-frame[data-mag="off"] .pair.zoom { opacity: 0; }
.cmp-frame .handle { position: absolute; top: 0; bottom: 0; left: var(--x, 50%); width: 2px; margin-left: -1px; background: #fff; box-shadow: 0 0 0 1px rgba(7, 18, 32, .3); pointer-events: none; }
.cmp-frame .handle span { position: absolute; top: 50%; left: 50%; width: 44px; height: 44px; transform: translate(-50%, -50%); border-radius: 50%; background: #fff; box-shadow: var(--shadow-md); display: grid; place-items: center; color: var(--navy-900); }
.cmp-frame .handle svg { width: 22px; height: 22px; }
.cmp-frame .lbl, .cmp-frame .level { position: absolute; font-family: var(--font-display); font-size: .72rem; font-weight: 600; letter-spacing: .12em; text-transform: uppercase; color: #fff; background: rgba(7, 18, 32, .62); padding: .32rem .7rem; border-radius: 999px; pointer-events: none; backdrop-filter: blur(4px); -webkit-backdrop-filter: blur(4px); }
.cmp-frame .lbl { top: .8rem; }
.cmp-frame .lbl.l { left: .8rem; }
.cmp-frame .lbl.r { right: .8rem; }
.cmp-frame .level { left: .8rem; bottom: .8rem; letter-spacing: .08em; text-transform: none; display: flex; gap: .45rem; align-items: center; }
.cmp-frame .level i { width: 8px; height: 8px; border-radius: 50%; background: var(--brand-400); }
.cmp-frame[data-mag="on"] .level i { background: var(--accent); }
/* Scale bar. --sbar is the bar length as a percentage of the frame width, so it stays true at every
   render size; the frame's aspect-ratio equals the source pixel aspect, so object-fit:cover never crops.
   Set per figure in the markup; the magnified pair carries its own value. Drawn on the frame rather than
   burned into the JPEGs, so it cannot drift under the wipe handle the way a per-frame annotation does. */
.cmp-frame .sbar { position: absolute; right: .8rem; bottom: .8rem; width: var(--sbar, 12%); display: flex; flex-direction: column; align-items: center; gap: .3rem; pointer-events: none; transition: width .28s ease; }
.cmp-frame .sbar b { font-family: var(--font-display); font-size: .68rem; font-weight: 600; letter-spacing: .06em; color: #fff; text-shadow: 0 1px 4px rgba(7, 18, 32, .95); white-space: nowrap; }
.cmp-frame .sbar i { width: 100%; height: 4px; border-radius: 1px; background: #fff; box-shadow: 0 1px 4px rgba(7, 18, 32, .95); }
.cmp-frame[data-mag="on"] .sbar { width: var(--sbar-zoom, var(--sbar, 12%)); }
.cmp-frame input[type=range] { position: absolute; inset: 0; width: 100%; height: 100%; margin: 0; opacity: 0; cursor: ew-resize; -webkit-appearance: none; appearance: none; background: none; }
.cmp-frame:focus-within { outline: 3px solid var(--brand-400); outline-offset: 3px; }
/* two comparisons side by side (a domain that has more than one result to show); each keeps its own handle */
.cmp-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: clamp(1.6rem, 3vw, 2.6rem); align-items: start; }
.cmp-grid .cmp { max-width: none; }
@media (max-width: 760px) { .cmp-grid { grid-template-columns: 1fr; } }
/* collaborator data never appears uncredited: the lab that produced the frames is named under the figure itself */
.figcap .credit { display: block; margin-top: .35rem; font-size: .8rem; color: var(--muted); }
/* figure credits stay text-only: a partner mark next to a caption reads as a stray badge
   rather than an attribution. Institute logos belong in the instrument cards, not under a figure. */
.cmp-bar { display: flex; align-items: center; justify-content: space-between; gap: 1rem; flex-wrap: wrap; margin-top: .9rem; }
.cmp-bar .hintline { font-size: .84rem; color: var(--muted); }
.mag { display: inline-flex; align-items: center; gap: .7rem; border: 1px solid var(--line); background: #fff; border-radius: 999px; padding: .4rem .5rem .4rem .95rem; cursor: pointer; color: var(--ink); font-family: var(--font-display); font-weight: 600; font-size: .92rem; box-shadow: var(--shadow-sm); }
.mag:hover { border-color: var(--brand-400); }
.mag svg { width: 18px; height: 18px; color: var(--brand-600); }
.mag .track { position: relative; width: 64px; height: 30px; border-radius: 999px; background: #c9d6e2; transition: background .2s; }
.mag .track::after { content: ""; position: absolute; top: 3px; left: 3px; width: 24px; height: 24px; border-radius: 50%; background: #fff; box-shadow: 0 1px 3px rgba(11, 29, 49, .35); transition: transform .2s; }
.mag .track b { position: absolute; top: 0; bottom: 0; display: grid; place-items: center; font-size: .62rem; letter-spacing: .1em; color: #fff; font-weight: 700; }
.mag .track b.off { right: 9px; }
.mag .track b.on { left: 9px; opacity: 0; }
.mag[aria-checked="true"] .track { background: var(--brand-600); }
.mag[aria-checked="true"] .track::after { transform: translateX(34px); }
.mag[aria-checked="true"] .track b.on { opacity: 1; }
.mag[aria-checked="true"] .track b.off { opacity: 0; }
@media (prefers-reduced-motion: reduce) { .cmp-frame .pair, .mag .track, .mag .track::after { transition: none; } }
@media (max-width: 640px) {
  .cmp-bar { flex-direction: column; align-items: flex-start; }
  .cmp-frame .lbl, .cmp-frame .level { font-size: .75rem; padding: .26rem .6rem; }   /* 12px floor: these name what the reader is looking at */
  .cmp-frame .sbar b { font-size: .72rem; }
}

/* ── side-by-side figure pairs (2026-09-10) ─────────────────────────────
   Replaces the drag-handle comparison where the two frames are separate
   acquisitions rather than the same field: a slider makes unregistered
   features jump under the handle. Two panels, labelled, one caption. */
.duo { margin: 0; }
.duo .panels { display: grid; grid-template-columns: 1fr 1fr; gap: .9rem; align-items: start; }
.duo .panel { margin: 0; }
.duo .panel .shot {
  border-radius: var(--radius-sm); overflow: hidden; background: var(--paper);
  box-shadow: 0 10px 22px -12px rgba(11, 29, 49, .38), 0 2px 6px rgba(11, 29, 49, .06);
}
.duo .panel .shot img { display: block; width: 100%; height: auto; }
.duo .panel .lbl {
  display: block; margin-top: .55rem; text-align: center;
  font-size: .74rem; font-weight: 700; letter-spacing: .09em; text-transform: uppercase;
  color: var(--muted);
}
.duo > figcaption { margin-top: .9rem; }
@media (max-width: 620px) { .duo .panels { gap: .6rem; } .duo .panel .lbl { font-size: .8rem; } }

/* ── consent banner (2026-09-10) ───────────────────────────────────────────────
   A bar along the bottom, not a full-screen interstitial: the page stays readable and usable while it is up,
   which is the point — declining must cost no more than accepting. Both buttons carry equal visual weight for
   the same reason (a greyed-out "Decline" next to a bright "Accept" is a dark pattern, and German DPAs treat
   it as invalid consent). It sits above the sticky product bar and respects the iOS safe area. */
/* MUST come before (and stay alongside) the display:flex rule: an author `display` beats the UA
   `[hidden] { display: none }` regardless of specificity, so without this the banner ignores
   `banner.hidden = true`, never dismisses, and permanently covers the foot of every page. */
.cookiebar[hidden] { display: none; }
.cookiebar { position: fixed; inset: auto 0 0 0; z-index: 60; padding: 0 1rem calc(1rem + env(safe-area-inset-bottom)); display: flex; justify-content: center; pointer-events: none; }
.cookiebar-card { pointer-events: auto; width: min(var(--wrap), 100%); background: #fff; border: 1px solid var(--line); border-radius: var(--radius); box-shadow: var(--shadow-lg); padding: 1.1rem clamp(1.1rem, 3vw, 1.7rem); display: flex; flex-wrap: wrap; align-items: center; gap: .9rem 1.6rem; }
.cookiebar-card h2 { font-size: 1rem; margin: 0; flex: 0 0 100%; }
.cookiebar-card p { flex: 1 1 22rem; margin: 0; font-size: .87rem; color: var(--slate); line-height: 1.5; }
.cookiebar-actions { display: flex; gap: .6rem; flex-wrap: wrap; }
.cookiebar-actions .btn { padding: .55rem 1.1rem; font-size: .9rem; }
/* Phone: the banner measured 305px tall at 390x844 — 36% of the viewport, for a notice the visitor is
   meant to dismiss in one tap. Shorter copy did most of the work (see base.html); this tightens what is
   left. The two buttons keep equal weight (equal size, equal emphasis — a quiet Decline is not consent)
   and an explicit 44px minimum height, which is more finger than they had before the copy was cut. */
@media (max-width: 620px) {
  .cookiebar { padding-left: .6rem; padding-right: .6rem; padding-bottom: calc(.6rem + env(safe-area-inset-bottom)); }
  .cookiebar-card { padding: .8rem .9rem; gap: .5rem .9rem; }
  .cookiebar-card h2 { font-size: .92rem; }
  .cookiebar-card p { flex-basis: 100%; font-size: .81rem; line-height: 1.45; }
  .cookiebar-actions { width: 100%; }
  .cookiebar-actions .btn { flex: 1; justify-content: center; min-height: 44px; padding: .5rem .8rem; font-size: .86rem; }
}

/* a <button> that has to sit inline in a run of footer links and read as one of them */
.linklike { background: none; border: 0; padding: 0; font: inherit; color: #8fa9bf; text-decoration: underline; cursor: pointer; }
.linklike:hover { color: #fff; }

/* A link inside a run of body text must be distinguishable by something other than colour (WCAG 1.4.1 / axe
   link-in-text-block): against grey prose the brand blue alone measures ~1.3:1, far under the 3:1 the rule
   asks for. Underlining the link — and only a link that sits inside a paragraph — restores the second cue
   without putting underlines under navigation, buttons or card links.

   A navigation list is not body text: the footer columns and the jump bar are lists of links and
   nothing else, so colour is not their only cue and an underline there is just noise. Excluding them
   inside the selector (rather than overriding afterwards) keeps the exemption from losing on
   specificity — `li a:not():not():not()` outweighs any plain `.site-footer a` override. */
p a:not(.btn):not(.dl-item):not(.linklike),
li:not(.site-footer li):not(.jumpbar li):not(.dl-list li) a:not(.btn):not(.dl-item):not(.cardlink) { text-decoration: underline; text-underline-offset: .15em; text-decoration-thickness: from-font; }
.site-footer p a, .hero p a, .lede a { text-decoration: none; }        /* these sit on their own coloured ground, where colour is not the only cue */

/* A news row is a card: the whole row is one target, so the paragraph rule above was underlining
   all four lines inside it — kind, date, title and excerpt — which reads as four separate links.
   The affordance belongs on the title, on hover, the way the rest of the cards behave. */
.news-row a.cardlink:hover .head { text-decoration: underline; text-underline-offset: .15em; text-decoration-thickness: from-font; }

/* ── added 2026-09-10: PHI objective-compatibility grid, formula blocks, dimension figure ── */

/* The compatibility grid is a matrix, not a label/value list: it must NOT collapse into stacked
   rows on a phone (a 7-column NA window read as 14 separate lines is unreadable), so it opts out
   of the .spectable phone rules and scrolls inside its own wrapper instead. */
/* position:relative contains the .sr-only cell labels: absolutely positioned against the viewport they
   would escape this scroller and widen the page to the table's 520 px — same fix as .zerntable-scroll. */
.objwrap{position:relative;overflow-x:auto;-webkit-overflow-scrolling:touch;margin-top:1rem;max-width:100%}
/* a grid/flex item defaults to min-width:auto, which sizes it to its content — the 520 px table would
   then widen the whole page instead of scrolling inside .objwrap. min-width:0 lets the column shrink. */
.objcol{min-width:0}
/* fixed layout: with auto layout the browser hands the slack to whichever columns hold the
   widest content, so 4x and 10x ballooned while 20x-100x were squeezed to the digits. Every
   magnification is the same lookup and gets the same column. */
.objtable{margin-top:0;min-width:520px;table-layout:fixed;font-variant-numeric:tabular-nums}
.objtable th,.objtable td{text-align:center;padding:.42rem .5rem}
.objtable thead th:first-child,.objtable tbody th{text-align:left;white-space:nowrap}
.objtable tbody th{width:auto;padding-right:1rem}
.objtable td{font-weight:600;color:var(--ink)}
/* verdict carried by fill + a left rule, so it survives greyscale printing and colour-blind viewing;
   the visually-hidden text in each cell carries it for screen readers */
.objtable td.ok{background:#e4f3e4;box-shadow:inset 3px 0 0 #3f8f4a}
.objtable td.bad{background:#fbe3e3;box-shadow:inset 3px 0 0 var(--accent-text)}
@media (max-width:640px){
  .objwrap .objtable,.objwrap .objtable tbody,.objwrap .objtable tr,
  .objwrap .objtable th,.objwrap .objtable td{display:revert;width:auto}
  .objwrap .objtable{display:table}
  .objwrap .objtable thead{display:table-header-group}
  .objwrap .objtable tr{display:table-row;padding:0}
  .objwrap .objtable th,.objwrap .objtable td{display:table-cell;padding:.55rem .45rem;border-bottom:1px solid var(--line)}
  .objwrap .objtable tbody th{font-size:.82rem;color:var(--brand-600)}
}
/* each swatch and its label are one inline-flex item: when the legend wraps on a phone the
   pair moves together, instead of the red swatch trailing the word "Compatible". */
.objlegend{display:flex;align-items:center;gap:.4rem 1.2rem;flex-wrap:wrap;margin:.5rem 0 .5rem;font-size:.82rem;color:var(--muted)}
.objlegend .item{display:inline-flex;align-items:center;gap:.45rem}
.objlegend .key{width:14px;height:14px;border-radius:3px;display:inline-block;flex:none}
.objlegend .key.ok{background:#e4f3e4;box-shadow:inset 3px 0 0 #3f8f4a}
.objlegend .key.bad{background:#fbe3e3;box-shadow:inset 3px 0 0 var(--accent-text)}
/* the grid scrolls inside .objwrap on a narrow screen; say so, but only where it actually scrolls */
.objhint{display:none;flex-basis:100%;font-style:italic}
@media (max-width:760px){.objhint{display:block}}

/* Formula block — the two sizing relations from the spec sheet, set so a reader can actually
   compute with them. Scrolls rather than wraps: a broken equation is worse than a scrollbar. */
.formula{background:var(--brand-050);border-left:3px solid var(--brand-400);border-radius:var(--radius-sm);
  padding:.85rem 1rem;margin-top:1rem;overflow-x:auto}
.formula .eq{font-family:var(--font-display);font-size:1.02rem;color:var(--navy-800);white-space:nowrap;letter-spacing:.01em}
.formula .eq sub{font-size:.7em}
.formula .leg{margin-top:.45rem;font-size:.85rem;color:var(--muted);white-space:normal}
/* compact variant: equation and legend on one line, so the lookup closes the spec sheet in a
   band rather than a chapter. Wraps to two lines below ~640 px, which is where the legend
   stops fitting beside a nowrap equation. */
.formula.compact{display:flex;align-items:baseline;gap:.35rem 1.1rem;flex-wrap:wrap;padding:.6rem .9rem;margin-top:.9rem}
.formula.compact .eq{flex:none}
.formula.compact .leg{margin-top:0;flex:1 1 20rem;min-width:0}
/* the grid, its legend and the note are one block; the note carries the link to the datasheet */
.objnote{margin:0;font-size:.86rem;max-width:80ch}
.objnote a{white-space:nowrap}
.specband.tight .bandhead{margin-bottom:.2rem}
.formula .leg b{color:var(--slate);font-weight:600}

/* Line-art drawing on white: the site's paper sections are off-white, so the drawing gets a white
   plate of its own rather than floating on a slightly different ground. */
.drawing{background:#fff;border:1px solid var(--line);border-radius:var(--radius);padding:1rem}
.drawing img{display:block;width:100%;height:auto}

/* ── click-to-load video facade (2026-09-10) ───────────────────────────────────────────
   A YouTube embed contacts Google the moment the page loads, which would break the promise
   in the privacy policy that nothing on a page reaches another provider unless the visitor
   asks for it. So the page ships a local poster frame and a play button; the iframe is
   built in JS on the first click, against youtube-nocookie.com. Until then no request
   leaves this domain. */
.ytfacade { position: relative; border-radius: var(--radius); overflow: hidden; box-shadow: var(--shadow-md); background: var(--navy-950); aspect-ratio: 16 / 9; }
.ytfacade .ytbtn { position: absolute; inset: 0; width: 100%; height: 100%; padding: 0; border: 0; background: none; cursor: pointer; display: block; }
.ytfacade .ytbtn img, .ytfacade .ytbtn picture { width: 100%; height: 100%; object-fit: cover; display: block; }
.ytfacade .ytbtn::after { content: ""; position: absolute; inset: 0; background: linear-gradient(180deg, rgba(7,18,32,.10), rgba(7,18,32,.42)); transition: background .25s ease; }
.ytfacade .ytbtn:hover::after, .ytfacade .ytbtn:focus-visible::after { background: linear-gradient(180deg, rgba(7,18,32,.04), rgba(7,18,32,.30)); }
.ytfacade .ytplay { position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%); z-index: 1;
  width: clamp(56px, 7vw, 76px); height: clamp(56px, 7vw, 76px); border-radius: 999px;
  background: rgba(255,255,255,.94); color: var(--brand-600); display: grid; place-items: center;
  box-shadow: 0 6px 22px rgba(7,18,32,.35); transition: transform .25s ease; }
.ytfacade .ytbtn:hover .ytplay, .ytfacade .ytbtn:focus-visible .ytplay { transform: translate(-50%, -50%) scale(1.07); }
.ytfacade .ytplay svg { width: 42%; height: 42%; margin-left: 8%; fill: currentColor; }
.ytfacade iframe { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; }
.ytnote { font-size: .85rem; color: var(--muted); margin: .7rem 0 0; max-width: 46em; }
.ytnote a { color: var(--slate); }
@media (prefers-reduced-motion: reduce) {
  .ytfacade .ytbtn::after, .ytfacade .ytplay { transition: none; }
  .ytfacade .ytbtn:hover .ytplay, .ytfacade .ytbtn:focus-visible .ytplay { transform: translate(-50%, -50%); }
}

/* ── home: the emerging-applications banner (2026-09-10) ────────────────────
   Three application pages ship; the emerging page carries four domains and will
   carry more. As separate tiles those four turn a 3x2 grid into a ragged 3+4,
   and every domain added to /applications/frontier/ forces the home grid to be
   re-dealt. One banner spanning the row takes any number of panels behind a
   single caption, so the count lives in the collage rather than in the layout.
   Which four fields is the whole message, so unlike a photo tile the caption
   line is not held back for hover. The ratio matches the collage exactly: a
   taller band on phones would look kinder but object-fit: cover then eats half
   of the outer two panels, so the band stays proportional and the names drop
   instead — the heading alone, one tap from the page that lists them. */
.tile.wide { grid-column: 1 / -1; aspect-ratio: 1116 / 267; }
.tile.wide .cap p { max-height: 6em; opacity: 1; }
@media (max-width: 620px) { .tile.wide .cap p { display: none; } }

/* Spacing utilities last on purpose: a component rule further down the file used to beat them at
   equal specificity — `.cmp { margin: 0 auto }` silently swallowed every `mt-2` on a comparison
   figure, which is why the caption above one sat flush against the frame. A utility the author
   wrote by hand should win over a component default; a component that must keep its own margin
   says so with two classes (`.figgrid figure.fig`) or a parent (`.specsheet .speclist`). */
.mt-1 { margin-top: 1rem; } .mt-2 { margin-top: 2rem; } .mt-3 { margin-top: 3rem; }
.mb-1 { margin-bottom: 1rem; } .mb-2 { margin-bottom: 2rem; }
