/* ---------------------------------------------------------------------------
   GENERATED — do not hand-edit.

   Concatenation of src/styles/*.css in the cascade order declared by
   src/styles/index.css, so this export stays byte-identical to the React site's
   styling. Regenerate after changing any stylesheet there.

   Source order: base.css -> nav.css -> hero.css -> sections.css -> technology.css -> demo.css -> approach.css -> team.css -> about.css -> footer.css -> motion.css -> responsive.css
   --------------------------------------------------------------------------- */

/* ===== base.css ================================================= */
* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

:root {
  --bg: #05050a;
  --bg-elevated: #0d0e1a;
  --navy: #191e54;
  --lavender-light: #dddff5;
  --lavender-mid: #bec3f9;
  --lavender-soft: #a7adde;
  --lavender-hover: #d5d7ed;
  /* Accent carried over from the redesign: it fills the buttons and tints the
     flash demo's chrome. #0b0d24 on #747ac1 is 4.8:1, so label text on the
     solid fill still clears AA. */
  --accent: #747ac1;
  --accent-hover: #8e93d4;
  --on-accent: #0b0d24;
  /* Desaturated on purpose — a calm site should not shout in pure red. Still
     4.9:1 on --bg, so form errors stay readable. */
  --danger: #e39aa4;
  --text-primary: #e8e9f5;
  --text-body: #d5d7ed;
  --text-muted: #8a8fb5;
  --cta-gradient: linear-gradient(155deg, #f1f2fb, #c7cae8);
  --border-subtle: rgba(255, 255, 255, 0.08);
  --hairline: rgba(167, 173, 222, 0.14);
  /* One family throughout — display keeps its own variable so headings can be
     split off again without touching every rule that uses it. */
  --font-sans: "Outfit", system-ui, -apple-system, sans-serif;
  --font-display: "Outfit", system-ui, -apple-system, sans-serif;
  --nav-height: 84px;
}

/* ---------- Scrolling ----------

   The default build glides: an in-page jump eases to its destination rather than
   cutting to it, and scrolling is otherwise 1:1 with input, resting wherever the
   visitor stops. Section snapping is the motion-opt-in behaviour and lives in
   motion.css.

   Anyone asking the OS for reduced motion gets the glide turned back off, so a
   jump lands instantly; that override lives in responsive.css. */

html {
  scroll-behavior: smooth;
}

html, body {
  width: 100%;
  background: var(--bg);
  color: var(--text-body);
  font-family: var(--font-sans);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-rendering: optimizeLegibility;
}

/* React's mount point sits between <body> and the page sections, so it has to
   stay a plain full-width block or the sticky scroll stage stops working. */
#root {
  width: 100%;
}

::selection {
  background: rgba(190, 195, 249, 0.3);
}

/* ===== nav.css ================================================= */
/* Three columns rather than space-between: the logo and the actions are
   different widths, so distributing the free space would leave the links
   sitting left of true center. Equal-weight outer tracks pin the middle one to
   the header's midpoint whatever the sides do. */
#site-nav {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 50;
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  height: var(--nav-height);
  padding: 0 clamp(24px, 5vw, 64px);
  background: linear-gradient(
    to bottom,
    rgba(2, 2, 6, 0.9) 0%,
    rgba(2, 2, 6, 0.55) 55%,
    rgba(2, 2, 6, 0) 100%
  );
}

.nav-logo {
  display: flex;
  align-items: center;
  justify-self: start;
}

.nav-logo img {
  height: 20px;
  width: auto;
  aspect-ratio: 476 / 99;
  display: block;
}

.nav-links {
  display: flex;
  justify-self: center;
  /* Tops out at the original 40px on wide screens, but tightens rather than
     crowding the contact button now that Demo and Team are in the list too. */
  gap: clamp(20px, 2.6vw, 40px);
}

.nav-link {
  position: relative;
  font-size: 14px;
  font-weight: 500;
  letter-spacing: 0.01em;
  color: var(--text-muted);
  text-decoration: none;
  padding-bottom: 6px;
  transition: color 0.25s ease;
}

.nav-link:hover {
  color: var(--text-primary);
}

.nav-link.active {
  color: var(--text-primary);
}

.nav-link.active::after {
  content: "";
  position: absolute;
  left: 50%;
  bottom: -2px;
  width: 4px;
  height: 4px;
  border-radius: 50%;
  background: var(--lavender-mid);
  transform: translateX(-50%);
  box-shadow: 0 0 8px rgba(190, 195, 249, 0.8);
}

.nav-contact {
  font-family: var(--font-sans);
  font-weight: 600;
  font-size: 14px;
  padding: 10px 24px;
  border-radius: 999px;
  background: var(--accent);
  color: var(--on-accent);
  text-decoration: none;
  box-shadow: 0 6px 18px rgba(116, 122, 193, 0.22);
  white-space: nowrap;
  transition: background 0.25s ease, transform 0.25s ease, box-shadow 0.25s ease;
}

/* Hover deepens the fill and the shadow; the lift is motion-opt-in, in
   motion.css. */
.nav-contact:hover {
  background: var(--accent-hover);
  box-shadow: 0 10px 26px rgba(116, 122, 193, 0.32);
}

.nav-link:focus-visible,
.nav-contact:focus-visible {
  outline: 2px solid var(--lavender-mid);
  outline-offset: 4px;
  border-radius: 6px;
}

/* ===== hero.css ================================================= */
/* ---------- Hero: still (default) ---------- */

.hero-still {
  position: relative;
  display: flex;
  align-items: center;
  min-height: 100vh;
  padding: calc(var(--nav-height) + 40px) clamp(24px, 6vw, 96px) 80px;
  overflow: hidden;
  background: var(--bg);
}

/* One frame, held. No filter or transform is applied to it at any point. */
.hero-still-image {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  z-index: 0;
}

/* Flat scrim rather than a scroll-driven vignette: it knocks the frame's
   contrast down and guarantees the headline's contrast ratio, and its opacity
   never changes. */
.hero-still-scrim {
  position: absolute;
  inset: 0;
  z-index: 1;
  /* background: linear-gradient(
    to right,
    rgba(5, 5, 10, 0.92) 0%,
    rgba(5, 5, 10, 0.78) 45%,
    rgba(5, 5, 10, 0.55) 100%
  ); */
  pointer-events: none;
}

/* Same 1140px measure as every other section, so the headline sits on the
   page's existing left edge instead of a hero-only one. */
.hero-still-copy {
  position: relative;
  z-index: 2;
  width: 100%;
  /* max-width: 1140px; */
  margin: 0 auto;
}

.hero-still-copy h1 {
  max-width: 640px;
  font-family: var(--font-display);
  font-weight: 500;
  font-size: clamp(30px, 4.2vw, 56px);
  line-height: 1.12;
  color: var(--text-primary);
  margin: 0;
  letter-spacing: -0.02em;
}

/* ---------- Hero: scroll-scrubbed (opt-in) ---------- */

.hero-track {
  position: relative;
  width: 100%;
  height: 500vh;
}

#scroll-stage {
  position: sticky;
  top: 0;
  left: 0;
  width: 100%;
  height: 100vh;
  overflow: hidden;
  background: var(--bg);
}

#frame-canvas {
  display: block;
  width: 100%;
  height: 100%;
}

#bottom-gradient {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 55%;
  background: linear-gradient(to top, #000 0%, rgba(0, 0, 0, 0) 100%);
  opacity: 0;
  pointer-events: none;
}

#hero-copy {
  position: absolute;
  z-index: 6;
  top: 34%;
  left: 3%;
  max-width: 640px;
  text-align: left;
  pointer-events: none;
}

#hero-copy h1 {
  font-family: var(--font-display);
  font-weight: 500;
  font-size: clamp(30px, 4.2vw, 56px);
  line-height: 1.12;
  color: var(--text-primary);
  margin: 0;
  letter-spacing: -0.02em;
  text-shadow: 0 2px 40px rgba(0, 0, 0, 0.45);
}

#hero-copy.centered {
  text-align: center;
}

/* ---------- Frame preloader (only mounted with motion on) ---------- */

#loader {
  position: fixed;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 14px;
  background: var(--bg);
  color: #eee;
  font-size: 14px;
  letter-spacing: 0.04em;
  z-index: 40;
  transition: opacity 0.4s ease;
}

#loader.hidden {
  opacity: 0;
  pointer-events: none;
}

#loader-bar {
  width: 200px;
  height: 3px;
  background: rgba(255, 255, 255, 0.15);
  border-radius: 2px;
  overflow: hidden;
}

#loader-bar-fill {
  width: 0%;
  height: 100%;
  background: #fff;
  transition: width 0.15s ease-out;
}

/* ===== sections.css ================================================= */
/* ---------- Shared section layout ---------- */

.content-section {
  position: relative;
  padding: calc(var(--nav-height) + 40px) clamp(24px, 6vw, 96px) 80px;
  background: var(--bg);
  min-height: 100vh;
  display: flex;
  align-items: center;
  /* `clip`, not `hidden`, and the difference is load-bearing: `hidden` makes
     every section its own scroll container, which would scope any snap target
     inside one (the flash demo) to that section instead of the page. `clip`
     clips the decorative glows identically without creating one. See the
     snapping block in base.css. */
  overflow: clip;
}

.section-inner {
  position: relative;
  z-index: 2;
  max-width: 1140px;
  margin: 0 auto;
  width: 100%;
}

.section-inner.narrow {
  max-width: 760px;
}

.section-glow {
  position: absolute;
  width: 55vw;
  height: 55vw;
  max-width: 900px;
  max-height: 900px;
  border-radius: 50%;
  background: radial-gradient(circle, rgba(143, 147, 212, 0.13) 0%, rgba(143, 147, 212, 0.04) 45%, transparent 70%);
  filter: blur(30px);
  pointer-events: none;
  z-index: 1;
}

.glow-right {
  top: 10%;
  right: -18vw;
}

.glow-left {
  bottom: -10%;
  left: -18vw;
}

.glow-center {
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
}

.eyebrow {
  display: inline-flex;
  align-items: center;
  gap: 12px;
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--lavender-soft);
  margin-bottom: 24px;
}

.eyebrow::before {
  content: "";
  width: 28px;
  height: 1px;
  background: linear-gradient(to right, var(--lavender-soft), transparent);
}

.content-section h2 {
  font-family: var(--font-display);
  font-weight: 500;
  font-size: clamp(32px, 4vw, 54px);
  color: var(--text-primary);
  margin: 0 0 24px;
  line-height: 1.14;
  letter-spacing: -0.02em;
}

.section-lead {
  font-family: var(--font-sans);
  font-size: 17px;
  line-height: 1.75;
  color: var(--text-muted);
  /* max-width: 560px; */
  margin: 0 0 20px;
}

.section-head {
  margin-bottom: 24px;
}

/* `.reveal` styling lives in motion.css — with motion off the content is
   simply rendered in place, with no class and no observer. */

/* ===== technology.css ================================================= */
/* ---------- Technology ---------- */

.tech-grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 460px);
  gap: clamp(40px, 6vw, 96px);
  align-items: center;
}

.trigger-list {
  list-style: none;
  margin-top: 36px;
}

.trigger-list li {
  display: flex;
  align-items: flex-start;
  gap: 18px;
  padding: 18px 0;
  border-top: 1px solid var(--hairline);
}

.trigger-list li:last-child {
  border-bottom: 1px solid var(--hairline);
}

/* Ticked when the switch that covers this trigger is on, crossed when it is
   off. Colour comes from the row's state class via currentColor. */
.trigger-mark {
  flex-shrink: 0;
  width: 17px;
  height: 17px;
  margin-top: 2px;
  transition: color 0.25s ease;
}

.trigger-covered .trigger-mark {
  color: var(--lavender-mid);
}

.trigger-exposed .trigger-mark {
  color: var(--text-muted);
}

/* inline-block so the strike-through below measures the text, not the column.
   The detail line still sits underneath, because it is display: block. */
.trigger-title {
  position: relative;
  display: inline-block;
  font-size: 15px;
  font-weight: 600;
  color: var(--text-primary);
  letter-spacing: 0.01em;
  transition: color 0.25s ease;
}

.trigger-exposed .trigger-title {
  color: var(--text-muted);
}

/* A drawn line rather than text-decoration, because text-decoration cannot be
   animated — the wipe from left to right is added in motion.css. Held at
   scaleX(0) here so the default build simply shows or hides it. */
.trigger-title::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  top: 52%;
  height: 1px;
  background: currentColor;
  transform: scaleX(0);
  transform-origin: left center;
}

.trigger-exposed .trigger-title::after {
  transform: scaleX(1);
}

.trigger-detail {
  display: block;
  font-size: 13.5px;
  color: var(--text-muted);
  margin-top: 3px;
  line-height: 1.5;
}

.panel-wrap {
  position: relative;
}

.panel-wrap::before {
  content: "";
  position: absolute;
  inset: -12%;
  background: radial-gradient(circle, rgba(143, 147, 212, 0.18) 0%, transparent 65%);
  filter: blur(24px);
  z-index: 0;
}

.panel-mock {
  position: relative;
  z-index: 1;
  background: linear-gradient(165deg, rgba(22, 24, 44, 0.92), rgba(10, 11, 22, 0.96));
  border: 1px solid rgba(190, 195, 249, 0.16);
  border-radius: 26px;
  padding: 30px;
  box-shadow:
    0 40px 90px rgba(0, 0, 0, 0.5),
    inset 0 1px 0 rgba(255, 255, 255, 0.07);
}

.panel-mock-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
}

.panel-mock-header span {
  font-family: var(--font-sans);
  font-weight: 700;
  font-size: 17px;
  color: var(--text-primary);
  letter-spacing: -0.01em;
}

.panel-live-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: #7ee2a8;
  box-shadow: 0 0 10px rgba(126, 226, 168, 0.7);
}

.panel-mock-sub {
  font-size: 13px;
  color: var(--text-muted);
  margin: 6px 0 18px;
}

.panel-mock-toggle {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 16px;
  width: 100%;
  padding: 13px 0;
  border: 0;
  border-bottom: 1px solid var(--hairline);
  background: none;
  font-family: inherit;
  font-size: 14px;
  font-weight: 500;
  text-align: left;
  color: var(--text-body);
  cursor: pointer;
  transition: color 0.25s ease;
}

.panel-mock-toggle:hover {
  color: var(--text-primary);
}

.panel-mock-toggle:focus-visible,
.seg:focus-visible {
  outline: 2px solid var(--lavender-mid);
  outline-offset: 3px;
  border-radius: 6px;
}

/* Shared with the nav's motion toggle — see motion.css. */
.switch {
  width: 40px;
  height: 23px;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.08);
  position: relative;
  display: inline-block;
  flex-shrink: 0;
  transition: background 0.25s ease;
}

.switch::after {
  content: "";
  position: absolute;
  top: 2.5px;
  left: 2.5px;
  width: 18px;
  height: 18px;
  border-radius: 50%;
  background: var(--text-muted);
  transition: transform 0.25s ease, background 0.25s ease;
}

.switch.on {
  background: rgba(143, 147, 212, 0.4);
}

.switch.on::after {
  background: var(--lavender-light);
  transform: translateX(17px);
  box-shadow: 0 0 10px rgba(221, 223, 245, 0.5);
}

.panel-mock-segment {
  margin-top: 20px;
}

.seg-label {
  font-size: 13px;
  color: var(--text-body);
  font-weight: 600;
}

.seg-row {
  display: flex;
  gap: 8px;
  margin-top: 10px;
}

.seg {
  flex: 1;
  text-align: center;
  padding: 10px 0;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.045);
  border: 1px solid transparent;
  color: var(--lavender-soft);
  font-family: inherit;
  font-size: 13px;
  font-weight: 600;
  cursor: pointer;
  transition: background 0.25s ease, color 0.25s ease, box-shadow 0.25s ease;
}

.seg:hover {
  background: rgba(255, 255, 255, 0.09);
  color: var(--text-primary);
}

.seg.active,
.seg.active:hover {
  background: var(--cta-gradient);
  color: var(--navy);
  box-shadow: 0 6px 16px rgba(143, 147, 212, 0.25);
}

/* The green deepens with the sensitivity level: one variable carries the hue,
   and the fill, hairline, dot and glow all read from it, so a level only has to
   set --flag. Text stays lighter than the dot — a dark green on this panel
   would drop below a readable contrast. */
.panel-mock-flag {
  --flag: 126, 226, 168;
  margin: 20px 0 0;
  display: flex;
  align-items: center;
  gap: 9px;
  font-size: 12.5px;
  color: var(--text-muted);
  background: rgba(var(--flag), 0.07);
  border: 1px solid rgba(var(--flag), 0.24);
  padding: 10px 16px;
  border-radius: 999px;
  transition: background 0.3s ease, border-color 0.3s ease;
}

.flag-soft {
  --flag: 168, 235, 196;
}

.flag-medium {
  --flag: 82, 190, 130;
}

.flag-high {
  --flag: 38, 132, 84;
}

.panel-mock-flag .dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: rgb(var(--flag));
  box-shadow: 0 0 8px rgba(var(--flag), 0.7);
  flex-shrink: 0;
  transition: background 0.3s ease, box-shadow 0.3s ease;
}

/* ===== demo.css ================================================= */
/* ---------- Flash demo ----------

   Lives inside the Technology section rather than owning one, so the spacing
   here is the gap below `.tech-grid` — not section padding. */

.demo-block {
  margin-top: clamp(48px, 6vw, 80px);
  padding-top: clamp(40px, 5vw, 56px);
  border-top: 1px solid var(--hairline);
}

.demo-head {
  text-align: center;
  max-width: 660px;
  margin: 0 auto 32px;
}

/* One step down from the section's h2, which is the claim this demo backs up. */
.demo-head h3 {
  font-family: var(--font-display);
  font-weight: 500;
  font-size: clamp(22px, 2.6vw, 30px);
  line-height: 1.18;
  letter-spacing: -0.015em;
  color: var(--text-primary);
  margin: 0 0 16px;
}

.demo-head .section-lead {
  margin-left: auto;
  margin-right: auto;
}

/* The stage holds three stacked copies of the same frame. `touch-action: none`
   lets a touch drag move the handle instead of scrolling the page. */
.demo-stage {
  position: relative;
  border-radius: 20px;
  overflow: hidden;
  border: 1px solid var(--hairline);
  background: var(--bg-elevated);
  aspect-ratio: 1728 / 723;
  touch-action: none;
  cursor: ew-resize;
  user-select: none;
}

.demo-layer {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* Right of the handle: the untouched flash, screened back over the plate. The
   pulse is motion-opt-in and lives in motion.css. */
.demo-bloom {
  mix-blend-mode: screen;
  opacity: 0.62;
}

@keyframes demo-pulse {
  0%,
  100% {
    opacity: 0.18;
  }
  50% {
    opacity: 0.62;
  }
}

.demo-badge {
  position: absolute;
  top: 14px;
  font-family: var(--font-sans);
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
}

.demo-badge-on {
  left: 16px;
  color: var(--text-primary);
  background: rgba(5, 5, 10, 0.62);
  padding: 6px 12px;
  border-radius: 999px;
}

.demo-badge-off {
  right: 16px;
  color: rgba(232, 233, 245, 0.72);
}

.demo-divider {
  position: absolute;
  top: 0;
  bottom: 0;
  width: 2px;
  background: rgba(232, 233, 245, 0.8);
  transform: translateX(-1px);
}

.demo-handle {
  position: absolute;
  top: 50%;
  transform: translate(-50%, -50%);
  width: 50px;
  height: 50px;
  border-radius: 50%;
  background: var(--lavender-light);
  /* The chevrons inherit this via currentColor. */
  color: var(--accent);
  box-shadow: 0 6px 22px rgba(0, 0, 0, 0.5);
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: ew-resize;
}

.demo-handle:focus-visible {
  outline: 2px solid var(--lavender-mid);
  outline-offset: 4px;
}

.demo-readout {
  margin: 16px 0 0;
  text-align: center;
  font-size: 14px;
  color: var(--text-muted);
}

/* ===== approach.css ================================================= */
/* ---------- Approach ---------- */

.approach-section .section-head {
  margin-bottom: 12px;
}

.approach-list {
  display: grid;
  grid-template-columns: 1fr;
}

.approach-row {
  display: grid;
  grid-template-columns: minmax(200px, 320px) 1fr;
  gap: clamp(20px, 5vw, 52px);
  align-items: baseline;
  padding: 26px 0;
  border-top: 1px solid var(--hairline);
}

.approach-row:hover h3 {
  color: var(--lavender-light);
}

.approach-row h3 {
  font-family: var(--font-display);
  font-size: 21px;
  font-weight: 500;
  color: var(--text-primary);
  letter-spacing: -0.01em;
  transition: color 0.3s ease;
}

.approach-row p {
  font-size: 15px;
  line-height: 1.7;
  color: var(--text-muted);
}

/* ===== team.css ================================================= */
/* ---------- Team ---------- */

.team-head {
  text-align: center;
  margin-bottom: 32px;
}

.team-photo {
  position: relative;
  border-radius: 20px;
  overflow: hidden;
  border: 1px solid var(--hairline);
  margin-bottom: 20px;
}

.team-photo img {
  display: block;
  width: 100%;
  height: auto;
}

/* Scrim so the caption keeps contrast over the brightest part of the photo. */
.team-scrim {
  position: absolute;
  inset: 0;
  background: linear-gradient(
    to top,
    rgba(5, 5, 10, 0.78) 0%,
    rgba(5, 5, 10, 0.15) 42%,
    rgba(5, 5, 10, 0) 70%
  );
}

.team-caption {
  position: absolute;
  left: clamp(18px, 3vw, 30px);
  bottom: clamp(16px, 2.4vw, 24px);
  margin: 0;
  font-family: var(--font-display);
  font-weight: 500;
  font-size: clamp(15px, 1.8vw, 19px);
  color: var(--text-primary);
}

/* Fixed at three rather than auto-fit: with exactly three groups, auto-fit's
   intermediate two-column step would always strand one card on its own row. It
   drops straight to a single column at the 960px breakpoint in responsive.css. */
.team-grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 16px;
}

.team-group {
  padding: 26px 24px;
  background: var(--bg-elevated);
  border: 1px solid var(--hairline);
  border-radius: 18px;
}

/* Borrows the eyebrow's voice — this is a category label, not a title. */
.team-group-name {
  margin: 0 0 20px;
  font-family: var(--font-sans);
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--lavender-soft);
  text-align: center;
  /* Letter-spacing is applied after the last character too, so centering alone
     leaves the label sitting a hair left of true center. The indent puts that
     trailing space back on the front. */
  text-indent: 0.18em;
}

.team-members {
  list-style: none;
  display: grid;
  gap: 16px;
}

.team-member {
  display: flex;
  align-items: center;
  gap: 14px;
}

.member-initials {
  flex-shrink: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 38px;
  height: 38px;
  border-radius: 50%;
  background: rgba(116, 122, 193, 0.28);
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 14px;
  color: var(--text-primary);
}

.member-name {
  font-size: 15px;
  font-weight: 500;
  line-height: 1.35;
  color: var(--text-primary);
}

/* ===== about.css ================================================= */
/* ---------- About ---------- */

.about-inner {
  text-align: center;
}

.about-inner .section-lead {
  margin-left: auto;
  margin-right: auto;
  max-width: 620px;
}

.cta-button {
  display: inline-block;
  margin-top: 20px;
  padding: 15px 36px;
  border-radius: 999px;
  background: var(--accent);
  color: var(--on-accent);
  font-family: var(--font-sans);
  font-weight: 700;
  font-size: 15px;
  text-decoration: none;
  box-shadow: 0 12px 34px rgba(116, 122, 193, 0.28);
  transition: background 0.25s ease, transform 0.25s ease, box-shadow 0.25s ease;
}

/* Hover deepens the fill and the shadow; the lift is motion-opt-in, in
   motion.css. */
.cta-button:hover {
  background: var(--accent-hover);
  box-shadow: 0 18px 44px rgba(116, 122, 193, 0.38);
}

.cta-button:focus-visible {
  outline: 2px solid var(--lavender-mid);
  outline-offset: 4px;
}

/* ---------- Demo request form ---------- */

.request-form {
  /* Stacked so the button sits on its own line under the email field. */
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 14px;
  margin-top: 28px;
}

.request-input {
  /* Fills the column but never past the reading measure above it. */
  width: 100%;
  max-width: 320px;
  padding: 14px 22px;
  border: 1px solid var(--hairline);
  border-radius: 999px;
  background: var(--bg-elevated);
  color: var(--text-primary);
  font-family: var(--font-sans);
  font-size: 15px;
  transition: border-color 0.25s ease;
}

.request-input::placeholder {
  color: var(--text-muted);
}

.request-input:hover {
  border-color: var(--lavender-soft);
}

.request-input:focus-visible {
  outline: 2px solid var(--lavender-mid);
  outline-offset: 2px;
  border-color: var(--lavender-mid);
}

.request-input[aria-invalid='true'] {
  border-color: var(--danger);
}

.request-input:disabled {
  opacity: 0.6;
}

/* Rides on .cta-button for the accent fill, and undoes the parts of it that
   assume a standalone anchor rather than a control in the stack. Runs larger
   than the base CTA: it is the one action on this section. */
.request-submit {
  margin-top: 0;
  border: 0;
  padding: 18px 44px;
  font-family: var(--font-sans);
  font-size: 17px;
  cursor: pointer;
}

.request-submit:disabled {
  cursor: default;
  opacity: 0.65;
}

/* Always occupying a line so the stack does not jump when a message appears. */
.request-note {
  min-height: 1.3em;
  margin: 0;
  font-size: 14px;
  color: var(--danger);
}

.request-done {
  margin-top: 28px;
  font-size: 16px;
  color: var(--text-primary);
}

/* ===== footer.css ================================================= */
/* ---------- Footer ---------- */

footer#contact {
  background: #030305;
  padding: 64px clamp(24px, 6vw, 96px);
  border-top: 1px solid var(--border-subtle);
}

.footer-inner {
  max-width: 1140px;
  margin: 0 auto;
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  gap: 40px;
  align-items: start;
}

.footer-col {
  display: flex;
  flex-direction: column;
  gap: 14px;
}

.footer-left {
  align-items: flex-start;
}

.footer-center {
  flex-direction: row;
  gap: 28px;
  align-items: center;
  justify-content: center;
  padding-top: 4px;
}

.footer-center a {
  font-size: 14px;
  color: var(--text-muted);
  text-decoration: none;
  transition: color 0.25s ease;
}

.footer-center a:hover {
  color: var(--text-primary);
}

.footer-right {
  align-items: flex-end;
  text-align: right;
}

.footer-logo img {
  height: 18px;
  width: auto;
  aspect-ratio: 476 / 99;
  display: block;
}

.footer-tagline {
  font-family: var(--font-sans);
  font-size: 13.5px;
  color: var(--text-muted);
  line-height: 1.6;
  /* max-width: 260px; */
}

.footer-contact-link {
  color: var(--lavender-soft);
  text-decoration: none;
  font-size: 14px;
  transition: color 0.25s ease;
}

.footer-contact-link:hover {
  color: var(--lavender-hover);
}

.footer-copy {
  font-size: 12px;
  color: var(--text-muted);
  opacity: 0.7;
}

/* ===== motion.css ================================================= */
/* ---------- Motion opt-in ----------

   Everything in this file is inert until the visitor turns motion on, which
   adds `.motion` to <html>. The base stylesheets stay free of it — no reveal
   transitions, no hover displacement, no section snapping.

   Smooth scrolling is the one behaviour that is NOT gated here: it lives in
   base.css and applies to everyone, so it is inherited rather than restated
   below. What this file adds on top is where a scroll comes to rest.

   The frame scrub, vignette and headline slide are not here either: they are
   inline styles written by useScrollFrames, gated in JavaScript. */

/* ---------- Section snapping (opt-in) ----------

   A snap area taller than the viewport makes every position that covers the
   viewport valid, so a tall section becomes one long free-scroll zone rather
   than a thing you land on. That is tolerable for a section only slightly over
   height, but the technology section runs two screens and a fast fling would
   coast straight past the flash demo in its lower half. So that section opts out
   and hands its two screens the snap targets instead.

   This depends on `.content-section { overflow: clip }` in sections.css:
   `hidden` would make each section its own scroll container, and the targets
   inside one would then align to the section rather than the page. */

html.motion {
  scroll-snap-type: y mandatory;
}

/* Only `.hero-track` here — with motion on that is always the hero that
   renders, so the still hero can never be a snap area. */
html.motion .hero-track,
html.motion .content-section {
  scroll-snap-align: start;
}

/* Never let a fast fling skip a whole section. */
html.motion .content-section {
  scroll-snap-stop: always;
}

html.motion #technology {
  scroll-snap-align: none;
}

html.motion .tech-screen,
html.motion .demo-block {
  scroll-snap-align: start;
  scroll-snap-stop: always;
  /* Sections clear the fixed nav with their own top padding. These sit mid-page
     with no padding of their own, so the clearance comes from scroll-margin —
     same measure, so both screens rest at the same offset. */
  scroll-margin-top: calc(var(--nav-height) + 40px);
}

html.motion footer#contact {
  scroll-snap-align: end;
}

html.motion .nav-contact:hover {
  transform: translateY(-1px);
  box-shadow: 0 10px 26px rgba(116, 122, 193, 0.32);
}

html.motion .cta-button:hover {
  transform: translateY(-2px);
  box-shadow: 0 18px 44px rgba(116, 122, 193, 0.38);
}

/* The strike-through on a no-longer-filtered trigger wipes in from the left
   instead of appearing whole. The colour crossfade and the tick/cross swap both
   happen in the default build — they are the feedback for a switch the visitor
   just pressed — but the travelling line is decoration, so it waits for motion.
   Kept off the base build for the same reason only the toggle knob slides
   there: one moving part per interaction is the budget. */
html.motion .trigger-title::after {
  transition: transform 0.28s cubic-bezier(0.16, 1, 0.3, 1);
}

/* The flash demo's bloom only pulses for a visitor who asked for motion. In the
   default build it holds at full strength — a bright still frame is the point of
   the comparison, and it is the flicker, not the brightness, that this audience
   needs protecting from. */
html.motion .demo-bloom {
  animation: demo-pulse 2.6s ease-in-out infinite;
}

/* One-shot fade-and-lift on scroll. The `.reveal` class is only rendered when
   motion is on, so these rules never apply to the default build — they are
   scoped anyway to keep content visible if the class ever leaks through. */
html.motion .reveal {
  opacity: 0;
  transform: translateY(28px);
  transition: opacity 0.8s cubic-bezier(0.16, 1, 0.3, 1), transform 0.8s cubic-bezier(0.16, 1, 0.3, 1);
}

html.motion .reveal.in-view {
  opacity: 1;
  transform: translateY(0);
}

/* ---------- The toggle itself ---------- */

.nav-actions {
  display: flex;
  align-items: center;
  justify-self: end;
  gap: clamp(12px, 2vw, 24px);
}

.motion-toggle {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  min-height: 44px;
  padding: 0 4px;
  border: 0;
  background: none;
  font-family: inherit;
  font-size: 14px;
  font-weight: 500;
  color: var(--text-muted);
  cursor: pointer;
}

.motion-toggle:hover {
  color: var(--text-primary);
}

.motion-toggle:focus-visible {
  outline: 2px solid var(--lavender-mid);
  outline-offset: 3px;
  border-radius: 6px;
}

.motion-toggle .switch {
  /* The knob slide is the one transform left in the calm build. It is 25px of
     travel inside a 40px control, triggered by the user's own click, and it is
     the affordance that makes the switch legible as a switch. */
  transition: background 0.15s ease;
}

.motion-toggle .switch::after {
  transition: transform 0.15s ease, background 0.15s ease;
}

.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* ===== responsive.css ================================================= */
/* ---------- Responsive ---------- */

@media (max-width: 960px) {
  .tech-grid {
    grid-template-columns: 1fr;
    gap: 48px;
  }

  .approach-row {
    grid-template-columns: 1fr;
    gap: 8px;
  }

  /* Straight from three columns to one — see team.css for why there is no
     two-column step. */
  .team-grid {
    grid-template-columns: 1fr;
  }
}

@media (max-width: 860px) {
  .nav-links {
    display: none;
  }

  #hero-copy {
    left: 6%;
    max-width: 85%;
  }

  /* The label is the switch's visible name, so it stays; only the gap tightens
     once the nav links are gone. */
  .nav-actions {
    gap: 12px;
  }

  .footer-inner {
    grid-template-columns: 1fr;
    gap: 32px;
  }

  .footer-center {
    justify-content: flex-start;
    flex-wrap: wrap;
  }

  .footer-right {
    align-items: flex-start;
    text-align: left;
  }
}

/* ---------- Motion safety ----------

   Partly a backstop, partly the mechanism. The default build has no snapping and
   no reveal transitions, and the frame scrub is inline-styled by JavaScript so a
   CSS rule could never have stopped it — that gating lives in useScrollFrames.
   But smooth scrolling *does* ship by default now, and this is the only thing
   that turns it off.

   Scoped to `:not(.motion)` deliberately. Someone who found the toggle and
   switched motion on has made an explicit, informed, local choice; overriding
   it from the OS preference would leave them in a half-animated state where the
   canvas still scrubs but nothing else eases. */

@media (prefers-reduced-motion: reduce) {
  /* Needs its own rule rather than riding along with the universal selector
     below: the document's scroll-behavior is read off the root element, and
     `html:not(.motion) *` matches html's descendants, never html itself. Without
     this the default build would keep animating every in-page jump. */
  html:not(.motion) {
    scroll-behavior: auto !important;
  }

  html:not(.motion) * {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}
