/* ==========================================================================
   Katayoon Samadi — portfolio design system
   One shell, one type system, one spacing rhythm. Only --accent varies.
   ========================================================================== */

:root {
  /* Type */
  --font-display: "Bricolage Grotesque", "Archivo", Helvetica, Arial, sans-serif;
  --font-body: "Archivo", Helvetica, Arial, sans-serif;
  --font-mono: "Spline Sans Mono", ui-monospace, SFMono-Regular, Menlo, monospace;
  --font-script: "Yellowtail", cursive;

  /* Neutral shell — from the Figma home page */
  --paper: #ffffff;
  --ink: #0a0a0b;
  --ink-2: #18181b;
  --ink-3: #27272a;
  --muted: #71717a;
  --faint: #a1a1aa;
  --ghost: #888888;
  --rule: #d4d4d8;
  --rule-soft: #e4e4e7;
  --surface: #f4f4f5;

  /* Brand — the wordmark dot. Never varies per project. */
  --brand-red: #b90101;

  /* Project accent — overridden per case study */
  --accent: #18181b;
  --accent-hover: #000000;
  --accent-tint: #f4f4f5;
  --accent-ink: #ffffff;

  /* Rhythm */
  --page-max: 1440px;
  --page-pad-x: 64px;
  --section-gap: 160px;
  --radius-pill: 999px;

  /* Motion */
  --ease-out: cubic-bezier(0.16, 1, 0.3, 1);
  --dur: 620ms;
}

@media (max-width: 900px) {
  :root { --page-pad-x: 28px; --section-gap: 104px; }
}
@media (max-width: 640px) {
  :root { --page-pad-x: 20px; --section-gap: 84px; }
}

* { box-sizing: border-box; }

html {
  scroll-behavior: smooth;
  /* Fixed-position chrome (the jump rail, the tap ripple) escapes body's
     containing block, so body's clip can't contain it. Clip at the root. */
  overflow-x: clip;
}

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--font-body);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  overflow-x: clip;
}

img { max-width: 100%; }

a { color: inherit; text-decoration: none; }

::selection { background: var(--accent); color: var(--accent-ink); }

:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
  border-radius: 4px;
}

.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;
}

.skip-link {
  position: absolute; left: 16px; top: -60px; z-index: 200;
  background: var(--ink); color: #fff; padding: 12px 18px; border-radius: 8px;
  font-family: var(--font-mono); font-size: 12px; letter-spacing: 0.1em;
  transition: top 180ms var(--ease-out);
}
.skip-link:focus { top: 16px; }

/* --------------------------------------------------------------------------
   Icons
   -------------------------------------------------------------------------- */
.ms {
  font-family: "Material Symbols Rounded";
  font-weight: normal; font-style: normal;
  line-height: 1; letter-spacing: normal; text-transform: none;
  display: inline-block; white-space: nowrap; word-wrap: normal;
  direction: ltr; -webkit-font-feature-settings: "liga";
  -webkit-font-smoothing: antialiased;
}

/* --------------------------------------------------------------------------
   Site chrome — sticky glass nav, shared by every page
   -------------------------------------------------------------------------- */
.site-nav {
  position: fixed; inset: 0 0 auto 0; z-index: 90;
  display: flex; align-items: center; justify-content: space-between;
  gap: 20px;
  padding: 14px var(--page-pad-x);
  background: color-mix(in srgb, var(--nav-bg, #ffffff) 62%, transparent);
  -webkit-backdrop-filter: blur(18px) saturate(180%);
  backdrop-filter: blur(18px) saturate(180%);
  border-bottom: 1px solid transparent;
  color: var(--nav-fg, var(--ink));
  transform: translateY(-102%);
  transition: transform 420ms var(--ease-out), border-color 300ms ease,
              background-color 300ms ease;
}
.site-nav.is-visible { transform: translateY(0); }
.site-nav.is-visible { border-bottom-color: var(--nav-rule, rgba(0, 0, 0, 0.08)); }

.site-nav__mark {
  font-family: var(--font-display);
  font-size: 19px; font-weight: 800; letter-spacing: -0.03em;
}
.site-nav__mark img {
  display: block;
  height: 19px;
  width: auto;
}
.site-nav__mark .dot { color: var(--brand-red); }
.site-nav__mark .last { font-weight: 500; opacity: 0.55; }

.site-nav__links {
  display: flex; align-items: center; gap: 8px;
  font-family: var(--font-mono); font-size: 11px; letter-spacing: 0.12em;
}
.site-nav__links a {
  padding: 9px 15px; border-radius: var(--radius-pill);
  border: 1px solid var(--nav-rule, rgba(0, 0, 0, 0.12));
  transition: background-color 200ms ease, color 200ms ease, border-color 200ms ease;
  white-space: nowrap;
}
.site-nav__links a:hover {
  background: var(--accent); border-color: var(--accent); color: var(--accent-ink);
}
@media (max-width: 720px) {
  .site-nav__links a.is-optional { display: none; }
}

/* Icon-only nav action. Circular so it reads as a button, and sized to a
   proper 42px target rather than the 33px a text pill gives. */
.site-nav__icon {
  width: 42px; height: 42px;
  padding: 0 !important;
  display: grid;
  place-items: center;
  border-radius: 999px;
  flex: 0 0 auto;
}
.site-nav__icon .ms { font-size: 20px; line-height: 1; }

/* Scroll progress */
.progress {
  position: fixed; top: 0; left: 0; z-index: 95;
  height: 2px; width: 100%;
  transform: scaleX(0); transform-origin: 0 50%;
  background: var(--accent);
  transition: transform 90ms linear;
}

/* --------------------------------------------------------------------------
   Motion — reveal on scroll
   -------------------------------------------------------------------------- */
.reveal {
  opacity: 0;
  transform: translateY(20px);
  transition: opacity var(--dur) var(--ease-out),
              transform var(--dur) var(--ease-out);
  transition-delay: var(--reveal-delay, 0ms);
}
.reveal.is-in { opacity: 1; transform: none; }

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  .reveal { opacity: 1 !important; transform: none !important; transition: none !important; }
  .site-nav { transition: none; }
  *, *::before, *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
  }
}

/* --------------------------------------------------------------------------
   Glass — the signature. Vertical panes over the wordmark.
   -------------------------------------------------------------------------- */
.glass {
  position: absolute; inset: 0;
  display: flex; pointer-events: none;
  z-index: 3;
}
.glass__pane {
  position: relative;
  height: 100%;
  overflow: hidden;
  -webkit-backdrop-filter: blur(var(--pane-blur, 3px)) saturate(118%) brightness(1.015);
  backdrop-filter: blur(var(--pane-blur, 3px)) saturate(118%) brightness(1.015);
  background: linear-gradient(
    162deg,
    rgba(255, 255, 255, 0.30) 0%,
    rgba(255, 255, 255, 0.05) 40%,
    rgba(30, 30, 31, 0.018) 76%,
    rgba(30, 30, 31, 0.045) 100%
  );
  box-shadow:
    inset 1px 0 0 rgba(255, 255, 255, 0.72),
    inset -1px 0 0 rgba(30, 30, 31, 0.06),
    inset 0 1px 0 rgba(255, 255, 255, 0.55);
  will-change: transform;
  transform: translate3d(var(--pane-x, 0px), 0, 0);
  transition: transform 900ms var(--ease-out);
}
.glass__pane::after {
  content: "";
  position: absolute; inset: 0;
  pointer-events: none;
  background: linear-gradient(
    to right,
    rgba(255, 255, 255, 0.45) 0,
    rgba(255, 255, 255, 0) 12%,
    rgba(255, 255, 255, 0) 88%,
    rgba(30, 30, 31, 0.06) 100%
  );
}

/* The refraction: a clipped copy of the wordmark, nudged sideways inside each
   pane. Backdrop blur alone only frosts — the offset copy is what makes it
   read as glass with something underneath. */
.glass__ghost {
  position: absolute;
  pointer-events: none;
  opacity: 0;
  /* Lighter and red-leaning, so the refracted copy reads as a separate layer
     from the near-black wordmark underneath it. */
  color: var(--ghost-ink, color-mix(in srgb, var(--brand-red) 38%, #c9c9cf));
  transform: translate3d(var(--ghost-x, 0px), 0, 0) scaleY(var(--ghost-sy, 1));
  transform-origin: 0 50%;
  transition: transform 900ms var(--ease-out);
}
.glass__ghost.is-ready { opacity: 0.86; }

.glass.is-live .glass__pane,
.glass.is-live .glass__ghost { transition: transform 240ms cubic-bezier(0.22, 0.61, 0.36, 1); }

/* --------------------------------------------------------------------------
   Prototype widget — tap-through device frames inside case studies
   -------------------------------------------------------------------------- */
.proto {
  --proto-shell: #2f2f31;
  display: grid;
  gap: 28px;
  justify-items: center;
}
.proto__head {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  justify-content: center;
  font-family: var(--font-mono); font-size: 10px; letter-spacing: 0.18em;
  color: var(--proto-label, var(--muted));
  text-transform: uppercase;
}
.proto__head .ms { font-size: 15px; color: var(--accent); }

.proto__stage {
  position: relative;
  background: var(--proto-shell);
  box-shadow: 0 -1px 0 rgba(255, 255, 255, 0.1) inset,
              0 40px 80px -40px rgba(0, 0, 0, 0.55);
}
.proto--phone .proto__stage {
  width: min(300px, 78vw);
  padding: 10px;
  border-radius: 38px;
}
.proto__device { width: min(940px, 100%); }
.proto--laptop .proto__stage {
  width: 100%;
  padding: 13px 13px 16px;
  border-radius: 18px 18px 6px 6px;
}
.proto--laptop .proto__notch {
  width: 52px; height: 4px; border-radius: 999px; background: #1e1e1e;
  margin: 0 auto 10px;
}
.proto--laptop .proto__base {
  height: 11px; width: 112%; transform: translateX(-5.35%);
  background: #8e9095; border-radius: 0 0 12px 12px; position: relative;
}
.proto--laptop .proto__base::after {
  content: ""; position: absolute; left: 50%; top: 0; transform: translateX(-50%);
  width: 15%; height: 4px; border-radius: 0 0 6px 6px; background: #6f7176;
}

.proto__screen {
  position: relative;
  overflow: hidden;
  background: #0b0b0c;
}
.proto--phone .proto__screen { border-radius: 29px; aspect-ratio: 9 / 19.5; }
.proto--laptop .proto__screen { border-radius: 4px; aspect-ratio: 16 / 9.4; }

.proto__slides { position: absolute; inset: 0; }
.proto__slide {
  position: absolute; inset: 0;
  overflow-y: auto; overflow-x: hidden;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
  opacity: 0;
  transform: translateX(var(--slide-from, 24px)) scale(0.985);
  transition: opacity 360ms var(--ease-out), transform 420ms var(--ease-out);
  pointer-events: none;
  background: #fff;
}
.proto__slide::-webkit-scrollbar { display: none; }
.proto__slide.is-active {
  opacity: 1; transform: none; pointer-events: auto;
}
.proto__slide img { width: 100%; display: block; }

.proto__hotspot {
  position: absolute; inset: 0;
  cursor: pointer;
  background: transparent;
  border: 0;
  width: 100%;
}

.proto__controls {
  display: flex; align-items: center; gap: 18px; flex-wrap: wrap;
  justify-content: center;
}
.proto__btn {
  width: 44px; height: 44px; border-radius: 999px;
  border: 1px solid var(--proto-rule, var(--rule));
  background: transparent; color: inherit;
  display: grid; place-items: center; cursor: pointer;
  transition: background-color 180ms ease, color 180ms ease,
              border-color 180ms ease, transform 180ms var(--ease-out);
}
.proto__btn:hover:not(:disabled) {
  background: var(--accent); border-color: var(--accent); color: var(--accent-ink);
  transform: translateY(-2px);
}
.proto__btn:disabled { opacity: 0.3; cursor: default; }
.proto__btn .ms { font-size: 20px; }
.proto__btn svg { display: block; }

.proto__dots { display: flex; gap: 8px; }
.proto__dot {
  width: 8px; height: 8px; padding: 0; border-radius: 999px; cursor: pointer;
  border: 1px solid var(--proto-rule, var(--rule)); background: transparent;
  transition: all 200ms var(--ease-out);
}
.proto__dot.is-active { background: var(--accent); border-color: var(--accent); width: 26px; }

.proto__caption {
  font-family: var(--font-mono); font-size: 12px; letter-spacing: 0.1em;
  color: var(--proto-label, var(--muted)); text-align: center;
  min-height: 1.2em;
}
.proto__caption b { color: var(--accent); font-weight: 500; }

/* --------------------------------------------------------------------------
   FinHub reaction picker
   -------------------------------------------------------------------------- */
.rx { position: relative; display: inline-flex; }
.rx__pill {
  position: relative; display: flex; gap: 8px; padding: 8px;
  border-radius: 32px; background: #fff;
  box-shadow: 0 -4px 12px 0 rgba(0, 0, 0, 0.05), 0 4px 6px 0 rgba(0, 0, 0, 0.09);
}
.rx__item {
  width: 24px; height: 24px; padding: 0; border: 0; border-radius: 32px;
  background-size: cover; background-position: center; background-repeat: no-repeat;
  cursor: pointer; transition: transform 120ms ease;
}
.rx__item:hover, .rx__item:focus-visible { transform: scale(1.28); }
.rx__tip {
  position: absolute; top: -34px; left: 20px; transform: translateX(-50%);
  padding: 4px 8px; border-radius: 8px; background: #0f172a;
  box-shadow: 0 4px 6px 0 rgba(0, 0, 0, 0.09);
  font-family: var(--font-body); font-weight: 500; font-size: 12px; line-height: 20px;
  color: #fff; white-space: nowrap; pointer-events: none; opacity: 0;
  transition: left 140ms ease, opacity 120ms ease;
}
.rx-chip {
  display: inline-flex; gap: 8px; padding: 8px 16px; border-radius: 32px;
  align-items: center; border: 0; background: transparent;
  box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.12);
  font-family: var(--font-body); font-weight: 500; font-size: 16px; line-height: 24px;
  color: rgba(0, 0, 0, 0.6); cursor: default;
  transition: background-color 160ms ease;
}
.rx-chip:hover { background: #f1f5f9; }
.rx-chip.is-picked {
  background: #f1f5f9; box-shadow: inset 0 0 0 1px rgb(0, 0, 0);
  color: rgba(0, 0, 0, 0.87); cursor: pointer;
}
.rx-chip__icon { width: 24px; height: 24px; mix-blend-mode: darken; display: none; }
.rx-chip.is-picked .rx-chip__icon { display: block; }
.rx-chip__icon img { width: 100%; display: block; }
.rx-chip__face { flex-shrink: 0; display: block; }
.rx-chip.is-picked .rx-chip__face { display: none; }

/* --------------------------------------------------------------------------
   Case-study page footer + prev / next
   -------------------------------------------------------------------------- */
.pager {
  max-width: var(--page-max); margin: 0 auto;
  padding: 0 var(--page-pad-x) 96px;
  display: grid; gap: 1px;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 280px), 1fr));
  background: var(--rule-soft);
  border: 1px solid var(--rule-soft);
  border-radius: 14px; overflow: hidden;
  padding: 0; margin-inline: auto;
  width: min(100% - (2 * var(--page-pad-x)), var(--page-max));
  margin-bottom: 96px;
}
.pager__cell {
  background: var(--paper); padding: 34px 34px 38px;
  display: flex; flex-direction: column; gap: 10px;
  transition: background-color 220ms ease;
}
.pager__cell:hover { background: var(--surface); }
.pager__label {
  font-family: var(--font-mono); font-size: 10px; letter-spacing: 0.18em;
  color: var(--faint);
  display: flex; align-items: center; gap: 8px;
}
.pager__label .ms { font-size: 15px; color: var(--accent); }
.pager__name {
  font-family: var(--font-display); font-size: 30px; font-weight: 800;
  letter-spacing: -0.03em;
}
.pager__desc { font-size: 15px; color: var(--muted); line-height: 1.5; }

/* ------------------------------------------------------------------------
   Cursor light
   ------------------------------------------------------------------------ */
/* On case studies the blob rides in front of the artwork but behind the words,
   so it washes over imagery and never sits on top of text. The prototype
   frames are skipped — their parts are absolutely positioned. */
.cs :is(h1, h2, h3, h4, h5, p, ul, ol, dl, figcaption, blockquote, table):not(.proto *) {
  position: relative;
  z-index: 60;
}
main > .pager { position: relative; z-index: 60; }

/* The hero light — a liquid blob that lives on the wordmark dot and follows
   the pointer. It sits at z-index 1, under .glass--hero (2), so the panes'
   backdrop-filter blurs and refracts it as it drifts behind them.
   Two counter-morphing lobes read as one shifting mass; the wrapper stretches
   along the direction of travel so fast moves smear like a liquid. */
.hero__light {
  position: fixed;
  top: 0; left: 0;
  width: 0; height: 0;
  pointer-events: none;
  /* Above the page content, so it passes over the project cards. The hero and
     footer are lifted above it, so it still slides behind their glass. */
  z-index: 50;
  opacity: 0;
  transition: opacity 700ms ease;
  will-change: transform;
}
.hero__light.is-on { opacity: 1; }

.hero__light i {
  position: absolute;
  display: block;
  border-radius: 46% 54% 61% 39% / 47% 42% 58% 53%;
  background-color: color-mix(in srgb, var(--blob-base, var(--accent)) 34%, transparent);
  /* Absorbing a colour fades in rather than snapping. */
  transition: background-color 1500ms ease;
  will-change: border-radius, transform;
}
/* Each lobe morphs on its own cycle and drifts on another, so once the blob
   has picked up several colours they visibly move around inside it. */
.hero__light .lobe-a {
  left: -34px; top: -34px; width: 68px; height: 68px;
  filter: blur(13px);
  animation: blob-morph 7.5s ease-in-out infinite, lobe-a-drift 13s ease-in-out infinite;
}
.hero__light .lobe-b {
  left: -26px; top: -28px; width: 52px; height: 52px;
  filter: blur(11px); opacity: 0.72;
  animation: blob-morph-2 6.1s ease-in-out infinite, lobe-b-drift 17s ease-in-out infinite;
}
.hero__light .lobe-c {
  left: -29px; top: -20px; width: 56px; height: 56px;
  filter: blur(13px); opacity: 0.64;
  animation: blob-morph 8.9s ease-in-out infinite reverse, lobe-c-drift 21s ease-in-out infinite;
}
.hero__light .lobe-d {
  left: -22px; top: -32px; width: 44px; height: 44px;
  filter: blur(11px); opacity: 0.6;
  animation: blob-morph-2 7.2s ease-in-out infinite reverse, lobe-d-drift 15s ease-in-out infinite;
}

@keyframes lobe-a-drift {
  0%, 100% { transform: translate(0, 0) rotate(0deg); }
  33%      { transform: translate(15px, -11px) rotate(120deg); }
  66%      { transform: translate(-12px, 13px) rotate(240deg); }
}
@keyframes lobe-b-drift {
  0%, 100% { transform: translate(6px, 8px) rotate(0deg); }
  50%      { transform: translate(-16px, -12px) rotate(-180deg); }
}
@keyframes lobe-c-drift {
  0%, 100% { transform: translate(-9px, 4px) rotate(0deg); }
  40%      { transform: translate(13px, 14px) rotate(150deg); }
  75%      { transform: translate(9px, -14px) rotate(280deg); }
}
@keyframes lobe-d-drift {
  0%, 100% { transform: translate(10px, -6px) rotate(0deg); }
  45%      { transform: translate(-14px, 9px) rotate(-160deg); }
}

@media (prefers-reduced-motion: reduce) {
  .hero__light { display: none; }
}

/* --------------------------------------------------------------------------
   Jump rail — quick access to the top and bottom of a long page
   -------------------------------------------------------------------------- */
.jump {
  position: fixed;
  right: clamp(14px, 2vw, 26px);
  bottom: clamp(14px, 2vw, 26px);
  z-index: 92;
  display: flex;
  flex-direction: column;
  gap: 8px;
  opacity: 0;
  transform: translateY(10px);
  pointer-events: none;
  transition: opacity 260ms var(--ease-out), transform 260ms var(--ease-out);
}
.jump.is-on { opacity: 1; transform: none; pointer-events: auto; }
.jump button {
  width: 42px; height: 42px;
  display: grid; place-items: center;
  border-radius: 999px;
  background: color-mix(in srgb, var(--nav-bg, #ffffff) 78%, transparent);
  -webkit-backdrop-filter: blur(14px) saturate(160%);
  backdrop-filter: blur(14px) saturate(160%);
  border: 1px solid var(--nav-rule, rgba(0, 0, 0, 0.12));
  color: var(--ink);
  transition: background-color 180ms ease, color 180ms ease,
              border-color 180ms ease, transform 180ms var(--ease-out);
}
.jump button:hover {
  background: var(--accent); border-color: var(--accent);
  color: var(--accent-ink); transform: translateY(-2px);
}
.jump button .ms { font-size: 20px; }
@media (prefers-reduced-motion: reduce) { .jump { transition: none; } }

/* --------------------------------------------------------------------------
   Case studies on a phone. These pages are built with inline styles, so the
   rigid bits need overriding here.
   -------------------------------------------------------------------------- */
/* Phone screenshots are about 1:2.2 — far taller than any sensible card. Give
   the panel a steady shape and fit the whole screen inside it rather than
   cropping away two thirds of the UI. */
.cs-shot {
  aspect-ratio: 3 / 4;
  display: grid;
  place-items: center;
  overflow: hidden;
}
.cs-shot img {
  width: 100%;
  height: 100%;
  object-fit: contain !important;
  object-position: center !important;
}

/* The inset phone shot is a tall 828x1792 screen. Left at width:100% it grows
   without limit on a wide viewport, so cap the height and let width follow. */
.cs-inset {
  max-height: 520px;
  width: auto !important;
  margin-inline: auto;
}

/* ---------------------------------------------------------------------------
   Case-study summary — the 15-second read. A recruiter scans this and moves on;
   anyone who wants the depth scrolls. Deliberately quiet: mono labels, no
   panel, one hairline rule, so it never competes with the hero above it.
   -------------------------------------------------------------------------- */
.cs-tldr {
  max-width: 1240px;
  margin: 0 auto;
  padding: 0 clamp(20px, 5vw, 56px);
}
.cs-tldr dl {
  margin: 0;
  border-top: 1px solid currentColor;
  border-color: color-mix(in srgb, currentColor 16%, transparent);
  display: grid;
  gap: 0;
}
.cs-tldr div {
  display: grid;
  grid-template-columns: 116px minmax(0, 1fr);
  gap: 0 28px;
  padding: 20px 0;
  border-bottom: 1px solid color-mix(in srgb, currentColor 12%, transparent);
}
.cs-tldr dt {
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--accent);
  padding-top: 4px;
}
.cs-tldr dd {
  margin: 0;
  font-size: clamp(16px, 1.5vw, 18px);
  line-height: 1.6;
  max-width: 62ch;
  text-wrap: pretty;
}
/* ---------------------------------------------------------------------------
   Hero backdrop — the project's own art, washed into the top-right of the
   hero. Painted as a background layer on the element that owns the page
   colour, not as a pseudo-element: a background can never cover content, which
   a positioned ::before does. The gradient fades the art into --hero-fade, so
   it dissolves into the page rather than relying on opacity.
   -------------------------------------------------------------------------- */
.cs-hero-art {
  background-image:
    /* Fades the art out toward the text on the left. */
    linear-gradient(to left,
      transparent 0%,
      color-mix(in srgb, var(--hero-fade, #fff) 42%, transparent) 26%,
      var(--hero-fade, #fff) 56%),
    /* And dissolves the bottom edge into the page, so it has no hard cut-off. */
    linear-gradient(to bottom,
      transparent 0%,
      color-mix(in srgb, var(--hero-fade, #fff) 38%, transparent) 34%,
      color-mix(in srgb, var(--hero-fade, #fff) 82%, transparent) 54%,
      var(--hero-fade, #fff) 68%),
    var(--hero-art, none) !important;
  background-repeat: no-repeat !important;
  background-position: top right, top right, top right !important;
  background-size:
    min(74vw, 1060px) auto,
    min(74vw, 1060px) auto,
    min(74vw, 1060px) auto !important;
}
/* A full-length page screenshot scrolls inside its frame rather than being
   cropped to a slice or stretched down the page. */
.cs-scroll {
  height: 620px;
  overflow-y: auto;
  overflow-x: hidden;
  -webkit-overflow-scrolling: touch;
  overscroll-behavior: contain;
  scrollbar-width: thin;
}
.cs-scroll img { width: 100%; height: auto; display: block; }
@media (max-width: 620px) { .cs-scroll { height: 420px; } }

@media (max-width: 620px) {
  .cs-hero-art {
    background-size: 128vw auto, 128vw auto, 128vw auto !important;
  }
}

@media (max-width: 620px) {
  .cs-tldr div { grid-template-columns: 1fr; gap: 8px; padding: 16px 0; }
  .cs-tldr dt { padding-top: 0; }
}

@media (max-width: 620px) {
  /* Any multi-column grid collapses to one column at phone width. The
     comparison table is the exception — it stays a grid, just compressed.
     Matching on the property name rather than specific pixel values keeps
     this from silently breaking when the markup is retuned. */
  .cs [style*="grid-template-columns"] {
    grid-template-columns: 1fr !important;
  }
  /* Side-by-side flex blocks wrap instead of overflowing. */
  .cs [style*="display: flex"][style*="justify-content: space-between"],
  .cs [style*="display:flex"][style*="justify-content:space-between"] {
    flex-wrap: wrap !important;
  }
  /* Flex items given a desktop-width basis must be allowed to shrink. */
  .cs [style*="flex:1 1 4"], .cs [style*="flex: 1 1 4"],
  .cs [style*="flex:0 1 4"], .cs [style*="flex: 0 1 4"],
  .cs [style*="flex:1 1 5"], .cs [style*="flex: 1 1 5"],
  .cs [style*="flex:1 1 6"], .cs [style*="flex: 1 1 6"] {
    flex-basis: 100% !important;
    min-width: 0 !important;
  }
  /* Nothing in a flex row may refuse to shrink. */
  .cs [style*="display: flex"] > *,
  .cs [style*="display:flex"] > * { min-width: 0; }

  /* A tall device screenshot set into a panel: at full width once the panel
     stacks, it towers over the copy. Keep it device-sized. */
  .cs-inset { max-width: 236px; margin-inline: auto; }
  .cs-inset-copy { padding-bottom: 26px !important; }

  /* Stacked, a full-height phone screen per card is an endless scroll, so the
     panel gets squarer — the screen still shows whole, just smaller. */
  .cs-shot { aspect-ratio: 1 / 1; }

  /* Section rhythm is now a clamp in the markup itself, so only the inner
     cell paddings need reining in on a phone. */
  .cs [style*="padding:44px"]      { padding: 24px !important; }
  .cs [style*="padding: 44px"]     { padding: 24px !important; }
  .cs [style*="padding:32px 0 32px 24px"] { padding: 22px 0 !important; }
  .cs [style*="padding: 30px 28px"], .cs [style*="padding:30px 28px"] { padding: 20px 18px !important; }
  .cs [style*="padding: 26px 28px 30px"]  { padding: 20px 18px 22px !important; }
  .cs [style*="padding: 28px 30px"] { padding: 20px 18px !important; }

  /* Big pill CTAs are sized for a desktop hero; on a phone they run to the
     edges and wrap. Shrink the padding and the label with them. */
  .cs a[style*="padding:22px 38px"],
  .cs a[style*="padding:20px 34px"] {
    padding: 14px 22px !important;
    font-size: 15px !important;
    gap: 8px !important;
  }
  .cs a[style*="padding:22px 38px"] span,
  .cs a[style*="padding:20px 34px"] span { font-size: 18px !important; }

  /* The laptop frame's base bar is drawn wider than its screen by design. */
  .proto__base { max-width: 100%; }

  /* The reaction pill is nine items wide — let it wrap. */
  .rx__pill { flex-wrap: wrap; justify-content: center; }

  /* flex:0 0 auto rows of images refuse to shrink or wrap sensibly. */
  .cs [style*="flex:0 0 auto"], .cs [style*="flex: 0 0 auto"] {
    flex: 1 1 100% !important;
    min-width: 0 !important;
  }
  /* Images given a pixel width still cap to the column. */
  .cs img[style*="width:"], .cs img[style*="width: "] {
    max-width: 100% !important;
    height: auto;
  }

  /* Wide comparison tables are the exception: this comes after the blanket
     collapse above, so it wins on order and keeps its four columns. */
  .cs [style*="repeat(3, minmax("] {
    grid-template-columns: minmax(84px, 1.6fr) repeat(3, minmax(52px, 1fr)) !important;
    font-size: 12px;
  }
  .cs [style*="repeat(3, minmax("] > * {
    padding-left: 8px !important;
    padding-right: 8px !important;
    overflow-wrap: anywhere;
  }
}

/* A double click or tap pops the blob: the lobes fling apart and spring back. */
.hero__light.is-pop .lobe-a { animation: blob-morph 7.5s ease-in-out infinite, pop-a 620ms cubic-bezier(.2,1.5,.4,1) both; }
.hero__light.is-pop .lobe-b { animation: blob-morph-2 6.1s ease-in-out infinite, pop-b 620ms cubic-bezier(.2,1.5,.4,1) both; }
.hero__light.is-pop .lobe-c { animation: blob-morph 8.9s ease-in-out infinite reverse, pop-c 620ms cubic-bezier(.2,1.5,.4,1) both; }
.hero__light.is-pop .lobe-d { animation: blob-morph-2 7.2s ease-in-out infinite reverse, pop-d 620ms cubic-bezier(.2,1.5,.4,1) both; }
@keyframes pop-a { 0%{transform:none} 40%{transform:translate(-40px,-30px) scale(1.5)} 100%{transform:none} }
@keyframes pop-b { 0%{transform:none} 40%{transform:translate(42px,-24px) scale(1.45)} 100%{transform:none} }
@keyframes pop-c { 0%{transform:none} 40%{transform:translate(-30px,38px) scale(1.4)} 100%{transform:none} }
@keyframes pop-d { 0%{transform:none} 40%{transform:translate(34px,34px) scale(1.35)} 100%{transform:none} }

/* The ripple a touch leaves behind, in whatever colour the blob is carrying. */
.tap-ripple {
  position: fixed;
  z-index: 49;
  pointer-events: none;
  width: 20px; height: 20px;
  margin: -10px 0 0 -10px;
  border-radius: 50%;
  background: radial-gradient(circle,
    color-mix(in srgb, var(--ripple, var(--blob-base, var(--accent))) 42%, transparent) 0%,
    color-mix(in srgb, var(--ripple, var(--blob-base, var(--accent))) 16%, transparent) 55%,
    transparent 72%);
  animation: tap-ripple 640ms cubic-bezier(0.2, 0.7, 0.3, 1) forwards;
}
@keyframes tap-ripple {
  from { transform: scale(0.35); opacity: 0.9; }
  to   { transform: scale(11);   opacity: 0; }
}
@media (prefers-reduced-motion: reduce) { .tap-ripple { display: none; } }

/* Inline motion clips: behave exactly like the stills they replace. */
.cs-clip { display: block; width: 100%; height: auto; object-fit: cover; }
/* Under reduced motion the clip stays put on its poster frame — see
   initClips() — rather than disappearing. */


