/* Shared layout primitives. Scaffolding only — not final visual design. */

.sb-container {
  width: 100%;
  max-width: var(--sb-container-max-width);
  margin-inline: auto;
  padding-inline: var(--sb-space-md);
}

.sb-site {
  display: flex;
  flex-direction: column;
  min-height: 100vh;
}

.sb-main {
  flex: 1;
  padding-block: var(--sb-space-lg);
}

.sb-header__inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding-block: var(--sb-space-md);
}

.sb-footer__inner {
  display: flex;
  flex-direction: column;
  gap: var(--sb-space-xs);
  padding-block: var(--sb-space-md);
}

/* ==========================================================================
   Main hero prototype — scoped to the Main site only.
   Everything below is namespaced under `.main-hero*` or scoped behind the
   `.sb-site--main` body class so Worlds/FirstMove (which reuse the same
   shared/includes/header.php, shared/components/logo.php and
   navigation.php) are completely unaffected. The shared placeholder
   header is hidden on Main only; main-hero__header replaces it with its
   own markup rendered from public/main/index.php.
   ========================================================================== */

.sb-site--main {
  /* The .main-hero full-bleed breakout below uses a 100vw/negative-margin
     trick, which can overhang by the scrollbar width on desktop browsers
     with classic (non-overlay) scrollbars. Contain that here rather than
     touching shared reset.css. */
  overflow-x: hidden;
}

.sb-site--main .sb-header {
  display: none;
}

.sb-site--main .sb-footer {
  display: none;
}

.sb-site--main .sb-main {
  padding-block: 0;
}

/* --- Custom glass header (Main only) ---
   A floating panel that shares the hero content's grid, not an
   independent capsule floating in the middle: the outer element carries
   a small constant inset (`--sb-space-md` on the sides, so the panel
   never touches the true viewport edge) plus the top floating gap, and
   `.main-hero__header-inner` is sized so that — at every viewport width,
   capped or uncapped — its own left/right padding lands the logo/nav
   exactly on `.main-hero__content`'s padding edge (where the headline
   starts). The max-width and padding-inline below are deliberately
   `calc()`'d off the same tokens `.main-hero__content` uses
   (`--sb-container-wide` / `--sb-space-lg`), minus the outer inset
   (`--sb-space-md` on each side), so the two stay in lockstep: if either
   token changes, this alignment keeps holding without retuning. See
   components.css for the visual (glass/blur/shadow) treatment. */

.main-hero__header {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 50;
  padding: 1.25rem var(--sb-space-md) 0;
}

/* Top scrim: the header's own footprint only covers the glass panel
   itself (inset from the true edges), so without this, scrolling text
   is still visible through the gaps above/around the floating panel —
   the "tab/overlap" artifact.

   `position: fixed` (not `absolute`) is deliberate: an absolutely
   positioned `::before` resolves `top: 0` against the nearest positioned
   ancestor's padding-box edge, which *should* coincide with the
   viewport top here (the header has no border), but that makes the
   scrim's anchoring dependent on the header's box model staying exactly
   as-is — fragile, and a real gap was still visible in practice.
   `position: fixed; top: 0;` anchors directly and unambiguously to the
   viewport itself, with no dependency on the parent's padding/border at
   all, guaranteeing full coverage from y=0 regardless of header box
   changes later.

   Stacking still nests correctly even though this is now `fixed`: a
   pseudo-element's containing block for *position* can be the viewport,
   but its stacking context is still determined by DOM ancestry — since
   `.main-hero__header` (the parent) already creates a stacking context
   via `z-index: 50`, this `z-index: -1` descendant still paints *below*
   its static sibling `.main-hero__header-inner` (which always paints
   above negative-z-index descendants in the same context) while the
   whole `z-index: 50` layer paints above ordinary page content.

   Taller than the header's own ~81px footprint so the gradient has room
   to fade out below it, not cut off sharply. `pointer-events: none` so
   it never intercepts clicks. See components.css for the gradient
   itself. */

.main-hero__header::before {
  content: "";
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  height: 7.5rem;
  z-index: -1;
  pointer-events: none;
}

.main-hero__header-inner {
  width: 100%;
  max-width: calc(var(--sb-container-wide) - 2 * var(--sb-space-md));
  margin-inline: auto;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sb-space-md);
  padding: 0.65rem calc(var(--sb-space-lg) - var(--sb-space-md));
}

.main-hero__nav {
  display: flex;
  align-items: center;
  gap: var(--sb-space-lg);
}

/* --- Hero: full-bleed, breaks out of the shared .sb-container --- */

.main-hero {
  position: relative;
  width: 100vw;
  margin-left: calc(50% - 50vw);
  margin-right: calc(50% - 50vw);
  min-height: 100vh;
  min-height: 100svh;
  display: flex;
  align-items: center;
  overflow: hidden;
  background-color: var(--sb-color-bg);
}

.main-hero__media {
  position: absolute;
  inset: 0;
  z-index: 0;
  background-size: cover;
  background-position: center;
}

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

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

.main-hero__overlay,
.main-hero__glow {
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
}

.main-hero__content {
  position: relative;
  z-index: 2;
  width: 100%;
  max-width: var(--sb-container-wide);
  margin-inline: auto;
  padding-inline: var(--sb-space-lg);
  padding-block: 6rem var(--sb-space-xl);
}

.main-hero__content-inner {
  display: flex;
  flex-direction: column;
  gap: var(--sb-space-sm);
  max-width: 36rem;
}

/* Graduated rhythm: each boundary gets a deliberate amount of extra space
   on top of the content-inner's base gap, instead of one uniform gap for
   every element — eyebrow stays close to the headline, while the actions
   and supporting line get clearer separation. */

.main-hero__subheadline {
  margin-block-start: 0.375rem;
}

.main-hero__actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sb-space-md);
  margin-block-start: 1.25rem;
}

.main-hero__supporting {
  margin-block-start: 0.5rem;
}

.main-hero__scrollhint {
  position: absolute;
  left: 50%;
  bottom: var(--sb-space-lg);
  transform: translateX(-50%);
  z-index: 2;
}

/* --- First continuation section: "Conversation becomes a place."
   Editorial two-column split (intro text / three short points), not a
   features grid — kept compact rather than a long section. Background
   is the same `--sb-color-bg` the hero overlay already fades into, so
   there's no hard seam between hero and section.

   Top padding: the fixed header renders at ~89px tall on desktop (~70px
   on mobile) and never leaves the viewport — `6rem` (96px) clears it
   with margin at both breakpoints, matching the value
   `.main-hero__content` already uses for the same purpose. (No
   `scroll-margin-top` here anymore — `#request-access` now lives on
   `.main-cta`, the actual final CTA section, see below.) --- */

.main-continuation {
  background-color: var(--sb-color-bg);
  padding-block: 6rem calc(var(--sb-space-xl) * 1.5);
}

.main-continuation__inner {
  width: 100%;
  max-width: var(--sb-container-wide);
  margin-inline: auto;
  padding-inline: var(--sb-space-lg);
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.1fr);
  gap: var(--sb-space-xl);
  align-items: start;
}

.main-continuation__intro {
  display: flex;
  flex-direction: column;
  gap: var(--sb-space-sm);
  max-width: 28rem;
}

.main-continuation__points {
  display: flex;
  flex-direction: column;
}

.main-continuation__point {
  padding-block: var(--sb-space-md);
  border-top: 1px solid var(--sb-color-border);
}

@media (max-width: 768px) {
  .main-continuation__inner {
    grid-template-columns: 1fr;
    gap: var(--sb-space-lg);
  }
}

/* --- Second continuation section: "One platform. Many kinds of worlds."
   Centered intro above a three-column editorial row, divided by hairline
   rules instead of cards — deliberately a different arrangement from
   `.main-continuation` above (which splits intro/list left-right) so the
   page doesn't repeat the same block twice, while staying in the same
   restrained, border-only visual language. Kept compact: one intro
   block plus three short items, no grid of boxes. --- */

.main-platform {
  background-color: var(--sb-color-bg);
  /* Top value matches `.main-continuation`'s fixed-header safe-area fix
     above — same reasoning, this section can also become the top-of-
     viewport content during normal scroll. */
  padding-block: 6rem var(--sb-space-xl);
}

.main-platform__inner {
  width: 100%;
  max-width: var(--sb-container-wide);
  margin-inline: auto;
  padding-inline: var(--sb-space-lg);
}

.main-platform__intro {
  display: flex;
  flex-direction: column;
  gap: var(--sb-space-sm);
  max-width: 38rem;
  margin-inline: auto;
  margin-bottom: var(--sb-space-xl);
  text-align: center;
}

.main-platform__cases {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--sb-space-lg);
}

.main-platform__case {
  padding-inline-start: var(--sb-space-lg);
  border-left: 1px solid var(--sb-color-border);
}

.main-platform__case:first-child {
  padding-inline-start: 0;
  border-left: none;
}

@media (max-width: 768px) {
  .main-platform__cases {
    grid-template-columns: 1fr;
    gap: var(--sb-space-md);
  }

  .main-platform__case {
    padding-inline-start: 0;
    padding-block-start: var(--sb-space-md);
    border-left: none;
    border-top: 1px solid var(--sb-color-border);
  }

  .main-platform__case:first-child {
    padding-block-start: 0;
    border-top: none;
  }
}

/* --- Final CTA: "Build the first worlds with us."
   A single compact, centered panel — not a full-width signup banner —
   so it reads as one last deliberate moment, not another content
   section. `#request-access` (the hero/header CTA anchor target) now
   lives here; same fixed-header safe-area reasoning as the two sections
   above applies, hence the matching `6rem` top padding and
   `scroll-margin-top`. --- */

.main-cta {
  background-color: var(--sb-color-bg);
  padding-block: 6rem var(--sb-space-xl);
  scroll-margin-top: 6rem;
}

.main-cta__inner {
  width: 100%;
  /* Widened from the original 36rem to comfortably fit the two-column
     field rows in the early-access form without feeling cramped — still
     a compact, centered panel, not a full-width banner. */
  max-width: 40rem;
  margin-inline: auto;
  padding: var(--sb-space-xl) var(--sb-space-lg);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--sb-space-md);
  text-align: center;
}

/* --- Legal pages (Main only): Privacy, Cookies, AI Notice, Terms, Do Not
   Sell or Share (public/main/{privacy,cookies,ai-notice,terms,do-not-
   sell-or-share}.php). Same fixed-header safe-area top padding as the
   other sections; narrower than the page's other sections since this is
   long-form reading content, not a wide layout. --- */

.main-legal {
  background-color: var(--sb-color-bg);
  padding-block: 6rem var(--sb-space-xl);
}

.main-legal__shell {
  width: 100%;
  max-width: 42rem;
  margin-inline: auto;
}

@media (max-width: 640px) {
  .main-legal__shell {
    max-width: 100%;
  }
}

/* --- Real footer (Main only) — replaces the shared scaffolding footer,
   which is hidden for this site via `.sb-site--main .sb-footer` above.
   Same top safe-area padding as the other sections for consistency,
   even though the footer being the last element on the page makes a
   persistent overlap unlikely — avoids even a brief flicker during the
   scroll motion that reaches the bottom of the page. --- */

.main-footer {
  background-color: var(--sb-color-bg);
  border-top: 1px solid var(--sb-color-border);
}

.main-footer__inner {
  width: 100%;
  max-width: var(--sb-container-wide);
  margin-inline: auto;
  padding-inline: var(--sb-space-lg);
  /* Bottom value trimmed from --sb-space-xl now that the legal links row
     below adds its own padding-block — keeps the total gap between the
     copyright line and the legal row proportionate instead of doubling
     up on bottom spacing. */
  padding-block: 6rem var(--sb-space-lg);
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--sb-space-md);
}

.main-footer__brand {
  display: flex;
  flex-direction: column;
  gap: var(--sb-space-xs);
}

.main-footer__nav {
  display: flex;
  gap: var(--sb-space-lg);
}

@media (max-width: 640px) {
  .main-footer__inner {
    flex-direction: column;
    align-items: center;
    text-align: center;
  }
}

/* --- Quiet legal links row (Main only) — visually subordinate to the
   main footer row above it: smaller text, muted color (see
   components.css), separated by a thin top border instead of competing
   for attention with the brand/nav/copyright row. --- */

.main-footer__legal-row {
  border-top: 1px solid var(--sb-color-border);
}

.main-footer__legal {
  width: 100%;
  padding-inline: var(--sb-space-lg);
  padding-block: var(--sb-space-md);
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--sb-space-sm) var(--sb-space-lg);
}

/* --- Responsive --- */

@media (max-width: 640px) {
  .main-hero__nav-link {
    display: none;
  }

  .main-hero__header {
    padding: 0.75rem 0.75rem 0;
  }

  .main-hero__header-inner {
    /* Same alignment logic as the base rule above: outer 0.75rem +
       inner 1.25rem = content's 2rem padding, so the logo still lines
       up with the headline at this breakpoint too. */
    padding: 0.55rem 1.25rem;
  }

  .main-hero__content {
    padding-block: 5rem var(--sb-space-lg);
  }

  .main-hero__actions {
    flex-direction: column;
    align-items: stretch;
  }
}

/* ==========================================================================
   Worlds hero v1 — scoped to the Worlds site only, mirrors the Main hero's
   structural pattern (fixed glass header, full-bleed video hero, content
   sections, custom footer) but entirely namespaced under `.worlds-*` so
   nothing here can collide with or affect `.main-*` selectors. See
   components.css for the colour/gradient treatment that differentiates
   Worlds (cyan-led, grid-textured) from Main despite sharing the same
   hero-background video assets on purpose.
   ========================================================================== */

.sb-site--worlds {
  /* Same scrollbar-overhang containment as `.sb-site--main` above, needed
     for the same reason: `.worlds-hero`'s 100vw/negative-margin breakout. */
  overflow-x: hidden;
}

.sb-site--worlds .sb-header {
  display: none;
}

.sb-site--worlds .sb-footer {
  display: none;
}

.sb-site--worlds .sb-main {
  padding-block: 0;
}

/* --- Custom glass header (Worlds only). Unlike Main's header, this has
   no scroll-linked "firms up" state — shared/scripts/main.js only
   targets `.main-hero__header` by exact class name, and extending that
   shared script is out of scope here, so this header just renders in a
   single, already-legible glass state at all scroll positions. --- */

.worlds-hero__header {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 50;
  padding: 1.25rem var(--sb-space-md) 0;
}

.worlds-hero__header::before {
  content: "";
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  height: 7.5rem;
  z-index: -1;
  pointer-events: none;
}

.worlds-hero__header-inner {
  width: 100%;
  max-width: calc(var(--sb-container-wide) - 2 * var(--sb-space-md));
  margin-inline: auto;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sb-space-md);
  padding: 0.65rem calc(var(--sb-space-lg) - var(--sb-space-md));
}

.worlds-hero__nav {
  display: flex;
  align-items: center;
  gap: var(--sb-space-lg);
}

/* --- Hero: full-bleed, breaks out of the shared .sb-container, same
   technique as `.main-hero`. --- */

.worlds-hero {
  position: relative;
  width: 100vw;
  margin-left: calc(50% - 50vw);
  margin-right: calc(50% - 50vw);
  min-height: 100vh;
  min-height: 100svh;
  display: flex;
  align-items: center;
  overflow: hidden;
  background-color: var(--sb-color-bg);
}

.worlds-hero__media {
  position: absolute;
  inset: 0;
  z-index: 0;
  background-size: cover;
  background-position: center;
}

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

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

.worlds-hero__overlay,
.worlds-hero__grid,
.worlds-hero__glow {
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
}

.worlds-hero__content {
  position: relative;
  z-index: 2;
  width: 100%;
  max-width: var(--sb-container-wide);
  margin-inline: auto;
  padding-inline: var(--sb-space-lg);
  padding-block: 6rem var(--sb-space-xl);
}

.worlds-hero__content-inner {
  display: flex;
  flex-direction: column;
  gap: var(--sb-space-sm);
  max-width: 36rem;
}

.worlds-hero__subheadline {
  margin-block-start: 0.375rem;
}

/* --- Hero early-access form: reuses `.worlds-cta__form`'s structural
   properties (flex column, gap, text-align) and `.worlds-cta__row`'s
   two-column-collapsing-to-one grid (see the `.worlds-cta__row` mobile
   override below) for the field/row/button mechanics; this rule adds
   the breathing room from the subheadline above. This is the only
   early-access form on the page. The `#request-access` anchor target is
   `.worlds-hero__form-wrap` (below), not the form/success element
   itself — that wrapper is the common ancestor of the success message,
   the form-level error message, and the form, so a nav-CTA jump or a
   failed/succeeded POST back to `#request-access` always lands with
   whichever of those is showing fully in view, not scrolled past it. */

.worlds-hero__form {
  margin-block-start: 1.25rem;
}

.worlds-hero__form-success {
  margin-block-start: 1.25rem;
}

/* `scroll-margin-top` matches the fixed header's height so the jump
   lands below it instead of being covered, on both desktop and mobile
   (mobile's header is shorter, so this conservative value still clears
   it with a little extra room to spare). */
.worlds-hero__form-wrap {
  scroll-margin-top: 6rem;
}

/* Visually-hidden label for the compact hero fields — placeholder text
   carries the visible cue ("Name" / "Email") to keep the hero form
   compact, while this keeps an accessible label in the a11y tree. */
.worlds-hero__form-label {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
}

.worlds-hero__scrollhint {
  position: absolute;
  left: 50%;
  bottom: var(--sb-space-lg);
  transform: translateX(-50%);
  z-index: 2;
}

/* --- Value proposition section, right after the hero ("From a word to
   a world."): headline + body + three step cards + a "Learn more" anchor
   jump down to `.worlds-details`. Replaces the earlier
   conversation/process/cases sections (same `.worlds-cta__inner`-style
   glass-card treatment reused for the step cards — see components.css). --- */

.worlds-value {
  background-color: var(--sb-color-bg);
  padding-block: 6rem var(--sb-space-xl);
}

.worlds-value__inner {
  width: 100%;
  max-width: var(--sb-container-wide);
  margin-inline: auto;
  padding-inline: var(--sb-space-lg);
  display: flex;
  flex-direction: column;
  gap: var(--sb-space-xl);
}

/* Editorial (left) + media stage (right) row. `minmax(min-content, 43%)`
   on the editorial column is a deliberate safety net, not just a visual
   ratio: it guarantees the column can never shrink narrower than the
   headline's own unwrapped width (see `white-space: nowrap` on
   `.worlds-value__headline-line` below), so the two-line headline can
   never collide with or get clipped by the media column, regardless of
   exactly how wide the text renders at a given viewport. In the normal
   case (headline comfortably fits under 43%), the media column — `1fr`,
   i.e. the rest of the row — ends up around ~57%, keeping it visually
   dominant per the reference. `align-items: start` (not `center`) keeps
   the media's top edge level with the eyebrow instead of vertically
   centering against the taller text column, which was leaving a dead
   gap above the media. Two columns are preserved down to the `640px`
   breakpoint (through tablet widths) — only true mobile widths stack to
   one column, so the media never shrinks to a thumbnail. */
.worlds-value__top {
  display: grid;
  grid-template-columns: minmax(min-content, 43%) minmax(0, 1fr);
  align-items: start;
  gap: var(--sb-space-lg);
}

.worlds-value__editorial {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--sb-space-md);
}

.worlds-value__headline {
  display: flex;
  flex-direction: column;
}

/* Each line is forced to stay on one row (see the min-content grid
   safety net above) so "Describe it." / "Step inside it." can never
   accidentally break mid-phrase into three or four lines — the failure
   mode this is specifically fixing. Relaxed back to normal wrapping on
   mobile below, where an intentional wrap is fine. */
.worlds-value__headline-line {
  white-space: nowrap;
}

.worlds-value__body {
  max-width: 30rem;
}

/* Arrow glyph nested inside the shared `.worlds-hero__btn--primary`
   anchor — sized off the button's own font-size (1em) rather than a
   fixed px value, and scoped to this one icon only, so the generic
   shared button classes stay untouched. */
.worlds-value__btn-icon {
  width: 1em;
  height: 1em;
  margin-left: 0.6rem;
  flex-shrink: 0;
}

/* --- Media stage: the PlanetIsland poster/video, deliberately *not*
   framed as a bordered card (no border, no background fill box, no
   glow — the previous ambient glow was removed entirely: it was making
   the rectangular boundary it sat behind *more* visible, not less, so
   this relies solely on true alpha edge-fading — see the mask on
   `.worlds-media-stage__visual` in components.css — plus the internal
   zoom below, rather than any colored light source). `.worlds-media-
   stage` itself stays a plain, unstyled layout box, *except* for the
   decorative beam extension immediately below — deliberately kept on
   this outer, unmasked element (not on `.worlds-media-stage__visual`,
   which has `overflow: hidden`) specifically so it can extend past the
   media's own top edge into the surrounding page, continuing the
   source video's light beam rather than being clipped at the same
   boundary the beam itself fades into. --- */
.worlds-media-stage {
  position: relative;
  width: 100%;
}

/* Narrow, subtle decorative continuation of the source beam (see
   components.css for its gradients) — not a glow around the whole
   media, just a small soft column above the beam's own on-screen
   position (52%, matching `object-position`/`transform-origin`/mask
   center throughout this section). Compensates for the true top alpha
   fade below (in components.css) softening the beam near the real edge
   — this picks the motif back up just above where the video itself
   fades out, rather than reopening the source rectangle to keep the
   beam at full strength. z-index: 2 — above `.worlds-media-stage__
   visual` (1) so it isn't hidden behind the media, below `__wireframe`/
   `__scan` (3/4) so the temporary reveal still draws on top of it
   during materialization, same as everything else here. Purely
   decorative and static (no animation), so it needs no reduced-motion
   handling. */
.worlds-media-stage::before {
  content: "";
  position: absolute;
  left: 52%;
  top: -6%;
  width: 7%;
  height: 24%;
  transform: translateX(-50%);
  z-index: 2;
  pointer-events: none;
}

/* The "inner crop/viewport layer": the vertical (top/bottom) half of
   the real edge mask, the fallback, and the `overflow: hidden` that
   keeps the scaled poster/video below from spilling past this box all
   live here — the mask's horizontal (side) half lives on the poster/
   video elements themselves instead (see components.css for why: it
   lets the two fades combine without needing `mask-composite`). Kept
   deliberately separate from `.worlds-media-stage`'s own children
   (`__wireframe`/`__scan`, still siblings of this element — see
   components.css) that carry the *temporary* one-time materialize
   reveal. `object-fit: cover` below already fills this box edge-to-edge
   (its `aspect-ratio`, 4/3 = 1.333, is deliberately narrower than the
   source's native 16/9 = 1.778, so `cover` is always
   *height*-constrained — the full source height, top light beam to
   bottom clouds, is always shown with zero vertical crop from this step
   alone; only the excess width gets trimmed from the sides). The
   `transform: scale()` on the poster/video is an *additional* zoom on
   top of that cover crop — see below. See the narrower ratios in the
   media queries below: less crop/zoom is applied at smaller viewports
   on purpose, rather than reusing this desktop amount everywhere. */
.worlds-media-stage__visual {
  position: relative;
  z-index: 1;
  width: 100%;
  aspect-ratio: 4 / 3;
  overflow: hidden;
}

.worlds-media-stage__poster,
.worlds-media-stage__video {
  position: absolute;
  inset: 0;
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  /* X only: nudges the cover crop a little right of frame-center to
     center it on the island/city cluster's own actual horizontal
     center rather than the source frame's geometric middle. Y is
     inert for the cover step (vertical never crops there — see the
     comment above) but still used as the `transform-origin` below. */
  object-position: 52% 50%;
  /* The requested additional internal zoom, layered on top of the
     cover crop above (not a replacement for it) — this is what makes
     the island itself, not just the box around it, visibly larger.
     Centered on the same point as `object-position`. `overflow: hidden`
     on the parent (`.worlds-media-stage__visual`) clips the resulting
     overflow instead of letting it spill past the stage. */
  transform: scale(1.18);
  transform-origin: 52% 50%;
}

.worlds-media-stage__poster {
  z-index: 1;
}

.worlds-media-stage__video {
  z-index: 2;
}

/* Universal (non-`@supports`-gated) edge-fade fallback — required
   alongside the `mask-image` progressive enhancement in components.css
   for browsers without mask support. Reaches the section's own solid
   background color at the true outer edges (not a low-opacity tint —
   see components.css), so there is no rectangular boundary left to see
   either way. Sits above the poster/video but still below the
   materialize wireframe/scan overlays (both `z-index: 3`/`4` on
   `.worlds-media-stage` itself, a separate, outer stacking context from
   this element's own `z-index: 1`, so the two numbering scales don't
   collide). Also subject to this element's own `overflow: hidden` and
   (during the one-time reveal) `clip-path`, exactly like the
   poster/video — so it never draws outside the visual's own box, and
   fades in/out in sync with the reveal rather than appearing as a
   separate hard-edged layer. */
.worlds-media-stage__visual::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 3;
  pointer-events: none;
}

.worlds-media-stage__wireframe,
.worlds-media-stage__scan {
  position: absolute;
  inset: 0;
  z-index: 3;
  pointer-events: none;
}

.worlds-media-stage__scan {
  z-index: 4;
}

/* --- Feature tray: one outer panel, three icon-left columns separated
   by short fading hairlines (no per-column card border/background — see
   components.css for the panel finish, separator gradients, and
   colors). Padding is intentionally tighter than the old stacked
   icon-over-text version — see docs reference — to read as a dense,
   cinematic strip rather than three SaaS cards with dead air. --- */
.worlds-feature-tray {
  position: relative;
  width: 100%;
  padding: 2.25rem 2.5rem 1.75rem;
  border-radius: var(--sb-radius-lg);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 1.5rem;
  overflow: hidden;
}

.worlds-feature-tray__list {
  position: relative;
  z-index: 1;
  width: 100%;
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
}

.worlds-feature-tray__item {
  position: relative;
  display: flex;
  align-items: center;
  gap: 1.375rem;
  padding-inline: 1.75rem;
}

.worlds-feature-tray__item:first-child {
  padding-inline-start: 0;
}

.worlds-feature-tray__item:last-child {
  padding-inline-end: 0;
}

/* Gradient hairline in the gutter before each item but the first —
   fades at both ends so it never touches the panel's own top/bottom
   edge (see components.css for the gradient itself). */
.worlds-feature-tray__item:not(:first-child)::before {
  content: "";
  position: absolute;
  inset-block: 0;
  left: 0;
  width: 1px;
}

.worlds-feature-tray__icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  width: 3.375rem;
  height: 3.375rem;
  border-radius: 50%;
}

.worlds-feature-tray__icon svg {
  width: 1.5rem;
  height: 1.5rem;
}

.worlds-feature-tray__text {
  min-width: 0;
}

.worlds-feature-tray__footer {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr);
  align-items: center;
  column-gap: 1.5rem;
  width: 100%;
}

.worlds-feature-tray__footer-content {
  display: inline-flex;
  align-items: center;
  gap: 0.6rem;
  white-space: nowrap;
}

/* 1.5rem = 24px mark size. */
.worlds-feature-tray__footer-mark {
  display: inline-block;
  flex-shrink: 0;
  width: 1.5rem;
  height: 1.5rem;
}

@media (max-width: 1024px) {
  .worlds-value__top {
    /* Same min-content safety net as the desktop rule, just a slightly
       larger cap — less raw width is available at this breakpoint, so
       the headline needs a bit more of the row before it fits
       comfortably (see the matching font-size step-down in
       components.css). */
    grid-template-columns: minmax(min-content, 45%) minmax(0, 1fr);
    gap: var(--sb-space-md);
  }

  /* A gentler crop than the desktop 4/3 (7/5 = 1.4, vs. 1.333), plus a
     slightly smaller additional zoom (1.13 vs. desktop's 1.18) — still
     enlarges the island noticeably (~44% combined vs. the old plain
     `contain` approach) without cropping as aggressively as desktop at
     a width where less room is available overall. */
  .worlds-media-stage__visual {
    aspect-ratio: 7 / 5;
  }

  .worlds-media-stage__poster,
  .worlds-media-stage__video {
    transform: scale(1.13);
  }
}

@media (max-width: 640px) {
  .worlds-value {
    padding-block: 3.5rem var(--sb-space-lg);
  }

  .worlds-value__inner {
    gap: var(--sb-space-xl);
  }

  .worlds-value__top {
    grid-template-columns: 1fr;
    gap: var(--sb-space-lg);
  }

  /* An intentional wrap is fine once stacked to one column and given
     the full row width — see components.css for the matching
     mobile-appropriate font-size. */
  .worlds-value__headline-line {
    white-space: normal;
  }

  /* Gentler still than the 1024px ratio (3/2 = 1.5, closer to the
     source's native 16/9 = 1.778), plus the mildest additional zoom
     (1.08) — deliberately *not* reusing the desktop crop/zoom amounts
     here. At a narrow, already-tight mobile width, over-cropping would
     start to feel too zoomed-in and risk crowding the composition; this
     keeps the media feeling nearly full-width with only a mild, safe
     trim of the outer sky (~28% larger combined than the old plain
     `contain` approach, vs. desktop's ~57%). */
  .worlds-media-stage__visual {
    aspect-ratio: 3 / 2;
  }

  .worlds-media-stage__poster,
  .worlds-media-stage__video {
    transform: scale(1.08);
  }
}

/* --- Feature tray stacked layout: its own breakpoint (860px), wider
   than the 640px the rest of this section reflows at. The icon-left
   composition needs more horizontal room per column than the old
   icon-above-text layout did — three columns of icon + text got
   cramped and started wrapping titles well above 640px, so the tray
   stacks to one column earlier than its siblings. --- */
@media (max-width: 860px) {
  .worlds-feature-tray {
    padding: 1.75rem 1.25rem 1.5rem;
  }

  .worlds-feature-tray__list {
    grid-template-columns: 1fr;
    gap: 1.5rem;
  }

  .worlds-feature-tray__item {
    padding-inline: 0;
    padding-block-start: 1.5rem;
  }

  .worlds-feature-tray__item:first-child {
    padding-block-start: 0;
  }

  /* Vertical hairline becomes a horizontal fading rule above each
     stacked item but the first (color/gradient in components.css). */
  .worlds-feature-tray__item:not(:first-child)::before {
    inset-block: auto;
    top: 0;
    left: 0;
    right: 0;
    width: auto;
    height: 1px;
  }
}

/* --- Footer-only narrow-mobile fit: at 390px and below the mark+text
   line's nowrap intrinsic width (~244px) exceeds the space actually
   left inside the tray (~239px at 390, ~209px at 360) once the tray's
   own side padding and the two flanking fading lines are accounted
   for, and gets visibly clipped by the tray's `overflow: hidden`
   (layout.css, `.worlds-feature-tray`) rather than wrapping or
   shrinking — grid/flex items default to `min-width: auto`, which for
   nowrap text resolves to its full unbroken width, so nothing here
   was ever free to shrink on its own.

   Measured empirically (headless Chrome, actual rendered boxes): a
   13px/0.4rem-gap line stays comfortably inside the tray down to
   ~375px, so 390px only needs that size/gap reduction and keeps its
   one line. Below that — where 360px lives — the same line is still
   too wide even at that size, so `white-space: normal` becomes a
   deliberate centered-wrap fallback instead of shrinking type further.
   380px was chosen as the wrap cutover (a few px of margin above the
   measured ~375px crossover) so the switch happens before any
   near-miss clipping, not after. This intentionally does not touch
   the mark's box size, the three feature items, or anything outside
   400px. */
@media (max-width: 400px) {
  .worlds-feature-tray__footer {
    column-gap: 1rem;
  }

  .worlds-feature-tray__footer-content {
    gap: 0.4rem;
    font-size: 0.8125rem;
  }
}

@media (max-width: 380px) {
  .worlds-feature-tray__footer-content {
    white-space: normal;
    text-align: center;
  }
}

/* --- Detailed "how it works" content, unfolded by `.worlds-value`'s
   "Learn more" button (`#worlds-details` — see shared/scripts/main.js
   for the small progressive-enhancement script that collapses it via
   the `hidden` attribute and toggles it on click). With no JS, the
   `hidden` attribute is never applied in the first place, so this stays
   fully visible and reachable by scrolling — see main.js for why this
   approach was chosen over baking a collapsed state into the markup
   itself. A flat sequence of heading+list/copy groups rather than
   nested subsections, matching how the content itself is structured.
   `scroll-margin-top` matches the fixed header's height, same
   convention as the hero form below. --- */

.worlds-details {
  background-color: var(--sb-color-bg);
  padding-block: var(--sb-space-xl);
  scroll-margin-top: 6rem;
}

/* The actual JS-driven scroll/focus target once "Learn more" is
   activated (see shared/scripts/main.js). The fixed header plus a clear
   reading gap needs more room than the section's native anchor offset. */
#worlds-details-intro {
  scroll-margin-top: 10rem;
}

@media (max-width: 640px) {
  #worlds-details-intro {
    scroll-margin-top: 9.75rem;
  }
}

@media (prefers-reduced-motion: reduce) {
  #worlds-details-intro {
    scroll-margin-top: 9.5rem;
  }
}

@media (max-width: 640px) and (prefers-reduced-motion: reduce) {
  #worlds-details-intro {
    scroll-margin-top: 8.25rem;
  }
}

.worlds-details__inner {
  width: 100%;
  max-width: var(--sb-container-wide);
  margin-inline: auto;
  padding-inline: var(--sb-space-lg);
  display: flex;
  flex-direction: column;
  /* ~50% more than the previous 4rem (var(--sb-space-xl)) — breathing
     room between groups, not inside any single group's own rows; this
     is the only gap that changed here, .worlds-details's own
     padding-block (the space before the footer) is untouched. */
  gap: calc(var(--sb-space-xl) * 1.5);
}

/* Restored immersive-creation visual (see source-media/visuals/worlds/
   for the source and assets/visuals/worlds/ for the deployed webp/jpg)
   — now a right-side panel paired with the "How a world takes shape"
   copy via `.worlds-details__group--visual` below, not the full-width
   standalone banner it briefly was, and not a revival of the old
   two-column layout's chat-bubble/orbit UI either (just the right-side
   image relationship). `aspect-ratio` switches per breakpoint exactly
   as before — wide cinematic 5:3 on desktop, 4:3 (the source image's
   native ratio) on mobile, so the full composition stays visible with
   no meaningful crop. The crop itself is `object-fit: cover` on the one
   underlying image, not separate cropped files. */
.worlds-details__visual {
  position: relative;
  overflow: hidden;
  border-radius: var(--sb-radius-lg);
  aspect-ratio: 5 / 3;
}

.worlds-details__visual-image {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

@media (max-width: 768px) {
  .worlds-details__visual {
    aspect-ratio: 4 / 3;
  }
}

.worlds-details__group {
  display: flex;
  flex-direction: column;
  gap: var(--sb-space-md);
}

/* First group only ("How a world takes shape") — copy/steps on the
   left, the restored visual on the right. Text column kept wider than
   the image column (1.4fr vs 1fr) so the panel reads as a supporting
   visual, not a competing hero image. */
.worlds-details__group--visual {
  display: grid;
  grid-template-columns: minmax(0, 1.4fr) minmax(0, 1fr);
  align-items: center;
  gap: var(--sb-space-xl);
}

.worlds-details__group-copy {
  display: flex;
  flex-direction: column;
  gap: var(--sb-space-md);
}

.worlds-details__group-body {
  max-width: 42rem;
}

.worlds-details__steps {
  display: flex;
  flex-direction: column;
}

.worlds-details__step {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: var(--sb-space-sm);
  padding-block: var(--sb-space-md);
  border-top: 1px solid var(--sb-color-border);
}

.worlds-details__step:first-child {
  border-top: none;
  padding-top: 0;
}

.worlds-details__list {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--sb-space-md) var(--sb-space-xl);
}

.worlds-details__item {
  display: flex;
  align-items: flex-start;
  gap: var(--sb-space-sm);
}

.worlds-details__item-mark {
  flex-shrink: 0;
  margin-top: 0.5rem;
  width: 0.4rem;
  height: 0.4rem;
  border-radius: 50%;
}

@media (max-width: 768px) {
  .worlds-details {
    padding-block: 3.5rem var(--sb-space-lg);
  }

  .worlds-details__inner {
    /* Same ~50% increase as the desktop gap above, scaled from this
       breakpoint's own previous value (var(--sb-space-lg), 2rem). */
    gap: calc(var(--sb-space-lg) * 1.5);
  }

  .worlds-details__group--visual {
    grid-template-columns: 1fr;
    gap: var(--sb-space-md);
  }

  .worlds-details__list {
    grid-template-columns: 1fr;
  }
}

/* --- Early-access form field mechanics (Worlds only). The form itself
   now lives in the hero (`.worlds-hero__form`, the `#request-access`
   anchor target — see its `scroll-margin-top` above), but these
   `.worlds-cta__*` field/row/honeypot/success classes are kept and
   reused there since the field styling intentionally mirrors Main's
   `.main-cta__*` values exactly (border, background, focus ring already
   Lucid Cyan there too), so the form doesn't need a separate
   "Worlds-ified" visual pass — see components.css. --- */

.worlds-cta__form {
  width: 100%;
  display: flex;
  flex-direction: column;
  gap: var(--sb-space-md);
  text-align: left;
}

.worlds-cta__row {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--sb-space-md);
}

.worlds-cta__field {
  display: flex;
  flex-direction: column;
  gap: 0.375rem;
}

.worlds-cta__hp {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  left: -9999px;
}

.worlds-cta__success {
  width: 100%;
  padding: var(--sb-space-md) var(--sb-space-lg);
  border: 1px solid var(--sb-color-border);
  border-radius: var(--sb-radius-sm);
  background-color: rgba(85, 221, 251, 0.08);
}

@media (max-width: 640px) {
  .worlds-cta__row {
    grid-template-columns: 1fr;
  }
}

/* --- Inline action row next to "Learn more" — a plain flex row, not a
   new section/card, so the default collapsed view gets a "Request
   access" action without adding any new visual block. Sits inside
   `.worlds-value__inner` (itself `align-items: flex-start`), so this row
   stays left-aligned with the rest of the value-prop content instead of
   stretching/centering. `flex-wrap` lets it wrap on narrow screens
   instead of overflowing. --- */
.worlds-value__actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sb-space-md);
}

/* --- Closing CTA at the end of the expanded details content (after
   "Who it's for"), before the footer. A compact divider-strip, same
   hairline pattern as `.worlds-details__step`'s own `border-top`, not a
   bordered/shadowed card — deliberately lighter than the page's glass-
   card treatment so it reads as a closing line, not a second hero. --- */
.worlds-final-cta {
  padding-block: var(--sb-space-lg) 0;
  border-top: 1px solid var(--sb-color-border);
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: var(--sb-space-sm);
}

.worlds-final-cta__subtext {
  max-width: 30rem;
}

.worlds-final-cta__btn {
  margin-block-start: var(--sb-space-xs);
}

/* --- Worlds footer — same structural pattern as Main's `.main-footer`
   (brand/nav/copyright row + a quieter legal-links row below a thin
   border), namespaced separately. Legal links point at Main's actual
   legal pages (Worlds has none of its own). --- */

.worlds-footer {
  background-color: var(--sb-color-bg);
  border-top: 1px solid var(--sb-color-border);
}

.worlds-footer__inner {
  width: 100%;
  max-width: var(--sb-container-wide);
  margin-inline: auto;
  padding-inline: var(--sb-space-lg);
  /* Trimmed top padding from 6rem to var(--sb-space-xl) (4rem) — too
     much empty space sat between the border-top and the footer text;
     still breathable, just tighter. Bottom is untouched. */
  padding-block: var(--sb-space-xl) var(--sb-space-lg);
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--sb-space-md);
}

.worlds-footer__brand {
  display: flex;
  flex-direction: column;
  gap: var(--sb-space-xs);
}

.worlds-footer__nav {
  display: flex;
  gap: var(--sb-space-lg);
}

.worlds-footer__legal-row {
  border-top: 1px solid var(--sb-color-border);
}

.worlds-footer__legal {
  width: 100%;
  padding-inline: var(--sb-space-lg);
  padding-block: var(--sb-space-md);
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--sb-space-sm) var(--sb-space-lg);
}

@media (max-width: 640px) {
  .worlds-footer__inner {
    flex-direction: column;
    align-items: center;
    text-align: center;
  }
}

/* --- Worlds responsive --- */

@media (max-width: 640px) {
  .worlds-hero__header {
    padding: 0.75rem 0.75rem 0;
  }

  .worlds-hero__header-inner {
    padding: 0.55rem 1.25rem;
  }

  .worlds-hero__content {
    padding-block: 5rem var(--sb-space-lg);
  }
}
