/* EQLabs marketing site — layout & components.
   Composition rules mirror the design system's ui_kits/website surface
   (Header/Hero/Features/Footer); values come from tokens.css only. */

/* ---------- Shared ---------- */
.container { max-width: 1080px; margin: 0 auto; padding: 0 24px; }

.skip-link {
  position: absolute; left: -9999px; top: 0; z-index: 100;
  background: var(--surface-page); color: var(--text-heading);
  padding: 10px 16px; border-radius: var(--radius-sm);
}
.skip-link:focus { left: 12px; top: 12px; }
/* Off-screen but still read: the <h1> on pages whose hero shows no headline. */
.visually-hidden {
  position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; border: 0;
  overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap;
}

.eyebrow {
  font-size: var(--text-xs);
  font-weight: var(--weight-semibold);
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
  color: var(--text-eyebrow);
}

.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  font-family: var(--font-body); font-size: 15px; font-weight: var(--weight-semibold);
  border-radius: var(--radius-pill); padding: 12px 24px; border: 1px solid transparent;
  cursor: pointer; text-decoration: none;
  transition: background var(--duration-fast) var(--ease-out),
              color var(--duration-fast) var(--ease-out),
              box-shadow var(--duration-fast) var(--ease-out);
}
.btn:hover { text-decoration: none; }
/* The global :focus-visible rule below squares corners to --radius-sm; a
   focused button keeps its pill. */
.btn:focus-visible { outline: none; box-shadow: var(--focus-ring); border-radius: var(--radius-pill); }
.btn-primary { background: var(--brand); color: var(--text-on-brand); }
.btn-primary:hover { background: var(--purple-600); color: var(--text-on-brand); }
/* Bordered secondary button, beside a primary one (Contact's "Email us"). */
.btn-ghost { background: transparent; color: var(--text-nav); border-color: var(--border-default); }
.btn-ghost:hover { color: var(--text-nav-active); border-color: var(--border-strong); }
.btn-on-dark { background: var(--white); color: var(--navy-900); }
.btn-on-dark:hover { background: var(--gray-100); color: var(--navy-900); }

:focus-visible { outline: none; box-shadow: var(--focus-ring); border-radius: var(--radius-sm); }

/* ---------- Header ---------- */
.site-header {
  position: sticky; top: 0; z-index: 20;
  display: flex; align-items: center; justify-content: space-between; gap: 24px;
  padding: 14px 32px;
  background: var(--surface-header);
  border-bottom: 1px solid var(--border-default);
}
.logo { display: inline-flex; align-items: center; gap: 10px; text-decoration: none; }
.logo:hover { text-decoration: none; }
.logo img { height: 34px; width: 34px; display: block; }
.wordmark {
  font-family: var(--font-display); font-size: 22px; font-weight: var(--weight-bold);
  letter-spacing: var(--tracking-tight); line-height: 1; color: var(--text-wordmark);
}
.wordmark b { font-weight: var(--weight-bold); color: #b622ff; }
.header-actions { display: flex; align-items: center; gap: 20px; }

/* Header logo is the one-piece EQLabs lockup; the footer keeps the same
   lockup, always in its dark variant, since the footer band is deep purple
   in both themes. */
.site-header .logo,
.site-footer .logo {
  width: 100px;
  height: 41px;
  background: url("/assets/eqlabs-logo-lockup.png") left center / contain no-repeat;
}
.site-header .logo img, .site-header .logo .wordmark,
.site-footer .logo img, .site-footer .logo .wordmark { display: none; }
.site-footer .logo { background-image: url("/assets/eqlabs-logo-lockup-dark.png"); }

/* @dark-safe: both paths below must stay in step. */
[data-theme="dark"] .site-header .logo {
  background-image: url("/assets/eqlabs-logo-lockup-dark.png");
}
@media (prefers-color-scheme: dark) {
  .site-header .logo { background-image: url("/assets/eqlabs-logo-lockup-dark.png"); }
  [data-theme="light"] .site-header .logo {
    background-image: url("/assets/eqlabs-logo-lockup.png");
  }
}

/* "Log in" leaves the site for the VERA app, so it takes the ghost-button
   treatment already used by the theme toggle, to sit apart from nav links. */
.nav-action {
  border: 1px solid var(--border-default);
  border-radius: 999px;
  padding: 7px 15px;
  font-size: var(--text-sm);
  color: var(--text-nav);
  text-decoration: none;
}
.nav-action:hover { border-color: var(--border-strong); color: var(--text-nav-active); }
/* The demo booking is the one call to action in the header: filled, not bordered. */
.nav-action-primary { background: var(--brand); border-color: var(--brand); color: var(--text-on-brand); }
.nav-action-primary:hover { background: var(--purple-600); border-color: var(--purple-600); color: var(--text-on-brand); }
.site-nav { display: flex; align-items: center; gap: 28px; }
.site-nav a {
  font-size: var(--text-sm); font-weight: var(--weight-medium); color: var(--text-nav);
  white-space: nowrap;
}
/* The logo, nav links, login pill and theme toggle are all meant to stay at
   their specified size — none has spare content to shed under pressure, so
   letting them flex-shrink just squeezes the logo to near-nothing and wraps
   nav text mid-word. The 480px breakpoint below is what actually makes room:
   this only stops the squeeze once there's room to fit into. */
.site-header .logo,
.site-nav a,
.nav-action,
.theme-toggle { flex-shrink: 0; }
.site-nav a:hover { color: var(--text-nav-active); }
.site-nav a[aria-current="page"] { color: var(--text-nav-active); }

/* ---------- Hero (statement style) ---------- */
/* All heroes share one geometry: a common min-height with vertically
   centered content, and a FIXED mark size. Percentage-based mark sizing made
   the sphere content-height-dependent — a different size on every page.
   Fixed 340px + min-height 420px guarantees identical marks and ≥29px
   clearance everywhere (even at the pulse animation's 1.06 scale peak);
   longer content only grows the band. */
.hero {
  position: relative; overflow: hidden; background: var(--surface-hero);
  padding: 88px 24px; min-height: 420px;
  display: flex; flex-direction: column; justify-content: center;
  text-align: center;
}
/* Vertically centered, bleeding off only the right edge. `translate` and
   `scale` are independent properties, so centering composes with the pulse. */
.hero-mark {
  position: absolute; right: -80px; top: 50%;
  translate: 0 -50%;
  height: 340px; width: auto; aspect-ratio: 1;
  pointer-events: none; opacity: 0.5; user-select: none;
}
.hero-inner {
  position: relative; z-index: 1; max-width: 820px; margin: 0 auto;
  display: flex; flex-direction: column; align-items: center; gap: 22px;
}
.hero h1 {
  margin: 0; font-size: clamp(38px, 6.4vw, 64px); font-weight: var(--weight-semibold);
  max-width: 500px;
}
/* Home hero: the VERA lockup replaces the headline; a short tagline plays
   the lede's role underneath it, with a short subline below that. The
   wordmark artwork carries no dot of its own: the halftone sphere is a
   separate image placed where the dot sits, so it can pulse. The <h1> is the
   positioning box — the shared .hero h1 rule would otherwise cap it at 500px
   and leave a text line box under the image, hence font-size 0. */
.hero h1.hero-lockup {
  position: relative; width: min(520px, 80vw); aspect-ratio: 1450 / 605;
  max-width: none; margin: 0 0 6px; font-size: 0; line-height: 0;
}
.hero-vera-lockup { position: relative; display: block; width: 100%; height: 100%; object-fit: contain; }
.hero-dot {
  position: absolute; left: 39.8%; top: 26%; width: 20%; aspect-ratio: 1;
  translate: -50% -50%; pointer-events: none; user-select: none;
}
.hero-tagline { font-family: var(--font-display); font-weight: 500; max-width: 24ch; }
/* Doubled class: .hero .lede below has the same specificity and comes later. */
.hero-home .lede.hero-tagline { font-size: clamp(22px, 2.6vw, 30px); line-height: 1.3; color: var(--text-heading); }
.hero-subline { margin: 0; font-size: var(--text-lg); line-height: 1.5; color: var(--text-body); max-width: 40ch; }

/* Home hero photo crossfade. The stack sits under .hero-inner (z-index 1)
   inside the hero's overflow clip; every photo is absolutely stacked and
   starts transparent. hero-bg.css — generated by build.mjs from home.json —
   supplies the keyframes, the cycle length and one animation-delay per photo,
   so the timing follows the photo list instead of a count kept by hand here,
   and the delays stay out of inline style attributes, which the CSP blocks. */
.hero-bg { position: absolute; inset: 0; z-index: 0; overflow: hidden; pointer-events: none; }
.hero-bg img {
  position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover;
  opacity: 0; animation: hero-fade var(--hero-cycle, 144s) linear infinite;
}
.hero-veil { position: absolute; inset: 0; background: var(--hero-veil); }

/* One orchestrated load moment: hero children rise in, staggered; the halftone
   mark fades in and pulses slowly (the VERA iOS app's pulse, slowed to
   ambience). pulse animates the independent `scale` property, so it composes
   with the centering `translate`. */
@keyframes rise { from { opacity: 0; transform: translateY(14px); } to { opacity: 1; transform: none; } }
@keyframes fade { from { opacity: 0; } }
@keyframes pulse { 50% { scale: 1.06; } }
.hero-inner > * { animation: rise 0.55s var(--ease-out) backwards; }
.hero-inner > :nth-child(2) { animation-delay: 90ms; }
.hero-inner > :nth-child(3) { animation-delay: 180ms; }
.hero-inner > :nth-child(4) { animation-delay: 270ms; }
.hero-mark, .hero-dot { animation: fade 0.9s ease backwards, pulse 8s ease-in-out infinite; }
/* Roboto Mono's 62ch measure (~670px) runs under the halftone mark bleeding
   off the right edge; a fixed px cap keeps clear space at every width. The
   lede is the hero's statement, so it takes the display face at heading
   colour, as in the design. */
.hero .lede {
  margin: 0; max-width: 500px; font-family: var(--font-display);
  font-size: clamp(18px, 2vw, 22px); font-weight: 500; line-height: 1.35; color: var(--text-heading);
}
/* About's hero carries a plain body paragraph instead of a lede. */
.hero-body { margin: 16px 0 0; max-width: 52ch; color: var(--text-body); line-height: 1.6; }
.hero-actions { display: flex; flex-wrap: wrap; align-items: center; justify-content: center; gap: 16px; margin-top: 20px; }

/* ---------- Sections ---------- */
.section { padding: 72px 0; }
.section-subtle { background: var(--surface-subtle); }
/* In-page anchors (nav links, footer links) stop under the sticky header. */
.section[id], .cta-band[id] { scroll-margin-top: 80px; }
@media (prefers-reduced-motion: no-preference) { html { scroll-behavior: smooth; } }
.section-head { max-width: 720px; margin: 0 auto; text-align: center; }
.section-head h2 { margin: 10px 0 14px; font-size: var(--text-2xl); font-weight: var(--weight-semibold); }
.section-head p { margin: 0; color: var(--text-muted); }
.section-closing {
  margin: 40px auto 0; max-width: 640px; text-align: center;
  font-family: var(--font-display); font-size: clamp(18px, 2vw, 22px); font-weight: 500;
  color: var(--text-heading); line-height: 1.35;
}

/* Callout band (home): one bordered statement with the sphere beside it. */
.callout {
  max-width: 760px; margin: 0 auto; display: flex; align-items: center; gap: 20px;
  background: var(--surface-card); border: 2px solid var(--brand); border-radius: var(--radius-lg);
  box-shadow: var(--shadow-sm); padding: 28px 32px;
}
.callout-icon { width: 48px; height: 48px; object-fit: contain; flex-shrink: 0; }
.callout p { margin: 0; color: var(--text-heading); line-height: 1.6; text-wrap: pretty; }

/* Cards (what we do / contact / security) */
.card-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); gap: 20px; margin-top: 36px; }
/* Contact's cards open their section, with no head above them to clear. */
.card-grid-flush { margin-top: 0; }
.card {
  background: var(--surface-card); border: 1px solid var(--border-default);
  border-radius: var(--radius-lg); padding: 24px; box-shadow: var(--shadow-sm);
  transition: box-shadow var(--duration-med) var(--ease-out), transform var(--duration-med) var(--ease-out);
}
.card:hover { box-shadow: var(--shadow-md); transform: translateY(-2px); }
/* Every 16px-radius card in the design (.eq-card and the inline-styled stage,
   testimonial and callout cards) carries the 3px brand-gradient hairline that
   .usecase below already has. */
.card, .stage, .testimonial, .callout { position: relative; overflow: hidden; }
.card::before, .stage::before, .testimonial::before, .callout::before {
  content: ""; position: absolute; top: 0; left: 0; right: 0; height: 3px;
  background: var(--grad-brand); z-index: 2; pointer-events: none;
}
/* Bare icons, no plate (per Bahar's originals) — a fixed box keeps the
   baseline rhythm consistent across the differing artwork. */
.card-icon,
.section-icon {
  display: block;
  width: 44px; height: 44px;
  object-fit: contain;
  margin-bottom: 14px;
}
.card h3 { margin: 0 0 8px; font-size: 18px; font-weight: var(--weight-semibold); }
.card p { margin: 0; font-size: var(--text-sm); color: var(--text-muted); }

/* Stage accordion (home). Each stage is a native <details>, so opening it
   needs no script; the grid aligns cards to the top so an open one grows
   without stretching its neighbours. Columns step 4 → 2 → 1 with the design. */
.stages, .usecase-grid { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 20px; margin-top: 36px; align-items: start; }
@media (max-width: 1100px) { .stages, .usecase-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 600px) { .stages, .usecase-grid { grid-template-columns: minmax(0, 1fr); } }
/* Modalities: the cards stretch to one height so the bench photos, pinned to
   each card's bottom, line up across the row (the stages stay start-aligned
   so an open one doesn't stretch its neighbours), and the grid sits 8px under
   its eyebrow-only head, both as in the design. */
.usecase-grid { align-items: stretch; margin-top: 8px; }
.stage {
  background: var(--surface-card); border: 1px solid var(--border-default);
  border-radius: var(--radius-lg); box-shadow: var(--shadow-sm); padding: 28px;
  transition: box-shadow var(--duration-med) var(--ease-out), transform var(--duration-med) var(--ease-out);
}
.stage:hover { transform: translateY(-2px); box-shadow: var(--shadow-md); }
.stage-summary {
  position: relative; display: flex; flex-direction: column; gap: 14px;
  min-height: 150px; padding-right: 28px; cursor: pointer; list-style: none;
}
.stage-summary::-webkit-details-marker { display: none; }
/* The open/closed sign, top right. It is decorative: the `/ ""` alternative
   text keeps it out of the summary's accessible name (otherwise screen readers
   append "plus" to every stage); engines without that syntax keep the plain
   declaration before it. */
.stage-summary::after {
  content: "+"; content: "+" / ""; position: absolute; top: 0; right: 0;
  font-family: var(--font-mono); font-size: 22px; line-height: 1; color: var(--text-muted);
}
.stage[open] .stage-summary::after { content: "\2212"; content: "\2212" / ""; }
.stage-icon { width: 44px; height: 44px; object-fit: contain; }
.stage-label {
  margin: 0; font-family: var(--font-body); font-size: 12px; font-weight: 600;
  letter-spacing: var(--tracking-wide); text-transform: uppercase; color: var(--text-brand);
}
.stage-title { margin: -8px 0 0; font-family: var(--font-display); font-size: var(--text-lg); font-weight: 600; color: var(--text-heading); line-height: 1.3; }
.stage-body { display: flex; flex-direction: column; gap: 6px; margin-top: 18px; cursor: default; }
.stage-kicker {
  margin: 12px 0 0; font-family: var(--font-body); font-size: 12px; font-weight: 600;
  letter-spacing: var(--tracking-wide); text-transform: uppercase; color: var(--text-muted);
}
.stage-body > .stage-kicker:first-child { margin-top: 0; }
.stage-text { margin: 0; font-size: var(--text-sm); color: var(--text-body); line-height: 1.6; text-wrap: pretty; }
.stage-note { margin: 6px 0 0; font-size: var(--text-sm); font-weight: 600; color: var(--text-heading); line-height: 1.5; }
.stage-scenario { margin: 18px 0 0; padding-top: 18px; border-top: 1px solid var(--border-default); display: flex; flex-direction: column; gap: 8px; }
.stage-scenario .stage-kicker { margin: 0; }
.stage-scenario blockquote {
  margin: 0; font-family: var(--font-display); font-size: var(--text-md); font-weight: 500;
  color: var(--text-heading); line-height: 1.45; text-wrap: pretty;
}

/* Modality cards (home). Same vocabulary as .card — 1px border, 3px
   brand-gradient hairline on top — but each card carries its own copy and a
   bench photo the lab supplied, so it's authored separately from .card. The
   grid shares the stages' column steps above. */
.usecase {
  position: relative; display: flex; flex-direction: column; gap: 14px;
  background: var(--surface-card); border: 1px solid var(--border-default);
  border-radius: var(--radius-md); padding: 26px 22px 22px; overflow: hidden;
  transition: box-shadow var(--duration-med) var(--ease-out), transform var(--duration-med) var(--ease-out);
}
.usecase::before {
  content: ""; position: absolute; top: 0; left: 0; right: 0; height: 3px;
  background: var(--grad-brand); z-index: 2;
}
.usecase:hover { transform: translateY(-2px); box-shadow: var(--shadow-md); }
/* Icon on its own row, label beneath, as in the design: side by side, a 92px
   icon leaves a four-column card under 90px for the label, and "Translational"
   gets clipped. */
.usecase-head { display: grid; grid-template-columns: minmax(0, 1fr); gap: 14px; align-items: center; }
/* Line-art icons at 76px; the two photographic ones read small and get 92px. */
.usecase-icon { display: block; flex: 0 0 auto; height: 76px; width: 76px; object-fit: contain; }
.usecase-icon[src$="body.png"], .usecase-icon[src$="labbench.png"] { height: 92px; width: 92px; }
.usecase-label {
  display: block; margin: 0; font-family: var(--font-display); font-size: var(--text-md); font-weight: var(--weight-semibold);
  color: var(--text-heading); line-height: 1.3;
}
.usecase-body { margin: 0; color: var(--text-muted); font-size: var(--text-sm); line-height: 1.6; }
.usecase-media { margin-top: auto; height: 220px; overflow: hidden; border-radius: var(--radius-sm); }
.usecase-media img { display: block; width: 100%; height: 100%; object-fit: cover; }
@media (max-width: 640px) {
  .usecase-media { height: 180px; }
}

/* Testimonials (home) */
.testimonial {
  background: var(--surface-card); border: 1px solid var(--border-default);
  border-radius: var(--radius-lg); box-shadow: var(--shadow-sm); padding: 28px;
  display: flex; flex-direction: column; gap: 16px;
}
.testimonial-icon { width: 44px; height: 44px; object-fit: contain; }
.testimonial-tag {
  margin: 0; font-family: var(--font-body); font-size: 12px; font-weight: 600;
  letter-spacing: var(--tracking-wide); text-transform: uppercase; color: var(--text-brand);
}
.testimonial blockquote {
  margin: 0; font-family: var(--font-display); font-size: var(--text-lg); font-weight: 500;
  color: var(--text-heading); line-height: 1.45;
}

/* Mission (about): the statement, then the IQ/EQ belief, then the principles. */
.mission-statement {
  margin: 0 auto 48px; max-width: 30ch; text-align: center; text-wrap: balance;
  font-family: var(--font-display); font-size: clamp(20px, 2.4vw, 26px); font-weight: 500;
  line-height: 1.35; color: var(--text-heading);
}
.mission-belief { max-width: 62ch; margin: 0 auto 56px; display: flex; flex-direction: column; gap: 16px; text-align: center; }
.mission-belief p { margin: 0; color: var(--text-body); line-height: 1.6; text-wrap: pretty; }
.mission-belief .mission-belief-title {
  font-family: var(--font-display); font-size: var(--text-lg); font-weight: 600;
  line-height: 1.35; color: var(--text-heading); text-wrap: balance;
}

/* Principles trio (about) — centered, with an icon above each. */
.principles-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 24px; }
.principles-lead { font-size: var(--text-lg); color: var(--text-heading); font-family: var(--font-display); margin: 0 0 28px; text-align: center; }
.principles-centered { text-align: center; }
.principle-col { display: flex; flex-direction: column; align-items: center; gap: 16px; }
.principle-icon { width: 44px; height: 44px; object-fit: contain; }
.principle h3 { position: relative; margin: 0 0 10px; padding-top: 14px; font-size: var(--text-lg); }
.principle h3::before {
  content: ""; position: absolute; top: 0; left: 50%; translate: -50% 0; width: 40px; height: 3px;
  background: var(--grad-brand); border-radius: 2px;
}
.principle p { margin: 0; font-size: var(--text-sm); color: var(--text-muted); }

/* Team (about) */
.team-intro { margin: 0 auto; max-width: 720px; text-align: center; color: var(--text-muted); line-height: 1.6; }
.team-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 240px)); gap: 32px; margin-top: 36px; justify-content: center; }
.member img {
  aspect-ratio: 4 / 5; object-fit: cover; width: 100%;
  border-radius: var(--radius-lg); box-shadow: var(--shadow-sm);
}
.member h3 { margin: 14px 0 2px; font-size: 18px; }
.member .role { font-size: var(--text-xs); font-weight: var(--weight-semibold); letter-spacing: var(--tracking-wide); text-transform: uppercase; color: var(--text-muted); }
.member-bullets {
  margin: 14px 0 0; padding: 0; list-style: none; display: flex; flex-direction: column;
  gap: 6px; font-size: var(--text-sm); color: var(--text-muted); line-height: 1.5;
}

/* Ecosystem partners (about) */
.ecosystem-label {
  margin: 56px 0 20px; font-family: var(--font-body); font-size: 12px; font-weight: 600;
  letter-spacing: var(--tracking-wide); text-transform: uppercase; color: var(--text-muted);
  text-align: center;
}
.logo-grid { display: flex; flex-wrap: wrap; justify-content: center; gap: 16px; }
.logo-cell {
  flex: 1 1 150px; max-width: 190px; height: 88px;
  display: flex; align-items: center; justify-content: center;
  transition: opacity var(--duration-fast) var(--ease-out);
}
.logo-cell:hover { opacity: 0.7; }
.logo-cell img { max-width: 100%; width: auto; object-fit: contain; }

/* Compliance badges (security) */
.compliance-card { display: flex; flex-direction: column; align-items: flex-start; gap: 12px; }
.compliance-card h3 { margin: 0; font-family: var(--font-display); font-size: var(--text-lg); font-weight: 600; color: var(--text-heading); line-height: 1.2; }
.compliance-card p { margin: 0; }
.badge {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: var(--text-xs); font-weight: var(--weight-semibold);
  padding: 4px 10px; border-radius: var(--radius-pill);
}
.badge-warning { background: var(--badge-warning-bg); color: var(--badge-warning-fg); }
.badge-dot { width: 6px; height: 6px; border-radius: 50%; background: currentColor; }

/* CTA band (home demo, security) */
.cta-band { position: relative; overflow: hidden; background: var(--surface-inverse); padding: 88px 24px; text-align: center; }
.cta-inner { position: relative; max-width: 760px; margin: 0 auto; display: flex; flex-direction: column; align-items: center; gap: 20px; }
.cta-band .eyebrow { color: var(--cyan-300); }
.cta-band .statement { margin: 0; font-family: var(--font-display); font-size: clamp(22px, 3vw, 30px); font-weight: var(--weight-medium); color: var(--white); letter-spacing: var(--tracking-tight); line-height: var(--leading-snug); }
.cta-band .cta-body { margin: 0; color: rgba(255, 255, 255, 0.72); max-width: 52ch; }

/* Dot field decoration (footer + cta) */
.dot-field {
  position: absolute; pointer-events: none;
  background-image: radial-gradient(circle, rgba(209, 121, 255, 0.55) 2.4px, transparent 2.6px);
  background-size: 16px 16px;
  -webkit-mask-image: radial-gradient(circle at 50% 50%, #000 0%, transparent 75%);
  mask-image: radial-gradient(circle at 50% 50%, #000 0%, transparent 75%);
}
.dot-field.bl { bottom: -100px; left: -70px; width: 380px; height: 300px; }
.dot-field.tr { top: -90px; right: -60px; width: 360px; height: 280px; }

/* Contact */
.contact-email { margin-top: 14px; font-family: var(--font-mono); font-size: var(--text-sm); }
/* The booking section sits under the audience cards, hidden until Book a demo
   opens it (cal-embed.js), so it continues the cards' section rather than
   starting a padded one of its own. */
.booking { padding-top: 0; }
.booking[hidden] { display: none; }
.booking-head { display: flex; align-items: center; justify-content: space-between; gap: 16px; }
.booking-title { margin: 0; font-family: var(--font-display); font-size: var(--text-lg); font-weight: var(--weight-semibold); color: var(--text-heading); }
/* Focused by script when the section opens, so screen readers land on it. */
.booking-title:focus { outline: none; }
/* The Cal.com booking calendar (issue #29). Cal.com's iframe sizes itself;
   the min-height holds about its loaded height while it loads, so the
   fallback line and footer don't jump. Not "cal-embed": embed.js styles that
   class (its iframe's) with a later stylesheet, which overrode this rule.
   Heights measured 2026-10-01 on a five-week month (a six-week month is one
   row taller): below 816px Cal.com's calendar grows with the width (595px
   at 320, 979px at 768), above it the side-by-side layout is 538-570px.
   Each value sits a few pixels under the measured height, so no gap stays
   below the calendar. The fallback link stays visible for anyone the embed
   fails for. */
.booking-embed { width: 100%; min-height: calc(85vw + 320px); margin-top: 24px; }
@media (min-width: 816px) { .booking-embed { min-height: 536px; } }
@media (min-width: 1024px) { .booking-embed { min-height: 566px; } }
/* Cal.com's wrapper is a wrapping flex box whose first line is its (empty)
   loading skeleton; the spare height split between the lines put the loading
   iframe mid-box, and it jumped to the top once it had its height. */
.booking-embed cal-inline { align-content: flex-start; }
.cal-fallback { margin: 16px 0 0; text-align: center; font-size: var(--text-sm); color: var(--text-muted); }

/* Academic access (/academic): request form, then the thank-you panel
   (academic.js). */
.academic-wrap { max-width: 620px; }
.academic-form, .academic-thanks {
  background: var(--surface-card); border: 1px solid var(--border-default);
  border-radius: 16px; box-shadow: var(--shadow-sm);
}
.academic-form { padding: 32px; display: grid; gap: 18px; }
.academic-form[hidden], .academic-thanks[hidden], .academic-error[hidden] { display: none; }
.field-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 18px; }
.field { display: flex; flex-direction: column; gap: 6px; font-size: var(--text-sm); font-weight: var(--weight-semibold); color: var(--text-heading); }
.field input, .field select, .field textarea {
  width: 100%; box-sizing: border-box; padding: 12px 14px;
  border: 1px solid var(--border-strong); border-radius: 10px;
  background: var(--surface-card); color: var(--text-heading);
  font-family: var(--font-body); font-size: 16px; font-weight: normal;
}
.field textarea { resize: vertical; }
/* Spam trap: people never see or reach it, bots fill it in, and the backend
   keeps such a request marked spam without emailing it. Off-screen rather
   than display:none, which some bots skip. */
.field-trap { position: absolute; left: -10000px; width: 1px; height: 1px; overflow: hidden; }
.academic-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 16px; }
.academic-actions .btn[disabled] { opacity: 0.7; cursor: progress; }
.academic-error { margin: 0; font-size: var(--text-sm); color: var(--text-body); }
.academic-thanks { padding: 40px 32px; display: flex; flex-direction: column; gap: 14px; align-items: center; text-align: center; }
.academic-thanks h2 { margin: 0; font-size: clamp(22px, 2.6vw, 28px); font-weight: 500; }
.academic-thanks h2:focus { outline: none; }
.academic-thanks p { margin: 0; color: var(--text-body); line-height: 1.6; max-width: 44ch; }

/* ---------- Footer ---------- */
.site-footer { position: relative; overflow: hidden; background: var(--surface-footer); color: #fff; padding: 56px 24px 32px; }
.footer-inner { position: relative; max-width: 1080px; margin: 0 auto; display: flex; flex-wrap: wrap; align-items: flex-start; justify-content: space-between; gap: 32px; }
.footer-nav { display: flex; flex-direction: column; gap: 10px; font-size: var(--text-sm); }
.footer-nav a, .site-footer a { color: rgba(255, 255, 255, 0.72); }
.footer-nav a:hover, .site-footer a:hover { color: #fff; text-decoration: none; }
.footer-heading { font-size: 13px; font-weight: var(--weight-bold); text-transform: uppercase; letter-spacing: var(--tracking-wide); color: rgba(255, 255, 255, 0.5); margin: 0 0 4px; }
.socials { display: flex; gap: 14px; align-items: center; margin-top: 6px; }
.socials img { width: 20px; height: 20px; filter: brightness(0) invert(1); opacity: 0.75; transition: opacity var(--duration-fast) var(--ease-out); }
.socials a:hover img { opacity: 1; }
.footer-bottom {
  position: relative; max-width: 1080px; margin: 40px auto 0; padding-top: 20px;
  border-top: 1px solid rgba(255, 255, 255, 0.15);
  font-size: var(--text-xs); color: rgba(255, 255, 255, 0.5);
}

/* ---------- 404 ---------- */
.error-hero { padding: 120px 24px; text-align: center; }
.error-hero .code { font-family: var(--font-mono); font-size: var(--text-sm); color: var(--text-muted); letter-spacing: var(--tracking-wide); }
.error-hero h1 { margin: 10px 0 24px; font-size: clamp(30px, 4.6vw, 46px); }

/* ---------- Theme-dependent artwork ----------
   Where a rule can read a token it does (the invert below), so tokens.css
   stays the one switching mechanism. Asset *swaps* (content: url()) cannot be
   tokenised per element, so they carry the [data-theme] / media-query pair
   explicitly — keep each pair in step. */
/* molecule.svg's path carries no fill attribute, so it renders pure black —
   invisible on the dark ground (0.94:1, i.e. darker than the page itself), and
   unfixable with brightness(), which is multiplicative and so leaves pure black
   unchanged. Inverted to white in dark; untouched in light. The principle and
   use-case artwork carries explicit fills, so none of it is caught by this rule. */
img[src$="molecule.svg"] { filter: var(--icon-invert); }

/* Home hero: dot-less VERA wordmark, purple on light / reverse artwork on dark. */
[data-theme="dark"] img[src$="vera-hero-nodot-light.png"] { content: url("/assets/vera-hero-nodot-dark.png"); }
@media (prefers-color-scheme: dark) {
  img[src$="vera-hero-nodot-light.png"] { content: url("/assets/vera-hero-nodot-dark.png"); }
  [data-theme="light"] img[src$="vera-hero-nodot-light.png"] { content: url("/assets/vera-hero-nodot-light.png"); }
}

/* Use-case tiles: each icon swaps to artwork recolored into the brand ramp on
   dark; the light artwork sits bare on the card, so there is no plate to drop. */
[data-theme="dark"] .usecase-icon[src$="petri.png"] { content: url("/assets/verticals/petri-dark.png"); }
[data-theme="dark"] .usecase-icon[src$="body.png"] { content: url("/assets/verticals/body-dark.png"); }
[data-theme="dark"] .usecase-icon[src$="labbench.png"] { content: url("/assets/verticals/labbench-dark.png"); }
[data-theme="dark"] .usecase-icon[src$="dna.svg"] { content: url("/assets/verticals/dna-dark.svg"); }
@media (prefers-color-scheme: dark) {
  .usecase-icon[src$="petri.png"] { content: url("/assets/verticals/petri-dark.png"); }
  .usecase-icon[src$="body.png"] { content: url("/assets/verticals/body-dark.png"); }
  .usecase-icon[src$="labbench.png"] { content: url("/assets/verticals/labbench-dark.png"); }
  .usecase-icon[src$="dna.svg"] { content: url("/assets/verticals/dna-dark.svg"); }
  [data-theme="light"] .usecase-icon[src$="petri.png"] { content: url("/assets/verticals/petri.png"); }
  [data-theme="light"] .usecase-icon[src$="body.png"] { content: url("/assets/verticals/body.png"); }
  [data-theme="light"] .usecase-icon[src$="labbench.png"] { content: url("/assets/verticals/labbench.png"); }
  [data-theme="light"] .usecase-icon[src$="dna.svg"] { content: url("/assets/verticals/dna.svg"); }
}

/* Ecosystem partner logos: the artwork is dark-on-transparent, so it sits
   bare on the light ground with no plate. Six of the nine swap to artwork
   drawn for a dark ground in the investor deck (Slater, RIHub, Portal, Ocean
   State Labs, NVIDIA Inception, AWS), and Brown swaps to the light-ink version
   of its horizontal lockup; RIBio and Google Cloud keep their original artwork,
   which reads fine on either ground. */
[data-theme="dark"] .logo-cell img[src$="logo-brown-h.png"] { content: url("/assets/logo-brown-h-dark.png"); }
[data-theme="dark"] .logo-cell img[src$="logo-slater.png"] { content: url("/assets/logo-slater-dark.png"); }
[data-theme="dark"] .logo-cell img[src$="logo-rihub.png"] { content: url("/assets/logo-rihub-dark.png"); }
[data-theme="dark"] .logo-cell img[src$="logo-portal.png"] { content: url("/assets/logo-portal-dark.png"); }
[data-theme="dark"] .logo-cell img[src$="logo-nvidia-inception.png"] { content: url("/assets/logo-nvidia-inception-dark.png"); }
[data-theme="dark"] .logo-cell img[src$="logo-aws.svg"] { content: url("/assets/logo-aws-dark.png"); }
[data-theme="dark"] .logo-cell img[src$="osl-logo-light.png"] { content: url("/assets/osl-logo-dark.png"); }
@media (prefers-color-scheme: dark) {
  .logo-cell img[src$="logo-brown-h.png"] { content: url("/assets/logo-brown-h-dark.png"); }
  .logo-cell img[src$="logo-slater.png"] { content: url("/assets/logo-slater-dark.png"); }
  .logo-cell img[src$="logo-rihub.png"] { content: url("/assets/logo-rihub-dark.png"); }
  .logo-cell img[src$="logo-portal.png"] { content: url("/assets/logo-portal-dark.png"); }
  .logo-cell img[src$="logo-nvidia-inception.png"] { content: url("/assets/logo-nvidia-inception-dark.png"); }
  .logo-cell img[src$="logo-aws.svg"] { content: url("/assets/logo-aws-dark.png"); }
  .logo-cell img[src$="osl-logo-light.png"] { content: url("/assets/osl-logo-dark.png"); }
  [data-theme="light"] .logo-cell img[src$="logo-brown-h.png"] { content: url("/assets/logo-brown-h.png"); }
  [data-theme="light"] .logo-cell img[src$="logo-slater.png"] { content: url("/assets/logo-slater.png"); }
  [data-theme="light"] .logo-cell img[src$="logo-rihub.png"] { content: url("/assets/logo-rihub.png"); }
  [data-theme="light"] .logo-cell img[src$="logo-portal.png"] { content: url("/assets/logo-portal.png"); }
  [data-theme="light"] .logo-cell img[src$="logo-nvidia-inception.png"] { content: url("/assets/logo-nvidia-inception.png"); }
  [data-theme="light"] .logo-cell img[src$="logo-aws.svg"] { content: url("/assets/logo-aws.svg"); }
  [data-theme="light"] .logo-cell img[src$="osl-logo-light.png"] { content: url("/assets/osl-logo-light.png"); }
}

/* ---------- Theme toggle ----------
   Hidden until theme.js sets data-theme-ready on <html>, so it never appears as
   dead UI for a visitor whose JavaScript is blocked — they still get the
   OS-preference dark theme from the media query in tokens.css. */
.theme-toggle { display: none; }
:root[data-theme-ready] .theme-toggle {
  display: inline-flex; align-items: center; justify-content: center;
  width: 34px; height: 34px; padding: 0;
  background: none; border: 1px solid var(--border-default);
  border-radius: var(--radius-pill);
  color: var(--text-nav); cursor: pointer;
  transition: color var(--duration-fast) var(--ease-out),
              border-color var(--duration-fast) var(--ease-out);
}
.theme-toggle:hover { color: var(--text-nav-active); border-color: var(--border-strong); }
.theme-toggle svg { width: 17px; height: 17px; display: block; }
/* Show the icon for the theme the click would switch TO. */
.theme-toggle .icon-sun { display: var(--toggle-sun); }
.theme-toggle .icon-moon { display: var(--toggle-moon); }

/* ---------- Responsive / a11y ---------- */
@media (max-width: 960px) {
  .hero { min-height: 340px; }
  .hero-mark { height: 260px; right: -70px; opacity: 0.35; }
}
@media (max-width: 640px) {
  .hero-mark { display: none; }
  .site-header { padding: 12px 16px; }
  .site-nav { gap: 16px; }
  .hero { padding: 64px 20px 56px; min-height: 0; }
  .section { padding: 56px 0; }
  .callout { padding: 24px 20px; }
  .stage { padding: 22px; }
}
/* Below 768px the logo, three nav links, two pills and the toggle no longer
   fit one row at full size (they need about 740px), so the header wraps as
   the design's does: the actions drop under the logo, right-aligned, and wrap
   again among themselves if they must. The tighter gaps keep it to one row
   down to about 720px. The 480px rules below shrink everything back onto one
   row. */
@media (max-width: 767px) {
  .site-header { flex-wrap: wrap; gap: 8px 24px; }
  .header-actions { flex-wrap: wrap; justify-content: flex-end; margin-left: auto; gap: 8px 12px; }
  .site-nav { gap: 16px; }
}
/* Between the 480px compact bar and ~720px the wrapped header is two rows,
   ~116px tall, so in-page anchors need a deeper stop to land below it. */
@media (min-width: 481px) and (max-width: 719px) {
  .section[id], .cta-band[id] { scroll-margin-top: 130px; }
}
/* Between 481 and 560px the wrapped row (nav, both pills, toggle) is wider than
   the viewport and the header grew to three rows, burying anchor targets.
   Log in, which is also in the footer, gives way first. */
@media (max-width: 560px) {
  .nav-action:not(.nav-action-primary) { display: none; }
}
/* On a phone bar only the demo booking survives, per the design; the rest of
   the nav is one scroll away in the footer. */
@media (max-width: 420px) {
  .site-nav, .nav-action:not(.nav-action-primary) { display: none; }
}
/* Below 480px the header's fixed-size pieces (logo, nav, login pill, toggle)
   no longer fit at their full-width sizing — the P2 finding from #16's
   review reproduced this at 320/375px, where flex-shrink squeezed the
   background-image logo down to ~16px and wrapped the nav. Shrinking each
   piece here (instead of letting flex do it) keeps everything legible and on
   one row down to a 320px viewport. */
@media (max-width: 480px) {
  .site-header { padding: 8px 10px; gap: 8px; }
  .site-header .logo { width: 64px; height: 26px; }
  .header-actions { gap: 8px; }
  .site-nav { gap: 8px; }
  .site-nav a { font-size: 11px; }
  .nav-action { padding: 4px 8px; font-size: 11px; }
  /* Higher-specificity selector needed: :root[data-theme-ready] .theme-toggle
     below otherwise wins over a plain .theme-toggle override here regardless
     of media query or source order. */
  :root[data-theme-ready] .theme-toggle { width: 26px; height: 26px; }
  .theme-toggle svg { width: 14px; height: 14px; }
}
@media (prefers-reduced-motion: reduce) {
  .card, .usecase, .stage, .btn { transition: none; }
  .card:hover, .usecase:hover, .stage:hover { transform: none; }
  .hero-inner > *, .hero-mark, .hero-dot, .hero-bg img { animation: none; }
  .hero-bg img:first-of-type { opacity: 1; }
}
