/* ============================================================================
   Depth layer — 3D scenes, glass, light and motion.

   Loaded after kit.css. It adds depth to existing components rather than
   replacing them, so a page that does not opt in looks exactly as before and
   every class in kit.css still means what it meant.

   Everything here degrades: no backdrop-filter, no 3D transforms and no
   IntersectionObserver still leaves a readable, correctly-coloured page. And
   every animation is suppressed under prefers-reduced-motion, because motion is
   decoration here — never the only way to understand a screen.
   ========================================================================== */

:root{
  --d-perspective:1400px;
  --d-lift:0px;                       /* set per-card by the tilt script */
  --glass-bg:rgba(255,255,255,.72);
  --glass-brd:rgba(255,255,255,.55);
  --glass-blur:18px;
  --glow-brand:0 0 0 1px rgba(31,94,255,.18), 0 18px 60px -12px rgba(31,94,255,.42);
  --glow-accent:0 0 0 1px rgba(255,107,61,.20), 0 18px 60px -12px rgba(255,107,61,.42);
  --sheen:linear-gradient(115deg,transparent 20%,rgba(255,255,255,.55) 45%,transparent 65%);
  --depth-1:0 1px 2px rgba(15,23,42,.06), 0 4px 10px -4px rgba(15,23,42,.10);
  --depth-2:0 2px 6px rgba(15,23,42,.07), 0 14px 34px -10px rgba(15,23,42,.16);
  --depth-3:0 6px 16px rgba(15,23,42,.09), 0 34px 70px -18px rgba(15,23,42,.26);
  --depth-4:0 10px 26px rgba(15,23,42,.11), 0 60px 120px -30px rgba(15,23,42,.34);
}
[data-theme="dark"]{
  --glass-bg:rgba(18,28,50,.62);
  --glass-brd:rgba(255,255,255,.10);
  --depth-1:0 1px 2px rgba(0,0,0,.5);
  --depth-2:0 2px 6px rgba(0,0,0,.55), 0 14px 34px -10px rgba(0,0,0,.6);
  --depth-3:0 6px 16px rgba(0,0,0,.6), 0 34px 70px -18px rgba(0,0,0,.7);
  --depth-4:0 10px 26px rgba(0,0,0,.65), 0 60px 120px -30px rgba(0,0,0,.8);
}
@media (prefers-color-scheme: dark){
  :root:not([data-theme="light"]){
    --glass-bg:rgba(18,28,50,.62); --glass-brd:rgba(255,255,255,.10);
    --depth-2:0 2px 6px rgba(0,0,0,.55), 0 14px 34px -10px rgba(0,0,0,.6);
    --depth-3:0 6px 16px rgba(0,0,0,.6), 0 34px 70px -18px rgba(0,0,0,.7);
    --depth-4:0 10px 26px rgba(0,0,0,.65), 0 60px 120px -30px rgba(0,0,0,.8);
  }
}

/* --- the light behind everything ---------------------------------------- */
/* An animated gradient mesh, painted with radial gradients rather than an image,
   so it costs nothing to download and recolours itself from the brand tokens. */
.mesh{ position:relative; isolation:isolate; overflow:hidden }
.mesh::before{
  content:""; position:absolute; inset:-25%; z-index:-1; pointer-events:none;
  background:
    radial-gradient(42% 46% at 18% 14%, color-mix(in srgb,var(--brand-500) 34%, transparent), transparent 66%),
    radial-gradient(38% 40% at 84% 8%,  color-mix(in srgb,var(--accent-500) 30%, transparent), transparent 62%),
    radial-gradient(50% 52% at 72% 82%, color-mix(in srgb,var(--brand-700) 28%, transparent), transparent 68%),
    radial-gradient(34% 36% at 8% 78%,  color-mix(in srgb,var(--accent-500) 20%, transparent), transparent 60%);
  filter:blur(26px) saturate(120%);
  animation:mesh-drift 26s ease-in-out infinite alternate;
}
.mesh--soft::before{ opacity:.45 }
.mesh--dark{ background:var(--brand-900); color:#fff }
.mesh--dark::before{ filter:blur(34px) saturate(150%); opacity:.85 }
@keyframes mesh-drift{
  0%  { transform:translate3d(0,0,0) scale(1) }
  50% { transform:translate3d(-3%,2%,0) scale(1.06) }
  100%{ transform:translate3d(2%,-3%,0) scale(1.03) }
}
/* A fine grain over the gradient. Flat gradients band on wide screens; noise hides it. */
.mesh::after{
  content:""; position:absolute; inset:0; z-index:-1; pointer-events:none; opacity:.22;
  background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='3'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23n)' opacity='.5'/%3E%3C/svg%3E");
}

/* --- glass --------------------------------------------------------------- */
.glass{
  background:var(--glass-bg);
  border:1px solid var(--glass-brd);
  border-radius:var(--radius-lg);
  box-shadow:var(--depth-2);
  backdrop-filter:blur(var(--glass-blur)) saturate(160%);
  -webkit-backdrop-filter:blur(var(--glass-blur)) saturate(160%);
}
/* Where backdrop-filter is unsupported the translucent fill alone reads as washed out,
   so fall back to an opaque surface rather than an unreadable one. */
@supports not ((backdrop-filter:blur(1px)) or (-webkit-backdrop-filter:blur(1px))){
  .glass{ background:var(--paper) }
}
.glass--edge{ position:relative; overflow:hidden }
.glass--edge::before{
  content:""; position:absolute; inset:0; border-radius:inherit; pointer-events:none;
  background:linear-gradient(140deg,rgba(255,255,255,.6),transparent 42%);
  mix-blend-mode:overlay;
}

/* --- 3D scene ------------------------------------------------------------ */
.scene{ perspective:var(--d-perspective); perspective-origin:50% 40%; transform-style:preserve-3d }
.scene--near{ --d-perspective:900px }
.scene--far{ --d-perspective:2200px }

/* Pointer-reactive tilt. The script writes --rx/--ry; with JS off the card simply
   sits flat, which is a perfectly good card. */
.tilt{
  transform-style:preserve-3d;
  transform:rotateX(var(--rx,0deg)) rotateY(var(--ry,0deg)) translateZ(var(--d-lift));
  transition:transform .5s cubic-bezier(.19,1,.22,1), box-shadow .5s cubic-bezier(.19,1,.22,1);
  will-change:transform;
}
.tilt[data-tilting="true"]{ transition:transform .08s linear }
.tilt:hover{ --d-lift:14px; box-shadow:var(--depth-4) }
/* Children lift out of the card at their own depth, which is what stops a tilt from
   looking like a flat picture of a tilted card. */
.tilt .layer-1{ transform:translateZ(18px) }
.tilt .layer-2{ transform:translateZ(38px) }
.tilt .layer-3{ transform:translateZ(64px) }
.tilt .layer-4{ transform:translateZ(96px) }

/* --- cards with depth ---------------------------------------------------- */
.card--3d{
  background:var(--paper);
  border:1px solid color-mix(in srgb,var(--ink-300) 60%, transparent);
  box-shadow:var(--depth-2);
  position:relative; overflow:hidden;
}
.card--3d::before{
  content:""; position:absolute; inset:0 0 auto; height:1px;
  background:linear-gradient(90deg,transparent,color-mix(in srgb,var(--brand-500) 55%,transparent),transparent);
}
.card--3d:hover{ box-shadow:var(--depth-3) }
.card--glow:hover{ box-shadow:var(--glow-brand) }

/* --- the phone, with real depth ------------------------------------------ */
/* An explicit width, because as a flex item wrapping a grid whose only column is
   minmax(0,1fr) it sizes to content — and that content is happy to be zero. The phone
   rendered as a ten-pixel sliver. */
/* Straight on, not rotated. Depth here comes from the bezel, the glass edge and the
   shadow — a 14-degree yaw skewed the conversation, which is the one thing on the page
   we are actually asking anyone to read. */
.phone3d{ position:relative; width:368px; max-width:100%; transform-style:preserve-3d }
.phone3d .phone{
  box-shadow:
    0 2px 4px rgba(15,23,42,.12),
    0 12px 28px -8px rgba(15,23,42,.28),
    0 50px 90px -30px rgba(15,23,42,.45),
    inset 0 0 0 1px rgba(255,255,255,.06);
}

/* --- photography --------------------------------------------------------- */
/* A photo is a surface with depth, not a rectangle dropped on the page. */
.shot{ position:relative; border-radius:var(--radius-lg); overflow:hidden;
  box-shadow:var(--depth-2); background:var(--ink-100); isolation:isolate;
  border:1px solid var(--hairline) }
.shot img{ width:100%; height:100%; object-fit:cover; display:block;
  transition:transform .9s cubic-bezier(.19,1,.22,1) }
.shot:hover img{ transform:scale(1.045) }
/* A scrim, not a curtain. At 78% of near-black the previous overlay turned every
   photograph into a dark smudge — which defeats the entire reason for having them. It
   only needs to be dark enough under the caption to keep the text legible. */
.shot::after{
  content:""; position:absolute; inset:0; pointer-events:none;
  background:linear-gradient(to top, rgba(6,10,20,.72) 0%, rgba(6,10,20,.30) 26%, transparent 55%);
}
.shot--nocaption::after{ background:linear-gradient(to top, rgba(6,10,20,.30), transparent 45%) }
.shot--plain::after{ display:none }
.shot__caption{ position:absolute; inset:auto 0 0; z-index:1; padding:var(--space-5);
  color:#fff; font-size:var(--text-sm); line-height:1.5 }
.shot__caption strong{ display:block; font-size:var(--text-lg); font-weight:700; letter-spacing:-.01em }
.shot--16x9{ aspect-ratio:16/9 } .shot--4x3{ aspect-ratio:4/3 }
.shot--1x1{ aspect-ratio:1/1 }   .shot--9x16{ aspect-ratio:9/16 }
/* Reserve the space before the image arrives so nothing jumps as photos load. */
.shot img[loading="lazy"]{ background:var(--ink-100) }

/* --- scroll reveal ------------------------------------------------------- */
/* The hidden state is scoped to .has-reveal, which kit3d.js sets on <html> only once it
   is running and has an observer to un-hide with. Without that guard a JS error, a
   blocked module or an old browser would leave whole sections of the page permanently
   at opacity 0 — content that exists, is announced to a screen reader, and cannot be
   seen. Invisible-by-default is never worth an entrance animation. */
.has-reveal [data-reveal]{ opacity:0; transform:translate3d(0,18px,0) }
.has-reveal [data-reveal][data-shown="true"]{ opacity:1; transform:none;
  transition:opacity .7s cubic-bezier(.19,1,.22,1), transform .7s cubic-bezier(.19,1,.22,1) }
.has-reveal [data-reveal="scale"]{ transform:translate3d(0,18px,0) scale(.97) }
.has-reveal [data-reveal="left"]{ transform:translate3d(-24px,0,0) }
.has-reveal [data-reveal="right"]{ transform:translate3d(24px,0,0) }
/* Staggering is set inline by the script from the element's index in its group. */
[data-reveal]{ transition-delay:var(--reveal-delay,0ms) }

/* --- parallax ------------------------------------------------------------ */
.parallax{ will-change:transform; transform:translate3d(0,var(--par-y,0px),0) }

/* --- marquee ------------------------------------------------------------- */
.marquee{ display:flex; gap:var(--space-3); overflow:hidden;
  -webkit-mask-image:linear-gradient(90deg,transparent,#000 8%,#000 92%,transparent);
  mask-image:linear-gradient(90deg,transparent,#000 8%,#000 92%,transparent) }
.marquee__track{ display:flex; gap:var(--space-3); flex:none;
  animation:marquee 46s linear infinite }
.marquee:hover .marquee__track{ animation-play-state:paused }
@keyframes marquee{ from{ transform:translateX(0) } to{ transform:translateX(calc(-100% - var(--space-3))) } }

/* --- accents ------------------------------------------------------------- */
/* Kept inside one hue family. Running blue all the way to violet made the last line
   read as a different brand from the first. */
.grad-text{
  background:linear-gradient(96deg,var(--brand-500) 0%,var(--brand-300) 48%,var(--accent-300) 100%);
  -webkit-background-clip:text; background-clip:text; color:transparent;
  /* A gradient on transparent text disappears entirely in forced-colours mode. */
}
@media (forced-colors: active){ .grad-text{ color:CanvasText; background:none } }

.rule-glow{ height:1px; border:0; background:linear-gradient(90deg,transparent,
  color-mix(in srgb,var(--brand-500) 60%,transparent),transparent) }

/* align-self, because an inline-flex child of a column flex container still stretches to
   the container's width — which turned a small status pill into a full-width green bar. */
.pill-live{ display:inline-flex; align-self:flex-start; align-items:center; gap:6px; font-size:var(--text-xs);
  font-weight:600; padding:.25em .8em; border-radius:var(--radius-pill);
  background:color-mix(in srgb,var(--ok) 14%,transparent); color:var(--ok) }
.pill-live::before{ content:""; width:6px; height:6px; border-radius:50%; background:var(--ok);
  box-shadow:0 0 0 0 color-mix(in srgb,var(--ok) 60%,transparent);
  animation:pulse-dot 2.4s ease-out infinite }
@keyframes pulse-dot{ 70%{ box-shadow:0 0 0 8px transparent } 100%{ box-shadow:0 0 0 0 transparent } }

/* --- the whole layer stands down when asked ------------------------------ */
@media (prefers-reduced-motion: reduce){
  .mesh::before{ animation:none }
  .marquee__track{ animation:none }
  .pill-live::before{ animation:none }
  .tilt{ transform:none!important; transition:none }
  .phone3d{ transform:none }
  .shot:hover img{ transform:none }
  .has-reveal [data-reveal]{ opacity:1; transform:none }
}

/* ============================================================== spotlight === */
/* A soft light behind the hero art. On a near-black ground this is what makes a product
   shot sit *in* the page rather than on top of it, and it costs one pseudo-element. */
/* `overflow: clip` rather than `hidden`: a rotated phone paints outside its layout box
   and would otherwise widen the page, but `hidden` would also make this a scroll
   container and trap the sticky elements inside it. */
.spotlight{ position:relative; isolation:isolate; overflow-x:clip }
.spotlight::before{
  content:""; position:absolute; z-index:-1; inset:-18% -12%;
  background:radial-gradient(48% 46% at 50% 42%,
    color-mix(in srgb,var(--brand-500) 34%,transparent), transparent 70%);
  filter:blur(52px);
}
.spotlight--accent::before{
  background:radial-gradient(48% 46% at 50% 42%,
    color-mix(in srgb,var(--accent-500) 32%,transparent), transparent 70%);
}

/* --- display type -------------------------------------------------------- */
.display{ font-size:clamp(2.75rem,1.6rem + 4.6vw,var(--text-6xl)); line-height:1.02;
  letter-spacing:-.045em; font-weight:700; text-wrap:balance }
.display--center{ text-align:center; margin-inline:auto; max-width:18ch }
.section--center{ text-align:center }
.section--center .section__head{ align-items:center; margin-inline:auto; text-align:center }
.section--center .eyebrow{ justify-content:center }
.section--center .cluster{ justify-content:center }

/* --- the scroll reveal, with scale --------------------------------------- */
/* Apple's reveal is a scale as well as a fade: content arrives rather than appearing.
   The distance is small on purpose — a big travel reads as a slideshow. */
.has-reveal [data-reveal]{ opacity:0; transform:translate3d(0,26px,0) scale(.985) }
.has-reveal [data-reveal][data-shown="true"]{ opacity:1; transform:none;
  transition:opacity .8s cubic-bezier(.16,1,.3,1), transform .9s cubic-bezier(.16,1,.3,1) }

