/*
 * The feature run.
 *
 * Each section owns an accent, and scrolling one into view retints the whole
 * page: the header button, the rules, the numbers. That is not decoration for
 * its own sake. "Every experience can look like yours" is one of the harder
 * claims to make in words, and a page that visibly restyles itself while you
 * read makes it without a screenshot.
 *
 * Everything animated here is transform and opacity only, so it stays on the
 * compositor. A marketing page that stutters while it boasts about a product
 * being smooth is its own rebuttal.
 */

/* No rules between features. They were left over from an earlier layout of
   boxed panels; the spacing carries the rhythm on its own, and a hairline every
   six rem was just a row of grey lines down the page. */
.feature { padding: 6rem 0; }
@media (max-width: 46rem) {
  .feature { padding: 3.5rem 0; }
  /* The copy and its panel are one thought, so they sit closer together than
     two neighbouring features do. */
  .feature-grid { gap: 1.5rem; }
  .chips { margin-top: 1.1rem; }
  .tiles { margin-top: 1.75rem; }
  .cards { margin-top: 1.75rem; }
  .demo  { margin-top: 1.5rem; }
}

.feature-grid { display: grid; gap: 2rem; align-items: start; }
@media (min-width: 54rem) {
  .feature-grid { grid-template-columns: 1fr 1fr; gap: 4rem; align-items: center; }
  .feature:nth-child(even) .feature-copy { order: 2; }
}

.feature h2 { max-width: 16ch; }
.feature .lead { max-width: 40ch; }

.chips { display: flex; flex-wrap: wrap; gap: 0.45rem; margin-top: 1.5rem; }
.chip {
  font-size: 0.8rem; color: var(--muted);
  border: 1px solid var(--line); border-radius: 999px;
  padding: 0.28rem 0.7rem; background: var(--bg-raised);
}
.chip.on { color: var(--accent); border-color: color-mix(in srgb, var(--accent) 45%, transparent); }

/* The visual half. A plain frame with the accent doing the talking, rather
   than fake screenshots that would go stale the first time the app changes. */
.panel {
  position: relative; overflow: hidden;
  background: var(--bg-raised);
  border: 1px solid var(--line);
  border-radius: 22px;
  aspect-ratio: 4 / 3;
  display: flex; align-items: center; justify-content: center;
}
/* Names a view timeline for the things inside to hang off.
   A bare view() would not work here: it resolves against the nearest ancestor
   scroll container, and this panel is one, because it clips. The timeline was
   therefore attached to a box that never scrolls and sat frozen at halfway.
   Naming it on the panel measures the panel against the DOCUMENT instead,
   which is the scrolling everybody can actually see. */
.panel { view-timeline-name: --panel-view; }

.panel::after {
  content: ''; position: absolute; inset: 0;
  background: radial-gradient(circle at 50% 45%, color-mix(in srgb, var(--accent) 16%, transparent) 0%, transparent 62%);
  pointer-events: none;
}

/* Zone rings: concentric, the way a radius reads on the editor map. */
.zone-stack { position: relative; width: 62%; aspect-ratio: 1; }
.zone-stack span {
  position: absolute; inset: 0; margin: auto;
  border-radius: 999px; border: 1px solid var(--accent);
  opacity: 0.5;
}
.zone-stack span:nth-child(1) { width: 100%; height: 100%; opacity: 0.18; }
.zone-stack span:nth-child(2) { width: 66%;  height: 66%;  opacity: 0.34; }
.zone-stack span:nth-child(3) { width: 34%;  height: 34%;  opacity: 0.7; }
.zone-stack span:nth-child(4) {
  width: 12px; height: 12px; background: var(--accent); border: 0;
  box-shadow: 0 0 22px var(--accent); opacity: 1;
}

/* The sweep. A wedge of light rotating under the rings, plus a ping leaving
   the centre, which is what a radius actually does: it is a catchment waiting
   for somebody to enter it, not a static circle. */
.zone-stack .sweep {
  inset: 0; width: 100%; height: 100%; border: 0; opacity: 1;
  background: conic-gradient(from 0deg,
    color-mix(in srgb, var(--accent) 42%, transparent) 0deg,
    transparent 55deg, transparent 360deg);
  mask: radial-gradient(circle, #000 0 49%, transparent 50%);
  -webkit-mask: radial-gradient(circle, #000 0 49%, transparent 50%);
  animation: zone-sweep 4.2s linear infinite;
}
.zone-stack .ping {
  inset: 0; width: 100%; height: 100%;
  border: 1px solid var(--accent); opacity: 0;
  animation: zone-ping 4.2s cubic-bezier(0.15, 0.7, 0.25, 1) infinite;
}
.zone-stack .ping.b { animation-delay: 2.1s; }

@keyframes zone-sweep { to { transform: rotate(360deg); } }
@keyframes zone-ping {
  0%   { transform: scale(0.16); opacity: 0; }
  12%  { opacity: 0.55; }
  100% { transform: scale(1); opacity: 0; }
}

/* Waveform: bars at fixed heights, breathing. */
.wave { display: flex; align-items: center; gap: 5px; height: 46%; }
.wave i {
  display: block; width: 5px; border-radius: 999px;
  background: var(--accent); opacity: 0.75;
  animation: wave-pulse 1.9s ease-in-out infinite;
}

/* Chat: two bubbles, one of them the character's. */
.bubbles { width: 74%; display: flex; flex-direction: column; gap: 0.6rem; }
.bubbles b {
  font-weight: 400; font-size: 0.86rem; line-height: 1.45;
  padding: 0.6rem 0.85rem; border-radius: 14px; max-width: 84%;
}
.bubbles b.them {
  align-self: flex-start; background: color-mix(in srgb, var(--accent) 18%, var(--bg));
  border: 1px solid color-mix(in srgb, var(--accent) 40%, transparent);
  color: var(--text); border-bottom-left-radius: 5px;
}
.bubbles b.you {
  align-self: flex-end; background: #27272a; color: var(--muted);
  border-bottom-right-radius: 5px;
}

/* Lock: a keyhole ring that eases open once, when the section arrives. */
.lock { position: relative; width: 34%; aspect-ratio: 1; }
.lock span {
  position: absolute; inset: 0; border-radius: 999px;
  border: 2px solid var(--accent); opacity: 0.85;
}

.lock svg {
  position: absolute; inset: 0; margin: auto;
  width: 26%; height: 26%; color: var(--accent);
  opacity: 0; transform: scale(0.7);
  transition: opacity 0.6s ease 0.5s, transform 0.6s cubic-bezier(0.2, 0.6, 0.3, 1) 0.5s;
}
.in-view .lock svg { opacity: 0.85; }
/* Arrives after the inner ring has closed, so the two read as one movement
   rather than two things happening at once. */
.in-view .lock svg { transform: scale(1); }

/* Collectibles: a row of tokens that fill in. */
.tokens { display: flex; gap: 0.7rem; }
.tokens span {
  width: 40px; height: 40px; border-radius: 10px;
  border: 1px solid color-mix(in srgb, var(--accent) 45%, transparent);
  background: transparent; color: var(--accent);
  display: flex; align-items: center; justify-content: center;
  transition: background 0.5s ease, transform 0.5s cubic-bezier(0.2, 0.6, 0.3, 1);
}
.tokens svg { width: 18px; height: 18px; opacity: 0.35; transition: opacity 0.5s ease; }
/* They fill in left to right, the way a counter does on a walk, and the last
   one stays empty because there is always one more thing to find. */
.in-view .tokens span { background: color-mix(in srgb, var(--accent) 22%, transparent); }
.in-view .tokens svg { opacity: 1; }
.in-view .tokens span:nth-child(2), .in-view .tokens span:nth-child(2) svg { transition-delay: 0.1s; }
.in-view .tokens span:nth-child(3), .in-view .tokens span:nth-child(3) svg { transition-delay: 0.2s; }
.in-view .tokens span:nth-child(4), .in-view .tokens span:nth-child(4) svg { transition-delay: 0.3s; transform: scale(1.1); }
.in-view .tokens span:nth-child(5) { transition-delay: 0.4s; background: transparent; }
.in-view .tokens span:nth-child(5) svg { transition-delay: 0.4s; opacity: 0.25; }

/* AR: out of focus foliage, with something in front of it that is not there.
   A reticle said "targeting". Depth is what actually says augmented reality:
   the object holds still against a background that moves, which is the whole
   trick a phone is doing when it pins something to a place. */
.ar-scene { position: absolute; inset: 0; overflow: hidden; }

.bokeh { position: absolute; inset: -8%; }
.bokeh i {
  position: absolute; border-radius: 999px;
  filter: blur(14px);
  background: radial-gradient(circle at 35% 35%, rgba(190, 240, 200, 0.85), rgba(60, 130, 85, 0.45) 55%, transparent 74%);
}
/* Two depths. The far layer is smaller, dimmer and blurrier, and it travels
   less, which is what makes the near layer read as nearer. */
.bokeh.far i  { filter: blur(22px); opacity: 0.45; }
.bokeh.near i { filter: blur(11px); opacity: 0.8; }

/* A flat square could not say "an object is standing in that place". A box
   that turns can, because seeing more than one face of a thing is how you know
   it has volume and is sitting in the world rather than printed on the glass.
   Dragging it is deliberately not offered: a thing you can spin with a mouse
   reads as a 3D viewer, and the claim is that it is anchored to a spot. */
.ar-stage {
  position: absolute; left: 50%; top: 50%;
  width: 78px; height: 78px; margin: -39px 0 0 -39px;
  perspective: 420px;
}
.ar-object {
  position: absolute; inset: 0;
  transform-style: preserve-3d;
  animation: ar-turn 13s linear infinite;
}
.ar-object i {
  position: absolute; inset: 0;
  background: var(--accent);
  border: 1px solid color-mix(in srgb, #ffffff 32%, var(--accent));
  box-shadow: 0 0 40px -6px var(--accent);
}
.ar-object i:nth-child(1) { transform: translateZ(39px); }
.ar-object i:nth-child(2) { transform: rotateY(180deg) translateZ(39px); opacity: 0.72; }
.ar-object i:nth-child(3) { transform: rotateY(90deg)  translateZ(39px); opacity: 0.8; }
.ar-object i:nth-child(4) { transform: rotateY(-90deg) translateZ(39px); opacity: 0.8; }
.ar-object i:nth-child(5) { transform: rotateX(90deg)  translateZ(39px); opacity: 0.9; }
.ar-object i:nth-child(6) { transform: rotateX(-90deg) translateZ(39px); opacity: 0.55; }

@keyframes ar-turn {
  from { transform: rotateX(-14deg) rotateY(0deg); }
  to   { transform: rotateX(-14deg) rotateY(360deg); }
}

/* The foliage drifts on its own now rather than waiting for a scroll, so the
   background is alive even when the page is still. */
.bokeh.far  { animation: ar-drift-far  19s ease-in-out infinite; }
.bokeh.near { animation: ar-drift-near 13s ease-in-out infinite; }
@keyframes ar-drift-far {
  0%, 100% { transform: translate3d(-2%, 1.5%, 0) scale(1.04); }
  50%      { transform: translate3d(2%, -1.5%, 0) scale(1); }
}
@keyframes ar-drift-near {
  0%, 100% { transform: translate3d(3.5%, -2.5%, 0) scale(1); }
  50%      { transform: translate3d(-3.5%, 2.5%, 0) scale(1.06); }
}

/* Parallax on the scroll position, with no scroll listener anywhere: the
   browser drives these off the compositor. Where view timelines are not
   supported the layers simply hold still, which still reads correctly, so
   there is no fallback to write. */
/* The whole stage drifts with the scroll, which keeps the depth cue without
   the two layers being driven by two different clocks. */
@supports (animation-timeline: view()) {
  .ar-stage { animation: ar-par-stage linear both; animation-timeline: --panel-view; }
}
@keyframes ar-par-stage { from { transform: translateY(-9%); } to { transform: translateY(9%); } }

/* Theming: a word and a set of bars that keep repainting.
   The rest of the page changes accent once per section. This panel runs
   through a whole palette on its own, because "pick any colours you like" is
   a range rather than a single alternative. The word is there so the change
   is legible as type colour and not just as decoration. */
.theme-demo { display: flex; flex-direction: column; gap: 1.1rem; width: 66%; }
.theme-word {
  font-size: clamp(2.2rem, 6vw, 3rem); font-weight: 800;
  letter-spacing: -0.03em; line-height: 1;
  animation: theme-cycle 14s linear infinite;
}
.theme-bars { display: flex; flex-direction: column; gap: 0.6rem; }
.theme-bars i {
  display: block; height: 12px; border-radius: 999px;
  background: currentColor; animation: theme-cycle 14s linear infinite;
}
.theme-bars i:nth-child(1) { width: 100%; animation-delay: -1.2s; }
.theme-bars i:nth-child(2) { width: 74%;  animation-delay: -3.4s; opacity: 0.8; }
.theme-bars i:nth-child(3) { width: 48%;  animation-delay: -5.6s; opacity: 0.6; }

/* Scroll drives it where the browser can do that, so the palette moves with
   the reader rather than on a clock they are not watching. Time-based
   otherwise, because a still swatch would say nothing at all. */
@supports (animation-timeline: view()) {
  .theme-word, .theme-bars i {
    animation: theme-cycle linear both;
    animation-timeline: --panel-view;
  }
  .theme-bars i:nth-child(2) { animation-range: 5% 95%; }
  .theme-bars i:nth-child(3) { animation-range: 10% 90%; }
}

@keyframes theme-cycle {
  0%   { color: #10b981; }
  17%  { color: #06b6d4; }
  34%  { color: #6366f1; }
  50%  { color: #a855f7; }
  67%  { color: #ec4899; }
  84%  { color: #f59e0b; }
  100% { color: #10b981; }
}

@keyframes wave-pulse {
  0%, 100% { transform: scaleY(0.4); }
  50%      { transform: scaleY(1); }
}

/* ── Scroll reveal ──────────────────────────────────────────────────────── */
.reveal {
  opacity: 0; transform: translateY(18px);
  transition: opacity 0.7s ease, transform 0.7s cubic-bezier(0.2, 0.6, 0.3, 1);
}
.reveal.in-view { opacity: 1; transform: none; }
.reveal.d1 { transition-delay: 0.08s; }
.reveal.d2 { transition-delay: 0.16s; }

/* The accent transition is on :root, so header, chips and panels all move
   together rather than one lagging behind the rest. */
:root { transition: --accent 0.8s ease; }

@media (prefers-reduced-motion: reduce) {
  .reveal { opacity: 1; transform: none; transition: none; }
  .wave i { animation: none; }
  .bokeh.far, .bokeh.near, .ar-object, .ar-stage { animation: none; }
  .zone-stack .sweep, .zone-stack .ping { animation: none; opacity: 0.2; }
  .theme-word, .theme-bars i { animation: none; color: var(--accent); }
  .in-view .lock span:nth-child(2),
  .in-view .tokens span { transition: none; }
}

/* ── Example experience slot ────────────────────────────────────────────── */
/* Sized to a phone in landscape so the real embed can drop straight in
   without the section reflowing around it on the day it arrives. */
.demo {
  margin: 2rem 0 0;
  border: 1px solid var(--line);
  border-radius: 22px;
  background: var(--bg-raised);
  /* Wider and shorter than the phone it will mostly be used on, because the
     thing being demonstrated here is a map with zones on it, and a map needs
     width. Taller on a phone, where there is none to spare. */
  aspect-ratio: 16 / 11;
  max-width: none;
  display: flex; align-items: center; justify-content: center;
  position: relative; overflow: hidden;
}

.demo iframe { width: 100%; height: 100%; border: 0; display: block; border-radius: 22px; }
.demo-inner { display: flex; flex-direction: column; align-items: center; gap: 0.9rem; }
.demo-dot {
  width: 10px; height: 10px; border-radius: 999px;
  background: var(--accent); box-shadow: 0 0 24px var(--accent);
  animation: calibrate-bloom 4.5s ease-in-out infinite;
}
@media (prefers-reduced-motion: reduce) { .demo-dot { animation: none; } }


/* ── Ideas grid ─────────────────────────────────────────────────────────── */
.tiles { display: grid; gap: 1rem; margin-top: 2.5rem; grid-template-columns: repeat(2, 1fr); }
@media (min-width: 46rem) { .tiles { grid-template-columns: repeat(3, 1fr); } }

.tile h3 { margin: 0.75rem 0 0; font-size: 0.95rem; }
/* Placeholder until real images land. A flat grey box would look broken; a
   faint tint of the section accent reads as "not filled in yet" instead. */
.tile-img {
  aspect-ratio: 4 / 3; border-radius: 14px;
  border: 1px solid var(--line);
  background-color: var(--bg-raised);
  background-size: cover; background-position: center;
  position: relative; overflow: hidden;
}
.tile-img::after {
  content: ''; position: absolute; inset: 0;
  background: radial-gradient(circle at 40% 35%, color-mix(in srgb, var(--accent) 14%, transparent), transparent 70%);
}
/* Once a background-image is set the tint would sit over the photograph. */
.tile-img[style*="background-image"]::after { display: none; }

@media (min-width: 34rem) {
  .demo { aspect-ratio: 16 / 10; }
}
@media (max-width: 34rem) {
  /* Tall enough for the player's own welcome screen to lay out properly. At
     3/4 the frame was about 440px and the player responded by collapsing its
     map preview to a thin strip, so the embed advertised a map with no map in
     it. This is roughly a phone's proportions, which is what is inside it. */
  .demo { aspect-ratio: 6 / 11; }
}


/* ── Demo shield ────────────────────────────────────────────────────────── */
/* The iframe holds a scrollable map. Left live, it swallows the wheel whenever
   the pointer crosses it, and the page appears to stop scrolling. So it is
   inert until clicked and goes inert again when the pointer leaves. */
.demo iframe { pointer-events: none; }
.demo[data-active] iframe { pointer-events: auto; }

.demo-shield {
  position: absolute; inset: 0; z-index: 2;
  border: 0; padding: 0; cursor: pointer;
  background: rgba(9, 9, 11, 0.55);
  display: flex; align-items: center; justify-content: center;
  transition: opacity 0.25s ease;
  font: inherit; color: inherit;
}
.demo[data-active] .demo-shield { opacity: 0; pointer-events: none; }
.demo-shield-pill {
  background: var(--accent); color: #09090b;
  font-weight: 700; font-size: 0.9rem;
  padding: 0.6rem 1.1rem; border-radius: 999px;
  box-shadow: 0 8px 26px rgba(0, 0, 0, 0.45);
}
.demo-shield:hover .demo-shield-pill { filter: brightness(1.08); }

/* The tile placeholders keep their own hue rather than following the section,
   so the grid does not restyle itself while somebody is reading it. */
.tile-img::after { background: radial-gradient(circle at 40% 35%, rgba(16, 185, 129, 0.12), transparent 70%); }
