/* join.css — /join and /signup only (owner-approved v5 design, 2026-10-22;
 * v6 element order from the signup-checkout plan 10/23 §3.2: plan tiles
 * first, labelled email and password, the priced Continue, Google with the
 * same price under "or", one renewal line and the device fine print).
 *
 * One compact card (max 440px) that fits a 1280x860 screen without scrolling,
 * a minimal top bar (logo; "Have an account? Sign in" and the theme toggle)
 * and no site footer. Every control is one height (--jn-ctl).
 *
 * Themes: light by default (cream, a warm peach haze and a little mint, coral
 * sparkles); dark from prefers-color-scheme or the toggle (deep space, a faint
 * violet nebula, white twinkling stars and occasional 4-point glints). The
 * toggle sets <html data-theme> and pearl/join-theme.js remembers it. Every
 * token is --jn-* so nothing collides with hv2.css's :root aliases (--bg,
 * --text, ...), and every rule is scoped to body.join-body.
 */
:root {
  --jn-bg: #F6F5F2;
  --jn-haze:
    radial-gradient(760px 560px at 16% 10%, rgba(255, 207, 188, .55), rgba(255, 207, 188, 0) 70%),
    radial-gradient(760px 560px at 84% 72%, rgba(255, 138, 96, .20), rgba(255, 138, 96, 0) 68%),
    radial-gradient(620px 460px at 50% 40%, rgba(255, 230, 210, .55), rgba(255, 230, 210, 0) 70%),
    radial-gradient(520px 420px at 6% 96%, rgba(127, 227, 196, .12), rgba(127, 227, 196, 0) 70%);
  --jn-card: rgba(255, 255, 255, .78);
  --jn-card-border: rgba(28, 26, 34, .07);
  --jn-card-shadow: 0 24px 60px -28px rgba(120, 60, 30, .28), 0 2px 6px rgba(120, 60, 30, .05);
  --jn-text: #1C1A22;
  --jn-muted: #6B6872;
  --jn-line: #E4E1DB;
  --jn-field: #FFFFFF;
  --jn-field-hover: #CFCBC3;
  --jn-chip-on: #FFF6F1;
  --jn-tag-bg: #1C1A22;
  --jn-tag-text: #FFFFFF;
  --jn-coral: #FF8259;
  --jn-coral-2: #FFA17E;
  --jn-coral-deep: #BB4A2D;
  --jn-link: #BB4A2D;
  --jn-error: #C2364A;
  --jn-dialog: #F6F5F2;
  --jn-spark: #E0805C;
  --jn-spark-glow: rgba(255, 130, 89, .45);
  /* Friend reveal (#joinPerk, .join-plan__up) */
  --jn-perk-bg: linear-gradient(180deg, rgba(255, 130, 89, .11), rgba(255, 161, 126, .05));
  --jn-perk-line: rgba(255, 130, 89, .4);
  --jn-perk-glow: rgba(255, 130, 89, .55);
  --jn-perk-from: #FFFFFF;
  --jn-perk-to: linear-gradient(135deg, #FFA17E, #FF8259);
  --jn-perk-to-text: #2A140C;
  --jn-perk-chip-bg: #FFF1EA;
  --jn-perk-chip-text: #A63E22;
  --jn-perk-dot-a: #FF8259;
  --jn-perk-dot-b: #FFC2A8;
  --jn-perk-shine: rgba(255, 255, 255, .6);
  /* Tile transformation (the scratched-out names, the Best deal Plus tile) */
  --jn-strike: #FF6A3D;
  --jn-old: #8E8A96;
  --jn-deal-bg: linear-gradient(135deg, #FFB293, #FF7F55);
  --jn-deal-text: #2A140C;
  --jn-deal-glow: rgba(255, 106, 61, .5);
  --jn-deal-tint: linear-gradient(180deg, #FFF4ED, #FFFFFF);
  --jn-ctl: 46px;
  --jn-r: 12px;
  color-scheme: light;
}
:root[data-theme="dark"] {
  --jn-bg: #0C0B14;
  --jn-haze:
    radial-gradient(760px 560px at 22% 18%, rgba(124, 92, 214, .22), rgba(124, 92, 214, 0) 70%),
    radial-gradient(700px 520px at 80% 78%, rgba(255, 130, 89, .12), rgba(255, 130, 89, 0) 70%),
    radial-gradient(900px 700px at 50% 50%, rgba(40, 30, 80, .45), rgba(40, 30, 80, 0) 75%);
  --jn-card: rgba(24, 21, 36, .78);
  --jn-card-border: rgba(255, 255, 255, .08);
  --jn-card-shadow: 0 30px 80px -30px rgba(0, 0, 0, .8), 0 0 0 1px rgba(160, 140, 255, .06);
  --jn-text: #F2EFF8;
  --jn-muted: #A29DB3;
  --jn-line: rgba(255, 255, 255, .12);
  --jn-field: rgba(255, 255, 255, .05);
  --jn-field-hover: rgba(255, 255, 255, .24);
  --jn-chip-on: rgba(255, 130, 89, .12);
  --jn-tag-bg: #F2EFF8;
  --jn-tag-text: #15131B;
  --jn-coral-deep: #FF9A76;
  --jn-link: #FFB194;
  --jn-error: #FF8A9B;
  --jn-dialog: #15131F;
  --jn-spark: #FFFFFF;
  --jn-spark-glow: rgba(255, 255, 255, .5);
  --jn-perk-bg: linear-gradient(180deg, rgba(255, 130, 89, .17), rgba(140, 107, 232, .09));
  --jn-perk-line: rgba(255, 154, 118, .42);
  --jn-perk-glow: rgba(255, 154, 118, .6);
  --jn-perk-from: rgba(255, 255, 255, .08);
  --jn-perk-to: linear-gradient(135deg, #FF9A76, #FF8259);
  --jn-perk-to-text: #2A140C;
  --jn-perk-chip-bg: #2B1F2D;
  --jn-perk-chip-text: #FFB194;
  --jn-perk-dot-a: #FF9A76;
  --jn-perk-dot-b: #A98BFF;
  --jn-perk-shine: rgba(255, 255, 255, .2);
  --jn-strike: #FF9A76;
  --jn-old: #8F89A3;
  --jn-deal-bg: linear-gradient(135deg, #FFB194, #FF8259);
  --jn-deal-text: #2A140C;
  --jn-deal-glow: rgba(255, 154, 118, .55);
  --jn-deal-tint: linear-gradient(180deg, rgba(255, 130, 89, .16), rgba(255, 130, 89, .05));
  color-scheme: dark;
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --jn-bg: #0C0B14;
    --jn-haze:
      radial-gradient(760px 560px at 22% 18%, rgba(124, 92, 214, .22), rgba(124, 92, 214, 0) 70%),
      radial-gradient(700px 520px at 80% 78%, rgba(255, 130, 89, .12), rgba(255, 130, 89, 0) 70%),
      radial-gradient(900px 700px at 50% 50%, rgba(40, 30, 80, .45), rgba(40, 30, 80, 0) 75%);
    --jn-card: rgba(24, 21, 36, .78);
    --jn-card-border: rgba(255, 255, 255, .08);
    --jn-card-shadow: 0 30px 80px -30px rgba(0, 0, 0, .8), 0 0 0 1px rgba(160, 140, 255, .06);
    --jn-text: #F2EFF8;
    --jn-muted: #A29DB3;
    --jn-line: rgba(255, 255, 255, .12);
    --jn-field: rgba(255, 255, 255, .05);
    --jn-field-hover: rgba(255, 255, 255, .24);
    --jn-chip-on: rgba(255, 130, 89, .12);
    --jn-tag-bg: #F2EFF8;
    --jn-tag-text: #15131B;
    --jn-coral-deep: #FF9A76;
    --jn-link: #FFB194;
    --jn-error: #FF8A9B;
    --jn-dialog: #15131F;
    --jn-spark: #FFFFFF;
    --jn-spark-glow: rgba(255, 255, 255, .5);
    --jn-perk-bg: linear-gradient(180deg, rgba(255, 130, 89, .17), rgba(140, 107, 232, .09));
    --jn-perk-line: rgba(255, 154, 118, .42);
    --jn-perk-glow: rgba(255, 154, 118, .6);
    --jn-perk-from: rgba(255, 255, 255, .08);
    --jn-perk-to: linear-gradient(135deg, #FF9A76, #FF8259);
    --jn-perk-to-text: #2A140C;
    --jn-perk-chip-bg: #2B1F2D;
    --jn-perk-chip-text: #FFB194;
    --jn-perk-dot-a: #FF9A76;
    --jn-perk-dot-b: #A98BFF;
    --jn-perk-shine: rgba(255, 255, 255, .2);
    --jn-strike: #FF9A76;
    --jn-old: #8F89A3;
    --jn-deal-bg: linear-gradient(135deg, #FFB194, #FF8259);
    --jn-deal-text: #2A140C;
    --jn-deal-glow: rgba(255, 154, 118, .55);
    --jn-deal-tint: linear-gradient(180deg, rgba(255, 130, 89, .16), rgba(255, 130, 89, .05));
    color-scheme: dark;
  }
}

body.join-body {
  margin: 0;
  min-height: 100vh;
  min-height: 100dvh;
  display: flex;
  flex-direction: column;
  overflow-x: hidden;
  color: var(--jn-text);
  background: var(--jn-bg);
  font: 400 15px/1.5 'DM Sans', system-ui, -apple-system, sans-serif;
  -webkit-font-smoothing: antialiased;
}
.join-body *, .join-body *::before, .join-body *::after { box-sizing: border-box; }
/* Base resets at zero specificity (:where), so every component rule below wins. */
:where(.join-body) a { color: var(--jn-link); text-underline-offset: 3px; }
:where(.join-body) button, :where(.join-body) input { font: inherit; color: inherit; }
:where(.join-body) :focus-visible { outline: 2px solid var(--jn-coral); outline-offset: 2px; }
.join-body [hidden] { display: none !important; }

/* Haze and sparkles sit behind everything. */
.join-haze {
  position: fixed; inset: -10%; z-index: 0; pointer-events: none;
  background: var(--jn-haze);
  animation: jn-drift 26s ease-in-out infinite alternate;
}
@keyframes jn-drift { from { transform: translate3d(0, 0, 0) scale(1); } to { transform: translate3d(-2%, 1.5%, 0) scale(1.05); } }
.join-sky { position: fixed; inset: 0; z-index: 0; pointer-events: none; }
.join-sky i {
  position: absolute; width: 2px; height: 2px; border-radius: 50%;
  background: var(--jn-spark); opacity: .2;
  animation: jn-twinkle var(--d, 5s) ease-in-out infinite; animation-delay: var(--dl, 0s);
}
.join-sky i.big { width: 3px; height: 3px; box-shadow: 0 0 6px 1px var(--jn-spark-glow); }
.join-sky b {
  position: absolute; width: 12px; height: 12px; opacity: 0;
  animation: jn-glint var(--d, 7s) ease-in-out infinite; animation-delay: var(--dl, 0s);
}
.join-sky b::before, .join-sky b::after {
  content: ''; position: absolute; left: 50%; top: 50%;
  background: var(--jn-spark); border-radius: 1px; transform: translate(-50%, -50%);
}
.join-sky b::before { width: 12px; height: 1.4px; }
.join-sky b::after { width: 1.4px; height: 12px; }
@keyframes jn-twinkle { 0%, 100% { opacity: .15; } 50% { opacity: .85; } }
@keyframes jn-glint {
  0%, 70%, 100% { opacity: 0; transform: scale(.4) rotate(0deg); }
  80% { opacity: .95; transform: scale(1) rotate(20deg); }
  90% { opacity: 0; transform: scale(.5) rotate(40deg); }
}
@media (prefers-reduced-motion: reduce) {
  .join-haze, .join-sky i, .join-sky b { animation: none; }
  .join-sky b { display: none; }
}

/* Minimal top bar */
.join-top {
  position: relative; z-index: 1;
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  padding: 18px clamp(16px, 4vw, 40px);
}
.join-logo {
  display: inline-flex; align-items: center; gap: 8px;
  text-decoration: none; color: var(--jn-text);
  font: 500 1.25rem/1 'Newsreader', Georgia, serif;
}
.join-logo .nav-logo-mark { width: 24px; height: 24px; }
.join-logo b { color: var(--jn-coral-deep); font-weight: 500; }
.join-top__right { display: inline-flex; align-items: center; gap: 14px; }
.join-signin { color: var(--jn-muted); text-decoration: none; font-size: 14px; white-space: nowrap; }
.join-signin b { color: var(--jn-text); font-weight: 600; }
.join-theme {
  width: 32px; height: 32px; flex: none;
  display: grid; place-items: center;
  border-radius: 999px; border: 1px solid var(--jn-line);
  background: transparent; color: var(--jn-text); cursor: pointer; padding: 0;
}
.join-theme svg { width: 16px; height: 16px; }
.join-theme .join-theme__sun { display: none; }
:root[data-theme="dark"] .join-theme .join-theme__sun { display: block; }
:root[data-theme="dark"] .join-theme .join-theme__moon { display: none; }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .join-theme .join-theme__sun { display: block; }
  :root:not([data-theme="light"]) .join-theme .join-theme__moon { display: none; }
}

/* The card */
.join-main {
  position: relative; z-index: 1; flex: 1;
  display: flex; align-items: center; justify-content: center;
  padding: 8px 16px 40px;
}
.join-card {
  width: min(440px, 100%); min-width: 0;
  background: var(--jn-card); border: 1px solid var(--jn-card-border); border-radius: 20px;
  padding: 28px 28px 22px;
  box-shadow: var(--jn-card-shadow);
  -webkit-backdrop-filter: blur(18px) saturate(1.2); backdrop-filter: blur(18px) saturate(1.2);
}
.join-card h1 {
  margin: 0; text-align: center; color: var(--jn-text);
  font: 500 1.7rem/1.12 'Newsreader', Georgia, serif; letter-spacing: -.02em;
  text-wrap: balance;
}
.join-lede { margin: 8px 0 18px; text-align: center; color: var(--jn-muted); font-size: 13.5px; line-height: 1.45; text-wrap: balance; }
/* /join/resume: the friend upgrade note under the plan line (join-resume.js, static). */
.join-perk-note { margin: -10px 0 4px; text-align: center; color: var(--jn-coral-deep); font-size: 12.5px; font-weight: 600; line-height: 1.4; }
/* Where the visitor came from (only when the link says): one quiet pill. */
.join-context {
  display: table; margin: -4px auto 12px; padding: 4px 11px;
  border-radius: 999px; background: var(--jn-chip-on); border: 1px solid var(--jn-card-border);
  color: var(--jn-coral-deep); font-size: 12px; font-weight: 600; line-height: 1.4; text-align: center;
}

.join-form { display: flex; flex-direction: column; margin: 0; }

/* Continue with Google, in the slot #1029 reserved ([data-google-slot]). */
.join-google-block { display: flex; flex-direction: column; }
.join-google-block:has(> .join-google:empty) { display: none; }
.join-google__btn {
  width: 100%; height: var(--jn-ctl);
  display: flex; align-items: center; justify-content: center; gap: 10px;
  padding: 0 16px; white-space: nowrap;
  background: var(--jn-field); border: 1px solid var(--jn-line); border-radius: var(--jn-r);
  color: var(--jn-text); font-weight: 600; cursor: pointer;
}
.join-google__btn:hover:not(:disabled) { border-color: var(--jn-field-hover); }
.join-google__btn:disabled { opacity: .65; cursor: default; }
.join-google__btn svg { width: 18px; height: 18px; flex: none; }
.join-google-note { margin: 5px 0 0; text-align: center; color: var(--jn-muted); font-size: 11.5px; }
.join-google__msg { margin: 8px 0 0; text-align: center; font-size: 13px; line-height: 1.45; color: var(--jn-error); }
.join-google__msg a { color: inherit; font-weight: 600; }
.join-or { display: flex; align-items: center; gap: 12px; margin: 12px 0; color: var(--jn-muted); font-size: 12.5px; }
.join-or::before, .join-or::after { content: ''; flex: 1; height: 1px; background: var(--jn-line); }

/* Email, password, promo: visible labels (§3.2), helper text under the field. */
.join-field { display: flex; flex-direction: column; margin: 0 0 12px; }
.join-label { margin: 0 0 5px; font-size: 13px; font-weight: 600; color: var(--jn-text); }
.join-field input {
  width: 100%; min-width: 0; height: var(--jn-ctl); padding: 0 14px;
  background: var(--jn-field); border: 1px solid var(--jn-line); border-radius: var(--jn-r);
  color: var(--jn-text); font-size: 15px;
}
.join-field input::placeholder { color: var(--jn-muted); opacity: .85; }
.join-field input:hover { border-color: var(--jn-field-hover); }
.join-field input:focus { border-color: var(--jn-coral); outline: none; box-shadow: 0 0 0 3px rgba(255, 130, 89, .18); }
.join-field input[aria-invalid="true"] { border-color: var(--jn-error); }
/* Errors are inline text, never a box. */
.join-field-err, .join-error { margin: 6px 0 0; font-size: 13px; line-height: 1.4; color: var(--jn-error); }
.join-error { text-align: center; }
.join-hint { margin: 6px 0 0; font-size: 12.5px; line-height: 1.45; color: var(--jn-muted); }
#joinPasswordHelp { margin-top: 5px; font-size: 12px; }
#joinPasswordHelp.join-hint--met { color: var(--jn-text); }
#joinPasswordHelp.join-hint--met::before { content: '\2713\00a0'; color: var(--jn-coral-deep); font-weight: 700; }
.join-suggest { color: var(--jn-text); }
.join-suggest .join-link { font-size: inherit; font-weight: 600; }
/* 409: the email already has an account; the form becomes sign-in. */
.join-exists {
  margin: 0 0 4px; padding: 10px 12px; border-radius: var(--jn-r);
  background: var(--jn-chip-on); border: 1px solid var(--jn-card-border);
  font-size: 13px; line-height: 1.45; color: var(--jn-text);
}
.join-exists b { font-weight: 600; word-break: break-all; }
.join-exists a { font-weight: 600; white-space: nowrap; }
.join-pw { position: relative; }
.join-pw input { padding-right: 64px; }
.join-pw__show {
  position: absolute; right: 6px; top: 50%; transform: translateY(-50%);
  height: 34px; padding: 0 10px; border: 0; border-radius: 8px; background: none;
  color: var(--jn-muted); font-size: 13px; font-weight: 600; cursor: pointer;
}
.join-pw__show:hover { color: var(--jn-text); }

/* Plan: three compact tiles */
.join-plans { margin: 6px 0 0; padding: 0; border: 0; min-width: 0; }
.join-plans__label { display: flex; gap: 4px; align-items: baseline; margin: 0 0 8px; font-size: 13px; font-weight: 600; }
.join-plans__label span { font-weight: 400; color: var(--jn-muted); }
.join-plans__row { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 8px; }
.join-plan {
  position: relative; min-width: 0; min-height: 74px;
  display: flex; flex-direction: column; align-items: flex-start; gap: 2px;
  padding: 10px 10px 9px; text-align: left; cursor: pointer;
  background: var(--jn-field); border: 1px solid var(--jn-line); border-radius: var(--jn-r);
  color: var(--jn-text);
}
.join-plan { transition: border-color .15s ease, box-shadow .15s ease, background-color .15s ease, transform .15s ease; }
.join-plan:hover { border-color: var(--jn-field-hover); }
.join-plan:active { transform: scale(.985); }
.join-plan[aria-pressed="true"] {
  border: 1.5px solid transparent;
  background: linear-gradient(var(--jn-chip-on), var(--jn-chip-on)) padding-box, linear-gradient(var(--jn-tile-base), var(--jn-tile-base)) padding-box, linear-gradient(135deg, var(--jn-ring-a), var(--jn-ring-b) 55%, var(--jn-ring-c)) border-box;
  box-shadow: 0 10px 22px -16px rgba(255, 110, 70, .9);
}
/* The gradient ring sits on an opaque tile fill, so the tile can't be see-through. */
:root { --jn-ring-a: #FF8259; --jn-ring-b: #FFA17E; --jn-ring-c: #FFD2BC; --jn-tile-base: #FFFFFF; }
:root[data-theme="dark"] { --jn-ring-a: #FF9A76; --jn-ring-b: #FF8259; --jn-ring-c: #8C6BE8; --jn-tile-base: #1B1828; }
@media (prefers-color-scheme: dark) { :root:not([data-theme="light"]) { --jn-ring-a: #FF9A76; --jn-ring-b: #FF8259; --jn-ring-c: #8C6BE8; --jn-tile-base: #1B1828; } }
/* The chosen tile carries a small check, top right inside the tile. */
.join-plan::after {
  content: ''; position: absolute; top: 10px; right: 9px; width: 16px; height: 16px; border-radius: 999px;
  border: 1.5px solid var(--jn-line); background: transparent;
  transition: background-color .15s ease, border-color .15s ease;
}
.join-plan[aria-pressed="true"]::after {
  animation: jn-check .12s ease-out;
  border-color: var(--jn-coral-deep);
  background: var(--jn-coral-deep) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M4.2 8.3l2.4 2.4 5-5.2' fill='none' stroke='white' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") center / 12px no-repeat;
}
.join-plan b { font-size: 14px; font-weight: 700; }
.join-plan__price { font-size: 17px; font-weight: 600; font-variant-numeric: tabular-nums; letter-spacing: -.01em; line-height: 1.25; }
.join-plan__price small { color: var(--jn-muted); font-size: 11.5px; font-weight: 400; letter-spacing: 0; }
.join-plan__what { font-size: 11.5px; line-height: 1.35; color: var(--jn-muted); }
/* Credits (owner 2026-10-28): what each plan buys, the line under the price (it replaced
   the "1–2 classes" / "3–5 classes" / infinity-mark lines). */
.join-plan__credits { font-size: 11.5px; font-weight: 600; line-height: 1.35; color: var(--jn-text); font-variant-numeric: tabular-nums; white-space: nowrap; }
@media (prefers-reduced-motion: reduce) { .join-inf__run { animation: none; stroke-dasharray: none; } }
.join-plan__tag {
  position: absolute; top: -8px; right: 8px; padding: 1px 7px; border-radius: 999px;
  background: var(--jn-tag-bg); color: var(--jn-tag-text);
  font-size: 10px; font-weight: 600; letter-spacing: .02em; white-space: nowrap;
}

/* "Compare plans" · "Have a code?", centred under the tiles */
.join-links { display: flex; justify-content: center; align-items: center; gap: 8px; margin: 10px 0 14px; }
.join-links__dot { color: var(--jn-muted); font-size: 13px; }
.join-link {
  margin: 0; padding: 0; border: 0; background: none; cursor: pointer;
  color: var(--jn-link); font-size: 13px; text-decoration: underline; text-underline-offset: 3px;
}
.join-links .join-compare-trigger { margin: 0; padding: 0; font: inherit; font-size: 13px; color: var(--jn-link); text-underline-offset: 3px; font-weight: 400; display: inline; }
.join-links:has(+ .join-promo:not([hidden])) { margin-bottom: 0; }
.join-promo { margin: 10px 0 14px; }
.join-promo__row { display: flex; gap: 8px; }
.join-promo__row input { flex: 1 1 auto; }
.join-promo__apply {
  flex: 0 0 auto; height: var(--jn-ctl); padding: 0 16px;
  border: 1px solid var(--jn-line); border-radius: var(--jn-r); background: var(--jn-field);
  color: var(--jn-text); font-weight: 600; cursor: pointer;
}
.join-promo__apply:hover { border-color: var(--jn-field-hover); }
.join-promo__apply:disabled { opacity: .65; cursor: default; }
.join-promo .join-hint { margin-top: 6px; }

/* ── Friend reveal (Referrals v2, 2026-10-26) ──────────────────────────────
 * A friend's code shows #joinPerk under the promo status (A friend sent you a
 * free upgrade · Base → Plus · Plus → Pro) and a "<To> first month" chip on
 * the Base and Plus tiles. The resting styles ARE the finished state. The
 * motion exists only while join.js has `is-play` on the banner and
 * `join--perk-play` on the form (~3.4 s after a FRESH accept), so a restored
 * code, a repeat Apply and prefers-reduced-motion just show the result.
 * Transform, opacity and stroke-dashoffset move, plus one grid-rows open.
 * Nothing above the promo field changes size: the chips hang off the tiles'
 * bottom edge, so someone typing a code never watches the field jump.
 * Calm by rule (owner 2026-10-26: no overlap at any frame): nothing scales past
 * its own box, no two texts share a place while both are visible (the old one
 * is gone before the new one arrives), and each step starts after the one
 * before it has landed. The banner and the tiles take turns, Base then Plus:
 * 0 s card opens · .2 s Base slides in, the arrow draws, .6 s Plus settles
 * (one ring) · .65 s row 2 repeats for Plus → Pro · 1.5 s one shimmer · done
 * by ~1.8 s (the tiles' own steps are in the next block). */
.join-perk { display: grid; grid-template-rows: 1fr; }
.join-perk__clip { min-height: 0; overflow: hidden; }
.join-perk__card {
  position: relative; overflow: hidden; margin-top: 10px; padding: 12px 12px 10px;
  background: var(--jn-perk-bg); border: 1px solid var(--jn-perk-line); border-radius: var(--jn-r);
}
.join-perk__title {
  display: flex; align-items: center; justify-content: center; gap: 8px; margin: 0;
  color: var(--jn-text); font-size: 13.5px; font-weight: 700; line-height: 1.3; text-align: center; text-wrap: balance;
}
.join-perk__gift { width: 20px; height: 20px; flex: none; overflow: visible; color: var(--jn-coral-deep); }
.join-perk__lid { transform-box: fill-box; transform-origin: 50% 100%; }
.join-perk__ladder {
  list-style: none; margin: 10px 0 0; padding: 0;
  display: flex; flex-wrap: wrap; justify-content: center; gap: 8px 18px;
}
.join-perk__row { position: relative; display: inline-flex; align-items: center; gap: 6px; transition: opacity .4s ease; }
/* The row for the chosen tile is lit; the other is quiet (Pro chosen: both).
   While the reveal plays both rows are lit (it teaches both upgrades), then
   the quiet one settles back (jn-perk-settle; its end frame is this rule). */
.join-perk__row[data-active="false"] { opacity: .55; }
.join-perk__row:nth-child(1) { --perk-t: .2s; }
.join-perk__row:nth-child(2) { --perk-t: .65s; }
.join-perk__chip {
  position: relative; display: inline-flex; align-items: center; min-height: 26px; padding: 0 11px;
  border: 1px solid transparent; border-radius: 999px;
  font-size: 12.5px; font-weight: 700; line-height: 1; white-space: nowrap;
}
.join-perk__chip--from { background: var(--jn-perk-from); border-color: var(--jn-line); color: var(--jn-text); }
.join-perk__chip--to { background: var(--jn-perk-to); color: var(--jn-perk-to-text); box-shadow: 0 6px 14px -8px var(--jn-perk-glow); }
.join-perk__arrow { width: 34px; height: 14px; flex: none; overflow: visible; color: var(--jn-coral-deep); }
.join-perk__arrow path { fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; stroke-dasharray: 100; stroke-dashoffset: 0; }
.join-perk__fine { margin: 10px 0 0; color: var(--jn-muted); font-size: 11.5px; line-height: 1.45; text-align: center; text-wrap: balance; }
/* Terms §17 link: wraps words already in the line, so the banner's height never changes. */
.join-perk__fine a { color: var(--jn-link); }
/* The "Plus" / "Pro" chip's one soft ring pulse (decorative, off at rest). */
.join-perk__chip--to::before { content: ''; position: absolute; inset: 0; border: 2px solid var(--jn-perk-dot-a); border-radius: inherit; opacity: 0; pointer-events: none; }
/* One shimmer across the card, then rest. */
.join-perk__card::after {
  content: ''; position: absolute; top: 0; bottom: 0; left: 0; width: 38%; opacity: 0; pointer-events: none;
  background: linear-gradient(90deg, transparent, var(--jn-perk-shine), transparent);
  transform: translateX(-120%) skewX(-18deg);
}

/* "<To> first month" under the Base and Plus tiles: hangs off the bottom edge. */
.join-plan__up {
  position: absolute; left: 0; right: 0; bottom: -9px; z-index: 1;
  width: max-content; max-width: calc(100% + 6px); margin: 0 auto; padding: 2px 8px;
  overflow: hidden; white-space: nowrap; text-align: center; pointer-events: none;
  background: var(--jn-perk-chip-bg); color: var(--jn-perk-chip-text);
  border: 1px solid var(--jn-perk-line); border-radius: 999px;
  font-size: 11px; font-weight: 700; line-height: 1.25; letter-spacing: 0;
  box-shadow: 0 3px 8px -5px var(--jn-perk-glow);
}

/* The motion (fresh accept only). Each end frame equals the resting style above. */
.join-perk.is-play { animation: jn-perk-open .4s ease-out backwards; }
.join-perk.is-play .join-perk__card { animation: jn-perk-pop .45s ease-out backwards; }
.join-perk.is-play .join-perk__card::after { animation: jn-perk-shine .8s ease-in-out 1.2s backwards; }
.join-perk.is-play .join-perk__lid { animation: jn-perk-lid .6s ease-in-out .25s backwards; }
.join-perk.is-play .join-perk__chip--from { animation: jn-perk-from .3s ease-out var(--perk-t, .2s) backwards; }
.join-perk.is-play .join-perk__arrow path { animation: jn-perk-draw .35s ease-out calc(var(--perk-t, .2s) + .15s) backwards; }
.join-perk.is-play .join-perk__chip--to { animation: jn-perk-to .4s ease-out calc(var(--perk-t, .2s) + .4s) backwards; }
.join-perk.is-play .join-perk__chip--to::before { animation: jn-perk-ring .6s ease-out calc(var(--perk-t, .2s) + .5s) backwards; }
.join-perk.is-play .join-perk__row[data-active="false"] { animation: jn-perk-settle .4s ease 1.35s backwards; }
@keyframes jn-perk-open { from { grid-template-rows: 0fr; } to { grid-template-rows: 1fr; } }
@keyframes jn-perk-pop { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: none; } }
@keyframes jn-perk-lid { 0% { transform: none; } 40% { transform: translateY(-2px) rotate(-8deg); } 100% { transform: none; } }
@keyframes jn-perk-from { from { opacity: 0; transform: translateX(-8px); } to { opacity: 1; transform: none; } }
@keyframes jn-perk-draw { from { stroke-dashoffset: 100; } to { stroke-dashoffset: 0; } }
@keyframes jn-perk-to { from { opacity: 0; transform: scale(.9); } to { opacity: 1; transform: none; } }
@keyframes jn-perk-ring { 0% { opacity: 0; transform: scale(1); } 20% { opacity: .7; } 100% { opacity: 0; transform: scale(1.25, 1.5); } }
@keyframes jn-perk-shine { 0% { opacity: 0; transform: translateX(-120%) skewX(-18deg); } 12%, 88% { opacity: 1; } 100% { opacity: 0; transform: translateX(340%) skewX(-18deg); } }
@keyframes jn-perk-settle { from { opacity: 1; } to { opacity: .55; } }

/* ── The free-month reveal (owner 2026-10-28: "really cool and apparent it is
 * free for a month") ───────────────────────────────────────────────────────────
 * A code with a free-month seat shows #joinFree: "Code unlocked", a big FREE,
 * the plan's price scratched to $0, and three ticks. The seat's tile gets a
 * "Free for N days" chip and the submit button glows. The resting styles ARE the
 * finished state; the motion runs only while join.js has `is-play` on the card
 * and `join--free-play` on the form (~3.2 s after a FRESH accept):
 * 0 s card pops · .15 s lock opens · .35 s burst + FREE stamps in · .9 s old
 * price scratched · 1.15 s $0 drops in · 1.4 s plan line · 1.55 s ticks ·
 * 1.9 s one shimmer · the glow under FREE breathes on. */
.join-free { display: grid; grid-template-rows: 1fr; }
.join-free__clip { min-height: 0; overflow: hidden; padding: 0 2px 16px; margin-bottom: -12px; }
.join-free__card {
  position: relative; overflow: hidden; isolation: isolate; margin-top: 10px; padding: 14px 14px 13px;
  text-align: center; border-radius: calc(var(--jn-r) + 4px);
  background: radial-gradient(120% 90% at 50% 0%, rgba(255, 161, 126, .28), transparent 60%), var(--jn-perk-bg);
  border: 1.5px solid var(--jn-perk-line);
  box-shadow: 0 10px 22px -16px var(--jn-perk-glow);
}
.join-free__card > :not(.join-free__burst) { position: relative; z-index: 1; }
.join-free__kicker {
  display: inline-flex; align-items: center; gap: 6px; margin: 0; padding: 3px 10px 3px 8px;
  border-radius: 999px; background: var(--jn-perk-chip-bg); color: var(--jn-perk-chip-text);
  font-size: 11px; font-weight: 800; letter-spacing: .08em; text-transform: uppercase;
}
.join-free__lock { width: 14px; height: 14px; flex: none; overflow: visible; }
.join-free__shackle { transform-box: fill-box; transform-origin: 0% 100%; }
.join-free__head { display: flex; align-items: baseline; justify-content: center; gap: 8px; margin: 8px 0 0; line-height: 1; }
.join-free__word {
  position: relative; font-size: clamp(40px, 12vw, 52px); font-weight: 900; letter-spacing: -.02em;
  background: linear-gradient(100deg, #FF6A3D 0%, #FFA17E 35%, #8C6BE8 70%, #FF6A3D 100%) 0 0 / 200% 100%;
  -webkit-background-clip: text; background-clip: text; color: transparent;
  filter: drop-shadow(0 6px 14px var(--jn-perk-glow));
  animation: jn-free-flow 6s linear infinite;
}
.join-free__for { color: var(--jn-text); font-size: 17px; font-weight: 800; }
.join-free__price { display: flex; align-items: baseline; justify-content: center; gap: 10px; margin: 8px 0 0; }
.join-free__old { position: relative; color: var(--jn-muted); font-size: 15px; font-weight: 700; }
.join-free__old::after {
  content: ''; position: absolute; left: -3px; right: -3px; top: 52%; height: 2px; border-radius: 2px;
  background: var(--jn-coral-deep); transform-origin: 0 50%;
}
.join-free__new { color: var(--jn-text); font-size: 26px; font-weight: 900; letter-spacing: -.01em; }
.join-free__plan { margin: 4px 0 0; color: var(--jn-text); font-size: 13px; font-weight: 700; }
.join-free__ticks {
  list-style: none; margin: 10px 0 0; padding: 0;
  display: flex; flex-wrap: wrap; justify-content: center; gap: 6px 14px;
  color: var(--jn-muted); font-size: 12px; font-weight: 600;
}
.join-free__ticks li { display: inline-flex; align-items: center; gap: 5px; }
.join-free__ticks li::before {
  content: '✓'; display: inline-grid; place-items: center; width: 15px; height: 15px; border-radius: 50%;
  background: var(--jn-perk-to); color: var(--jn-perk-to-text); font-size: 10px; font-weight: 900;
}
.join-free__ticks li:nth-child(1) { --ft: 1.55s; }
.join-free__ticks li:nth-child(2) { --ft: 1.67s; }
.join-free__ticks li:nth-child(3) { --ft: 1.79s; }
/* One shimmer across the card, then rest. */
.join-free__card::after {
  content: ''; position: absolute; top: 0; bottom: 0; left: 0; z-index: 2; width: 34%; opacity: 0; pointer-events: none;
  background: linear-gradient(90deg, transparent, var(--jn-perk-shine), transparent);
  transform: translateX(-120%) skewX(-18deg);
}
/* The confetti: built once by join.js, invisible at rest. */
.join-free__burst { position: absolute; left: 50%; top: 42%; z-index: 0; width: 0; height: 0; pointer-events: none; }
.join-free__burst i { position: absolute; left: -4px; top: -2px; width: 8px; height: 4px; border-radius: 1px; opacity: 0; }
.join-free__burst i.is-dot { width: 6px; height: 6px; border-radius: 50%; }
/* The seat's tile: its chip reads in the same coral as the card. */
.join-plan__free { background: var(--jn-perk-to); color: var(--jn-perk-to-text); border-color: transparent; }
/* The button names $0 and keeps a soft glow while the free month is applied. */
.join--free .join-submit { box-shadow: 0 10px 26px -10px var(--jn-perk-glow), 0 0 0 0 var(--jn-perk-glow); }

/* The motion (fresh accept only). Each end frame equals the resting style above. */
.join-free.is-play { animation: jn-perk-open .4s ease-out backwards; }
.join-free.is-play .join-free__card { animation: jn-free-pop .55s cubic-bezier(.2, 1.4, .4, 1) backwards; }
.join-free.is-play .join-free__card::after { animation: jn-perk-shine .9s ease-in-out 1.9s backwards; }
.join-free.is-play .join-free__kicker { animation: jn-perk-pop .35s ease-out .1s backwards; }
.join-free.is-play .join-free__shackle { animation: jn-free-unlock .5s ease-in-out .15s backwards; }
.join-free.is-play .join-free__word { animation: jn-free-stamp .6s cubic-bezier(.2, 1.5, .35, 1) .35s backwards, jn-free-flow 6s linear infinite; }
.join-free.is-play .join-free__for { animation: jn-perk-from .35s ease-out .7s backwards; }
.join-free.is-play .join-free__old { animation: jn-perk-pop .3s ease-out .75s backwards; }
.join-free.is-play .join-free__old::after { animation: jn-free-strike .3s cubic-bezier(.6, .05, .25, 1) .95s backwards; }
.join-free.is-play .join-free__new { animation: jn-free-drop .5s cubic-bezier(.2, 1.6, .4, 1) 1.15s backwards; }
.join-free.is-play .join-free__plan { animation: jn-perk-pop .35s ease-out 1.4s backwards; }
.join-free.is-play .join-free__ticks li { animation: jn-perk-to .3s ease-out var(--ft, 1.55s) backwards; }
.join-free.is-play .join-free__burst i { animation: jn-free-burst 1.3s cubic-bezier(.15, .7, .3, 1) calc(.35s + var(--bd, 0ms)) backwards; }
.join--free-play .join-plan__free { animation: jn-chip-in .3s ease-out 1.3s backwards; }
.join--free-play .join-submit { animation: jn-free-glow 1.2s ease-out 1.6s; }
@keyframes jn-free-pop { from { opacity: 0; transform: translateY(10px) scale(.94); } to { opacity: 1; transform: none; } }
@keyframes jn-free-unlock { 0% { transform: none; } 45% { transform: translateY(-2.5px) rotate(-14deg); } 100% { transform: none; } }
@keyframes jn-free-stamp { 0% { opacity: 0; transform: scale(2.2) rotate(-6deg); } 60% { opacity: 1; } 100% { opacity: 1; transform: none; } }
@keyframes jn-free-strike { from { transform: scaleX(0); } to { transform: scaleX(1); } }
@keyframes jn-free-drop { 0% { opacity: 0; transform: translateY(-16px) scale(.6); } 100% { opacity: 1; transform: none; } }
@keyframes jn-free-flow { from { background-position: 0 0; } to { background-position: -200% 0; } }
@keyframes jn-free-burst {
  0% { opacity: 1; transform: translate(0, 0) rotate(0); }
  70% { opacity: 1; }
  100% { opacity: 0; transform: translate(var(--bx), calc(var(--by) + 60px)) rotate(var(--br)); }
}
@keyframes jn-free-glow {
  0% { box-shadow: 0 10px 26px -10px var(--jn-perk-glow), 0 0 0 0 var(--jn-perk-glow); }
  100% { box-shadow: 0 10px 26px -10px var(--jn-perk-glow), 0 0 0 14px transparent; }
}
@media (prefers-reduced-motion: reduce) {
  .join-free, .join-free *, .join-free *::before, .join-free *::after, .join-plan__free, .join--free .join-submit { animation: none !important; }
}

/* ── The tiles change with the code (owner 2026-10-26) ─────────────────────────
 * A friend's code moves every tile up one plan for the first month: on the Base
 * tile "Base" is scratched out and "Plus" settles in beside it, "2,000 credits" is
 * scratched out and Plus's own credits line takes its place; on the Plus tile the
 * same for "Pro" and "Unlimited credits". The Plus tile is THE deal (Best deal ribbon,
 * a lift and a glow, Pro's real price scratched out in its chip) and the Pro
 * tile steps back (still a normal, selectable tile).
 * join.js ADDS nodes (.join-plan__to, __credits--to, __deal, __note, __up) and the
 * classes is-upgraded / is-deal / is-dim; it never rewrites the tile's own text,
 * so removing the code removes exactly what it added. The tile's price stays
 * the PAID price. The resting styles ARE the finished state; the motion is only
 * the `join--perk-play` animations below, whose end frames equal the resting
 * styles (so the timer ending the class never pops anything).
 * Height: the upgraded tiles keep their three rows (the two names share row
 * one, the two descriptors stack in row three), so nothing above the promo
 * field moves when a code is accepted or edited.
 * Timeline (--tt = the tile's start; Base .25 s, Plus .5 s, Pro's note .95 s).
 * One thing happens in a place at a time: +0 old name scratched · +.15 new name
 * settles in beside it (its own cell, nothing scales over the price) · +.3 old
 * line scratched · +.52 old line gone · +.7 new line in (the old one has left
 * the cell, so the two never show at once) · +.8 first-month chip. Plus extras:
 * the "Most students" tag leaves +.55 and the ribbon + lift arrive +.75 (never
 * both at the top edge), one ring at +.85. */
.join-plan { --jn-scratch: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 8' preserveAspectRatio='none'%3E%3Cpath d='M1.5 5.6C9 2.9 17 7 27 4.4s24 2 35.5-1.8' fill='none' stroke='%23000' stroke-width='1.9' stroke-linecap='round'/%3E%3C/svg%3E"); }
.join-plan[data-plan="starter"] { --tt: .25s; }
.join-plan[data-plan="standard"] { --tt: .5s; }
.join-plan[data-plan="pro"] { --tt: .95s; }
.join-plan.is-upgraded {
  display: grid; grid-template-columns: auto minmax(0, 1fr); grid-auto-rows: min-content;
  align-content: start; justify-items: start; align-items: baseline; column-gap: 5px; row-gap: 2px;
}
.join-plan.is-upgraded > b { grid-column: 1; grid-row: 1; position: relative; color: var(--jn-old); }
.join-plan.is-upgraded > .join-plan__to {
  grid-column: 2; grid-row: 1; min-width: 0; transform-origin: 0 70%;
  color: var(--jn-coral-deep); font-size: 14px; font-weight: 700; line-height: 1.5; white-space: nowrap;
}
.join-plan.is-upgraded > .join-plan__price { grid-column: 1 / -1; grid-row: 2; }
.join-plan.is-upgraded > .join-plan__credits { grid-column: 1 / -1; grid-row: 3; }
/* The old credits line stays in its cell (so the tile keeps its height) and fades out;
   the first month's line takes the same cell. */
.join-plan.is-upgraded > .join-plan__credits:not(.join-plan__credits--to) { position: relative; opacity: 0; }
/* The hand-drawn scratch: a slightly wobbly coral stroke across the old text. */
.join-plan.is-upgraded > b::after, .join-plan.is-upgraded > .join-plan__credits:not(.join-plan__credits--to)::after {
  content: ''; position: absolute; left: -3px; right: -3px; top: 50%; height: 8px; margin-top: -4px;
  background: var(--jn-strike); transform: rotate(-4deg); pointer-events: none;
  -webkit-mask: var(--jn-scratch) center / 100% 100% no-repeat; mask: var(--jn-scratch) center / 100% 100% no-repeat;
}
/* The chip on the Plus tile carries Pro's real price, scratched out. */
.join-plan__up s { text-decoration: line-through; text-decoration-thickness: 1.2px; text-decoration-color: var(--jn-strike); }
.join-plan.is-deal .join-plan__up { left: -8px; right: -8px; max-width: none; padding: 2px 7px; font-size: 10.5px; }
/* The deal: a ribbon over the top edge, a lift and a glow (also when it is not the chosen tile). */
.join-plan__deal {
  position: absolute; top: -9px; left: 0; right: 0; z-index: 2; width: max-content; margin: 0 auto; padding: 0 9px 0 7px;
  display: inline-flex; align-items: center; gap: 4px; height: 18px; border-radius: 999px; pointer-events: none;
  background: var(--jn-deal-bg); color: var(--jn-deal-text);
  font-size: 10.5px; font-weight: 700; line-height: 18px; letter-spacing: .01em; white-space: nowrap;
  box-shadow: 0 4px 10px -4px var(--jn-deal-glow);
}
.join-plan__deal::before {
  content: ''; width: 9px; height: 9px; flex: none; background: currentColor;
  clip-path: polygon(50% 0, 61% 35%, 98% 35%, 68% 57%, 79% 91%, 50% 70%, 21% 91%, 32% 57%, 2% 35%, 39% 35%);
}
.join-plan.is-deal .join-plan__tag { display: none; }
.join-plan.is-deal { transform: translateY(-2px) scale(1.025); box-shadow: 0 14px 28px -14px var(--jn-deal-glow); }
.join-plan.is-deal:not([aria-pressed="true"]) { border-color: var(--jn-perk-line); background: var(--jn-deal-tint); }
.join-plan.is-deal:active { transform: translateY(-1px) scale(1.01); }
.join-plan.is-deal::before {
  content: ''; position: absolute; inset: -2px; border: 2px solid var(--jn-strike); border-radius: calc(var(--jn-r) + 2px);
  opacity: 0; pointer-events: none;
}
/* Pro steps back (never disabled): its own text is dimmed, its note stays clear. */
.join-plan.is-dim:not([aria-pressed="true"]) > :not(.join-plan__note) { opacity: .7; }
.join-plan__note {
  position: absolute; left: -2px; right: -2px; top: calc(100% + 4px); margin: 0 auto; max-width: 112px;
  color: var(--jn-coral-deep); font-size: 10.5px; font-weight: 600; line-height: 1.25; text-align: center; text-wrap: balance; pointer-events: none;
}

/* The motion (fresh accept only). The old name/line stay where they are while they are
   struck out; the new ones arrive in their own cells only after the old one has left. */
.join--perk-play .join-plan.is-upgraded > b { animation: jn-old-dim .25s ease var(--tt) backwards; }
.join--perk-play .join-plan.is-upgraded > b::after { animation: jn-scratch .25s cubic-bezier(.6, .05, .25, 1) var(--tt) backwards; }
.join--perk-play .join-plan.is-upgraded > .join-plan__to { animation: jn-settle-in .3s ease-out calc(var(--tt) + .15s) backwards; }
.join--perk-play .join-plan.is-upgraded > .join-plan__credits:not(.join-plan__credits--to)::after { animation: jn-scratch .2s cubic-bezier(.6, .05, .25, 1) calc(var(--tt) + .3s) backwards; }
.join--perk-play .join-plan.is-upgraded > .join-plan__credits:not(.join-plan__credits--to) { animation: jn-old-out .16s ease-in calc(var(--tt) + .52s) backwards; }
.join--perk-play .join-plan.is-upgraded > .join-plan__credits--to { animation: jn-settle-in .3s ease-out calc(var(--tt) + .7s) backwards; }
.join--perk-play .join-plan.is-upgraded > .join-plan__up { animation: jn-chip-in .28s ease-out calc(var(--tt) + .8s) backwards; }
/* The Plus tile: the "Most students" tag leaves, then the ribbon and the lift arrive, then one ring. */
.join--perk-play .join-plan.is-deal > .join-plan__tag { display: block; animation: jn-tag-out .16s ease-in calc(var(--tt) + .55s) both; }
.join--perk-play .join-plan__deal { animation: jn-deal-in .28s ease-out calc(var(--tt) + .75s) backwards; }
.join--perk-play .join-plan.is-deal { animation: jn-deal-lift .28s ease-out calc(var(--tt) + .75s) backwards; }
.join--perk-play .join-plan.is-deal::before { animation: jn-deal-ring .5s ease-out calc(var(--tt) + .85s) backwards; }
/* The Pro tile steps back, then its note arrives. */
.join--perk-play .join-plan.is-dim:not([aria-pressed="true"]) > :not(.join-plan__note) { animation: jn-dim .35s ease var(--tt) backwards; }
.join--perk-play .join-plan__note { animation: jn-note-in .35s ease-out calc(var(--tt) + .05s) backwards; }
@keyframes jn-old-dim { from { color: var(--jn-text); } to { color: var(--jn-old); } }
@keyframes jn-scratch { from { clip-path: inset(0 100% 0 0); } to { clip-path: inset(0 0 0 0); } }
@keyframes jn-settle-in { from { opacity: 0; transform: translateY(3px); } to { opacity: 1; transform: none; } }
@keyframes jn-old-out { from { opacity: 1; } to { opacity: 0; } }
@keyframes jn-chip-in { from { opacity: 0; transform: translateY(4px); } to { opacity: 1; transform: none; } }
@keyframes jn-tag-out { from { opacity: 1; } to { opacity: 0; } }
@keyframes jn-deal-in { from { opacity: 0; transform: translateY(-5px); } to { opacity: 1; transform: none; } }
@keyframes jn-deal-lift { from { transform: none; } to { transform: translateY(-2px) scale(1.025); } }
@keyframes jn-deal-ring {
  0% { opacity: 0; transform: scale(1); }
  20% { opacity: .75; }
  100% { opacity: 0; transform: scale(1.05, 1.1); }
}
@keyframes jn-dim { from { opacity: 1; } to { opacity: .7; } }
@keyframes jn-note-in { from { opacity: 0; transform: translateY(-3px); } to { opacity: 1; transform: none; } }
@media (prefers-reduced-motion: reduce) {
  .join-perk, .join-perk *, .join-perk *::before, .join-perk *::after, .join-plan__up { animation: none !important; }
  .join-perk__row { transition: none; }
  .join-plan.is-upgraded, .join-plan.is-upgraded *, .join-plan.is-upgraded *::after, .join-plan.is-deal::before, .join-plan.is-dim > *, .join-plan__note { animation: none !important; }
  .join--perk-play .join-plan.is-deal > .join-plan__tag { display: none; }
}

/* Continue · $price/mo */
.join-submit {
  width: 100%; height: var(--jn-ctl); margin-top: 4px; padding: 0 16px;
  display: flex; align-items: center; justify-content: center;
  border: 0; border-radius: var(--jn-r); cursor: pointer;
  background: linear-gradient(180deg, var(--jn-coral-2), var(--jn-coral));
  color: #2A140C; font-weight: 700; font-size: 15px;
  box-shadow: 0 8px 20px -10px rgba(255, 130, 89, .8);
}
.join-submit:hover:not(:disabled) { filter: brightness(1.03); }
.join-submit:disabled { opacity: .7; cursor: default; }
/* A tile change nudges the button whose price just changed. */
.join-submit.join-bump { animation: jn-bump .32s ease-out; }
@keyframes jn-bump { 0% { transform: scale(1); } 40% { transform: scale(1.018); box-shadow: 0 10px 26px -8px rgba(255, 130, 89, .95); } 100% { transform: scale(1); } }
@keyframes jn-check { from { transform: scale(.4); } to { transform: scale(1); } }
/* Pressable: a coral edge under the primary button, a quiet one under Google. */
.join-submit { box-shadow: 0 3px 0 var(--jn-coral-deep), 0 10px 22px -10px rgba(255, 130, 89, .8); transition: transform .08s ease, box-shadow .08s ease; }
.join-submit:active:not(:disabled) { transform: translateY(2px); box-shadow: 0 1px 0 var(--jn-coral-deep), 0 6px 14px -10px rgba(255, 130, 89, .8); }
.join-google__btn { box-shadow: 0 3px 0 var(--jn-line); transition: transform .08s ease, box-shadow .08s ease; }
.join-google__btn:active:not(:disabled) { transform: translateY(2px); box-shadow: 0 1px 0 var(--jn-line); }
@media (prefers-reduced-motion: reduce) { .join-submit.join-bump { animation: none; } .join-plan { transition: none; } .join-plan[aria-pressed="true"]::after { animation: none; } }
/* The one reassurance line under both buttons. */
.join-assure { margin: 12px 0 0; text-align: center; color: var(--jn-muted); font-size: 12px; line-height: 1.45; text-wrap: balance; }
.join-assure a { white-space: nowrap; color: var(--jn-link); }
.join-footnote { margin: 4px 0 0; text-align: center; color: var(--jn-muted); opacity: .85; font-size: 11px; line-height: 1.45; text-wrap: balance; }
.join-footnote a { white-space: nowrap; color: var(--jn-link); }
/* What happens after you pay: one quiet row under the fine print. */
.join-after {
  margin: 8px 0 0; padding: 8px 0 0; list-style: none;
  display: flex; flex-wrap: wrap; justify-content: center; gap: 2px 10px;
  border-top: 1px solid var(--jn-line);
  color: var(--jn-muted); font-size: 11px; line-height: 1.45;
}
.join-after li { display: inline-flex; align-items: center; gap: 4px; white-space: nowrap; }
.join-after b {
  width: 14px; height: 14px; flex: none; display: inline-grid; place-items: center;
  border-radius: 999px; border: 1px solid var(--jn-line);
  font-size: 9px; font-weight: 700; color: var(--jn-text);
}

/* Signed in, not paid yet (§3.8): replaces the form. */
.join-returning { display: flex; flex-direction: column; align-items: stretch; gap: 10px; text-align: center; }
.join-returning__plan { margin: 0 0 4px; color: var(--jn-text); font-size: 15px; }
.join-returning .join-submit { margin-top: 0; }
.join-returning__out { margin: 4px 0 0; color: var(--jn-muted); font-size: 13px; }
.join-returning__out .join-link { font-size: 13px; }

/* Compare plans: the existing #joinPlanComparison dialog, restyled compact.
   pearl.css's .join-comparison rules (1180px wide, cream) are overridden here. */
body.join-body .join-comparison {
  width: min(560px, calc(100% - 32px)); max-height: calc(100dvh - 32px);
  margin: auto; padding: 22px 22px 18px; overflow: auto;
  border: 1px solid var(--jn-card-border); border-radius: 18px;
  background: var(--jn-dialog); color: var(--jn-text); box-shadow: var(--jn-card-shadow);
}
body.join-body .join-comparison::backdrop { background: rgba(12, 11, 20, .45); -webkit-backdrop-filter: blur(4px); backdrop-filter: blur(4px); }
.join-comparison__head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 12px; }
.join-comparison__head h2 { margin: 0; color: var(--jn-text); font: 500 1.4rem/1.1 'Newsreader', Georgia, serif; }
body.join-body .join-comparison__close {
  position: static; width: 32px; height: 32px; padding: 0;
  display: grid; place-items: center;
  border-radius: 999px; border: 1px solid var(--jn-line); background: transparent;
  color: var(--jn-text); font-size: 18px; line-height: 1; cursor: pointer;
}
.join-comparison table { width: 100%; border-collapse: collapse; font-size: 13.5px; }
.join-comparison th, .join-comparison td { padding: 9px 6px; text-align: center; border-bottom: 1px solid var(--jn-line); }
.join-comparison th { font-weight: 700; }
.join-comparison td:first-child, .join-comparison th:first-child { text-align: left; color: var(--jn-muted); font-weight: 500; }
.join-comparison__fine { margin: 10px 0 14px; color: var(--jn-muted); font-size: 12px; }
.join-comparison__pick { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 8px; }
.join-comparison__pick button {
  height: var(--jn-ctl); min-width: 0; padding: 0 6px;
  border-radius: var(--jn-r); border: 1px solid var(--jn-line); background: var(--jn-field);
  color: var(--jn-text); font-weight: 600; cursor: pointer;
}
.join-comparison__pick button:hover, .join-comparison__pick button[aria-pressed="true"] { border-color: var(--jn-coral-deep); }
.join-comparison__pick button[aria-pressed="true"] { box-shadow: 0 0 0 1px var(--jn-coral-deep); background: var(--jn-chip-on); }

@keyframes jn-fade-in { from { opacity: 0; } to { opacity: 1; } }
@media (max-width: 480px) {
  /* Phone width (owner 2026-10-26: even, same size, no overlaps): the old name sits small and
     struck ABOVE the new one, the four rows stack in one column, the three tiles keep one size
     (the deal lifts by position only, no scale), and the pills sit inside their tile. */
  .join-plans__row { gap: 8px; margin-bottom: 26px; }
  .join-plan { padding: 10px 9px 15px; }
  .join-plan.is-upgraded { grid-template-columns: minmax(0, 1fr); row-gap: 2px; }
  .join-plan.is-upgraded > b { grid-column: 1; grid-row: 1; justify-self: start; font-size: 11px; font-weight: 600; line-height: 1.2; }
  .join-plan.is-upgraded > .join-plan__to { grid-column: 1; grid-row: 2; font-size: 14px; line-height: 1.3; }
  .join-plan.is-upgraded > .join-plan__price { grid-row: 3; }
  .join-plan.is-upgraded > .join-plan__credits { grid-row: 4; }
  .join-plan.is-upgraded > b::after { left: -2px; right: -2px; height: 6px; margin-top: -3px; }
  .join-plan.is-deal, .join-plan.is-deal:active { transform: translateY(-2px); }
  .join-plan.is-deal .join-plan__up { left: 0; right: 0; max-width: 100%; padding: 2px 5px; font-size: 9.5px; }
  .join-plan__up { bottom: -10px; max-width: 100%; }
  .join-plan__note { left: 0; right: 0; max-width: none; top: calc(100% + 6px); }
  /* Fade only, so the new name never travels into its neighbours' boxes. */
  .join--perk-play .join-plan.is-upgraded > .join-plan__to, .join--perk-play .join-plan.is-upgraded > .join-plan__credits--to { animation-name: jn-fade-in; }
}
@media (max-width: 420px) {
  .join-main { padding: 0 12px 28px; }
  .join-top { padding-top: 12px; padding-bottom: 12px; }
  .join-card { padding: 20px 16px 18px; border-radius: 18px; }
  .join-card h1 { font-size: 1.5rem; }
  .join-lede { margin-bottom: 14px; }
  .join-plan::after { top: 9px; right: 7px; width: 14px; height: 14px; background-size: 10px; }
  .join-plans__row { gap: 6px; }
  .join-plan { padding: 9px 8px 8px; }
  .join-plan__what { font-size: 11px; }
  .join-signin__lead { display: none; }
  .join-comparison table { font-size: 12.5px; }
  .join-comparison th, .join-comparison td { padding: 8px 3px; }
  .join-comparison__pick button { font-size: 13px; }
  /* Friend reveal: tighter card, shorter arrow, a smaller tile chip. */
  .join-perk__card { padding: 11px 10px 9px; }
  .join-perk__title { font-size: 13px; }
  .join-perk__ladder { gap: 8px 14px; }
  .join-perk__chip { padding: 0 9px; font-size: 12px; }
  .join-perk__arrow { width: 26px; }
  .join-plan__up { padding: 2px 6px; font-size: 10px; }
  /* Tile transformation: the Plus chip drops "Pro " (the tile already says Pro), smaller ribbon and note. */
  .join-plan__up-to { display: none; }
  .join-plan.is-deal .join-plan__up { padding: 2px 6px; font-size: 10px; }
  .join-plan__deal { height: 17px; padding: 0 8px 0 6px; font-size: 10px; line-height: 17px; }
  .join-plan__note { font-size: 10px; max-width: 100px; }
  .join-plan.is-upgraded { column-gap: 4px; }
  .join-plan.is-upgraded > .join-plan__to, .join-plan.is-upgraded > b { font-size: 13.5px; }
}
@media (max-width: 380px) {
  .join-plan.is-upgraded > .join-plan__to { font-size: 13px; }
  .join-plan.is-upgraded > b { font-size: 10.5px; }
}
@media (max-width: 340px) {
  .join-plan__up { padding: 2px 4px; font-size: 9px; }
  .join-plan__note { font-size: 9px; }
  .join-plan.is-deal .join-plan__up { left: -10px; right: -10px; padding: 2px 4px; font-size: 9px; }
  .join-plan.is-upgraded > .join-plan__to { font-size: 12px; }
  .join-plan.is-upgraded > b { font-size: 10px; }
}

/* Week pass (owner 2026-10-28): one slim, quieter row under the three monthly tiles, a
   cheap try-out that never competes with them. Same aria-pressed ring and check when
   chosen; dashed and see-through otherwise. Kept last so the phone-width tile padding
   above does not reshape it. */
.join-plan--week {
  grid-column: 1 / -1; min-height: 0;
  display: grid; grid-template-columns: auto auto 1fr; align-items: baseline; column-gap: 6px; row-gap: 0;
  padding: 6px 34px 6px 14px;
  background: transparent; border-style: dashed;
}
.join-plan--week::after { top: 50%; margin-top: -8px; }
.join-plan--week .join-plan__price { grid-column: 1; grid-row: 1 / 3; align-self: stretch; margin-right: 8px; display: flex; align-items: baseline; align-content: center; flex-wrap: wrap; padding-right: 14px; border-right: 1px dashed var(--jn-line); }
.join-plan--week .join-plan__price { align-items: center; }
.join-plan--week b { grid-column: 2; grid-row: 1; font-size: 14px; white-space: nowrap; }
.join-plan--week .join-plan__credits { grid-column: 3; grid-row: 1; white-space: nowrap; }
.join-plan--week .join-plan__credits::before { content: '· '; color: var(--jn-muted); }
.join-plan--week .join-plan__norenew { grid-column: 2 / -1; grid-row: 2; white-space: nowrap; font-size: 11.5px; font-weight: 600; color: var(--jn-muted); }
/* A friend's code hangs chips and notes under the monthly tiles: leave them room. */
.join--friend .join-plan--week { margin-top: 30px; }
.join-comparison__same { margin: 12px 0 0; color: var(--jn-text); font-size: 13px; font-weight: 600; }
.join-comparison__week { margin: -6px 0 14px; color: var(--jn-muted); font-size: 12px; }
.join-comparison__week b { color: var(--jn-text); }
.join-comparison__pick .join-comparison__pick-week { grid-column: 1 / -1; border-style: dashed; font-weight: 500; }
@media (max-width: 480px) {
  .join-plan--week { padding: 8px 30px 8px 10px; }
  .join-plan--week::after { top: 50%; margin-top: -7px; }
}
@media (max-width: 480px) {
  .join-plan__credits { font-size: 11px; letter-spacing: -.01em; }
  .join-plan--week .join-plan__price { padding-right: 12px; }
  /* Pro's two-line note hangs lower at phone width: keep the "Try it out" tag clear of it. */
  .join--friend .join-plan--week { margin-top: 40px; }
}
