/* Mira Life reconstruction: layout, components and sections.
   Design values come from tokens.css, measured from the live reference. */

/* ------------------------------------------------------------------ reset */
*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; background: var(--black); }
body {
  margin: 0;
  /* The page opens on the hero's surface and closes on the black footer.
     The body was white, so any slack at either end, and the overscroll
     area, flashed a white band that belongs to no section. */
  background: var(--black);
  color: var(--ink);
  font-family: var(--font-sans);
  font-size: var(--fs-body);
  font-weight: var(--fw-regular);
  line-height: var(--lh-body);
  -webkit-font-smoothing: antialiased;
  overflow-x: clip; /* clip, not hidden: hidden would kill position:sticky */
}
img, svg, video { display: block; max-width: 100%; }
img, video { height: auto; }
a { color: inherit; text-decoration: none; }
button { font: inherit; color: inherit; }
h1, h2, h3, h4, p, ul, ol, figure, blockquote { margin: 0; }
ul, ol { padding: 0; list-style: none; }
:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; border-radius: 4px; }

/* --------------------------------------------------------------- utilities */
.container {
  width: 100%;
  max-width: var(--content);
  margin-inline: auto;
  padding-inline: var(--gutter);
}
.container--mid { max-width: var(--content-mid); }
.container--narrow { max-width: var(--content-narrow); }

/* Section surfaces. The reference runs dark hero, three black, six white,
   then seven black; each section below declares which it is. */
.section { padding-block: var(--section-y); position: relative; }
.section--black { background: var(--black); color: var(--on-dark); }
.section--ink { background: var(--ink); color: var(--on-dark); }
.section--white { background: var(--surface); color: var(--ink); }
.section--flush { padding-block: 0; }
.section--lead-in { padding-top: var(--section-y-lg); }
/* The closing phone is cut off by this edge, so the section has to clip. */
.section--lead-in { overflow: hidden; }

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

/* --------------------------------------------------------------- typography */
h1, .h1 { font-size: var(--fs-h1); font-weight: var(--fw-bold); line-height: var(--lh-h1); }
h2, .h2 { font-size: var(--fs-h2); font-weight: var(--fw-bold); line-height: var(--lh-h2); }
h3, .h3 { font-size: var(--fs-h3); font-weight: var(--fw-bold); line-height: var(--lh-h3); }

/* The reference writes its big headings in two tones: the whole line sits in
   the muted grey and the emphasised clause is lifted to full contrast, using
   spans inside the heading. */
.h-split { color: var(--on-dark); }
.h-split .dim { color: var(--grey); }
.section--white .h-split { color: var(--ink); }
.section--white .h-split .dim { color: var(--grey); }

.eyebrow {
  display: inline-block;
  font-size: var(--fs-eyebrow);
  font-weight: var(--fw-bold);
  text-transform: uppercase;
  color: var(--accent);
}
.eyebrow--pill {
  padding: 7px 14px;
  border-radius: var(--radius-btn);
  background: var(--accent-soft);
}

.lead {
  font-size: var(--fs-lead);
  line-height: var(--lh-lead);
  color: var(--grey);
}
.section--white .lead { color: var(--text); }
.lead b, .lead strong { color: var(--on-dark); font-weight: var(--fw-bold); }
.section--white .lead b, .section--white .lead strong { color: var(--ink); font-weight: var(--fw-bold); }
.lead p + p { margin-top: 0.8em; }

.stack { display: flex; flex-direction: column; }
.stack--sm { gap: 12px; }
.stack--md { gap: 20px; }
.stack--lg { gap: 32px; }
.stack--xl { gap: 48px; }
.center { text-align: center; }
.center .btn-row { justify-content: center; }
.measure { max-width: 860px; }
.measure--center { max-width: 860px; margin-inline: auto; }

/* ----------------------------------------------------------------- buttons */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 10px;
  padding: var(--btn-pad-y) var(--btn-pad-x);
  min-height: 48px;
  border: 0; border-radius: var(--radius-btn);
  font-size: var(--fs-body); font-weight: var(--fw-medium);
  cursor: pointer; text-align: center;
  transition: transform var(--dur-fast) var(--ease),
              background-color var(--dur-fast) var(--ease),
              color var(--dur-fast) var(--ease), opacity var(--dur-fast) var(--ease);
}
/* On a dark surface the primary button is white; on a light surface it is ink. */
.btn--primary { background: var(--brand); color: var(--brand-ink); }
.section--white .btn--primary { background: var(--brand); color: var(--brand-ink); }
.btn--primary:hover { background: var(--brand-hover); }
.btn--ghost { background: var(--btn-ghost-bg); color: var(--btn-ghost-fg); }
.section--white .btn--ghost {
  background: var(--white); color: var(--ink);
  box-shadow: inset 0 0 0 1px rgba(23, 24, 28, 0.13);
}
.btn--ghost:hover { background: rgba(70, 78, 86, 0.6); }
.section--white .btn--ghost:hover { background: #e7e9ec; }
.btn-row { display: flex; flex-wrap: wrap; gap: 12px; }
.btn-row--center { justify-content: center; }
/* A ghost button on the black band needs a visible edge of its own. */
.btn--ghost-dark {
  background: transparent;
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.22);
  color: var(--white);
}
.btn--ghost-dark:hover { background: rgba(255, 255, 255, 0.08); }

.note { font-size: var(--fs-small); color: var(--grey); }
.section--white .note { color: var(--text); }

/* ------------------------------------------------------------------ header
     Reference: position fixed, 72px tall, transparent outer bar with a
     floating pill inside it. */
.site-header {
  /* Above the pinned sections, which go position:fixed as the page scrolls
     through them and would otherwise paint over the bar. Below the popup. */
  position: fixed; inset: 0 0 auto 0; z-index: 1500;
  height: var(--header-h);
  display: flex; align-items: center;
  padding-inline: 10px;
  pointer-events: none;
}
/* The pill is glass in both states, never a solid bar. The fill stays well
   under half opacity and the blur and saturation do the work, so whatever is
   behind it keeps showing through: the hairline and the shadow are what give
   it an edge on a light band, not a heavier fill. */
.site-header__pill {
  pointer-events: auto;
  width: 100%; max-width: var(--content);
  margin-inline: auto;
  height: 52px;
  display: flex; align-items: center; gap: 24px;
  padding: 0 8px 0 16px;
  border-radius: var(--radius-btn);
  background: rgba(23, 24, 28, 0.44);
  border: 1px solid rgba(255, 255, 255, 0.14);
  box-shadow: 0 10px 30px -14px rgba(0, 0, 0, 0.5);
  backdrop-filter: blur(20px) saturate(165%);
  -webkit-backdrop-filter: blur(20px) saturate(165%);
  color: var(--on-dark);
  transition:
    background-color var(--dur) var(--ease),
    border-color var(--dur) var(--ease),
    box-shadow var(--dur) var(--ease),
    color var(--dur) var(--ease);
}
/* Over a light band the glass inverts: still translucent, still blurred. */
.site-header.is-light .site-header__pill {
  background: rgba(255, 255, 255, 0.52);
  border-color: rgba(23, 24, 28, 0.07);
  box-shadow: 0 10px 30px -16px rgba(23, 24, 28, 0.28);
  color: var(--ink);
}

.brand { display: inline-flex; align-items: center; gap: 9px; font-weight: var(--fw-semibold); flex: none; }
/* The mark is vector now and has no background of its own, so no circle
   crop and no plate behind it: it sits straight on the glass. */
.brand__mark { width: 40px; height: 40px; }
.brand__name { font-size: 1.0625rem; letter-spacing: -0.01em; }

.nav { margin-inline: auto; }
.nav__list { display: flex; align-items: center; gap: 26px; }
.nav__link {
  font-size: var(--fs-body); font-weight: var(--fw-regular);
  color: inherit; opacity: 0.85;
  transition: opacity var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease);
}
.nav__link:hover { opacity: 1; color: var(--link); }

/* Contract address pill and the X link, both in the header pill.
   Same geometry as the language control they replace, so the bar is unchanged. */
.ca {
  display: inline-flex; align-items: center; gap: 7px; flex: none;
  height: 36px; padding: 0 12px;
  border: 0; border-radius: var(--radius-btn);
  background: rgba(255, 255, 255, 0.1); color: inherit;
  font-size: var(--fs-small); cursor: pointer;
  font-family: inherit;
}
.site-header.is-light .ca { background: rgba(23, 24, 28, 0.07); }
.ca__label { opacity: 0.6; font-size: 0.75rem; letter-spacing: 0.04em; }
.ca__value { font-variant-numeric: tabular-nums; }
.ca.is-empty { cursor: default; }
.ca.is-empty .ca__value { opacity: 0.55; }

.social__link--header {
  width: 36px; height: 36px; flex: none;
  background: rgba(255, 255, 255, 0.1); color: inherit;
}
.site-header.is-light .social__link--header { background: rgba(23, 24, 28, 0.07); }
.social__link--header:hover { background: rgba(255, 255, 255, 0.2); color: inherit; }
.site-header.is-light .social__link--header:hover { background: rgba(23, 24, 28, 0.14); }
.social__link--header svg { width: 15px; height: 15px; }

.header__cta {
  flex: none; min-height: 36px; padding: 0 16px;
  background: var(--brand); color: var(--brand-ink);
  border-radius: var(--radius-btn); font-size: var(--fs-small);
  font-weight: var(--fw-medium); display: inline-flex; align-items: center; cursor: pointer;
  border: 0;
}
.site-header.is-light .header__cta { background: var(--brand); color: var(--brand-ink); }

.nav-toggle {
  display: none; width: 36px; height: 36px; flex: none;
  align-items: center; justify-content: center;
  background: rgba(255, 255, 255, 0.1); border: 0; border-radius: 50%; cursor: pointer;
}
.site-header.is-light .nav-toggle { background: rgba(23, 24, 28, 0.07); }
.nav-toggle__bars, .nav-toggle__bars::before, .nav-toggle__bars::after {
  display: block; width: 15px; height: 1.5px; background: currentColor; content: '';
  transition: transform var(--dur) var(--ease), opacity var(--dur-fast) var(--ease);
}
.nav-toggle__bars { position: relative; }
.nav-toggle__bars::before { position: absolute; top: -5px; }
.nav-toggle__bars::after { position: absolute; top: 5px; }
.nav-toggle[aria-expanded='true'] .nav-toggle__bars { background: transparent; }
.nav-toggle[aria-expanded='true'] .nav-toggle__bars::before { top: 0; transform: rotate(45deg); }
.nav-toggle[aria-expanded='true'] .nav-toggle__bars::after { top: 0; transform: rotate(-45deg); }

/* --------------------------------------------------------------- 1. hero
     Full-bleed photograph, dark, with the whole block centred. */
.hero {
  position: relative; isolation: isolate;
  min-height: min(100svh, 1040px);
  padding-block: calc(var(--header-h) + 48px) 96px;
  display: flex; align-items: center; justify-content: center;
  background: var(--hero-surface);
  color: var(--ink);
  text-align: center;
  overflow: hidden;
}
.hero__inner { position: relative; z-index: 2; display: flex; flex-direction: column; align-items: center; gap: 22px; }
.hero__title { max-width: 22ch; color: var(--ink); letter-spacing: -0.035em; }
.hero__lead { max-width: 54ch; color: var(--text); }
.hero__badges {
  display: flex; flex-wrap: wrap; gap: 24px; justify-content: center;
  font-size: var(--fs-small); color: var(--text);
}

/* The hero tile field, as orbits.

   .orbit        a ring, centred on the hero, turned by motion.js
   .orbit__slot  one position on that ring: rotate to the angle, step out by
                 the radius. CSS owns this and it never changes.
   .orbit__counter  turned the opposite way by the same amount, so the tile
                 below it stays upright however far the ring has travelled.
   .tile         undoes its slot's angle and applies its own small tilt.

   The radii are set so no ring ever reaches the centre column: the copy keeps
   exactly the position and measure it would have with no tiles at all. */
.hero__field { position: absolute; inset: 0; z-index: 1; pointer-events: none; }
.orbit {
  position: absolute; left: 50%; top: 50%;
  width: 0; height: 0;
  will-change: transform;
}
/* The generated radii are for a 1920-wide stage. --field scales every radius
   together, so the scatter and the keep-out distance stay proportional at any
   width instead of letting tiles crowd the copy on a narrow screen. */
/* Every term here is a length, deliberately. clamp() comparing a unitless
   number with a vw value is invalid, which makes the whole custom property
   invalid, which makes the slot's transform invalid, which drops all 46 tiles
   on the centre point. 0.052vw is 1px at a 1920 stage, which is the width the
   radii were generated for. */
.orbit__slot {
  /* 0.0695vw is 1px at 1440, the width the keep-out radius was worked out
     for. The floor matters more than the ceiling: below it the innermost
     tiles would close in on the copy faster than the type shrinks, and on a
     phone they crossed the headline. Holding the floor pushes them off the
     sides instead, which is what the narrow breakpoints thin out. */
  --r: clamp(
    calc(var(--rpx) * 0.62px),
    calc(var(--rpx) * 0.0695vw),
    calc(var(--rpx) * 1px)
  );
}
.orbit__slot {
  position: absolute; left: 0; top: 0;
  transform: rotate(var(--a)) translate(var(--r));
}
/* The landing layer, and the reason it exists.

   The opening displacement used to be applied to the tile itself. A tile
   sits below .orbit__counter, which GSAP rotates to cancel the ring, so the
   tile's own x axis is the spoke rotated by that cancellation. During the
   landing the cancellation is large, so moving the tile along x sent it off
   sideways instead of in toward the middle: the field opened from about two
   thirds out rather than from the centre.

   This element is a direct child of the slot, so its x axis IS the spoke,
   whatever the counter below it is doing. It carries no CSS transform, so
   GSAP is the only writer. */
.orbit__launch { position: absolute; left: 0; top: 0; will-change: transform; }

/* GSAP owns this element's transform and nothing else does.

   The slot angle used to be cancelled here with the CSS 'rotate' property,
   on the theory that it composes with transform rather than being replaced
   by it. Chrome folds 'rotate' into the computed transform, GSAP reads that
   fold once when it first writes the element, and every write after that
   replaces the lot: the -a quietly vanished and tiles past 90 degrees came
   out upside down. So this element now carries no CSS transform at all, and
   the slot angle is cancelled on the tile below instead. */
.orbit__counter {
  position: absolute; left: 0; top: 0;
  will-change: transform;
}
.tile {
  position: absolute;
  left: 0; top: 0; width: var(--w);
  margin: 0;
  /* Undoes its slot's angle and adds its own tilt. GSAP animates x, scale
     and opacity here during the landing but never rotation, so it parses
     this rotation once and carries it through untouched. */
  transform: translate(-50%, -50%) rotate(calc(-1 * var(--a) + var(--tilt)));
  border-radius: 14px;
  overflow: hidden;
  background: var(--white);
  box-shadow:
    0 1px 1px rgba(23, 24, 28, 0.05),
    0 6px 14px -5px rgba(23, 24, 28, 0.13),
    0 16px 32px -18px rgba(23, 24, 28, 0.12);
}
/* Two stacked layers per tile, crossfaded by motion.js so the field keeps
   changing. The tile owns the aspect ratio now, not the image, because an
   absolutely positioned image cannot give its parent a height. */
.tile { aspect-ratio: var(--ar, 4 / 5); }
.tile__img {
  position: absolute; inset: 0;
  display: block; width: 100%; height: 100%;
  object-fit: cover;
  opacity: 0;
  transition: opacity 900ms var(--ease);
}
.tile__img.is-on { opacity: 1; }
@media (prefers-reduced-motion: reduce) {
  .tile__img { transition: none; }
}
/* A photograph is cropped to the shape its tile was given, so one source
   image appears as a portrait, a square and a landscape across the field. */

/* The depth layer: further out, softer, and out of focus. */
.tile--back {
  filter: blur(4px);
  opacity: 0.42;
  box-shadow: 0 20px 44px -18px rgba(23, 24, 28, 0.16);
}

/* ------------------------------------------------------------ media blocks */
.media { position: relative; overflow: hidden; border-radius: var(--radius-media); background: var(--ink); }
.media video, .media img { width: 100%; height: 100%; object-fit: cover; }
.media--wide { aspect-ratio: 16 / 9; }
.media--square { aspect-ratio: 1 / 1; }
.media--portrait { aspect-ratio: 3 / 4; }

/* A placeholder stands in for a reference asset that could not be downloaded.
   The marker is deliberate: it must never read as a matched asset. */
.is-placeholder { outline: 1px dashed rgba(150, 158, 166, 0.5); outline-offset: -1px; }

/* ------------------------------------------------------------- 2. about */
.about { display: grid; gap: 40px; justify-items: start; }
.about__media { width: 100%; }

/* ------------------------------------------------------------- 3. quote */
.quote {
  display: grid; gap: clamp(28px, 4vw, 64px);
  grid-template-columns: 1fr; align-items: center;
}
@media (min-width: 900px) { .quote { grid-template-columns: minmax(0, 0.85fr) minmax(0, 1fr); } }
.quote__img { border-radius: var(--radius-media); overflow: hidden; }
.quote__mark { font-size: 2rem; color: var(--grey); line-height: 1; }
.quote__text { font-size: var(--fs-lead); line-height: var(--lh-lead); color: var(--grey); font-style: italic; }
.quote__text b { color: var(--on-dark); font-style: normal; }
.quote__attr { color: var(--grey); font-size: var(--fs-small); }

/* -------------------------------------------------- 4. dialogue cards */
.dialogue { display: grid; gap: 20px; grid-template-columns: 1fr; }
@media (min-width: 900px) { .dialogue { grid-template-columns: minmax(0, 1fr) minmax(0, 1.15fr); align-items: stretch; } }
.dialogue__col { display: grid; gap: 20px; }
.card {
  border-radius: var(--radius-card); overflow: hidden;
  background: #0e0f12; border: 1px solid var(--hairline-dark);
}
.section--white .card { background: #f4f5f7; border-color: var(--hairline-light); }
.card img { width: 100%; }

/* ----------------------------------------------------- 5. six dimensions */
.dimensions__media { max-width: 820px; margin-inline: auto; }
/* The brand mark replaces the reference's abstract clip. It glides along a
   dotted flight path: slow, small, and frozen under reduced motion by the
   global rule at the end of this file. */
.logostage {
  position: relative; aspect-ratio: 16 / 9;
  display: grid; place-items: center;
  color: rgba(23, 24, 28, 0.16);
}
.logostage__mark {
  position: relative;
  width: clamp(200px, 32vw, 340px);
  aspect-ratio: 1;
}
/* Both planes sit on the same full-canvas frame, so at rest they stack back
   into the mark pixel for pixel. motion.js flies them in from opposite ends
   of the dotted path; with no JS they are simply already together. */
.logostage__plane {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  will-change: transform;
}

.dimensions__grid {
  display: grid; gap: 32px 24px; grid-template-columns: repeat(2, 1fr);
  margin-top: 64px;
}
@media (min-width: 760px) { .dimensions__grid { grid-template-columns: repeat(3, 1fr); } }
@media (min-width: 1100px) { .dimensions__grid { grid-template-columns: repeat(6, 1fr); } }
.dimension { display: flex; flex-direction: column; gap: 12px; }
.dimension__icon { width: 40px; height: 40px; }
.dimension__title { font-size: 1rem; font-weight: var(--fw-bold); line-height: 1.3; }
.dimension__text { font-size: var(--fs-small); color: var(--text); }
.dimension__text p + p { margin-top: 0.6em; }

/* ------------------------------------------- 7. mira-phone-tabs (pinned)
     A sticky tab strip whose active item changes as the block scrolls. */
.phone-tabs__nav {
  position: sticky; top: calc(var(--header-h) + 8px); z-index: 5;
  display: flex; gap: 6px; justify-content: center; flex-wrap: wrap;
  padding: 6px; margin-bottom: 40px;
  border-radius: var(--radius-btn);
  background: rgba(255, 255, 255, 0.82);
  backdrop-filter: blur(14px); -webkit-backdrop-filter: blur(14px);
  width: fit-content; margin-inline: auto;
}
.phone-tabs__nav-item {
  display: inline-flex; align-items: center; gap: 7px;
  padding: 9px 14px; border: 0; border-radius: var(--radius-btn);
  background: transparent; cursor: pointer;
  font-size: var(--fs-small); color: var(--text); white-space: nowrap;
  transition: background-color var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease);
}
.phone-tabs__nav-item:hover { color: var(--ink); }
.phone-tabs__nav-item.is-active { background: var(--ink); color: var(--white); }
.phone-tabs__nav-icon { width: 17px; height: 17px; flex: none; }
.phone-tabs__nav-item.is-active .phone-tabs__nav-icon { filter: brightness(0) invert(1); }
.phone-tabs__stage { display: grid; gap: 32px; justify-items: center; }
.phone-tabs__images { position: relative; width: min(320px, 76vw); aspect-ratio: 1 / 2; }
.phone-tabs__image {
  position: absolute; inset: 0; opacity: 0;
  transition: opacity var(--dur-slow) var(--ease);
}
.phone-tabs__image img { width: 100%; height: 100%; object-fit: contain; }
.phone-tabs__image.is-active { opacity: 1; }
.phone-tabs__texts { position: relative; max-width: 640px; text-align: center; min-height: 5em; }
.phone-tabs__text {
  position: absolute; inset: 0; opacity: 0;
  transition: opacity var(--dur) var(--ease);
  color: var(--text); font-size: var(--fs-lead); line-height: var(--lh-lead);
}
.phone-tabs__text.is-active { position: relative; opacity: 1; }

/* ------------------------------------------ 8 and 13. mira-scroll-tabs */
/* align-items: stretch, so the rail column is as tall as the text column
   and each track can take an equal share of it. With align-items: start the
   four fixed-height tracks bunched at the top and drifted out of line with
   the stages they belong to. */
.scroll-tabs { display: grid; gap: 28px; grid-template-columns: auto minmax(0, 1fr); align-items: stretch; }
/* The stepper, as four individual sliders.

   It was four small dots, which told you where you were and nothing else.
   Each stage now has its own track that fills while you are scrolling
   through that stage, so the indicator shows progress within a step as well
   as which step you are on. Stages already passed stay filled, so the rail
   reads as a journey rather than as a radio group. */
.scroll-tabs__nav {
  display: flex; flex-direction: column; gap: 10px;
  padding-block: 6px;
  flex: none;
}
.scroll-tabs__dot {
  position: relative;
  /* an equal share of the rail, so a track sits beside its own stage */
  flex: 1 1 0; min-height: 30px;
  width: 4px; padding: 0; border: 0; cursor: pointer;
  border-radius: 999px;
  background: var(--hairline-light);
  overflow: hidden;
  transition: background-color var(--dur) var(--ease);
}
.section--black .scroll-tabs__dot { background: var(--hairline-dark); }
.scroll-tabs__dot::after {
  content: '';
  position: absolute; inset: 0;
  border-radius: inherit;
  background: var(--brand);
  transform: scaleY(var(--fill, 0));
  transform-origin: top;
  transition: transform 140ms linear;
}
/* A stage you have already scrolled past keeps its track filled, dimmed, so
   the rail shows the whole journey and not only the current step. */
.scroll-tabs__dot.is-done::after { transform: scaleY(1); opacity: 0.45; }
.scroll-tabs__dot.is-active::after { opacity: 1; }
.scroll-tabs__dot:hover { background: rgba(90, 94, 216, 0.25); }

@media (max-width: 760px) {
  .scroll-tabs__dot { width: 100%; height: 4px; flex: 1 1 0; min-height: 0; }
  .scroll-tabs__dot::after { transform: scaleX(var(--fill, 0)); transform-origin: left; }
  .scroll-tabs__dot.is-done::after { transform: scaleX(1); }
}

.scroll-tabs__item { opacity: 0.28; transition: opacity var(--dur-slow) var(--ease); padding-block: 10px; }
.scroll-tabs__item.is-active { opacity: 1; }
.scroll-tabs__title { font-size: 1.125rem; font-weight: var(--fw-bold); margin-bottom: 6px; }
.scroll-tabs__text { color: var(--text); }
.section--black .scroll-tabs__text { color: var(--grey); }

/* ------------------------------------------------- 9. body signals (radial)
     The reference arranges four cards around a centred logo. */
.signals { position: relative; display: grid; gap: 20px; }
.signals__hub {
  display: flex; align-items: center; justify-content: center; gap: 10px;
  font-size: 1.5rem; font-weight: var(--fw-bold);
  padding-block: 20px;
}
.signals__hub img { width: 32px; height: 32px; }
.signal {
  padding: 22px; border-radius: var(--radius-card);
  background: var(--white); border: 1px solid var(--hairline-light);
  max-width: 340px;
}
.signal__icon { width: 28px; height: 28px; margin-bottom: 12px; }
.signal__text { font-size: var(--fs-small); color: var(--text); }
.signal__text b { color: var(--ink); font-weight: var(--fw-bold); }
@media (min-width: 900px) {
  .signals {
    grid-template-columns: 1fr auto 1fr;
    grid-template-areas: '.    top   .'
                         'left hub   right'
                         '.    bot   .';
    align-items: center; justify-items: center; gap: 28px;
  }
  .signals__a { grid-area: top; }
  .signals__b { grid-area: left; }
  .signals__hub { grid-area: hub; }
  .signals__c { grid-area: right; }
  .signals__d { grid-area: bot; }
}

/* -------------------------------------------- 11. mira-info-carousel */
.info-carousel__viewport { overflow: hidden; }
.info-carousel__track {
  display: flex; gap: 20px;
  transition: transform var(--dur-slow) var(--ease); will-change: transform;
}
.info-carousel__slide { flex: 0 0 100%; min-width: 0; }
@media (min-width: 760px) { .info-carousel__slide { flex-basis: calc((100% - 20px) / 2); } }
@media (min-width: 1100px) { .info-carousel__slide { flex-basis: calc((100% - 40px) / 3); } }
.info-carousel__inner {
  height: 100%; padding: 28px;
  border-radius: var(--radius-card);
  background: #0e0f12; border: 1px solid var(--hairline-dark);
}
.info-carousel__title { font-size: 1.25rem; font-weight: var(--fw-bold); margin-bottom: 10px; }
.info-carousel__text { font-size: var(--fs-list); line-height: var(--lh-list); color: var(--grey); }
.info-carousel__text p { margin-bottom: 12px; }
.info-carousel__text ul { display: grid; gap: 10px; }
.info-carousel__text li { position: relative; padding-left: 16px; }
.info-carousel__text li::before {
  content: ''; position: absolute; left: 0; top: 8px;
  width: 5px; height: 5px; border-radius: 50%; background: var(--accent);
}
.info-carousel__nav_row { display: flex; justify-content: flex-end; margin-top: 24px; }
.info-carousel__nav { display: flex; gap: 10px; }
.carousel-btn {
  width: 44px; height: 44px; display: inline-flex; align-items: center; justify-content: center;
  border-radius: 50%; border: 0; cursor: pointer;
  background: var(--white); color: var(--ink);
  transition: opacity var(--dur-fast) var(--ease);
}
.carousel-btn:disabled { opacity: 0.3; cursor: default; }
.carousel-btn svg { width: 22px; height: 22px; }
.carousel-btn--next svg { transform: rotate(180deg); }

/* ------------------------------------------------- 12. mira-reviews */
.reviews { display: grid; gap: 20px; grid-template-columns: 1fr; }
@media (min-width: 760px) { .reviews { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 1060px) { .reviews { grid-template-columns: repeat(3, 1fr); } }
.review {
  padding: 24px; border-radius: var(--radius-card);
  background: #0e0f12; border: 1px solid var(--hairline-dark);
  display: flex; flex-direction: column; gap: 14px;
}
.review__header { display: flex; align-items: center; gap: 12px; }
.review__avatar { width: 40px; height: 40px; border-radius: 50%; overflow: hidden; flex: none; }
.review__name { font-weight: var(--fw-semibold); font-size: 0.9375rem; }
.review__meta { display: flex; flex-direction: column; gap: 2px; }
.review__stars { display: inline-flex; gap: 2px; }
.review__star { width: 13px; height: 13px; }
.review__duration { font-size: 0.8125rem; color: var(--grey); }
.review__text { color: var(--grey); font-size: var(--fs-small); }

/* ---------------------------------------------------- 14. stats row */
.stats { display: grid; gap: 20px; grid-template-columns: repeat(2, 1fr); }
@media (min-width: 900px) { .stats { grid-template-columns: repeat(4, 1fr); } }
.stat { display: flex; flex-direction: column; gap: 8px; }
.stat__value {
  font-size: clamp(2rem, 1.4rem + 2vw, 3rem);
  font-weight: var(--fw-bold); line-height: 1;
  font-variant-numeric: tabular-nums;
}
.stat__label { color: var(--grey); font-size: var(--fs-small); }

/* --------------------------------------- 15. mira-phone-scroll (pinned) */
.phone-scroll__track { display: grid; gap: 40px; grid-template-columns: 1fr; }
@media (min-width: 820px) { .phone-scroll__track { grid-template-columns: repeat(3, 1fr); } }
.phone-scroll__slide {
  display: flex; flex-direction: column; align-items: center; gap: 20px;
  text-align: center; opacity: 0.4;
  transition: opacity var(--dur-slow) var(--ease);
}
.phone-scroll__slide.is-active { opacity: 1; }
.phone-scroll__phone { width: min(260px, 68vw); }
.phone-scroll__icon { width: 28px; height: 28px; }
.phone-scroll__text { color: var(--grey); font-size: var(--fs-small); max-width: 26ch; }

/* -------------------------------------------------------- 17. blog grid */
.posts { display: grid; gap: 24px; grid-template-columns: 1fr; }
/* 3 / 2 / 1 columns, from the reference loop-grid settings */
@media (min-width: 760px) { .posts { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 1060px) { .posts { grid-template-columns: repeat(3, 1fr); } }
.post-card { display: flex; flex-direction: column; gap: 14px; }
.post-card__media { border-radius: var(--radius-card); overflow: hidden; aspect-ratio: 16 / 9; }
.post-card__media img { width: 100%; height: 100%; object-fit: cover; transition: transform var(--dur-slow) var(--ease); }
.post-card:hover .post-card__media img { transform: scale(1.04); }
.post-card__title { font-size: 1.125rem; font-weight: var(--fw-bold); line-height: 1.35; }
.post-card__excerpt { color: var(--grey); font-size: var(--fs-small); }

/* ------------------------------------------------------------------ footer */
.site-footer { background: var(--black); color: var(--on-dark); padding-block: 64px; }
.site-footer__top {
  display: grid; gap: 40px; grid-template-columns: 1fr;
  padding-bottom: 32px; border-bottom: 1px solid var(--hairline-dark);
}
@media (min-width: 820px) { .site-footer__top { grid-template-columns: minmax(0, 1fr) auto auto; align-items: start; } }
.footer-menu__list { display: grid; gap: 12px; }
.footer-menu__link {
  color: var(--grey); font-size: var(--fs-small);
  transition: color var(--dur-fast) var(--ease);
  background: none; border: 0; padding: 0; text-align: left; cursor: pointer;
}
.footer-menu__link:hover { color: var(--white); }
.social { display: flex; gap: 10px; }
.social__link {
  width: 38px; height: 38px; display: inline-flex; align-items: center; justify-content: center;
  border-radius: 50%; background: rgba(255, 255, 255, 0.08); color: var(--grey);
  transition: background-color var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease);
}
.social__link:hover { background: rgba(255, 255, 255, 0.16); color: var(--white); }
.social__link svg { width: 17px; height: 17px; }
.site-footer__bottom {
  padding-top: 28px; display: flex; flex-wrap: wrap; gap: 16px;
  justify-content: space-between; align-items: center;
}
.site-footer__disclaimer { color: var(--grey); font-size: 0.8125rem; max-width: 62ch; }
.site-footer__copy { color: var(--grey); font-size: 0.8125rem; }

/* ------------------------------------------------------------------- popup */
.popup { position: fixed; inset: 0; z-index: 2000; display: none; }
.popup.is-open { display: block; }
.popup__overlay { position: absolute; inset: 0; background: rgba(0, 0, 0, 0.6); backdrop-filter: blur(4px); }
.popup__dialog {
  position: relative; z-index: 1;
  width: min(520px, calc(100vw - 32px));
  max-height: calc(100vh - 48px); overflow-y: auto;
  margin: 24px auto; padding: clamp(24px, 3vw, 40px);
  background: var(--white); color: var(--ink);
  border-radius: 24px; box-shadow: 0 24px 70px rgba(0, 0, 0, 0.35);
  animation: popup-in var(--dur) var(--ease);
}
@keyframes popup-in { from { opacity: 0; transform: translateY(12px) scale(0.98); } }
.popup__close {
  position: absolute; top: 14px; right: 14px;
  width: 36px; height: 36px; display: inline-flex; align-items: center; justify-content: center;
  border-radius: 50%; border: 0; background: var(--faint); cursor: pointer;
}
.popup__close:hover { background: rgba(23, 24, 28, 0.1); }
.popup__title { margin-bottom: 20px; font-size: 1.375rem; font-weight: var(--fw-bold); }

.field { display: grid; gap: 7px; margin-bottom: 16px; }
.field__label { font-size: var(--fs-small); font-weight: var(--fw-medium); }
.field__control {
  width: 100%; min-height: 48px; padding: 0 14px;
  border: 1px solid var(--hairline-light); border-radius: 12px;
  background: var(--white); color: var(--ink); font: inherit;
  transition: border-color var(--dur-fast) var(--ease), box-shadow var(--dur-fast) var(--ease);
}
.field__control:focus { border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-soft); outline: none; }
select.field__control { appearance: none; cursor: pointer;
  background-image: linear-gradient(45deg, transparent 50%, var(--grey) 50%),
                    linear-gradient(135deg, var(--grey) 50%, transparent 50%);
  background-position: calc(100% - 20px) 50%, calc(100% - 15px) 50%;
  background-size: 5px 5px, 5px 5px; background-repeat: no-repeat;
}
.form__consent { margin: 18px 0; font-size: 0.8125rem; color: var(--text); }
.form__consent a { color: var(--link); text-decoration: underline; }
.form__submit { width: 100%; background: var(--ink); color: var(--white); }
.form__status { margin-top: 14px; font-size: var(--fs-small); display: none; }
.form__status.is-visible { display: block; }

/* --------------------------------------------------------- scroll reveal */
.reveal { opacity: 0; transform: translateY(24px); }
.reveal.is-in {
  opacity: 1; transform: none;
  transition: opacity var(--dur-slow) var(--ease), transform var(--dur-slow) var(--ease);
}
@media (prefers-reduced-motion: reduce) {
  .reveal { opacity: 1; transform: none; transition: none; }
  *, *::before, *::after { animation-duration: 0.001ms !important; transition-duration: 0.001ms !important; }
}

/* ------------------------------------------------------------------ motion
   The word mask. motion.js wraps each word of an editorial heading in .mw and
   raises .mw__i into place. The mask is inline-block so the heading keeps its
   own line wrapping, and the vertical padding stops descenders being clipped
   by the overflow. Without JS these spans never exist and nothing changes. */
.mw {
  display: inline-block;
  overflow: hidden;
  vertical-align: top;
  padding-bottom: 0.14em;
  margin-bottom: -0.14em;
}
.mw__i { display: inline-block; will-change: transform; }

/* Directional underline on the nav: grows from the side the pointer entered. */
.nav__link--ul { position: relative; }
.nav__link--ul::after {
  content: ''; position: absolute; left: 0; right: 0; bottom: -4px; height: 1px;
  background: currentColor;
  transform: scaleX(0); transform-origin: right;
  transition: transform var(--dur) var(--ease);
}
.nav__link--ul:hover::after { transform: scaleX(1); transform-origin: left; }

/* Transform-only hover and press, so nothing in the layout moves. */
.btn, .header__cta, .carousel-btn, .post-card, .review, .signal, .card, .tile {
  will-change: transform;
}

/* ------------------------------------------------------------- responsive */
/* The field thins as the viewport narrows: the back layer goes first, then
   the outer rings, so a phone shows a handful of tiles rather than fifty. */
@media (max-width: 1100px) {
  .tile--back { display: none; }
}
@media (max-width: 860px) {
  .orbit--3 { display: none; }
}
@media (max-width: 620px) {
  .orbit--2 { display: none; }
  .orbit--1 .orbit__slot:nth-child(2n) { display: none; }
}

/* ------------------------------------------------------------------ motion
   The word mask. motion.js wraps each word of an editorial heading in .mw and
   raises .mw__i into place. The mask is inline-block so the heading keeps its
   own line wrapping, and the vertical padding stops descenders being clipped
   by the overflow. Without JS these spans never exist and nothing changes. */
.mw {
  display: inline-block;
  overflow: hidden;
  vertical-align: top;
  padding-bottom: 0.14em;
  margin-bottom: -0.14em;
}
.mw__i { display: inline-block; will-change: transform; }

/* Directional underline on the nav: grows from the side the pointer entered. */
.nav__link--ul { position: relative; }
.nav__link--ul::after {
  content: ''; position: absolute; left: 0; right: 0; bottom: -4px; height: 1px;
  background: currentColor;
  transform: scaleX(0); transform-origin: right;
  transition: transform var(--dur) var(--ease);
}
.nav__link--ul:hover::after { transform: scaleX(1); transform-origin: left; }

/* Transform-only hover and press, so nothing in the layout moves. */
.btn, .header__cta, .carousel-btn, .post-card, .review, .signal, .card, .tile {
  will-change: transform;
}

/* ------------------------------------------------------------- responsive */
@media (max-width: 1024px) {
  .nav { display: none; }
  .header__cta { display: none; }
  .nav-toggle { display: inline-flex; }
  .site-header__pill { gap: 12px; }
  .social__link--header { margin-left: auto; }
  .nav.is-open {
    display: block; position: absolute; left: 10px; right: 10px; top: calc(100% + 8px);
    margin: 0; padding: 14px 18px 18px;
    border-radius: 22px; background: var(--white); color: var(--ink);
    box-shadow: 0 16px 40px rgba(0, 0, 0, 0.22);
  }
  .nav.is-open .nav__list { flex-direction: column; align-items: stretch; gap: 2px; }
  .nav.is-open .nav__link { padding: 11px 0; font-size: 1rem; opacity: 1; }
}
@media (max-width: 760px) {
  :root { --section-y: 64px; --section-y-lg: 88px; --hero-y: 72px; }
  .hero { min-height: 640px; }
  .btn { width: 100%; }
  .btn-row { flex-direction: column; align-items: stretch; width: 100%; }
  .stats { grid-template-columns: 1fr; }
  .scroll-tabs { grid-template-columns: 1fr; }
  .scroll-tabs__nav { flex-direction: row; }
  .site-footer__bottom { flex-direction: column; align-items: flex-start; }
  .phone-tabs__nav { overflow-x: auto; flex-wrap: nowrap; justify-content: flex-start; max-width: 100%; }
}

/* Anchor targets clear the fixed header. */
[id] { scroll-margin-top: calc(var(--header-h) + 20px); }
html { scroll-behavior: smooth; }
@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } }

/* Pin wrappers are created at runtime by app.js. The wrapper reserves the
   section's height plus its travel; the section is fixed while scrolling
   through the surplus, matching the reference's .mira-pin-wrapper. */
.mira-pin-wrapper { position: relative; }
.mira-pin-wrapper > .mira-pin-section { left: 0; z-index: 1; }

/* Evidence panel: one wide artwork above the stepper. The reference serves a
   desktop plate and a taller mobile plate and shows only one at a time. */
.evidence__panel { border-radius: var(--radius-media); overflow: hidden; }
.evidence__panel img { width: 100%; height: auto; }
.evidence__panel-mobile { display: none; }
@media (max-width: 760px) {
  .evidence__panel-desktop { display: none; }
  .evidence__panel-mobile { display: block; }
}
/* The dots used to sit in a rounded pill, which this rule drew. The pill
   also pinned the rail to the top with align-self: start, and that is what
   kept the four sliders bunched above their stages however tall the text
   column grew. With a track per stage the pill is redundant: the tracks are
   the rail. */
.scroll-tabs__nav { align-self: stretch; }
@media (max-width: 760px) {
  .scroll-tabs__nav { align-self: auto; }
}
