/* ============================================================
   guest-experience.css — Coommit guest entry/exit cinematic layer
   Scope: views/guest-join.ejs + views/guest-thanks.ejs ONLY.
   Extends style_auth.css (.auth-v2 design system). Brand tokens
   mirror style_roomV3.css [data-theme="light"] so the staged room
   reads as the real one.

   LIGHT MODE (2026-08-03). This file used to be entirely dark, and
   the exit page's spectacle was built on emission: a white core
   igniting, a screen flash, additive particles. None of that
   survives a white sheet — you cannot light up a surface that is
   already at maximum luminance.

   ── EXIT SEQUENCE, v4 (2026-08-04) ───────────────────────────
   v3 was filmed again at 75fps on a 2560x1440 screen and stepped.
   It had the right dramaturgy and three defects, all of them the
   same defect: it was built out of three independent layers that
   took turns — a CSS room, a CSS seam, a canvas burst — and each
   handover was a cut.

     · THE BALL. `.gx-ink` rose to full opacity on `ease` while the
       collapse ran on cubic-bezier(0.62, 0, 0.9, 0.36). At 40ms in,
       the pigment was at ~90% and the card had moved 4%: a large
       flat purple ball on an intact room, video tile column still
       crisp over it. Two properties describing one physical event
       cannot ride two curves.
     · THE BLANK. The room went display:none at 880ms; the burst did
       not start until 1000ms. For 120ms the only thing on a white
       sheet was a 4px hairline. That gap is what the eye reports as
       "it stops".
     · THE POP. The burst's first frame was its strongest — core,
       24 rays and 96 fragments all at full alpha, with nothing
       preceding them.

   v4 keeps one rule: THE OBJECT IS NEVER HANDED OVER, only ever
   transformed.

     1. the room is a card lying on paper, and it INHALES;
     2. it CLOSES along one axis, 320ms, hard ease-in — and its
        pigment floods on the SAME curve, so the colour is the
        collapse rather than an event on top of it;
     3. a BAR OF INK takes over. The `.gx-seam` div is gone: the
        canvas draws the bar and MEASURES the card's real box every
        frame (getBoundingClientRect carries the ancestor
        transform), so the bar has the card's exact silhouette at
        every instant. Matching by measurement, not by two
        hand-tuned curves that drift;
     4. the bar CHARGES — draws in to a third of its width,
        thickens, blooms — for 140ms. This is the frame budget v3
        spent on a blank page, and it is what makes the break read
        as a consequence rather than as a cut;
     5. it BREAKS. The burst's core is born as that bar's lens and
        rounds out over 160ms. A shockwave travels through the
        page's OWN dot grid (the canvas is the product): the dots
        near the front are pushed outward, darkened and smeared,
        then settle. Spatter rays ride the same front;
     6. the debris are pieces of the canvas — sticky chips, grid
        dots, connector shards — thrown from ALONG the seam, and
        drained by one global ramp between 0.72s and 0.98s so the
        sheet is clean before the copy is legible;
     7. the message arrives WHILE the front is still travelling, and
        the three claims are the objects the burst threw.

   ~2.6s door to door, with no frame in which nothing is happening —
   which is worth more than the 0.4s v3 saved by leaving one blank.
   Depth comes from cast shadow and saturation, never from glow.
   ============================================================ */

:root {
  /* Room surfaces — the real room's light theme, verbatim */
  --gx-room-bg: #ebebef;
  --gx-surface: #ffffff;
  --gx-surface-2: #fafafb;
  --gx-stroke: rgba(17, 17, 24, 0.08);
  --gx-stroke-strong: rgba(17, 17, 24, 0.14);
  --gx-ink: #1a1a22;
  --gx-ink-2: #4a4a5c;
  --gx-muted: #6b6b7d;

  /* Brand ramp. The split matters: #6366f1 is 4.47:1 on white and
     may only decorate; anything carrying text or acting as a fill
     behind white text starts at #4f46e5. */
  --gx-indigo: #6366f1;
  --gx-indigo-text: #4f46e5;
  --gx-indigo-deep: #4338ca;
  --gx-indigo-darkest: #312e81;
  --gx-lavender: #a5b4fc;
  --gx-violet: #7c3aed;
  --gx-cyan: #0891b2;

  /* Canvas object colours, straight from the product */
  --gx-sticky-yellow: #fef08a;
  --gx-sticky-green: #bbf7d0;
  --gx-cursor-blue: #2563eb;
  --gx-cursor-pink: #db2777;
  --gx-echo-card: #201e3c;

  /* Elevation. Brand-tinted, never neutral black — a white card with
     a grey shadow reads as a screenshot pasted onto the page. */
  --gx-shadow-sm: 0 1px 2px rgba(17, 17, 24, 0.05), 0 4px 12px rgba(49, 46, 129, 0.10);
  --gx-shadow-md: 0 2px 4px rgba(17, 17, 24, 0.05), 0 12px 28px rgba(49, 46, 129, 0.14);
  --gx-shadow-lg: 0 4px 10px rgba(17, 17, 24, 0.05), 0 24px 60px rgba(49, 46, 129, 0.16),
                  0 60px 120px rgba(49, 46, 129, 0.12);
}

/* ============================================================
   1. THE STAGE — full-viewport staged Coommit room
   ============================================================ */

.gx-stage {
  position: fixed;
  inset: 0;
  z-index: 0;
  overflow: hidden;
  background: transparent;
  transform-origin: 50% 46%;
}

/* The room itself, seen through frosted glass.
   The overscan exists only to keep the blur from revealing a hard edge,
   and a blur radius is measured in PIXELS — so the bleed is too. The
   previous `inset: -3%` plus `scale(1.03)` was a percentage answer to a
   pixel problem: at 1440 it pushed the video tile column and the toolbar
   roughly 66px past the viewport, so the staged room we were showing was
   a cropped one. 12px covers a 4.5px blur with room to spare.

   Since 2026-08-03 this partial is included by the EXIT page only (the
   entry page dropped its staged room), and the exit variant in section 3
   overrides inset, transform and filter anyway — but a rule that is wrong
   is worth fixing rather than leaving as a trap for the next include. */
.gx-room {
  position: absolute;
  inset: -12px;
  background: var(--gx-room-bg);
  filter: blur(4.5px) saturate(1.04);
}
/* (exit room styling lives in section 3: a white card lying on paper) */

/* Signature dot grid of the canvas */
.gx-room::before {
  content: "";
  position: absolute;
  inset: 0;
  background-image: radial-gradient(circle, rgba(17, 17, 24, 0.13) 1.4px, transparent 1.4px);
  background-size: 22px 22px;
}

/* Frosted veil: keeps the room readable but softened, focuses the center.
   On paper the vignette is a pale fog rolling in from the edges, not a
   black gradient — darkening the corners of a light room reads as dirt. */
.gx-veil {
  position: absolute;
  inset: 0;
  background:
    radial-gradient(ellipse 95% 75% at 50% 44%,
      rgba(245, 245, 248, 0) 0%,
      rgba(238, 238, 245, 0.42) 58%,
      rgba(214, 214, 228, 0.80) 100%),
    linear-gradient(180deg, rgba(240, 240, 246, 0.32) 0%, transparent 16%, transparent 80%, rgba(224, 224, 236, 0.44) 100%),
    radial-gradient(ellipse 60% 50% at 50% 46%, rgba(99, 102, 241, 0.05), transparent 70%);
}

/* The ink layer. Idle at zero on the entry page; on the exit page the
   collapse steps drive it up until the room IS a drop of pigment. This
   is the light-mode replacement for the old "brightness rises as it
   implodes" — brightness on white means vanishing, saturation means
   concentrating. */
.gx-ink {
  position: absolute;
  inset: 0;
  opacity: 0;
  pointer-events: none;
  transform: scale(1.7);
  transform-origin: 50% 46%;
  /* Nine stops, not four. Filmed on a 2560px screen this gradient was
     visibly banded — a 0.88 → 0 alpha ramp spread over 20% of a
     1000px-wide box gives each 8-bit step ~10px to itself, and on a
     light sheet those steps are contour lines. The extra stops cost
     nothing and break the ramp into intervals no step can survive. */
  background: radial-gradient(circle at 50% 46%,
    var(--gx-indigo-text) 0%,
    var(--gx-indigo-deep) 28%,
    rgba(49, 46, 129, 0.96) 44%,
    rgba(49, 46, 129, 0.86) 54%,
    rgba(49, 46, 129, 0.66) 61%,
    rgba(49, 46, 129, 0.42) 67%,
    rgba(49, 46, 129, 0.2) 71%,
    rgba(49, 46, 129, 0.07) 75%,
    rgba(49, 46, 129, 0) 80%);
}

/* ---------- 1a. Video tile column (left edge, like the real room) ---- */
.gx-people {
  position: absolute;
  left: 34px;
  top: 50%;
  transform: translateY(-50%);
  display: flex;
  flex-direction: column;
  gap: 18px;
  width: 196px;
}
.gx-person {
  position: relative;
  height: 132px;
  border-radius: 12px;
  border: 1px solid var(--gx-stroke);
  background: var(--gx-surface-2);
  box-shadow: var(--gx-shadow-sm);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 8px;
}
.gx-avatar {
  width: 54px;
  height: 54px;
  border-radius: 50%;
  background: var(--gx-indigo-text);
  color: #fff;
  font-weight: 700;
  font-size: 22px;
  display: flex;
  align-items: center;
  justify-content: center;
  border: 2px solid transparent;
}
.gx-person--p2 .gx-avatar { background: var(--gx-cursor-pink); }
.gx-pname {
  font-weight: 600;
  font-size: 13px;
  color: var(--gx-ink);
  letter-spacing: -0.01em;
  max-width: 90%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.gx-owner-badge {
  position: absolute;
  top: 8px;
  right: 8px;
  background: var(--gx-indigo-text);
  color: #fff;
  font-size: 8px;
  font-weight: 800;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  padding: 3px 7px;
  border-radius: 10px;
  box-shadow: 0 2px 6px rgba(67, 56, 202, 0.30);
}
/* Alternating speaking rings: the room sounds like a conversation. A ring
   on paper is a solid stroke, not a halo — the old 12px glow disappeared
   against a light tile. */
.gx-person--p1 .gx-avatar { animation: gx-speak-a 9s ease-in-out infinite; }
.gx-person--p2 .gx-avatar { animation: gx-speak-b 9s ease-in-out infinite; }
@keyframes gx-speak-a {
  0%, 18%, 58%, 100% { box-shadow: 0 0 0 0 rgba(99, 102, 241, 0); border-color: transparent; }
  24%, 50% { box-shadow: 0 0 0 3px rgba(99, 102, 241, 0.35); border-color: var(--gx-indigo-deep); }
}
@keyframes gx-speak-b {
  0%, 60%, 96%, 100% { box-shadow: 0 0 0 0 rgba(99, 102, 241, 0); border-color: transparent; }
  68%, 88% { box-shadow: 0 0 0 3px rgba(99, 102, 241, 0.35); border-color: var(--gx-indigo-deep); }
}

/* ---------- 1b. Echo tile: the orb, alive ------------------------- */
.gx-person--echo {
  background: rgba(99, 102, 241, 0.06);
  border-color: rgba(99, 102, 241, 0.20);
}
.gx-echo-chip {
  position: absolute;
  top: 8px;
  left: 8px;
  display: flex;
  align-items: center;
  gap: 5px;
  font-size: 10px;
  font-weight: 700;
  color: var(--gx-indigo-deep);
}
.gx-echo-mark {
  width: 14px;
  height: 14px;
  border-radius: 4px;
  background: rgba(99, 102, 241, 0.16);
  color: var(--gx-indigo-text);
  font-size: 9px;
  display: flex;
  align-items: center;
  justify-content: center;
}
.gx-orb {
  position: relative;
  width: 72px;
  height: 72px;
  perspective: 250px;
}
/* On black the orb glowed. On paper it casts: the halo became a soft
   pigment bloom under the core, the way a marble sits on a page. */
.gx-orb-glow {
  position: absolute;
  inset: -24px;
  border-radius: 50%;
  background: radial-gradient(circle, rgba(99, 102, 241, 0.20), rgba(8, 145, 178, 0.07) 55%, transparent 74%);
  animation: gx-orb-breathe 4s ease-in-out infinite;
}
.gx-orb-core {
  position: absolute;
  left: 50%;
  top: 50%;
  width: 30px;
  height: 30px;
  margin: -15px 0 0 -15px;
  border-radius: 50%;
  background:
    radial-gradient(circle at 32% 28%, rgba(255, 255, 255, 0.55), transparent 52%),
    radial-gradient(circle at 50% 50%, #6366f1, #4f46e5 42%, #312e81 88%);
  box-shadow: 0 3px 10px rgba(49, 46, 129, 0.42), 0 8px 22px rgba(49, 46, 129, 0.22);
  animation: gx-orb-pulse 4s ease-in-out infinite;
}
.gx-orb-ring {
  position: absolute;
  left: 50%;
  top: 50%;
  border-radius: 50%;
  border: 1px solid rgba(79, 70, 229, 0.45);
}
.gx-orb-ring--1 {
  width: 48px;
  height: 48px;
  margin: -24px 0 0 -24px;
  animation: gx-orb-spin1 6s linear infinite;
}
.gx-orb-ring--2 {
  width: 66px;
  height: 66px;
  margin: -33px 0 0 -33px;
  border-color: rgba(8, 145, 178, 0.38);
  animation: gx-orb-spin2 9s linear infinite reverse;
}
.gx-orb-sat {
  position: absolute;
  left: 50%;
  top: 50%;
  width: 3px;
  height: 3px;
  margin: -1.5px;
  border-radius: 50%;
  background: var(--gx-indigo-text);
  box-shadow: 0 1px 3px rgba(49, 46, 129, 0.45);
  animation: gx-orb-orbit 7s linear infinite;
}
.gx-orb-sat--2 { animation-duration: 5s; animation-delay: -2s; }
.gx-orb-sat--3 { animation-duration: 9s; animation-delay: -5s; }
@keyframes gx-orb-pulse {
  0%, 100% { transform: scale(1); }
  50% { transform: scale(1.1); }
}
@keyframes gx-orb-breathe {
  0%, 100% { opacity: 0.6; transform: scale(1); }
  50% { opacity: 0.95; transform: scale(1.12); }
}
@keyframes gx-orb-spin1 {
  from { transform: rotateX(62deg) rotateZ(0deg); }
  to { transform: rotateX(62deg) rotateZ(360deg); }
}
@keyframes gx-orb-spin2 {
  from { transform: rotateX(-50deg) rotateY(18deg) rotateZ(0deg); }
  to { transform: rotateX(-50deg) rotateY(18deg) rotateZ(360deg); }
}
@keyframes gx-orb-orbit {
  from { transform: rotate(0deg) translateX(31px); }
  to { transform: rotate(360deg) translateX(31px); }
}
/* Echo summon FAB under the tile column (mirrors the real room's pill) */
.gx-echo-fab {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding: 9px 16px;
  border-radius: 12px;
  border: 1px solid rgba(99, 102, 241, 0.22);
  background: rgba(99, 102, 241, 0.08);
  color: var(--gx-indigo-text);
  font-size: 13px;
  font-weight: 600;
}
.gx-echo-fab img {
  width: 26px;
  height: 26px;
  filter: drop-shadow(0 2px 4px rgba(49, 46, 129, 0.28));
  animation: gx-mascot-bob 3.8s ease-in-out infinite;
}
@keyframes gx-mascot-bob {
  0%, 100% { transform: translateY(0) rotate(0deg); }
  50% { transform: translateY(-2px) rotate(1deg); }
}
.gx-echo-status {
  position: absolute;
  bottom: 8px;
  right: 8px;
  display: flex;
  align-items: center;
  gap: 4px;
  font-size: 9px;
  color: var(--gx-indigo-deep);
  background: rgba(255, 255, 255, 0.82);
  padding: 3px 7px;
  border-radius: 8px;
}
.gx-echo-dot {
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: #16a34a;
  animation: gx-blink 2s ease-in-out infinite;
}
@keyframes gx-blink {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.35; }
}

/* ---------- 1c. Canvas boxes (sticky notes + cards) ---------------- */
.gx-box {
  position: absolute;
  border-radius: 12px;
  background: var(--gx-surface);
  border: 1px solid var(--gx-stroke);
  box-shadow: var(--gx-shadow-sm);
  padding: 14px 16px;
}
.gx-box--yellow { background: var(--gx-sticky-yellow); border-color: rgba(161, 98, 7, 0.14); }
.gx-box--green { background: var(--gx-sticky-green); border-color: rgba(21, 128, 61, 0.14); }
.gx-box-title {
  font-size: 12px;
  font-weight: 700;
  color: var(--gx-ink);
  margin-bottom: 9px;
  letter-spacing: -0.01em;
}
/* Content is suggested with soft bars: believable at blur, no fake lorem */
.gx-bar {
  height: 6px;
  border-radius: 3px;
  background: rgba(26, 26, 34, 0.16);
  margin-bottom: 7px;
}
.gx-box--yellow .gx-bar,
.gx-box--green .gx-bar { background: rgba(26, 26, 26, 0.24); }
.gx-bar--70 { width: 70%; }
.gx-bar--50 { width: 50%; }
.gx-bar--85 { width: 85%; }
/* One line is being typed, live */
.gx-bar--typing {
  position: relative;
  width: 62%;
  transform-origin: left center;
  animation: gx-typing 7s steps(14) infinite;
}
.gx-bar--typing::after {
  content: "";
  position: absolute;
  right: -6px;
  top: -2px;
  width: 2px;
  height: 10px;
  background: var(--gx-indigo-text);
  animation: gx-caret 0.6s steps(1) infinite;
}
@keyframes gx-typing {
  0% { transform: scaleX(0.2); }
  55% { transform: scaleX(1); }
  78% { transform: scaleX(1); }
  84% { transform: scaleX(0.2); }
  100% { transform: scaleX(0.2); }
}
@keyframes gx-caret {
  0%, 100% { opacity: 1; }
  50% { opacity: 0; }
}
/* Echo-authored badge (the sparkle chip the real room shows) */
.gx-box--echo::before {
  content: "✦";
  position: absolute;
  top: -9px;
  left: -9px;
  width: 18px;
  height: 18px;
  border-radius: 50%;
  background: linear-gradient(135deg, var(--gx-indigo-text) 0%, var(--gx-indigo-deep) 100%);
  box-shadow: 0 2px 6px rgba(67, 56, 202, 0.32);
  color: #fff;
  font-size: 10px;
  line-height: 18px;
  text-align: center;
}
/* Slow organic drift, GPU-only */
.gx-float-a { animation: gx-float-a 12s ease-in-out infinite; }
.gx-float-b { animation: gx-float-b 15s ease-in-out infinite; }
.gx-float-c { animation: gx-float-c 10s ease-in-out infinite; }
@keyframes gx-float-a {
  0%, 100% { transform: translate(0, 0); }
  50% { transform: translate(3px, -4px); }
}
@keyframes gx-float-b {
  0%, 100% { transform: translate(0, 0); }
  50% { transform: translate(-4px, 3px); }
}
@keyframes gx-float-c {
  0%, 100% { transform: translate(0, 0); }
  50% { transform: translate(2px, 4px); }
}
/* The yellow note gets "dragged" a few px while cursor A pauses on it */
.gx-box--dragged { animation: gx-dragged 26s ease-in-out infinite; }
@keyframes gx-dragged {
  0%, 11% { transform: translate(0, 0); }
  15% { transform: translate(22px, -12px); }
  21% { transform: translate(22px, -12px); }
  27% { transform: translate(26px, -10px); }
  100% { transform: translate(26px, -10px); }
}

/* Room title (transparent title box, like the real canvas) */
.gx-room-title {
  position: absolute;
  font-size: 21px;
  font-weight: 700;
  color: var(--gx-ink);
  letter-spacing: -0.015em;
  opacity: 0.85;
  max-width: 34vw;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Connectors between boxes */
.gx-connectors {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
}
.gx-connectors path {
  stroke: var(--gx-indigo-text);
  stroke-width: 2.5;
  fill: none;
  opacity: 0.55;
}

/* ---------- 1d. Live cursors --------------------------------------- */
.gx-cursor {
  position: absolute;
  z-index: 4;
  will-change: transform;
}
.gx-cursor svg {
  filter: drop-shadow(0 1px 2px rgba(17, 17, 24, 0.28));
}
.gx-cursor-name {
  position: absolute;
  left: 15px;
  top: 14px;
  font-size: 11px;
  font-weight: 600;
  padding: 3px 8px;
  border-radius: 6px;
  color: #fff;
  white-space: nowrap;
  box-shadow: 0 2px 6px rgba(17, 17, 24, 0.18);
}
.gx-cursor--a { animation: gx-cur-a 26s ease-in-out infinite; }
.gx-cursor--b { animation: gx-cur-b 31s ease-in-out infinite; }
@keyframes gx-cur-a {
  0% { transform: translate(0, 0); }
  11% { transform: translate(7vw, 2vh); }
  21% { transform: translate(8.5vw, 1.2vh); }   /* pause: drags the note */
  38% { transform: translate(9vw, 34vh); }
  48% { transform: translate(9.3vw, 34.4vh); }  /* pause: reads the plan */
  68% { transform: translate(3vw, 20vh); }
  76% { transform: translate(3vw, 20vh); }
  100% { transform: translate(0, 0); }
}
@keyframes gx-cur-b {
  0% { transform: translate(0, 0); }
  14% { transform: translate(-6vw, -9vh); }
  24% { transform: translate(-5.7vw, -8.6vh); } /* pause: on decisions */
  42% { transform: translate(3vw, -46vh); }
  54% { transform: translate(3.4vw, -46vh); }   /* pause: watches typing */
  78% { transform: translate(-7vw, -22vh); }
  86% { transform: translate(-7vw, -22vh); }
  100% { transform: translate(0, 0); }
}

/* ---------- 1e. Toolbar / minimap / zoom (set dressing) ------------ */
.gx-toolbar {
  position: absolute;
  left: 50%;
  bottom: 22px;
  transform: translateX(-50%);
  display: flex;
  gap: 6px;
  padding: 8px 12px;
  border-radius: 12px;
  background: rgba(255, 255, 255, 0.88);
  border: 1px solid var(--gx-stroke);
  box-shadow: var(--gx-shadow-sm);
}
.gx-tool {
  width: 38px;
  height: 38px;
  border-radius: 11px;
  display: flex;
  align-items: center;
  justify-content: center;
}
.gx-tool-i {
  width: 16px;
  height: 16px;
  border: 2px solid rgba(26, 26, 34, 0.62);
  border-radius: 4px;
}
.gx-tool-i--grid {
  border: none;
  background:
    radial-gradient(circle 3px, var(--gx-indigo-text) 99%, transparent) 2px 2px / 8px 8px no-repeat,
    radial-gradient(circle 3px, var(--gx-indigo-text) 99%, transparent) 10px 2px / 8px 8px no-repeat,
    radial-gradient(circle 3px, var(--gx-indigo-text) 99%, transparent) 2px 10px / 8px 8px no-repeat,
    radial-gradient(circle 3px, var(--gx-indigo-text) 99%, transparent) 10px 10px / 8px 8px no-repeat;
}
.gx-tool-i--circle { border-radius: 50%; }
.gx-tool-i--text {
  border: none;
  color: rgba(26, 26, 34, 0.7);
  font-size: 15px;
  font-weight: 700;
  line-height: 1;
  text-align: center;
}
.gx-tool-i--pen {
  border: none;
  position: relative;
}
.gx-tool-i--pen::after {
  content: "";
  position: absolute;
  left: 1px;
  top: 7px;
  width: 15px;
  height: 2px;
  border-radius: 1px;
  background: rgba(26, 26, 34, 0.62);
  transform: rotate(-45deg);
}
.gx-tool--exit {
  background: rgba(220, 38, 38, 0.10);
}
.gx-tool--exit .gx-tool-i {
  border-color: #dc2626;
}
.gx-minimap {
  position: absolute;
  right: 20px;
  top: 50%;
  transform: translateY(-50%);
  width: 110px;
  height: 70px;
  border-radius: 10px;
  background: rgba(255, 255, 255, 0.88);
  border: 1px solid var(--gx-stroke);
  box-shadow: var(--gx-shadow-sm);
}
.gx-minimap::before {
  content: "";
  position: absolute;
  left: 18px;
  top: 14px;
  width: 30px;
  height: 10px;
  border-radius: 2px;
  background: rgba(26, 26, 34, 0.20);
  box-shadow: 28px 18px 0 -2px rgba(217, 168, 12, 0.55), 8px 30px 0 -3px rgba(34, 160, 92, 0.45);
}
.gx-zoom {
  position: absolute;
  right: 20px;
  top: 20px;
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 12px;
  border-radius: 10px;
  background: rgba(255, 255, 255, 0.88);
  border: 1px solid var(--gx-stroke);
  box-shadow: var(--gx-shadow-sm);
  color: var(--gx-muted);
  font-size: 11px;
  font-weight: 600;
}

/* ============================================================
   2. ENTRY PAGE — access card refinements
   ============================================================ */

.gx-live-pill {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 5px 12px;
  border-radius: 999px;
  background: var(--auth-success-bg);
  border: 1px solid var(--auth-success-stroke);
  color: var(--auth-success);
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  margin-bottom: 14px;
}
.gx-live-dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--auth-success);
  animation: gx-blink 2s ease-in-out infinite;
}
/* Safety reassurance under the CTA (2026-07-30).
   Arthur's clients hesitate to bring documents — and to SPEAK — in a tool they
   have never seen. This is layer 1 of a two-layer answer: one calm sentence
   here, the full detail one click away at /security. Deliberately NOT styled as
   a warning: a security banner with a yellow border produces the anxiety it was
   meant to remove. Muted text, indigo hairline, lock glyph, no shouting. */
.gx-safety {
  display: flex;
  align-items: flex-start;
  gap: 9px;
  margin-top: 16px;
  padding: 11px 13px;
  border-radius: 10px;
  background: var(--auth-info-bg);
  border: 1px solid var(--auth-info-stroke);
  font-size: 12.5px;
  line-height: 1.55;
  color: var(--auth-muted);
  text-align: left;
}
.gx-safety svg {
  width: 14px;
  height: 14px;
  flex: 0 0 auto;
  margin-top: 2px;
  color: var(--auth-accent-text);
}
.gx-safety a {
  color: var(--auth-accent-text);
  font-weight: 600;
  text-decoration: none;
  white-space: nowrap;
}
.gx-safety a:hover { text-decoration: underline; }

/* A6 — "join without a microphone" (2026-07-30).
   Sits with the safety block, not with the form fields, because it belongs to
   the same family of message: you keep control of your own devices. Deliberately
   quiet — it is an escape hatch for the minority who need it, not a competing
   call to action. */
.gx-nomic{
  display:flex; align-items:flex-start; gap:9px;
  margin-top:10px; padding:0 2px;
  font-size:12.5px; line-height:1.45; color:var(--auth-muted);
  cursor:pointer; user-select:none;
}
.gx-nomic input{
  flex:0 0 auto; margin:2px 0 0; width:14px; height:14px;
  accent-color:var(--auth-accent-text); cursor:pointer;
}
.gx-nomic:hover{ color:var(--auth-ink-2); }
/* Pre-join mic check — one quiet line under the no-mic choice. */
.gx-mictest{ margin-top:7px; padding:0 2px; font-size:12px; text-align:left; }
.gx-mictest a{ color:var(--auth-muted); text-decoration:none; }
.gx-mictest a:hover{ color:var(--auth-accent-text); text-decoration:underline; }

/* "Continue with Google" — the fast lane into the room. On a white card a
   white button needs an edge to exist at all, so the fill now carries the
   standard hairline instead of the old dark-sheet drop shadow. */
.gx-google-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  width: 100%;
  height: 46px;
  border: 1px solid var(--auth-stroke-strong);
  border-radius: 12px;
  background: #ffffff;
  color: #1f1f1f;
  font-family: inherit;
  font-size: 14px;
  font-weight: 600;
  letter-spacing: 0.01em;
  cursor: pointer;
  box-shadow: 0 1px 2px rgba(17, 17, 24, 0.05);
  transition: transform 0.15s ease, box-shadow 0.2s ease, background 0.2s ease, border-color 0.2s ease;
}
.gx-google-btn:hover {
  background: var(--auth-surface-2);
  border-color: #c4c4d4;
  transform: translateY(-1px);
  box-shadow: 0 2px 4px rgba(17, 17, 24, 0.06), 0 8px 20px rgba(49, 46, 129, 0.10);
}
.gx-google-btn:active { transform: translateY(0); }
.gx-google-btn:disabled {
  opacity: 0.65;
  cursor: default;
  transform: none;
}
.gx-google-btn svg { flex: 0 0 18px; }

/* Thin "or" seam between the Google shortcut and the manual form */
.gx-or {
  display: flex;
  align-items: center;
  gap: 12px;
  margin: 16px 0;
  color: var(--auth-muted);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}
.gx-or::before,
.gx-or::after {
  content: "";
  flex: 1;
  height: 1px;
}
.gx-or::before { background: linear-gradient(90deg, transparent, var(--auth-stroke-strong)); }
.gx-or::after { background: linear-gradient(90deg, var(--auth-stroke-strong), transparent); }

/* The entry page's staged room is full-bleed and busy. The access card does
   not fight it with a colour inversion any more — it wins on elevation. */
body[data-gx="entry"] .auth-shell {
  box-shadow:
    0 2px 6px rgba(17, 17, 24, 0.05),
    0 18px 44px rgba(17, 17, 24, 0.10),
    0 48px 96px rgba(49, 46, 129, 0.16);
}
.gx-stage--entry { background: var(--gx-room-bg); }

/* ============================================================
   3. EXIT PAGE — the room as an object on paper
   ============================================================ */

/* The stage layer itself is transparent so the page's own paper (the dot
   grid and ambient wash from style_auth.css) stays put while the room
   card collapses on top of it. On the dark version the whole viewport
   scaled; here only the object moves, which is what makes it read as an
   object at all. */
.gx-stage--exit { background: transparent; }
.gx-stage--exit .gx-room {
  inset: 6% 8%;
  border-radius: 18px;
  overflow: hidden;
  background: var(--gx-room-bg);
  border: 1px solid var(--gx-stroke);
  box-shadow: var(--gx-shadow-lg);
  filter: blur(1.8px) saturate(1.04);
  transform: none;
  transition: filter 0.42s ease, box-shadow 0.42s ease, border-color 0.42s ease;
}
@media (max-width: 720px) {
  .gx-stage--exit .gx-room { inset: 4% 5%; }
}

/* ---------- 3a. Beat 1 — the tug ----------------------------------
   Something has taken hold of the card before it starts to fall. 110ms,
   barely 1.5% of scale — an anticipation you register without being able
   to name, which is the only kind that survives being watched twice. */
.gx-stage--exit {
  transition: transform 0.19s cubic-bezier(0.32, 0, 0.28, 1);
  will-change: transform;
}
.gx-stage--exit.gx-tug { transform: scale(0.985); }
.gx-stage--exit.gx-tug .gx-room {
  filter: blur(1.2px) saturate(1.3);
  box-shadow: 0 3px 8px rgba(17, 17, 24, 0.07), 0 20px 48px rgba(49, 46, 129, 0.24);
}

/* ---------- 3b. Beat 2 — the fall ----------------------------------
   The card falls into a point at 50% / 46% — the same point the burst
   comes back out of. Two things make it read as gravity rather than as
   a zoom-out:

   · the easing is a HARD ease-in. Things do not fall at a constant rate,
     and a linear collapse is the single tell that separates "sucked in"
     from "scaled down";
   · it rotates while it shrinks, and the rotation is large (140°). A
     falling body conserves angular momentum, so it spins faster as it
     draws in — the canvas streaks in guest-experience.js wind the same
     way and at the same rate, which is what fuses the card and the
     spiral into one event instead of two effects playing together.

   The pigment rides the SAME curve, so the room saturates as it falls
   rather than being tinted on a schedule of its own. That decoupling is
   what made the previous version read as "a purple thing appeared". */
.gx-stage--exit.gx-fall {
  transition: transform 0.7s cubic-bezier(0.66, 0, 0.86, 0.32),
              opacity 0.16s ease 0.54s;
  transform: rotate(140deg) scale(0.012);
  opacity: 0;
}
.gx-stage--exit.gx-fall .gx-room {
  transition: filter 0.7s cubic-bezier(0.66, 0, 0.86, 0.32);
  filter: blur(2.6px) saturate(2.6) contrast(1.08);
  border-color: rgba(67, 56, 202, 0.8);
}
.gx-stage--exit .gx-ink { transition: opacity 0.3s ease, transform 0.3s ease; }
.gx-stage--exit.gx-tug .gx-ink { opacity: 0.12; transform: scale(1.2); }
.gx-stage--exit.gx-fall .gx-ink {
  transition: opacity 0.7s cubic-bezier(0.66, 0, 0.86, 0.32),
              transform 0.7s cubic-bezier(0.66, 0, 0.86, 0.32);
  opacity: 1;
  transform: scale(0.85);
}
.gx-stage--exit.gx-off { display: none; }

/* ---------- 3c. The bar of ink -------------------------------------
   There is no longer a `.gx-seam` element. It used to be a 4px fixed
   div that took over from the card and handed off to the canvas, which
   meant two handovers and — because 4px of hairline on a 2560x1440
   white sheet is nothing — 120ms of visibly blank page between them.

   The canvas now draws the bar itself, and it does so by MEASURING the
   card's real box every frame (getBoundingClientRect, which carries the
   ancestor transform) instead of re-deriving it from a duplicated
   easing curve. Same object, same silhouette, same clock, from the
   moment the card starts closing to the moment the burst dies. See
   `runCanvas` in public/js/guest-experience.js.

   ---------- 3d. The burst canvas -----------------------------------
   Bar → charge → break, then dot-grid shockwave + pigment arc + canvas
   debris. Plain source-over compositing: on a white sheet an additive
   pass paints nothing at all.

   Below .gx-convert (z-index 5) and not above it: the wave and the copy
   deliberately overlap in time, and a chip crossing a headline is
   litter no matter how good the physics. Underneath, the same overlap
   reads as the cards landing ON TOP of what the burst threw. */
.gx-burst {
  position: fixed;
  inset: 0;
  z-index: 4;
  pointer-events: none;
}

/* ============================================================
   4. CONVERSION CONTENT
   ============================================================ */

/* .auth-v2 is built around a single card that always fits, so it centres
   its child and clips the overflow. The exit page is a full composition
   and on a 375x812 phone it is taller than the viewport — with the
   inherited `overflow: hidden` the footer link was simply unreachable.
   The stage is position:fixed, so letting the page scroll costs nothing. */
body[data-gx="exit"] {
  align-items: flex-start;
  overflow-y: auto;
  overflow-x: hidden;
  padding: 0;
}

.gx-convert {
  position: relative;
  z-index: 5;
  /* These pages have no global border-box reset, so without this the
     56/44 padding was added ON TOP of the 100dvh and every desktop
     viewport ended up 100px scrollable for nothing. */
  box-sizing: border-box;
  min-height: 100dvh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  padding: 56px 22px 44px;
  max-width: 660px;
  margin: 0 auto;
  opacity: 0;
  visibility: hidden;
}
/* The stain the wave left behind. Without it the final frame is a flat
   white page and the whole sequence reads as something that happened to
   a different design. */
.gx-convert::before {
  content: "";
  /* Fixed, not absolute: the stain is deliberately wider than the column
     it sits behind, and an absolutely-positioned one contributed its
     overflow to the page — the exit page could be scrolled 150px sideways
     into empty margin. Fixed boxes do not add to scrollable overflow. */
  position: fixed;
  left: 50%;
  top: 44%;
  width: min(178vw, 1180px);
  height: min(112vh, 860px);
  transform: translate(-50%, -50%);
  background:
    radial-gradient(ellipse 46% 40% at 50% 46%, rgba(99, 102, 241, 0.15) 0%, transparent 68%),
    radial-gradient(ellipse 70% 62% at 50% 48%, rgba(129, 140, 248, 0.09) 0%, transparent 72%),
    radial-gradient(ellipse 96% 82% at 50% 50%, rgba(165, 180, 252, 0.05) 0%, transparent 76%);
  pointer-events: none;
  z-index: -1;
}
.gx-convert.gx-in {
  visibility: visible;
  opacity: 1;
  transition: opacity 0.5s ease;
}
.gx-convert > * {
  opacity: 0;
  transform: translateY(14px);
}
.gx-convert.gx-in > * {
  opacity: 1;
  transform: translateY(0);
  transition: opacity 0.62s cubic-bezier(0.16, 1, 0.3, 1), transform 0.62s cubic-bezier(0.16, 1, 0.3, 1);
}
/* Tighter than the 0.25 → 1.0s ladder this replaced. The reveal now starts
   while the wave is still travelling, so a slow stagger stops reading as
   choreography and starts reading as lag — the copy has to catch the front,
   not trail it by a second. */
.gx-convert.gx-in > *:nth-child(1) { transition-delay: 0.04s; }
.gx-convert.gx-in > *:nth-child(2) { transition-delay: 0.12s; }
.gx-convert.gx-in > *:nth-child(3) { transition-delay: 0.20s; }
.gx-convert.gx-in > *:nth-child(4) { transition-delay: 0.42s; }
.gx-convert.gx-in > *:nth-child(5) { transition-delay: 0.52s; }
.gx-convert.gx-in > *:nth-child(6) { transition-delay: 0.62s; }
/* The row of claims is exempt from the generic reveal: each card carries its
   own arrival (see @keyframes gx-land-*), and a container fading in under
   three flying cards double-dips the opacity. Specificity is deliberate — it
   has to beat `.gx-convert.gx-in > *` in both states. */
body[data-gx="exit"] .gx-convert > .gx-lived,
body[data-gx="exit"] .gx-convert.gx-in > .gx-lived {
  opacity: 1;
  transform: none;
  transition: none;
}

.gx-convert .logo {
  margin-bottom: 22px;
}
.gx-convert .logo img {
  height: 38px;
  width: auto;
  object-fit: contain;
}
.gx-hero {
  font-size: clamp(28px, 4.6vw, 40px);
  font-weight: 700;
  color: var(--auth-ink);
  letter-spacing: -0.022em;
  line-height: 1.16;
  margin: 0 0 14px;
  max-width: 24ch;
}
/* Both stops sit at or below #4f46e5, so the gradient never lands on a
   value that fails AA as text. */
.gx-hero .accent {
  background: linear-gradient(135deg, #4f46e5 0%, #4338ca 100%);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}
/* The three claims, styled as pieces of the Coommit canvas dropped on
   the page: yellow sticky + cursor tag, dark Echo-authored card + sparkle
   badge, green sticky + pinned badge. Same recipe as the product's canvas
   boxes and the staged room's notes. On white the Echo card stops being a
   dark patch and becomes the one deliberate accent in the row — so it is
   pushed further into ink rather than lightened. */
.gx-lived {
  width: 100%;
  max-width: 600px;
  margin: 30px 0 44px;
  display: flex;
  justify-content: center;
  align-items: stretch;
  gap: 16px;
  text-align: left;
}
.gx-lived-card {
  position: relative;
  flex: 1 1 0;
  max-width: 196px;
  display: flex;
  flex-direction: column;
  gap: 7px;
  border-radius: 12px;
  padding: 20px 16px 16px;
  box-shadow: var(--gx-shadow-md);
  opacity: 0;
  transition: transform 0.35s cubic-bezier(0.16, 1, 0.3, 1), box-shadow 0.35s ease, opacity 0.3s ease;
}
/* The three cards are the objects the burst threw: they fly in from where the
   seam broke and settle into their tilt.

   The arrival is an ANIMATION, not a transition, and its fill-mode is
   `backwards` only. That is the whole trick: during the delay the card holds
   the 0% keyframe (off-position), and the moment the animation ends the rule
   stops applying anything — so the hover transform below still wins, which it
   would not against `forwards`. Opacity is left to the transition above for
   the same reason. */
.gx-convert.gx-in .gx-lived-card { opacity: 1; }
.gx-convert.gx-in .gx-lived-card--canvas {
  animation: gx-land-a 0.72s cubic-bezier(0.18, 0.92, 0.28, 1) 0.16s backwards;
}
.gx-convert.gx-in .gx-lived-card--echo {
  animation: gx-land-b 0.72s cubic-bezier(0.18, 0.92, 0.28, 1) 0.25s backwards;
}
.gx-convert.gx-in .gx-lived-card--board {
  animation: gx-land-c 0.72s cubic-bezier(0.18, 0.92, 0.28, 1) 0.34s backwards;
}
.gx-convert.gx-in .gx-lived-card--canvas { transition-delay: 0.16s; }
.gx-convert.gx-in .gx-lived-card--echo { transition-delay: 0.25s; }
.gx-convert.gx-in .gx-lived-card--board { transition-delay: 0.34s; }
/* No `to` keyframe on purpose. An omitted 100% is filled in by the browser
   from the element's own computed transform, so each card lands on exactly the
   tilt its rule gives it — including the smaller angles the mobile media query
   substitutes. Writing the resting transform out here would have meant three
   more keyframe sets to keep in sync with the breakpoint.
   They overshoot past that angle first: a thrown object that lands flat has
   not been thrown. */
@keyframes gx-land-a {
  0%  { transform: translate(30px, -52px) rotate(-34deg) scale(0.86); }
  62% { transform: translate(-3px, 3px) rotate(-3deg) scale(1.025); }
}
@keyframes gx-land-b {
  0%  { transform: translate(0, -64px) rotate(23deg) scale(0.84); }
  62% { transform: translate(0, 4px) rotate(10deg) scale(1.03); }
}
@keyframes gx-land-c {
  0%  { transform: translate(-32px, -46px) rotate(31deg) scale(0.86); }
  62% { transform: translate(3px, 4px) rotate(-1deg) scale(1.025); }
}
.gx-lived-card strong {
  font-size: 13.5px;
  font-weight: 700;
  letter-spacing: -0.01em;
  line-height: 1.3;
}
.gx-lived-card p {
  margin: 0;
  font-size: 12.5px;
  line-height: 1.5;
}
/* Yellow sticky — the canvas you touched */
.gx-lived-card--canvas {
  background: var(--gx-sticky-yellow);
  border: 1px solid rgba(161, 98, 7, 0.16);
  transform: rotate(-8deg) translateY(10px);
}
.gx-lived-card--canvas strong { color: #1a1a1a; }
.gx-lived-card--canvas p { color: rgba(26, 26, 26, 0.74); }
/* Ink card — Echo-authored, sparkle badge like the product */
.gx-lived-card--echo {
  background: linear-gradient(170deg, #26234a 0%, var(--gx-echo-card) 55%, #17162c 100%);
  border: 1px solid rgba(99, 102, 241, 0.35);
  box-shadow: 0 2px 6px rgba(17, 17, 24, 0.10), 0 16px 36px rgba(32, 30, 60, 0.30);
  transform: rotate(5.5deg) translateY(-8px);
  padding-bottom: 34px;
  z-index: 1;
}
.gx-lived-card--echo strong { color: #f0eeff; }
.gx-lived-card--echo p { color: #b6b1dc; }
/* Green sticky — decisions pinned on the board */
.gx-lived-card--board {
  background: var(--gx-sticky-green);
  border: 1px solid rgba(21, 128, 61, 0.16);
  transform: rotate(-6.5deg) translateY(14px);
}
.gx-lived-card--board strong { color: #1a1a1a; }
.gx-lived-card--board p { color: rgba(26, 26, 26, 0.74); }
.gx-lived-card:hover {
  transform: rotate(0deg) translateY(-2px) scale(1.03);
  box-shadow: 0 6px 14px rgba(17, 17, 24, 0.07), 0 26px 56px rgba(49, 46, 129, 0.24);
  z-index: 2;
}
/* Cursor tag on the sticky — "You", straight out of the live canvas */
.gx-lived-cursor {
  position: absolute;
  top: -13px;
  right: -8px;
  display: flex;
  align-items: flex-end;
  gap: 1px;
}
.gx-lived-cursor svg { filter: drop-shadow(0 1px 2px rgba(17, 17, 24, 0.3)); }
.gx-lived-cursor-name {
  font-size: 10.5px;
  font-weight: 600;
  color: #fff;
  background: var(--gx-cursor-blue);
  padding: 3px 8px;
  border-radius: 6px;
  box-shadow: 0 2px 6px rgba(17, 17, 24, 0.2);
}
/* Echo-authored sparkle, top-left, same gradient as the product badge */
.gx-lived-spark {
  position: absolute;
  top: -9px;
  left: -9px;
  width: 20px;
  height: 20px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  background: linear-gradient(135deg, var(--gx-indigo-text) 0%, var(--gx-indigo-deep) 100%);
  box-shadow: 0 2px 6px rgba(67, 56, 202, 0.36);
  color: #fff;
  font-size: 11px;
}
/* Echo chip, bottom-right like the "Listening" pill in the room */
.gx-lived-chip {
  position: absolute;
  bottom: 10px;
  right: 12px;
  font-size: 9px;
  font-weight: 700;
  color: #d5d1ff;
  background: rgba(99, 102, 241, 0.30);
  padding: 3px 7px;
  border-radius: 8px;
  letter-spacing: 0.04em;
}
/* "Pinned" badge — the owner-badge recipe from the canvas */
.gx-lived-pin {
  position: absolute;
  top: -9px;
  right: 12px;
  font-size: 8px;
  font-weight: 800;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: #fff;
  background: var(--gx-indigo-text);
  padding: 3px 8px;
  border-radius: 10px;
  box-shadow: 0 3px 8px rgba(67, 56, 202, 0.32);
}
/* The emotional beat of the page. On white it needs weight, not glow —
   ink colour, a hair more size, and a short brand rule above it so the
   eye stops there instead of sliding to the button. */
.gx-truth {
  position: relative;
  font-size: 20px;
  font-weight: 650;
  color: var(--auth-ink);
  letter-spacing: -0.012em;
  line-height: 1.4;
  margin: 0 0 34px;
  padding-top: 22px;
  max-width: 30ch;
}
.gx-truth::before {
  content: "";
  position: absolute;
  left: 50%;
  top: 0;
  width: 34px;
  height: 3px;
  margin-left: -17px;
  border-radius: 2px;
  background: linear-gradient(90deg, var(--gx-indigo-text), var(--gx-indigo-deep));
}
.gx-cta-zone {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 14px;
  width: 100%;
}
/* The two calls to action are one stack, not two siblings floating in the CTA
   zone's 14px rhythm: 8px between them, so they read as a primary decision
   with an escape hatch under it. */
.gx-cta-actions {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  width: 100%;
}
.gx-cta-actions .btn-primary,
.gx-cta-actions .btn-secondary {
  width: auto;
  min-width: 320px;
}
.gx-cta-zone .btn-primary {
  height: 52px;
  font-size: 15px;
}
/* Deliberately quieter than the primary in every dimension — shorter, lighter
   type, muted ink, no elevation. It is the answer to "what did I just leave
   behind", not a competing offer. */
.gx-cta-alt {
  height: 44px;
  font-size: 13.5px;
  font-weight: 600;
  color: var(--auth-muted);
}
.gx-cta-alt svg { flex: 0 0 15px; opacity: 0.85; }
.gx-cta-alt:hover { color: var(--auth-ink); }
.gx-cta-zone .auth-footer {
  margin: 0;
}
/* CTA reassurance line on the exit page */
.gx-cta-note {
  font-size: 12.5px;
  color: var(--auth-muted);
  margin: 0;
}

/* The ambient wash from style_auth.css is tuned for a card-sized page.
   The exit page is one wide composition, so the residue of the burst
   spreads further and sits a touch stronger. */
body[data-gx="exit"]::after {
  background:
    radial-gradient(ellipse at center, rgba(99, 102, 241, 0.13) 0%, rgba(129, 140, 248, 0.06) 42%, transparent 74%);
}

/* ============================================================
   5. RESPONSIVE
   ============================================================ */

@media (max-width: 860px) {
  .gx-minimap, .gx-zoom { display: none; }
}
@media (max-width: 720px) {
  .gx-people { display: none; }
  .gx-cursor--b { display: none; }
  .gx-room-title { font-size: 17px; max-width: 60vw; }
  /* keep Echo presence: a floating orb tile, top area */
  .gx-person--echo.gx-person--solo {
    display: flex;
    position: absolute;
    left: 18px;
    top: 56px;
    width: 130px;
    height: 110px;
  }
  .gx-lived {
    flex-direction: column;
    align-items: center;
    gap: 16px;
    margin: 24px auto 38px;
    max-width: 300px;
  }
  .gx-lived-card { width: 100%; max-width: 264px; }
  .gx-lived-card--canvas { transform: rotate(-4deg); }
  .gx-lived-card--echo { transform: rotate(3deg) translateY(0); }
  .gx-lived-card--board { transform: rotate(-3.5deg); }
  .gx-convert { padding: 44px 20px 36px; }
  .gx-convert .logo img { height: 32px; }
  .gx-hero { max-width: 16ch; }
  .gx-truth { font-size: 18px; margin-bottom: 28px; }
  .gx-cta-actions .btn-primary,
  .gx-cta-actions .btn-secondary { width: 100%; min-width: 0; }
}
.gx-person--solo { display: none; }
@media (max-width: 720px) {
  .gx-person--solo { display: flex; }
}

/* ============================================================
   6. REDUCED MOTION — everything freezes, nothing hides
   ============================================================
   The failure mode to avoid here is a from-state that survives: the
   conversion content starts at opacity 0 and is only revealed by a JS
   class, so if the sequence never runs the page must be forced open
   rather than left mid-fade. The cinematic layers are removed outright
   — a static ink drop parked over the copy is worse than no drop. */

@media (prefers-reduced-motion: reduce) {
  /* Pseudo-elements need their own selector: `.gx-stage *` does not match
     them, so the caret on the "line being typed" (.gx-bar--typing::after)
     kept blinking for someone who had asked for motion to stop. */
  .gx-stage *::before,
  .gx-stage *::after {
    animation: none !important;
  }
  .gx-stage *,
  .gx-cursor,
  .gx-box--dragged,
  .gx-bar--typing,
  .gx-echo-fab img,
  .gx-live-dot,
  .gx-lived-card,
  .gx-convert.gx-in .gx-lived-card--canvas,
  .gx-convert.gx-in .gx-lived-card--echo,
  .gx-convert.gx-in .gx-lived-card--board {
    animation: none !important;
  }
  .gx-stage--exit,
  .gx-stage--exit .gx-room,
  .gx-stage--exit .gx-ink {
    transition: none !important;
  }
  .gx-stage--exit,
  .gx-burst {
    display: none !important;
  }
  /* .gx-lived-card is listed explicitly: it is a GRANDchild of .gx-convert, so
     the `> *` selector never reached it and the three claims stayed at
     opacity 0 — a blank row where the whole argument of the page lives. */
  .gx-convert,
  .gx-convert > *,
  .gx-lived-card {
    opacity: 1 !important;
    visibility: visible !important;
    transition: none !important;
  }
  .gx-convert,
  .gx-convert > * { transform: none !important; }
  /* The tilts stay: they are the layout, not the motion. */
  .gx-lived-card:hover { transform: none; }
}

/* The no-JS fallback lives in the <noscript> block of guest-thanks.ejs.
   A `.gx-nojs` class used to be styled here, but nothing ever added it —
   with scripting off there is no script left to add a class. */
