/* Styles the AutoRouter landing page: tokens first, then the one-screen layout. */

:root {
  --bg: #0b0b0d;
  --surface: #141518;
  --surface-2: #1b1c20;
  --fg: #f4f4f5;
  --muted: #a1a1aa;
  --soft: #cfcfd4;
  --faint: #6b6b74;
  --ring: rgba(255, 255, 255, 0.1);
  --accent: #9c8cff;

  --radius-sm: 6px;
  --radius-md: 12px;
  --radius-lg: 24px;

  /* Motion, shared with the app and the web app. Things that change in place
     (hover, colour, fades, entrances) use quart; things that travel (pills,
     rows, panels, cursors) use house. Only looping breaths use in-out. */
  --ease-house: cubic-bezier(0.6, 0.6, 0, 1);
  --ease-quart: cubic-bezier(0.165, 0.84, 0.44, 1);
  --ease-in-out: cubic-bezier(0.66, 0, 0.34, 1);

  --duration-hover: 150ms;
  --duration-icon: 180ms;
  --duration-fade: 250ms;
  --duration-pill: 260ms;
  --duration-row: 450ms;

  --shadow-card:
    0 1px 2px rgba(0, 0, 0, 0.3), 0 8px 24px rgba(0, 0, 0, 0.35),
    0 0 0 0.5px var(--ring);
  --shadow-elevated:
    0 4px 8px rgba(0, 0, 0, 0.2), 0 24px 64px rgba(0, 0, 0, 0.45),
    0 0 0 0.5px var(--ring);

  /* The walkthrough stage from the app: near-black, lit violet from the
     bottom right, with a hairline rim. */
  --stage: #090909;
  --stage-glow: rgba(103, 117, 237, 0.12);
  --stage-rim: rgba(255, 255, 255, 0.14);
  --radius-stage: 22px;

  --rainbow: linear-gradient(
    90deg,
    #62d96b,
    #5c98ff,
    #9c62ff,
    #ff6fae,
    #ffa64f
  );
}

@property --rim-angle {
  syntax: "<angle>";
  inherits: false;
  initial-value: 0deg;
}

* {
  box-sizing: border-box;
}

html {
  color-scheme: dark;
  -webkit-font-smoothing: antialiased;
}

body {
  margin: 0;
  background: #000;
  color: var(--fg);
  font:
    400 16px/1.5 system-ui,
    -apple-system,
    "SF Pro Text",
    sans-serif;
}

a {
  color: inherit;
  text-decoration: none;
}

button {
  border: 0;
  font: inherit;
  cursor: pointer;
}

/* Layout -------------------------------------------------------------------- */

/* One screen, no scrolling: name and a way in at the top left, three short
   answers at the bottom left, and the portal behind the whole page, its
   mark filling the right and its inputs reaching in toward the text. */
.screen {
  display: grid;
  grid-template-columns: minmax(320px, 5fr) 7fr;
  grid-template-rows: minmax(0, 1fr) auto;
  height: 100dvh;
  overflow: hidden;
}

.intro {
  grid-area: 1 / 1;
  position: relative;
  z-index: 1;
  padding: 48px 56px 0;
}

.props {
  grid-area: 2 / 1;
  position: relative;
  z-index: 1;
  padding: 0 56px 40px;
}

.portal {
  grid-area: 1 / 1 / 3 / 3;
  position: relative;
  margin: 0;
}

.portal canvas {
  display: block;
  width: 100%;
  height: 100%;
}

/* A faint CRT texture; fades keep the portal clear of the text on the left
   and dim its foot, while the outputs run right up to the top and right
   edges, behind the alpha badge. */
.portal::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  background:
    repeating-linear-gradient(
      0deg,
      rgba(255, 255, 255, 0.025) 0 1px,
      transparent 1px 3px
    ),
    linear-gradient(90deg, #000 28%, transparent 42%),
    linear-gradient(transparent 85%, #000);
}

/* Share the answers' bottom inset and sit above the portal in the same grid. */
.legal-footer {
  grid-area: 2 / 2;
  align-self: end;
  justify-self: end;
  z-index: 1;
  padding: 0 56px 40px;
}

.legal-links {
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-end;
  gap: 20px;
  color: var(--muted);
  font-size: 12px;
}

.legal-links a:hover {
  color: var(--fg);
}

.legal-links a:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 5px;
  border-radius: 2px;
}

/* Intro --------------------------------------------------------------------- */

.brand {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  font-weight: 500;
  letter-spacing: -0.01em;
}

.brand svg {
  width: 20px;
  height: 22px;
  overflow: visible;
}

h1 {
  max-width: 11ch;
  margin: 12vh 0 32px;
  font-size: clamp(40px, 5vw, 68px);
  font-weight: 500;
  line-height: 1.02;
  letter-spacing: -0.035em;
  text-wrap: balance;
}

.actions {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
}

.button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  height: 46px;
  padding: 0 22px;
  border-radius: 999px;
  font-size: 15px;
  font-weight: 500;
  transition:
    transform var(--duration-hover) var(--ease-quart),
    box-shadow var(--duration-hover) var(--ease-quart),
    background var(--duration-hover) var(--ease-quart);
}

.button:active {
  transform: scale(0.98);
}

.button.primary {
  position: relative;
  isolation: isolate;
  background: transparent;
  color: #111216;
}

/* Layers under the label: a soft comet glow, then the white fill inside a
   crisp comet rim. The fill rides on the rim layer so the glow never tints it. */
/* The app's rainbow rim: a comet of hues that circles a primary button.
   Declared here, not on :root, so each frame's --rim-angle reaches it; a
   :root value would be resolved once and never turn. */
.button.primary::before,
.button.primary::after {
  --rim: conic-gradient(
    from var(--rim-angle),
    rgba(255, 255, 255, 0.35) 0 66%,
    rgba(98, 217, 107, 0.45) 72%,
    rgba(92, 152, 255, 0.6) 80%,
    rgba(156, 98, 255, 0.75) 87%,
    rgba(255, 111, 174, 0.9) 94%,
    #ffa64f 100%
  );
  content: "";
  position: absolute;
  border-radius: inherit;
  animation: rim 3.6s linear infinite;
  pointer-events: none;
}

.button.primary::before {
  z-index: -1;
  inset: -1.5px;
  border: 1.5px solid transparent;
  background:
    linear-gradient(#fff, #fff) padding-box,
    var(--rim) border-box;
}

.button.primary::after {
  z-index: -2;
  inset: -3px;
  background: var(--rim);
  filter: blur(8px);
  opacity: 0.4;
  transition: opacity var(--duration-hover) var(--ease-quart);
}

.button.primary:hover::after {
  opacity: 0.75;
}

@keyframes rim {
  to {
    --rim-angle: 360deg;
  }
}

.button.quiet {
  color: var(--muted);
  box-shadow: 0 0 0 0.5px var(--ring);
}

.button.quiet:hover {
  color: var(--fg);
  background: var(--surface);
}

.button.small {
  height: 34px;
  padding: 0 14px;
  font-size: 14px;
}

/* Answers ------------------------------------------------------------------- */

/* Native exclusive accordion: opening one answer closes the others. */
.props details {
  max-width: 360px;
  border-top: 0.5px solid var(--ring);
  interpolate-size: allow-keywords;
}

.props details::details-content {
  block-size: 0;
  overflow: hidden;
  /* Room on the left for the countdown bar, which the clip would hide. */
  margin-left: -18px;
  padding-left: 18px;
  transition:
    block-size var(--duration-row) var(--ease-house),
    content-visibility var(--duration-row) allow-discrete;
}

.props details[open]::details-content {
  block-size: auto;
}

.props summary {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 14px 0;
  list-style: none;
  cursor: pointer;
  font-size: 15px;
  font-weight: 500;
  color: var(--soft);
  transition: color var(--duration-hover) var(--ease-quart);
}

.props summary::-webkit-details-marker {
  display: none;
}

.props summary::after {
  content: "+";
  color: var(--faint);
  font-weight: 400;
  transition: transform var(--duration-icon) var(--ease-quart);
}

.props details[open] summary,
.props summary:hover {
  color: var(--fg);
}

/* While answers cycle on their own, a rainbow bar beside the open answer's
   scene fills over its dwell time, so the next hand-over is never a surprise. */
.scene-frame {
  position: relative;
}

/* An 80px pill centred beside the scene: a faint track, and a rainbow fill
   that grows down it. A clip-path keeps the fill's ends round while it grows. */
.props.cycling details[open] .scene-frame::before,
.props.cycling details[open] .scene-frame::after {
  content: "";
  position: absolute;
  top: calc(50% - 40px);
  left: -16px;
  width: 3px;
  height: min(80px, 100%);
  border-radius: 999px;
}

.props.cycling details[open] .scene-frame::before {
  background: rgba(255, 255, 255, 0.1);
}

.props.cycling details[open] .scene-frame::after {
  background: linear-gradient(
    180deg,
    #62d96b,
    #5c98ff,
    #9c62ff,
    #ff6fae,
    #ffa64f
  );
  animation: dwell var(--dwell, 6s) linear both;
}

@keyframes dwell {
  from {
    clip-path: inset(0 0 100% 0 round 999px);
  }
  to {
    clip-path: inset(0 0 0 0 round 999px);
  }
}

.props details[open] summary::after {
  transform: rotate(45deg);
}

.props p {
  margin: 0;
  padding: 10px 0 16px;
  color: var(--muted);
  font-size: 14px;
  text-wrap: balance;
}

/* Alpha badge: quiet, in the top-right corner over the portal. */
.badge {
  position: fixed;
  top: 44px;
  right: 52px;
  z-index: 2;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 5px 12px;
  border-radius: 999px;
  box-shadow: inset 0 0 0 0.5px var(--ring);
  /* The code cells' glass, so the portal glows through softly. */
  background: rgba(34, 35, 41, 0.8);
  backdrop-filter: blur(4px);
  color: var(--muted);
  font-size: 13px;
}

.badge::before {
  content: "";
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--rainbow);
}

/* Scenes -------------------------------------------------------------------- */

/* Each answer's scene is the onboarding walkthrough's stage, drawn at 360×176
   and scaled to the column by scenes.js. */
.scene {
  position: relative;
  width: 100%;
  aspect-ratio: 360 / 176;
  overflow: hidden;
  border-radius: 14px;
  background:
    radial-gradient(
      circle at 100% 100%,
      rgba(103, 117, 237, 0.14),
      transparent 70%
    ),
    var(--stage);
}

.scene::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.12);
  pointer-events: none;
}

.canvas {
  position: absolute;
  top: 0;
  left: 0;
  width: 360px;
  height: 176px;
  transform-origin: 0 0;
  font-family:
    system-ui,
    -apple-system,
    sans-serif;
  transition: opacity var(--duration-fade) var(--ease-quart);
}

.scene.out .canvas {
  opacity: 0;
}

/* The agent app, cropped to its bottom-right corner; the cut edges fade. */
.window {
  position: absolute;
  top: -70px;
  left: -110px;
  width: 430px;
  height: 226px;
  border-radius: 12px;
  background: #171717;
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.14);
  mask:
    linear-gradient(90deg, transparent 0, #000 40%),
    linear-gradient(180deg, transparent 0, #000 45%);
  mask-composite: intersect;
}

.placeholder {
  position: absolute;
  right: 120px;
  bottom: 60px;
  color: rgba(255, 255, 255, 0.6);
  font-size: 12px;
  font-weight: 500;
}

.composer {
  position: absolute;
  bottom: 12px;
  left: 158px;
  width: 140px;
  gap: 6px;
  white-space: nowrap;
  height: 26px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0 4px 0 9px;
  border-radius: 8px;
  background: rgba(255, 255, 255, 0.055);
  color: rgba(255, 255, 255, 0.3);
  font-size: 9.5px;
}

.pill-picker {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 3px 6px 3px 7px;
  border-radius: 999px;
  background: rgba(255, 166, 79, 0.1);
  color: #f3be85;
  font-size: 8.5px;
  font-weight: 600;
}

/* A drawn chevron: a text ⌄ sits low and squashed at this size. */
.pill-picker .chevron {
  width: 7px;
  height: 4.5px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.6;
  stroke-linecap: round;
  stroke-linejoin: round;
  opacity: 0.8;
}

[data-scene="frontier"] .composer {
  width: 268px;
}

/* The handle: a short quarter arc at the window's corner. */
.handle {
  position: absolute;
  top: 147px;
  left: 311px;
  width: 22px;
  height: 22px;
  fill: none;
  stroke: rgba(255, 255, 255, 0.55);
  stroke-width: 3;
  stroke-linecap: round;
  transition: filter var(--duration-hover) var(--ease-quart);
}

.scene.lit .handle {
  stroke: url(#rainbow);
  filter: drop-shadow(0 0 4px rgba(156, 98, 255, 0.9));
}

.chip {
  position: absolute;
  right: 48px;
  bottom: 28px;
  display: flex;
  align-items: center;
  gap: 8px;
  height: 26px;
  padding: 0 12px;
  border-radius: 13px;
  background: rgba(28, 28, 30, 0.92);
  box-shadow:
    inset 0 0 0 0.5px rgba(255, 255, 255, 0.18),
    0 6px 14px rgba(0, 0, 0, 0.5);
  font-size: 10px;
  font-weight: 500;
  opacity: 0;
  transform: scale(0.98);
  transform-origin: bottom right;
  filter: blur(2px);
  transition:
    opacity var(--duration-fade) var(--ease-quart),
    transform var(--duration-fade) var(--ease-quart),
    filter var(--duration-fade) var(--ease-quart);
}

.chip i {
  color: var(--faint);
  font-style: normal;
}

.scene.active .chip {
  opacity: 1;
  transform: none;
  filter: none;
}

/* The pointer only glides while a state targets it; a reset snaps it back. */
.cursor {
  position: absolute;
  top: 0;
  left: 0;
  width: 14px;
  height: 14px;
  fill: #fff;
  stroke: rgba(0, 0, 0, 0.6);
  stroke-width: 1;
  filter: drop-shadow(0 1px 1px rgba(0, 0, 0, 0.5));
  opacity: 0;
  transform: translate(230px, 100px);
  transition: opacity var(--duration-fade) var(--ease-quart);
}

.scene.in .cursor {
  opacity: 1;
}

.scene.hover .cursor {
  transform: translate(318px, 150px);
  transition:
    opacity var(--duration-fade) var(--ease-quart),
    transform 900ms var(--ease-house);
}

/* Frontier: the picker's menu grows up from the AutoRouter pill. */
.menu {
  position: absolute;
  right: 28px;
  bottom: 44px;
  width: 150px;
  padding: 3px;
  border-radius: 9px;
  background: #212123;
  box-shadow:
    inset 0 0 0 0.5px rgba(255, 255, 255, 0.14),
    0 10px 24px rgba(0, 0, 0, 0.55);
  opacity: 0;
  transform: scale(0.98);
  transform-origin: bottom right;
  transition:
    opacity var(--duration-fade) var(--ease-quart),
    transform var(--duration-fade) var(--ease-quart);
}

.scene.open .menu {
  opacity: 1;
  transform: none;
}

.row {
  display: grid;
  grid-template-rows: 0fr;
  overflow: hidden;
  opacity: 0;
  transform: scale(0.98);
  filter: blur(2px);
  transition:
    grid-template-rows var(--duration-row) var(--ease-house),
    opacity var(--duration-fade) var(--ease-quart),
    transform var(--duration-fade) var(--ease-quart),
    filter var(--duration-fade) var(--ease-quart);
}

.row.own,
.row.shown {
  grid-template-rows: 1fr;
  opacity: 1;
  transform: none;
  filter: none;
}

.row > div {
  display: flex;
  align-items: center;
  gap: 6px;
  min-height: 0;
  padding: 0 6px;
  color: rgba(255, 255, 255, 0.82);
  font-size: 9.5px;
  line-height: 17px;
  white-space: nowrap;
}

.row img {
  width: 10px;
  height: 10px;
}

.row.router > div {
  border-radius: 6px;
  background: rgba(255, 255, 255, 0.07);
  color: #f3be85;
  font-weight: 600;
  line-height: 20px;
}

.row.router svg {
  width: 10px;
  height: 11px;
}

.row.router b {
  margin-left: auto;
}

/* Rules: the HUD's rules page at the app's 420×284 panel, scaled into the
   canvas. Sizes below are the app's points. */
.hud {
  position: absolute;
  top: 10px;
  left: 64.5px;
  width: 420px;
  height: 284px;
  overflow: hidden;
  border-radius: 18px;
  background: rgba(22, 22, 24, 0.95);
  box-shadow:
    inset 0 0 0 1px rgba(255, 255, 255, 0.16),
    0 12px 32px rgba(0, 0, 0, 0.5);
  color: rgba(255, 255, 255, 0.92);
  opacity: 0;
  filter: blur(2px);
  transform: scale(0.55);
  transform-origin: 0 0;
  scale: 0.96;
  transition:
    opacity var(--duration-fade) var(--ease-quart),
    filter var(--duration-fade) var(--ease-quart),
    scale var(--duration-fade) var(--ease-quart);
}

.scene.shown .hud {
  opacity: 1;
  filter: none;
  scale: 1;
}

.page {
  display: flex;
  flex-direction: column;
  padding: 18px;
  transition: transform 800ms var(--ease-house);
}

.scene.scrolled .page {
  transform: translateY(-236px);
}

.rules-label {
  padding-left: 4px;
  color: rgba(255, 255, 255, 0.55);
  font-size: 11px;
  font-weight: 500;
}

.rules-label.mine {
  margin-top: 18px;
  font-size: 12px;
}

.sets {
  display: flex;
  flex-direction: column;
  gap: 6px;
  margin-top: 8px;
}

/* A rule set card; the chosen one takes its set's colour, rim and glow. */
.set {
  position: relative;
  display: flex;
  align-items: flex-start;
  gap: 12px;
  padding: 10px 11px;
  border-radius: 11px;
  background: rgba(255, 255, 255, 0.03);
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.07);
  --lit: rgba(255, 255, 255, 0.55);
  transition:
    background var(--duration-hover) var(--ease-quart),
    box-shadow var(--duration-hover) var(--ease-quart),
    transform var(--duration-hover) var(--ease-quart);
}

.set::after {
  content: "";
  position: absolute;
  inset: 0;
  padding: 1.5px;
  border-radius: inherit;
  background: linear-gradient(
    to bottom right,
    var(--tone),
    color-mix(in srgb, var(--tone) 30%, transparent)
  );
  mask:
    linear-gradient(#000 0 0) content-box,
    linear-gradient(#000 0 0);
  mask-composite: exclude;
  opacity: 0;
  transition: opacity var(--duration-fade) var(--ease-quart);
}

.set.chosen {
  --lit: var(--tone);
  background: color-mix(in srgb, var(--tone) 6%, rgba(255, 255, 255, 0.03));
  box-shadow:
    inset 0 0 0 1px rgba(255, 255, 255, 0.07),
    0 3px 10px color-mix(in srgb, var(--tone) 22%, transparent);
}

.set.chosen::after {
  opacity: 1;
}

.set-icon {
  display: grid;
  flex: none;
  place-items: center;
  width: 30px;
  height: 30px;
  border-radius: 8.7px;
  background: color-mix(in srgb, var(--lit) 14%, transparent);
  color: var(--lit);
  transition:
    background var(--duration-hover) var(--ease-quart),
    color var(--duration-hover) var(--ease-quart);
}

.set-icon svg,
.pick svg,
.arrow {
  fill: none;
  stroke: currentColor;
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.set-icon svg {
  width: 15px;
  height: 15px;
}

.set.chosen .set-icon svg {
  animation: bounce var(--duration-fade) var(--ease-quart);
}

@keyframes bounce {
  40% {
    transform: scale(1.18);
  }
}

.set-text {
  display: flex;
  flex: 1;
  flex-direction: column;
  min-width: 0;
}

.set-text b {
  font-size: 12.5px;
  font-weight: 500;
  line-height: 15px;
}

.set-text > span:not(.tags) {
  overflow: hidden;
  margin-top: 3px;
  color: rgba(255, 255, 255, 0.55);
  font-size: 11.5px;
  line-height: 14px;
  white-space: nowrap;
  text-overflow: ellipsis;
}

/* The chosen set's trade-offs open in place, focusing in as they grow. */
.tags {
  display: grid;
  grid-template-rows: 0fr;
  opacity: 0;
  filter: blur(2px);
  transition:
    grid-template-rows var(--duration-row) var(--ease-house),
    opacity var(--duration-fade) var(--ease-quart),
    filter var(--duration-fade) var(--ease-quart);
}

.tags > span {
  display: flex;
  gap: 5px;
  overflow: hidden;
  min-height: 0;
}

.set.chosen .tags {
  grid-template-rows: 1fr;
  opacity: 1;
  filter: none;
}

.tags i {
  margin-top: 6px;
  padding: 1.5px 7px;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.06);
  color: rgba(255, 255, 255, 0.55);
  font-size: 10.5px;
  font-style: normal;
  font-weight: 500;
  line-height: 13px;
  white-space: nowrap;
}

.tags i.pro {
  background: color-mix(in srgb, #62d96b 14%, transparent);
  color: #62d96b;
}

.ring {
  flex: none;
  width: 26px;
  height: 26px;
  margin: 4px;
  opacity: 0.85;
  transform: rotate(-90deg);
}

.ring circle {
  fill: none;
  stroke-width: 3.6;
  stroke-linecap: round;
}

/* The user's rows; each opens its height as it focuses in. */
.rule-row {
  display: grid;
  grid-template-rows: 0fr;
  transition: grid-template-rows var(--duration-row) var(--ease-house);
}

.rule-row > div {
  overflow: hidden;
  min-height: 0;
}

.rule-row.shown {
  grid-template-rows: 1fr;
}

.rule {
  display: flex;
  align-items: center;
  gap: 6px;
  margin-top: 8px;
  padding: 8px;
  border-radius: 12px;
  background: rgba(255, 255, 255, 0.03);
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.08);
  opacity: 0;
  filter: blur(3px);
  scale: 0.98;
  transition:
    opacity var(--duration-fade) var(--ease-quart),
    filter var(--duration-fade) var(--ease-quart),
    scale var(--duration-fade) var(--ease-quart);
}

.rule-row.shown .rule {
  opacity: 1;
  filter: none;
  scale: 1;
}

.pick {
  display: flex;
  flex: 1;
  align-items: center;
  gap: 6px;
  min-width: 0;
  height: 30px;
  padding: 0 8px;
  border-radius: 8px;
  background: color-mix(in srgb, var(--ink, #fff) 14%, transparent);
  color: var(--ink, rgba(255, 255, 255, 0.92));
  font-size: 12.5px;
  font-weight: 500;
  white-space: nowrap;
}

.pick:not([style]) {
  background: rgba(255, 255, 255, 0.06);
}

.pick svg {
  width: 11px;
  height: 11px;
  stroke-width: 2.2;
}

.pick img {
  width: 13px;
  height: 13px;
}

.pick .chevron {
  width: 12px;
  height: 12px;
  margin-left: auto;
  stroke-width: 2.6;
}

.arrow {
  flex: none;
  width: 12px;
  height: 12px;
  color: rgba(255, 255, 255, 0.25);
  stroke-width: 2.6;
}

.group {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-top: 18px;
  padding: 11px 14px;
  border-radius: 14px;
  background: rgba(36, 36, 36, 0.95);
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.09);
}

.setting {
  display: flex;
  flex: 1;
  flex-direction: column;
  gap: 2px;
}

.setting b {
  font-size: 13px;
  font-weight: 500;
}

/* Both explanations share one cell and crossfade as the switch turns. */
.detail {
  display: grid;
  color: rgba(255, 255, 255, 0.55);
  font-size: 11.5px;
}

.detail span {
  grid-area: 1 / 1;
  transition:
    opacity var(--duration-fade) var(--ease-quart),
    filter var(--duration-fade) var(--ease-quart);
}

.detail span + span,
.scene.on .detail span:first-child {
  opacity: 0;
  filter: blur(2px);
}

.scene.on .detail span + span {
  opacity: 1;
  filter: none;
}

.switch {
  position: relative;
  flex: none;
  width: 32px;
  height: 18px;
  border-radius: 9px;
  background: rgba(255, 255, 255, 0.18);
  transition:
    background var(--duration-hover) var(--ease-quart),
    transform var(--duration-hover) var(--ease-quart);
}

.switch::after {
  content: "";
  position: absolute;
  top: 2px;
  left: 2px;
  width: 14px;
  height: 14px;
  border-radius: 50%;
  background: #fff;
  transition: transform var(--duration-pill) var(--ease-house);
}

.scene.on .switch {
  background: #2f7cf6;
}

.scene.on .switch::after {
  transform: translateX(14px);
}

/* The cursor comes in from the bottom right to the Fastest card, then down
   to the switch, and leaves once it has turned on. */
[data-scene="rules"] .cursor {
  transform: translate(310px, 163px);
}

[data-scene="rules"].at-set .cursor {
  transform: translate(227px, 130px);
  transition:
    opacity var(--duration-fade) var(--ease-quart),
    transform 1100ms var(--ease-house);
}

[data-scene="rules"].at-switch .cursor {
  transform: translate(266px, 140px);
  transition:
    opacity var(--duration-fade) var(--ease-quart),
    transform 550ms var(--ease-house);
}

[data-scene="rules"].done .cursor {
  opacity: 0;
}

.scene.press:not(.at-switch) [data-set="fast"] {
  transform: scale(0.98);
}

.scene.press.at-switch .switch {
  transform: scale(0.94);
}

.scene.press .cursor {
  scale: 0.9;
}

/* Motion -------------------------------------------------------------------- */

/* Each block focuses in on load: a small lift and a blur that clears. */
.intro,
.props {
  animation: focus-in var(--duration-row) var(--ease-quart) both;
}

.props {
  animation-delay: 150ms;
}

@keyframes focus-in {
  from {
    opacity: 0;
    transform: scale(0.98);
    filter: blur(2px);
  }
}

@media (prefers-reduced-motion: reduce) {
  .intro,
  .props {
    animation: none;
  }

  .button.primary::before,
  .button.primary::after {
    animation: none;
  }

  .props details::details-content {
    transition: none;
  }

  .set.chosen .set-icon svg {
    animation: none;
  }
}

/* Keep the bottom row visible and leave room for the invite form on laptops. */
@media (min-width: 801px) and (max-height: 800px) {
  .intro {
    padding-top: 24px;
  }

  h1 {
    margin: 8px 0;
    font-size: clamp(40px, 5vw, 60px);
  }
}

/* Phone widths: the portal fills the top, running corner to corner under the
   badge; the brand sits in its empty top left corner, the title and Join centre
   over its foot, and every answer stays open below. */
@media (max-width: 800px) {
  .screen {
    grid-template-columns: minmax(0, 1fr);
    grid-template-rows: 62svh auto auto auto auto;
    height: auto;
    min-height: 100dvh;
  }

  /* The intro's parts join the page grid, so each can be placed on its own;
     they keep the intro's focus-in. */
  .intro {
    display: contents;
  }

  /* Backwards fill only, so the title can still fade while typing. */
  .intro > * {
    animation: inherit;
    animation-fill-mode: backwards;
  }

  .brand {
    grid-area: 1 / 1;
    align-self: start;
    justify-self: start;
    z-index: 1;
    margin: 30px 16px 0;
  }

  /* Its own stack, so the fade below stays under the title. */
  .portal {
    grid-area: 1 / 1;
    z-index: 0;
  }

  /* Keep the scanlines but drop the vignette, so the arms reach the edges. */
  .portal::after {
    background: repeating-linear-gradient(
      0deg,
      rgba(255, 255, 255, 0.025) 0 1px,
      transparent 1px 3px
    );
  }

  /* Fade the portal's foot so the title over it stays legible. */
  .portal::before {
    content: "";
    position: absolute;
    inset: 55% 0 0;
    z-index: 1;
    pointer-events: none;
    background: linear-gradient(transparent, #000);
  }

  h1 {
    grid-area: 2 / 1;
    justify-self: center;
    z-index: 1;
    margin: -96px 16px 24px;
    text-align: center;
  }

  h1 {
    transition:
      opacity var(--duration-fade) var(--ease-quart),
      filter var(--duration-fade) var(--ease-quart);
  }

  /* While the code is typed, the title gives way to the cells, which
     invite.js lifts above the keyboard. */
  .typing h1 {
    opacity: 0;
    filter: blur(4px);
  }

  .gate {
    grid-area: 3 / 1;
    justify-self: center;
    z-index: 1;
    width: min(350px, 100% - 32px);
    transform: translateY(var(--lift, 0px));
    transition: transform var(--duration-row) var(--ease-house);
  }

  .gate .actions {
    justify-content: center;
  }

  /* The lifted cells float over the portal; let it glow through them. The
     extra .otp outranks the base cell rule, which comes later. */
  .otp .cells span {
    background: rgba(34, 35, 41, 0.8);
    backdrop-filter: blur(4px);
  }

  .gate .step {
    transform-origin: center;
  }

  .hint {
    text-align: center;
  }

  .props {
    grid-area: 4 / 1;
    padding: 32px 16px 28px;
  }

  .props details {
    max-width: none;
  }

  /* Answers render open from the first frame: no expanding, no cycling. */
  .props details::details-content {
    block-size: auto;
    content-visibility: visible;
    transition: none;
  }

  /* Every answer is open on phones, so its heading is a label, not a toggle. */
  .props summary {
    pointer-events: none;
  }

  .props summary::after {
    content: none;
  }

  /* Full width, with the links centred as a group; they bring their own
     gutter. */
  .legal-footer {
    grid-area: 5 / 1;
    justify-self: stretch;
    padding: 0 0 12px;
  }

  .legal-links {
    justify-content: center;
  }

  .badge {
    position: absolute;
    top: 26px;
    right: 16px;
  }
}

/* Invite gate --------------------------------------------------------------- */

/* Every step shares one grid cell, so swapping steps never moves the page.
   Steps cross-fade and focus in with blur; nothing slides. */
.gate {
  display: grid;
  max-width: 420px;
}

.gate .step {
  grid-area: 1 / 1;
  opacity: 0;
  filter: blur(4px);
  transform: scale(0.98);
  transform-origin: left center;
  visibility: hidden;
  transition:
    opacity var(--duration-fade) var(--ease-quart),
    filter var(--duration-fade) var(--ease-quart),
    transform var(--duration-fade) var(--ease-quart),
    visibility 0s linear var(--duration-fade);
}

.gate[data-step="start"] [data-for="start"],
.gate[data-step="code"] [data-for="code"] {
  opacity: 1;
  filter: none;
  transform: none;
  visibility: visible;
  transition-delay: 0s;
}

/* One transparent input spans the six cells and owns focus, typing and paste. */
.otp {
  position: relative;
  display: block;
  max-width: 350px;
}

.otp input {
  position: absolute;
  inset: 0;
  width: 100%;
  border: 0;
  outline: 0;
  opacity: 0;
  font-size: 16px;
  cursor: text;
}

/* Two groups of three around a dash, like the codes we hand out: XDY-431. */
.cells {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr)) 14px repeat(
      3,
      minmax(0, 1fr)
    );
  gap: 8px;
}

.cells .dash {
  align-self: center;
  height: 2px;
  border-radius: 1px;
  background: var(--faint);
}

.cells span {
  --cell-ring: rgba(255, 255, 255, 0.16);
  display: grid;
  place-items: center;
  height: 54px;
  border-radius: var(--radius-md);
  background: #222329;
  box-shadow: 0 0 0 0.5px var(--cell-ring);
  color: var(--fg);
  font-size: 22px;
  font-weight: 500;
  font-variant-numeric: tabular-nums;
  transition:
    box-shadow var(--duration-hover) var(--ease-quart),
    background var(--duration-hover) var(--ease-quart),
    transform var(--duration-hover) var(--ease-quart);
}

/* A typed character pops in place: scale and blur, no movement. */
.cells span.filled {
  animation: cell-in var(--duration-icon) var(--ease-quart);
}

form:not([data-state]) .cells span.active {
  --cell-ring: var(--accent);
  box-shadow:
    0 0 0 1px var(--cell-ring),
    0 0 0 4px rgba(156, 140, 255, 0.14);
}

form:not([data-state]) .cells span.active:empty::after {
  content: "";
  width: 1.5px;
  height: 22px;
  border-radius: 1px;
  background: var(--fg);
  animation: caret 1s steps(1) infinite;
}

/* Checking: the cells breathe while the API answers. */
form[data-state="checking"] .cells span {
  animation: checking 900ms var(--ease-in-out) infinite alternate;
}

/* Denied: a short, damped shake with a red ring, then the cells clear. */
form[data-state="denied"] .cells {
  animation: shake var(--duration-row) var(--ease-house);
}

form[data-state="denied"] .cells span {
  --cell-ring: #ff5f6d;
  box-shadow:
    0 0 0 1px var(--cell-ring),
    0 0 0 4px rgba(255, 95, 109, 0.14);
}

/* Accepted: every cell turns green together and one check focuses in. The
   page holds here until sign-in takes over, so the code never vanishes. */
form[data-state="accepted"] .cells span {
  --cell-ring: #3ecf6b;
  box-shadow:
    0 0 0 1px var(--cell-ring),
    0 0 16px -4px var(--cell-ring);
  background: color-mix(in srgb, #3ecf6b 12%, #222329);
}

.otp .check {
  position: absolute;
  top: 50%;
  left: calc(100% + 14px);
  display: grid;
  place-items: center;
  width: 28px;
  height: 28px;
  margin-top: -14px;
  border-radius: 50%;
  background: #3ecf6b;
  color: #06210f;
  opacity: 0;
  filter: blur(4px);
  transform: scale(0.6);
  transition:
    opacity var(--duration-fade) var(--ease-quart),
    filter var(--duration-fade) var(--ease-quart),
    transform var(--duration-fade) var(--ease-quart);
}

.otp .check svg {
  width: 16px;
  height: 16px;
}

form[data-state="accepted"] .otp .check {
  opacity: 1;
  filter: none;
  transform: none;
  transition-delay: 80ms;
}

.hint {
  margin: 12px 0 0 2px;
  color: var(--muted);
  font-size: 13px;
}

form[data-state="denied"] .hint span {
  color: #ff8a94;
}

form[data-state="accepted"] .hint span {
  color: #7be39b;
}

.link {
  margin-left: 6px;
  padding: 0;
  background: none;
  color: var(--soft);
}

.link:hover {
  color: var(--fg);
}

/* No way back mid-check or once the code is accepted. */
form[data-state]:not([data-state="denied"]) .link {
  visibility: hidden;
}

.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
}

@keyframes cell-in {
  from {
    transform: scale(0.9);
    filter: blur(2px);
  }
}

@keyframes caret {
  50% {
    opacity: 0;
  }
}

@keyframes checking {
  to {
    background: var(--surface-2);
  }
}

@keyframes shake {
  20% {
    transform: translateX(-6px);
  }
  40% {
    transform: translateX(5px);
  }
  60% {
    transform: translateX(-3px);
  }
  80% {
    transform: translateX(2px);
  }
}

@media (prefers-reduced-motion: reduce) {
  .gate .step,
  .modal,
  .sheet,
  .panel,
  .cells span {
    transition: none;
  }

  .cells span.filled,
  form[data-state] .cells,
  form[data-state] .cells span {
    animation: none;
  }
}

/* Sign-in modal ------------------------------------------------------------- */

/* Takes over the screen: the page dims behind a liquid-glass sheet that still
   picks up the portal's colour. Everything focuses in; nothing slides. */
.modal {
  position: fixed;
  inset: 0;
  z-index: 10;
  display: grid;
  place-items: center;
  padding: 32px 16px;
  overflow-y: auto;
  background: rgba(0, 0, 0, 0.4);
  backdrop-filter: blur(0);
  opacity: 0;
  transition:
    opacity var(--duration-fade) var(--ease-quart),
    backdrop-filter var(--duration-fade) var(--ease-quart);
}

.modal[hidden] {
  display: none;
}

.modal.open {
  backdrop-filter: blur(28px) saturate(1.2);
  opacity: 1;
}

/* Liquid glass: a translucent, saturated blur with a specular rim that is
   brightest at the top-left, a soft inner sheen, and a deep layered shadow. */
.sheet {
  --control-fill: rgba(255, 255, 255, 0.06);
  --control-ring: rgba(255, 255, 255, 0.1);
  position: relative;
  width: min(540px, 100%);
  border-radius: 38px;
  background:
    linear-gradient(
      160deg,
      rgba(255, 255, 255, 0.08),
      rgba(255, 255, 255, 0.01) 40%,
      rgba(255, 255, 255, 0.03)
    ),
    rgba(34, 35, 41, 0.72);
  backdrop-filter: blur(48px) saturate(1.8);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.14),
    inset 0 0 0 0.5px rgba(255, 255, 255, 0.08),
    0 2px 6px rgba(0, 0, 0, 0.25),
    0 24px 48px rgba(0, 0, 0, 0.35),
    0 48px 120px rgba(0, 0, 0, 0.5);
  filter: blur(6px);
  transform: scale(0.97);
  transition:
    filter var(--duration-fade) var(--ease-quart),
    transform var(--duration-row) var(--ease-house);
}

/* The specular rim: a 1px gradient border drawn with a mask. */
.sheet::before {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  padding: 1px;
  background: linear-gradient(
    150deg,
    rgba(255, 255, 255, 0.35),
    rgba(255, 255, 255, 0.06) 30%,
    rgba(255, 255, 255, 0.02) 60%,
    rgba(255, 255, 255, 0.16)
  );
  mask:
    linear-gradient(#000 0 0) content-box,
    linear-gradient(#000 0 0);
  mask-composite: exclude;
  pointer-events: none;
}

.modal.open .sheet {
  filter: none;
  transform: none;
}

/* The app's floating close: a small glass circle beyond the top-right corner. */
.close {
  position: absolute;
  top: -22px;
  right: -22px;
  z-index: 1;
  display: grid;
  place-items: center;
  width: 30px;
  height: 30px;
  border-radius: 50%;
  background: rgba(44, 45, 53, 0.6);
  backdrop-filter: blur(20px) saturate(1.6);
  box-shadow:
    inset 0 0 0 0.5px rgba(255, 255, 255, 0.16),
    inset 0 1px 0 rgba(255, 255, 255, 0.1),
    0 4px 12px rgba(0, 0, 0, 0.35);
  color: rgba(255, 255, 255, 0.55);
  transition:
    color var(--duration-hover) var(--ease-quart),
    box-shadow var(--duration-hover) var(--ease-quart),
    transform var(--duration-hover) var(--ease-quart);
}

.close:hover {
  box-shadow:
    inset 0 0 0 0.5px rgba(255, 255, 255, 0.32),
    inset 0 1px 0 rgba(255, 255, 255, 0.14),
    0 4px 12px rgba(0, 0, 0, 0.35);
  color: rgba(255, 255, 255, 0.92);
  transform: scale(1.06);
}

.close:active {
  transform: scale(0.98);
}

.close svg {
  width: 10px;
  height: 10px;
}

/* Hero: the routing story on the first scene's stage, set into the sheet's
   top edge. Everything appears by focusing in; nothing slides. */
.hero {
  padding: 14px 14px 0;
}

.hero .scene {
  border-radius: 28px 28px 14px 14px;
}

.hero .composer {
  left: 130px;
  width: 170px;
}

.hero .typed {
  overflow: hidden;
  text-overflow: ellipsis;
}

.hero .typed.filled {
  color: rgba(255, 255, 255, 0.82);
}

.hero .placeholder,
.hero .bubble,
.hero .reply {
  transition:
    opacity var(--duration-fade) var(--ease-quart),
    filter var(--duration-fade) var(--ease-quart);
}

.hero .scene.sent .placeholder {
  opacity: 0;
  filter: blur(2px);
}

/* The sent prompt, right-aligned above the composer. */
.hero .bubble {
  position: absolute;
  right: 130px;
  bottom: 84px;
  max-width: 170px;
  padding: 7px 9px;
  border-radius: 9px;
  background: rgba(255, 255, 255, 0.075);
  color: rgba(255, 255, 255, 0.87);
  font-size: 9.5px;
  font-weight: 500;
  line-height: 1.35;
  opacity: 0;
  filter: blur(2px);
}

.hero .reply {
  position: absolute;
  left: 130px;
  bottom: 46px;
  display: grid;
  gap: 4px;
  width: 190px;
  opacity: 0;
  filter: blur(2px);
}

.hero .reply .by {
  display: flex;
  align-items: center;
  gap: 5px;
  color: rgba(255, 255, 255, 0.42);
  font-size: 8px;
  font-weight: 500;
}

.hero .reply img {
  width: 10px;
  height: 10px;
}

.hero .reply .line {
  color: rgba(255, 255, 255, 0.65);
  font-size: 9px;
}

.hero .scene.sent .bubble,
.hero .scene.replied .reply {
  opacity: 1;
  filter: none;
}

/* The chip is one shell: its width eases (set by scenes.js) while labels
   crossfade in place, as the HUD's routing notice does. */
.hero .chip {
  isolation: isolate;
  justify-content: flex-end;
  padding: 0;
  overflow: hidden;
  transition:
    width var(--duration-pill) var(--ease-house),
    opacity var(--duration-fade) var(--ease-quart),
    transform var(--duration-fade) var(--ease-quart),
    filter var(--duration-fade) var(--ease-quart);
}

.hero .chip .label {
  position: absolute;
  right: 9px;
  display: flex;
  align-items: center;
  gap: 6px;
  white-space: nowrap;
  opacity: 0;
  transition: opacity var(--duration-fade) var(--ease-quart) 180ms;
}

.hero .chip .label.shown {
  opacity: 1;
}

.hero .chip .label.leaving {
  opacity: 0;
  transition: opacity var(--duration-fade) var(--ease-quart);
}

.hero .scene.routing .chip .label.shown {
  animation: pulse 1.4s var(--ease-in-out) infinite;
}

.hero .chip img {
  width: 12px;
  height: 12px;
}

.hero .chip b {
  font-weight: 500;
}

/* The one-word reason badge, like the HUD's reason chip. */
.hero .chip em {
  padding: 2px 6px;
  border-radius: 999px;
  background: rgba(156, 140, 255, 0.16);
  color: #c9c1ff;
  font-size: 8.5px;
  font-style: normal;
  font-weight: 500;
}

/* The HUD's routing beam (HUDRoutingBeam.swift): a conic gradient turns
   beneath a ring mask that keeps it to the chip's border. As in
   the app, a static gradient square rotates by transform, so each frame is
   composited, not repainted. Always on; routing turns it faster and brighter. */
.hero .beam {
  position: absolute;
  inset: 0;
  z-index: 1;
  overflow: hidden;
  border-radius: inherit;
  padding: 1px;
  mask:
    linear-gradient(#000 0 0) content-box,
    linear-gradient(#000 0 0);
  mask-composite: exclude;
  opacity: 0.65;
  pointer-events: none;
  transition:
    opacity var(--duration-fade) var(--ease-quart),
    padding var(--duration-pill) var(--ease-house);
}

.hero .beam i {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 260px;
  height: 260px;
  margin: -130px 0 0 -130px;
  background: conic-gradient(
    rgba(255, 159, 10, 0.8) 0%,
    rgba(255, 55, 95, 0.4) 20%,
    rgba(255, 255, 255, 0.2) 40%,
    rgba(191, 90, 242, 0.8) 60%,
    rgba(10, 132, 255, 0.4) 78%,
    rgba(255, 255, 255, 0.95) 94%,
    rgba(255, 159, 10, 0.8) 100%
  );
  will-change: transform;
  animation: turn 9s linear infinite;
}

.hero .scene.routing .beam {
  padding: 1.6px;
  opacity: 1;
}

.hero .scene.routing .beam i {
  animation-duration: 4s;
}

/* While the prompt types, the chip steps back until it's sent. */
.hero .scene.typing .chip {
  opacity: 0;
  filter: blur(2px);
  transform: scale(0.96);
}

@keyframes turn {
  to {
    transform: rotate(1turn);
  }
}

@keyframes pulse {
  50% {
    opacity: 0.45;
  }
}

/* Panels cross-fade in one place. Only the active panel sets the height,
   which invite.js animates, so each step fits its own content. */
.panels {
  position: relative;
  overflow: hidden;
  transition: height var(--duration-row) var(--ease-house);
}

.panel {
  position: absolute;
  inset: 0 0 auto;
  padding: 28px 32px 32px;
  opacity: 0;
  filter: blur(4px);
  visibility: hidden;
  transition:
    opacity var(--duration-fade) var(--ease-quart),
    filter var(--duration-fade) var(--ease-quart),
    visibility 0s linear var(--duration-fade);
}

.modal[data-step="sign-in"] [data-for="sign-in"],
.modal[data-step="verify"] [data-for="verify"],
.modal[data-step="done"] [data-for="done"] {
  position: relative;
  opacity: 1;
  filter: none;
  visibility: visible;
  transition-delay: 0s;
}

.panel h2 {
  margin: 0 0 8px;
  font-size: 22px;
  font-weight: 500;
  letter-spacing: -0.025em;
}

.panel .note {
  margin: 0 0 24px;
  color: var(--muted);
  font-size: 14px;
  line-height: 1.45;
  text-wrap: balance;
}

.panel .note b {
  color: var(--soft);
  font-weight: 500;
}

/* Raised rows: the surfaces actions sit on. */
.raised {
  border-radius: 18px;
  background: var(--control-fill);
  box-shadow: inset 0 0 0 0.5px var(--control-ring);
}

.social {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 8px;
  margin-bottom: 8px;
}

.social button {
  display: flex;
  align-items: center;
  gap: 12px;
  height: 56px;
  padding: 0 18px;
  border-radius: 16px;
  background: var(--control-fill);
  box-shadow: inset 0 0 0 0.5px var(--control-ring);
  color: var(--fg);
  font-size: 15px;
  font-weight: 500;
  transition:
    background var(--duration-hover) var(--ease-quart),
    transform var(--duration-hover) var(--ease-quart);
}

.social button:hover {
  background: rgba(255, 255, 255, 0.1);
}

.social button:active {
  transform: scale(0.98);
}

.social svg {
  width: 24px;
  height: 24px;
}

/* Continue sits 11px from the field's top and bottom, so 11px from its
   right edge too. */
.email {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 6px 11px 6px 18px;
}

.email:focus-within {
  box-shadow:
    inset 0 0 0 1px var(--accent),
    0 0 0 4px rgba(156, 140, 255, 0.14);
}

.email input {
  flex: 1;
  min-width: 0;
  height: 44px;
  border: 0;
  outline: 0;
  background: transparent;
  color: var(--fg);
  font: inherit;
  font-size: 15px;
}

.email input::placeholder {
  color: var(--faint);
}

kbd {
  display: inline-grid;
  place-items: center;
  min-width: 20px;
  height: 20px;
  margin-left: 2px;
  padding: 0 4px;
  border-radius: 6px;
  background: rgba(17, 18, 22, 0.08);
  color: inherit;
  font: inherit;
  font-size: 12px;
}

.panel .hint {
  min-height: 1lh;
  margin: 10px 0 0 4px;
}

/* The email hint only takes room when it has something to say. */
[data-hint="email"]:has(span:empty) {
  display: none;
}

[data-hint="email"] span {
  color: #ff8a94;
}

/* Sign-in acceptance: one quiet line under the way in. Links open a new tab so
   the modal keeps its place. */
.panel .legal {
  margin: 16px 0 0 4px;
  color: var(--faint);
  font-size: 12px;
  line-height: 1.5;
}

.panel .legal a {
  color: var(--muted);
  text-decoration: underline 1px rgba(255, 255, 255, 0.24);
  text-underline-offset: 3px;
  transition: color var(--duration-hover) var(--ease-quart);
}

.panel .legal a:hover {
  color: var(--fg);
}

.verify {
  display: grid;
  justify-items: center;
  padding: 22px 18px 14px;
}

.verify .otp {
  width: 100%;
}

.verify .hint {
  margin: 12px 0 0;
}

.verify .otp .check {
  left: auto;
  right: -40px;
}

.sheet .cells span {
  background: rgba(255, 255, 255, 0.07);
}

/* The download card: icon, name and detail, with the keycaps on the right. */
.action {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 14px 18px 14px 14px;
  transition:
    background var(--duration-hover) var(--ease-quart),
    box-shadow var(--duration-hover) var(--ease-quart),
    transform var(--duration-hover) var(--ease-quart);
}

/* Hovering the card, or holding ⌘ and ↵ together, readies the download. */
.action:hover,
.action.armed {
  background: rgba(255, 255, 255, 0.1);
  box-shadow:
    inset 0 0 0 1px rgba(156, 140, 255, 0.55),
    0 0 0 4px rgba(156, 140, 255, 0.12);
}

.action:active {
  transform: scale(0.98);
}

.action img {
  width: 44px;
  height: 44px;
  border-radius: 11px;
}

.action > span:not(.keys) {
  display: grid;
  flex: 1;
  gap: 2px;
}

.action strong {
  font-size: 15px;
  font-weight: 500;
}

.action small {
  color: var(--muted);
  font-size: 13px;
}

.keys kbd {
  background: rgba(255, 255, 255, 0.08);
  box-shadow: inset 0 0 0 0.5px var(--control-ring);
  color: var(--soft);
}

/* The ↵ glyph rides high in the system font; drop it 1px to centre it. */
kbd.enter {
  padding-top: 2px;
}

/* A held key lights its keycaps wherever they appear. */
kbd {
  transition:
    box-shadow var(--duration-hover) var(--ease-quart),
    color var(--duration-hover) var(--ease-quart);
}

kbd {
  box-shadow: inset 0 0 0 1px transparent;
}

.keys kbd.held {
  box-shadow: inset 0 0 0 1px #fff;
  color: #fff;
}

/* On the white button the same press reads in the button's ink. */
.button kbd.held {
  box-shadow: inset 0 0 0 1px #111216;
  color: #111216;
}

.button kbd {
  margin-left: 0;
}

/* The button's 8px gap separates label and keys; keys sit 2px apart. */
.button kbd + kbd {
  margin-left: -6px;
}

.install {
  display: grid;
  gap: 10px;
  margin: 18px 0 0;
  padding: 0;
  list-style: none;
  color: var(--muted);
  font-size: 14px;
}

.install li {
  display: flex;
  align-items: center;
  gap: 12px;
}

.install b {
  display: grid;
  place-items: center;
  width: 22px;
  height: 22px;
  border-radius: 50%;
  background: var(--control-fill);
  box-shadow: inset 0 0 0 0.5px var(--control-ring);
  color: var(--soft);
  font-size: 12px;
  font-weight: 500;
}

/* Footer: a hairline, then a plain secondary and the primary with keycaps. */
.foot {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 20px;
  margin: 24px -32px -28px;
  padding: 20px 20px 20px 32px;
  border-top: 0.5px solid var(--control-ring);
}

.foot .link {
  margin: 0;
  color: var(--soft);
  font-size: 15px;
}

[data-for="verify"] .foot {
  justify-content: flex-start;
}

@media (prefers-reduced-motion: reduce) {
}

@media (max-width: 600px) {
  .close {
    top: -12px;
    right: -4px;
  }

  .panel {
    padding: 24px 20px;
  }

  .foot {
    margin: 20px -20px -24px;
    padding: 14px 20px;
  }
}

/* Sign-in methods ------------------------------------------------------------ */

/* The hovered method wears the primary button's turning rainbow rim; only it
   animates. Continue earns its rim once the email field is focused and has
   text, when it is the thing to press. */
.social button,
.email {
  position: relative;
}

.social button::before,
.email::before {
  --rim: conic-gradient(
    from var(--rim-angle),
    rgba(255, 255, 255, 0.35) 0 66%,
    rgba(98, 217, 107, 0.45) 72%,
    rgba(92, 152, 255, 0.6) 80%,
    rgba(156, 98, 255, 0.75) 87%,
    rgba(255, 111, 174, 0.9) 94%,
    #ffa64f 100%
  );
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  padding: 1.5px;
  background: var(--rim);
  mask:
    linear-gradient(#000 0 0) content-box,
    linear-gradient(#000 0 0);
  mask-composite: exclude;
  opacity: 0;
  pointer-events: none;
  transition: opacity var(--duration-hover) var(--ease-quart);
}

.social button:hover::before,
.email:hover::before {
  opacity: 1;
  animation: rim 3.6s linear infinite;
}

/* Inside the field the button is rounded in proportion to the field: 18px
   on 56px tall scales to 11px on the button's 34px. Its keycap sits as far
   from the right edge as from the top and bottom. */
.email .button {
  border-radius: calc(18px * 34 / 56);
  padding-right: 7px;
}

.email .button.primary::before {
  background:
    linear-gradient(#fff, #fff) padding-box,
    linear-gradient(rgba(255, 255, 255, 0.5), rgba(255, 255, 255, 0.5))
      border-box;
  animation: none;
}

.email .button.primary::after {
  opacity: 0;
  animation: none;
}

.email:focus-within:has(input:not(:placeholder-shown)) .button.primary::before {
  background:
    linear-gradient(#fff, #fff) padding-box,
    var(--rim) border-box;
  animation: rim 3.6s linear infinite;
}

.email:focus-within:has(input:not(:placeholder-shown)) .button.primary::after {
  opacity: 0.4;
  animation: rim 3.6s linear infinite;
}
