/* ============================================================================
   style_auth.css — shared look for login / signup / guest / legal / waitlist
   ---------------------------------------------------------------------------
   LIGHT MODE (2026-08-03). These pages used to ship a pure-black surface that
   matched nothing else the user would see after signing in: the app itself is
   light. Someone landing on /app/login and then on /app was crossing what felt
   like two different products. Every token below is the SAME value the app
   declares in style_app.css under [data-theme="light"], so the two surfaces
   cannot drift apart again.

   Palette contract (all ratios measured against #ffffff):
     --auth-accent        #6366F1  4.47:1 — DECORATION ONLY (dots, glow, rings)
     --auth-accent-text   #4f46e5  6.29:1 — link text + button fills (AA pass)
   The distinction is not cosmetic. #6366F1 as body-size link text measures
   4.47:1 and fails AA by a hair; the old dark sheet used #a5b4fc, which on a
   white surface is effectively invisible. Text and fills take the darker end
   of the ramp, ambience takes the lighter one.
   ============================================================================ */

:root {
  /* These pages never load style_app.css, so they carry their own copy of the
     brand token rather than a literal stack per rule.
     Order matters: the browser takes the FIRST family it can resolve. This stack
     used to start with -apple-system, so Manrope was never reached on any
     machine — every auth, legal, waitlist and guest page shipped in the OS
     system font while claiming to use the brand face. */
  --sans: 'Manrope', -apple-system, ui-sans-serif, system-ui, sans-serif;

  /* Surfaces — mirrors style_app.css [data-theme="light"] */
  --auth-bg: #f5f5f8;
  --auth-surface: #ffffff;
  --auth-surface-2: #fafafc;
  --auth-surface-3: #f0f0f4;
  --auth-stroke: #e6e6ee;        /* dividers, card edges — decorative */
  /* Control boundaries (inputs, OAuth buttons, secondary buttons). Darker than
     --auth-stroke on purpose: with a white field fill (below) the edge is the
     only thing separating a control from the card it sits on, so it must read
     alone. Still short of WCAG 1.4.11's 3:1 (that needs ~#85859a, which reads
     as a grey 2010s form) — the compromise is a heavier edge than the
     decorative stroke, with the focus ring taking over on interaction. */
  --auth-stroke-strong: #b6b6cc;
  --auth-stroke-hover: #9c9cb4;
  /* White on purpose (Alex, 2026-08-14): the old #f3f3f8 fill sat 3/255 away
     from the disabled fill (--auth-surface-3) — enabled fields read as locked
     next to the pure-white Google button. White = "type here"; grey stays the
     readonly/disabled signal. */
  --auth-field-fill: #ffffff;

  /* Ink ramp */
  --auth-ink: #111118;        /* headings                      16.1:1 */
  --auth-ink-2: #3f3f52;      /* body copy                      9.4:1 */
  --auth-muted: #6b6b7d;      /* secondary / captions           5.1:1 */
  --auth-placeholder: #9c9cae;/* placeholders only              2.9:1 */

  /* Brand */
  --auth-accent: #6366F1;
  --auth-accent-text: #4f46e5;
  --auth-accent-deep: #4338ca;
  --auth-accent-soft: rgba(99, 102, 241, 0.10);
  --auth-accent-ring: rgba(99, 102, 241, 0.18);

  /* Status */
  --auth-danger: #dc2626;
  --auth-danger-bg: #fef2f2;
  --auth-danger-stroke: #fecaca;
  --auth-success: #1f8f5a;
  --auth-success-bg: #f0fdf6;
  --auth-success-stroke: #bbf7d0;
  --auth-info-bg: #f5f5ff;
  --auth-info-stroke: #dcdcfb;

  /* Elevation — light-theme shadows are tinted with the brand hue rather than
     neutral black, which is what keeps a white card from looking like a
     screenshot pasted onto the page. */
  --auth-shadow-card:
    0 1px 2px rgba(17, 17, 24, 0.04),
    0 8px 24px rgba(17, 17, 24, 0.06),
    0 24px 56px rgba(79, 70, 229, 0.07);
  --auth-shadow-btn: 0 1px 2px rgba(67, 56, 202, 0.24), 0 6px 16px rgba(79, 70, 229, 0.26);
  --auth-shadow-btn-hover: 0 2px 4px rgba(67, 56, 202, 0.28), 0 10px 26px rgba(79, 70, 229, 0.34);

  --auth-r-sm: 6px;
  --auth-r-md: 10px;
  --auth-r-lg: 18px;
}

/* The reset this sheet never had.
   style_app.css opens with `*, *::before, *::after { box-sizing: border-box }`,
   but the ~15 pages served by THIS sheet never load it — so every one of them
   was writing `width:100%` next to a padding and getting a content box. Four
   independent surfaces hit the same trap and each patched it locally:
   .auth-shell, .ws-shell (waitlist signup), .tos-shell (terms/security) and
   .gx-convert (guest exit) — three of them shipping a real horizontal scroll at
   375px before someone measured it. Fixing the four symptoms leaves the trap
   armed for the fifth. Scoped to .auth-v2 rather than declared globally so it
   cannot reach a page that happens to load this sheet alongside another. */
.auth-v2,
.auth-v2 *,
.auth-v2 *::before,
.auth-v2 *::after {
  box-sizing: border-box;
}

.auth-v2 {
  margin: 0;
  min-height: 100vh;
  min-height: 100dvh;
  font-family: var(--sans);
  color: var(--auth-ink-2);
  background: var(--auth-bg);
  /* Scroll, don't clip. This was `overflow: hidden` (meant to keep the ambient
     layers from making scrollbars — both are position:fixed, so they never
     could) and it propagated to the viewport: on phones the guest-join card is
     taller than the visual viewport and nothing below the fold was reachable
     by touch (WebKit only pans once a focused input forces a programmatic
     scroll). Six sibling pages had each patched this locally (terms, security,
     waitlist ×2, call-files, guest exit); this is the root fix. */
  overflow-x: hidden;
  overflow-y: auto;
  position: relative;
  display: flex;
  align-items: center;
  /* When the card is taller than the viewport, plain `center` pushes its top
     above the scroll origin — unreachable. `safe center` falls back to
     flex-start exactly then; browsers that don't parse it keep plain center
     and are covered by the 480px/640px media queries below. */
  align-items: safe center;
  justify-content: center;
  padding: 24px;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

/* Fine dot grid covering everything. On the dark sheet this was a texture
   borrowed from the landing; here it earns its place differently — it is the
   canvas grid, the one surface the whole product is built on. Two layers offset
   from each other so it reads as paper rather than as a repeating tile. */
.auth-v2::before {
  content: "";
  position: fixed;
  inset: 0;
  background-image:
    radial-gradient(rgba(99, 102, 241, 0.16) 1px, transparent 1px),
    radial-gradient(rgba(99, 102, 241, 0.08) 1px, transparent 1px);
  background-size: 32px 32px, 96px 96px;
  background-position: 0 0, 16px 16px;
  pointer-events: none;
  z-index: 0;
  mask-image: radial-gradient(ellipse 100% 80% at 50% 50%, #000 30%, transparent 88%);
  -webkit-mask-image: radial-gradient(ellipse 100% 80% at 50% 50%, #000 30%, transparent 88%);
}

/* Ambient wash that slowly drifts. A blurred purple blob that reads as "glow"
   on black reads as "smudge" on white, so this is a much wider, much fainter
   tint with no blur filter — the light theme's version of the same idea. */
.auth-v2::after {
  content: "";
  position: fixed;
  top: -25%;
  left: 25%;
  width: 70%;
  height: 90%;
  background:
    radial-gradient(ellipse at center, rgba(99, 102, 241, 0.10) 0%, rgba(129, 140, 248, 0.05) 40%, transparent 72%);
  pointer-events: none;
  z-index: 0;
  animation: auth-glow-drift 22s ease-in-out infinite alternate;
}

@keyframes auth-glow-drift {
  0%   { transform: translate(-10%, -5%) scale(1);    opacity: 0.95; }
  50%  { transform: translate(12%, 8%)   scale(1.10); opacity: 1;    }
  100% { transform: translate(-14%, 6%)  scale(1.05); opacity: 0.85; }
}

/* Floating particles layer. A glowing dot is a dark-mode device — it works by
   emitting light against an absence of it. Inverted here into small solid
   indigo motes with a soft cast shadow: the same drifting motion, but they read
   as objects on paper instead of sparks in a void. */
.auth-particles {
  position: fixed;
  inset: 0;
  pointer-events: none;
  z-index: 1;
}
.auth-particles .p {
  position: absolute;
  width: 4px;
  height: 4px;
  border-radius: 50%;
  background: var(--auth-accent);
  box-shadow: 0 1px 3px rgba(79, 70, 229, 0.28);
  opacity: 0;
  will-change: transform, opacity;
}
/* 20 particles with staggered animations for organic motion */
.auth-particles .p:nth-child(1)  { left: 8%;  top: 20%; animation: auth-p1 14s ease-in-out 0s    infinite; }
.auth-particles .p:nth-child(2)  { left: 85%; top: 65%; animation: auth-p2 18s ease-in-out 1.5s  infinite; }
.auth-particles .p:nth-child(3)  { left: 22%; top: 78%; animation: auth-p1 16s ease-in-out 3s    infinite; }
.auth-particles .p:nth-child(4)  { left: 65%; top: 15%; animation: auth-p2 20s ease-in-out 0.8s  infinite; }
.auth-particles .p:nth-child(5)  { left: 12%; top: 55%; animation: auth-p1 22s ease-in-out 2.2s  infinite; }
.auth-particles .p:nth-child(6)  { left: 92%; top: 30%; animation: auth-p2 15s ease-in-out 4s    infinite; }
.auth-particles .p:nth-child(7)  { left: 45%; top: 88%; animation: auth-p1 19s ease-in-out 1s    infinite; }
.auth-particles .p:nth-child(8)  { left: 75%; top: 48%; animation: auth-p2 17s ease-in-out 2.8s  infinite; }
.auth-particles .p:nth-child(9)  { left: 35%; top: 25%; animation: auth-p1 21s ease-in-out 5s    infinite; }
.auth-particles .p:nth-child(10) { left: 58%; top: 72%; animation: auth-p2 16s ease-in-out 3.5s  infinite; }
.auth-particles .p:nth-child(11) { left: 5%;  top: 42%; animation: auth-p1 23s ease-in-out 6s    infinite; }
.auth-particles .p:nth-child(12) { left: 95%; top: 82%; animation: auth-p2 14s ease-in-out 0.3s  infinite; }
.auth-particles .p:nth-child(13) { left: 28%; top: 8%;  animation: auth-p1 20s ease-in-out 4.5s  infinite; }
.auth-particles .p:nth-child(14) { left: 50%; top: 35%; animation: auth-p2 18s ease-in-out 2s    infinite; }
.auth-particles .p:nth-child(15) { left: 72%; top: 92%; animation: auth-p1 15s ease-in-out 5.5s  infinite; }
.auth-particles .p:nth-child(16) { left: 18%; top: 68%; animation: auth-p2 22s ease-in-out 1.2s  infinite; }
.auth-particles .p:nth-child(17) { left: 88%; top: 12%; animation: auth-p1 17s ease-in-out 3.8s  infinite; }
.auth-particles .p:nth-child(18) { left: 40%; top: 60%; animation: auth-p2 19s ease-in-out 0.5s  infinite; }
.auth-particles .p:nth-child(19) { left: 62%; top: 5%;  animation: auth-p1 24s ease-in-out 2.5s  infinite; }
.auth-particles .p:nth-child(20) { left: 2%;  top: 90%; animation: auth-p2 16s ease-in-out 4.2s  infinite; }

/* Peak opacity is deliberately lower than the dark sheet's 0.9. A solid dot on
   white is far more present than a glowing one on black at the same value. */
@keyframes auth-p1 {
  0%, 100% { transform: translate(0, 0);        opacity: 0; }
  15%      {                                    opacity: 0.30; }
  50%      { transform: translate(40px, -60px); opacity: 0.42; }
  85%      {                                    opacity: 0.22; }
}
@keyframes auth-p2 {
  0%, 100% { transform: translate(0, 0);        opacity: 0; }
  15%      {                                    opacity: 0.26; }
  50%      { transform: translate(-50px, 40px); opacity: 0.38; }
  85%      {                                    opacity: 0.18; }
}

/* The card. Glass over black became a solid white surface: a translucent card
   on a light background just looks like a rendering mistake. Depth now comes
   from the layered, brand-tinted shadow rather than from a blur. */
.auth-v2 .auth-shell {
  position: relative;
  z-index: 2;
  /* These pages ship no global box-sizing reset (they never load
     style_app.css), so `width:100%` here is a CONTENT width and the 36px of
     side padding sits outside it. Today the flex parent shrinks the card back
     and nothing overflows — but that safety net is one `flex-shrink:0` away
     from disappearing, and the failure mode is a clipped card on a 320px
     phone, on the sign-in page. Declared explicitly rather than relied upon. */
  box-sizing: border-box;
  width: 100%;
  max-width: 420px;
  background: var(--auth-surface);
  border: 1px solid var(--auth-stroke);
  border-radius: var(--auth-r-lg);
  padding: 44px 36px 34px;
  box-shadow: var(--auth-shadow-card);
  animation: auth-card-in 0.6s cubic-bezier(0.34, 1.56, 0.64, 1) both;
}
@keyframes auth-card-in {
  from { opacity: 0; transform: translateY(16px) scale(0.97); }
  to   { opacity: 1; transform: translateY(0)   scale(1); }
}

.auth-v2 .auth-head {
  text-align: center;
  margin-bottom: 28px;
}
.auth-v2 .auth-head .logo {
  display: flex;
  justify-content: center;
  margin-bottom: 22px;
}
.auth-v2 .auth-head .logo img {
  height: 32px;
  width: auto;
  object-fit: contain;
}
.auth-v2 .auth-head h1 {
  font-size: 22px;
  font-weight: 700;
  letter-spacing: -0.015em;
  color: var(--auth-ink);
  margin: 0 0 8px;
  line-height: 1.25;
}
.auth-v2 .auth-head h1 .accent {
  background: linear-gradient(135deg, var(--auth-accent-text) 0%, var(--auth-accent-deep) 100%);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}
.auth-v2 .auth-head p {
  font-size: 13.5px;
  color: var(--auth-muted);
  line-height: 1.55;
  margin: 0;
}
.auth-v2 .auth-head p strong {
  color: var(--auth-ink-2);
  font-weight: 600;
}

/* Form elements */
.auth-v2 .form-group {
  margin-bottom: 16px;
}
.auth-v2 .form-label {
  display: block;
  font-size: 12px;
  font-weight: 600;
  color: var(--auth-ink-2);
  margin-bottom: 6px;
  letter-spacing: 0.02em;
}
.auth-v2 .form-input,
.auth-v2 textarea.form-input,
.auth-v2 select.form-input {
  width: 100%;
  height: 46px;
  padding: 0 36px 0 14px; /* right padding reserves space for browser autofill icon */
  background: var(--auth-field-fill);
  border: 1px solid var(--auth-stroke-strong);
  border-radius: var(--auth-r-md);
  color: var(--auth-ink);
  font-size: 14px;
  font-family: inherit;
  box-sizing: border-box;
  text-overflow: ellipsis; /* long placeholders ("What should the room call you?") clip cleanly at 320-375px */
  transition: border-color 0.15s, background 0.15s, box-shadow 0.15s;
}
.auth-v2 textarea.form-input {
  height: auto;
  min-height: 96px;
  padding: 12px 14px;
  line-height: 1.55;
  resize: vertical;
}
.auth-v2 .form-input::placeholder {
  color: var(--auth-placeholder);
}
.auth-v2 .form-input:hover:not(:focus):not([readonly]):not(:disabled) {
  border-color: var(--auth-stroke-hover);
  background: var(--auth-surface);
}
/* Chrome paints autofilled fields a fixed pale yellow that ignores background.
   The inset shadow trick is the only way to override it; on the dark sheet this
   painted a near-black slab, which is exactly the artefact we are removing. */
.auth-v2 .form-input:-webkit-autofill,
.auth-v2 .form-input:-webkit-autofill:hover,
.auth-v2 .form-input:-webkit-autofill:focus,
.auth-v2 .form-input:-webkit-autofill:active {
  -webkit-text-fill-color: var(--auth-ink);
  -webkit-box-shadow: 0 0 0 1000px var(--auth-surface) inset;
  caret-color: var(--auth-ink);
  transition: background-color 9999s ease-out;
}
.auth-v2 .form-input:focus {
  outline: none;
  border-color: var(--auth-accent);
  background: var(--auth-surface);
  box-shadow: 0 0 0 3px var(--auth-accent-ring);
}
.auth-v2 .form-input[readonly] {
  background: var(--auth-surface-3);
  cursor: not-allowed;
  color: var(--auth-muted);
}
/* A disabled field on these pages is never an empty field waiting for input —
   it is the account's own email, shown back so the person can confirm they are
   claiming the right one. That is content, so it takes the readable ink, not
   the placeholder grey it was inheriting. */
.auth-v2 .form-input:disabled {
  background: var(--auth-surface-3);
  color: var(--auth-muted);
  -webkit-text-fill-color: var(--auth-muted); /* Safari dims disabled inputs on its own */
  opacity: 1;
  cursor: not-allowed;
}
.auth-v2 .form-input[aria-invalid="true"],
.auth-v2 .form-input.is-invalid {
  border-color: var(--auth-danger);
}
.auth-v2 .form-input[aria-invalid="true"]:focus,
.auth-v2 .form-input.is-invalid:focus {
  box-shadow: 0 0 0 3px rgba(220, 38, 38, 0.14);
}
.auth-v2 .form-hint {
  font-size: 11px;
  color: var(--auth-muted);
  margin: 6px 0 0;
}

/* Primary + secondary buttons share these baseline shape rules so they
   line up perfectly when stacked. Only background / border / color differ. */
.auth-v2 .btn-primary,
.auth-v2 .btn-secondary {
  width: 100%;
  height: 46px;
  padding: 0 20px;
  border-radius: var(--auth-r-md);
  font-size: 14.5px;
  font-weight: 600;
  font-family: inherit;
  letter-spacing: -0.01em;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  text-decoration: none;
  transition: transform 0.1s, box-shadow 0.15s, background 0.15s, border-color 0.15s, color 0.15s;
  box-sizing: border-box;
}
/* The fill starts at --auth-accent-text, not --auth-accent: white on #6366F1
   measures 4.47:1 and misses AA for 14.5px text. This gradient clears it. */
.auth-v2 .btn-primary {
  background: linear-gradient(180deg, var(--auth-accent-text) 0%, var(--auth-accent-deep) 100%);
  color: #fff;
  border: none;
  box-shadow: var(--auth-shadow-btn);
}
.auth-v2 .btn-primary:hover {
  background: linear-gradient(180deg, #4438c9 0%, #382db0 100%);
  box-shadow: var(--auth-shadow-btn-hover);
}
.auth-v2 .btn-primary:active { transform: translateY(1px); box-shadow: var(--auth-shadow-btn); }
.auth-v2 .btn-primary:disabled {
  background: #b9b7ee;
  box-shadow: none;
  cursor: wait;
}
.auth-v2 .btn-secondary {
  background: var(--auth-surface);
  color: var(--auth-ink-2);
  border: 1px solid var(--auth-stroke-strong);
}
.auth-v2 .btn-secondary:hover {
  background: var(--auth-surface-3);
  border-color: var(--auth-stroke-hover);
  color: var(--auth-ink);
}
.auth-v2 .btn-secondary:active { transform: translateY(1px); }

/* OAuth buttons */
.auth-v2 .oauth-buttons {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin-bottom: 22px;
}
.auth-v2 .oauth-buttons form { margin: 0; }
.auth-v2 .btn-oauth {
  width: 100%;
  height: 44px;
  padding: 0 16px;
  background: var(--auth-surface);
  border: 1px solid var(--auth-stroke-strong);
  border-radius: var(--auth-r-md);
  color: var(--auth-ink);
  font-size: 14px;
  font-weight: 500;
  font-family: inherit;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  transition: background 0.15s, border-color 0.15s, box-shadow 0.15s, transform 0.1s;
  box-sizing: border-box;
}
.auth-v2 .btn-oauth:hover {
  background: var(--auth-surface-2);
  border-color: var(--auth-stroke-hover);
  box-shadow: 0 1px 2px rgba(17, 17, 24, 0.05);
}
.auth-v2 .btn-oauth:active { transform: translateY(1px); box-shadow: none; }
/* The GitHub mark is drawn with fill:currentColor and inherited white on the
   dark sheet. It now inherits near-black, which is the correct brand rendering
   on a light surface — but it must not go grey when the button is hovered. */
.auth-v2 .btn-oauth svg { flex: none; }

/* One visible keyboard ring for every interactive element on these pages.
   :focus-visible rather than :focus so a mouse click never draws it.

   Inputs are deliberately NOT in this list. They already announce focus with
   the 3px accent halo above, and `autofocus` counts as focus-visible in
   Chrome — so the first field of every form was rendering the halo AND an
   offset outline stacked on top of each other on page load. One ring per
   element; the input's ring is the box-shadow. */
.auth-v2 a:focus-visible,
.auth-v2 button:focus-visible,
.auth-v2 .btn-primary:focus-visible,
.auth-v2 .btn-secondary:focus-visible,
.auth-v2 .btn-oauth:focus-visible {
  outline: 2px solid var(--auth-accent-text);
  outline-offset: 2px;
  border-radius: var(--auth-r-sm);
}
.auth-v2 .btn-primary:focus-visible,
.auth-v2 .btn-secondary:focus-visible,
.auth-v2 .btn-oauth:focus-visible { border-radius: var(--auth-r-md); }

.auth-v2 .auth-divider {
  text-align: center;
  margin: 22px 0;
  position: relative;
  color: var(--auth-muted);
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.15em;
}
.auth-v2 .auth-divider::before,
.auth-v2 .auth-divider::after {
  content: "";
  position: absolute;
  top: 50%;
  width: 38%;
  height: 1px;
  background: linear-gradient(90deg, transparent, var(--auth-stroke-strong));
}
.auth-v2 .auth-divider::before { left: 0; }
.auth-v2 .auth-divider::after  { right: 0; background: linear-gradient(270deg, transparent, var(--auth-stroke-strong)); }

/* Status messages */
.auth-v2 .auth-error {
  background: var(--auth-danger-bg);
  border: 1px solid var(--auth-danger-stroke);
  color: #991b1b;
  padding: 10px 14px;
  border-radius: var(--auth-r-md);
  font-size: 13px;
  line-height: 1.5;
  margin-bottom: 20px;
}
.auth-v2 .auth-success {
  background: var(--auth-success-bg);
  border: 1px solid var(--auth-success-stroke);
  color: #14663f;
  padding: 10px 14px;
  border-radius: var(--auth-r-md);
  font-size: 13px;
  line-height: 1.5;
  margin-bottom: 20px;
}
.auth-v2 .auth-info {
  background: var(--auth-info-bg);
  border: 1px solid var(--auth-info-stroke);
  color: #3730a3;
  padding: 10px 14px;
  border-radius: var(--auth-r-md);
  font-size: 13px;
  line-height: 1.5;
  margin-bottom: 20px;
}

/* Footer link */
.auth-v2 .auth-footer {
  text-align: center;
  margin-top: 26px;
  font-size: 13px;
  color: var(--auth-muted);
}
.auth-v2 .auth-footer a {
  color: var(--auth-accent-text);
  text-decoration: none;
  font-weight: 600;
  transition: color 0.15s;
}
.auth-v2 .auth-footer a:hover { color: var(--auth-accent-deep); text-decoration: underline; }

/* Any inline link inside the card — several pages write <a> straight into a
   paragraph and relied on the old #a5b4fc, which is unreadable on white.
   `.auth-footer a` is excluded: this selector is more specific than the footer
   rule above, so without the exclusion it would win and the footer block would
   be dead code that silently ignores any future edit made to it. */
.auth-v2 .auth-shell a:not(.btn-primary):not(.btn-secondary):not(.btn-oauth):not(.auth-footer a) {
  color: var(--auth-accent-text);
  font-weight: 600;
  text-decoration: none;
  transition: color 0.15s;
}
.auth-v2 .auth-shell a:not(.btn-primary):not(.btn-secondary):not(.btn-oauth):hover {
  color: var(--auth-accent-deep);
  text-decoration: underline;
}

/* Lock icon circle for access-denied pages */
.auth-v2 .status-icon {
  width: 60px;
  height: 60px;
  margin: 0 auto 18px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  background: radial-gradient(circle at 50% 50%, rgba(99, 102, 241, 0.16) 0%, rgba(99, 102, 241, 0.07) 55%, transparent 80%);
}
.auth-v2 .status-icon svg {
  width: 30px;
  height: 30px;
  stroke: var(--auth-accent-text);
  fill: none;
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
}

/* Stacked CTAs on access-denied */
.auth-v2 .cta-stack {
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.auth-v2 .cta-stack form { margin: 0; }

/* ── Responsive ──────────────────────────────────────────────────────────────
   The card was fixed at 44/36 padding at every width, so on a 375px screen it
   left 12px of breathing room on each side and the heading wrapped awkwardly.
   Below 480px the card gives up its side padding and its own drop shadow —
   at that width it is the page, not a card floating on one. */
@media (max-width: 480px) {
  /* env() is 0 unless the page opted into viewport-fit=cover (guest-join did);
     it keeps the fineprint row above the home indicator / Chrome iOS bar. */
  .auth-v2 { padding: 16px 12px calc(16px + env(safe-area-inset-bottom)); align-items: flex-start; }
  .auth-v2 .auth-shell {
    padding: 32px 22px 26px;
    border-radius: 16px;
    margin-top: 24px;
  }
  .auth-v2 .auth-head { margin-bottom: 22px; }
  .auth-v2 .auth-head h1 { font-size: 20px; }
  .auth-v2 .auth-head .logo { margin-bottom: 18px; }
  .auth-v2 .auth-head .logo img { height: 28px; }
  /* 16px is the threshold below which iOS Safari zooms the viewport on focus. */
  .auth-v2 .form-input { font-size: 16px; }
}

/* Short viewports (landscape phones). The page scrolls by default now, but
   keep the explicit flex-start + margins: `safe center` above has no effect in
   browsers that don't parse it, and a centered card taller than a landscape
   viewport would clip its top. */
@media (max-height: 640px) {
  .auth-v2 { align-items: flex-start; overflow-y: auto; }
  .auth-v2 .auth-shell { margin: 20px 0; }
}

/* Reduced motion: kill particles, glow drift and the card entrance for users
   who prefer that. The card must still be visible — `animation: none` on a
   `both`-filled entrance would otherwise leave it at its from-state. */
@media (prefers-reduced-motion: reduce) {
  .auth-v2::after,
  .auth-particles .p { animation: none !important; }
  .auth-particles { display: none; }
  .auth-v2 .auth-shell { animation: none !important; opacity: 1; transform: none; }
}

/* Forced-colors (Windows high contrast): the ambient layers carry no meaning
   and the OS palette will not reproduce them faithfully. */
@media (forced-colors: active) {
  .auth-v2::before,
  .auth-v2::after,
  .auth-particles { display: none; }
  .auth-v2 .auth-shell { border: 1px solid CanvasText; }
}
