/* ── Reset ── */
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }

/* Screen-reader-only: visually hidden, still announced by assistive tech. */
.sr-only {
  position: absolute !important;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0, 0, 0, 0);
  white-space: nowrap; border: 0;
}

/* Visible keyboard focus for interactive controls (buttons/links/selects).
   Uses :focus-visible so it only appears for keyboard/AT users. Text inputs,
   textareas and contenteditable cells are intentionally EXCLUDED: browsers fire
   :focus-visible on them even on mouse click (you're about to type), which put
   an odd ring on the chat box. The caret already shows their focus, and the
   editable table cells define their own focus style. */
a:focus-visible, button:focus-visible, select:focus-visible,
[tabindex]:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: var(--r-xs);
}

/* Respect reduced-motion: drop the carousel slide + dot scale animations. */
@media (prefers-reduced-motion: reduce) {
  .ctpl-carousel-track { transition: none; }
  .ctpl-dot { transition: none; }
}

/* ── Skeleton Loaders ── */
@keyframes shimmer {
  0% { background-position: -1000px 0; }
  100% { background-position: 1000px 0; }
}

.skeleton {
  background: linear-gradient(90deg, var(--bg3) 0%, var(--bg4) 50%, var(--bg3) 100%);
  background-size: 1000px 100%;
  animation: shimmer 2s infinite;
  border-radius: var(--r-sm);
  display: block;
}

.skeleton-text {
  height: 12px;
  margin-bottom: var(--sp-2);
  width: 100%;
}

.skeleton-text.short { width: 60%; }
.skeleton-text.medium { width: 80%; }
.skeleton-text.long { width: 100%; }

.skeleton-card-avatar {
  width: 48px; height: 48px;
  border-radius: var(--r-sm);
  flex-shrink: 0;
}

.skeleton-card-content { flex: 1; min-width: 0; }

/* Job card skeleton */
.job-card-skeleton {
  background: var(--bg2);
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
  padding: var(--sp-35);
  margin-bottom: var(--sp-25);
}

.job-card-skeleton-header {
  display: flex; gap: var(--sp-3); margin-bottom: var(--sp-3);
}

.job-card-skeleton-logo {
  width: 48px; height: 48px;
  border-radius: var(--r-sm);
  flex-shrink: 0;
}

.job-card-skeleton-title { height: 16px; margin-bottom: var(--sp-15); }
.job-card-skeleton-company { height: 12px; margin-bottom: var(--sp-2); width: 70%; }
.job-card-skeleton-location { height: 12px; width: 60%; margin-bottom: var(--sp-25); }
.job-card-skeleton-badge { height: 20px; width: 70px; }

/* Chat message skeleton */
.chat-msg-skeleton {
  display: flex;
  gap: var(--sp-2);
  margin-bottom: var(--sp-3);
  align-items: flex-end;
}

.chat-msg-skeleton.user {
  flex-direction: row-reverse;
}

.chat-skeleton-avatar {
  width: 28px; height: 28px;
  border-radius: 50%;
  flex-shrink: 0;
}

.chat-skeleton-bubble {
  flex: 1;
  min-width: 0;
  max-width: 70%;
  height: 40px;
  border-radius: 12px;
}

.chat-msg-skeleton.user .chat-skeleton-bubble {
  background: var(--accent-bg);
}

.chat-msg-skeleton.ai .chat-skeleton-bubble {
  background: var(--ai-bg);
  border: 1px solid var(--ai-border);
}

/* Table skeleton (for jobs list) */

@media (prefers-reduced-motion: reduce) {
  .skeleton { animation: none; opacity: 0.6; }
}

/* ── Design tokens ──
   All palette, type, spacing, radius, shadow, z and motion tokens for
   BOTH themes live in tokens.css (loaded before this file). */

/* ══════════════════════════════════════════════════════════════════
   COMPONENTS — shared primitives (buttons, inputs, toggle, segmented
   picker, surfaces, chips). One interaction convention everywhere:
   hover = one surface step up, press = surface step + scale(0.98),
   focus = :focus-visible accent ring, disabled = opacity .5.
   Screens migrate onto these; per-screen button/input classes get
   deleted as they empty out. Chat + Jobs keep their own (frozen).
   ══════════════════════════════════════════════════════════════════ */

/* ── Buttons ── */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--sp-15);
  height: 36px; padding: 0 var(--sp-45);
  /* Generously rounded pill, matching the settings sign-out button — every
     action button across the app shares this shape (colours stay per-variant). */
  border: none; border-radius: var(--r-xl);
  font-family: var(--font-sans); font-size: var(--fs-base); font-weight: 600;
  line-height: 1; color: var(--text);
  background: transparent; cursor: pointer; flex-shrink: 0;
  transition: background var(--dur-press) var(--ease-out),
              color var(--dur-press) var(--ease-out),
              transform var(--dur-press) var(--ease-out);
}
.btn:active { transform: scale(0.98); }
.btn:disabled { opacity: 0.5; cursor: default; transform: none; }
.btn:focus-visible { outline: none; box-shadow: var(--ring); }
.btn svg { width: 16px; height: 16px; flex-shrink: 0; }

.btn-primary { background: var(--accent); color: var(--accent-text); }
.btn-primary:hover { background: var(--accent-hover); }
.btn-primary:active { background: var(--accent-pressed); }

.btn-secondary { background: var(--bg2); color: var(--text); }
.btn-secondary:hover { background: var(--bg3); }
.btn-secondary:active { background: var(--bg-active); }

/* Ink — the workhorse primary CTA (accent is reserved for brand moments). */
.btn-ink { background: var(--ink); color: var(--bg); }
.btn-ink:hover { background: color-mix(in srgb, var(--ink) 86%, var(--bg)); }

.btn-ghost { color: var(--text2); }
.btn-ghost:hover { background: var(--bg2); color: var(--text); }
.btn-ghost:active { background: var(--bg3); }

.btn-danger { background: var(--red); color: #ffffff; }
.btn-danger:hover { background: color-mix(in srgb, var(--red), #000 10%); }
.btn-danger:focus-visible { box-shadow: var(--ring-danger); }

.btn-sm { height: 30px; padding: 0 var(--sp-3); font-size: var(--fs-sm); }
.btn-lg { height: 48px; width: 100%; font-size: var(--fs-lg); }

/* Stacked full-width action pair (primary over secondary). Children are
   .btn .btn-lg variants; the stack only owns direction + the one gap. */
.btn-stack { display: flex; flex-direction: column; gap: var(--sp-25); width: 100%; }

/* ── Spinner — the one loading-ring primitive; size and colour adapt
   per site via a small override class. ── */
.spinner {
  display: inline-block; width: 14px; height: 14px; flex-shrink: 0;
  border: 2px solid var(--border2); border-top-color: currentColor;
  border-radius: 50%; animation: spin 0.7s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }

/* ── Text inputs / textareas ── */
.input {
  width: 100%; box-sizing: border-box;
  background: var(--bg2); border: 1px solid transparent;
  border-radius: var(--r); padding: var(--sp-25) var(--sp-3);
  font-family: var(--font-sans); font-size: var(--fs-base);
  line-height: var(--lh-snug); color: var(--text);
  transition: border-color var(--t), box-shadow var(--t), background var(--t);
  resize: vertical;
}
.input:hover { background: var(--bg-mid); }
body.dark .input:hover { background: var(--bg4); }
/* Focus matches the tracker search bar / chat input: no accent ring — the
   surface lifts a shade with a soft drop shadow. Consistent across every input
   and textarea in the app. */
.input:focus { outline: none; border-color: transparent; background: var(--bg3); box-shadow: var(--shadow-lift); }
body.dark .input:focus { background: var(--bg3); }
.input::placeholder { color: var(--placeholder); }
.input:disabled { opacity: 0.5; }
body.dark .input { background: var(--bg3); }

/* ── Dropdown (custom select) — native option popups can't be themed ── */
.dd { position: relative; flex: 1 1 auto; }
.dd-btn {
  display: flex; align-items: center; justify-content: space-between; gap: var(--sp-2);
  width: 100%; padding: var(--sp-2) var(--sp-3); box-sizing: border-box;
  background: var(--bg2); border: 1px solid transparent; border-radius: var(--r);
  font-family: var(--font-sans); font-size: var(--fs-base); color: var(--text);
  cursor: pointer; text-align: left;
  transition: border-color var(--t), background var(--t);
}
body.dark .dd-btn { background: var(--bg3); }
.dd-btn:hover { background: var(--bg-mid); }
body.dark .dd-btn:hover { background: var(--bg4); }
.dd-btn:focus-visible { outline: none; border-color: var(--accent); box-shadow: var(--ring); }
.dd-value { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.dd-btn > svg { width: 14px; height: 14px; color: var(--text3); flex-shrink: 0; transition: transform var(--dur-pop) var(--ease-out); }
.dd.open .dd-btn > svg { transform: rotate(180deg); }
.dd-menu {
  /* Fixed so the menu escapes the section card's overflow:hidden (an
     absolute menu near the card's bottom edge gets clipped). JS sets
     top/left/width from the trigger on open, flipping up when there's
     no room below. */
  position: fixed;
  z-index: var(--z-dropdown);
  background: var(--ai-bg); border: 1px solid var(--border);
  border-radius: var(--r); box-shadow: var(--shadow-2);
  padding: var(--sp-1); display: none;
  box-sizing: border-box;
  max-height: 60vh; overflow-y: auto;
}
body.dark .dd-menu { background: var(--bg3); }
.dd.open .dd-menu { display: block; animation: ddIn var(--dur-pop) var(--ease-out); }
@keyframes ddIn { from { opacity: 0; transform: translateY(-3px); } to { opacity: 1; transform: none; } }
.dd-opt {
  display: flex; align-items: center; justify-content: space-between; gap: var(--sp-2);
  width: 100%; padding: var(--sp-2) var(--sp-25); box-sizing: border-box;
  border: none; background: none; border-radius: var(--r-sm);
  font-family: var(--font-sans); font-size: var(--fs-base); color: var(--text);
  cursor: pointer; text-align: left;
  transition: background var(--t);
}
.dd-opt:hover { background: var(--bg2); }
body.dark .dd-opt:hover { background: var(--bg4); }
.dd-opt:focus-visible { outline: none; box-shadow: inset 0 0 0 2px var(--accent-bg); background: var(--bg2); }
body.dark .dd-opt:focus-visible { background: var(--bg4); }
.dd-opt.is-selected { font-weight: 600; }
.dd-check { width: 15px; height: 15px; flex-shrink: 0; color: var(--accent); opacity: 0; }
.dd-opt.is-selected .dd-check { opacity: 1; }
@media (prefers-reduced-motion: reduce) {
  .dd.open .dd-menu { animation: none; }
  .dd-btn > svg { transition: none; }
}

/* ── Segmented picker ── */
.seg { display: flex; gap: var(--sp-15); }
.seg-opt {
  flex: 1; padding: var(--sp-2) var(--sp-15);
  background: var(--ai-bg); border: 1.5px solid var(--border);
  border-radius: var(--r-sm);
  font-family: var(--font-sans); font-size: var(--fs-sm); font-weight: 500;
  color: var(--text2); cursor: pointer; text-align: center;
  transition: background var(--dur-press) var(--ease-out),
              border-color var(--dur-press) var(--ease-out),
              color var(--dur-press) var(--ease-out);
}
.seg-opt:hover { border-color: var(--border2); }
.seg-opt:focus-visible { outline: none; box-shadow: var(--ring); }
.seg-opt.active {
  background: var(--accent-bg); border-color: var(--accent);
  color: var(--accent); font-weight: 600;
}
.surface-3 { background: var(--ai-bg); border-radius: var(--r-xl); box-shadow: var(--shadow-3); }

/* ── Pro chip — marks Pro features in menus and settings rows ── */
/* Pro tag — the mint sprout badge (renderProTag). Self-coloured SVG, sized
   by height; the pill's own aspect ratio sets the width. */
.pro-tag { display: inline-block; flex-shrink: 0; height: 20px; width: auto; vertical-align: middle; }
/* Legacy text-pill fallback, kept for any stray .pro-chip references. */
.pro-chip {
  display: inline-flex; align-items: center; flex-shrink: 0;
  padding: var(--sp-05) var(--sp-15); border-radius: var(--r-pill);
  background: var(--accent); color: var(--accent-text, #171717);
  font-family: var(--font-sans); font-size: var(--fs-2xs); font-weight: 700;
  letter-spacing: var(--ls-caps); text-transform: uppercase; line-height: var(--lh-snug);
}

/* ── Dialogs — jobiConfirm / jobiPrompt ── */
.dlg-backdrop {
  position: fixed; inset: 0; z-index: var(--z-modal-backdrop);
  display: flex; align-items: center; justify-content: center;
  padding: var(--sp-6);
  background: rgba(0,0,0,0.4);
  backdrop-filter: blur(2px);
  opacity: 0; transition: opacity var(--dur-pop) var(--ease-out);
}
.dlg-backdrop.show { opacity: 1; }
.dlg {
  position: relative; z-index: var(--z-modal);
  width: 100%; max-width: 320px; box-sizing: border-box;
  padding: var(--sp-5);
  transform: scale(0.98);
  transition: transform var(--dur-pop) var(--ease-out);
}
.dlg-backdrop.show .dlg { transform: scale(1); }
.dlg-title {
  font-family: var(--font-sans); font-size: var(--fs-lg); font-weight: 700;
  color: var(--text); line-height: var(--lh-tight);
}
.dlg-body {
  margin-top: var(--sp-2);
  font-size: var(--fs-base); color: var(--text2); line-height: var(--lh-relaxed);
}
.dlg-body p { margin: 0 0 var(--sp-2); }
.dlg-body p:last-child { margin-bottom: 0; }
.dlg-field { display: flex; flex-direction: column; gap: var(--sp-15); margin-top: var(--sp-35); }
.dlg-field-label { font-size: var(--fs-sm); font-weight: 600; color: var(--text2); }
.dlg-actions { display: flex; gap: var(--sp-2); margin-top: var(--sp-5); }
.dlg-actions .btn { flex: 1; }

@media (prefers-reduced-motion: reduce) {
  .btn:active, .seg-opt:active { transform: none; }
  .dlg, .dlg-backdrop.show .dlg { transform: none; }
}

/* Dark mode body — flat near-black surface beneath the animated orbs
   canvas (#dark-orbs-canvas). The orbs glow ON TOP of this surface.
   (Dark palette tokens live in tokens.css.) */
body.dark {
  background-image: none;
  background-color: var(--bg);
}

/* Dark mode orbs canvas — fixed full-viewport backdrop, painted by
   the JS animation loop in ensureDarkOrbsCanvas(). z-index -1 keeps
   it behind all content; pointer-events: none lets clicks pass to
   the UI as if it weren't there. The mask fades the canvas to
   transparent past the upper ~55% of the viewport, so the orb reads
   as a soft directional glow filling the upper half of the popup
   and the lower section remains a clean dark surface for content. */
#dark-orbs-canvas {
  position: fixed;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  display: block;
  -webkit-mask-image: linear-gradient(to bottom, #000 0%, #000 45%, transparent 60%);
          mask-image: linear-gradient(to bottom, #000 0%, #000 45%, transparent 60%);
}

/* ── Dark mode polish ──────────────────────────────────────────────
   Surfaces that look subtly wrong in dark mode because they rely on
   colour tokens that were never re-defined for dark (like --tint),
   plus a couple of element-specific adjustments where a light-mode
   choice (purple shimmer, dark mascot ink) doesn't translate.
   ─────────────────────────────────────────────────────────────── */

/* Input wrap — same glass-over-gradient treatment as the cards.
   Sits at the bottom of the viewport where the gradient is at its
   darkest, so 5% alpha gives a subtle but visible pill outline
   without flattening the gradient. Focus bumps alpha for activity
   signal. The textarea inside (.chat-input) is already transparent,
   so the wrap is the only colour-bearing surface for the input. */
body.dark .chat-input-wrap                { background: var(--glass-1); }
body.dark .chat-input-wrap:focus-within   { background: rgba(255, 255, 255, 0.10); }

/* The outer input row (parent of .chat-input-wrap) uses var(--bg) by
   default, which clips the body's plum gradient at the bottom edge.
   Transparent here lets the gradient run all the way through the
   chat surface, including behind the input pill. */
body.dark .chat-input-row { background: transparent; }

/* Header is fully transparent in dark mode so the body's plum-charcoal
   gradient bleeds cleanly through the nav strip. The gradient's
   warmest stop is at the top of the viewport — exactly where the
   nav sits — so making the nav opaque would crop the most expressive
   part of the gradient. Icons stay at their default dark-mode colours,
   which read fine against the plum top. */
body.dark .header { background: transparent; }

/* Welcome heading shimmer — colour matches the dark mode orb (blue
   #3b82f6) so the shimmer reads as part of the same lit space. Use
   a slightly lighter, more luminous version (#60a5fa @ 90% alpha)
   so the shimmer pops against the warm-cream text rather than
   blending into it. */
body.dark .chat-welcome-heading {
  background-image: linear-gradient(
    100deg,
    var(--text) 0%,
    var(--text) 42%,
    rgba(96, 165, 250, 0.9) 50%,
    var(--text) 58%,
    var(--text) 100%
  );
}

/* Rive mascot — canvas-rendered, so its ink can't be styled via CSS
   per-path. The simplest correct fix is to invert the canvas colours:
   black line-art becomes white, transparent stays transparent. Works
   cleanly for paper-character mascots like Jobi's where the asset is
   essentially "ink on transparent". The eyes invert too, which is
   exactly what was asked for. Mesh mascot has its own per-element
   fills already, so it's not affected by this rule. */
body.dark #chat-mascot-canvas { filter: invert(1); }

/* ── Dark mode atmospheric chrome (non-chat screens) ─────────────────
   Storage, Jobs, Settings, and every other screen — make their major
   surfaces translucent so the body orb gradient shows through. Same
   pattern as chat surfaces: rgba(255,255,255,0.04-0.06) for default,
   bumped slightly on hover. Borders soften to subtle white tints
   instead of warm-brown tokens so they don't clash with the blue
   atmosphere. Brand orange (--accent) is preserved — it's the user's
   product identity and should still pop as a primary action.
   ─────────────────────────────────────────────────────────────── */

/* Storage tab bar — transparent so the section divider reads as a
   horizontal grouping line over the gradient rather than a strip
   of solid surface. */
body.dark .tab-bar { background: transparent; border-bottom-color: var(--glass-2); }

/* Storage section head + upload button */
body.dark .storage-upload-btn {
  background: var(--glass-1);
  border-color: transparent;
}
body.dark .storage-upload-btn:hover { background: var(--glass-2); }

/* Storage list — file items + case-study cards share the settings-row
   surface: translucent white fill, no border, hover one shade warmer. */
body.dark .storage-item,
body.dark .case-study-card {
  background: var(--glass-1);
  border-color: transparent;
}
body.dark .storage-item:hover,
body.dark .storage-item-link:hover,
body.dark .case-study-card:hover {
  background: var(--glass-2);
  border-color: transparent;
}

/* Storage drop zone (empty state) — filled surface, no stroke. */
body.dark .storage-empty-drop {
  background: var(--glass-1);
}
body.dark .storage-empty-drop.clickable:hover { background: var(--glass-2); }

/* Storage state messages (loading / error / empty text container) */
body.dark .storage-state { background: transparent; }

/* Job cards — same glass treatment. Status colour bars on the left
   edge are preserved (those are tinted via --st-color/--st-bg
   variables and aren't affected by background overrides). */
body.dark .job-card {
  background: rgba(255, 255, 255, 0.04);
  border-color: transparent;
}
body.dark .job-card:hover {
  background: rgba(255, 255, 255, 0.07);
  border-color: transparent;
}

/* Settings rows — each row is its own grouped-list surface. */
body.dark .s-row {
  background: var(--glass-1);
  border-color: transparent;
}
body.dark .s-row-link:hover { background: var(--glass-2); }

/* AI message bubbles — the warm-brown var(--ai-bubble) looks like a
   warm-mode element pasted into the blue atmosphere. Translucent white
   gives bubbles the same surface treatment as cards/input and lets the
   orb breathe through subtly behind the content. User bubbles are the
   neutral high-contrast surface (--user-bubble), so coral stays reserved
   for actions and never competes with the CTAs. */
body.dark .msg.ai .msg-bubble {
  background: none;
  color: var(--text);
}


/* Interview cards — the setup card ("ready to start?") and the options
   card ("what type of interview?") both used solid var(--bg2). The
   option buttons inside used var(--tint) (the same near-white-in-dark
   bug as the input pill before the earlier fix). Whole interview
   surface now matches the chat UI's glass treatment: translucent
   cards, translucent options, blue tint for the selected state. */
body.dark .interview-setup-card,
body.dark .interview-q-card {
  background: #1F1F1F;
}
body.dark .interview-q-option {
  background: rgba(255, 255, 255, 0.06);
  color: var(--text);
}
body.dark .interview-q-option:hover { background: rgba(255, 255, 255, 0.10); }
body.dark .interview-q-option.selected,
body.dark .interview-q-option.selected:hover {
  background: rgba(59, 130, 246, 0.22);
  color: #ffffff;
}
body.dark .interview-q-input {
  background: rgba(255, 255, 255, 0.06);
  border-color: transparent;
  color: var(--text);
}
body.dark .interview-q-input:focus {
  background: rgba(255, 255, 255, 0.10);
}

/* Interview CTA + submit follow the accent/ink tokens in BOTH themes
   (coral with dark text in dark). The old dark-only blue-strong override
   predated the accent rules and sat outside the system; the secondary
   keeps the quiet translucent treatment. */
body.dark .interview-setup-continue {
  background: var(--glass-1);
  color: var(--text2);
}
body.dark .interview-setup-continue:hover { background: var(--glass-2); color: var(--text); }

body.dark .iv-notice {
  background: rgba(255, 255, 255, 0.05);
  border-color: rgba(255, 255, 255, 0.10);
}


/* Interview screen body — transparent in dark mode so the body-level
   orb canvas (the same backdrop shown on the chat screen) reads
   through. In light mode the .iv-screen base + .iv-aurora-bg layer
   work together for the cream-and-purple aurora; in dark mode the
   body orb is the singular atmospheric layer. */
body.dark .iv-screen { background: var(--bg); }

/* ── Interview orb slot ──────────────────────────────────────────
   Replaces the mascot in the interview screen. The orb (ElevenLabs UI
   port) renders inside a circular framed container — matches the
   demo's "rounded-full with inset shadow" wrapper. The frame provides
   visual containment so the orb's dark body reads as an "object on
   the page" rather than a black blob. */
.iv-orb-slot {
  width: 124px;
  height: 124px;
  margin: 24px auto 32px;
  flex-shrink: 0;
  border-radius: 50%;
  overflow: hidden;
  background: rgba(0, 0, 0, 0.35);
  box-shadow:
    inset 0 2px 8px rgba(0, 0, 0, 0.5),
    inset 0 0 12px rgba(0, 0, 0, 0.3);
  display: flex;
  align-items: center;
  justify-content: center;
  pointer-events: none;
}
.iv-orb-canvas {
  display: block;
  width: 100%;
  height: 100%;
}

/* The orb is absolutely pinned to the centre of .iv-center-area, so it
   never moves as the text below it changes length. margin:0 overrides
   the standalone slot margins. */
.iv-screen .iv-center-area .iv-orb-slot {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  margin: 0;
}

/* Mesh mascot — the visible "stroke" is the gradient-filled ring
   (rects clipped to the donut shape), and the eyes are filled with
   the same gradient. In dark mode the default mesh gradient's top
   stops are near-black, so the upper half reads muddy-on-dark.
   Override to the plum-pink stroke gradient defined inside the SVG
   (#meshDarkStroke) — vertical, pink at top to plum at bottom, the
   same "lit from above" feel as the body gradient. The body fill
   stays at var(--bg) so the mascot interior continues to match the
   chat surface. */
body.dark .mesh-mascot-svg rect { fill: url(#meshDarkStroke); }
body.dark .mesh-eye { fill: url(#meshDarkStroke); }

body {
  width: 100%;
  height: 100vh;
  min-width: 320px;
  background: var(--bg);
  color: var(--text);
  font-family: var(--font-sans);
  font-size: var(--fs-md);
  line-height: var(--lh-normal);
  overflow: hidden;
  transition: background 0.22s ease, color 0.22s ease;
}

::-webkit-scrollbar { width: 3px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb { background: var(--border2); border-radius: 3px; }

#root { height: 100vh; display: flex; flex-direction: column; }
.app  { display: flex; flex-direction: column; height: 100%; }

/* ──────────────────────────────────────
   HEADER
────────────────────────────────────── */
.header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0 var(--sp-35);
  height: 50px;
  /* No bottom border. The new chat UI uses fill-based separation (no
     strokes anywhere); a hard line under the nav would clash with that
     language. Whitespace + content positioning provides the boundary. */
  background: var(--bg);
  flex-shrink: 0;
  transition: background 0.22s ease;
  /* Sits above the aurora overlay (z-index 100) so nav icons and the
     logo stay visible and clickable when aurora theme is active. */
  position: relative;
  z-index: 200;
}

.header-left { display: flex; align-items: center; gap: var(--sp-2); }

.back-btn {
  width: 30px; height: 30px;
  background: transparent; border: none;
  /* Circular — matches the new fill-based, no-corners language of the
     suggestion cards and input pill. */
  border-radius: 50%;
  color: var(--text3); cursor: pointer;
  display: flex; align-items: center; justify-content: center;
  transition: all var(--t); margin-left: -4px;
}
.back-btn:hover { background: var(--bg2); color: var(--text); }
.back-btn svg { width: 16px; height: 16px; }

.logo {
  width: 26px; height: 26px;
  background: var(--ink);
  border-radius: var(--r-sm);
  display: flex; align-items: center; justify-content: center;
  flex-shrink: 0;
  transition: background 0.22s ease;
}
.logo svg { width: 13px; height: 13px; fill: var(--bg); }

.header-right { display: flex; align-items: center; gap: var(--sp-05); }

/* Circular icon buttons — fill comes in on hover (--bg2, lighter)
   and stays on active (--bg3, slightly stronger) so the current screen
   reads as visually pressed-in. Three-tier interaction state: rest =
   transparent, hover = subtle warm fill, active = a touch firmer. */
.icon-btn {
  width: 32px; height: 32px;
  background: transparent; border: none;
  border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  cursor: pointer; color: var(--text3);
  transition: background var(--t), color var(--t), transform var(--dur-press) var(--ease-out);
  position: relative;
}
.icon-btn:hover  { background: var(--tint); color: var(--text2); }
/* --tint is deliberately not re-anchored in dark (tokens.css), so give the
   header icons a glass hover instead of a near-white flash. */
body.dark .icon-btn:hover { background: var(--glass-2); }
.icon-btn.active { background: var(--bg2);  color: var(--text);  }
.icon-btn:active { transform: scale(0.98); }
.icon-btn svg { width: 17px; height: 17px; }

/* ──────────────────────────────────────
   CONTENT
────────────────────────────────────── */
.content { flex: 1; overflow: hidden; display: flex; flex-direction: column; }

@keyframes slideIn   { from { opacity:0; transform:translateX(10px);  } to { opacity:1; transform:translateX(0); } }
@keyframes slideBack { from { opacity:0; transform:translateX(-10px); } to { opacity:1; transform:translateX(0); } }
.view-enter { animation: slideIn   0.17s ease; }
.view-back  { animation: slideBack 0.17s ease; }

/* ──────────────────────────────────────
   CHAT
────────────────────────────────────── */

.msg.user 
.msg.ai 

/* Chips */

/* Input row */

.mic-btn svg { width: 15px; height: 15px; }
@keyframes micPulse { 0%,100% { box-shadow:0 0 0 0 color-mix(in srgb, var(--accent) 30%, transparent); } 50% { box-shadow:0 0 0 5px transparent; } }

.send-btn svg { width: 14px; height: 14px; }

/* ──────────────────────────────────────
   SCREEN WRAPPER
────────────────────────────────────── */
.screen-view { display: flex; flex-direction: column; height: 100%; }

/* ──────────────────────────────────────
   TABS
────────────────────────────────────── */
.tab-bar {
  display: flex; border-bottom: 1px solid var(--border);
  padding: 0 var(--sp-45); flex-shrink: 0; background: var(--bg);
  transition: background 0.22s ease;
  overflow-x: auto;
  scrollbar-width: none;
}
.tab-bar::-webkit-scrollbar { display: none; }
.tab-btn {
  padding: var(--sp-25) var(--sp-25) var(--sp-2);
  font-size: var(--fs-base); font-weight: 500; color: var(--text3);
  cursor: pointer; border: none; background: transparent;
  border-bottom: 2px solid transparent; margin-bottom: -1px;
  transition: all var(--t); font-family: var(--font-sans);
  display: flex; align-items: center; gap: var(--sp-1);
  white-space: nowrap; flex-shrink: 0;
}
.tab-btn:hover  { color: var(--text2); }
.tab-btn.active { color: var(--text); border-bottom-color: var(--text); }
.tab-btn:focus-visible, .storage-upload-btn:focus-visible, .mini-btn:focus-visible {
  outline: none; box-shadow: var(--ring);
}

/* Storage screen header — back to Settings + title above the tab bar. */

.screen-body {
  flex: 1; overflow-y: auto; overflow-x: hidden;
  padding: var(--sp-4) var(--sp-45) var(--sp-5);
  display: flex; flex-direction: column; gap: var(--sp-1);
}
/* In a side panel the user can drag the width quite wide; cap the content
   column so forms and lists stay a comfortable reading width and centre it,
   rather than stretching edge-to-edge. */
.screen-body > * { width: 100%; max-width: 560px; margin-left: auto; margin-right: auto; }

/* ──────────────────────────────────────
   JOBS SCREEN
────────────────────────────────────── */
/* New-job recommendation card */

/* NAV — arrows + reject (✕), pinned at the bottom (the centred card group
   above takes the flexible space). */
.jc-nav-arrow {
  width: 38px; height: 38px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  background: var(--bg);
  border: 1px solid var(--border2);
  color: var(--text2);
  cursor: pointer;
  transition: background var(--t), border-color var(--t), color var(--t);
}
.jc-nav-arrow:hover:not(:disabled) {
  background: var(--accent-bg);
  border-color: var(--accent);
  color: var(--accent);
}
.jc-nav-arrow:disabled {
  opacity: 0.35;
  cursor: default;
}
.jc-nav-arrow svg { width: 18px; height: 18px; }

/* Reject (✕) — slightly larger, centred between the arrows. */
.jc-nav-reject {
  width: 46px; height: 46px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  background: var(--bg);
  border: 1.5px solid var(--border2);
  color: var(--text3);
  cursor: pointer;
  transition: background var(--t), border-color var(--t), color var(--t);
}
.jc-nav-reject:hover {
  background: var(--red-bg, rgba(200,60,60,0.08));
  border-color: var(--red, #c83c3c);
  color: var(--red, #c83c3c);
}
.jc-nav-reject svg { width: 20px; height: 20px; }

/* Empty states — centre vertically in the panel rather than sitting high
   with a void beneath (the panel is much taller than the old popup). */
.jobs-empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  flex: 1;
  min-height: 460px;
  padding: 32px 30px 44px;
}
.jobs-empty-hero { display: flex; flex-direction: column; align-items: center; }
.jobs-empty-illus { width: 122px; margin-bottom: var(--sp-6); }
.jobs-empty-illus .jt-mascot { width: 100%; height: auto; display: block; }
/* A subtle, occasional blink of the mascot's right (magnified) eye. */
.jt-eye-blink { transform-box: fill-box; transform-origin: center; animation: jt-blink 5.6s ease-in-out infinite; }
@keyframes jt-blink { 0%, 94%, 100% { transform: scaleY(1); } 97% { transform: scaleY(0.08); } }
@media (prefers-reduced-motion: reduce) { .jt-eye-blink { animation: none; } }
.jobs-empty-title { font-family: var(--font-serif); font-size: 18px; font-weight: 600; letter-spacing: var(--ls-tight); color: var(--text); margin-bottom: var(--sp-25); }
.jobs-empty-sub { font-family: var(--font-sans); font-size: var(--fs-base); color: var(--text2); line-height: 1.55; max-width: 300px; }

/* ──────────────────────────────────────
   TRACKED JOBS (live backend)
────────────────────────────────────── */
/* Job cards use the settings surface language: borderless filled surface,
   large radius, hover just warms the fill (no stroke). */
.job-card {
  position: relative;
  background: var(--bg2);
  border: none;
  border-radius: var(--r-lg);
  margin-bottom: var(--sp-2);
  transition: background var(--t);
  cursor: pointer;   /* the whole card opens its drawer */
}
.job-card:hover { background: var(--bg3); }
.job-card:focus-visible { outline: none; box-shadow: var(--ring); }
/* Cards are a neutral surface (matching the Discover hero) — the status
   colour lives in the pill/tags, not a full-card wash. */
.job-card::before { display: none; }
.job-card > * { position: relative; }
.job-card:hover { background: var(--bg3); }
.job-card-closed { opacity: 0.66; }

/* ── Tracker: a single card list (table mode removed) ── */
.jobs-track-body > * { max-width: 1100px; }

/* Page-aware banner: "save this job" / "in your tracker" (zero-cost feature). */
.pagejob {
  width: 100%; flex-shrink: 0; display: flex; align-items: center; gap: 11px;
  margin: 12px auto 2px; padding: 13px 15px;
  border-radius: var(--r-lg); text-align: left;
  border: none; background: var(--bg2);
  font-family: var(--font-sans);
}
/* Save state: a left "main" button + a divider'd pencil on the right. */

.pagejob .company-logo { flex: 0 0 auto; width: 30px; height: 30px; }
.pagejob .company-logo-mono { font-size: var(--fs-sm); }

/* "Add to tracker?" card: company pill top-right, title + sub, Save/Applied. */
.pagejob-add {
  position: relative; display: block; overflow: hidden;
  padding: var(--sp-4); border: none; background: var(--hero-bg);
  /* Animatable so the banner can slide down on appear and up on dismiss,
     collapsing its own height + margins so the content below moves with it
     (like a notification). overflow:hidden clips the content as it collapses. */
  max-height: 260px;
  transition: max-height 0.34s var(--ease-in-out),
              opacity 0.24s var(--ease-out),
              transform 0.24s var(--ease-out),
              margin-top 0.34s var(--ease-in-out),
              margin-bottom 0.34s var(--ease-in-out),
              padding-top 0.34s var(--ease-in-out),
              padding-bottom 0.34s var(--ease-in-out);
}
/* Collapsed = hidden state, used for both the pre-entrance frame and the
   dismiss exit. Same end state → enter and exit mirror each other. */
.pagejob-add.is-collapsed {
  max-height: 0;
  opacity: 0;
  transform: translateY(-4px);
  margin-top: 0;
  margin-bottom: 0;
  padding-top: 0;
  padding-bottom: 0;
}
@media (prefers-reduced-motion: reduce) {
  .pagejob-add { transition: opacity 0.2s ease, max-height 0.2s ease; }
  .pagejob-add.is-collapsed { transform: none; }
}
.pagejob-add-close {
  position: absolute; top: 9px; right: 9px; z-index: 2;
  width: 26px; height: 26px; display: flex; align-items: center; justify-content: center;
  background: none; border: none; border-radius: 6px; cursor: pointer;
  color: rgba(255,255,255,0.45); transition: color var(--t), background var(--t);
}
.pagejob-add-close:hover { color: #fff; background: rgba(255,255,255,0.08); }
.pagejob-add-close svg { width: 16px; height: 16px; }
.pagejob-add-title { font-size: 16px; font-weight: 700; color: #fff; letter-spacing: var(--ls-tight); padding-right: 34px; }
.pagejob-add-sub { font-size: var(--fs-sm); color: rgba(255,255,255,0.6); line-height: 1.4; margin-top: 5px; }
.pagejob-add-actions { display: flex; gap: 9px; margin-top: var(--sp-35); justify-content: flex-end; }
.pagejob-add-btn {
  height: 26px; padding: 0 var(--sp-4); border: none; border-radius: 6px;
  font-family: var(--font-sans); font-size: 12.5px; font-weight: 600; cursor: pointer;
  transition: background var(--t), filter var(--t);
}
.pagejob-add-secondary { background: var(--hero-accent-bg); color: #fff; }
.pagejob-add-secondary:hover { background: var(--hero-accent-bg-hover); }
.pagejob-add-primary { background: var(--hero-accent); color: var(--hero-accent-text); }
.pagejob-add-primary:hover { filter: brightness(1.05); }
/* Processing: a slight dim + progress cursor is enough — the label already
   says "Saving…"/"Applying…", so no spinner. */
.pagejob-add-btn.is-processing { opacity: 0.72; cursor: progress; }

/* Action toast (e.g. Undo after a one-tap save). */
.jobi-toast.jobi-toast-action { display: flex; align-items: center; gap: 14px; text-align: left; pointer-events: auto; }
.jobi-toast-msg { flex: 1 1 auto; min-width: 0; }
.jobi-toast-btn { background: none; border: none; color: var(--accent); font-family: inherit; font-weight: 600; font-size: var(--fs-base); cursor: pointer; padding: 5px 9px; margin: -3px -5px -3px 0; border-radius: 8px; flex-shrink: 0; transition: background 0.14s ease; }
.jobi-toast-btn:hover { background: var(--accent-bg); }
.job-card-main {
  display: flex; align-items: flex-start; gap: 13px;
  padding: 15px 16px;
}
.job-card-body { flex: 1; min-width: 0; }
/* Top row: title + company on the left, status pill (+ pin) on the right.
   The meta/applied/tags rows live BELOW this row (siblings, not nested), so
   they span the body's full width — from the logo to the card's right edge —
   instead of being boxed into the narrow column left of the status pill. */
.job-card-head { display: flex; align-items: flex-start; gap: var(--sp-25); }
.job-card-headings { flex: 1; min-width: 0; }
.job-card-title {
  font-family: var(--font-sans); font-size: var(--fs-md); font-weight: 600; color: var(--text);
  letter-spacing: var(--ls-tight);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
/* Company sits a clear step below the title, one line, one shade brighter
   than the meta below it so the hierarchy reads title > company > meta. */
.job-card-co {
  font-size: var(--fs-sm); color: var(--text2); margin-top: var(--sp-05);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.job-card-applied { margin-top: var(--sp-1); font-size: var(--fs-xs); color: var(--text4); }
/* Meta row under the card body: the "Cover letter attached" note (and, if it
   returns, the parked Tailored-CV chip). Left-aligned so it reads as part of
   the meta stack beneath "Applied". */
.job-card-tags { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin-top: 6px; justify-content: flex-start; }
/* "Cover letter attached" — a quiet, non-interactive note. The letter opens
   from the drawer, not here, so this is a plain indicator, not a button. */
.job-card-attached { display: inline-flex; align-items: center; gap: 5px; font-family: var(--font-sans); font-size: 11.5px; font-weight: 500; color: var(--text3); }
.job-card-attached svg { width: 12px; height: 12px; opacity: 0.85; flex-shrink: 0; }
/* Tailored-CV chip (parked behind TAILOR_CV_ENABLED): a solid black tag with
   white text, distinguished by its icon + label. */
.job-card-tag {
  display: inline-flex; align-items: center; gap: 5px;
  padding: 5px 10px; border-radius: var(--r-sm);
  background: #111111; color: #ffffff;
  font-family: var(--font-sans);
  font-size: 11.5px; font-weight: 500; line-height: 1;
  text-decoration: none; cursor: pointer;
  border: none;
  transition: background var(--t);
}
.job-card-tag:hover { background: #232323; }
.job-card-tag svg { width: 12px; height: 12px; opacity: 0.9; }
.job-card-right { display: flex; align-items: center; gap: var(--sp-15); flex-shrink: 0; }
/* Card pin: a small badge on the company logo's top-left corner, out of the
   status-pill cluster. Quiet until card hover; accent + always-visible once
   pinned. Absolute so it never reserves layout space or shifts the row. */
.job-card-pin {
  position: absolute; top: 7px; left: 7px; z-index: 4;
  width: 22px; height: 22px; border-radius: 50%; padding: 0;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--bg2); border: none; color: var(--text3); cursor: pointer;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.35);
  opacity: 0; transition: opacity var(--t), color var(--t), background var(--t);
}
.job-card-pin svg { width: 13px; height: 13px; }
.job-card:hover .job-card-pin { opacity: 1; }
.job-card-pin:hover { color: var(--text); background: var(--bg3); }
.job-card-pin.is-pinned { opacity: 1; color: var(--accent); }

/* Soft-tint tag: the status colour as text on its own faint tint. This
   guarantees strong text contrast (a saturated hue on a muted version of
   itself) regardless of theme — no per-status white/dark juggling. */
.job-status-pill {
  display: inline-flex; align-items: center; gap: var(--sp-1);
  padding: 5px 10px; border-radius: 6px;
  font-family: var(--font-sans); font-size: var(--fs-xs); font-weight: 600;
  border: none; cursor: pointer; white-space: nowrap;
  transition: all var(--t);
  background: var(--st-bg, var(--bg3));
  color: var(--st-text, var(--text2));
}
.job-status-pill svg { width: 11px; height: 11px; opacity: 0.9; }
.job-status-pill:hover { filter: brightness(1.08); }

/* Per-status colour identity. One source per status drives the card's left
   accent stripe, the status pill, and the JD tag together — so every status
   reads as a distinct colour and same-status elements always match.
   Palette is warm/earthy to sit with Jobi's burnt-orange + beige aesthetic:
   sand → terracotta → clay-rose → olive, with warm brick/taupe for closed. */
/* ── Stage colours — one token set with distinct hues, switched by theme.
   Drives the card stripe + wash, the status pills, JD tags, the summary bar,
   section labels and the stage picker, so every surface stays in lockstep.
   Applied=blue, Interviewing=violet, Offer=green, Rejected=red, Saved=amber,
   Withdrawn=grey — a clear spread instead of the old all-warm family. ── */
/* Per-status palette — a clear semantic progression so each stage reads
   at a glance: amber = saved/pending, blue = applied/in-pipeline,
   purple = interviewing/active, green = offer/win, red = rejected/no,
   grey = withdrawn/inactive. Values are tuned bright + saturated to pop
   on the dark surface (the wallet-UI direction). */
/* Stage palette tokens (--stg-*) live in tokens.css. */
.job-card.jc-saved,        .jt-row.jc-saved,        .pagejob.jc-saved        { --st-color:var(--stg-saved);        --st-bg:var(--stg-saved-bg);        --st-text:var(--stg-saved-text); }
.job-card.jc-applied,      .jt-row.jc-applied,      .pagejob.jc-applied      { --st-color:var(--stg-applied);      --st-bg:var(--stg-applied-bg);      --st-text:var(--stg-applied-text); }
.job-card.jc-interviewing, .jt-row.jc-interviewing, .pagejob.jc-interviewing { --st-color:var(--stg-interviewing); --st-bg:var(--stg-interviewing-bg); --st-text:var(--stg-interviewing-text); }
.job-card.jc-offer,        .jt-row.jc-offer,        .pagejob.jc-offer        { --st-color:var(--stg-offer);        --st-bg:var(--stg-offer-bg);        --st-text:var(--stg-offer-text); }
.job-card.jc-rejected,     .jt-row.jc-rejected,     .pagejob.jc-rejected     { --st-color:var(--stg-rejected);     --st-bg:var(--stg-rejected-bg);     --st-text:var(--stg-rejected-text); }
.job-card.jc-withdrawn,    .jt-row.jc-withdrawn,    .pagejob.jc-withdrawn    { --st-color:var(--stg-withdrawn);    --st-bg:var(--stg-withdrawn-bg);    --st-text:var(--stg-withdrawn-text); }

/* Status dropdown menu */
/* Status dropdown — a popover anchored under the status pill (it no longer
   expands the card/row). The pill's container is the positioning context. */
.job-card-right, td.jt-status { position: relative; }
.job-status-menu {
  position: absolute; top: calc(100% + 6px); right: 0; z-index: var(--z-dropdown);
  min-width: 184px;
  background: var(--bg2); border: 1px solid var(--border);
  border-radius: var(--r); box-shadow: 0 10px 28px rgba(0,0,0,0.38);
  padding: 5px; display: flex; flex-direction: column;
}
body.dark .job-status-menu { background: #2a2a2a; border-color: rgba(255,255,255,0.12); }
.job-status-opt {
  display: flex; align-items: center; gap: var(--sp-2);
  text-align: left;
  padding: var(--sp-2) var(--sp-25); border-radius: 6px;
  background: none; border: none; cursor: pointer; white-space: nowrap;
  font-family: var(--font-sans); font-size: 12.5px; color: var(--text2);
  transition: background var(--t), color var(--t);
}
.job-status-opt svg { width: 14px; height: 14px; flex: 0 0 auto; }
.job-status-opt:hover { background: var(--bg3); color: var(--text); }
.job-status-opt.current { color: var(--text); font-weight: 600; }
/* Colored dot per stage in the menu. */
.jso-dot { width: 8px; height: 8px; border-radius: 50%; flex: 0 0 auto; }
.jso-saved        .jso-dot { background: var(--stg-saved); }
.jso-applied      .jso-dot { background: var(--stg-applied); }
.jso-interviewing .jso-dot { background: var(--stg-interviewing); }
.jso-offer        .jso-dot { background: var(--stg-offer); }
.jso-rejected     .jso-dot { background: var(--stg-rejected); }
.job-status-del { color: var(--red); margin-top: 3px; border-top: 1px solid var(--border); border-radius: 0; padding-top: 9px; }
body.dark .job-status-del { border-top-color: rgba(255,255,255,0.08); }
.job-status-del:hover { background: var(--red-bg); color: var(--red); }

/* Closed (rejected/withdrawn) — quiet, collapsed */
.jobs-closed-toggle {
  display: flex; align-items: center; justify-content: space-between;
  width: 100%;
  margin-top: var(--sp-35); padding: 9px 4px;
  background: none; border: none; cursor: pointer;
  font-family: var(--font-sans); font-size: var(--fs-md); font-weight: 700;
  letter-spacing: var(--ls-tight); color: var(--text);
  border-top: 1px solid var(--border);
}

/* Add-job form */
/* Fills the scroll area so the actions can anchor to the bottom. */
.jobs-add-form { display: flex; flex-direction: column; gap: var(--sp-5); padding: var(--sp-2) 0; flex: 1; }
.jobs-textarea { min-height: 92px; resize: vertical; line-height: 1.5; }
/* Stacked action buttons — accent (save) on top, secondary (cancel)
   below, no strokes. Pushed to the bottom of the form. */
.jobs-add-actions { margin-top: auto; padding-top: var(--sp-4); display: flex; flex-direction: column; gap: var(--sp-2); }
.jobs-add-actions .ob-auth-error:empty { display: none; }

/* Sort dropdown — standard menu, right-aligned. */
.jobs-sort-btn {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-15);
  padding: 7px 12px;
  background: var(--ai-bg);
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
  font-family: var(--font-sans);
  font-size: 12.5px;
  font-weight: 600;
  color: var(--text2);
  cursor: pointer;
  transition: border-color var(--t), background var(--t);
}
.jobs-sort-btn:hover { border-color: var(--border2); }
.jobs-sort-btn svg { width: 14px; height: 14px; }

/* Grouped-list rows (settings language): each file is a filled bg2 surface,
   no border, rows 2px apart with large outer / small inner corners. */
.storage-item {
  display: flex; align-items: center; gap: var(--sp-3);
  padding: 13px 16px; min-height: 56px;
  background: var(--bg2); border: none;
  border-radius: var(--r-xs); transition: background var(--t);
}
.storage-list > .storage-item:first-child { border-top-left-radius: var(--r-xl); border-top-right-radius: var(--r-xl); }
.storage-list > .storage-item:last-child { border-bottom-left-radius: var(--r-xl); border-bottom-right-radius: var(--r-xl); }
.storage-item:hover { background: var(--bg3); }
/* Link cards are clickable — the whole row opens the URL. */
.storage-item-link { cursor: pointer; }
.storage-item-link:hover { background: var(--bg3); }
.file-open-cue {
  display: flex; align-items: center; justify-content: center;
  width: 24px; height: 24px; color: var(--text4);
  transition: color var(--t);
}
.file-open-cue svg { width: 13px; height: 13px; }
.storage-item-link:hover .file-open-cue { color: var(--accent); }

.file-type-icon {
  width: 36px; height: 36px; border-radius: var(--r-sm);
  display: flex; align-items: center; justify-content: center;
  flex-shrink: 0;
}
.fi-pdf  { background: var(--red-bg);    }
.fi-link { background: var(--orange-bg); }
.file-type-icon svg { width: 16px; height: 16px; }
.fi-pdf  svg { color: var(--red);    }
.fi-link svg { color: var(--orange); }

.file-info { flex: 1; min-width: 0; }
.file-name { font-size: var(--fs-base); font-weight: 500; color: var(--text); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.file-meta { font-size: var(--fs-sm); color: var(--text3); margin-top: var(--sp-05); }

.file-actions { display: flex; gap: var(--sp-05); }
.mini-btn {
  width: 28px; height: 28px; background: transparent; border: none;
  border-radius: var(--r-sm); cursor: pointer; color: var(--text3);
  display: flex; align-items: center; justify-content: center;
  transition: all var(--t);
}
.mini-btn:hover        { background: var(--bg3); color: var(--text2); }
.mini-btn.danger:hover { background: var(--red-bg); color: var(--red); }
.mini-btn svg { width: 12px; height: 12px; }

/* ──────────────────────────────────────
   SETTINGS — flat sectioned layout
────────────────────────────────────── */
.settings-body {
  flex: 1; overflow-y: auto; overflow-x: hidden;
  padding: var(--sp-45) var(--sp-45) var(--sp-8);
  display: flex; flex-direction: column;
}
.settings-body::-webkit-scrollbar { width: 0; }
/* Centre settings content in wider panels, matching .screen-body. */
.settings-body > * { width: 100%; max-width: 560px; margin-left: auto; margin-right: auto; }

/* Account card — Google pfp + name + plan badge, tappable into Profile.
   Standalone group so it carries the large radius on all four corners. */
.s-acct-row { padding: var(--sp-35) var(--sp-4); gap: var(--sp-3); }
.s-acct-avatar {
  width: 40px; height: 40px; border-radius: 50%;
  background: var(--ink); color: var(--bg);
  display: flex; align-items: center; justify-content: center;
  font-size: var(--fs-md); font-weight: 600; flex-shrink: 0;
}
.s-acct-avatar-img {
  width: 40px; height: 40px; border-radius: 50%;
  object-fit: cover; flex-shrink: 0; background: var(--bg3);
}
.s-acct-name {
  font-size: var(--fs-lg); font-weight: 700; color: var(--text);
  letter-spacing: var(--ls-tight);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* Larger Pro tag on the account card (it's the identity header, not a row chip). */
.s-acct-row .pro-tag { height: 27px; }


/* Settings "Jobi+" upgrade card (free users): brand logo, the same four
   bullets as the paywall, and a compact coral "Get Jobi+" CTA bottom-right. */
/* Jobi+ upsell — an always-dark branded card (from the approved Subscription
   card design): a sprout-crown badge rises over the top-right, the wordmark
   and sprout-bulleted features sit below, and a salmon CTA closes it. Self-
   contained fixed colours (like the paywall), so it looks identical in both
   themes rather than following the settings surface. */
/* Climbing card — Jobi hangs off the top edge of a card. Two copies of the
   same-viewBox art anchor to the card's top: head + eyes behind (z0), the
   card (z1), the arms in front gripping over the edge (z2). Both mascot
   layers are SIBLINGS of the card in one coordinate space, so they stay
   pixel-aligned. The cut line is at svg y=130 (just under the eyes), so the
   layers ride margin-top:-28.95% (130/449 of card width; margin percentages
   resolve against width), and the wrap reserves that head extent above. */
.climb-wrap {
  position: relative;
  flex-shrink: 0;            /* never get compressed as a .settings-body flex item */
  width: 100%;
  margin: 29% 0 var(--sp-55);
}
.climb-layer {
  position: absolute; top: 0; left: 0; width: 100%;
  margin-top: -28.95%;
  pointer-events: none;
}
.climb-layer svg { display: block; width: 100%; height: auto; }
.climb-back { z-index: 0; }
.climb-arms { z-index: 2; }
.climb-wrap > .pro-card,
.climb-wrap > .pro-feature-card { position: relative; z-index: 1; }

.pro-card {
  display: block;
  background: #1c1c1e; border-radius: 24px;
  padding: 30px 26px 22px;
  overflow: visible;
}
.pro-card .jobiplus-logo { height: 30px; width: auto; display: block; position: relative; }
.pro-card .pro-features { margin-top: var(--sp-5); display: flex; flex-direction: column; gap: var(--sp-3); }
.pro-card .pro-feat { display: flex; align-items: center; gap: var(--sp-25); }
.pro-card .pro-feat-sprout { flex: 0 0 auto; width: 18px; height: 18px; display: block; }
.pro-card .pro-feat-sprout svg { display: block; width: 100%; height: 100%; }
.pro-card .pro-feat-label { font-size: var(--fs-base); font-weight: 500; color: #c6c2be; }
/* Salmon CTA — always the mockup's fixed salmon/ink, not the theme accent, so
   it reads on the always-dark card in both themes. Centred pill. */
.pro-card-cta {
  display: block; margin: var(--sp-6) auto var(--sp-05);
  height: 44px; padding: 0 var(--sp-8);
  border: none; border-radius: var(--r-pill);
  background: #f29fa0; color: #171717;
  font-family: var(--font-sans); font-size: var(--fs-md); font-weight: 700; line-height: 44px;
  cursor: pointer;
  transition: background var(--t), transform var(--dur-press) var(--ease-out);
}
.pro-card-cta:hover { background: #f6b1b2; }
.pro-card-cta:active { transform: scale(0.98); }
.pro-card-cta:focus-visible { outline: none; box-shadow: 0 0 0 3px rgba(242, 159, 160, 0.4); }
/* Clean, self-contained mascot wrapper (happy-jobi in onboarding done). */
.pro-mascot-wrap { display: block; width: 100%; height: 100%; }
.pro-mascot-wrap .jobi-grad-svg { display: block; width: 100%; height: 100%; }

/* Section — title sits above each filled card group. Title kept small
   and uppercase: it's a scanning aid, not content. Making 5 of these
   Playfair would create competing headlines. */
.s-section { margin-bottom: var(--sp-45); }
.s-section-title {
  font-size: var(--fs-xs); font-weight: 600; letter-spacing: var(--ls-caps);
  text-transform: uppercase; color: var(--text3);
  padding: 0 var(--sp-15) var(--sp-25);
}

/* Section group — the card dissolves into individual row surfaces:
   rows in a group sit 2px apart, big radius on the group's outer
   corners, small on the shared inner ones (the grouped-list rhythm
   of the reference). The hairline gap does the dividing, so there
   are no strokes anywhere. */
.s-section-card {
  background: none; border: none; border-radius: 0;
  overflow: visible; padding: 0;
  display: flex; flex-direction: column; gap: var(--sp-05);
}

/* Uniform setting row — each row is its own filled surface. Small
   radius by default; the group's first/last rows pick up the large
   outer corners below. */
.s-row {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--sp-3);
  padding: var(--sp-35) var(--sp-4); min-height: 56px;
  background: var(--bg2); border: none; border-radius: var(--r-xs);
  box-sizing: border-box; transition: background var(--t);
}
.s-section-card > .s-row:first-child { border-top-left-radius: var(--r-xl); border-top-right-radius: var(--r-xl); }
.s-section-card > .s-row:last-child { border-bottom-left-radius: var(--r-xl); border-bottom-right-radius: var(--r-xl); }
.s-row-text { flex: 1; min-width: 0; }
.s-row-label { font-size: var(--fs-base); font-weight: 500; color: var(--text); display: flex; align-items: center; gap: var(--sp-15); }
.s-row-desc { font-size: var(--fs-sm); color: var(--text3); margin-top: var(--sp-05); line-height: var(--lh-snug); }
.s-row-value { font-size: var(--fs-sm); color: var(--text3); font-family: monospace; }
.s-prof-v { font-size: var(--fs-base); color: var(--text2); text-align: right; max-width: 62%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* Stacked row — wide controls (selects, the theme seg) never fight the
   text for width: glyph + text share the first line exactly like a
   normal row, and the control fills its own full-width line below,
   symmetric inside the card (a partial indent reads as accidental). */
.s-row-stack { flex-direction: column; align-items: stretch; gap: var(--sp-25); }
.s-row-main { display: flex; align-items: center; gap: var(--sp-3); }
.s-row-ctl { display: flex; }
.s-row-ctl .dd,
.s-row-ctl .seg { flex: 1 1 auto; max-width: none; }

.s-row-danger { cursor: pointer; }
.s-row-danger .s-row-label { color: var(--red); }
.s-row-danger .s-row-glyph { color: var(--red); }
.s-row-danger:hover { background: var(--red-bg); }

/* Dictation shortcut keycap: shows the bound combo; click → "Press keys…"
   recording state until a new combination (or Escape) lands. */
.s-keycap {
  font-family: monospace; font-size: var(--fs-sm); font-weight: 600;
  color: var(--text2); background: none;
  border: 1px solid var(--border); border-bottom-width: 2px; border-radius: var(--r-sm);
  padding: var(--sp-1) var(--sp-25); cursor: pointer; white-space: nowrap;
  transition: color var(--t), border-color var(--t);
}
.s-keycap:hover { color: var(--text); border-color: var(--text3); }
.s-keycap.recording { color: var(--accent); border-color: var(--accent); border-bottom-width: 1px; }

/* Sub-page top bar (settings sub-views, Storage, Cover letter template):
   back chevron pinned left, centred serif title. The bar is the lighter
   strip; the content scrolling beneath it sits on the darker screen bg. */
.s-subbar {
  flex-shrink: 0;
  display: grid; grid-template-columns: 44px minmax(0, 1fr) 44px;
  align-items: center;
  min-height: 64px; padding: var(--sp-4) var(--sp-2) var(--sp-15);
  background: var(--bg2);
}
.s-subbar-title {
  grid-column: 2; margin: 0;
  font-family: var(--font-serif); font-size: var(--fs-2xl); font-weight: 700;
  color: var(--text); letter-spacing: var(--ls-tight); text-align: center;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.s-subhead-back { width: 34px; height: 34px; flex: 0 0 auto; display: inline-flex; align-items: center; justify-content: center; border: none; background: none; color: var(--text2); border-radius: var(--r); cursor: pointer; transition: background var(--t), color var(--t); }
.s-subhead-back:hover { background: var(--bg2); color: var(--text); }
.s-subhead-back svg { width: 20px; height: 20px; }
.s-subbar .s-subhead-back { grid-column: 1; justify-self: start; }
.s-subbar .s-subhead-back:hover { background: var(--bg3); }

/* Editable profile form — the settings grouped list, but each row holds a
   field. Purpose line under the header sits flush left (settings-body
   children are width:100%, so extra left margin overflows into a scroll). */
.s-prof-sub { font-size: var(--fs-base); color: var(--text2); line-height: var(--lh-snug); margin: -10px 0 22px; }
/* Field row — its own filled surface, same as .s-row: a muted label with the
   input sitting directly on the surface (no nested box). Group first/last rows
   pick up the large outer corners, matching the setting rows. */
.s-frow {
  display: flex; flex-direction: column; gap: var(--sp-05);
  padding: var(--sp-25) var(--sp-4) var(--sp-3); min-height: 56px; box-sizing: border-box;
  background: var(--bg2); border-radius: var(--r-xs);
  transition: background var(--t);
}
.s-section-card > .s-frow:first-child { border-top-left-radius: var(--r-xl); border-top-right-radius: var(--r-xl); }
.s-section-card > .s-frow:last-child { border-bottom-left-radius: var(--r-xl); border-bottom-right-radius: var(--r-xl); }
.s-frow:focus-within { background: var(--bg3); }
.s-frow-label { font-size: var(--fs-sm); font-weight: 500; color: var(--text3); }
.s-frow-input {
  width: 100%; box-sizing: border-box;
  background: none; border: none; padding: 0; margin: 0; outline: none;
  font-family: var(--font-sans); font-size: var(--fs-base);
  line-height: var(--lh-snug); color: var(--text);
  resize: none;
}
.s-frow-input::placeholder { color: var(--placeholder); }
/* Long-form fields read as prose; they auto-grow to their content (JS), so no
   resize handle and no inner scrollbar. */
textarea.s-frow-input { line-height: var(--lh-normal); overflow: hidden; }
/* Long-form rows preview collapsed at exactly two lines (3em at --lh-normal)
   so Profile stays scannable; focusing the field expands it. The bottom fade
   only appears when content is actually clipped (.is-clipped, set by the
   auto-grow JS). */
.s-frow-long textarea.s-frow-input { max-height: 3em; }
.s-frow-long.is-clipped:not(:focus-within) textarea.s-frow-input {
  -webkit-mask-image: linear-gradient(to bottom, #000 50%, transparent 100%);
          mask-image: linear-gradient(to bottom, #000 50%, transparent 100%);
}
.s-frow-long:focus-within textarea.s-frow-input { max-height: none; }
.s-prof-hint { font-size: var(--fs-sm); color: var(--text3); text-align: center; margin-top: var(--sp-05); }
.s-prof-hint-foot { padding: var(--sp-3) var(--sp-1) 0; }
/* Loading skeleton for the profile form (backend fetch in flight). */
.s-skel-label { width: 90px; height: 12px; border-radius: var(--r-xs); margin-bottom: var(--sp-15); }
.s-skel-input { width: 100%; height: 22px; border-radius: var(--r-xs); }

/* Single-surface group card (cover-letter Structure / Accent): one filled bg2
   panel holding a control block, matching the grouped-list surfaces. */
.s-card { background: var(--bg2); border-radius: var(--r-xl); padding: var(--sp-35) var(--sp-4); }

/* Theme pills — borderless surfaces; selection matches the onboarding
   option cards (solid --select fill, dark text), not the accent outline. */
.s-theme-seg .seg-opt { padding: var(--sp-15) var(--sp-1); border: none; background: var(--bg3); }
.s-theme-seg .seg-opt:hover { background: var(--bg4); border-color: transparent; }
.s-theme-seg .seg-opt.active {
  background: var(--select); border-color: transparent;
  color: var(--select-on);
}

/* Link rows hover to --bg3 — same pattern as chat suggest cards:
   filled state goes one shade warmer on hover, not bordered. */
.s-row-link { cursor: pointer; transition: background var(--t); }
.s-row-link:hover { background: var(--bg3); }

/* Picker sub-page rows (Language, Dictation cleanup) — real <button>s, so
   reset the UA styles the div rows never needed; mint check marks the
   chosen option, same selection colour as everywhere else. */
button.s-row { font: inherit; color: inherit; text-align: left; width: 100%; }
.s-pick-check { color: var(--select); display: flex; flex-shrink: 0; }
.s-pick-check svg { width: 17px; height: 17px; }

/* Leading row glyph — a bare muted stroke icon. A tinted tile per row
   turns the column into decoration; a quiet glyph stays wayfinding. */
.s-row-glyph {
  display: flex; align-items: center; justify-content: center;
  flex-shrink: 0; width: 24px;
  color: var(--text3);
}
.s-row-glyph svg { width: 18px; height: 18px; }


/* AI context block (outside card, full width) */
.s-context-hint { font-size: var(--fs-xs); color: var(--text3); margin-top: var(--sp-15); padding: 0 var(--sp-1); line-height: var(--lh-snug); }

/* Sign out — a quiet ghost button that only turns red on intent. */
.s-signout-btn { width: 100%; margin-top: var(--sp-2); }
.s-signout-btn:hover { color: var(--red); background: var(--red-bg); }
.s-signout-btn svg { width: 14px; height: 14px; }

/* Settings footer — Change password as a quiet text link, sign-out and
   destructive Clear data follow. Visual separation from main stack via
   added top margin. */
.s-footer { margin-top: var(--sp-5); }
/* Toggle */
.toggle {
  position: relative; width: 36px; height: 20px; flex-shrink: 0;
  border: none; padding: 0;
  border-radius: var(--r-pill);
  background: var(--bg4); cursor: pointer;
  transition: background 0.18s var(--ease-out);
}
.toggle:focus-visible { outline: none; box-shadow: var(--ring); }
/* On-state wears the onboarding selection colour (--select), so "chosen"
   reads the same everywhere. */
.toggle.on { background: var(--select); }
.toggle-knob {
  display: block;
  position: absolute; top: 2px; left: 2px;
  width: 16px; height: 16px; border-radius: 50%;
  background: #ffffff;
  box-shadow: 0 1px 2px rgba(0,0,0,0.2);
  transition: transform 0.18s var(--ease-out);
}
.toggle.on .toggle-knob { transform: translateX(16px); }
@media (prefers-reduced-motion: reduce) {
  .toggle, .toggle-knob { transition: none; }
}

/* ──────────────────────────────────────
   EMPTY STATE
────────────────────────────────────── */
.empty {
  display: flex; flex-direction: column;
  align-items: center; justify-content: center;
  flex: 1; color: var(--text3); font-size: var(--fs-base);
  gap: var(--sp-15); padding: var(--sp-8); text-align: center;
}
.empty svg { width: 28px; height: 28px; opacity: 0.3; margin-bottom: var(--sp-1); }

/* ──────────────────────────────────────

/* ──────────────────────────────────────
   ONBOARDING — matches Figma designs
────────────────────────────────────── */

.onboarding {
  display: flex;
  flex-direction: column;
  height: 100%;
  width: 100%;
  background: var(--bg);
  overflow: hidden;
}

/* Scrollable body */
.ob-body {
  flex: 1;
  overflow-y: auto;
  padding: var(--sp-6) var(--sp-45) var(--sp-2);
  display: flex;
  flex-direction: column;
  border: none;
  outline: none;
}
.ob-body::-webkit-scrollbar { width: 0; height: 0; display: none; background: transparent; }
.ob-body::-webkit-scrollbar-track { background: transparent; display: none; }
.ob-body::-webkit-scrollbar-thumb { background: transparent; }

/* ── Onboarding paywall / trial-choice screen ──────────────────────
   Pixel-faithful port of the Figma "Pro · Paywall Trial" frame: hero Jobi+
   logo, a filled 6-item feature card, two plan pills (weekly outlined blue),
   a coral card-trial CTA and a green no-card button over the grass backdrop.
   Values (px / colours) are taken directly from the Figma spec. */
.ob-paywall-body { padding: 0; }
.obpw-screen {
  position: relative; flex: 1; min-height: 100%; width: 100%;
  display: flex; flex-direction: column;
  background: #000 url("pro-trial-bg.jpg") center bottom / 100% auto no-repeat;
}
/* Expired (hard-gate) variant has no top bar, so the hero sits a touch higher. */
.obpw-expired .obpw-content { padding-top: 56px; }
/* Top bar: back chevron + coral progress, pinned over the backdrop. */
.obpw-topbar {
  position: absolute; top: 0; left: 0; right: 0; z-index: 2;
  display: flex; align-items: center; gap: 8px; padding: 16px 16px 8px;
}
.obpw-back { width: 22px; height: 22px; flex-shrink: 0; padding: 0; background: none; border: none; color: #b5aea8; cursor: pointer; display: flex; align-items: center; justify-content: center; }
.obpw-back svg { width: 22px; height: 22px; }
.obpw-progress { flex: 1; min-width: 0; height: 6px; background: #262626; border-radius: 3px; overflow: hidden; }
.obpw-progress-fill { height: 6px; background: #f29fa0; border-radius: 3px; }

.obpw-content { position: relative; z-index: 1; flex: 1; display: flex; flex-direction: column; gap: 15px; padding: 86px 21px 44px; }
.obpw-hero { display: flex; flex-direction: column; align-items: center; gap: 28px; padding-top: 8px; }
/* Logo as two overlaid layers: the fixed "letters" wordmark, and the "o" glyph
   absolutely positioned in the gap so it can animate independently. */
.obpw-logo { position: relative; width: 272.7px; height: 83px; }
.obpw-letters { position: absolute; inset: 0; }
.obpw-letters-svg, .obpw-o-svg { display: block; width: 100%; height: 100%; }
.obpw-o { position: absolute; left: 55px; top: 0; width: 61.3px; height: 82px; transform-origin: center bottom; z-index: 2; will-change: transform; }
/* Contact shadow on the hill; positioned + driven entirely by paywallEntrance.js. */
.obpw-oshadow {
  position: absolute; left: 0; top: 0; width: 46px; height: 13px; border-radius: 50%;
  background: radial-gradient(ellipse at center, rgba(0,0,0,0.5) 0%, rgba(0,0,0,0) 72%);
  transform-origin: center center; opacity: 0; pointer-events: none; z-index: 0; will-change: transform, opacity;
}
.obpw-title { font-family: var(--font-sans); font-size: 24px; font-weight: 800; line-height: 1.13; color: #fff; text-align: center; }
.obpw-group { display: flex; flex-direction: column; gap: 8px; width: 100%; }

/* Feature card — filled #252525 surface, six sprout + label rows. */
.obpw-card { background: #252525; border-radius: 20px; padding: 16px 20px; display: flex; flex-direction: column; gap: 16px; box-shadow: 0 6px 18px rgba(0,0,0,0.07); }
.obpw-feat { display: flex; align-items: center; gap: 12px; height: 24px; }
.obpw-feat-sprout { width: 18px; height: 18px; flex-shrink: 0; display: flex; margin-left: 2px; }
.obpw-feat-sprout svg { width: 100%; height: 100%; }
.obpw-feat-label { font-family: var(--font-sans); font-weight: 500; font-size: 14px; line-height: 24px; letter-spacing: -0.18px; color: #fff; }

/* Plan pills — filled #252525; the selected one gets a 2px blue outline. */
.obpw-plans { display: flex; gap: 4px; }
.obpw-plan {
  box-sizing: border-box; flex: 1; min-width: 0; text-align: left; cursor: pointer;
  display: flex; flex-direction: column; gap: 4px; align-items: flex-start;
  padding: 16px; background: #252525; border: none; border-radius: 18px;
  box-shadow: 0 4px 12px rgba(0,0,0,0.06); transition: box-shadow var(--t);
}
/* Selected outline is an inset shadow (not a border) so it never adds height. */
.obpw-plan.is-selected { box-shadow: inset 0 0 0 2px #a8c7fa, 0 4px 12px rgba(0,0,0,0.06); }
.obpw-plan-amt { font-family: var(--font-sans); font-weight: 800; font-size: 18px; line-height: 1.35; color: #fff; }
.obpw-plan-label { font-family: var(--font-sans); font-weight: 400; font-size: 12px; line-height: 1.35; color: #86868b; }
.obpw-best { font-weight: 700; color: #efb8c8; }

/* Card-trial CTA (coral) + no-card trial button (green), on the grass. */
.obpw-cta-group { display: flex; flex-direction: column; align-items: center; gap: 10px; padding: 20px 3px 0; margin-top: auto; }
.obpw-cta, .obpw-nocard {
  width: 100%; border: none; cursor: pointer; border-radius: 74px; padding: 14px;
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 4px;
  transition: filter var(--t), transform var(--t);
}
.obpw-cta { height: 64px; background: #f29fa0; }
.obpw-nocard { height: 54px; background: #6ef585; }
.obpw-cta:hover, .obpw-nocard:hover { filter: brightness(1.04); }
.obpw-cta:active, .obpw-nocard:active { transform: scale(0.99); }
.obpw-cta-main, .obpw-nocard-main { font-family: var(--font-sans); font-weight: 600; font-size: 14px; line-height: 1.2; letter-spacing: 0.1px; color: #000; white-space: nowrap; }
.obpw-cta-sub, .obpw-nocard-sub { font-family: var(--font-sans); font-weight: 500; font-size: 10px; line-height: 1.3; color: #86868b; text-align: center; }
/* Waiting state while the Stripe Checkout tab is open: the CTAs are replaced by
   a muted, gently-pulsing button (plus a Cancel escape below it). */
.obpw-cta.is-processing { background: #2d2d2d; cursor: default; animation: obpwWait 1.4s ease-in-out infinite; }
.obpw-cta.is-processing:hover { filter: none; }
.obpw-cta.is-processing .obpw-cta-main { color: #f7f4f2; }
.obpw-cta.is-processing .obpw-cta-sub { color: #8a8480; }
@keyframes obpwWait { 0%, 100% { opacity: 1; } 50% { opacity: 0.72; } }
@media (prefers-reduced-motion: reduce) { .obpw-cta.is-processing { animation: none; } }
.obpw-checkout-cancel { background: none; border: none; color: #b5aea8; font-family: var(--font-sans); font-weight: 600; font-size: 13px; padding: 8px 12px; cursor: pointer; }
.obpw-checkout-cancel:hover { color: #f7f4f2; }

/* ── Google button ── */
/* Google auth button = .btn .btn-secondary .btn-lg + these residuals. */
.ob-google-btn { gap: var(--sp-2); margin-bottom: var(--sp-4); }
.ob-google-btn svg { width: 16px; height: 16px; flex-shrink: 0; }
.ob-google-btn.loading { cursor: wait; }

.ob-auth-error {
  color: var(--red);
  font-family: var(--font-sans);
  font-size: var(--fs-sm);
  line-height: var(--lh-snug);
  min-height: 0;
  margin-top: var(--sp-2);
  text-align: center;
}
.ob-auth-error:empty { margin-top: 0; }

/* ── Form fields ── */
.ob-field {
  display: flex;
  flex-direction: column;
  gap: var(--sp-1);
  margin-bottom: var(--sp-25);
}
.ob-label {
  font-size: var(--fs-sm);
  font-weight: 600;
  letter-spacing: 0;
  text-transform: none;
  color: var(--text2);
}
/* Filled surface + soft focus, identical to the app-wide .input treatment. */
.ob-input {
  width: 100%;
  background: var(--bg2);
  border: none;
  border-radius: var(--r);
  padding: var(--sp-3) var(--sp-3);
  font-family: var(--font-sans);
  font-size: var(--fs-md);
  color: var(--text);
  outline: none;
  transition: background var(--t), box-shadow var(--t);
  box-sizing: border-box;
}
.ob-input::placeholder { color: var(--placeholder); }
.ob-input:focus {
  background: var(--bg3);
  box-shadow: var(--shadow-lift);
}

/* ── Upload zones ── */
.ob-upload {
  border: 1.5px dashed var(--border2);
  border-radius: var(--r);
  padding: var(--sp-55) var(--sp-4);
  text-align: center;
  cursor: pointer;
  transition: all var(--t);
  background: var(--bg2);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--sp-1);
  margin-bottom: var(--sp-3);
}
.ob-upload:hover { border-color: var(--accent); background: var(--accent-bg); }
.ob-upload-icon {
  width: 36px;
  height: 36px;
  border-radius: var(--r-sm);
  background: var(--bg3);
  display: flex;
  align-items: center;
  justify-content: center;
  margin-bottom: var(--sp-05);
  transition: background var(--t);
}
.ob-upload:hover .ob-upload-icon { background: var(--accent-bg); }
.ob-upload-icon svg { width: 16px; height: 16px; color: var(--text2); }
.ob-upload:hover .ob-upload-icon svg { color: var(--accent); }
.ob-upload-title { font-size: var(--fs-base); font-weight: 500; color: var(--text2); }
.ob-upload-sub   { font-size: var(--fs-sm); color: var(--text3); }

/* ── Footer ── */
.ob-footer {
  padding: var(--sp-8) var(--sp-45) var(--sp-6);
  display: flex;
  flex-direction: column;
  gap: var(--sp-25);
  flex-shrink: 0;
}

/* Onboarding buttons share the app's rounded-pill shape (var(--r-xl)); the
   primary keeps its coral fill, the secondary is a borderless filled surface. */
/* Footer actions are shared .btn variants; the footer gap is the
   .btn-stack gap so every stacked pair matches app-wide. */

/* ── Slide animations ── */
@keyframes obSlideIn   { from { opacity:0; transform:translateX(16px); } to { opacity:1; transform:translateX(0); } }
@keyframes obSlideBack { from { opacity:0; transform:translateX(-16px);} to { opacity:1; transform:translateX(0); } }
.ob-slide-in   { animation: obSlideIn   0.2s ease; }
.ob-slide-back { animation: obSlideBack 0.2s ease; }

/* ── Screen 1: Welcome ── */


/* Only the welcome screen suppresses scroll — form steps scroll if tall */
.ob-body.ob-welcome { overflow: hidden; }

/* The splash matches the rest of the app — plain themed background, no
   gradient/glow. (.onboarding already sets var(--bg).) */
.onboarding.ob-step-1 { background: var(--bg); }

/* ── Splash (step 1): full-bleed hero, brand wordmark, tagline ── */
.ob-splash2 {
  height: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
}
/* The illustration bleeds to the top and side edges (cancels the ob-body pad). */
.ob-splash2-hero {
  flex: 0 1 auto;
  height: 280px;
  min-height: 0;
  width: 100%;
  display: flex;
  align-items: flex-end;
  justify-content: center;
  overflow: hidden;
}
.ob-hero-rive { display: block; width: 100%; height: 100%; }
.ob-splash2-wordmark { color: var(--text); flex-shrink: 0; padding-top: var(--sp-2); }
/* The character + tagline sit centred in the space below the top logo. */
.ob-splash2-group {
  flex: 1 1 auto;
  min-height: 0;
  width: 100%;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  text-align: center;
  gap: var(--sp-25);
}
.ob-wordmark-svg { height: 42px; width: auto; display: block; }
.ob-wordmark-svg .logo-ink { fill: var(--text); }
.ob-wordmark-svg .logo-eye { fill: var(--bg); }
.ob-splash2-title {
  font-family: var(--font-serif);
  font-size: var(--fs-3xl);
  font-weight: 700;
  color: var(--text);
  letter-spacing: var(--ls-tight);
  line-height: var(--lh-tight);
  max-width: 300px;
  margin: 0;
}
/* Google CTA — dark ink button (icon + label). */
.ob-btn-google {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--sp-25);
  background: #1C1C22;
  color: #f7f4f2;
}
.ob-btn-google:hover { background: #26262e; }
.ob-btn-google svg { width: 18px; height: 18px; flex-shrink: 0; }

/* ── Top bar: back arrow + progress bar (post-splash steps) ── */
.ob-top {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  padding: var(--sp-35) var(--sp-45) var(--sp-1);
  flex-shrink: 0;
}
.ob-back {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  flex-shrink: 0;
  background: none;
  border: none;
  color: var(--text2);
  cursor: pointer;
  border-radius: 50%;
  transition: background var(--t), color var(--t);
}
.ob-back:hover { background: var(--bg2); color: var(--text); }
.ob-back svg { width: 20px; height: 20px; }
.ob-progress {
  flex: 1;
  height: 6px;
  border-radius: 3px;
  background: var(--bg3);
  overflow: hidden;
}
.ob-progress-fill {
  height: 100%;
  border-radius: 3px;
  background: var(--accent);
  transition: width 0.32s cubic-bezier(0.22, 1, 0.36, 1);
}

/* ── Context screens (steps 3–5): tap-only selection ── */
.ob-ctx { display: flex; flex-direction: column; padding-top: var(--sp-2); }
.ob-ctx-title {
  font-family: var(--font-serif);
  font-size: var(--fs-3xl);
  font-weight: 700;
  line-height: var(--lh-tight);
  letter-spacing: var(--ls-tight);
  color: var(--text);
  margin: 0 0 var(--sp-25);
}
.ob-ctx-sub {
  font-family: var(--font-sans);
  font-size: var(--fs-lg);
  line-height: var(--lh-normal);
  color: var(--text2);
  margin: 0 0 var(--sp-6);
}
.ob-read-rive { display: block; width: 140px; height: 140px; }

/* ── "You're all set" finish screen — on the Jobi+ grass hill (same backdrop
   image the paywall uses), full-bleed with the CTA sitting on the grass. ── */
.ob-done-body { padding: 0; }
.obdone-screen {
  position: relative; flex: 1; min-height: 100%; width: 100%;
  display: flex; flex-direction: column;
  background: #000 url("pro-trial-bg.jpg") center bottom / 100% auto no-repeat;
}
.obdone-content {
  flex: 1;
  display: flex; flex-direction: column; align-items: center;
  justify-content: center;   /* centre the logo/title/pills vertically */
  gap: var(--sp-4); padding: 0 21px; text-align: center;
}
/* Same size as the paywall logo (.obpw-logo is 272.7px wide). */
.obdone-logo { width: 272.7px; max-width: 100%; }
.obdone-logo svg { display: block; width: 100%; height: auto; }
.obdone-title { font-family: var(--font-sans); font-size: 24px; font-weight: 800; color: #fff; margin: var(--sp-1) 0 0; text-align: center; }
.obdone-chips { display: flex; flex-wrap: wrap; gap: var(--sp-2); justify-content: center; align-content: center; max-width: 300px; margin-top: var(--sp-1); }
.obdone-chip {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 6px 14px; background: #f0a6a2; border-radius: var(--r-pill);
  font-family: var(--font-sans); font-size: 13px; font-weight: 600; color: #2a2320;
  opacity: 0; animation: obDoneChip 0.4s ease-out both;
}
.obdone-chip-ic { width: 14px; height: 14px; flex-shrink: 0; }
.obdone-note { font-size: 12px; color: #dcd8d6; }
.obdone-cta { padding: 20px 21px 44px; margin-top: auto; }
.obdone-btn { width: 100%; height: 52px; background: #26262e; color: #f7f4f2; border-radius: 74px; font-size: 15px; font-weight: 600; }
.obdone-btn:hover { background: #30303a; }
@keyframes obDoneChip {
  from { opacity: 0; transform: translateY(7px); }
  to   { opacity: 1; transform: none; }
}
@media (prefers-reduced-motion: reduce) {
  .obdone-chip { animation: none; opacity: 1; }
}
.ob-police-svg { display: block; width: 100%; height: auto; filter: drop-shadow(0 6px 16px rgba(0, 0, 0, 0.16)); }

/* Privacy Policy links inside onboarding body copy — one quiet convention
   (used on the data-safety consent row and the CV screen's footnote). */
.ob-ctx a[data-open-ext], .ob-check-link {
  color: var(--text2); text-decoration: underline; text-underline-offset: 2px;
  font-weight: 600; transition: color var(--t);
}
.ob-ctx a[data-open-ext]:hover, .ob-check-link:hover { color: var(--text); }

/* ── Splash notice — the privacy/terms acknowledgment under the Google CTA.
   It sits where data processing begins, so it can never be bypassed. ── */
.ob-notice {
  margin: var(--sp-25) 0 0;
  font-size: var(--fs-xs);
  line-height: var(--lh-normal);
  color: var(--text3);
  text-align: center;
}
/* Consent line at the CV upload — the moment sensitive data is shared. */
.ob-cv-note {
  margin: var(--sp-3) 0 0;
  font-size: var(--fs-xs);
  line-height: var(--lh-snug);
  color: var(--text3);
}
.ob-notice a {
  color: var(--text3); text-decoration: underline; text-underline-offset: 2px;
  font-weight: 600; transition: color var(--t);
}
.ob-notice a:hover { color: var(--text2); }

/* ── Payoff — "Here's what Jobi learned about you" ──
   A read-first summary card, not a form: recognition is the moment. Rows fade
   in staggered (delays inline, same rhythm as the Done chips). */
.ob-payoff-card {
  background: var(--bg2);
  border: none;
  border-radius: var(--r-xl);
  padding: var(--sp-5) var(--sp-45);
  display: flex; flex-direction: column; gap: var(--sp-25);
}
.ob-payoff-row { opacity: 0; animation: obDoneChip 0.4s ease-out both; }
.ob-payoff-name {
  font-family: var(--font-serif);
  font-size: var(--fs-2xl);
  font-weight: 700;
  letter-spacing: var(--ls-tight);
  color: var(--text);
}
.ob-payoff-tagline { font-size: var(--fs-base); color: var(--text2); margin-top: calc(-1 * var(--sp-1)); }
.ob-payoff-kv {
  display: flex; align-items: center; justify-content: space-between; gap: var(--sp-3);
  padding-top: var(--sp-25);
  border-top: 1px solid var(--border);
}
.ob-payoff-kv span { font-size: var(--fs-sm); color: var(--text3); flex: 0 0 auto; }
.ob-payoff-kv b { font-size: var(--fs-base); font-weight: 600; color: var(--text); text-align: right; }
/* The one inline input (empty target role / missing identity field): the most
   valuable gap becomes engagement instead of a blank. */
.ob-payoff-kv-input input.ob-input { flex: 1; margin: 0; max-width: 62%; }
.ob-payoff-exp {
  font-size: var(--fs-sm); line-height: var(--lh-normal); color: var(--text2);
  padding-top: var(--sp-25); border-top: 1px solid var(--border);
  display: -webkit-box; -webkit-line-clamp: 4; -webkit-box-orient: vertical; overflow: hidden;
}
/* Clamp the chips to at most 3 rows (fixed 22px chip height + 6px gap = 78px of
   rows, plus the 10px top padding above them). */
.ob-payoff-chips {
  display: flex; flex-wrap: wrap; gap: var(--sp-15);
  padding-top: var(--sp-25); border-top: 1px solid var(--border);
  max-height: 88px; overflow: hidden;
}
.ob-payoff-chip {
  height: 22px; padding: 0 var(--sp-25);
  display: inline-flex; align-items: center;
  background: var(--bg3); border-radius: var(--r-pill);
  font-size: var(--fs-2xs); font-weight: 600; color: var(--text2);
}
.ob-payoff-chip.is-more { background: transparent; color: var(--text3); }
/* Hydrating state: the reading mascot big and centred in the whole screen. */
.ob-payoff-loading { flex: 1; align-items: center; justify-content: center; text-align: center; }
.ob-payoff-reading { display: flex; justify-content: center; }
.ob-payoff-loading .ob-payoff-reading .ob-read-rive { width: 220px; height: 220px; }
.ob-payoff-reading .ob-read-rive { width: 160px; height: 160px; }
.ob-payoff-loading-t { font-size: var(--fs-xl); font-weight: 600; color: var(--text); margin: var(--sp-3) 0 var(--sp-1); }
.ob-payoff-loading-s { font-size: var(--fs-base); color: var(--text3); margin: 0; }
.ob-payoff-form { display: flex; flex-direction: column; }
.ob-payoff-form .ob-field { margin-bottom: var(--sp-3); }
@media (prefers-reduced-motion: reduce) {
  .ob-payoff-row { animation: none; opacity: 1; }
}

/* ── Post-onboarding setup checklist ──
   A small permanent "Checklist x/x" pill above the chat input; tapping it opens
   the JD-style bottom sheet with the items. */
/* Solid accent chip, same colour as the primary CTA, with a lower radius than
   a full pill so it reads as a small button above the composer. */
.checklist-pill {
  align-self: flex-start;
  display: inline-flex; align-items: center;
  margin: 0 0 calc(-1 * var(--sp-15)) var(--sp-4);   /* aligns with the input's left padding */
  height: 28px; padding: 0 var(--sp-3);
  background: var(--accent); border: none; border-radius: var(--r);
  font-family: var(--font-sans); font-size: var(--fs-sm); font-weight: 600; color: var(--accent-text);
  cursor: pointer; transition: background var(--t);
}
.checklist-pill:hover { background: var(--accent-hover); }
.checklist-pill-t b { font-weight: 700; font-variant-numeric: tabular-nums; }

/* Checklist rows — used inside the setup drawer. */
.setup-sheet-rows { display: flex; flex-direction: column; margin-top: var(--sp-1); }
.setup-row {
  display: flex; gap: var(--sp-25); align-items: flex-start; width: 100%;
  padding: var(--sp-3) 0; text-align: left;
  background: none; border: none; border-top: 1px solid var(--border);
  cursor: pointer; border-radius: 0;
}
.setup-row:first-of-type { border-top: none; }
.setup-row.is-done { cursor: default; }
.setup-check {
  flex: 0 0 auto; width: 18px; height: 18px; margin-top: 1px;
  border: 1.8px solid var(--border2); border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  color: var(--select-on);
  transition: background var(--t), border-color var(--t);
}
.setup-row.is-done .setup-check { background: var(--select); border-color: var(--select); }
.setup-check svg { width: 11px; height: 11px; }
.setup-row-t { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.setup-row-t b { font-size: var(--fs-base); font-weight: 600; color: var(--text); }
.setup-row.is-done .setup-row-t b { color: var(--text3); text-decoration: line-through; }
.setup-row-t small { font-size: var(--fs-sm); color: var(--text3); line-height: var(--lh-snug); }
.setup-row:not(.is-done):hover .setup-row-t b { color: var(--accent); }
.setup-links-form {
  display: flex; flex-direction: column; gap: var(--sp-2);
  padding: var(--sp-25) 0 var(--sp-1);
  border-top: 1px solid var(--border);
}
.setup-links-form .ob-input { margin-bottom: 0; }
.setup-links-save { align-self: flex-end; padding: var(--sp-15) var(--sp-4); }

/* Single-select option cards (experience / timeline) */
.ob-opts { display: flex; flex-direction: column; gap: var(--sp-25); }
.ob-opt {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-3);
  width: 100%;
  text-align: left;
  padding: var(--sp-35) var(--sp-4);
  background: var(--bg2);
  border: none;                       /* borderless surface pill */
  border-radius: var(--r-xl);
  cursor: pointer;
  transition: background var(--t), color var(--t);
}
.ob-opt:hover { background: var(--bg3); }
/* Selected: solid mint, dark text + a dark check badge. */
.ob-opt.on { background: var(--select); }
.ob-opt.on .ob-opt-label,
.ob-opt.on .ob-opt-title { color: var(--select-on); }
.ob-opt-label {
  font-family: var(--font-sans);
  font-size: var(--fs-lg);
  font-weight: 600;
  color: var(--text);
  /* Match the check-badge height so an unselected row (no badge) is exactly as
     tall as the selected one (which carries the 24px disc). */
  display: inline-flex;
  align-items: center;
  min-height: 24px;
}
/* Check badge — only visible on the selected card; dark disc, mint tick. */
.ob-opt-check {
  display: none;
  align-items: center;
  justify-content: center;
  width: 24px;
  height: 24px;
  flex-shrink: 0;
  background: #090909;
  color: var(--select);
  border-radius: 50%;
}
.ob-opt-check svg { width: 15px; height: 15px; }
.ob-opt.on .ob-opt-check { display: flex; }

/* ──────────────────────────────────────
   CHAT SCREEN
────────────────────────────────────── */

/* Layout shell */
.chat-view {
  display: flex;
  flex-direction: column;
  height: 100%;
  overflow: hidden;
}

/* Active-conversation chip row */

/* Quiet "Saved to memory" reassurance — small grey text, gentle fade */
.jobi-saved-memory {
  align-self: center;
  margin: var(--sp-15) 0 var(--sp-05);
  font-family: var(--font-sans);
  font-size: var(--fs-xs);
  color: var(--text3);
  letter-spacing: 0.2px;
  opacity: 0;
  transform: translateY(2px);
  transition: opacity 0.5s ease, transform 0.5s ease;
  pointer-events: none;
}
.jobi-saved-memory.show {
  opacity: 0.7;
  transform: translateY(0);
}

/* Messages scroll area */
.chat-messages {
  flex: 1;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  padding-top: var(--sp-3);
  padding-bottom: var(--sp-2);
}
.chat-messages::-webkit-scrollbar { width: 0; }
/* While Jobi is thinking, open up room at the bottom so the peeking mascot has
   space to breathe and never crowds the last message. */
.chat-messages.is-thinking { padding-bottom: 104px; }

/* Welcome (empty) — vertically centred, no top offset needed */
.chat-messages.is-welcome {
  justify-content: center;
  padding-top: 0;
}

.chat-welcome-inner {
  text-align: center;
  padding: 0 28px;
  display: flex;
  flex-direction: column;
  align-items: center;
  /* Nudge the optical centre up a touch so the block sits in the visual
     middle of the panel rather than the mathematical middle (the divider +
     mascot at the bottom otherwise make it feel top-heavy). */
  margin-top: -24px;
}

/* ── Variant B (A/B layout test) ───────────────────────────────────
   Mascot leads the empty state instead of a title; suggestion cards
   are transparent on idle (only gaining a glass surface on hover).
   Everything else is identical to Dark A. */
.chat-welcome-inner.is-variant-b {
  /* No bottom mascot in B, so the optical-centre nudge isn't needed —
     reset it so the mascot-led block centres naturally. */
  margin-top: 0;
}
.chat-welcome-mascot {
  display: flex;
  align-items: center;
  justify-content: center;
  /* Generous gap below so the mascot reads as a distinct focal anchor
     leading the welcome state, not crowding the suggestion list. */
  margin-bottom: 30px;
  /* Reserve the mascot's footprint so the layout doesn't jump when the
     Rive character pops in after its intro delay — the box starts empty
     but already occupies its final height. Matches the 150px canvas. */
  min-height: 150px;
}
/* Welcome mascot = Rive animation (Artboard 1 / welcome_motion). The static
   white SVG (renderJobiWhiteMascot) ships as a sibling fallback and shows
   until the Rive runtime confirms load, at which point .rive-on swaps the
   canvas in and hides the SVG. If Rive ever fails (no WASM, load error),
   the SVG simply stays — the welcome screen never shows an empty box. */
/* Both layers start hidden so the artboard begins EMPTY — the character
   then pops up from the bottom via the welcome_motion intro. The static
   SVG is shown ONLY if Rive genuinely fails (rive-failed), never as a
   pre-roll, so the user never sees the character before it animates in. */
.chat-welcome-mascot .welcome-mascot-canvas { display: none; }
.chat-welcome-mascot .jobi-grad-wrap { display: none; }
/* Rive centres the artboard with its own padding, so the character only
   fills ~69% of the canvas. 150px makes the visible character ~104px —
   matching the old static mascot. (canvas.… for specificity over the
   shared 108px .chat-mascot sizing rule.) */
.chat-welcome-mascot canvas.welcome-mascot-canvas { width: 150px; height: 150px; }
.chat-welcome-mascot.rive-on .welcome-mascot-canvas { display: block; }
.chat-welcome-mascot.rive-failed .jobi-grad-wrap { display: block; }
/* Pull the daily greeting up toward the mascot so they read as one unit.
   It sits in the canvas's bottom padding (slight overlap with the artboard
   box, clear of the character itself). */
.chat-welcome-mascot + .chat-welcome-greeting { margin-top: -40px; }
/* In the welcome-top position the mascot flows in the column (not
   absolutely pinned like the divider variant) and is scaled up for
   presence — 88px reads as a small corner decoration; ~108px reads as
   the screen's focal point. */
.chat-welcome-mascot .chat-mascot,
.chat-welcome-mascot .mesh-mascot-wrap,
.chat-welcome-mascot .aura-mascot-wrap {
  position: static;
  margin: 0;
  width: 108px;
  height: 108px;
}

/* Gentle idle bob shared by the welcome-screen mascot. */
@keyframes darkb-mascot-bob {
  0%, 100% { transform: translateY(0); }
  50%      { transform: translateY(-6px); }
}

/* ── Dark B mascot treatment ───────────────────────────────────────
   The mascot leads the welcome screen, so it gets a luminous, cool-
   toned look that ties it to the blue orb backdrop instead of the
   stark inverted white it has elsewhere.
   - Rive (canvas): can't be styled per-stroke, so it's recoloured via
     a filter chain — invert black→white, then sepia+saturate+hue-rotate
     tints the white toward light blue, and a drop-shadow adds a soft
     blue halo so the mascot reads as lit rather than pasted on.
   - Mesh / Aura (SVG): already blue-gradient filled, so they just get
     the matching halo plus a slight brightness lift. */
body.dark.variant-b .chat-welcome-mascot #chat-mascot-canvas {
  filter:
    invert(1) sepia(0.55) saturate(4.5) hue-rotate(185deg) brightness(1.05)
    drop-shadow(0 0 14px rgba(96, 165, 250, 0.40));
}
body.dark.variant-b .chat-welcome-mascot .mesh-mascot-wrap,
body.dark.variant-b .chat-welcome-mascot .aura-mascot-wrap {
  filter:
    brightness(1.08)
    drop-shadow(0 0 14px rgba(96, 165, 250, 0.38));
}

/* Transparent suggestion cards on idle in variant B; glass on hover. */

.chat-welcome-heading {
  font-family: var(--font-serif);
  font-size: 56px;
  font-weight: 600;
  color: var(--text);
  /* Slightly looser line-height so descenders (like the 'g' in "go")
     aren't clipped by the line box when background-clip: text is
     active. Plus a small padding-bottom for the same reason — the
     clip area equals the element's content box, so tight boxes
     truncate the bottom of glyphs. */
  line-height: 1.15;
  letter-spacing: var(--ls-tight);
  padding-bottom: var(--sp-1);
  /* Larger negative space below — the heading is the empty state's
     single anchor; it should feel free-standing rather than docked to
     the suggestion list. Gemini-direction relies on this kind of
     breathing room more than on any specific decorative element. */
  margin: 0 0 48px;
  max-width: 13ch;
  text-wrap: balance;
  /* Subtle shimmer — a soft purple highlight band travels across the
     dark text once every 6 seconds. Achieved by clipping a linear
     gradient to the text shape (background-clip: text) and animating
     the gradient's position. The base colour of the gradient matches
     var(--text), so the text reads as solid dark with a moving
     highlight rather than as gradient-coloured throughout. */
  background-image: linear-gradient(
    100deg,
    var(--text) 0%,
    var(--text) 42%,
    rgba(167, 139, 250, 0.85) 50%,
    var(--text) 58%,
    var(--text) 100%
  );
  background-size: 250% 100%;
  background-position: 95% 0;
  background-repeat: no-repeat;
  -webkit-background-clip: text;
          background-clip: text;
  -webkit-text-fill-color: transparent;
  color: transparent;
  animation: heading-shimmer 8s ease-in-out infinite;
}
/* Shimmer with a long rest phase. Positions stay STRICTLY inside the
   0% – 100% range — at background-size: 250%, only this range
   guarantees the background fully covers the element. Going outside
   (e.g., 110% or -10% as I had earlier) leaves a thin uncovered
   slice on one edge, which background-clip: text renders as missing
   text. The shimmer band is at 50% of the gradient, so values near
   100% (rest) or 0% (post-sweep) keep the band off the visible area
   in both states while still fully painting the text. */
@keyframes heading-shimmer {
  0%, 75% { background-position: 95% 0; }
  100%    { background-position:  5% 0; }
}
@media (prefers-reduced-motion: reduce) {
  .chat-welcome-heading {
    animation: none;
    background-image: none;
    color: var(--text);
    -webkit-text-fill-color: currentColor;
  }
}
/* The welcomeHeading() generator wraps the name in <em> tags — we let
   the tag stay in the markup (so we don't have to rewrite every variant)
   but render it identically to the surrounding text. No italic, no
   accent colour. The greeting now reads as one continuous line. */
.chat-welcome-heading em {
  color: inherit;
  font-style: normal;
  font-weight: inherit;
}

/* ── Interview setup card (chat-embedded) ───────────────────────────
   Appears in the chat when the user requests a mock interview, before
   we actually start the voice call. Sits as an AI message variant (no
   text bubble — the card replaces it). Lives in the coffee bubble so
   it feels like Jobi speaking, but with structured content: title,
   brief, three guidance bullets, then the choice — Join the voice
   interview (primary, orange CTA) or stay in chat (soft secondary).
   The phone-handset icon on Join reuses I.interview, the same glyph
   used in the nav, so the visual language is consistent. */
.interview-setup-card {
  display: flex;
  flex-direction: column;
  gap: var(--sp-4);
  background: #1F1F1F;
  border: none;
  border-radius: var(--r-xl);
  padding: var(--sp-5) var(--sp-5) var(--sp-45);
  width: 100%;
  font-family: var(--font-sans);
}
/* Divider under the interview card's title, matching the tool cards. */
.interview-hr { height: 1px; background: var(--border); width: 100%; }
.interview-setup-title {
  font-family: var(--font-serif);
  font-size: var(--fs-xl);
  font-weight: 500;
  color: var(--text);
  margin: 0;
  line-height: 1.25;
  letter-spacing: -0.005em;
}
.interview-setup-bullets {
  display: flex;
  flex-direction: column;
  gap: 10px;
  margin: 0;
  padding: 0;
  list-style: none;
}
.interview-setup-bullet {
  display: flex;
  gap: var(--sp-25);
  font-size: var(--fs-base);
  color: var(--text2);
  line-height: 1.45;
}
.interview-setup-bullet::before {
  content: '';
  display: block;
  flex-shrink: 0;
  width: 4px;
  height: 4px;
  background: var(--ink);
  border-radius: 50%;
  margin-top: 7px;
}
.interview-setup-bullet b {
  color: var(--text);
  font-weight: 600;
}
.interview-setup-cta {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 9px;
  width: 100%;
  background: var(--accent);
  color: var(--accent-text);
  font-family: var(--font-sans);
  font-size: 14.5px;
  font-weight: 500;
  letter-spacing: -0.005em;
  border: none;
  border-radius: var(--r-lg);
  padding: 13px 16px;
  margin-top: var(--sp-15);
  cursor: pointer;
  transition: background var(--t), transform 0.1s ease;
}
.interview-setup-cta:hover  { background: var(--accent-hover); }
.interview-setup-cta:active { transform: scale(0.99); }
.interview-setup-cta-icon {
  display: inline-flex;
  width: 16px;
  height: 16px;
  align-items: center;
  justify-content: center;
  color: var(--accent-text);
}
.interview-setup-cta-icon svg { width: 16px; height: 16px; }
/* ── Continue-in-chat button ──
   Sized to match the Join CTA above but visibly subordinate: Join is
   the one primary action on this card, so the text-chat fallback is a
   quiet filled surface, not a second solid CTA. */
.interview-setup-continue {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 9px;
  width: 100%;
  background: var(--tint);
  color: var(--text2);
  font-family: var(--font-sans);
  font-size: 14.5px;
  font-weight: 500;
  letter-spacing: -0.005em;
  border: none;
  border-radius: var(--r-lg);
  padding: 13px 16px;
  cursor: pointer;
  transition: background var(--t), transform 0.1s ease;
}
.interview-setup-continue:hover  { background: var(--bg-mid); color: var(--text); }
.interview-setup-continue:active { transform: scale(0.99); }

/* ── Interview question card ───────────────────────────────────────
   The "what type of interview?" multiple-choice card. Appears BEFORE
   the setup card; user picks one of the four formats (or types their
   own via "Other"). Once answered, the buttons disable and the chosen
   one stays highlighted as a record of the decision. Lives in the
   coffee bubble like the setup card; options inside use the lighter
   cream (--tint) so they read as tappable surfaces against the coffee. */
.interview-q-card {
  display: flex;
  flex-direction: column;
  gap: var(--sp-3);
  background: #1F1F1F;
  border: none;
  border-radius: var(--r-xl);
  padding: var(--sp-45) var(--sp-55);
  width: 100%;
  font-family: var(--font-sans);
}
.interview-q-prompt {
  font-family: var(--font-sans);
  font-size: var(--fs-md);
  color: var(--text);
  margin: 0;
  line-height: 1.4;
  font-weight: 500;
}
.interview-q-options {
  display: flex;
  flex-direction: column;
  gap: var(--sp-15);
}
.interview-q-option {
  background: var(--tint);
  color: var(--text);
  font-family: var(--font-sans);
  font-size: 13.5px;
  font-weight: 500;
  border: none;
  border-radius: 12px;
  padding: 11px 14px;
  cursor: pointer;
  text-align: left;
  transition: background var(--t), color var(--t), opacity var(--t), transform 0.1s ease;
}
.interview-q-option:hover  { background: var(--bg-mid); }
.interview-q-option:active { transform: scale(0.99); }
.interview-q-option:disabled {
  cursor: default;
  opacity: 0.45;
}
.interview-q-option.selected {
  background: var(--ink);
  color: #ffffff;
}
.interview-q-option.selected:disabled { opacity: 1; }
.interview-q-option.selected:hover    { background: var(--ink); }
.interview-q-custom {
  display: flex;
  gap: var(--sp-15);
  margin-top: var(--sp-05);
}
.interview-q-input {
  flex: 1;
  background: var(--tint);
  color: var(--text);
  font-family: var(--font-sans);
  font-size: 13.5px;
  border: 1px solid var(--border);
  border-radius: 12px;
  padding: 11px 14px;
  outline: none;
  transition: border-color var(--t), background var(--t);
}
.interview-q-input::placeholder { color: var(--text3); }
.interview-q-input:focus        { border-color: var(--ink); background: var(--bg); }
.interview-q-submit {
  background: var(--ink);
  color: var(--bg);
  font-family: var(--font-sans);
  font-size: 13.5px;
  font-weight: 500;
  border: none;
  border-radius: 12px;
  padding: 11px 16px;
  cursor: pointer;
  white-space: nowrap;
  transition: background var(--t), transform 0.1s ease;
}
.interview-q-submit:hover  { background: color-mix(in srgb, var(--ink) 86%, var(--bg)); }
.interview-q-submit:active { transform: scale(0.99); }

/* ── Divider line with mascot peeking over it ── */
.chat-divider {
  position: relative;
  /* The divider's job is now purely structural — it gives the mascot a
     bottom: 0 anchor to sit on. No visible line; the input pill below
     reads cleanly against the chat scroll area without a separator. */
  height: 1px;
  background: transparent;
  flex-shrink: 0;
  margin: 0 var(--sp-4);
}
.chat-mascot {
  position: absolute;
  bottom: 0;
  right: 6px;
  /* Default sizing (88×88) is the Rive mascot variant. The mesh-
     gradient variant overrides width/height to 66×67 via its own
     .mesh-mascot-wrap modifier class. */
  width: 88px;
  height: 88px;
  display: block;
  pointer-events: none;
  opacity: 1;
  transform: translateY(0);
  transition: opacity 0.28s ease, transform 0.28s ease;
  z-index: 1;           /* behind the input row (z-index: 2) */
}
/* Mascot hides downward behind the input frame.
   Translation distance scales with size — at 88px we need ~64px of
   slide to fully clear the divider. */
.chat-divider.mascot-hidden .chat-mascot {
  opacity: 0;
  transform: translateY(64px);
}

/* ═══════════════════════════════════════════════════════════════════
   Mesh-gradient mascot (temporary swap for the Rive mascot)
   ───────────────────────────────────────────────────────────────────
   Pure SVG implementation: a linear vertical gradient (navy → blue →
   pink) as the structural backbone, with three radial overlays (pink
   bottom-left, sky-blue rim light upper-right, soft shadow on the
   left) clipped to the ghost-blob path. White ellipses on top serve
   as eyes — they blink via CSS animation of the `ry` attribute and
   follow the cursor via JS (see bindMeshMascotEyes in popup.js).
   Bob is translateY-only — the original component combined translate
   with scaleY squish but that introduced a 1px edge artifact during
   the scale, so we ship without the squish in this port.
   ═══════════════════════════════════════════════════════════════════ */
.chat-mascot.mesh-mascot-wrap {
  /* Mahin's mascot has a 111:113 viewBox — essentially square. Sizing
     adjusted to maintain similar visual weight (~4300 sq px) to the
     previous 60×75 portrait ghost. */
  width:  66px;
  height: 67px;
  mix-blend-mode: normal;
  overflow: visible;
}
.mesh-mascot-svg {
  display: block;
  width:  100%;
  height: 100%;
  overflow: visible;
  animation: mesh-mascot-bob 2.8s ease-in-out infinite;
  transform-origin: center;
}
.mesh-body-fill {
  /* Opaque body underneath the donut clip — uses the page-background
     custom property so the mascot interior matches white in light mode
     and dark in dark mode automatically. */
  fill: var(--bg);
}
.mesh-eye {
  /* Filled with the same vertical gradient as the body, so the eyes
     take on whatever colour the gradient happens to be at their cy
     position (around 46% down → mid-blue). */
  fill: url(#meshBase);
  /* Spring-easing for cursor follow — JS writes the cx/cy values, this
     transition smooths the movement so the eyes don't jitter. */
  transition: cx 0.4s cubic-bezier(0.34, 1.56, 0.64, 1),
              cy 0.4s cubic-bezier(0.34, 1.56, 0.64, 1);
  animation: mesh-eye-blink 3s infinite ease-in-out;
}
@keyframes mesh-mascot-bob {
  0%, 100% { transform: translateY(0); }
  50%      { transform: translateY(-6px); }
}
@keyframes mesh-eye-blink {
  0%, 90%, 100% { ry: 9; }
  95%           { ry: 1; }
}
@media (prefers-reduced-motion: reduce) {
  .mesh-mascot-svg, .mesh-eye {
    animation: none;
  }
}

/* ── Aura mascot — sibling of the mesh mascot, different silhouette
   (taller rounded tablet with a wavy bottom), bigger eyes, the same
   5-colour pastel mesh gradient inside. Squash-and-stretch bob with
   top-centre origin gives the body a "settling" quality on each
   bounce, distinct from the mesh mascot's plain vertical bob. */
.aura-mascot-wrap {
  /* The viewBox is taller-than-wide (231×289 = 0.8 aspect), so default
     SVG aspect-preservation fits it inside the .chat-mascot dimensions
     with empty space on the sides. That's fine; the visible mascot
     reads as a single centred figure.
     scale(0.85) shrinks the rendered SVG by 15% without changing the
     layout slot — surrounding chrome stays put, the mascot just sits
     visually smaller within its allotted box. */
  overflow: visible;
  transform: scale(0.85);
  transform-origin: center;
}
.aura-mascot-svg {
  display: block;
  width:  100%;
  height: 100%;
  overflow: visible;
  animation: aura-mascot-bob 2.8s ease-in-out infinite;
  transform-origin: top center;
}
.aura-body-fill {
  /* Same pattern as mesh-body-fill: page-bg colour so the silhouette
     interior matches whichever theme is active, then the clipped
     gradient stack draws on top. */
  fill: var(--bg);
}
.aura-eye {
  /* Eye colour adapts via var(--text) — dark ink in light mode, warm
     cream in dark mode. High contrast against the pastel gradient
     interior in either case. */
  fill: var(--text);
  transition: cx 0.4s cubic-bezier(0.34, 1.56, 0.64, 1),
              cy 0.4s cubic-bezier(0.34, 1.56, 0.64, 1);
  animation: aura-eye-blink 3.2s infinite ease-in-out;
}
@keyframes aura-mascot-bob {
  /* Translate + scaleY produces a soft squash-stretch — at the top of
     the bounce the body is taller (1.08), at rest it returns to 1.0.
     Origin at top centre means the top stays fixed while the bottom
     stretches downward; reads as a body "settling" rather than
     "stretching outward in both directions". */
  0%, 100% { transform: translateY(0)    scaleY(1);    }
  50%      { transform: translateY(-8px) scaleY(1.08); }
}
@keyframes aura-eye-blink {
  /* Larger eye size than the mesh mascot's blink (30 → 3 vs 9 → 1).
     Same overall rhythm — long open period, fast snap closed at 95%. */
  0%, 90%, 100% { ry: 30; }
  95%           { ry: 3; }
}
@media (prefers-reduced-motion: reduce) {
  .aura-mascot-svg, .aura-eye {
    animation: none;
  }
}

/* ── Message bubbles ── */
.msg {
  display: flex;
  flex-direction: column;
  max-width: 82%;
  padding: var(--sp-3) var(--sp-4) 0;
}
.msg.ai   { align-self: stretch; max-width: 100%; width: 100%; }
.msg.user { align-self: flex-end; }
/* Email cards need more room than a chat bubble */
.msg.ai:has(.email-card) { max-width: 100%; width: 100%; }
/* Interview setup card also breaks out to the full chat column — the
   type-selector pills and structured content need the room. */
.msg.ai:has(.interview-setup-card),
.msg.ai:has(.interview-q-card) { max-width: 100%; width: 100%; }

/* Rich cards sit as distinct objects: give the gap BEFORE a card and the gap
   AFTER a card the same, slightly larger value, so the rhythm around a card is
   symmetric (a card's internal padding otherwise makes the before-gap read
   larger than the after-gap). */
.msg:has(.acv,
.email-card,
.interview-setup-card,
.interview-q-card),
.msg:has(.acv,
.email-card,
.interview-setup-card,
.interview-q-card) + .msg { padding-top: var(--sp-45); }

.msg-bubble {
  padding: 11px 15px;
  border-radius: var(--r-xl);
  font-size: 13.5px;
  line-height: 1.55;
}
.msg.ai .msg-bubble {
  background: none;
  color: var(--text);
  border-radius: 0;
  box-shadow: none;
  padding: 0;
}

/* Per-message action toolbar — collapsed until hover; pinned-open under the
   latest AI reply. Collapsing (rather than reserving) keeps message spacing
   tight when idle; the gap we added gives the revealed row room. */
.msg-actions {
  display: flex;
  align-items: center;
  gap: var(--sp-05);
  height: 30px;        /* reserved at all times so hover never shifts layout */
  margin-top: var(--sp-05);
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.14s ease;
}
.msg.user .msg-actions { justify-content: flex-end; }
.msg:hover .msg-actions,
.msg-actions.is-pinned {
  opacity: 1;
  pointer-events: auto;
}
.msg-act {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 28px;
  height: 28px;
  padding: 0;
  border: none;
  background: none;
  color: var(--text3);
  border-radius: var(--r-sm);
  cursor: pointer;
  transition: background var(--t), color var(--t);
}
.msg-act:hover { background: var(--bg2); color: var(--text); }
.msg-act svg { width: 15px; height: 15px; }
.msg-act.is-active { color: var(--accent); }
.msg-act.copied { color: var(--green); }

/* ── WhatsApp-style bubble pop ────────────────────────────────────
   When a new message bubble appears (sent OR received), it pops in
   from its tail corner: starts slightly smaller, slightly offset
   down, fully transparent — settles to full size in ~240ms with a
   soft ease-out. The transform-origin is set per role so the bubble
   "grows" from the corner the tail sits at — bottom-left for AI
   (received), bottom-right for the user (sent). That's the detail
   that makes the animation feel anchored rather than floating.
   The text inside doesn't animate separately; it just becomes
   visible inside the bubble as it scales up, exactly like WhatsApp. */
.msg-bubble-fresh {
  animation: msg-bubble-pop 0.24s cubic-bezier(0.16, 1, 0.3, 1) both;
}
.msg.ai .msg-bubble-fresh   { transform-origin: bottom left;  }
.msg.user .msg-bubble-fresh { transform-origin: bottom right; }
@keyframes msg-bubble-pop {
  from {
    opacity: 0;
    transform: scale(0.88) translateY(4px);
  }
  to {
    opacity: 1;
    transform: scale(1) translateY(0);
  }
}

/* Respect users who prefer reduced motion — bubble simply appears,
   no scale, no translate. Same visual destination, no journey. */
@media (prefers-reduced-motion: reduce) {
  .msg-bubble-fresh { animation: none; }
}

/* ── Markdown rendering inside AI bubbles ── */
.msg.ai .msg-bubble .md-p {
  margin: 0;
  line-height: 1.6;
}
.msg.ai .msg-bubble .md-p + .md-p,
.msg.ai .msg-bubble .md-p + .md-ul,
.msg.ai .msg-bubble .md-ul + .md-p {
  margin-top: 9px;           /* paragraph spacing */
}
.msg.ai .msg-bubble .md-ul {
  margin: var(--sp-1) 0 0;
  padding-left: var(--sp-45);
  list-style: none;
}
.msg.ai .msg-bubble .md-ul li {
  position: relative;
  line-height: 1.55;
  padding-left: var(--sp-05);
  margin: 3px 0;             /* tight, comfortable bullet spacing */
}
.msg.ai .msg-bubble .md-ul li::before {
  content: '';
  position: absolute;
  left: -12px;
  top: 0.62em;
  width: 4px;
  height: 4px;
  border-radius: 50%;
  background: var(--text3);
}
.msg.ai .msg-bubble strong { font-weight: 600; }
.msg.ai .msg-bubble em { font-style: italic; }

/* Title line in a response (e.g. "Review"). Used when extractTitle()
   detects a short standalone first line. Aurora-friendly: clean serif,
   dark text, no italics, no orange. */
.msg.ai .msg-bubble .md-title {
  font-family: var(--font-serif);
  font-weight: 500;
  color: var(--text);
  font-size: 16px;
  line-height: 1.3;
  margin-bottom: var(--sp-2);
}

/* Markdown headings — used by company-research briefings and any
   other AI response that produces ### / ## / # structure. Three
   distinct visual tiers so the eye can scan the briefing quickly
   rather than reading top-to-bottom:
   •  h1 — large serif (rarely used, "section of a big response")
   •  h2 — medium sans-serif semibold (typical section header)
   •  h3 — small uppercase aurora-purple (sub-section / label) —
          this is the scannable "field name" treatment that turns a
          long briefing into a navigable list of chunks
   •  h4 — smaller, weight 600, dark (rare nesting case) */
.msg.ai .msg-bubble .md-h1 {
  font-family: var(--font-serif);
  font-size: 18px;
  font-weight: 600;
  color: var(--text);
  line-height: 1.3;
  margin: var(--sp-35) 0 var(--sp-2);
}
.msg.ai .msg-bubble .md-h2 {
  font-family: var(--font-sans);
  font-size: var(--fs-md);
  font-weight: 600;
  color: var(--text);
  line-height: var(--lh-snug);
  margin: var(--sp-35) 0 var(--sp-15);
}
.msg.ai .msg-bubble .md-h3 {
  font-family: var(--font-sans);
  font-size: var(--fs-xs);
  font-weight: 600;
  letter-spacing: var(--ls-caps);
  text-transform: uppercase;
  color: #7c5fc7;
  line-height: var(--lh-snug);
  margin: var(--sp-3) 0 var(--sp-1);
}
.msg.ai .msg-bubble .md-h4 {
  font-family: var(--font-sans);
  font-size: 12.5px;
  font-weight: 600;
  color: var(--text);
  line-height: var(--lh-snug);
  margin: var(--sp-25) 0 var(--sp-1);
}
/* No top margin on the very first heading inside a bubble — keeps
   the heading flush to the top of the bubble rather than floating
   with a visible gap. */
.msg.ai .msg-bubble .md-h1:first-child,
.msg.ai .msg-bubble .md-h2:first-child,
.msg.ai .msg-bubble .md-h3:first-child,
.msg.ai .msg-bubble .md-h4:first-child {
  margin-top: 0;
}

/* ── Company research card header ──────────────────────────────────
   Renders above the briefing's markdown when m.companyResearch is
   set. Two-line affordance: a tiny aurora-purple "Company research"
   caption (signals "Jobi did live research for this") above the
   identified company name in serif (visible confirmation the right
   company was found). A faint divider line separates this header
   from the briefing body so the eye can chunk the response into
   "what this is" + "the briefing itself." */
.msg.ai .msg-bubble .research-card-header {
  margin-bottom: var(--sp-3);
  padding-bottom: var(--sp-25);
  border-bottom: 1px solid var(--border);
}
.msg.ai .msg-bubble .research-card-label {
  font-family: var(--font-sans);
  font-size: var(--fs-2xs);
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: #7c5fc7;
  margin-bottom: var(--sp-1);
}
.msg.ai .msg-bubble .research-card-title {
  font-family: var(--font-serif);
  font-size: 18px;
  font-weight: 500;
  color: var(--text);
  line-height: 1.25;
}
.msg.user .msg-bubble {
  background: var(--user-bubble);
  color: var(--user-bubble-text);
  border-bottom-right-radius: 5px;
}

/* ── Email template card ── */
.email-card {
  align-self: flex-start;
  width: 100%;
  background: var(--tint);
  border: none;
  border-radius: 12px;
  overflow: hidden;
}
.email-card-head {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  padding: 11px 13px;
  border-bottom: 1px solid var(--border);
  background: transparent;
}
.email-card-icon {
  display: inline-flex;
  width: 16px; height: 16px;
  color: var(--ink);
}
.email-card-icon svg { width: 16px; height: 16px; }
.email-card-title {
  flex: 1;
  font-family: var(--font-sans);
  font-style: normal;
  font-weight: 600;
  font-size: var(--fs-base);
  color: var(--text);
  letter-spacing: 0;
}
.email-icon-btn {
  width: 26px; height: 26px;
  display: flex; align-items: center; justify-content: center;
  background: transparent;
  border: none; border-radius: var(--r-sm);
  color: var(--text3);
  cursor: pointer;
  transition: background var(--t), color var(--t);
}
.email-icon-btn:hover { background: var(--bg3); color: var(--text); }
.email-icon-btn svg { width: 15px; height: 15px; }

.email-subject-row {
  display: flex;
  align-items: baseline;
  gap: var(--sp-2);
  padding: 11px 14px 7px;
}
.email-subject-label {
  font-size: 10.5px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--text3);
  flex-shrink: 0;
}
.email-subject-text {
  font-size: var(--fs-base);
  font-weight: 600;
  color: var(--text);
  line-height: 1.4;
}
.email-empty { color: var(--text3); font-weight: 400; font-style: italic; }

.email-body-text {
  padding: var(--sp-1) var(--sp-35) var(--sp-35);
  font-size: var(--fs-base);
  line-height: 1.6;
  color: var(--text);
  white-space: pre-wrap;
  word-break: break-word;
}

.email-card-actions {
  display: flex;
  justify-content: flex-end;
  gap: var(--sp-2);
  padding: 11px 13px;
  border-top: 1px solid var(--border);
  background: var(--bg2);
}
.email-btn {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-15);
  padding: var(--sp-2) var(--sp-35);
  border-radius: var(--r-sm);
  font-family: var(--font-sans);
  font-size: 12.5px;
  font-weight: 600;
  cursor: pointer;
  border: none;
  transition: opacity var(--t), background var(--t), transform var(--t);
}
.email-btn svg { width: 14px; height: 14px; }
.email-btn-primary {
  background: var(--ink);
  color: var(--bg);
}
.email-btn-primary:hover { opacity: 0.88; }
.email-btn-primary.copied { background: var(--green); color: #fff; }

/* Gmail-branded CTA. Follows Google's branded-button convention: white
   surface, subtle border + shadow, the full-colour Gmail mark sized up
   slightly so it carries the brand weight. */
.email-btn-gmail {
  background: #fff;
  color: #3c4043;
  border: 1px solid #dadce0;
  padding: 7px 14px 7px 12px;
  font-family: var(--font-sans);
  font-weight: 500;
  letter-spacing: 0.1px;
  box-shadow: 0 1px 2px rgba(60,64,67,0.08);
  transition: box-shadow var(--t), background var(--t);
}
.email-btn-gmail:hover {
  background: #f8f9fa;
  box-shadow: 0 1px 3px rgba(60,64,67,0.18), 0 2px 6px rgba(60,64,67,0.08);
}
.email-btn-gmail svg { width: 18px; height: auto; }
body.dark .email-btn-gmail {
  background: #1f1f1f;
  color: #e8eaed;
  border-color: #3c4043;
  box-shadow: none;
}
body.dark .email-btn-gmail:hover { background: #2a2a2a; }

.email-btn-ghost {
  background: transparent;
  color: var(--text2);
  border: 1px solid var(--border2);
}
.email-btn-ghost:hover { background: var(--bg3); }

/* Editing state */
.email-card.editing .email-field { padding: var(--sp-25) var(--sp-35) 0; }
.email-card.editing .email-field:last-of-type { padding-bottom: var(--sp-3); }
.email-label {
  display: block;
  font-size: 10.5px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--text3);
  margin-bottom: 5px;
}
.email-edit-subject, .email-edit-body {
  width: 100%;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
  padding: 9px 11px;
  font-family: var(--font-sans);
  font-size: var(--fs-base);
  color: var(--text);
  transition: border-color var(--t);
  box-sizing: border-box;
}
.email-edit-body {
  line-height: 1.6;
  resize: vertical;
  min-height: 120px;
}
.email-edit-subject:focus, .email-edit-body:focus {
  outline: none;
  border-color: var(--accent);
}

/* ── Input bar ── */
.chat-input-row {
  display: flex;
  align-items: stretch;     /* pill stretches to fill the row */
  padding: var(--sp-35) var(--sp-4) var(--sp-4);
  flex-shrink: 0;
  background: var(--bg);
  position: relative;
  z-index: 2;
}

/* ── Editor refine tag — a held selection from the cover-letter editor.
   Sits inline in the composer's icon row, beside the "+" (the ChatGPT
   composer-tag pattern): the quoted snippet shows both user and Jobi are
   talking about that text; the X ends the reference. Sage marks the held
   state (brand grammar: sage carries states). */
.refine-tag {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  max-width: 60%;
  height: 30px;
  margin-bottom: 5px;         /* optically centre against the 34px icon row */
  padding: 0 3px 0 10px;
  border-radius: var(--r-pill);
  background: color-mix(in srgb, var(--green) 16%, transparent);
  color: var(--green);
}
.refine-tag-ic { flex-shrink: 0; display: inline-flex; }
.refine-tag-ic svg { width: 13px; height: 13px; }
.refine-tag-t { min-width: 0; font-size: var(--fs-xs); font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.refine-tag-x { flex-shrink: 0; width: 22px; height: 22px; border: none; background: transparent; color: inherit; opacity: 0.75; border-radius: 50%; cursor: pointer; display: inline-flex; align-items: center; justify-content: center; transition: opacity var(--t), background var(--t); }
.refine-tag-x:hover { opacity: 1; background: color-mix(in srgb, var(--green) 24%, transparent); }
.refine-tag-x svg { width: 12px; height: 12px; }

/* Input pill — two height states:
   - Empty (placeholder showing): 50px single-line pill
   - Has content (any amount): 74px expanded pill, stays there
   The `:has(.chat-input:not(:placeholder-shown))` selector toggles
   the expanded state the moment the textarea has any text. No JS, no
   class toggling — the DOM's own state drives the visual. Width is
   locked via the textarea's `min-width: 0` so long text wraps instead
   of pushing the pill (and the icons) sideways. */
.chat-input-wrap {
  flex: 1;
  display: flex;
  flex-wrap: wrap;          /* permanently stacked: text row on top, icon row below */
  align-items: flex-end;
  background: var(--tint);
  border: none;
  border-radius: 22px;
  padding: var(--sp-15);
  min-height: 92px;         /* two-row floor (text row + icon row) */
  box-shadow: 0 1px 12px rgba(0,0,0,0.04), 0 1px 3px rgba(0,0,0,0.03);
  transition: background var(--t), box-shadow var(--t);
}
.chat-input-wrap:focus-within {
  background: var(--bg);
  box-shadow: var(--shadow-lift);
}

/* "Thinking" mascot (Artboard 2) — peeks up from BEHIND the input pill while
   Jobi composes. It's the first child of the row (painted before the pill),
   so its lower half hides behind the pill's top edge and only the head shows
   above. pointer-events:none so it never blocks the input. */
.chat-think-slot {
  position: absolute;
  top: -98px;
  right: 26px;
  width: 130px;
  height: 112px;          /* bottom edge = the input's top edge */
  overflow: hidden;       /* clips the body below → only the head/eyes peek over */
  pointer-events: none;
}
.chat-think-slot canvas {
  position: absolute;
  top: -16px; left: 0;           /* lift so head + eyes peek clearly over the input */
  width: 130px; height: 130px;   /* square; overflows the clip box → body hidden */
  display: block;
  transform: translateY(96px);   /* hidden: pushed down behind the input */
  transition: transform 0.4s cubic-bezier(0.22, 1, 0.36, 1);  /* clean ease-out, no overshoot */
}
.chat-think-slot canvas.up { transform: translateY(0); }   /* peek up while thinking */
.chat-input {
  flex: 1 1 100%;           /* full-width text row, sat above the icons */
  order: -1;
  min-width: 0;
  margin-bottom: var(--sp-15);
  background: transparent;
  border: none;
  outline: none;
  resize: none;
  font-family: var(--font-sans);
  font-size: 13.5px;
  color: var(--text);
  line-height: 1.45;
  /* 8px horizontal inset lands the text edges on the icon glyphs below
     (the + icon sits (34-18)/2 = 8px inside its button). */
  padding: var(--sp-3) var(--sp-2);
  /* Grow with content (field-sizing), capped, then scroll internally. */
  field-sizing: content;
  max-height: 140px;
  overflow-y: auto;
  overflow-wrap: break-word;
  word-break: break-word;
}
.chat-input::placeholder { color: var(--text3); }
/* Icon row sits beneath the text: attach on the left, mic + send pushed right. */
.chat-input-wrap .mic-btn { margin-left: auto; }

.mic-btn {
  width: 30px; height: 30px;
  background: transparent;
  border: none;
  color: var(--text3);
  cursor: pointer;
  display: flex; align-items: center; justify-content: center;
  flex-shrink: 0;
  padding: 0;
  margin-bottom: 5px;     /* baseline with send-btn + textarea's last line */
  border-radius: 50%;
  transition: color var(--t), background var(--t);
}
.mic-btn svg { width: 16px; height: 16px; }
.mic-btn:hover { color: var(--text2); }
/* Listening: calm accent colour + soft pulsing ring (not flashy). */
.mic-btn.listening {
  color: var(--accent);
  background: var(--accent-bg);
  animation: micPulse 1.8s ease-in-out infinite;
}

/* Friendly inline message for permission / availability issues. */
.mic-message {
  font-family: var(--font-sans);
  font-size: 11.5px;
  color: var(--text3);
  line-height: 1.45;
  padding: 0 var(--sp-4);
  max-height: 0;
  opacity: 0;
  overflow: hidden;
  transition: opacity 0.3s ease, max-height 0.3s ease, padding 0.3s ease;
}
.mic-message.show {
  opacity: 1;
  max-height: 60px;
  padding: var(--sp-15) var(--sp-4) var(--sp-05);
}

/* Send button — lives INSIDE the input pill, right of the mic. Sized to
   feel like an inline action button. Anchored to the bottom-right via
   `align-items: flex-end` on the parent pill.

   Animation trick: when the input is empty, this button collapses to
   `width: 0` AND `margin-left: 0`, removing it from the layout entirely.
   The mic then naturally slides right to take the rightmost position.
   When the user starts typing, the button grows back to 34px + 4px margin,
   pushing the mic left to make room. Pure flex flow — no JS, no
   absolute positioning, smooth transition on width drives the whole thing. */
.send-btn {
  width: 34px;
  height: 34px;
  margin-left: var(--sp-1);
  margin-bottom: 3px;
  background: var(--ink);
  border: none;
  border-radius: 50%;
  color: var(--bg);
  cursor: pointer;
  display: flex; align-items: center; justify-content: center;
  flex-shrink: 0;
  overflow: hidden;
  transition: width 0.22s cubic-bezier(0.4, 0, 0.2, 1),
              margin-left 0.22s cubic-bezier(0.4, 0, 0.2, 1),
              opacity 0.18s ease, transform 0.18s ease,
              background var(--t);
  opacity: 1;
  transform: scale(1);
}
.send-btn:hover    { opacity: 0.85; }
.send-btn:disabled { opacity: 0.4; cursor: default; }
.send-btn svg { width: 14px; height: 14px; flex-shrink: 0; }

/* Send button is permanent now that the toolbar has its own row. It simply
   dims and goes inert when the input is empty; any text lights it back up. */
.chat-input-wrap .chat-input:placeholder-shown ~ .send-btn {
  opacity: 0.35;
  pointer-events: none;
}

/* Dark mode: cream-on-cream has no contrast, so use the brand accent */
body.dark .send-btn { background: var(--accent); color: #222222; }

/* ──────────────────────────────────────
   STORAGE — tabbed redesign
────────────────────────────────────── */

/* 3-up tab bar variant */
.tab-bar-3 { padding: 0 var(--sp-35); }
.tab-bar-3 .tab-btn { flex: 0 0 auto; padding: var(--sp-3) 0 var(--sp-25); margin-right: var(--sp-45); font-size: var(--fs-base); }
.tab-bar-3 .tab-btn:last-child { margin-right: 0; }

/* Section header with count */
.storage-head-spacer { flex: 1; }
/* Upload / add button in the header — filled surface pill in the settings
   language (no stroke, rounded), quiet until hovered. */
.storage-upload-btn {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-15);
  padding: var(--sp-2) var(--sp-35);
  background: var(--bg2);
  color: var(--text);
  border: none;
  border-radius: var(--r-pill);
  font-family: var(--font-sans);
  font-size: var(--fs-base);
  font-weight: 600;
  cursor: pointer;
  transition: background var(--t);
}
.storage-upload-btn:hover { background: var(--bg3); }
.storage-upload-btn svg { width: 14px; height: 14px; }
body.dark .storage-upload-btn { background: rgba(255, 255, 255, 0.06); color: var(--text); }
body.dark .storage-upload-btn:hover { background: var(--glass-2); }

/* Empty-state drop area (doubles as upload affordance) — filled surface
   in the settings language, no dashed stroke; drag-active tints accent. */
.storage-empty-drop {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--sp-25);
  padding: 44px 20px;
  background: var(--bg2);
  border: none;
  border-radius: var(--r-xl);
  text-align: center;
  cursor: pointer;
  transition: background var(--t);
}
.storage-empty-drop:hover { background: var(--bg3); }
.storage-empty-drop.drag-active {
  background: var(--accent-bg);
  box-shadow: inset 0 0 0 2px var(--accent);
}
.storage-empty-icon {
  width: 48px; height: 48px;
  display: flex; align-items: center; justify-content: center;
  border-radius: var(--r-lg);
  background: var(--accent-bg);
  color: var(--accent);
}
.storage-empty-icon svg { width: 20px; height: 20px; }
.storage-empty-title {
  font-family: var(--font-sans);
  font-size: var(--fs-md);
  font-weight: 600;
  color: var(--text2);
}
.storage-empty-sub {
  font-family: var(--font-sans);
  font-size: var(--fs-sm);
  color: var(--text3);
}
/* When the populated list itself is a drop target */
.storage-list.drag-over {
  outline: 1.5px dashed var(--accent);
  outline-offset: 4px;
  border-radius: var(--r);
}

/* File list */
.storage-list { display: flex; flex-direction: column; gap: var(--sp-2); }

/* ──────────────────────────────────────
   PROFILE SCREEN (view-first, edit on tap)
────────────────────────────────────── */

/* Loading / empty / error states */
.storage-state {
  flex: 1;
  min-height: 280px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: var(--sp-6) var(--sp-4);
  text-align: center;
  font-family: var(--font-sans);
  font-size: var(--fs-base);
  color: var(--text3);
  line-height: var(--lh-normal);
}
.storage-state-error { color: var(--text2); }
/* Retry = .btn .btn-ink .btn-sm + centring residual. */
.storage-retry { display: flex; margin: var(--sp-25) auto 0; }

/* Case study cards — bookmark-style, not document-style. A CV is a
   single durable file; a case study is a *reference* to external work,
   one of a collection. The card surfaces title + distilled summary +
   source URL so the user can verify what Jobi understood from each.
   Subtle border + tint background keeps the cards quiet — these are
   meant to read as "what Jobi knows about my work" rather than
   demanding attention like primary content. */
.case-study-card {
  padding: var(--sp-35) var(--sp-4);
  background: var(--bg2);
  border: none;
  border-radius: var(--r-lg);
  display: flex;
  flex-direction: column;
  gap: var(--sp-15);
  transition: background var(--t);
}
.case-study-card:hover { background: var(--bg3); }
.case-study-card-row {
  display: flex;
  align-items: flex-start;
  gap: var(--sp-2);
}
.case-study-card-title {
  flex: 1;
  font-family: var(--font-serif);
  font-size: var(--fs-lg);
  font-weight: 500;
  line-height: var(--lh-snug);
  color: var(--ink);
 
}
.case-study-card-del {
  flex-shrink: 0;
  width: 24px;
  height: 24px;
  margin: -2px -4px 0 0;
  padding: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: transparent;
  border: none;
  border-radius: var(--r-sm);
  color: var(--text3);
  cursor: pointer;
  opacity: 0.7;
  transition: opacity var(--t), background var(--t), color var(--t);
}
.case-study-card-del:hover { opacity: 1; background: rgba(0,0,0,0.05); color: var(--text); }
.case-study-card-del svg { width: 14px; height: 14px; }
.case-study-card-summary {
  font-family: var(--font-sans);
  font-size: var(--fs-base);
  line-height: var(--lh-normal);
  color: var(--text2);
  /* Clamp to 4 lines so a verbose summary doesn't dominate the list.
     The full text lives in Jobi's memory regardless; the card is a
     surface for at-a-glance scanning, not the canonical record. */
  display: -webkit-box;
  -webkit-line-clamp: 4;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.case-study-card-source {
  font-family: var(--font-sans);
  font-size: var(--fs-xs);
  font-weight: 500;
  color: var(--text3);
  text-decoration: none;
 
  align-self: flex-start;
  padding-top: 1px;
  transition: color var(--t);
}
a.case-study-card-source:hover { color: var(--text); text-decoration: underline; text-underline-offset: 2px; }
.storage-inline-error {
  font-family: var(--font-sans);
  font-size: var(--fs-sm);
  color: var(--red);
  margin-bottom: var(--sp-2);
  text-align: center;
}

/* Uploading spinner on a storage item */
.storage-spinner { width: 16px; height: 16px; border-top-color: var(--accent); margin-right: var(--sp-1); }
.storage-item.uploading { opacity: 0.85; }

/* Icon colour variants */
.fi-doc-purple { background: rgba(139,92,246,0.15); } .fi-doc-purple svg { color: #8b5cf6; }
.fi-doc-green  { background: rgba(45,139,87,0.16);  } .fi-doc-green  svg { color: #3fa76a; }
.fi-link-blue  { background: rgba(59,115,212,0.16); } .fi-link-blue  svg { color: #5b9be0; }

/* Uploading state */
.storage-item.uploading {
  border-color: color-mix(in srgb, var(--accent) 30%, transparent);
  background: var(--accent-bg);
}
.storage-item.uploading .file-meta { color: var(--accent); font-weight: 500; }

/* Toast */
/* Toast: a sleek elevated card that floats above the surface — not a coloured
   block. Light default (clean white) with a dark override below; the depth
   comes from a hairline border + a soft layered shadow, not fill colour. */
.jobi-toast {
  position: fixed;
  bottom: 20px;
  left: 50%;
  transform: translateX(-50%) translateY(12px) scale(0.97);
  background: #000000;
  color: #ffffff;
  border: none;
  font-family: var(--font-sans);
  font-size: var(--fs-base);
  font-weight: 500;
  letter-spacing: -0.006em;
  line-height: 1.4;
  padding: 11px 16px;
  border-radius: 13px;
  box-shadow: 0 12px 34px rgba(0, 0, 0, 0.45), 0 2px 10px rgba(0, 0, 0, 0.3);
  opacity: 0;
  transition: opacity 0.24s cubic-bezier(0.22, 0.68, 0.24, 1),
              transform 0.28s cubic-bezier(0.22, 0.68, 0.24, 1);
  z-index: var(--z-toast);
  max-width: calc(100vw - 32px);
  white-space: normal;
  text-align: center;
  pointer-events: none;   /* a plain toast never intercepts taps on the app */
}
.jobi-toast.show { opacity: 1; transform: translateX(-50%) translateY(0) scale(1); }
@media (prefers-reduced-motion: reduce) {
  .jobi-toast { transition: opacity 0.2s ease; transform: translateX(-50%) scale(1); }
  .jobi-toast.show { transform: translateX(-50%) scale(1); }
}

/* ──────────────────────────────────────
   TAILORED COVER LETTER
   · Pulsing chat bubble while Sonnet generates (10–20s)
   · Full-screen preview rendering the backend's complete styled HTML doc in
     a sandboxed iframe (its CSS, our chrome)
   · Print-to-PDF via the iframe's own @media print rules
────────────────────────────────────── */

/* The Tailored-CV chip's styling lives on .job-card-tag (cover letters now show
   as a "Cover letter attached" note on the card, not a chip). */

/* The tracker's "Cover letter" tag now opens the letter in the editor tab as a
   read-only preview (see openCoverPreviewForJob), so the old in-panel preview
   screen and its styles were removed. */

/* ──────────────────────────────────────
   COVER LETTER TEMPLATE CUSTOMISATION
   A dedicated screen for choosing style, font, accent and sign-off. Live
   preview at the top reacts to selections; persisted to chrome.storage.
────────────────────────────────────── */

/* ═══════════════════════════════════════════════════════════════
   Cover letter template — restyled in the settings grouped-list
   language: each group (Structure, Accent Colour, Font, Sign-Off) is
   one filled bg2 surface (18dp) with its label inside; options are
   borderless bg3 pills whose selected state fills with --select, the
   same selection colour as the settings toggles and theme pills.
   ═══════════════════════════════════════════════════════════════ */
/* Cover-letter template screen. Layout + spacing come from .settings-body
   (shared with the settings sub-pages, so it reads as the same screen family);
   .ctpl only scopes the M3 tokens the carousel widgets use. */
.ctpl { --m3-ease: cubic-bezier(0.2, 0, 0, 1); --m3-dur: 0.2s;
        --m3-hover: color-mix(in srgb, var(--text) 8%, transparent);
        --m3-press: color-mix(in srgb, var(--text) 10%, transparent); }

/* Focus ring for the carousel widgets on this screen (the Font/Sign-off rows
   and swatches follow the settings focus treatment). */
.ctpl-accent:focus-visible, .ctpl-arrow:focus-visible, .ctpl-dot:focus-visible {
  outline: none; box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 55%, transparent);
}

/* ── Structure: carousel inside the group surface ── */
/* The panel provides the fill; the media strip is an inset bg3 card. Card
   height is CONSTANT: fixed media strip + a single-line caption, so switching
   styles or resizing the panel never changes the group's height. */
.ctpl-stack { width: 100%; background: none; border: none; border-radius: 0; overflow: visible; }
.ctpl-stack-media { width: 100%; height: 208px; background: var(--bg3); border-radius: var(--r); overflow: hidden; }
.ctpl-carousel-track { display: flex; height: 100%; transition: transform 0.4s var(--m3-ease); }
.ctpl-slide { flex: 0 0 100%; min-width: 100%; height: 100%; display: flex; align-items: center; justify-content: center; padding: var(--sp-3) 0; }
.ctpl-stack-bar { display: flex; align-items: center; gap: var(--sp-35); padding: var(--sp-25) 0 0; }
.ctpl-stack-caption { flex: 1 1 auto; min-width: 0; text-align: center; }
/* Style name matches the panel labels (Accent Colour, Font, ...). */
.ctpl-stack-name { font-family: var(--font-sans); font-size: var(--fs-base); line-height: var(--lh-normal); font-weight: 500; letter-spacing: 0; color: var(--text); }
/* XSmall round icon button (32dp) inside the card. */
.ctpl-arrow {
  width: 32px; height: 32px; flex: 0 0 auto;
  display: inline-flex; align-items: center; justify-content: center;
  border: none; background: none; color: var(--text2);
  border-radius: var(--r-pill); cursor: pointer; padding: 0;
  transition: background var(--m3-dur) var(--m3-ease), border-radius var(--m3-dur) var(--m3-ease), color var(--m3-dur) var(--m3-ease);
}
.ctpl-arrow:hover { background: var(--m3-hover); color: var(--text); }
.ctpl-arrow:active { background: var(--m3-press); border-radius: 8px; }
.ctpl-arrow svg { width: 18px; height: 18px; }
.ctpl-prev svg { transform: rotate(180deg); }   /* base chevron points right → flip to left */
.ctpl-next svg { transform: none; }              /* base chevron already points right */
.ctpl-dots { display: flex; justify-content: center; align-items: center; gap: var(--sp-15); padding: var(--sp-2) 0 var(--sp-05); }
.ctpl-dot { width: 7px; height: 7px; border-radius: 50%; border: none; background: var(--border2); cursor: pointer; padding: 0; transition: background var(--m3-dur) var(--m3-ease); }
.ctpl-dot:hover { background: var(--text3); }
.ctpl-dot.is-active { background: var(--select); }

/* The mini "page" — always light (it represents a printed letter). */
.ctpl-lofi {
  height: 100%; aspect-ratio: 1 / 1.32;
  background: #fff; border-radius: 8px;
  box-shadow: 0 1px 3px rgba(0,0,0,0.3);
  padding: 11px 10px; display: flex; flex-direction: column; gap: var(--sp-15);
  cursor: pointer;
}
.lofi-bar { background: #dde1e6; border-radius: 2px; height: 3.5px; }

/* Header — two columns: name + tagline (left), contact strip (right). This is
   the real template's .header (flex, space-between). Greeting/body/sign-off are
   left-aligned for every style, exactly as the renderer outputs. */
.lofi-header { display: flex; justify-content: space-between; align-items: flex-start; gap: var(--sp-2); }
.lofi-hleft { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: 3.5px; align-items: flex-start; }
.lofi-name { height: 9px; width: 90%; background: #aab0b9; border-radius: 3px; }
.lofi-tag { width: 62%; }
.lofi-hright { flex: 0 0 auto; display: flex; flex-direction: column; gap: 3px; align-items: flex-end; }
.lofi-link { height: 3.5px; width: 20px; border-radius: 2px; background: var(--ctpl-accent); opacity: 0.55; }

.lofi-greet { width: 52%; margin-top: 1px; }
.lofi-para { display: flex; flex-direction: column; gap: 3.5px; }
.lofi-para .lofi-bar { width: 100%; }
.lofi-para .lofi-bar:last-child { width: 72%; }
.lofi-sign { display: flex; flex-direction: column; gap: 3.5px; margin-top: auto; align-items: flex-start; }
.lofi-signoff { width: 30%; }
.lofi-signname { width: 42%; height: 5.5px; background: #aab0b9; }

/* MODERN — left name, contact stacked top-right (the default; matches output). */
.ctpl-lofi.is-style-modern .lofi-name { height: 10px; }

/* EDITORIAL — centred letterhead (name + tagline stacked and centred), then the
   contacts as a centred horizontal byline. hleft must NOT flex-grow here or it
   pushes the byline down and the name reads as missing. Body stays left. */
.ctpl-lofi.is-style-editorial .lofi-header { flex-direction: column; align-items: center; gap: var(--sp-15); }
.ctpl-lofi.is-style-editorial .lofi-hleft { flex: 0 0 auto; width: 100%; align-items: center; gap: var(--sp-1); }
.ctpl-lofi.is-style-editorial .lofi-name { width: 66%; height: 10px; }
.ctpl-lofi.is-style-editorial .lofi-tag { width: 46%; }
.ctpl-lofi.is-style-editorial .lofi-hright { flex: 0 0 auto; flex-direction: row; justify-content: center; align-items: center; gap: 5px; }
.ctpl-lofi.is-style-editorial .lofi-link { width: 15px; }

/* Structure card holds the carousel with no inner chrome of its own — the
   surrounding .s-card provides the fill; the media strip is an inset bg3 card. */
.s-card .ctpl-stack { gap: 0; }

/* ── Accent swatches: 32dp rounded squares, check on the selected one ── */
/* One row, never wraps; swatches that would surpass the row are clipped. */
.ctpl-accent-row { display: flex; gap: var(--sp-2); flex-wrap: nowrap; overflow: hidden; }
.ctpl-accent {
  width: 32px; height: 32px;
  display: inline-flex; align-items: center; justify-content: center;
  border: none; border-radius: 8px; padding: 0; cursor: pointer;
  transition: transform var(--m3-dur) var(--m3-ease);
}
.ctpl-accent:hover { transform: scale(1.08); }
.ctpl-accent svg { width: 20px; height: 20px; }


/* ═══════════════════════════════════════════════════════════════
   Voice Interview Mode
   ═══════════════════════════════════════════════════════════════
   A dedicated full-surface screen built around the orb (visual focal
   point). State drives both the orb's animation and which controls
   appear. The screen is fully responsive within the side panel and
   doesn't depend on any other layout. */

.iv-screen {
  display: flex;
  flex-direction: column;
  flex: 1 1 auto;
  min-height: 0;
  position: relative;
  padding: var(--sp-5) var(--sp-5) var(--sp-6);
}

/* Welcome / ended state — CSS Grid with three rows: title (auto, top),
   center-area (1fr, fills the middle), CTA group (auto, bottom). */
.iv-welcome-inner {
  display: grid;
  grid-template-rows: auto 1fr auto;
  height: 100%;
  width: 100%;
  text-align: center;
  justify-items: center;
  padding: var(--sp-1) 0 var(--sp-2);
  box-sizing: border-box;
}

/* The center-area is the 1fr middle row. It's the positioning context
   for the absolutely-pinned orb and the text-flow beneath it. Because
   both the orb and the text are absolutely positioned WITHIN this area,
   neither affects the other's layout — the orb stays dead-centre no
   matter how the text below it grows or changes. This is what stops
   the "orb re-centres when text appears" jump. */
.iv-center-area {
  position: relative;
  width: 100%;
  min-height: 180px;   /* guarantees the area has height even before
                          any text mounts, so the orb has somewhere to
                          centre into on first paint */
}

/* Text sits just below the pinned orb (orb is 124px, so half + gap ≈
   74px below the area's centre line). Absolutely positioned so its
   content length never pushes the orb. */
.iv-text-flow {
  position: absolute;
  top: 50%;
  left: 0;
  right: 0;
  margin-top: 74px;
  padding: 0 var(--sp-55);
  box-sizing: border-box;
  text-align: center;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--sp-2);
}

/* CTA group at the bottom — start button, hint, any notices. */
.iv-cta-group {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--sp-25);
  width: 100%;
}

/* Active state — same three-zone Grid as the welcome state.
   Row 1 (auto): progress indicator at top
   Row 2 (1fr): focal — orb + status + question + live transcription,
     centred in all the remaining space
   Row 3 (auto): controls pinned to the bottom
   Controls are now a grid child (third row) rather than a margin-top:
   auto flex sibling — simpler and can't drift. */
.iv-active-inner {
  display: flex;
  flex-direction: column;
  flex: 1 1 auto;
  min-height: 0;
  width: 100%;
  text-align: center;
  box-sizing: border-box;
  padding-top: var(--sp-2);
}

/* Rolling 3-line subtitle viewport. Fixed height = 3 lines, so nothing
   above ever shifts; the inner text is translated upward as karaoke
   advances (see _scrollInterviewSubs) — new line in, oldest line out. */
.iv-subs {
  width: 100%;
  max-width: 300px;
  font-size: var(--fs-lg);
  height: calc(3 * 1.5em);
  overflow: hidden;
  position: relative;
  -webkit-mask-image: linear-gradient(to bottom, transparent 0, #000 9%, #000 91%, transparent 100%);
          mask-image: linear-gradient(to bottom, transparent 0, #000 9%, #000 91%, transparent 100%);
}
.iv-subs-inner {
  position: relative;
  font-family: var(--font-serif, 'Inter', -apple-system, BlinkMacSystemFont, sans-serif);
  font-size: var(--fs-lg);
  line-height: var(--lh-normal);
  color: var(--text);
  text-align: center;
  padding: 0 var(--sp-1);
  transition: transform 0.45s cubic-bezier(0.22, 0.61, 0.36, 1);
  will-change: transform;
}


/* Quiet progress indicator at the very top. Hidden when empty. */

.iv-title {
  font-family: var(--font-serif, 'Inter', -apple-system, BlinkMacSystemFont, sans-serif);
  font-size: 24px;
  font-weight: 500;
  margin: var(--sp-2) 0 0;
  color: var(--text);
}

.iv-subtitle {
  font-family: var(--font-sans);
  font-size: var(--fs-md);
  line-height: var(--lh-normal);
  color: var(--text2);
  margin: 0;
  max-width: 280px;
}

.iv-notice {
  font-family: var(--font-sans);
  font-size: var(--fs-base);
  line-height: var(--lh-normal);
  color: var(--text2);
  background: var(--bg2);
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
  padding: var(--sp-25) var(--sp-35);
  max-width: 300px;
}
.iv-notice.iv-error { color: var(--red); border-color: var(--red); background: var(--red-bg); }

/* CV nudge — visually distinct from error/permission notices. Warm accent
   tint + an inline action link, so the user can fix the gap in one tap. */
.iv-cv-nudge {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--sp-2);
  background: var(--accent-bg);
  border-color: color-mix(in srgb, var(--accent) 30%, transparent);
  color: var(--text);
}
.iv-cv-link {
  background: none;
  border: none;
  padding: 0;
  color: var(--accent);
  font-family: var(--font-sans);
  font-size: var(--fs-base);
  font-weight: 600;
  cursor: pointer;
}
.iv-cv-link:hover { text-decoration: underline; }

.iv-start-btn {
  margin-top: var(--sp-15);
  padding: var(--sp-3) 28px;
  background: var(--ink);
  color: var(--bg);
  border: none;
  border-radius: var(--r-pill);
  font-family: var(--font-sans);
  font-size: var(--fs-md);
  font-weight: 600;
 
  cursor: pointer;
  transition: opacity var(--t), transform var(--t);
}
.iv-start-btn:hover { background: color-mix(in srgb, var(--ink) 86%, var(--bg)); }
.iv-start-btn:active { transform: scale(0.98); }

.iv-hint {
  font-family: var(--font-sans);
  font-size: var(--fs-sm);
  color: var(--text3);
  margin-top: var(--sp-05);
}

/* ── Orb (the visual focal point) ──────────────────────────── */

/* ── Active session UI ─────────────────────────────────────── */
.iv-status {
  font-family: var(--font-sans);
  font-size: var(--fs-xs);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 1.6px;
  color: var(--text3);
  margin-top: var(--sp-05);
}

.iv-question {
  font-family: var(--font-serif, 'Inter', -apple-system, BlinkMacSystemFont, sans-serif);
  font-size: 23px;
  line-height: 1.34;
  color: var(--text);
  max-width: none;
  margin: 0;
  text-align: left;
}

/* Karaoke-style word highlighting during the speaking state. Unspoken
   words sit in a slightly muted tone — still readable, but visually
   distinct from the running highlight. As the audio plays, each word
   gets .iv-word-spoken added (incrementally, in JS) and shifts to full
   text colour. The transition stays short so the highlight feels in
   time with speech rather than trailing behind. */
.iv-word {
  color: var(--text2);
  transition: color 0.12s ease;
}
.iv-word.iv-word-spoken {
  color: var(--text);
}
/* Only reserve vertical space when actually listening; otherwise the empty
   wrap takes up room the question could use. */
.iv-live {
  font-family: var(--font-sans);
  font-size: var(--fs-md);
  line-height: var(--lh-normal);
  color: var(--text2);
  font-style: italic;
  text-align: center;
  min-height: 1.5em;
  max-height: 4.6em;
  overflow-y: auto;
} 

/* 11. Text-shimmer — gradient sweeps across text horizontally. Best
         pattern for "generating..." copy where you want elegance not noise. */
@keyframes jobi-text-shimmer {
  0%   { background-position: 200% 0; }
  100% { background-position: -200% 0; }
}
.jobi-loader-text-shimmer {
  font-family: var(--font-sans);
  background: linear-gradient(
    90deg,
    var(--text2) 0%,
    var(--text)  50%,
    var(--text2) 100%
  );
  background-size: 200% 100%;
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
  color: transparent;
  animation: jobi-text-shimmer 2.4s linear infinite;
}

/* ═══════════════════════════════════════════════════════════════════
   Interview screen
   ═══════════════════════════════════════════════════════════════════ */

/* Positioned flex column; content stacks with controls docked at the
   bottom. Transparent background — like every other screen's content —
   so the shared body-level orb backdrop (#dark-orbs-canvas, the soft
   top glow) shows through in both light and dark, instead of an opaque
   fill painting over it. */
.iv-screen {
  position: relative;
  overflow: hidden;
  background: var(--bg);   /* solid — matches the other screens, no orb-backdrop gradient */
}

.iv-welcome-inner,
.iv-active-inner {
  position: relative;
  z-index: 1;
}

/* Active state: content takes the available space, controls anchor
   at the bottom via the flex flow (no more margin-top:auto needed
   on .iv-controls since they're a screen-level sibling now). */
.iv-active-inner {
  /* Reset the centred-controls-anchor that lived here before. The
     controls are now a sibling, not a child. */
}

/* ── Contrast tuning for the interview screen ─────────────────────
   The aurora's translucent purple blobs tint the cream base in
   patches, so the default secondary text colours (--text2 / --text3)
   lose contrast where the bloom is densest. Bump each tier up by one
   step so the typography holds its weight regardless of which spot of
   the aurora it lands on.
   The end-interview button uses the default ghost-button styling
   (dark-grey text + neutral border + red hover) — those colours were
   designed for a light surface and read cleanly against the aurora
   without a special override. */
.iv-screen .iv-subtitle    { color: var(--text);  }    /* was --text2 */
.iv-screen .iv-hint        { color: var(--text2); }            /* was --text3 */

/* ── Progressive word reveal ──────────────────────────────────────
   Words are hidden by default (opacity 0) and fade in as the TTS
   audio reaches them — the existing audio.ontimeupdate handler in
   popup.js adds .iv-word-spoken to each word in order. The reveal
   is a 220ms ease, slightly slower than the previous "dim → full"
   so the appearance reads as deliberate rather than abrupt.
   Replaces the prior karaoke-highlight model where all words
   showed muted from the start. */
.iv-word {
  opacity: 0;
  transition: opacity 0.22s ease-out;
}
.iv-word.iv-word-spoken {
  opacity: 1;
}

/* Keep header transparent so the iv-aurora-bg (which extends behind
   the header via position:fixed) shows through uninterrupted. */
body:has(.iv-screen) .header {
  background: transparent;
}

/* ── End-interview button — stronger contrast against aurora ───────
   The default ghost styling (--text2 colour + --border 1px) loses
   definition against the purple aurora. Override with a tinted-white
   fill + a medium-grey border so the button reads as a clear
   secondary action regardless of where the aurora blooms land. */

/* ── Stronger weight + darker tones for interview chrome ───────────
   Body text on a non-flat (aurora-tinted) surface needs more weight
   and slightly darker tones to read cleanly. Title stays at 500
   (already heavy), but bumping subtitle/status/question to 500 too
   gives them the gravity to hold their own against the purple bloom. */
.iv-screen .iv-subtitle    { font-weight: 500; color: var(--text); }
.iv-screen .iv-status      { font-weight: 600; }
.iv-screen .iv-question    { font-weight: 500; }
.iv-screen .iv-hint        { color: var(--text2); }

/* ═══════════════════════════════════════════════════════════════════
   Chat attachments — attach button + chip preview
   ═══════════════════════════════════════════════════════════════════ */

/* Wrap needs position:relative so the chip can absolute-position
   itself above the input area. */
.chat-input-wrap { position: relative; }

/* ── Attach button ────────────────────────────────────────────────
   A flat icon (no circle background) that lives at the left edge of
   the input wrap — Gemini-style. Follows the SAME expansion animation
   as the input pill: same 0.22s cubic-bezier(0.4, 0, 0.2, 1) timing
   for the width transition so the icon appears as the input grows,
   not as a separate gesture. Subtle hover state for affordance. */
.attach-btn {
  width: 34px;
  height: 34px;
  margin-right: var(--sp-15);
  margin-bottom: 3px;
  background: transparent;
  border: none;
  border-radius: 50%;
  color: var(--text3);
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  overflow: hidden;
  opacity: 1;
  transition: width 0.22s cubic-bezier(0.4, 0, 0.2, 1),
              margin-right 0.22s cubic-bezier(0.4, 0, 0.2, 1),
              opacity 0.18s ease,
              color var(--t),
              background var(--t),
              transform 0.18s ease;
}
.attach-btn svg { width: 18px; height: 18px; transition: transform 0.2s ease; }
.attach-btn.is-active svg { transform: rotate(45deg); }
/* While the menu is open the cross must sit above the backdrop, otherwise
   the backdrop (z-index 40) swallows the pointer and :hover never fires. */
.attach-btn.is-active { position: relative; z-index: 50; }
.attach-btn:hover {
  color: var(--text);
  background: var(--bg2);
}
.attach-btn:active { transform: scale(0.92); }

/* Reveal triggers: focus or typed content */
.chat-input-wrap:focus-within .attach-btn,
.chat-input-wrap:has(.chat-input:not(:placeholder-shown)) .attach-btn {
  width: 34px;
  margin-right: var(--sp-15);
  opacity: 1;
}

/* Tighten the wrap's left padding when the attach button appears, so
   the textarea doesn't sit too far in (otherwise 18px + 34px + 6px
   pushes content well off the left edge). */
.chat-input-wrap:focus-within,
.chat-input-wrap:has(.chat-input:not(:placeholder-shown)) {
  padding-left: var(--sp-15);
}

/* ── PDF download button — loading state ──────────────────────────
   The /api/cover-letter/pdf call takes 2-4s for PDFShift to render
   the document. Spinner + disabled state during the wait so users
   don't think the click missed (and re-click, queuing duplicate
   PDF jobs against the rate limit). */
.cover-pdf-spinner { width: 13px; height: 13px; margin-right: var(--sp-15); vertical-align: -2px; opacity: 0.75; }
.cover-preview-btn-loading {
  cursor: progress;
  opacity: 0.85;
}

/* ── Cover-template letterhead inputs ─────────────────────────────
   Text inputs for tagline + LinkedIn + portfolio inside the cover-
   template settings screen. Match the visual language of the other
   ctpl sections (selected-state outline, small radius, breathing
   room) so they feel native to the screen. */

/* ══ Analyst .acv card shell ═══════════════════════════════════════
   Now used only by the tailor-CV carousel and the cover-letter card
   (JD analysis + company research moved to the .jdc/.jds cards). A flex
   column on a bg2 surface; nav pins to the bottom. NO internal scroll —
   long content grows the card and the chat scrolls. */
/* Every card is at least the free chat space tall (--acv-card-h, set via JS) so
   cards read as one consistent size. The card is a flex column; nav/foot pin to
   the bottom. NO internal scroll - long content grows the card and the CHAT
   scrolls (so scrolling up the chat is never hijacked by a card). */
.acv { width: 100%; align-self: flex-start; background: var(--bg2); border-radius: var(--r-lg); padding: var(--sp-45) var(--sp-45) var(--sp-4); box-sizing: border-box; min-height: var(--acv-card-h, auto); display: flex; flex-direction: column; }
.acv-head, .acv > .acv-card-title { flex: 0 0 auto; }
.acv-scroll { flex: 1 1 auto; min-height: 0; }
.acv:focus { outline: none; }
.acv:focus-visible { outline: 2px solid var(--accent-bg); outline-offset: 4px; border-radius: var(--r); }

.acv-head { display: flex; align-items: center; justify-content: space-between; gap: var(--sp-25); margin-bottom: var(--sp-4); padding-bottom: 11px; border-bottom: 1px solid var(--border); }
.acv-eyebrow { display: inline-flex; align-items: center; gap: 7px; min-width: 0; font-family: var(--font-sans); font-size: 12.5px; font-weight: 500; letter-spacing: 0; color: var(--text2); overflow: hidden; }
.acv-eyebrow-ic { display: inline-flex; flex: 0 0 auto; color: var(--text3); }
.acv-eyebrow-ic svg { width: 14px; height: 14px; }
.acv-eyebrow-co { font-weight: 600; color: var(--text); white-space: nowrap; }
.acv-counter { flex: 0 0 auto; font-family: ui-monospace, 'SF Mono', Menlo, monospace; font-size: var(--fs-xs); font-weight: 500; color: var(--text3); letter-spacing: 0.04em; font-variant-numeric: tabular-nums; }
.acv-track { display: flex; align-items: flex-start; will-change: transform; }

/* Inline emphasis. The change/standout highlight is a bright coral marker. */
.acv-mark { background: var(--accent); color: var(--accent-text); border-radius: 3px; padding: 0 var(--sp-1); font-weight: 600; }
.acv u { text-decoration-color: var(--accent); text-underline-offset: 2px; }
.acv strong { font-weight: 700; color: var(--text); }

.acv-card-title { font-family: var(--font-serif); font-size: var(--fs-lg); font-weight: 600; letter-spacing: var(--ls-tight); text-transform: none; color: var(--select); margin-bottom: var(--sp-3); }

/* Cover letter: footer actions + edit affordances. */
.acv-actions { display: flex; flex-wrap: wrap; gap: var(--sp-2); margin-top: var(--sp-4); padding-top: var(--sp-35); border-top: 1px solid var(--border); }
.acv-btn { display: inline-flex; align-items: center; gap: 7px; padding: 9px 14px; border-radius: var(--r-pill); border: none; background: var(--bg3); color: var(--text); font-family: var(--font-sans); font-size: 12.5px; font-weight: 500; cursor: pointer; transition: background var(--t); }
.acv-btn:hover { background: var(--bg4); }
.acv-btn svg { width: 14px; height: 14px; flex: 0 0 auto; }
.acv-btn-primary { background: var(--accent); color: var(--accent-text); }
.acv-btn-primary:hover { background: var(--accent-hover); }
.acv-btn-ghost { background: transparent; box-shadow: inset 0 0 0 1px var(--border2); }
.acv-btn-ghost:hover { background: var(--bg3); }
.acv-icon-btn { flex: 0 0 auto; display: inline-flex; align-items: center; justify-content: center; width: 28px; height: 28px; border-radius: 8px; border: none; background: transparent; color: var(--text3); cursor: pointer; transition: all var(--t); }
.acv-icon-btn:hover { background: var(--bg3); color: var(--text); }
.acv-icon-btn svg { width: 16px; height: 16px; }
.acv-edit { flex: 1 1 auto; min-height: 120px; width: 100%; box-sizing: border-box; background: var(--bg3); border: 1px solid var(--border); border-radius: var(--r); color: var(--text); font-family: var(--font-sans); font-size: 13.5px; line-height: 1.6; padding: var(--sp-3); resize: none; }
/* Tagline edit inside the cover-letter card (saves to the profile). */
.acv-tagline-input { margin-bottom: var(--sp-25); }
.acv-edit:focus { outline: none; border-color: var(--border2); }

/* narrative (warm) */
.acv-narr-body { font-size: var(--fs-md); color: var(--text); line-height: 1.55; margin: 0 0 11px; text-wrap: pretty; hyphens: auto; hyphenate-limit-chars: 10 4 3; overflow-wrap: break-word; }
.acv-narr-body:last-child { margin-bottom: 0; }

/* nav: arrows + dots */
.acv-nav { display: flex; align-items: center; gap: var(--sp-25); margin-top: auto; padding-top: 13px; border-top: 1px solid var(--border); }
.acv-dots { flex: 1; display: flex; align-items: center; justify-content: center; gap: var(--sp-15); }


/* ══ JD analysis card — verdict-first ══════════════════════════════════
   Replaces the analyst carousel for JD analysis (company research keeps it).
   The colour trio maps to existing tokens — sage=--green, salmon=--red, and
   tan = the gold the fit card already uses (readable per theme); tints derive
   via color-mix so no new brand colours are introduced. Vars live on both the
   card and the sheet (the sheet mounts on <body>, outside .jdc). */
.jdc, .jds { --jd-sage: var(--green); --jd-salmon: var(--red); --jd-tan: #9a6b12; }
body.dark .jdc, body.dark .jds { --jd-tan: #d3a64a; }
/* Per-colour channel: any element carrying a colour class exposes --c. */
.jdc-sage   { --c: var(--jd-sage); }
.jdc-salmon { --c: var(--jd-salmon); }
.jdc-tan    { --c: var(--jd-tan); }
.jdc-blue   { --c: var(--blue); }

/* The card matches the analyst .acv shell (bg2, r-lg, 18/18/16 padding, a
   hairline under the header) so it reads as the same family of rich chat card. */
.jdc { font-family: var(--font-sans); width: 100%; box-sizing: border-box; background: #1F1F1F; border-radius: 20px; padding: var(--sp-45) var(--sp-45) var(--sp-4); display: flex; flex-direction: column; }

/* Header */
.jdc-head { display: flex; align-items: flex-start; justify-content: space-between; gap: var(--sp-3); margin-bottom: var(--sp-35); padding-bottom: var(--sp-3); border-bottom: 1px solid var(--border); }
/* Company card: single-line title, so centre the tag on the same row as it. */
.jdc-head-center { align-items: center; }
.jdc-titles { min-width: 0; }
.jdc-co { font-size: var(--fs-md); font-weight: 600; color: var(--text); letter-spacing: var(--ls-tight); line-height: var(--lh-snug); }
.jdc-role { font-size: var(--fs-sm); color: var(--text3); margin-top: 1px; line-height: var(--lh-snug); }
/* Fixed white/black tag in both themes (a deliberate branded label, like the
   theme-independent hero surfaces). */
/* Name tag: white label on a dark neutral (#171717), a step darker than the
   card. A rounded rectangle (not a pill) with generous vertical padding. */
.jdc-type { flex-shrink: 0; margin-top: 1px; font-size: var(--fs-2xs); font-weight: 700; letter-spacing: var(--ls-caps); text-transform: uppercase; color: #ffffff; background: #171717; padding: 7px 12px; border-radius: 8px; white-space: nowrap; }

/* Hero score */
.jdc-hero { display: flex; align-items: baseline; gap: var(--sp-2); margin-bottom: var(--sp-25); }
.jdc-score { font-family: 'Coiny', var(--font-sans); font-size: 38px; font-weight: 400; line-height: 1; letter-spacing: 0; font-variant-numeric: tabular-nums; color: #FED43A; }
.jdc-max { font-family: var(--font-sans); font-size: var(--fs-md); font-weight: 400; color: var(--text4); letter-spacing: 0; margin-left: 2px; }
.jdc-qual { font-size: var(--fs-base); color: var(--text3); }
.jdc-qual-lead { color: var(--text); font-weight: 700; }
.jdc-qual-band { color: var(--text3); }

/* Verdict — the single most important line, on the type scale (14px). */
.jdc-verdict { font-size: var(--fs-md); font-weight: 500; color: var(--text); line-height: var(--lh-normal); letter-spacing: var(--ls-tight); text-wrap: pretty; margin-bottom: var(--sp-4); }
.jdc-em { color: var(--c, var(--text)); font-weight: 600; }
/* Company card lead ("what they do") — the headline insight in place of a score. */
.jdc-lead { font-size: var(--fs-md); font-weight: 500; color: var(--text); line-height: var(--lh-normal); letter-spacing: var(--ls-tight); text-wrap: pretty; margin-bottom: var(--sp-4); }

/* Tailor CV CTA at the foot of the card — the primary next action. */
.jdc-cta {
  display: flex; align-items: center; justify-content: center; gap: var(--sp-2);
  width: 100%; margin-top: var(--sp-35);
  padding: 12px; border: none; border-radius: var(--r);
  background: var(--accent); color: var(--accent-text);
  font-family: var(--font-sans); font-size: var(--fs-base); font-weight: 600;
  cursor: pointer; transition: background var(--t), transform var(--dur-press) var(--ease-out);
}
.jdc-cta:hover { background: var(--accent-hover); }
.jdc-cta:active { transform: scale(0.99); }
.jdc-cta svg { width: 16px; height: 16px; flex-shrink: 0; }

/* Rows — nested well surfaces (bg3) inside the bg2 card. Icon-less: a bold
   white title + a muted description, with a chevron on the right. The row is
   neutral regardless of its facet (no band colours or icons), so the card
   reads as clean, uniform text. */
.jdc-rows { display: flex; flex-direction: column; gap: var(--sp-2); }
.jdc-row { display: flex; align-items: center; gap: var(--sp-25); width: 100%; text-align: left; padding: 14px 16px; border: none; cursor: pointer; background: var(--bg3); border-radius: 12px; font-family: inherit; color: var(--text); transition: background var(--t), transform var(--dur-press) var(--ease-out); }
.jdc-row:hover { background: var(--bg4); }
.jdc-row:active { transform: scale(0.985); }
.jdc-row:focus-visible { outline: none; box-shadow: 0 0 0 3px color-mix(in srgb, var(--jd-sage) 55%, transparent); }
.jdc-row-main { flex: 1; min-width: 0; }
.jdc-row-label { font-size: var(--fs-base); line-height: var(--lh-snug); color: var(--text); }
.jdc-row-label b { font-weight: 700; color: var(--text); }
.jdc-row-tag { font-weight: 400; color: var(--text3); }
.jdc-row-desc { font-size: var(--fs-sm); color: var(--text3); margin-top: 3px; line-height: var(--lh-snug); display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; overflow: hidden; }
.jdc-row-chev { flex-shrink: 0; align-self: center; display: inline-flex; color: var(--text3); opacity: 0.5; }
.jdc-row-chev svg { width: 16px; height: 16px; }

/* ── Bottom sheet (scoped to the panel via position:fixed) ── */
.jds-scrim { position: fixed; inset: 0; z-index: var(--z-modal); display: flex; align-items: flex-end; background: rgba(0, 0, 0, 0.55); opacity: 0; transition: opacity 300ms var(--ease-out); }
.jds-scrim.is-open { opacity: 1; }
.jds { width: 100%; max-height: 78%; background: var(--bg); border-top-left-radius: 24px; border-top-right-radius: 24px; box-shadow: var(--shadow-3); display: flex; flex-direction: column; outline: none; transform: translateY(100%); transition: transform 300ms var(--ease-out); }
.jds.is-open { transform: translateY(0); }
.jds-handle { flex-shrink: 0; width: 36px; height: 4px; border-radius: var(--r-pill); background: var(--border2); margin: 10px auto 4px; }
.jds-body { overflow-y: auto; padding: var(--sp-2) var(--sp-5) var(--sp-6); }
.jds-head { display: flex; align-items: center; gap: var(--sp-3); margin-bottom: 0; }
/* Divider between every drawer's header and its content below. */
.jds-hr { height: 1px; background: var(--border); margin: var(--sp-3) 0 var(--sp-4); }
.jds-title { font-size: var(--fs-lg); font-weight: 700; color: var(--text); letter-spacing: var(--ls-tight); line-height: 1.2; }
.jds-sub { font-size: var(--fs-sm); color: var(--text3); margin-top: 1px; }
.jds-intro { font-size: var(--fs-base); color: var(--text2); line-height: var(--lh-relaxed); margin: 0 0 var(--sp-3); }
.jds-intro:last-child { margin-bottom: 0; }
.jds-items { display: flex; flex-direction: column; }
.jds-item { display: flex; gap: 11px; padding: 13px 0; border-top: 1px solid var(--border); }
.jds-item:first-child { border-top: none; padding-top: 2px; }
.jds-dot { flex-shrink: 0; width: 6px; height: 6px; border-radius: 50%; margin-top: 8px; background: var(--c, var(--text3)); }
.jds-dot.jdc-neutral { background: var(--text3); }
.jds-item-t { font-size: var(--fs-base); color: var(--text3); line-height: var(--lh-normal); }
.jds-item-t b { color: var(--text); font-weight: 600; }
.jds-chips { display: flex; flex-wrap: wrap; gap: var(--sp-2); }
.jds-chip { border: none; cursor: pointer; font-family: inherit; background: var(--bg2); color: var(--text); font-size: var(--fs-sm); font-weight: 500; padding: 8px 13px; border-radius: var(--r-pill); transition: background var(--t), color var(--t); }
.jds-chip:hover { background: var(--bg3); }
.jds-chip.is-copied { background: color-mix(in srgb, var(--green) 20%, transparent); color: var(--green); }
.jds-chip.is-copied::after { content: '✓'; margin-left: 5px; font-weight: 700; }
.jds-chip:focus-visible { outline: none; box-shadow: 0 0 0 3px color-mix(in srgb, var(--green) 55%, transparent); }
/* Sources sheet — tappable source links + the official website. */
.jds-links { display: flex; flex-direction: column; }
.jds-link { display: flex; align-items: center; gap: 10px; padding: 12px 0; border-top: 1px solid var(--border); text-decoration: none; color: var(--text); }
.jds-link:first-child { border-top: none; padding-top: 2px; }
.jds-link-t { font-size: var(--fs-base); min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.jds-link:hover .jds-link-t { color: var(--blue); }
.jds-link-host { margin-left: auto; flex-shrink: 0; font-size: var(--fs-sm); color: var(--text3); }
/* "Your angle" sheet — a smart question to ask, as a tinted callout. */
.jds-ask { margin-top: var(--sp-4); padding: var(--sp-3); border-radius: var(--r); background: color-mix(in srgb, var(--c, var(--text)) 12%, transparent); }
.jds-ask-label { font-size: var(--fs-2xs); font-weight: 700; letter-spacing: var(--ls-caps); text-transform: uppercase; color: var(--c, var(--text2)); margin-bottom: 4px; }
.jds-ask-q { font-size: var(--fs-base); color: var(--text); font-style: italic; line-height: var(--lh-snug); }
/* Tailored-CV sheet: the "Why these changes" callout body (plain, not the
   italic question style), the changed-word marker (pink TEXT, not a highlight
   block: the bullet reads grey with Jobi's changes in salmon), and the
   full-width copy action. background:transparent overrides <mark>'s UA yellow. */
.jds-why-t { font-size: var(--fs-base); color: var(--text); line-height: var(--lh-normal); }
.jds-mark { background: transparent; color: var(--accent); }
.jds-copy { margin-top: var(--sp-45); width: 100%; display: inline-flex; align-items: center; justify-content: center; gap: 7px; padding: var(--sp-25) var(--sp-35); background: var(--bg2); border: none; border-radius: var(--r-pill); color: var(--text); font-family: var(--font-sans); font-size: var(--fs-sm); font-weight: 600; cursor: pointer; transition: background var(--t), color var(--t); }
.jds-copy:hover { background: var(--bg3); }
.jds-copy.is-copied { background: color-mix(in srgb, var(--green) 20%, transparent); color: var(--green); }
.jds-copy svg { width: 14px; height: 14px; flex: 0 0 auto; }

/* ── Job drawer (tracker) content, inside the shared .jds sheet ── */
/* Full-width divider between the drawer's sections. */
.jobsheet-hr { height: 1px; background: var(--border); margin: var(--sp-3) 0 var(--sp-45); }
.jobsheet-summary { margin: 0 0 var(--sp-5); }
.jobsheet-sum-p { font-size: var(--fs-base); color: var(--text2); line-height: var(--lh-normal); margin: 0 0 var(--sp-3); }
.jobsheet-sum-p:last-child { margin-bottom: 0; }
.jobsheet-sum-list { margin: 0; padding: 0; list-style: none; display: flex; flex-direction: column; gap: var(--sp-25); }
.jobsheet-sum-list li {
  position: relative; padding-left: var(--sp-4);
  font-size: var(--fs-base); color: var(--text2); line-height: var(--lh-normal);
}
.jobsheet-sum-list li::before {
  content: ''; position: absolute; left: 4px; top: 9px;
  width: 5px; height: 5px; border-radius: 50%; background: var(--accent);
}
.jobsheet-nosum { font-size: var(--fs-sm); color: var(--text3); line-height: var(--lh-normal); margin: 0 0 var(--sp-5); }
.jobsheet-details { display: flex; flex-direction: column; }
.jobsheet-row { display: flex; align-items: baseline; justify-content: space-between; gap: var(--sp-3); padding: var(--sp-35) 0; border-top: 1px solid var(--border); }
.jobsheet-row-k { font-size: var(--fs-sm); color: var(--text3); flex: 0 0 auto; }
.jobsheet-row-v { font-size: var(--fs-base); font-weight: 600; color: var(--text); text-align: right; }
.jobsheet-link {
  margin-top: var(--sp-45); width: 100%;
  display: inline-flex; align-items: center; justify-content: center;
  padding: var(--sp-25) var(--sp-35);
  background: var(--bg2); border-radius: var(--r-pill);
  color: var(--text); font-family: var(--font-sans); font-size: var(--fs-sm); font-weight: 600;
  text-decoration: none; transition: background var(--t);
}
.jobsheet-link:hover { background: var(--bg3); }
.jobsheet-link.is-busy { opacity: 0.65; cursor: progress; }

@media (prefers-reduced-motion: reduce) {
  .jds-scrim { transition-duration: 120ms; }
  .jds { transition: none; transform: none; }   /* opacity-only entrance via the scrim */
}

/* ── Light mode: the interview surface follows the light UI ───────────
   No dark-token override — the screen uses the normal light tokens (cream
   surfaces, dark text) and the plain app background, matching every other
   screen. In dark mode the screen is transparent over the body backdrop
   (see body.dark .iv-screen). The dock/gear get light-mode tints below so
   their controls stay visible on the cream. */
body:not(.dark) .iv-dock-btn {
  background: var(--glass-1);
  border-color: var(--glass-3);
}
body:not(.dark) .iv-dock-btn:hover {
  background: var(--glass-2);
}

/* ════════════════════════════════════════════════════════════════
   Light-mode parity with dark. Dark uses transparent nav surfaces (so the
   orb bleeds through) and rgba(255,255,255,…) tints/borders/hovers. Light
   needs the same structure with rgba(0,0,0,…) so those states stay visible
   on a white surface. Mirrors the body.dark rules above.
   ════════════════════════════════════════════════════════════════ */
/* Transparent nav surfaces so the orb shows through in light too. */
body.light .header,
body.light .chat-input-row { background: transparent; }
body.light .tab-bar { background: transparent; border-bottom-color: rgba(0, 0, 0, 0.08); }

/* Input field tint. */
body.light .chat-input-wrap              { background: var(--glass-1); }
body.light .chat-input-wrap:focus-within { background: rgba(0, 0, 0, 0.06); }

/* Chips + list/card hovers — dark-tinted so they register on white. */
body.light .storage-upload-btn:hover            { background: rgba(0, 0, 0, 0.05); }
body.light .storage-empty-drop.clickable:hover  { background: rgba(0, 0, 0, 0.04); }
body.light .case-study-card:hover               { background: rgba(0, 0, 0, 0.03); }
body.light .job-card:hover                       { background: rgba(0, 0, 0, 0.03); }
body.light .s-row-link:hover                     { background: rgba(0, 0, 0, 0.04); }

/* Light mode uses the recoloured mascot (warm-grey body), so it just needs a
   soft shadow for depth rather than a synthetic outline. */


/* ════════════════════════════════════════════════════════════════
   TRACKER REDESIGN — unified pipeline (saved folded in), summary bar,
   import/export menu. Stage colours mirror the job-card status palette so
   the summary bar, section labels and card stripes read as one system.
   ════════════════════════════════════════════════════════════════ */

/* Two-tab bar (Pipeline · Discover) — same rhythm as the 3-tab variant. */
.tab-bar-2 { padding: 0 var(--sp-35); }
.tab-bar-2 .tab-btn { flex: 0 0 auto; padding: 12px 0 11px; margin-right: var(--sp-5); font-size: 12.5px; }
.tab-bar-2 .tab-btn:last-child { margin-right: 0; }

/* Empty state — two clear ways in. */
.jobs-empty-actions { margin-top: 34px; max-width: 320px; }

/* ════════════════════════════════════════════════════════════════
   TRACKER — toolbar (sort + import/export) and renovated Add-a-job form.
   ════════════════════════════════════════════════════════════════ */

/* ── Add / edit form ── */
.jobs-field { display: flex; flex-direction: column; gap: var(--sp-2); }
/* Sentence-case labels matching the settings profile form (was uppercase). */
.jobs-field-label {
  font-size: var(--fs-sm); font-weight: 600; letter-spacing: 0;
  text-transform: none; color: var(--text2); padding-left: 1px;
}
.jobs-field-label .req { color: var(--accent); }
.jobs-field-row { display: flex; gap: var(--sp-25); }
.jobs-field-row .jobs-field { flex: 1; min-width: 0; }

/* Stage picker — a .seg specialization: each option lights up in its own
   pipeline colour when active, tying the form to the tracker's colours. */
.seg-opt[class*="jspick-"].active { color: #fff; border-color: transparent; }
.jspick-saved.active        { background:var(--stg-saved); }
.jspick-applied.active      { background:var(--stg-applied); }
.jspick-interviewing.active { background:var(--stg-interviewing); }
.jspick-offer.active        { background:var(--stg-offer); }
/* Dark mode flattens the stage colours to a grey (#3E3E3E), so the active
   label needs LIGHT text (matching the card status pills), not dark. */
body.dark .seg-opt[class*="jspick-"].active { color:#F7F4F2; }

/* ── Add-job pickers in the new UI: borderless filled pills. Work arrangement
   takes the app-wide mint selection colour; the stage picker keeps its
   pipeline colours (re-scoped here so they still win over the mint base). ── */
.jobs-add-form .seg { gap: var(--sp-2); }
.jobs-add-form .seg-opt {
  border: none; background: var(--bg3); color: var(--text2);
  border-radius: var(--r-sm); font-weight: 600;
}
.jobs-add-form .seg-opt:hover { background: var(--bg4); border-color: transparent; color: var(--text); }
.jobs-add-form [aria-label="Work arrangement"] .seg-opt.active {
  background: var(--select); color: var(--select-on); border-color: transparent;
}
.jobs-add-form .seg-opt[class*="jspick-"].active { border-color: transparent; }
.jobs-add-form .jspick-saved.active        { background: var(--stg-saved); }
.jobs-add-form .jspick-applied.active      { background: var(--stg-applied); }
.jobs-add-form .jspick-interviewing.active { background: var(--stg-interviewing); }
.jobs-add-form .jspick-offer.active        { background: var(--stg-offer); }

/* ════════════════════════════════════════════════════════════════
   SETTINGS FOOTER — sign out sits directly below the groups: no divider
   above it and no stroke on the button, per the grouped-list redesign.
   ════════════════════════════════════════════════════════════════ */
.s-footer {
  margin-top: var(--sp-25);
  display: flex; flex-direction: column; gap: var(--sp-2);
}
/* Sign out — full-width filled surface matching the row groups (same
   fill, large radius), no border. */
.s-footer .s-signout-btn {
  display: flex; align-items: center; justify-content: center; gap: var(--sp-2);
  width: 100%; height: 48px; padding: 0 var(--sp-4); margin: 0;
  background: var(--bg2); border: none;
  border-radius: var(--r-xl);
  font-family: var(--font-sans); font-size: var(--fs-lg); font-weight: 600;
  color: var(--text); cursor: pointer;
  transition: color var(--t), background var(--t);
}
.s-footer .s-signout-btn svg { width: 16px; height: 16px; }
.s-footer .s-signout-btn:hover { background: var(--bg3); }

/* ════════════════════════════════════════════════════════════════
   INTERVIEW — Perplexity-style immersive layout + interview settings.
   The interview surface is always-dark; the settings screen matches it.
   Tokens inside .iv-screen / .ivs-screen are the dark set (redeclared in
   light mode below), so var(--text) etc. read correctly on the dark field.
   ════════════════════════════════════════════════════════════════ */

/* In-screen settings gear (replaces the global nav on this surface). */
.iv-gear {
  position: absolute; top: 16px; right: 16px; z-index: 6;
  display: flex; align-items: center; justify-content: center;
  padding: var(--sp-1); background: none; border: none;
  color: var(--text3); cursor: pointer; transition: color var(--t);
}
.iv-gear:hover { color: var(--text); background: none; }
.iv-gear svg { width: 22px; height: 22px; }

/* Central question area — serif headline with karaoke; the rolling
   transform (_scrollInterviewSubs) lives on #iv-subs-inner, so the question
   typography sits there for correct line-height maths. */
.iv-scroll {
  flex: 1 1 auto; min-height: 0;
  width: 100%;
  display: flex; align-items: flex-start; justify-content: flex-start;
  overflow-y: auto; overflow-x: hidden; position: relative;
  padding: 44px 22px 0; box-sizing: border-box;
}
#iv-subs-inner {
  position: relative; width: 100%; max-width: none;
  font-family: var(--font-serif, Georgia, serif);
  font-size: 25px; line-height: 1.34; color: var(--text); text-align: left;
}
.iv-question { padding: 0 var(--sp-1); }
.iv-state-listening .iv-question { font-size: 19px; opacity: 0.5; }
.iv-live { font-family: var(--font-sans); font-size: var(--fs-lg); line-height: var(--lh-normal); color: var(--text); }

/* Bottom dock: status + [X · orb · mic]. */
.iv-dock-area {
  flex-shrink: 0; display: flex; flex-direction: column;
  align-items: center; gap: var(--sp-4); padding-top: var(--sp-25);
}
.iv-status {
  font-family: var(--font-sans); font-size: var(--fs-base); font-weight: 500;
  color: var(--text2); min-height: 18px;
}
.iv-dock { display: flex; align-items: center; justify-content: center; gap: var(--sp-6); }
.iv-dock-btn {
  width: 52px; height: 52px; border-radius: 50%; flex-shrink: 0;
  display: flex; align-items: center; justify-content: center;
  background: var(--glass-1); border: 1px solid var(--glass-3);
  color: var(--text2); cursor: pointer; transition: all var(--t);
}
.iv-dock-btn:hover { background: var(--glass-3); color: var(--text); }
.iv-dock-btn svg { width: 22px; height: 22px; }
.iv-dock-x:hover { border-color: rgba(224,106,92,0.5); color: #e06a5c; background: rgba(224,106,92,0.12); }

/* ── Interview face (eyes + mouth character) ──────────────────────── */
.iv-face-slot { display: block; }
.iv-face-holder, .iv-face-svg { width: 100%; height: 100%; display: block; }
.iv-face-svg .eye {
  fill: var(--text); transform-box: fill-box; transform-origin: center; will-change: transform;
}
.iv-face-svg .mouth {
  fill: var(--text); stroke: var(--text); stroke-linecap: round; stroke-linejoin: round;
  transform-box: fill-box; transform-origin: center; will-change: transform;
}
/* low pivot → the listening nod foreshortens (head-pitch) instead of sliding */
.iv-face-svg .face { transform-box: fill-box; transform-origin: 50% 165%; }
.iv-face-svg .iv-paper { fill: var(--text); }
.iv-face-svg .pline { fill: var(--text4); transition: fill .22s ease; }
.iv-face-svg .pline.active { fill: var(--accent); }

/* Active: the face is the full-screen centrepiece; the question overlays its
   top third (hidden while thinking, since the document scan is the focus). */
.iv-face-stage { position: relative; flex: 1 1 auto; min-height: 0; width: 100%; }
.iv-face-stage .iv-face-slot { position: absolute; inset: 0; }
/* The orb docks at the bottom of the stage (just above the status + controls);
   the question fills a fixed box above it and can never reach the orb — a long
   one is clipped and rolls (karaoke) within its box. */
.iv-face-stage .iv-orb-slot {
  position: absolute; left: 50%; bottom: 6px; top: auto;
  transform: translateX(-50%); margin: 0;
}
.iv-face-stage .iv-scroll {
  position: absolute; top: 0; left: 0; right: 0; bottom: 150px; z-index: 2;
  display: flex; align-items: flex-start; justify-content: center;
  overflow-y: auto; overflow-x: hidden; scrollbar-width: none;
  padding: 24px 22px 0; box-sizing: border-box;
  pointer-events: none; transition: opacity 0.3s ease;
}
.iv-face-stage .iv-scroll::-webkit-scrollbar { width: 0; height: 0; }
.iv-state-thinking .iv-face-stage .iv-scroll { opacity: 0; }

/* Welcome / ended: face as a centred mascot in the middle row. */
.iv-center-area .iv-face-slot {
  position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);
  width: 150px; height: 200px;
}

/* Gradient Jobi mascot (cursor-tracking white eyes). Same asset in light and
   dark — the multi-hue body reads on both surfaces, so no per-theme recolour.
   Sized/animated like the previous welcome mascot; the .jeye groups are
   translated by bindMeshMascotEyes on mousemove. */
.chat-welcome-mascot .jobi-grad-wrap {
  width: 104px; height: 104px;
  animation: darkb-mascot-bob 4.2s ease-in-out infinite;
}
.jobi-grad-svg { width: 100%; height: 100%; display: block; overflow: visible; transform: translateZ(0); }
.jeye { transition: transform 0.16s ease-out; will-change: transform; }


/* ── Company logo badge (job cards) ────────────────────────────────
   Circular badge. A coloured monogram is the always-present base; a
   real logo (logo.dev) paints over it on a white chip once it loads,
   so transparent/dark marks stay visible regardless of theme. */
.company-logo {
  position: relative;
  width: 40px; height: 40px;
  flex-shrink: 0;
  border-radius: 11px;
  overflow: hidden;
}
.company-logo-mono {
  position: absolute; inset: 0;
  display: flex; align-items: center; justify-content: center;
  background: var(--mono-bg, var(--bg3));
  color: #fff;
  font-family: var(--font-sans);
  font-size: var(--fs-md); font-weight: 700; letter-spacing: var(--ls-tight);
}
.company-logo-img {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  object-fit: cover;
  background: #fff;
  box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.06);
  opacity: 0;
  transition: opacity 0.2s ease;
}
.company-logo-img.is-loaded { opacity: 1; }
/* Detected transparent marks (e.g. Apple) get breathing room instead of
   filling edge-to-edge; opaque tiles keep the default cover fill. */
.company-logo-img.is-mark { object-fit: contain; padding: 7px; }


/* ── Tracker stat tiles (tappable stage filters) ──────────────────
   Four tiles standing in for the old funnel bar. Each shows a stage
   count; tapping filters the list to that stage, tapping again clears.
   Icon chip + active wash reuse the per-stage palette (--stg-*). */
/* Filter pill dropdown */

.jobs-filter-pill {
  position: relative;
  display: inline-block;
  z-index: var(--z-dropdown);
}

.jobs-filter-btn {
  display: inline-flex; align-items: center; gap: var(--sp-15);
  padding: 9px 15px;
  background: var(--bg2);
  border: none;
  border-radius: var(--r-pill);
  font-family: var(--font-sans);
  font-size: var(--fs-sm);
  font-weight: 600;
  color: var(--text);
  cursor: pointer;
  white-space: nowrap;
  transition: background var(--t);
}

.jobs-filter-btn:hover {
  background: var(--bg3);
}

.jobs-filter-btn:active {
  transform: scale(0.97);
}

.jobs-filter-btn-label { flex: 1; }

.jobs-filter-btn-chevron {
  width: 14px;
  height: 14px;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: transform var(--t);
  opacity: 0.7;
}

.jobs-filter-pill.open .jobs-filter-btn-chevron {
  transform: rotate(180deg);
}

/* Dropdown menu — scales in from its trigger (top-right), not from nothing.
   Origin-aware + starts at scale(0.96) (never from 0) + ease-out under 300ms. */
.jobs-filter-menu {
  position: absolute;
  top: calc(100% + 8px);
  right: 0;
  z-index: var(--z-dropdown);
  background: var(--bg2);
  border: none;
  border-radius: var(--r-lg);
  min-width: 150px;
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.25);
  overflow: hidden;
  padding: var(--sp-1);
  transform-origin: top right;
  opacity: 0;
  transform: scale(0.96);
  visibility: hidden;
  pointer-events: none;
  transition: opacity var(--dur-pop) var(--ease-out),
              transform var(--dur-pop) var(--ease-out),
              visibility 0s linear var(--dur-pop);
}

.jobs-filter-pill.open .jobs-filter-menu {
  opacity: 1;
  transform: scale(1);
  visibility: visible;
  pointer-events: auto;
  transition: opacity var(--dur-pop) var(--ease-out),
              transform var(--dur-pop) var(--ease-out);
}

.jobs-filter-option {
  display: block;
  width: 100%;
  padding: var(--sp-25) var(--sp-3);
  background: none;
  border: none;
  border-radius: var(--r-sm);
  font-family: var(--font-sans);
  font-size: 12.5px;
  font-weight: 500;
  color: var(--text);
  text-align: left;
  cursor: pointer;
  transition: background var(--t), color var(--t);
}

.jobs-filter-option:hover {
  background: var(--bg3);
}

/* Selected option uses the app-wide mint selection colour. */
.jobs-filter-option.active {
  background: var(--select);
  color: var(--select-on);
  font-weight: 600;
}

/* Keep old classes for compatibility but hide them */

.jobs-filter-empty { text-align: center; color: var(--text3); font-size: 12.5px; padding: 30px 12px; }

/* Tracker list section label with filter pill on the right */
.jobs-list-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-3);
  padding: var(--sp-55) var(--sp-05) var(--sp-35);
}

.jobs-list-title {
  flex: 1;
  min-width: 0;
}

.jobs-list-filter {
  flex-shrink: 0;
  position: relative;
}

/* ── Unified section subtitle ─────────────────────────────────────
   One typographic style for every section header across the app
   (the "Recent applications" look): 14px bold, sentence-case,
   full-contrast text. Each class keeps its own margins/spacing from
   its original rule — this only standardises the type + colour.
   Placed after all the originals so it wins the cascade. */
.jobs-list-title,
.s-section-title {
  font-family: var(--font-sans);
  font-size: var(--fs-md);
  font-weight: 700;
  letter-spacing: var(--ls-tight);
  text-transform: none;
  color: var(--text);
}

/* Settings group labels stay subtle (small, muted) so the carded rows lead,
   overriding the shared bold label rule above. */
.s-section-title {
  font-family: var(--font-sans);
  font-size: var(--fs-sm); font-weight: 600; letter-spacing: 0.01em;
  text-transform: none; color: var(--text3);
  padding: 0 var(--sp-1) var(--sp-25);
}

/* ── Tracker search + controls ────────────────────────────────────
   Search field sits beside a square Add button. The field is a filled
   surface (no faint floating outline); focus swaps to the base bg with
   an accent ring so the default state reads solid, not unfinished. */
.jobs-controls { display: flex; align-items: center; margin-top: var(--sp-35); }
/* One unified bar: the search field and the add button share a single filled
   surface (chat-input style), so there's no loud standalone block. The add
   button is a compact circle nested at the right edge. */
.jobs-search {
  flex: 1; min-width: 0;
  display: flex; align-items: center; gap: var(--sp-25);
  padding: 0 var(--sp-15) 0 var(--sp-4); height: 48px;
  background: var(--bg2);
  border: none;
  border-radius: var(--r-xl);
  transition: background var(--t), box-shadow var(--t);
}
/* Focus mirrors the chat input: no ring, the surface just lifts a shade with a
   soft drop shadow (see .chat-input-wrap:focus-within). */
.jobs-search:focus-within {
  background: var(--bg3);
  box-shadow: var(--shadow-lift);
}
.jobs-search-ic { display: flex; flex-shrink: 0; color: var(--text3); }
.jobs-search-ic svg { width: 17px; height: 17px; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
.jobs-search-input {
  flex: 1; min-width: 0;
  border: none; background: none; outline: none;
  font-family: var(--font-sans); font-size: 13.5px; color: var(--text);
}
.jobs-search-input::placeholder { color: var(--text3); }
.jobs-search-clear {
  display: flex; flex-shrink: 0; padding: var(--sp-1);
  border: none; background: none; cursor: pointer;
  color: var(--text3); border-radius: 6px;
  transition: background var(--t), color var(--t);
}
.jobs-search-clear:hover { background: var(--bg4); color: var(--text); }
.jobs-search-clear svg { width: 14px; height: 14px; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
/* Add job — compact circular button nested inside the bar's right edge. A
   quiet neutral disc with just the plus in the brand accent, so it reads as
   part of the bar rather than a loud standalone block. */
.jobs-add-btn {
  width: 38px; height: 38px; flex-shrink: 0;
  display: flex; align-items: center; justify-content: center;
  border: none; border-radius: 50%;
  background: var(--bg4); color: var(--accent);
  cursor: pointer;
  transition: background var(--t), transform var(--dur-press) var(--ease-out);
}
.jobs-add-btn:hover  { background: var(--bg-active, var(--bg4)); }
.jobs-add-btn:active { transform: scale(0.92); }
.jobs-add-btn svg { width: 18px; height: 18px; }
/* On focus-within the bar warms to bg3, so lift the disc a shade to stay read. */
.jobs-search:focus-within .jobs-add-btn { background: var(--bg-active, var(--bg4)); }

/* Storage header row — plain count + primary action (replaces the old
   uppercase section label, which duplicated the active tab). */
.storage-head-row { display: flex; align-items: center; padding: var(--sp-2) var(--sp-1) var(--sp-3); }
.storage-head-count { font-size: var(--fs-sm); font-weight: 500; color: var(--text3); }

/* Result/action cards (research, JD analysis, CV, email, cover letter) use
   the same translucent "glass over gradient" fill as the interview option
   cards in dark mode, for a consistent card language across the chat. */
body.dark .email-card { background: rgba(255, 255, 255, 0.06); }
/* The artboard: fixed 390:804 aspect, scaled to COVER the panel (fills it
   edge to edge, clipping only the far hill/grass overflow) so there is no
   black letterbox — the close sits at the top and the CTA at the bottom, as
   in the mockup. It is a container, so the cqw units below (1% of its width)
   make every size + position track the mockup at any panel size. */
.pro-art { position:relative; flex-shrink:0; container-type:inline-size; width:100%; width:max(100%, calc(100svh * 390 / 804)); aspect-ratio:390 / 804; overflow:hidden; font-family:var(--font-sans); color:#fff; }
/* Content layer — fills the panel exactly, so left/right % are the real panel
   padding (not the overflowing artboard's). container-type makes cqw = 1% of
   the panel width. Same vertical %s as the hill: since .pro-art covers, its
   height equals the panel height, so the two stay aligned. */
.pro-stage { position:absolute; inset:0; z-index:1; container-type:inline-size; font-family:var(--font-sans); color:#fff; }

.pro-glow { position:absolute; left:-84.10%; top:55.60%; width:279.49%; height:50.87%; border-radius:50%; background:#4CD964; z-index:0; }

.grass-tuft { position:absolute; z-index:1; }
.grass-tuft svg { display:block; width:100%; height:auto; }

/* Close — ghost glass circle, matching the app's .icon-btn treatment
   (transparent-ish surface, subtle hover), with the standard I.close glyph. */
.pro-close { position:absolute; right:6%; top:2.5%; width:8.7cqw; height:8.7cqw; z-index:5; display:flex; align-items:center; justify-content:center; border:none; border-radius:50%; background:rgba(255,255,255,0.08); color:rgba(255,255,255,0.85); cursor:pointer; transition:background var(--t), color var(--t), transform var(--dur-press) var(--ease-out); }
.pro-close:hover { background:rgba(255,255,255,0.16); color:#fff; }
.pro-close:active { transform:scale(0.94); }
.pro-close:focus-visible { outline:none; box-shadow:0 0 0 3px rgba(255,255,255,0.25); }
.pro-close svg { width:5cqw; height:5cqw; }

/* Jobi+ logo hero. */
.pro-logo { position:absolute; left:14.87%; top:11%; width:70%; z-index:2; }
.pro-logo .jobiplus-logo { display:block; width:100%; height:auto; }

/* Sprout mascot peeking over the feature card (same art as the Settings
   subscription card): head+eyes behind the card, arms gripping in front. */
.pro-mascot { position:absolute; right:14%; top:38%; width:47%; margin-top:-13.6%; pointer-events:none; }
.pro-mascot svg { display:block; width:100%; height:auto; }
.pro-mascot-back { z-index:1; }
.pro-mascot-arms { z-index:3; }
.pro-headline { position:absolute; top:23%; left:11.28%; right:11.28%; width:auto; z-index:2; box-sizing:border-box; margin:0; padding:0; text-align:center; font-family:var(--font-sans); font-weight:800; font-size:6.15cqw; line-height:1.13; letter-spacing:0; color:#fff; }

/* Feature card — four sprout-bullet rows spread to fill the card. */
.pro-feature-card { position:absolute; left:11.28%; top:38%; width:77.44%; height:24%; z-index:2; box-sizing:border-box; background:#252525; border-radius:5.13cqw; padding:4.1cqw 5.13cqw; display:flex; flex-direction:column; box-shadow:0 6px 18px rgba(0,0,0,0.07); }
.pro-feature-card .pro-features { flex:1; width:100%; display:flex; flex-direction:column; justify-content:center; gap:6cqw; }
.pro-feat { display:flex; align-items:center; gap:2.5cqw; }
.pro-feat-sprout { flex:0 0 auto; width:4.4cqw; height:4.4cqw; display:block; }
.pro-feat-sprout svg { display:block; width:100%; height:100%; }
.pro-feat-label { font-size:3.59cqw; font-weight:500; line-height:1.71; letter-spacing:-0.013em; color:#fff; }

/* Plan picker — weekly | monthly. Selection state is the outline only. */
.pro-plan { position:absolute; top:65.17%; width:37.18%; height:9.45%; z-index:2; box-sizing:border-box; display:flex; flex-direction:column; justify-content:flex-start; align-items:flex-start; gap:1.03cqw; padding:4.1cqw; background:#1A1A1A; border:2px solid transparent; border-radius:4.62cqw; cursor:pointer; font-family:inherit; text-align:left; }
.pro-plan-a { left:11.28%; }
.pro-plan-b { left:51.54%; }
.pro-plan:hover { background:#242424; }
.pro-plan:focus-visible { outline:none; box-shadow:0 0 0 3px rgba(168,199,250,0.35); }
.pro-plan.is-selected { background:#252525; border-color:#A8C7FA; box-shadow:0 4px 12px rgba(0,0,0,0.06); }
.pro-plan.is-selected:hover { background:#2b2b2b; }
.pro-plan-amt { font-size:4.62cqw; font-weight:800; color:#fff; }
.pro-plan-label { font-size:3.08cqw; font-weight:400; color:#86868B; }

/* CTA — pink pill. */
.pro-cta { position:absolute; left:11.28%; top:87.44%; width:77.44%; height:6.72%; z-index:2; box-sizing:border-box; display:flex; align-items:center; justify-content:center; border:none; border-radius:999px; background:#EFB8C8; color:#000; font-family:var(--font-sans); font-size:3.59cqw; font-weight:600; line-height:1.2; letter-spacing:0.1px; cursor:pointer; }
.pro-cta:hover { background:#f2c4d1; }
.pro-cta:active { transform:scale(0.99); }
.pro-cta:focus-visible { outline:none; box-shadow:0 0 0 3px rgba(239,184,200,0.45); }

/* Fine print — white, per the mockup. */
.pro-fineprint { position:absolute; top:95.65%; left:11.28%; right:11.28%; width:auto; z-index:2; box-sizing:border-box; padding:0; text-align:center; font-family:var(--font-sans); font-weight:500; font-size:3.21cqw; color:#fff; }
.pro-dev-link { margin-top:var(--sp-35); background:none; border:1px dashed var(--border2); color:var(--text3); font-family:inherit; font-size:var(--fs-sm); padding:var(--sp-1) var(--sp-25); border-radius:var(--r-xs); cursor:pointer; }
.pro-dev-link:hover { color:var(--text2); border-color:var(--text3); }

/* ── Subscription section (Pro, in Settings) ─────────────────── */
/* Hero summary card, then the app's grouped setting rows below it
   (auto-renew, billing, support, cancel) — same surface as every other
   settings group, no nested cards. */
.s-sub { background:var(--bg2); border:none; border-radius:var(--r-xl); padding:var(--sp-45); margin-bottom:var(--sp-45); }
.s-sub-head { display:flex; align-items:center; justify-content:space-between; gap:var(--sp-2); }
/* Brand is the Jobi+ logo now (renderJobiPlusLogo), not a text wordmark. */
.s-sub .jobiplus-logo { height:24px; width:auto; display:block; }
/* On theme-following surfaces the lockup letters take the text colour and
   the o-eyes take the page colour: white eyes on dark letters in light
   mode, near-black eyes on light letters in dark. Always-dark hero cards
   (pro card, paywall feature card) keep the baked-in fills. */
.s-sub .jobiplus-logo .logo-ink { fill: var(--text); }
.s-sub .jobiplus-logo .logo-eye { fill: var(--bg); }
.s-sub-status { display:flex; align-items:center; gap:var(--sp-15); font-size:var(--fs-sm); font-weight:600; color:var(--text2); background:var(--accent-bg); padding:var(--sp-1) var(--sp-25); border-radius:var(--r-pill); white-space:nowrap; }
.s-sub-dot { width:6px; height:6px; border-radius:50%; background:var(--green, #2e9e5b); }
/* Cancelling: an amber dot + neutral pill, so "winding down" reads at a glance. */
.s-sub-status.is-ending { background:rgba(230,180,80,0.14); }
.s-sub-status.is-ending .s-sub-dot { background:#e6b450; }
/* Past due: a red dot + tint, so a failed payment can't be missed. */
.s-sub-status.is-past { background:var(--red-bg); color:var(--text); }
.s-sub-status.is-past .s-sub-dot { background:var(--red); }
.s-sub-plan { display:flex; align-items:baseline; justify-content:space-between; gap:var(--sp-2); margin-top:var(--sp-4); }
.s-sub-plan-name { font-size:var(--fs-lg); font-weight:700; color:var(--text); }
.s-sub-plan-price { font-size:var(--fs-lg); font-weight:700; color:var(--text); }
.s-sub-plan-per { font-size:var(--fs-base); font-weight:500; color:var(--text3); }
.s-sub-note { margin-top:var(--sp-15); font-size:var(--fs-sm); color:var(--text3); line-height:var(--lh-snug); }
.s-sub.is-past .s-sub-note { color:var(--red); }
/* Read-only key/value fact rows in the Details group (reuse the .s-row surface). */
.s-sub-kv { justify-content:space-between; }
.s-sub-kv-k { font-size:var(--fs-base); color:var(--text3); }
.s-sub-kv-v { font-size:var(--fs-base); font-weight:600; color:var(--text); text-align:right; }
/* Placeholder for the payment-method value while the Stripe card lookup runs. */
.s-sub-kv-skel { display:inline-block; width:88px; height:12px; vertical-align:middle; border-radius:var(--r-sm); }
/* Trailing "opens externally" arrow on the billing + support rows. */
.s-row-ext { display:inline-flex; align-items:center; color:var(--text3); flex-shrink:0; }
.s-row-ext svg { width:16px; height:16px; }
.s-sub-devwrap { display:flex; justify-content:center; margin-top:var(--sp-1); }

/* Tracker cards shown inline in chat (answering "have I applied to X?"). */
.chat-applied-cards { display: flex; flex-direction: column; gap: var(--sp-2); width: 100%; }
.chat-applied-cards .job-card { margin: 0; }

/* ── Composer tools menu (Claude-style, anchored above the +) ─────── */
.chat-input-row { position: relative; }
.chat-tools-backdrop { position: fixed; inset: 0; z-index: var(--z-dropdown); display: none; }
.chat-tools-backdrop.is-open { display: block; }
.chat-tools-menu {
  position: absolute; left: 22px; bottom: calc(100% + 6px); z-index: 50;
  min-width: 246px; max-width: calc(100% - 40px);
  background: var(--bg); border: none;
  border-radius: var(--r-lg); padding: 7px;
  box-shadow: 0 14px 36px rgba(0, 0, 0, 0.18), 0 2px 8px rgba(0, 0, 0, 0.07);
  display: none; flex-direction: column;
}
body.dark .chat-tools-menu { background: var(--bg3); box-shadow: 0 18px 44px rgba(0, 0, 0, 0.6); }
.chat-tools-menu.is-open { display: flex; animation: toolsMenuIn 0.14s ease; }
@keyframes toolsMenuIn { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: translateY(0); } }
.chat-tool {
  display: flex; align-items: center; gap: 11px;
  width: 100%; text-align: left;
  padding: 9px 10px; border: none; background: none;
  font-family: inherit; font-size: 13.5px; font-weight: 500; color: var(--text);
  border-radius: 9px; cursor: pointer; transition: background var(--t), color var(--t);
}
.chat-tool:hover { background: var(--tint); }
body.dark .chat-tool:hover { background: var(--bg4); }
.chat-tool-ic { flex: 0 0 auto; width: 19px; height: 19px; display: inline-flex; align-items: center; justify-content: center; color: var(--text3); transition: color var(--t); }
.chat-tool-ic svg { width: 18px; height: 18px; }
/* Pro marker on gated tool rows — pushed to the trailing edge. */
.chat-tool-chip { margin-left: auto; }
.chat-tool:hover .chat-tool-ic { color: var(--accent); }

/* ── Empty-state daily greeting (just under the mascot) ──────────── */
.chat-welcome-greeting {
  /* Display font at display weight — everywhere else Hanken runs 600-800;
     at 400 it read as a stray system font next to the Inter UI. */
  font-family: var(--font-serif);
  font-size: 23px; font-weight: 600; letter-spacing: var(--ls-tight);
  color: var(--text2); text-align: center; margin-top: var(--sp-35);
  /* Never run full-width: cap the width and balance the wrap so a long
     greeting breaks into two evenly-weighted, centred lines instead of one
     edge-to-edge line. Short greetings stay on one line. */
  max-width: 90%;
  text-wrap: balance;
}
.chat-welcome-greeting em { color: var(--text); font-style: normal; font-weight: 700; }
/* The greeting fades in as one piece, AFTER the mascot's intro (see
   fadeInGreeting). It stays hidden pre-paint via .tw-pending so it can't flash
   at full opacity before the fade begins. */
.chat-welcome-greeting.tw-pending { opacity: 0; }
.chat-welcome-greeting.tw-fade-in { animation: greeting-fade-in 520ms ease both; }
@keyframes greeting-fade-in {
  from { opacity: 0; transform: translateY(6px); }
  to   { opacity: 1; transform: none; }
}
@media (prefers-reduced-motion: reduce) {
  .chat-welcome-greeting.tw-pending { opacity: 1; }
  .chat-welcome-greeting.tw-fade-in { animation: none; }
}

