/* ══════════════════════════════════════════════════════════════════════════
   /beta — the one form a stranger fills in to reach this product.

   IT LIVES IN THE SHELL NOW, which is why this file exists at all. The page
   used to be a standalone stage: `web.frontend_layout`, a hand-linked copy of
   three sheets, its own theme boot and two hundred lines of CSS inside a
   <style> tag. A reader who arrived from the rail lost the rail, and the site
   became a different site for the length of one form.

   The template calls `website.layout` instead, so nix_website's portal layout
   wraps it in the same rail-and-panel every other page wears, and this is the
   page sheet the head links by name. The stage rules are gone with it: the
   panel is the stage, and nothing here positions itself against the window.

   Its markup is in agent_base, which nix_website depends on — the sheet has
   to live on this side because a module may not require what requires it.
   ══════════════════════════════════════════════════════════════════════════ */

/* The pair: the guide on one side, the form on the other, centred in a screen
   of its own. min-height is 100% of the panel — not a viewport unit — so the
   form fills the page it is on and the site's foot begins where that page
   ends, below the fold, the way it does on the blog and the sign-in. */
.ab-beta {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: clamp(40px, 7vw, 110px);
  min-height: 100%;
  padding: var(--space-6) 0 var(--space-8);
  margin: 0 auto;
}

.ab-beta__form {
  width: min(92%, 420px);
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.ab-beta__title {
  font-size: 30px;
  font-weight: 400;
  margin: 0;
  text-align: left;
}

.ab-beta__sub {
  font-size: 14.5px;
  line-height: 1.55;
  color: var(--nix-text-soft);
  margin: 0 0 12px;
  text-align: left;
}

/* The site's fields, as the lead form draws them: a ground a step above the
   page, no drawn edge, and a second step on focus. The two public forms this
   product has answer a pointer the same way — the one on /services was
   changed first and this followed it the same day, rather than the site
   carrying two ideas of what an input looks like. */
.ab-beta__field {
  width: 100%;
  padding: 14px 16px;
  border: 0;
  border-radius: var(--radius-chip);
  background: var(--chip-tint);
  color: var(--nix-text);
  font: inherit;
  font-size: 15px;
  transition: background var(--dur-2) ease;
}

.ab-beta__field::placeholder { color: var(--nix-text-soft); }

/* :focus, not :focus-visible — the latter matches a text field on a plain
   click in every engine, so the ring drew itself around a box the reader had
   just clicked into and read as an error state. The ground answers instead,
   and it may not go away: it is the only thing that says which field the
   keyboard is in. */
.ab-beta__field:focus,
.ab-beta__field:focus-visible {
  outline: none;
  background: var(--chip-hover);
}

.ab-beta__submit:focus-visible,
.ab-beta__back:focus-visible {
  outline: var(--focus-ring);
  outline-offset: var(--focus-offset);
}

textarea.ab-beta__field { min-height: 110px; resize: vertical; }

.ab-beta__submit {
  align-self: flex-start;
  padding: 13px 26px;
  border: 0;
  border-radius: var(--radius-chip);
  cursor: pointer;
  background: var(--nix-ink);
  color: var(--nix-on-ink);
  font-size: 15px;
  margin-top: 4px;
  transition: background var(--dur-2) ease;
}

/* A GROUND, NOT OPACITY. Fading the button fades the label with it — the two
   move together and the text ends up closer to the page it sits on, which is
   the opposite of what a hover should do. */
.ab-beta__submit:hover {
  background: color-mix(in srgb, var(--nix-ink) 88%, var(--nix-paper));
}

.ab-beta__sent {
  font-size: 16px;
  line-height: 1.6;
  color: var(--nix-text);
  text-align: left;
  margin: 0;
}

.ab-beta__back {
  color: var(--nix-text-soft);
  text-decoration: none;
  font-size: 13px;
  margin-top: 14px;
  display: inline-block;
}

.ab-beta__back:hover { color: var(--nix-text); }

/* ── The guide, CSS edition: the assisted orb's face without a single byte of
     WebGL — glass rim, aurora core, two eyes that blink on no schedule a
     person can predict. beta-orb.js swaps in the real one when it can, and
     this is what stands there until it does. ── */
.ab-orb {
  position: relative;
  width: clamp(240px, 22vw, 340px);
  aspect-ratio: 1;
  flex: none;
  animation: ab-orb-float 7s ease-in-out infinite;
}

.ab-orb__core {
  position: absolute;
  inset: 0;
  border-radius: 50%;
  background:
    radial-gradient(58% 52% at 46% 38%, rgba(108, 134, 255, 0.92), transparent 74%),
    radial-gradient(46% 42% at 68% 30%, rgba(150, 172, 255, 0.75), transparent 72%),
    radial-gradient(44% 44% at 30% 52%, rgba(176, 138, 255, 0.66), transparent 74%),
    radial-gradient(42% 40% at 64% 66%, rgba(255, 158, 208, 0.5), transparent 72%),
    radial-gradient(40% 40% at 40% 70%, rgba(132, 240, 226, 0.42), transparent 72%),
    #15161d;
  filter: blur(2px);
}

.ab-orb__rim {
  position: absolute;
  inset: -1%;
  border-radius: 50%;
  background: radial-gradient(closest-side, transparent 78%, rgba(244, 243, 241, 0.16) 92%, rgba(244, 243, 241, 0.34) 97%, transparent 100%);
}

.ab-orb__glow {
  position: absolute;
  inset: -14%;
  border-radius: 50%;
  background: radial-gradient(closest-side, rgba(140, 160, 255, 0.16), transparent 70%);
}

.ab-orb__eye {
  position: absolute;
  top: 36%;
  width: 6.5%;
  height: 22%;
  border-radius: var(--radius-pill);
  background: #fff;
  box-shadow: 0 0 18px rgba(255, 255, 255, 0.65);
  animation: ab-orb-blink 6.4s infinite;
  transform-origin: center;
}

.ab-orb__eye--l { left: 38%; }

.ab-orb__eye--r { left: 55%; animation-delay: 0.05s; }

@keyframes ab-orb-float {
  0%, 100% { transform: translateY(0); }
  50% { transform: translateY(-12px); }
}

@keyframes ab-orb-blink {
  0%, 41%, 45%, 100% { transform: scaleY(1); }
  43% { transform: scaleY(0.08); }
  71%, 75% { transform: scaleY(1); }
  73% { transform: scaleY(0.12); }
}

.wizard-orb-float { position: absolute; inset: 0; }

.wizard-orb-fallback { position: absolute; inset: 0; transition: opacity 300ms ease; }

.wizard-orb-fallback.is-off { opacity: 0; }

.wizard-orb-canvas {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  opacity: 0;
  transition: opacity 300ms ease;
}

.wizard-orb-canvas.is-live { opacity: 1; }

/* THIS PAGE IS PUBLIC AND PHONE-SIZED ON PURPOSE. The account shell's mobile
   gate (portal.css) hides main.page outright under 768px, because a device
   list is not a phone screen. A stranger asking for access is exactly a phone
   screen, so it opts out.

   It should never see that gate now — the shell is chosen by the path and
   /beta is not /my — and the rule stays anyway: it costs three lines and it
   is the difference between a phone showing this form and a phone showing
   nothing at all. */
@media (max-width: 720px) {
  #wrapwrap > main.page,
  #wrapwrap main { display: flex !important; }
}

/* The guide ABOVE, the form under it, both centred — a column, not a missing
   orb. */
@media (max-width: 900px) {
  .ab-beta {
    flex-direction: column;
    gap: 26px;
    min-height: 0;
    text-align: center;
  }

  .ab-orb { order: -1; width: min(52vw, 210px); }

  .ab-beta__form { align-items: stretch; width: 100%; max-width: 480px; }

  .ab-beta__title,
  .ab-beta__sub,
  .ab-beta__sent { text-align: center; }

  .ab-beta__submit { align-self: center; }
}

@media (prefers-reduced-motion: reduce) {
  .ab-orb,
  .ab-orb__eye { animation: none; }
}
