/*
 * onboarding.css — LAYOUT ONLY for the first-run wizard.
 *
 * Every colour, font, radius and shadow comes from static/carpe.css. This file used to sit on top
 * of a FORKED copy of the design tokens under static/onboarding/ds/, which had already drifted
 * from production: it still @imported Google Fonts after the app had stopped (making /onboarding
 * the app's only third-party request — on the screen whose own copy said privacy is the
 * foundation), and it redefined .cm-laurel to display:inline-flex against carpe.css's
 * display:block, a real conflict for any page loading both. That fork is deleted. If you need a
 * token here, take it from carpe.css; do not reintroduce a second source of truth.
 *
 * The prototype's ~200 rules for screens that no longer exist (coach-mark overlays, mock
 * dashboards, bank lists, the donut ring, the fake 402x838 phone with its hardcoded "9:41" status
 * bar under the user's real clock) are gone with them. This is a real responsive page.
 */

* { box-sizing: border-box; }

/* Any class that sets `display` beats the UA's `[hidden] { display: none }`, regardless of
   specificity — so `.ob-totp { display: flex }` rendered the whole QR/secret/code-input block on
   first paint, before the user had asked to set anything up. carpe.css already carries the same
   one-line fix for .cm-btn[hidden]; this states it once for every element on the page rather than
   waiting to be bitten per-class. */
[hidden] { display: none !important; }

.ob-body {
  margin: 0;
  min-height: 100dvh;
  background: var(--surface-page);
  color: var(--text-body);
  font-family: 'Plus Jakarta Sans', system-ui, -apple-system, sans-serif;
  -webkit-font-smoothing: antialiased;
  display: flex;
  flex-direction: column;
}

/* ── header ───────────────────────────────────────────────────────────── */
.ob-hdr {
  display: flex; align-items: center; gap: 12px;
  padding: max(14px, env(safe-area-inset-top)) 16px 10px;
  position: sticky; top: 0;
  background: var(--surface-page);
  z-index: 5;
}
.ob-back, .ob-skip {
  background: none; border: 0;
  font: inherit; font-size: 14px; font-weight: 600;
  cursor: pointer; padding: 6px 10px;
  border-radius: var(--radius-pill);
  min-height: 44px; min-width: 44px;   /* touch-target floor; these are on every screen */
  display: inline-flex; align-items: center;
}
.ob-back { color: var(--accent); }
.ob-back:disabled { opacity: .3; cursor: default; }
.ob-skip { color: var(--muted); }
.ob-skip:hover { color: var(--text-body); }

.ob-dots { flex: 1; display: flex; gap: 5px; align-items: center; justify-content: center; }
.ob-dot {
  height: 4px; flex: 1 1 0; max-width: 34px;
  border-radius: 999px;
  background: color-mix(in srgb, var(--muted) 26%, transparent);
  transition: background .25s ease;
}
.ob-dot.done { background: color-mix(in srgb, var(--accent) 55%, transparent); }
.ob-dot.on { background: var(--accent); }

/* ── screens ──────────────────────────────────────────────────────────── */
.ob-main {
  flex: 1; width: 100%;
  max-width: 460px;          /* readable measure on desktop, full width on a phone */
  margin: 0 auto;
  padding: 8px 20px max(28px, env(safe-area-inset-bottom));
  display: flex; flex-direction: column;
  overflow-y: auto;
}
.ob-screen { display: none; flex-direction: column; gap: 14px; animation: ob-in .28s ease both; }
/* Fill the main column so the action cluster can anchor to the bottom on every screen — the eye
   learns one place the button lives, instead of it floating to a different height per step. */
/* Vertically centre the content group WITHOUT justify-content:center, which clips the top
   unscrollably when content exceeds the viewport (the expanded QR screen on a small phone with the
   keyboard up). Two zero-height pseudo flex items with auto margins split the free space when
   content fits, and collapse to 0 when it doesn't — so tall content top-aligns and scrolls
   normally instead of clipping. The pseudos are real flex items, so the screen's `gap` would
   otherwise add 14px around each; margin:0 on the gap edge keeps them true spacers. */
.ob-screen.on { display: flex; flex: 1 1 auto; min-height: 0; }
.ob-screen.on::before { content: ""; margin: auto 0 0; }
.ob-screen.on::before { margin-bottom: -14px; }   /* cancel the flex gap below ::before */
.ob-screen.on::after  { content: ""; margin: 0 0 auto; }
.ob-screen.on::after  { margin-top: -14px; }       /* cancel the flex gap above ::after */
/* The trailing button group sits directly under the (now vertically centered) content. */
.ob-actions { display: flex; flex-direction: column; gap: 6px; padding-top: 18px; }
.ob-actions .ob-fine { text-align: center; margin-top: 2px; }
@keyframes ob-in { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: none; } }
@media (prefers-reduced-motion: reduce) { .ob-screen { animation: none; } }

.ob-screen h1 {
  margin: 2px 0 0;
  font-size: clamp(24px, 6.5vw, 30px);
  font-weight: var(--weight-black);
  letter-spacing: -.02em; line-height: 1.15;
  color: var(--text-body);
}
.ob-lede { margin: 0; font-size: 15px; line-height: 1.55; color: var(--muted); word-spacing: .04em; }
.ob-screen h1:focus { outline: none; }   /* focused only for SR announcement; no visible ring on a heading */
.ob-lede b { color: var(--text-body); font-weight: 700; }
.ob-lede em { font-style: italic; }
.ob-fine { margin: 0; font-size: 12.5px; line-height: 1.5; color: var(--muted); }

/* Brand mark + creed (welcome / done) */
.ob-mark {
  position: relative;
  width: 118px; height: 118px;
  /* The laurel is absolutely positioned and renders a U that reaches the box's lower edge, so the
     logo sits in its opening. Without real bottom margin the wordmark below collides with it —
     it printed straight through "CARPE MONEY". */
  margin: 14px auto 18px;
  display: grid; place-items: center;
  color: var(--accent);
}
/* Stack every child in ONE grid cell. Without this the laurel and the logo are two grid ITEMS in
   a single-column grid, so they lay out in two ROWS: the box overflows and the logo prints
   straight through the wordmark below. carpe.css's .cm-laurel is display:block and in-flow (the
   deleted fork made it inline-flex and the prototype absolutely positioned it), so the overlay
   has to be arranged here. */
.ob-mark > * { grid-area: 1 / 1; align-self: center; justify-self: center; }
.ob-mark > img, .ob-mark > i, .ob-mark > svg { position: relative; z-index: 1; }
.ob-mark > img { border-radius: 16px; border: 1px solid var(--frame-gold); }
.ob-brand {
  text-align: center; font-size: 11px; font-weight: 800;
  letter-spacing: .18em; color: var(--accent);
  margin-top: -6px;   /* pull up against the mark; they read as one lockup */
}
.ob-creed {
  text-align: center; font-size: 10px; font-weight: 700;
  letter-spacing: .2em; text-transform: uppercase;
  color: var(--muted); margin-top: 2px;
}
.ob-screen[data-screen="welcome"] h1,
.ob-screen[data-screen="done"] h1 { text-align: center; }

.ob-icon {
  width: 60px; height: 60px;
  border-radius: 18px;
  display: grid; place-items: center;
  background: color-mix(in srgb, var(--accent) 12%, transparent);
  color: var(--accent);
  margin-top: 10px;
}
.ob-icon.danger {
  background: color-mix(in srgb, var(--danger) 12%, transparent);
  color: var(--danger);
}

/* Fact list (welcome) */
.ob-facts { list-style: none; margin: 4px 0 0; padding: 0; display: flex; flex-direction: column; gap: 13px; }
.ob-facts li {
  display: flex; gap: 11px; align-items: flex-start;
  font-size: 14px; line-height: 1.5; color: var(--muted); word-spacing: .04em;
}
.ob-facts li > i, .ob-facts li > svg { flex: none; margin-top: 2px; color: var(--accent); }
.ob-facts b { color: var(--text-body); font-weight: 700; }

/* Choice cards (twofa second-step picker). Fill the sparse screen's middle and make the choice
   tappable rather than a lonely button on empty beige. */
.ob-choices { display: flex; flex-direction: column; gap: 10px; margin-top: 4px; }
.ob-choice {
  display: flex; align-items: center; gap: 13px; width: 100%; text-align: left;
  padding: 15px; font: inherit; cursor: pointer;
  border-radius: var(--radius-md);
  border: 1.5px solid color-mix(in srgb, var(--muted) 22%, transparent);
  background: var(--surface-card); color: var(--text-body);
}
.ob-choice:hover { border-color: var(--accent); }
.ob-choice-ic {
  width: 42px; height: 42px; flex: none; border-radius: 12px;
  display: grid; place-items: center;
  background: color-mix(in srgb, var(--accent) 12%, transparent); color: var(--accent);
}
.ob-choice-tx { flex: 1; min-width: 0; display: flex; flex-direction: column; }
.ob-choice-tx b { font-size: 15px; font-weight: 700; color: var(--text-body); }
.ob-choice-tx small { font-size: 12.5px; color: var(--muted); margin-top: 1px; word-spacing: .04em; }
.ob-choice > i, .ob-choice > svg { color: var(--muted); flex: none; }
.ob-choice:disabled { cursor: default; }
/* While a plan save is in flight both cards disable; dim the un-picked one so the tapped card
   (kept bright via .sel) reads as the pending choice. */
.ob-choice:disabled:not(.sel) { opacity: .5; }
/* Plan cards carry a longer two-number sub; keep it to one line (so both cards match height)
   even at five-figure incomes, and hold a common min-height as a backstop. */
#ob-goal-plans .ob-choice { min-height: 66px; }
#ob-goal-plans .ob-choice-tx small { font-size: 11.5px; word-spacing: 0; }
/* Plan card the user just picked: accent border + the trailing check lights up. */
.ob-choice.sel { border-color: var(--accent); background: color-mix(in srgb, var(--accent) 7%, var(--surface-card)); }
.ob-choice.sel > i, .ob-choice.sel > svg { color: var(--accent); }
/* Quiet inline link under the plan cards ("set a specific amount instead") */
.ob-linkbtn {
  display: block; width: 100%; background: none; border: 0;
  color: var(--accent); font: inherit; font-size: 13px; font-weight: 600;
  padding: 13px 8px; min-height: 44px; margin-top: 2px; cursor: pointer; text-align: center;
}
.ob-linkbtn:hover { text-decoration: underline; }
/* Links inside fine print (the create screen's "Already have an account?") stay
   on-brand — the UA default blue looked pasted-in against the parchment. (#180) */
.ob-fine a { color: var(--accent); font-weight: 600; text-decoration: none; }
.ob-fine a:hover { text-decoration: underline; }
/* Create screen (#180): pull each label onto its field — the screen's uniform 14px
   gap otherwise spaces label→field the same as field→next-label, so nothing groups.
   And the "Sign in" escape hatch gets a real tap target (the 44px floor every other
   control here holds). [js-review Minor on e908030] */
#ob-create-form .ob-label { margin-bottom: -8px; }
.ob-signin-link { display: inline-block; padding: 12px 16px; min-height: 44px;
                  font-size: 13.5px; }

/* Notes — neutral by default, explicit when something actually happened */
.ob-note {
  font-size: 13.5px; line-height: 1.5; word-spacing: .04em;
  padding: 11px 13px;
  border-radius: var(--radius-md);
  background: color-mix(in srgb, var(--muted) 10%, transparent);
  color: var(--text-body);
}
.ob-note.subtle { background: color-mix(in srgb, var(--accent) 6%, transparent); color: var(--muted); }
.ob-note.bad { background: color-mix(in srgb, var(--danger) 12%, transparent); color: var(--danger); font-weight: 600; }
.ob-note.good { background: color-mix(in srgb, var(--good) 13%, transparent); color: var(--good); font-weight: 600; }

/* Buttons: .cm-btn comes from carpe.css — this only sets wizard rhythm */
.ob-cta { width: 100%; justify-content: center; margin-top: 4px; min-height: 50px; font-size: 15px; }
.ob-secondary {
  width: 100%; background: none; border: 0;
  color: var(--muted); font: inherit; font-size: 14.5px; font-weight: 600;
  padding: 12px; cursor: pointer; border-radius: var(--radius-pill); min-height: 44px;
}
.ob-secondary:hover { color: var(--text-body); }

/* Inputs. 16px minimum, or WebKit zooms the viewport on focus — the bug fixed in 8b428c6. */
.ob-label { font-size: 12.5px; font-weight: 700; color: var(--muted); }
.ob-input {
  width: 100%;
  font: inherit; font-size: 16px;
  padding: 13px 14px;
  border-radius: var(--radius-md);
  border: 1.5px solid color-mix(in srgb, var(--muted) 26%, transparent);
  background: var(--surface-card); color: var(--text-body);
}
.ob-input:focus { outline: none; border-color: var(--accent); }

.ob-amount {
  display: flex; align-items: center; gap: 6px;
  padding: 14px 16px;
  border-radius: var(--radius-md);
  border: 1.5px solid color-mix(in srgb, var(--muted) 26%, transparent);
  background: var(--surface-card);
}
.ob-amount:focus-within { border-color: var(--accent); }
.ob-cur { font-size: 24px; font-weight: 800; color: var(--text-body); }
.ob-per { font-size: 14px; font-weight: 700; color: var(--muted); }
.ob-amount-in {
  flex: 1; min-width: 0;
  font: inherit; font-size: 34px; font-weight: 800; letter-spacing: -.02em;
  border: 0; background: none; color: var(--text-body); padding: 0;
  font-variant-numeric: tabular-nums;
}
.ob-amount-in:focus { outline: none; }

/* Authenticator */
.ob-totp { display: flex; flex-direction: column; gap: 10px; }
.ob-totp img {
  width: 180px; height: 180px; align-self: center;
  border-radius: var(--radius-md);
  background: #fff;   /* a QR needs a light quiet zone even in dark mode */
  padding: 8px;
}
.ob-totp code {
  font-family: ui-monospace, 'SF Mono', monospace;
  font-size: 14px; letter-spacing: .08em;
  word-break: break-all; color: var(--text-body);
}

/* Recovery codes — deliberately prominent. Highest-stakes screen in this flow.
   ONE column at every width, never two — the same conclusion recovery_codes.html and
   totp_reenroll.html reached in #154, which this screen re-grew from scratch. A code is
   xxxx-xxxx-xxxx-xxxx; two columns inside a 390px phone leave ~143px each, so every code
   broke after the third group ("9a9a-06e8-4890" / "e9bb") on the one screen a member has
   to transcribe from. The old 380px guard missed iPhone 13 (390) and 15 Pro Max (430)
   entirely — it only ever fired on the phones that were already fine. A viewport-width
   breakpoint can't rescue this either: .ob-main caps at 460px, so the card never gets
   wide enough for a second column to fit. nowrap holds the line where ui-monospace falls
   back to a wider face; overflow-x is the escape valve, as on both siblings. */
.ob-codes {
  margin: 0; padding: 12px 14px;
  border-radius: var(--radius-md);
  background: var(--surface-card);
  border: 1.5px solid color-mix(in srgb, var(--danger) 30%, transparent);
  display: grid; grid-template-columns: 1fr; gap: 2px;
  list-style: none; counter-reset: ob-code;
  font-family: ui-monospace, 'SF Mono', monospace;
  font-size: 15px; letter-spacing: .04em;
  white-space: nowrap; overflow-x: auto;
}
/* The ordinal is drawn here rather than by the <ol> marker: an outside marker paints to
   the LEFT of its grid item, and the second column had only the 14px gap to paint into,
   so "10." printed straight through the tail of code 9. Its own right-aligned column also
   puts 1–9 and 10 on one axis. counter() content stays out of textContent, so Copy all
   and Download still hand over bare codes. */
.ob-codes li {
  counter-increment: ob-code;
  display: grid; grid-template-columns: 2ch 1fr; gap: 10px; align-items: baseline;
  padding: 3px 6px; border-radius: var(--radius-sm);
}
.ob-codes li:nth-child(odd) { background: color-mix(in srgb, var(--muted) 8%, transparent); }
.ob-codes li::before {
  content: counter(ob-code);
  text-align: right; color: var(--muted); font-size: 12.5px; letter-spacing: 0;
}
.ob-ack {
  display: flex; gap: 10px; align-items: center;
  font-size: 14px; color: var(--text-body); cursor: pointer;
  padding: 4px 2px; min-height: 44px;
}
.ob-ack input { width: 19px; height: 19px; accent-color: var(--accent); flex: none; }

/* Toast */
.ob-toast {
  position: fixed; left: 50%; bottom: max(22px, env(safe-area-inset-bottom));
  transform: translate(-50%, 12px);
  background: var(--surface-card); color: var(--text-body);
  border-radius: var(--radius-pill);
  padding: 11px 18px; font-size: 14px; font-weight: 600;
  box-shadow: 0 6px 24px color-mix(in srgb, var(--accent) 18%, transparent);
  opacity: 0; pointer-events: none;
  transition: opacity .2s ease, transform .2s ease;
  z-index: 20; max-width: min(90vw, 420px); text-align: center;
}
.ob-toast.on { opacity: 1; transform: translate(-50%, 0); }

/* ── Plaid link animation (ported from the original onboarding) ─────────── */
.ob-plaidx { display: flex; flex-direction: column; align-items: center; gap: 20px; padding: 6px 0 2px; }
.ob-xfer { display: flex; align-items: flex-start; justify-content: space-between; width: 100%; max-width: 300px; }
.ob-xnode { display: flex; flex-direction: column; align-items: center; gap: 8px; width: 78px; flex: none; }
.ob-xdisc {
  width: 62px; height: 62px; border-radius: 18px;
  display: flex; align-items: center; justify-content: center;
  border: 1px solid var(--border-strong); box-shadow: var(--shadow-card); position: relative;
}
.ob-xdisc.carpe { background: var(--surface-card); }
.ob-xdisc.carpe img { width: 38px; height: 38px; border-radius: 11px; }
.ob-xdisc.plaid { background: #0a0a0a; color: #fff; font-weight: 800; font-size: 14px; letter-spacing: .01em; }
.ob-xdisc::after {
  content: ""; position: absolute; inset: -5px; border-radius: 22px;
  border: 2px solid var(--accent); opacity: 0; animation: ob-xpulse 2.8s infinite;
}
.ob-xdisc.plaid::after { border-color: var(--accent2); animation-delay: 1.4s; }
.ob-xlbl { font-size: 11px; font-weight: 700; color: var(--muted); }
.ob-xlane { flex: 1; position: relative; height: 62px; margin: 0 4px; }
.ob-xtrack {
  position: absolute; top: 50%; left: 0; right: 0; height: 2px; transform: translateY(-50%);
  background: repeating-linear-gradient(90deg, var(--border-strong) 0 5px, transparent 5px 11px);
  background-size: 22px 2px; animation: ob-xflow 1.1s linear infinite;
}
.ob-xkey {
  position: absolute; top: 50%; width: 30px; height: 30px; margin: -15px 0 0 -15px;
  border-radius: 10px; display: flex; align-items: center; justify-content: center;
  color: #fff; box-shadow: var(--shadow-card);
}
.ob-xkey svg.bk { width: 16px; height: 16px; }
.ob-xlock {
  position: absolute; right: -4px; bottom: -4px; width: 15px; height: 15px; border-radius: 5px;
  background: var(--frame-gold); color: #1c1410;
  display: flex; align-items: center; justify-content: center; border: 1.5px solid var(--surface-card);
}
.ob-xlock svg { width: 9px; height: 9px; }
.ob-xkey.out { background: var(--accent); animation: ob-xout 2.8s cubic-bezier(.5,0,.5,1) infinite; }
.ob-xkey.in { background: var(--accent2); animation: ob-xin 2.8s cubic-bezier(.5,0,.5,1) infinite; }
@keyframes ob-xflow { to { background-position: 22px 0; } }
@keyframes ob-xpulse { 0% { opacity: .55; transform: scale(.92); } 40%, 100% { opacity: 0; transform: scale(1.12); } }
@keyframes ob-xout { 0% { left: 0; opacity: 0; transform: scale(.6); } 8% { opacity: 1; transform: scale(1); } 42% { opacity: 1; } 50%, 100% { left: 100%; opacity: 0; transform: scale(.6); } }
@keyframes ob-xin { 0%, 50% { left: 100%; opacity: 0; transform: scale(.6); } 58% { opacity: 1; transform: scale(1); } 92% { opacity: 1; } 100% { left: 0; opacity: 0; transform: scale(.6); } }
@media (prefers-reduced-motion: reduce) {
  .ob-xtrack, .ob-xkey.out, .ob-xkey.in, .ob-xdisc::after { animation: none; }
  .ob-xkey.in { display: none; }   /* leave a single static token on the track */
}
