/* Relief — warm, soft, romantic base styles.
   Theme model: DARK is the default. Light is opt-in via Settings →
   Appearance ('light'), or via Auto ('auto') when the OS itself is in
   light mode. The pre-paint script in /lib/theme-init.js writes a
   data-theme attribute on <html> based on localStorage; absence of the
   attribute falls through to the dark default below. */

:root,
:root[data-theme="dark"] {
  --bg:        #1a1416;
  --bg-card:   #261c1f;
  --bg-banner: #2d2025;
  --ink:       #f5e6e9;
  --ink-soft:  #c5a8af;
  --rose:      #4a2a32;
  --rose-deep: #c97a8e;
  --plum:      #d99cb6;
  --gold:      #e7c79b;
  --danger:    #f08585;
  --shadow:    0 8px 32px rgba(0, 0, 0, 0.4);
  --radius:    20px;
  --safe-top:    env(safe-area-inset-top);
  --safe-bot:    env(safe-area-inset-bottom);
}

/* Light vars — explicit choice OR Auto fallback when OS isn't dark. */
:root[data-theme="light"],
:root[data-theme="auto"] {
  --bg:        #fff8f6;
  --bg-card:   #fff;
  --bg-banner: #fff8f6;
  --ink:       #3a2a2f;
  --ink-soft:  #7a5e66;
  --rose:      #f5d0d6;
  --rose-deep: #e89aa8;
  --plum:      #b86b8a;
  --gold:      #e7c79b;
  --danger:    #8a3030;
  --shadow:    0 8px 32px rgba(184, 107, 138, 0.12);
}

/* Auto + OS dark → revert to dark. Same specificity as the
   :root[data-theme="auto"] block above; comes after, so it wins
   when the media query matches. */
@media (prefers-color-scheme: dark) {
  :root[data-theme="auto"] {
    --bg:        #1a1416;
    --bg-card:   #261c1f;
    --bg-banner: #2d2025;
    --ink:       #f5e6e9;
    --ink-soft:  #c5a8af;
    --rose:      #4a2a32;
    --rose-deep: #c97a8e;
    --plum:      #d99cb6;
    --danger:    #f08585;
    --shadow:    0 8px 32px rgba(0, 0, 0, 0.4);
  }
}

* { box-sizing: border-box; }
[x-cloak] { display: none !important; }

html, body { height: 100%; margin: 0; }

body {
  background: var(--bg);
  color: var(--ink);
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui,
               'Helvetica Neue', Arial, sans-serif;
  font-size: 17px;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  overscroll-behavior-y: none;
}

.app-shell {
  min-height: 100vh;
  min-height: 100dvh;
  display: flex;
  flex-direction: column;
  padding: calc(var(--safe-top) + 16px) 20px calc(var(--safe-bot) + 16px);
  max-width: 560px;
  width: 100%;
  margin: 0 auto;
}

h1 {
  font-size: 28px;
  font-weight: 600;
  margin: 0 0 8px;
  letter-spacing: -0.02em;
}
h2 {
  font-size: 20px;
  font-weight: 600;
  margin: 24px 0 8px;
}
h2:first-child { margin-top: 0; }
p { margin: 0 0 12px; color: var(--ink-soft); }

.card {
  background: var(--bg-card);
  border-radius: var(--radius);
  padding: 24px;
  margin-bottom: 16px;
  box-shadow: var(--shadow);
}

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  border: none;
  border-radius: 16px;
  padding: 16px 24px;
  font-size: 17px;
  font-weight: 600;
  cursor: pointer;
  width: 100%;
  min-height: 56px;
  transition: transform 0.08s ease, opacity 0.15s ease;
  -webkit-tap-highlight-color: transparent;
  font-family: inherit;
}
.btn:active:not(:disabled) { transform: scale(0.98); }
.btn:disabled { opacity: 0.5; cursor: not-allowed; }

.btn-primary {
  background: linear-gradient(135deg, var(--rose-deep), var(--plum));
  color: white;
}
.btn-secondary {
  background: var(--rose);
  color: var(--ink);
}
.btn-ghost {
  background: transparent;
  color: var(--ink-soft);
}

.input {
  display: block;
  width: 100%;
  border: 2px solid var(--rose);
  background: var(--bg-card);
  color: var(--ink);
  border-radius: 14px;
  padding: 14px 16px;
  font-size: 17px;
  font-family: inherit;
  margin-bottom: 12px;
  -webkit-appearance: none;
}
.input:focus { outline: none; border-color: var(--plum); }

.code-input {
  font-family: ui-monospace, 'SF Mono', Menlo, monospace;
  font-size: 22px;
  letter-spacing: 0.08em;
  text-align: center;
  text-transform: lowercase;
}

.error {
  background: #f8e0e0;
  color: #7a2030;
  border-radius: 12px;
  padding: 12px 16px;
  margin-bottom: 12px;
  font-size: 15px;
}

.muted { color: var(--ink-soft); font-size: 14px; }
.center { text-align: center; }
.row { display: flex; gap: 12px; }
.row > * { flex: 1; }

.couple-code-display {
  font-family: ui-monospace, 'SF Mono', Menlo, monospace;
  font-size: 26px;
  letter-spacing: 0.1em;
  text-align: center;
  padding: 20px;
  background: var(--rose);
  border-radius: 14px;
  margin: 16px 0;
  color: var(--ink);
  user-select: all;
}

.spinner {
  display: inline-block;
  width: 18px; height: 18px;
  border: 2px solid currentColor;
  border-top-color: transparent;
  border-radius: 50%;
  animation: spin 0.8s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }

/* ─── Pull-to-refresh ─── */
/* Outer wrap is full-width fixed at the top, anchored 40px above the
   viewport so a translateY of 0 hides the indicator. The inner
   element receives Alpine-driven inline transform/opacity. */
.pull-indicator-wrap {
  position: fixed;
  top: -40px;
  left: 0;
  right: 0;
  display: flex;
  justify-content: center;
  pointer-events: none;
  z-index: 100;
}
.pull-indicator {
  width: 36px;
  height: 36px;
  background: var(--bg-card);
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.18);
  transition: opacity 0.18s ease;
  /* While the user is actively pulling we want the position to track
     the finger directly (no transform transition). The --settling
     modifier opts in to a smooth transform animation for the
     release-snap-back and post-refresh retraction. */
}
.pull-indicator--settling {
  transition: opacity 0.25s ease, transform 0.25s ease;
}
.pull-indicator-icon {
  display: inline-block;
  font-size: 20px;
  line-height: 1;
  color: var(--ink);
}
/* "Armed" — pull crossed the threshold but hasn't released yet.
   Tint the icon to telegraph that letting go will commit. */
.pull-indicator--armed .pull-indicator-icon {
  color: var(--rose-deep);
}
/* "Refreshing" — refreshAll() is in flight, spinner parked at 80px. */
.pull-indicator--refreshing .pull-indicator-icon {
  animation: spin 0.8s linear infinite;
  color: var(--rose-deep);
}
@media (prefers-reduced-motion: reduce) {
  .pull-indicator--refreshing .pull-indicator-icon { animation: none; }
}

/* ─── Install instructions ─── */
.install-step {
  display: flex;
  gap: 14px;
  margin: 18px 0;
  align-items: flex-start;
}
.install-step-num {
  flex: 0 0 32px;
  width: 32px;
  height: 32px;
  border-radius: 50%;
  background: var(--rose);
  color: var(--ink);
  font-weight: 700;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 15px;
  margin-top: 2px;
}
.install-step-body { flex: 1; min-width: 0; }
.install-step-title {
  font-size: 17px;
  font-weight: 600;
  color: var(--ink);
  margin-bottom: 4px;
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
}
.install-step .muted { font-size: 14px; margin: 4px 0 0; line-height: 1.45; }
.ios-icon {
  width: 26px;
  height: 26px;
  color: var(--plum);
  flex-shrink: 0;
}

/* ─── Mood ─── */
.mood-row {
  display: flex;
  align-items: center;
  padding: 10px 0;
  gap: 12px;
  border-bottom: 1px solid var(--rose);
}
.mood-row:last-of-type { border-bottom: none; }
.mood-row-label {
  flex: 0 0 70px;
  color: var(--ink-soft);
  font-size: 14px;
  font-weight: 600;
}
.mood-row-value { flex: 1; min-width: 0; font-size: 17px; }
.mood-row-action {
  flex: 0 0 auto;
  background: var(--rose);
  color: var(--ink);
  border: none;
  border-radius: 12px;
  padding: 8px 14px;
  font-size: 14px;
  font-weight: 600;
  cursor: pointer;
  font-family: inherit;
  -webkit-tap-highlight-color: transparent;
}
.mood-row-action:active:not(:disabled) { transform: scale(0.96); }
.mood-row-action:disabled { opacity: 0.5; }

.mood-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 8px;
}
.mood-chip {
  background: var(--bg);
  color: var(--ink);
  border: 2px solid var(--rose);
  border-radius: 14px;
  padding: 12px 10px;
  font-size: 15px;
  font-weight: 500;
  cursor: pointer;
  font-family: inherit;
  display: flex;
  align-items: center;
  gap: 6px;
  -webkit-tap-highlight-color: transparent;
  text-align: left;
}
.mood-chip:active:not(:disabled) { transform: scale(0.97); }
.mood-chip-current {
  background: var(--rose);
  border-color: var(--plum);
}
.mood-chip:disabled { opacity: 0.5; }

/* Proxy-mood: when one partner notes the other's mood for them, the
   resulting entry stays muted to make the source obvious in the UI.
   Italic + reduced opacity + a trailing pencil glyph make the "logged
   by the other side" signal unmistakable without an extra label. */
.mood-proxy {
  opacity: 0.5;
  font-style: italic;
}
.mood-proxy::after {
  content: ' ✎';
  font-size: 0.85em;
  opacity: 0.75;
  margin-left: 2px;
}
.mood-proxy strong { font-weight: 500; }

.proxy-banner {
  background: var(--bg-card);
  border: 1px dashed var(--plum);
  border-radius: var(--radius);
  padding: 12px 14px;
  color: var(--ink);
}

/* Silent-send opt-in line on send forms — small/muted because the
   notification path is the default and most users won't toggle it. */
.silent-send-row {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  font-size: 13px;
  color: var(--ink-soft);
  margin: 8px 0 12px;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.silent-send-row input[type="checkbox"] {
  margin-top: 3px;
  flex-shrink: 0;
}

/* ─── Landing / unauthenticated marketing page ─── */
/* The LP runs full-bleed on its own — when screen === 'landing', the
   parent .app-shell drops its 560px cap (.app-shell--landing) so each
   section can paint edge to edge. Content inside each band is then
   re-constrained by .lp-inner. The pattern is: full-width <section>
   carries the background tint, .lp-inner carries the readable column.
   This produces the layered stack you see on conventional marketing
   pages without breaking the otherwise-narrow app shell. */
.app-shell--landing {
  max-width: none;
  /* Drop horizontal/top gutters so each band runs edge-to-edge — the
     hero gradient is meant to bleed behind the iOS status bar /
     notch. Bottom safe-area is preserved to clear the home indicator
     so the footer doesn't get clipped on a notched iPhone. */
  padding: 0 0 var(--safe-bot);
}
.lp-shell {
  max-width: none;
  width: 100%;
  margin: 0;
  padding: 0;
  position: relative;
}

/* Landing-page theme toggle. Floats top-right of .lp-shell so it's
   always reachable but doesn't compete with hero CTAs. Sun-or-moon
   emoji depending on current theme — tap flips to the opposite. */
.lp-theme-toggle {
  position: fixed;
  top: calc(var(--safe-top) + 12px);
  right: 12px;
  width: 40px;
  height: 40px;
  border-radius: 50%;
  border: 1px solid var(--rose-deep);
  background: var(--bg-card);
  color: var(--ink);
  font-size: 20px;
  line-height: 1;
  cursor: pointer;
  z-index: 50;
  box-shadow: var(--shadow);
  -webkit-tap-highlight-color: transparent;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  transition: transform 0.15s ease, background 0.15s ease;
}
.lp-theme-toggle:hover {
  transform: scale(1.05);
}
.lp-theme-toggle:active {
  transform: scale(0.95);
}
/* The constrained reading column inside each full-bleed section. ~1080px
   keeps line lengths sane on desktop while letting the surrounding
   section paint behind it. Padding doubles as the section's vertical
   rhythm — bumped down on narrow screens where bands feel cramped. */
.lp-inner {
  max-width: 1080px;
  margin: 0 auto;
  padding: 72px 32px;
  width: 100%;
  box-sizing: border-box;
}
@media (max-width: 600px) {
  .lp-inner { padding: 48px 20px; }
}
.lp-hero {
  background: linear-gradient(180deg, var(--rose) 0%, var(--bg) 100%);
}
.lp-hero .lp-inner {
  text-align: center;
  padding: 96px 32px 80px;
}
@media (max-width: 600px) {
  .lp-hero .lp-inner { padding: 64px 20px 56px; }
}
.lp-hero h1 {
  font-size: 52px;
  line-height: 1.05;
  letter-spacing: -0.02em;
  margin: 0 auto 18px;
  max-width: 18ch;
}
.lp-hero .lead {
  font-size: 19px;
  color: var(--ink-soft);
  max-width: 620px;
  margin: 0 auto 28px;
  line-height: 1.5;
}
.lp-cta-row {
  display: flex;
  flex-direction: column;
  gap: 10px;
  align-items: stretch;
  max-width: 360px;
  margin: 0 auto;
}
.lp-cta-soft {
  color: var(--ink-soft);
  font-size: 13px;
  margin-top: 14px;
  text-align: center;
}
/* Default section: white card surface so the eye reads it as the
   "content" stratum. Alternate sections (.lp-section--alt) sink to
   the page bg for soft tonal alternation — the layer-cake effect. */
.lp-section {
  margin: 0;
  background: var(--bg-card);
}
.lp-section--alt {
  background: var(--bg);
}
.lp-section h2 {
  font-size: 32px;
  margin: 0 0 18px;
  letter-spacing: -0.01em;
  line-height: 1.15;
}
.lp-section p, .lp-section li {
  color: var(--ink);
  font-size: 17px;
  line-height: 1.6;
}
.lp-section .lead {
  font-size: 19px;
  color: var(--ink-soft);
  margin: 0 0 24px;
  max-width: 720px;
  line-height: 1.5;
}
.lp-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 16px;
  margin: 28px 0;
}
@media (min-width: 720px) {
  .lp-grid { grid-template-columns: repeat(3, 1fr); }
}
@media (min-width: 1000px) {
  .lp-grid { grid-template-columns: repeat(4, 1fr); }
}
@media (max-width: 600px) {
  .lp-grid { grid-template-columns: 1fr; }
  .lp-hero h1 { font-size: 34px; }
  .lp-hero .lead { font-size: 16px; }
  .lp-section h2 { font-size: 26px; }
  .lp-section .lead { font-size: 17px; }
}
/* Feature card. Background flips per parent section: on white sections
   we drop to --bg (cream) so the cards are visible against the section;
   on alt sections (--bg) we sit on --bg-card. The 1px rose border is
   the safety net that keeps cards readable in either direction even if
   the shadow gets washed out by a future theme tweak. */
.lp-feature {
  background: var(--bg);
  border: 1px solid var(--rose);
  border-radius: var(--radius);
  padding: 20px;
  box-shadow: var(--shadow);
}
.lp-section--alt .lp-feature {
  background: var(--bg-card);
}
.lp-feature h3 {
  font-size: 17px;
  font-weight: 600;
  margin: 0 0 6px;
}
.lp-feature p {
  color: var(--ink-soft);
  font-size: 15px;
  line-height: 1.5;
  margin: 0;
}
.lp-feature .emoji {
  font-size: 24px;
  display: block;
  margin-bottom: 6px;
}
/* Quiz section on the landing page — pack tiles in a compact grid
   that visually previews what's inside the feature without taking
   over the whole landing layout. */
.lp-quiz-packs {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 12px;
  margin: 28px 0;
}
@media (min-width: 720px) {
  .lp-quiz-packs { grid-template-columns: repeat(6, 1fr); }
}
@media (max-width: 540px) {
  .lp-quiz-packs { grid-template-columns: repeat(2, 1fr); }
}
.lp-quiz-pack {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  padding: 14px 10px;
  background: var(--bg-card);
  border: 1px solid var(--rose);
  border-radius: var(--radius);
  text-align: center;
}
.lp-quiz-pack .emoji {
  font-size: 26px;
  line-height: 1;
}
.lp-quiz-pack .name {
  font-weight: 600;
  font-size: 14px;
  color: var(--ink);
  margin-top: 4px;
}
.lp-quiz-pack .hint {
  font-size: 11px;
  color: var(--ink-soft);
  text-transform: uppercase;
  letter-spacing: 0.04em;
}
/* Locked landing tile — redacted name + dim treatment so the visual
   tells the story: you start known, the rest unfold as you go. */
.lp-quiz-pack--locked {
  opacity: 0.65;
  border-style: dashed;
}
.lp-quiz-pack--locked .name {
  letter-spacing: 0.15em;
  color: var(--ink-soft);
}
/* Screenshot showcase. CSS-built iPhone frames so the marketing
   shots feel like real product UI, not floating PNGs. Frame is a
   dark rounded rect with a thin highlight ring (the "edge"); the
   image fills the screen area at the iPhone 15 aspect (1179×2556).
   Caption sits below in a separate <figcaption> so it doesn't
   compete with what's on screen.
   Grid: 1 column on phones → 2 on tablets → 4 across at desktop.
   The hard cap of 240px per phone keeps the row from getting
   absurdly tall on ultra-wide viewports. */
.lp-showcase .lead {
  margin-bottom: 32px;
  text-align: center;
  margin-left: auto;
  margin-right: auto;
}
.lp-showcase h2 { text-align: center; }
.lp-phone-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: 32px;
  margin: 24px 0 0;
  justify-items: center;
}
@media (min-width: 600px) {
  .lp-phone-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 40px 24px; }
}
@media (min-width: 960px) {
  .lp-phone-grid { grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 48px 20px; }
}
.lp-phone-card {
  margin: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 14px;
  width: 100%;
  max-width: 240px;
}
.lp-phone {
  position: relative;
  width: 100%;
  aspect-ratio: 1179 / 2556;
  background: #0a0a0c;
  border-radius: 36px;
  padding: 7px;
  box-shadow:
    0 24px 48px -12px rgba(58, 42, 47, 0.35),
    0 6px 18px -6px rgba(58, 42, 47, 0.25),
    inset 0 0 0 1.5px rgba(255, 255, 255, 0.06);
}
.lp-phone img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  border-radius: 30px;
  background: #000;
}
.lp-phone-card figcaption {
  font-size: 14px;
  line-height: 1.5;
  color: var(--ink-soft);
  text-align: center;
  max-width: 28ch;
}
@media (max-width: 600px) {
  .lp-phone-card { max-width: 280px; }
  .lp-phone-card figcaption { font-size: 15px; }
}

/* Encryption callout — full-bleed band with a diagonal rose-to-card
   gradient. Inner column gets a wider max-width than the .lp-grid
   sections (text-heavy, no card grid) so it feels like a focused
   "manifesto" stratum in the cake. */
.lp-callout {
  background: linear-gradient(135deg, var(--rose) 0%, var(--bg-card) 70%);
}
.lp-callout .lp-inner { max-width: 880px; }
.lp-callout h2 { margin: 0 0 14px; font-size: 28px; }
.lp-callout p { color: var(--ink); margin: 0 0 12px; font-size: 17px; line-height: 1.6; }
.lp-callout .muted-note { color: var(--ink-soft); font-size: 14px; margin-top: 16px; }
.lp-principles ul {
  list-style: none;
  padding: 0;
  margin: 18px 0;
  display: grid;
  grid-template-columns: 1fr;
  gap: 0;
}
@media (min-width: 800px) {
  .lp-principles ul { grid-template-columns: 1fr 1fr; gap: 0 32px; }
}
.lp-principles li {
  padding: 16px 0;
  border-bottom: 1px solid var(--rose);
  display: flex;
  gap: 14px;
  align-items: flex-start;
}
.lp-principles li:last-of-type { border-bottom: none; }
@media (min-width: 800px) {
  .lp-principles li:nth-last-of-type(2) { border-bottom: none; }
}
.lp-principles .num {
  flex: 0 0 32px;
  width: 32px;
  height: 32px;
  border-radius: 50%;
  background: var(--rose);
  color: var(--ink);
  display: flex;
  align-items: center;
  justify-content: center;
  font-weight: 700;
  font-size: 15px;
}
.lp-principles .body p {
  margin: 0;
  font-size: 16px;
  line-height: 1.55;
  color: var(--ink-soft);
}
.lp-principles .body strong {
  display: block;
  margin-bottom: 4px;
  font-size: 17px;
  color: var(--ink);
}
/* FAQ inner column reads better narrower than the default 1080px —
   long Q/A copy at full layer-cake width feels stretched. Cards flip
   to --bg-card on alt sections (same rule as .lp-feature) so they
   stay visually distinct against either background. */
.lp-faq .lp-inner { max-width: 880px; }
/* Header row: H2 on the left, "Expand all / Collapse all" pill on
   the right. Wraps under on narrow viewports so the button doesn't
   crash into the heading. */
.lp-faq-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  margin: 0 0 18px;
  flex-wrap: wrap;
}
.lp-faq-header h2 { margin: 0; }
.lp-faq-toggle-all {
  background: transparent;
  border: 1px solid var(--rose-deep);
  color: var(--plum);
  padding: 7px 16px;
  border-radius: 999px;
  font-size: 13px;
  font-weight: 600;
  cursor: pointer;
  letter-spacing: 0.01em;
  transition: background 0.15s, color 0.15s;
  -webkit-tap-highlight-color: transparent;
}
.lp-faq-toggle-all:hover  { background: var(--rose); }
.lp-faq-toggle-all:active { transform: scale(0.97); }
.lp-faq details {
  background: var(--bg);
  border: 1px solid var(--rose);
  border-radius: 14px;
  padding: 18px 22px;
  margin-bottom: 10px;
  box-shadow: var(--shadow);
}
.lp-section--alt.lp-faq details { background: var(--bg-card); }
.lp-faq summary {
  font-weight: 600;
  cursor: pointer;
  list-style: none;
  font-size: 17px;
  color: var(--ink);
}
.lp-faq summary::after {
  /* Non-breaking space binds the arrow to the trailing word so a
     wide question doesn't drop the arrow to its own line. */
  content: '\00a0▸';
  color: var(--ink-soft);
}
.lp-faq details[open] summary::after { content: '\00a0▾'; }
.lp-faq details p {
  margin: 12px 0 0;
  color: var(--ink);
  font-size: 15px;
  line-height: 1.6;
}
.lp-footer {
  background: var(--bg);
  border-top: 1px solid var(--rose);
  text-align: center;
  color: var(--ink-soft);
  font-size: 13px;
}
.lp-footer .lp-inner {
  padding: 32px 24px;
  text-align: center;
}
.lp-footer a {
  color: var(--plum);
  text-decoration: none;
  margin: 0 8px;
}

/* ─── Modal overlay ─── */
.modal-overlay {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.55);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 20px;
  z-index: 50;
  -webkit-tap-highlight-color: transparent;
}
.modal-card {
  background: var(--bg-card);
  border-radius: var(--radius);
  padding: 20px;
  max-width: 420px;
  width: 100%;
  max-height: calc(100dvh - 40px);
  overflow-y: auto;
  box-shadow: var(--shadow);
  color: var(--ink);
}
.modal-card p { color: var(--ink); }
.modal-card .muted { color: var(--ink-soft); }

/* ─── Dry spell ─── */
/* Three escalation tiers. Visuals warm up, never alarmed — this is care
   infrastructure, not a fire alarm. Header bar makes the section's purpose
   obvious so it doesn't read as a wall of text. */
.dry-spell-card {
  position: relative;
}
.dry-spell-l1 { /* soft */
  border-left: 3px solid var(--rose);
}
.dry-spell-l2 { /* check-in */
  border-left: 3px dashed var(--rose-deep);
  background: linear-gradient(180deg, var(--bg-card), var(--bg) 200%);
}
.dry-spell-l3 { /* honest moment */
  border-left: 4px solid var(--plum);
  background: linear-gradient(180deg, var(--bg-card), color-mix(in srgb, var(--rose) 30%, var(--bg-card)) 200%);
}
.dry-spell-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 8px;
  font-size: 13px;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--ink-soft);
}
.dry-spell-l3 .dry-spell-header {
  color: var(--plum);
}
.dry-spell-days {
  font-size: 13px;
  font-weight: 600;
  color: var(--plum);
  letter-spacing: normal;
  text-transform: none;
}
.dry-spell-card p {
  margin: 0 0 8px;
  color: var(--ink);
  font-size: 15px;
  line-height: 1.5;
}
.dry-spell-card p + p {
  margin-top: 8px;
}
.dry-spell-card .muted-note {
  color: var(--ink-soft);
  font-size: 14px;
}
.dry-spell-actions {
  display: flex;
  gap: 8px;
  margin-top: 12px;
  flex-wrap: wrap;
}
.dry-spell-actions .btn {
  flex: 1 1 auto;
  min-height: 44px;
  padding: 10px 14px;
  font-size: 14px;
}
.dry-spell-preview-banner {
  background: var(--gold);
  color: var(--ink);
  text-align: center;
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  padding: 6px 12px;
  border-radius: 8px;
  margin-bottom: 12px;
}

/* ─── Stats ─── */
.stats-h {
  font-size: 16px;
  font-weight: 600;
  margin: 0 0 8px;
  color: var(--ink);
}
.stats-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 12px;
}
.stats-grid-3 { grid-template-columns: repeat(3, 1fr); }
.stats-grid-2 { grid-template-columns: repeat(2, 1fr); }
@media (max-width: 460px) {
  .stats-grid    { grid-template-columns: repeat(2, 1fr); }
  .stats-grid-3  { grid-template-columns: repeat(3, 1fr); }
}
.stats-stat {
  background: var(--bg);
  border-radius: 12px;
  padding: 10px;
  text-align: center;
}
.stats-stat-num {
  font-size: 22px;
  font-weight: 600;
  color: var(--plum);
  line-height: 1.1;
}
.stats-stat-label {
  font-size: 11px;
  color: var(--ink-soft);
  margin-top: 2px;
}
.stats-legend {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  margin-top: 8px;
  font-size: 12px;
  color: var(--ink-soft);
}
.stats-legend-row {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 13px;
  padding: 3px 0;
}
.stats-legend-dot {
  display: inline-block;
  width: 10px;
  height: 10px;
  border-radius: 50%;
  margin-right: 4px;
}

/* Engagement timeline — stacked bar chart, plain HTML/CSS so it
   renders cleanly at any width and Alpine x-for can build the bars
   from data (which doesn't work inside <svg>). */
.ec-chart {
  display: flex;
  align-items: stretch;
  height: 140px;
  margin-top: 4px;
}
.ec-y-axis {
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  font-size: 11px;
  color: var(--ink-soft);
  padding-right: 6px;
  min-width: 22px;
  text-align: right;
}
.ec-plot {
  flex: 1;
  display: flex;
  align-items: stretch;
  gap: 1px;
  border-bottom: 1px solid var(--rose);
  border-left: 1px solid var(--rose);
  padding: 0 1px;
  min-width: 0;
}
.ec-bar {
  flex: 1;
  display: flex;
  flex-direction: column-reverse;
  min-width: 0;
}
.ec-seg {
  width: 100%;
  border-radius: 2px 2px 0 0;
}
.ec-xaxis {
  display: flex;
  margin-left: 28px;       /* y-axis width + padding */
  margin-top: 4px;
  font-size: 10px;
  color: var(--ink-soft);
}
.ec-xlabel {
  flex: 1;
  text-align: center;
  white-space: nowrap;
  min-width: 0;
}
.ec-xlabel-hidden { visibility: hidden; }
.stats-bar-pair {
  display: flex;
  height: 12px;
  border-radius: 6px;
  overflow: hidden;
  background: var(--bg);
}
.stats-bar { transition: flex 0.2s ease; }
.stats-row {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 4px 0;
  font-size: 14px;
}
.stats-row-label {
  flex: 0 0 110px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.stats-row-bar {
  height: 8px;
  border-radius: 4px;
  min-width: 4px;
}
.stats-row-count {
  flex: 0 0 auto;
  color: var(--ink-soft);
  font-size: 13px;
  min-width: 24px;
  text-align: right;
}
.stats-dow {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  gap: 4px;
  margin-top: 8px;
}
.stats-dow-cell { text-align: center; }
.stats-dow-bar {
  background: var(--plum);
  border-radius: 8px;
  padding: 12px 4px;
  color: white;
  font-size: 13px;
  font-weight: 600;
  margin-bottom: 4px;
}
.stats-dow-label {
  font-size: 11px;
  color: var(--ink-soft);
}

/* Avatar — square thumbnail with rounded corners; degrades to a 👤
   placeholder when no avatar is set. Sized to match the mood-card row
   so it slots in cleanly next to display_name. */
.avatar-row {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  margin-bottom: 12px;
}
.avatar-thumb {
  width: 72px;
  height: 72px;
  border-radius: 12px;
  background: var(--bg);
  overflow: hidden;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
}
.avatar-thumb img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.avatar-placeholder {
  font-size: 32px;
  opacity: 0.4;
}
.avatar-mini {
  width: 28px;
  height: 28px;
  border-radius: 50%;
  background: var(--bg);
  overflow: hidden;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  vertical-align: middle;
  margin-right: 6px;
  flex-shrink: 0;
}
.avatar-mini img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.avatar-mini-placeholder {
  font-size: 14px;
  opacity: 0.4;
}

.mood-manage-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 8px 0;
  font-size: 15px;
  border-bottom: 1px solid var(--rose);
}
.mood-manage-row:last-child { border-bottom: none; }
.btn-tiny {
  background: transparent;
  color: var(--plum);
  border: none;
  font-size: 13px;
  font-family: inherit;
  cursor: pointer;
  padding: 4px 8px;
}
.btn-tiny:active:not(:disabled) { opacity: 0.6; }
.btn-tiny:disabled { opacity: 0.5; }

/* ─── Cards: pending request + celebration ───
   Border is the same in every theme; backgrounds default to dark
   gradients (matches the root theme model). Light overrides apply
   on explicit data-theme="light" or Auto when OS is light. */
.card-pending,
.card-celebrate {
  border: 2px solid var(--rose-deep);
}
.card-pending {
  background: linear-gradient(135deg, var(--bg-card) 0%, var(--rose) 100%);
}
.card-celebrate {
  background: linear-gradient(135deg, var(--rose) 0%, #5a432e 100%);
}
:root[data-theme="light"] .card-pending,
:root[data-theme="auto"] .card-pending {
  background: linear-gradient(135deg, #fff 0%, var(--rose) 100%);
}
:root[data-theme="light"] .card-celebrate,
:root[data-theme="auto"] .card-celebrate {
  background: linear-gradient(135deg, var(--rose) 0%, var(--gold) 100%);
}
@media (prefers-color-scheme: dark) {
  :root[data-theme="auto"] .card-pending {
    background: linear-gradient(135deg, var(--bg-card) 0%, var(--rose) 100%);
  }
  :root[data-theme="auto"] .card-celebrate {
    background: linear-gradient(135deg, var(--rose) 0%, #5a432e 100%);
  }
}

/* ─── Mood history calendar ─── */
.history-nav {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 16px;
}
.history-nav-btn {
  background: var(--rose);
  color: var(--ink);
  border: none;
  border-radius: 12px;
  padding: 8px 16px;
  font-size: 22px;
  font-family: inherit;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  min-width: 44px;
}
.history-nav-btn:disabled { opacity: 0.5; }
.history-nav-label {
  font-size: 18px;
  font-weight: 600;
  color: var(--ink);
}

.history-grid-head {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  font-size: 11px;
  color: var(--ink-soft);
  text-align: center;
  margin-bottom: 4px;
  text-transform: uppercase;
  letter-spacing: 0.05em;
}
.history-grid {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  gap: 4px;
}
.history-cell {
  /* No fixed aspect-ratio — emojis stack vertically (mine over partner's),
     so cells are slightly taller than wide on narrow phones, which keeps
     7 columns fitting inside the viewport without horizontal overflow. */
  min-height: 56px;
  background: var(--bg);
  border-radius: 8px;
  padding: 4px 2px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: flex-start;
  font-size: 11px;
}
.history-cell-blank {
  background: transparent;
}
.history-cell:not(.history-cell-blank) {
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.history-cell-active {
  outline: 2px solid var(--plum);
  outline-offset: -2px;
}

.history-detail {
  margin-top: 16px;
  padding-top: 16px;
  border-top: 1px solid var(--rose);
}
.history-detail-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  padding: 6px 0;
  font-size: 15px;
}
.history-cell-day {
  color: var(--ink-soft);
  font-size: 11px;
  font-weight: 600;
  margin-bottom: 2px;
}
.history-cell-emojis {
  /* Stacked vertically so both partners' emojis fit on narrow cells:
     mine on top, partner's underneath — implicit "rows" matching the
     mood card layout where 'You' is above your partner. */
  display: flex;
  flex-direction: column;
  gap: 1px;
  align-items: center;
  font-size: 14px;
  line-height: 1;
}
.history-emoji {
  display: inline-block;
}
.history-emoji-partner {
  /* No special treatment yet — just sits next to mine.
     Future: subtle border or filter to differentiate at a glance. */
}
.history-emoji-proxy {
  opacity: 0.4;
  filter: grayscale(0.4);
}
.history-cell-acts {
  display: flex;
  flex-wrap: wrap;
  gap: 1px;
  font-size: 13px;
  line-height: 1.1;
}
.history-emoji-overflow {
  font-size: 10px;
  color: var(--ink-soft);
  font-weight: 600;
}

.history-legend {
  display: flex;
  gap: 16px;
  justify-content: center;
  font-size: 12px;
  color: var(--ink-soft);
  margin-top: 16px;
}
/* Stacked variant: order top→bottom mirrors the cell's emoji stacking
   (yours on top, partner's underneath), so the rows are self-explanatory. */
.history-legend-stacked {
  flex-direction: column;
  gap: 2px;
  align-items: center;
  text-align: center;
}

/* ─── Image picker + display ─── */
.image-picker {
  display: flex;
  align-items: center;
  justify-content: center;
  border: 2px dashed var(--rose);
  border-radius: 14px;
  padding: 16px;
  font-size: 15px;
  color: var(--ink-soft);
  cursor: pointer;
  background: var(--bg);
  margin-bottom: 12px;
  -webkit-tap-highlight-color: transparent;
}
.image-picker:active { background: var(--rose); color: var(--ink); }

.initiation-image-wrap {
  margin-bottom: 12px;
}
.initiation-image {
  width: 100%;
  max-height: 480px;
  object-fit: cover;
  border-radius: 14px;
  display: block;
}
.initiation-image-loading {
  display: flex;
  align-items: center;
  justify-content: center;
  height: 120px;
  background: var(--rose);
  color: var(--ink-soft);
  border-radius: 14px;
}

/* ─── Explicit veil (tap-to-reveal placeholder) ─── */
.explicit-veil {
  width: 100%;
  min-height: 160px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 8px;
  background: repeating-linear-gradient(
    45deg,
    var(--rose) 0,
    var(--rose) 12px,
    var(--bg-card) 12px,
    var(--bg-card) 24px
  );
  color: var(--ink);
  border: 2px dashed var(--plum);
  border-radius: 14px;
  font-size: 14px;
  font-weight: 600;
  cursor: pointer;
  font-family: inherit;
  -webkit-tap-highlight-color: transparent;
}
.explicit-veil:active { transform: scale(0.98); transition: transform 0.08s; }
.explicit-veil-emoji { font-size: 28px; }

/* ─── Owner action row (edit text, replace/add/delete photo, delete) ─── */
.action-row {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin-top: 12px;
  padding-top: 12px;
  border-top: 1px solid var(--rose);
}
.action-btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  background: var(--bg-card);
  color: var(--ink-soft);
  border: 1px solid var(--rose);
  border-radius: 999px;
  padding: 6px 12px;
  font-size: 13px;
  font-family: inherit;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.action-btn:active:not(:disabled) { transform: scale(0.96); transition: transform 0.08s; }
.action-btn:disabled { opacity: 0.5; cursor: not-allowed; }
.action-btn-danger {
  color: var(--danger);
  border-color: var(--danger);
}

/* ─── Sent panels (collapsible "Requests Sent" / "Teases Sent") ─── */
.sent-panel {
  background: var(--bg-card);
  border-radius: var(--radius);
  margin-bottom: 16px;
  box-shadow: var(--shadow);
  overflow: hidden;
}
.sent-panel-header {
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: space-between;
  background: transparent;
  color: var(--ink);
  border: none;
  padding: 16px 20px;
  font-size: 16px;
  font-weight: 600;
  font-family: inherit;
  cursor: pointer;
  text-align: left;
  -webkit-tap-highlight-color: transparent;
}
.sent-panel-header:active { background: var(--rose); }
.sent-panel-count {
  color: var(--ink-soft);
  font-weight: 500;
  margin-left: 4px;
}
.sent-panel-chevron {
  color: var(--ink-soft);
  font-size: 14px;
  flex-shrink: 0;
  margin-left: 8px;
}
.sent-panel-body {
  padding: 0 12px 12px;
}
/* Inner cards inside a sent panel get tighter spacing so the nesting reads
   as "items in a section" rather than "card inside a card". */
.sent-panel-body .card {
  margin-bottom: 8px;
  box-shadow: none;
  border: 1px solid var(--rose);
}
.sent-panel-body .card:last-child {
  margin-bottom: 0;
}


/* ─── Gallery grid ─── */
.gallery-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 4px;
}
.gallery-item {
  aspect-ratio: 1 / 1;
  background: var(--rose);
  border-radius: 6px;
  overflow: hidden;
  cursor: pointer;
  position: relative;
  -webkit-tap-highlight-color: transparent;
}
.gallery-item:active { transform: scale(0.97); transition: transform 0.08s; }
.gallery-img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
.gallery-placeholder {
  width: 100%; height: 100%;
  display: flex; align-items: center; justify-content: center;
  color: var(--ink-soft);
}

/* ─── Viewer overlay ─── */
.viewer {
  position: fixed;
  inset: 0;
  background: rgba(20, 10, 12, 0.96);
  z-index: 100;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  padding: calc(var(--safe-top) + 16px) 0 calc(var(--safe-bot) + 16px);
}
.viewer-close {
  position: fixed;
  top: calc(var(--safe-top) + 16px);
  right: 16px;
  width: 44px; height: 44px;
  background: rgba(255, 255, 255, 0.12);
  color: white;
  border: none;
  border-radius: 50%;
  font-size: 22px;
  cursor: pointer;
  z-index: 2;
  -webkit-tap-highlight-color: transparent;
}
.viewer-image-wrap {
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 16px;
  min-height: 60vh;
  max-height: 80vh;
}
.viewer-img {
  max-width: 100%;
  max-height: 80vh;
  object-fit: contain;
}
.viewer-meta {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 8px 20px;
  color: white;
  font-size: 14px;
}
.viewer-meta .muted { color: rgba(255, 255, 255, 0.6); }
.viewer-meta .btn-tiny { color: rgba(255, 255, 255, 0.85); }
.viewer-nav {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  width: 44px; height: 80px;
  background: rgba(255, 255, 255, 0.08);
  color: white;
  border: none;
  font-size: 32px;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.viewer-nav:disabled { opacity: 0.2; }
.viewer-nav-prev { left: 0; border-radius: 0 8px 8px 0; }
.viewer-nav-next { right: 0; border-radius: 8px 0 0 8px; }

/* ─── Gallery tabs ─── */
.gallery-tabs {
  display: flex;
  gap: 6px;
  margin-bottom: 12px;
  flex-wrap: wrap;
}
.gallery-tab {
  flex: 1 1 auto;
  min-width: 0;
  background: var(--bg-card);
  color: var(--ink-soft);
  border: 1px solid var(--rose);
  border-radius: 999px;
  padding: 6px 12px;
  font-size: 13px;
  font-family: inherit;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.gallery-tab-on {
  background: var(--rose);
  border-color: var(--plum);
  color: var(--ink);
  /* Intentionally no font-weight change — bolder text widens labels just
     enough to reflow neighbors when flex: 1 1 auto redistributes space.
     Background + border + color already signal the active state. */
}

/* ─── Category pills (used in send forms + viewer) ─── */
.cat-row {
  display: flex;
  align-items: center;
  gap: 6px;
  flex-wrap: wrap;
}
.cat-pill {
  background: var(--bg-card);
  color: var(--ink-soft);
  border: 1px solid var(--rose);
  border-radius: 999px;
  padding: 4px 10px;
  font-size: 12px;
  font-family: inherit;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.cat-pill-on {
  background: var(--rose);
  border-color: var(--plum);
  color: var(--ink);
  font-weight: 600;
}
.cat-pill-dark {
  background: rgba(255, 255, 255, 0.08);
  color: rgba(255, 255, 255, 0.85);
  border-color: rgba(255, 255, 255, 0.2);
}
.cat-pill-dark.cat-pill-on {
  background: var(--rose-deep);
  border-color: var(--rose-deep);
  color: white;
}
.viewer-cat-row {
  display: flex;
  align-items: center;
  gap: 6px;
  flex-wrap: wrap;
  padding: 0 20px 8px;
}

/* Context strip below the photo: parent note + per-parent actions. */
.viewer-context {
  padding: 12px 20px 24px;
  color: rgba(255, 255, 255, 0.92);
}
.viewer-note {
  white-space: pre-wrap;
  margin: 0 0 12px;
  font-size: 15px;
  line-height: 1.45;
}
.viewer-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  padding-top: 12px;
  border-top: 1px solid rgba(255, 255, 255, 0.12);
}
.action-btn-dark {
  background: rgba(255, 255, 255, 0.08);
  color: rgba(255, 255, 255, 0.85);
  border-color: rgba(255, 255, 255, 0.2);
}
.action-btn-dark.action-btn-danger {
  color: #f5b7b7;
  border-color: rgba(245, 183, 183, 0.5);
}


/* ─── Level card ─── */
.level-card {
  background: linear-gradient(135deg, var(--bg-card) 0%, var(--rose) 100%);
}
.level-row {
  display: flex;
  align-items: center;
  gap: 14px;
}
.level-emoji {
  font-size: 36px;
  width: 56px; height: 56px;
  display: flex; align-items: center; justify-content: center;
  background: var(--bg-card);
  border-radius: 50%;
  flex-shrink: 0;
}
.level-name {
  font-size: 20px;
  font-weight: 700;
  color: var(--ink);
  letter-spacing: -0.01em;
}
.level-bar {
  height: 8px;
  background: var(--bg-card);
  border-radius: 999px;
  overflow: hidden;
}
.level-bar-fill {
  height: 100%;
  background: linear-gradient(90deg, var(--rose-deep), var(--plum));
  border-radius: 999px;
  transition: width 0.4s ease;
}

/* ─── Badge chips ─── */
.badge-row {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}
.badge-chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  background: var(--bg-card);
  border: 1px solid var(--rose);
  border-radius: 999px;
  padding: 4px 10px;
  font-size: 13px;
  color: var(--ink);
}
.badge-chip-emoji { font-size: 14px; }
.badge-chip-name  { color: var(--ink-soft); }

/* ─── Weekly summary ─── */
.week-row {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 6px 0;
  font-size: 15px;
  color: var(--ink);
}
.week-emoji {
  font-size: 18px;
  width: 24px;
  text-align: center;
}

/* ─── Acts checklist ─── */
.acts-grid {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}
.act-chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  background: var(--bg-card);
  color: var(--ink);
  border: 2px solid var(--rose);
  border-radius: 999px;
  padding: 6px 12px;
  font-size: 14px;
  font-family: inherit;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.act-chip:active { transform: scale(0.96); transition: transform 0.08s; }
.act-chip-on {
  background: var(--rose);
  border-color: var(--plum);
  color: var(--ink);
  font-weight: 600;
}

/* ─── Gender picker (used in onboarding + settings) ─── */
.gender-group {
  display: flex;
  gap: 8px;
  margin-bottom: 12px;
}
.gender-btn {
  flex: 1;
  background: var(--bg-card);
  color: var(--ink);
  border: 2px solid var(--rose);
  border-radius: 14px;
  padding: 12px 8px;
  font-size: 15px;
  font-family: inherit;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  min-width: 0;
}
.gender-btn:active { transform: scale(0.97); transition: transform 0.08s; }
.gender-btn-on {
  background: var(--rose);
  border-color: var(--plum);
  font-weight: 600;
}

/* ─── Applies-to pills (for moods manage screen) ─── */
.applies-row {
  display: flex;
  align-items: center;
  gap: 6px;
  flex-wrap: wrap;
}
.applies-pill {
  background: var(--bg-card);
  color: var(--ink-soft);
  border: 1px solid var(--rose);
  border-radius: 999px;
  padding: 4px 10px;
  font-size: 12px;
  font-family: inherit;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.applies-pill-on {
  background: var(--rose);
  border-color: var(--plum);
  color: var(--ink);
  font-weight: 600;
}
.applies-pill:disabled { opacity: 0.5; }

.mood-manage-card {
  padding: 10px 0;
  border-bottom: 1px solid var(--rose);
}
.mood-manage-card:last-child { border-bottom: none; }
.mood-manage-line {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 6px;
  font-size: 15px;
  color: var(--ink);
}

/* ─── Memories ─── */
.memory-item {
  padding: 16px 0;
  border-bottom: 1px solid var(--rose);
}
.memory-item:last-of-type { border-bottom: none; padding-bottom: 4px; }
.memory-date {
  font-size: 12px;
  color: var(--ink-soft);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  margin-bottom: 4px;
}
.memory-from {
  font-size: 15px;
  color: var(--ink);
}

/* ─── Hormone header ─── */
/* Extra top padding makes room for the personal-state badge labels
   that sit above the avatars (top: -16px on .hormone-state-badge). */
.hormone-header { padding: 30px 18px 18px; }
.hormone-grid {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  gap: 8px;
  align-items: start;
}
.hormone-side {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  min-width: 0;
}
.hormone-side-head {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  margin-bottom: 12px;
}
.hormone-avatar-row {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
}
/* Avatar + corner-badge wrapper. Position-relative so the absolute
   badge can sit just outside the avatar's clipped circle. */
.hormone-avatar-wrap {
  position: relative;
  display: inline-flex;
}
/* Personal-state badge — overlays the avatar's outer corner with a
   small label above an emoji circle. Top-left for the left avatar,
   top-right for the right, so the pair faces the screen edges. The
   wrapper itself is a flex column; only the emoji circle gets the
   card-colored disc. pointer-events:none so the avatar's click
   target stays intact. */
.hormone-state-badge {
  position: absolute;
  top: -16px;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 1px;
  pointer-events: none;
  z-index: 2;
}
.hormone-state-badge--left  { left: -10px; }
.hormone-state-badge--right { right: -10px; }
.hormone-state-badge-label {
  font-size: 9px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--ink-soft);
  background: var(--bg-card);
  padding: 1px 6px;
  border-radius: 999px;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12);
  white-space: nowrap;
  line-height: 1.3;
}
.hormone-state-badge-emoji {
  width: 32px;
  height: 32px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 18px;
  line-height: 1;
  background: var(--bg-card);
  border-radius: 50%;
  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.18);
}
.hormone-state-emoji {
  font-size: 24px;
  line-height: 1;
}
.hormone-state-emoji--couple {
  font-size: 36px;
}
.hormone-state-label {
  font-size: 10px;
  color: var(--ink-soft);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  white-space: nowrap;
}
.hormone-state-label--couple {
  font-size: 11px;
  font-weight: 600;
  color: var(--ink);
}
.hormone-couple-state {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  padding-top: 32px;
  flex-shrink: 0;
}
/* Mood action panels (proxy banner / picker / proxy picker) span the
   full width of the hormone-grid, sitting between the heads row and
   the bars row when active. Front-and-centers the active mood task. */
.hormone-action-row {
  grid-column: 1 / -1;
  margin-top: 14px;
}
.avatar-med {
  width: 100px;
  height: 100px;
  border-radius: 50%;
  background: var(--bg);
  overflow: hidden;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  border: 2px solid var(--rose);
}
.avatar-med img { width: 100%; height: 100%; object-fit: cover; }
.avatar-med-placeholder { font-size: 44px; opacity: 0.4; }
.avatar-med--clickable {
  cursor: pointer;
  transition: transform 0.15s ease, border-color 0.15s ease;
}
.avatar-med--clickable:hover,
.avatar-med--clickable:focus-visible {
  transform: scale(1.04);
  border-color: var(--rose-deep);
  outline: none;
}

/* Heat-state avatar glow. Driven by avatarHeatClass() — fires on
   the aching/explosive/radioactive state.keys regardless of which
   label set is showing (dogfood: Aching/Explosive/Radioactive,
   public: Reaching/Longing/Consumed). The couple-grid avatar
   circle pulses so the partner sees the state without having to
   read the bars. Hot stays celebratory and doesn't pulse. */
@keyframes avatar-heat-aching {
  0%, 100% { box-shadow: 0 0 12px 2px rgba(255, 120, 90, 0.45); }
  50%      { box-shadow: 0 0 22px 6px rgba(255, 120, 90, 0.78); }
}
@keyframes avatar-heat-explosive {
  0%, 100% {
    box-shadow:
      0 0 14px 3px rgba(255, 70, 50, 0.65),
      0 0 28px  6px rgba(255, 100, 60, 0.35);
  }
  50% {
    box-shadow:
      0 0 26px  8px rgba(255, 70, 50, 0.95),
      0 0 48px 14px rgba(255, 100, 60, 0.6);
  }
}
/* Radioactive — meltdown tier. Triple-shadow halo with a green
   isotope tint to set it apart from the orange-red lust ladder
   below it; the sickly green at the outer ring reads as "this is
   no longer hot in a fun way." Faster pulse than Explosive. */
@keyframes avatar-heat-radioactive {
  0%, 100% {
    box-shadow:
      0 0 12px 2px rgba(255, 60, 50, 0.85),
      0 0 28px 6px rgba(150, 255, 90, 0.45),
      0 0 50px 12px rgba(120, 255, 70, 0.25);
  }
  50% {
    box-shadow:
      0 0 22px 6px rgba(255, 60, 50, 1),
      0 0 44px 12px rgba(150, 255, 90, 0.85),
      0 0 80px 22px rgba(120, 255, 70, 0.55);
  }
}
.avatar-med.avatar-heat-aching {
  border-color: rgba(255, 120, 90, 0.9);
  animation: avatar-heat-aching 2.4s ease-in-out infinite;
}
.avatar-med.avatar-heat-explosive {
  border-color: rgba(255, 70, 50, 0.95);
  animation: avatar-heat-explosive 1.2s ease-in-out infinite;
}
.avatar-med.avatar-heat-radioactive {
  border-color: rgba(150, 255, 90, 1);
  animation: avatar-heat-radioactive 0.85s ease-in-out infinite;
}
@media (prefers-reduced-motion: reduce) {
  .avatar-med.avatar-heat-aching,
  .avatar-med.avatar-heat-explosive,
  .avatar-med.avatar-heat-radioactive { animation: none; }
}

/* Cold-state avatar treatment — symmetric to the heat ladder but
   for the low-lust + low-warm corner. The visual progression
   inverts the heat metaphor: where heat speeds up and intensifies
   as the state escalates, cold SLOWS DOWN — chilled tier gets a slow
   frost shimmer, cold tier a slower one, frozen tier is static (a
   still crystalline halo, no animation). The "shutdown / numb"
   semantic reads more honestly as stillness than as motion. Driven
   by avatarHeatClass() — fires on the chilled/cold/frozen state.keys
   regardless of which label set is showing (dogfood:
   Chilled/Cold/Frozen, public: Cooling/Drifting/Resting). */
@keyframes avatar-cool-chilled {
  0%, 100% { box-shadow: 0 0 12px 2px rgba(120, 170, 220, 0.45); }
  50%      { box-shadow: 0 0 20px 5px rgba(140, 190, 235, 0.70); }
}
@keyframes avatar-cool-cold {
  0%, 100% {
    box-shadow:
      0 0 14px 3px rgba(110, 170, 230, 0.65),
      0 0 28px  6px rgba(160, 200, 240, 0.30);
  }
  50% {
    box-shadow:
      0 0 22px  6px rgba(110, 170, 230, 0.85),
      0 0 40px 10px rgba(160, 200, 240, 0.50);
  }
}
.avatar-med.avatar-cool-chilled {
  border-color: rgba(140, 190, 235, 0.85);
  animation: avatar-cool-chilled 4.0s ease-in-out infinite;
}
.avatar-med.avatar-cool-cold {
  border-color: rgba(110, 170, 230, 0.95);
  animation: avatar-cool-cold 6.0s ease-in-out infinite;
}
/* Frozen — no animation. Static crystalline halo with a triple-shadow
   pale-blue/white layered glow. The stillness IS the signal: nothing
   is moving anymore. */
.avatar-med.avatar-cool-frozen {
  border-color: rgba(200, 230, 255, 1);
  box-shadow:
    0 0 12px 2px rgba(180, 220, 250, 0.85),
    0 0 28px 6px rgba(220, 240, 255, 0.55),
    0 0 50px 12px rgba(230, 245, 255, 0.30);
}
@media (prefers-reduced-motion: reduce) {
  .avatar-med.avatar-cool-chilled,
  .avatar-med.avatar-cool-cold { animation: none; }
}
.hormone-name {
  font-weight: 600;
  font-size: 14px;
  color: var(--ink);
  margin-top: 6px;
}
/* Mood block: three stacked rows under the name —
   emoji / label / icon. Each row has its own fixed slot so the
   pencil/refresh stays aligned across both columns regardless of how
   the labels wrap. */
.hormone-mood {
  font-size: 12px;
  color: var(--ink-soft);
  margin-top: 2px;
  line-height: 1.2;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
}
.hormone-mood-emoji {
  font-size: 18px;
  line-height: 1;
  min-height: 20px;
}
.hormone-mood-label {
  text-align: center;
  min-height: 14px;
}
.hormone-mood-icons {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 22px;
}
.hormone-mood .mood-proxy { font-style: italic; }
.hormone-bars {
  display: flex;
  flex-direction: column;
  gap: 7px;
  margin-bottom: 10px;
}
.hormone-bar-row {
  display: grid;
  grid-template-columns: 1fr auto;
  align-items: center;
  gap: 2px 8px;
}
.hormone-bar-label {
  grid-column: 1;
  font-size: 11px;
  color: var(--ink-soft);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.hormone-bar-pct {
  grid-column: 2;
  font-size: 11px;
  color: var(--ink-soft);
  font-variant-numeric: tabular-nums;
  justify-self: end;
}
.hormone-bar {
  grid-column: 1 / -1;
  height: 5px;
  background: var(--bg);
  border-radius: 999px;
  overflow: hidden;
}
.hormone-bar-fill {
  height: 100%;
  border-radius: 999px;
  transition: width 0.5s ease, background 0.3s ease;
  animation: hormone-bar-fill-in 0.8s cubic-bezier(0.22, 1, 0.36, 1) backwards;
}
@keyframes hormone-bar-fill-in {
  from { width: 0%; }
}
/* Stagger so the four bars cascade rather than firing simultaneously. */
.hormone-bar-row:nth-child(2) .hormone-bar-fill { animation-delay: 0.08s; }
.hormone-bar-row:nth-child(3) .hormone-bar-fill { animation-delay: 0.16s; }
.hormone-bar-row:nth-child(4) .hormone-bar-fill { animation-delay: 0.24s; }
@media (prefers-reduced-motion: reduce) {
  .hormone-bar-fill { animation: none; }
}
.hormone-bar-fill.tier-high { background: linear-gradient(90deg, var(--rose-deep), var(--gold)); }
.hormone-bar-fill.tier-mid  { background: linear-gradient(90deg, var(--rose-deep), var(--rose)); }
.hormone-bar-fill.tier-low  { background: var(--ink-soft); opacity: 0.55; }
.mood-icon-btn {
  background: transparent;
  border: none;
  cursor: pointer;
  font-size: 14px;
  line-height: 1;
  padding: 4px 6px;
  color: var(--ink-soft);
  border-radius: 6px;
  transition: background 0.15s ease, transform 0.15s ease;
}
.mood-icon-btn:hover:not(:disabled),
.mood-icon-btn:focus-visible {
  background: var(--rose);
  transform: scale(1.1);
  outline: none;
}
.mood-icon-btn:disabled { opacity: 0.4; cursor: not-allowed; }
.hormone-disclaimer {
  font-size: 11px;
  text-align: center;
  margin-top: 14px;
  margin-bottom: 0;
  opacity: 0.85;
}
.hormone-meta {
  font-size: 11px;
  text-align: center;
  margin-top: 4px;
  margin-bottom: 0;
}

/* ─── Tutorial overlay ─── */
/* Root is pointer-events:none so the highlighted element stays
   clickable. Tooltip and modal re-enable pointer-events on
   themselves. The dim layer is the spotlight's own outset box-shadow
   — not a separate full-screen click-catcher — which avoids stealing
   taps from the highlighted target. */
.tutorial-root {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;        /* tooltip uses left:50% — needs a real width */
  height: 0;          /* spotlight/tooltip position themselves absolutely */
  z-index: 1000;
  pointer-events: none;
}
.tutorial-root--modal {
  position: fixed;
  inset: 0;
  width: auto;
  height: auto;
  background: rgba(0, 0, 0, 0.55);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 20px;
  pointer-events: auto;
}
/* Spotlight uses position:absolute (anchored to the document, not the
   viewport) so it scrolls with the page natively. No scroll listener
   needed → no jank on iOS scroll. */
.tutorial-spotlight {
  position: absolute;
  background: transparent;
  border-radius: 14px;
  pointer-events: none;
  /* Subtle default: dim around + a soft outer glow + a thin rose
     ring at the perimeter. No animation — calm presence. */
  box-shadow:
    inset 0 0 0 1px var(--rose),
    0 0 12px 2px rgba(232, 154, 168, 0.5),
    0 0 0 9999px rgba(0, 0, 0, 0.55);
}
/* Strong variant: opt-in pulsing ring. Used for tiny targets like the
   mood pencil where a subtle glow alone is easy to miss. Set per-step
   via strongGlow:true. */
.tutorial-spotlight--strong {
  animation: tutorial-spotlight-pulse 1.3s ease-in-out infinite;
}
/* Passive (informational) spotlight steps: turn the cutout itself into
   the advance affordance, so a tap on the highlighted area moves to
   the next step instead of doing nothing. Active steps (with advanceOn)
   keep pointer-events:none from the base rule so clicks pass through
   to the underlying control. */
.tutorial-spotlight--clickable {
  pointer-events: auto;
  cursor: pointer;
}
@keyframes tutorial-spotlight-pulse {
  0%, 100% {
    box-shadow:
      inset 0 0 0 1px var(--rose),
      0 0 12px 2px rgba(232, 154, 168, 0.5),
      0 0 0 9999px rgba(0, 0, 0, 0.55);
  }
  50% {
    box-shadow:
      inset 0 0 0 3px var(--gold),
      0 0 28px 6px var(--rose-deep),
      0 0 0 9999px rgba(0, 0, 0, 0.55);
  }
}
/* Click-blocker frame: four position:absolute (document coords) divs
   surrounding the spotlight rect. Used on allowSpotlight steps so
   clicks inside the spotlight rect pass through to the highlighted
   control (the user can pick a mood, tap the trigger button) while
   clicks elsewhere on the page are absorbed. */
.tutorial-blocker-frame {
  position: absolute;
  background: transparent;
  pointer-events: auto;
}
/* Full-screen click absorber. Default for any spotlight step that
   isn't allowSpotlight — used on calendar, footer buttons, and form-
   description steps where allowing clicks within the spotlight would
   navigate the user away from the dashboard and trap them. The user
   advances via the tooltip's Next button. */
.tutorial-blocker-full {
  position: fixed;
  inset: 0;
  background: transparent;
  pointer-events: auto;
}
.tutorial-tooltip {
  position: absolute;
  background: var(--bg-card);
  color: var(--ink);
  border-radius: var(--radius);
  padding: 18px 20px;
  /* Strong shadow + accent border so the tooltip reads as foreground
     against the dimmed page. The colored top border draws the eye on
     step changes. */
  box-shadow: 0 16px 48px rgba(0, 0, 0, 0.5);
  border: 1px solid var(--rose);
  border-top: 4px solid var(--rose-deep);
  max-width: 320px;
  width: calc(100% - 32px);
  left: 50%;
  transform: translateX(-50%);
  animation: tutorial-pop 0.25s ease-out;
  pointer-events: auto;
}
.tutorial-modal {
  background: var(--bg-card);
  color: var(--ink);
  border-radius: var(--radius);
  padding: 24px;
  box-shadow: 0 12px 40px rgba(0, 0, 0, 0.4);
  max-width: 360px;
  width: 100%;
  animation: tutorial-pop 0.25s ease-out;
}
@keyframes tutorial-pop {
  from { opacity: 0; transform: translateX(-50%) scale(0.96); }
  to   { opacity: 1; transform: translateX(-50%) scale(1); }
}
.tutorial-modal { animation-name: tutorial-modal-pop; }
@keyframes tutorial-modal-pop {
  from { opacity: 0; transform: scale(0.96); }
  to   { opacity: 1; transform: scale(1); }
}
.tutorial-title {
  font-size: 18px;
  font-weight: 600;
  color: var(--ink);
  margin: 0 0 8px;
  line-height: 1.25;
}
.tutorial-body {
  font-size: 14px;
  color: var(--ink-soft);
  margin: 0 0 12px;
  line-height: 1.45;
}
.tutorial-progress {
  font-size: 11px;
  color: var(--ink-soft);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  margin-bottom: 12px;
}
.tutorial-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  align-items: center;
  justify-content: flex-end;
}
.tutorial-actions--stacked {
  flex-direction: column;
  align-items: stretch;
}
.tutorial-actions--stacked > .btn,
.tutorial-actions--stacked > .btn-tiny { width: 100%; }
.tutorial-btn-next {
  width: auto;
  min-height: 40px;
  padding: 10px 18px;
  font-size: 14px;
}
.tutorial-btn-skip,
.tutorial-btn-later {
  background: transparent;
  color: var(--ink-soft);
}
.tutorial-btn-skipall {
  font-size: 13px;
  padding: 10px 12px;
  text-align: center;
}
.tutorial-btn-skipall .muted {
  display: block;
  font-size: 11px;
  margin-top: 2px;
}

/* Settings → Walkthroughs list */
.walkthrough-list {
  list-style: none;
  padding: 0;
  margin: 0;
}
.walkthrough-list > li {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 12px 0;
  border-top: 1px solid var(--rose);
  gap: 12px;
}
.walkthrough-list > li:last-child {
  border-bottom: 1px solid var(--rose);
}
.walkthrough-meta {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}
.walkthrough-meta strong {
  font-size: 15px;
  color: var(--ink);
}
.walkthrough-meta .muted {
  font-size: 12px;
}

@media (prefers-reduced-motion: reduce) {
  .tutorial-tooltip,
  .tutorial-modal { animation: none; }
  .tutorial-spotlight--strong { animation: none; }
}

/* ─── Dashboard quiz callout ─── */
/* State-aware funnel card above the weekly summary. Renders louder
   than the default card (rose-deep border + warm gradient) so the
   user notices the next quiz step, but quiet enough to coexist
   with the rest of the dashboard. Hides when there's nothing to
   surface — see quizDashboardCallout() in app.js. */
.dashboard-quiz-cta {
  display: flex;
  align-items: center;
  gap: 14px;
  width: 100%;
  background: linear-gradient(135deg, var(--rose) 0%, var(--bg-card) 100%);
  border: 1px solid var(--rose-deep);
  border-top: 3px solid var(--rose-deep);
  cursor: pointer;
  text-align: left;
  font: inherit;
  color: inherit;
  transition: transform 0.15s ease, box-shadow 0.15s ease;
}
.dashboard-quiz-cta:hover,
.dashboard-quiz-cta:focus-visible {
  transform: translateY(-1px);
  box-shadow: 0 8px 22px rgba(232, 154, 168, 0.32);
  outline: none;
}
.dashboard-quiz-cta-emoji {
  font-size: 30px;
  line-height: 1;
  flex-shrink: 0;
}
.dashboard-quiz-cta-body {
  flex: 1;
  min-width: 0;
}
.dashboard-quiz-cta-headline {
  font-weight: 600;
  color: var(--ink);
  font-size: 15px;
  line-height: 1.3;
}
.dashboard-quiz-cta-sub {
  font-size: 12px;
  color: var(--ink-soft);
  margin-top: 2px;
  line-height: 1.35;
}
.dashboard-quiz-cta-action {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 2px;
  flex-shrink: 0;
}
.dashboard-quiz-cta-cta {
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  font-weight: 600;
  color: var(--rose-deep);
}
.dashboard-quiz-cta-arrow {
  font-size: 22px;
  color: var(--rose-deep);
  line-height: 1;
}
.dashboard-quiz-cta:hover .dashboard-quiz-cta-arrow {
  transform: translateX(3px);
  transition: transform 0.15s ease;
}

/* ─── Dashboard footer grid ─── */
/* Six entries in a 2-column grid; sign-out spans below, centered. The
   wrapper uses .card so the whole block reads as one unit instead of
   feeling like orphaned buttons hanging off the bottom. */
.dashboard-footer {
  margin-top: 8px;
}
.dashboard-footer-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 10px;
}

/* ─── Quiz feature ─── */
/* Six packs of 30 questions each, escalating from connection to kinky.
   Visual goals: feel inviting and warm, not clinical. Tier color hints
   ride the pack cards, the question card, and the match badges so the
   couple has a consistent visual sense of "where in the journey we are."
   See notes/quiz_design.md for the underlying mechanics. */

.quiz-screen { gap: 14px; }

.quiz-header-card {
  background: linear-gradient(135deg, var(--bg-card) 0%, var(--bg) 100%);
}
.quiz-title {
  margin: 0;
  font-size: 20px;
  font-weight: 600;
  color: var(--ink);
  flex: 1;
}
.quiz-subtitle {
  margin: 10px 0 0;
  font-size: 13px;
  color: var(--ink-soft);
  line-height: 1.4;
}

/* Matches CTA — large clickable card on the home view. The arrow drifts
   on hover so the card reads as actionable rather than informational. */
.quiz-matches-cta {
  display: flex;
  align-items: center;
  gap: 14px;
  width: 100%;
  background: linear-gradient(135deg, var(--rose) 0%, var(--bg-card) 100%);
  border: 1px solid var(--rose-deep);
  cursor: pointer;
  text-align: left;
  font: inherit;
  color: inherit;
  transition: transform 0.15s ease, box-shadow 0.15s ease;
}
.quiz-matches-cta:hover,
.quiz-matches-cta:focus-visible {
  transform: translateY(-1px);
  box-shadow: 0 6px 18px rgba(232, 154, 168, 0.3);
  outline: none;
}
.quiz-matches-cta-emoji  { font-size: 28px; line-height: 1; }
.quiz-matches-cta-body   { flex: 1; min-width: 0; }
.quiz-matches-cta-title  { font-weight: 600; color: var(--ink); }
.quiz-matches-cta-hint   { font-size: 13px; color: var(--ink-soft); margin-top: 2px; }
.quiz-matches-cta-arrow  {
  font-size: 22px;
  color: var(--rose-deep);
  transition: transform 0.15s ease;
}
.quiz-matches-cta:hover .quiz-matches-cta-arrow { transform: translateX(3px); }

/* Pack grid — single column on phones, more breathing room than the
   dashboard footer grid because each card is the headline content here. */
.quiz-pack-list {
  display: grid;
  grid-template-columns: 1fr;
  gap: 10px;
}
@media (min-width: 540px) {
  .quiz-pack-list { grid-template-columns: 1fr 1fr; }
}

.quiz-pack-card {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 16px;
  border-radius: var(--radius);
  border: 1px solid var(--rose);
  background: var(--bg-card);
  cursor: pointer;
  text-align: left;
  font: inherit;
  color: inherit;
  transition: transform 0.15s ease, box-shadow 0.15s ease, border-color 0.15s ease;
}
.quiz-pack-card:not(:disabled):hover,
.quiz-pack-card:not(:disabled):focus-visible {
  transform: translateY(-1px);
  box-shadow: 0 6px 18px rgba(232, 154, 168, 0.25);
  border-color: var(--rose-deep);
  outline: none;
}
.quiz-pack-card-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  font-size: 11px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.05em;
}
.quiz-pack-card-num    { color: var(--ink-soft); }
.quiz-pack-card-status { font-size: 14px; display: flex; align-items: center; gap: 6px; }
.quiz-pack-card-new-badge {
  font-size: 11px;
  font-weight: 600;
  padding: 2px 8px;
  border-radius: 999px;
  background: linear-gradient(135deg, var(--rose-deep), var(--gold));
  color: white;
  letter-spacing: 0.02em;
}
.quiz-pack-card-name-row {
  display: flex;
  align-items: center;
  gap: 10px;
}
.quiz-pack-card-emoji {
  font-size: 22px;
  line-height: 1;
  flex-shrink: 0;
}
.quiz-pack-card-name {
  font-size: 18px;
  font-weight: 600;
  color: var(--ink);
}
/* Locked packs show ?-marks matching the real name's length. The wider
   letter-spacing reads as an intentional "redacted" treatment rather
   than looking like a placeholder bug. */
.quiz-pack-card-name--hidden {
  letter-spacing: 0.15em;
  color: var(--ink-soft);
}
.quiz-pack-card-progress-row {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-top: 4px;
}
.quiz-pack-card-progress-bar {
  flex: 1;
  height: 5px;
  background: var(--bg);
  border-radius: 999px;
  overflow: hidden;
}
.quiz-pack-card-progress-fill {
  height: 100%;
  background: linear-gradient(90deg, var(--rose-deep), var(--gold));
  border-radius: 999px;
  transition: width 0.4s ease;
}
.quiz-pack-card-progress-label {
  font-size: 11px;
  color: var(--ink-soft);
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}

/* Tier accents — each pack card carries a subtle gradient hint on the
   left edge that telegraphs the tier (vanilla → erotic → kinky) so the
   couple has a visual sense of where they are in the escalation. */
.quiz-pack-card--vanilla::before,
.quiz-pack-card--erotic::before,
.quiz-pack-card--kinky::before {
  content: '';
  position: absolute;
  left: 0;
  top: 0;
  bottom: 0;
  width: 4px;
  border-radius: var(--radius) 0 0 var(--radius);
}
.quiz-pack-card--vanilla::before { background: linear-gradient(180deg, var(--rose), var(--rose-deep)); }
.quiz-pack-card--erotic::before  { background: linear-gradient(180deg, var(--rose-deep), var(--gold)); }
.quiz-pack-card--kinky::before   { background: linear-gradient(180deg, var(--rose-deep), var(--ink)); }

.quiz-pack-card--locked {
  cursor: not-allowed;
  opacity: 0.55;
}
.quiz-pack-card--locked:hover { transform: none; box-shadow: none; }
/* Completion uses the border-color shift (rose → gold) plus the ✓ in
   the card status corner. The tier gradient bar (::before) gets hidden
   on completed cards — its rose/gold/ink stripe fights the gold
   outline visually, and the gold border itself already conveys the
   tier-agnostic "this one's done" state. */
.quiz-pack-card--completed { border-color: var(--gold); }
.quiz-pack-card--completed::before { display: none; }

.quiz-footer-help {
  font-size: 12px;
  color: var(--ink-soft);
  text-align: center;
  margin: 10px 0 0;
}

/* Terms card — the yes-is-sticky agreement gate that blocks the pack
   picker until the user has read and accepted the rule. Strong border
   + warm gradient so it reads as load-bearing rather than incidental. */
.quiz-terms-card {
  background: linear-gradient(135deg, var(--rose) 0%, var(--bg-card) 100%);
  border: 1px solid var(--rose-deep);
  border-top: 4px solid var(--rose-deep);
}
.quiz-terms-title {
  margin: 0 0 10px;
  font-size: 18px;
  font-weight: 600;
  color: var(--ink);
}
.quiz-terms-lead {
  margin: 0 0 16px;
  font-size: 14px;
  color: var(--ink);
  line-height: 1.5;
}
.quiz-terms-rule {
  margin: 12px 0;
  padding: 12px 14px;
  background: var(--bg-card);
  border-radius: 10px;
  border-left: 3px solid var(--rose-deep);
}
.quiz-terms-rule-headline {
  font-size: 14px;
  font-weight: 600;
  color: var(--ink);
  margin-bottom: 4px;
}
.quiz-terms-rule p {
  margin: 0;
  font-size: 13px;
  color: var(--ink);
  line-height: 1.5;
}
.quiz-terms-final {
  margin: 16px 0 0;
  font-size: 13px;
  font-weight: 600;
  color: var(--ink);
  text-align: center;
}
.quiz-terms-actions {
  display: flex;
  gap: 10px;
  margin-top: 16px;
  justify-content: flex-end;
}

/* Empty / setup-not-complete state on the quiz home view. Surfaces
   when /api/quiz/state errors out or the question pool hasn't been
   seeded yet — better than showing the matches CTA over blank space. */
.quiz-empty-state {
  text-align: center;
  padding: 28px 18px;
  display: flex;
  flex-direction: column;
  gap: 8px;
  align-items: center;
}
.quiz-empty-state-emoji  { font-size: 36px; line-height: 1; }
.quiz-empty-state-title  { font-weight: 600; color: var(--ink); margin: 4px 0 0; }
.quiz-empty-state-body   { font-size: 13px; color: var(--ink-soft); margin: 0; max-width: 30em; }
.quiz-empty-state-hint   { font-size: 12px; color: var(--ink-soft); margin: 6px 0 8px; max-width: 30em; }
.quiz-empty-state-hint code {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 11px;
  background: var(--bg);
  padding: 1px 6px;
  border-radius: 4px;
}
.quiz-empty-state-detail {
  margin: 4px 0;
  padding: 8px 10px;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 11px;
  text-align: left;
  background: var(--bg);
  border-radius: 6px;
  max-width: 100%;
  white-space: pre-wrap;
  word-break: break-word;
  color: var(--ink);
  border: 1px solid var(--rose);
}

/* Pack header during answering — a slim card with the pack name + a
   progress bar that fills as questions get answered. */
.quiz-pack-header {
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.quiz-pack-header-name {
  font-size: 16px;
  font-weight: 600;
  color: var(--ink);
}
.quiz-pack-header-position {
  font-size: 12px;
  color: var(--ink-soft);
  font-variant-numeric: tabular-nums;
}
.quiz-pack-header-bar {
  height: 4px;
  background: var(--bg);
  border-radius: 999px;
  overflow: hidden;
}
.quiz-pack-header-bar-fill {
  height: 100%;
  background: linear-gradient(90deg, var(--rose-deep), var(--gold));
  transition: width 0.4s ease;
}
.quiz-pack-header-retake {
  font-size: 12px;
  padding: 4px 10px;
  background: transparent;
  border: 1px solid var(--rose);
  border-radius: 999px;
  color: var(--ink-soft);
  cursor: pointer;
  white-space: nowrap;
  transition: border-color 0.15s ease, color 0.15s ease;
}
.quiz-pack-header-retake:hover,
.quiz-pack-header-retake:focus-visible {
  border-color: var(--rose-deep);
  color: var(--ink);
  outline: none;
}
/* Retake banner — soft, informative, doesn't feel like a warning. Sits
   directly under the progress bar inside the pack header so the user
   knows their current ratings will appear pre-selected. */
.quiz-retake-banner {
  margin-top: 8px;
  padding: 8px 12px;
  font-size: 12px;
  color: var(--ink);
  background: linear-gradient(135deg, var(--rose) 0%, var(--bg-card) 100%);
  border: 1px solid var(--rose-deep);
  border-radius: 10px;
  line-height: 1.4;
}

/* Question card — the centerpiece. Generous padding, big typography,
   center-aligned. The 5-button rating row sits below. */
.quiz-question-card {
  background: var(--bg-card);
  border-radius: var(--radius);
  padding: 28px 22px;
  box-shadow: 0 6px 24px rgba(0, 0, 0, 0.08);
  border: 1px solid var(--rose);
  display: flex;
  flex-direction: column;
  gap: 22px;
  align-items: center;
  text-align: center;
  position: relative;
}

/* ─── Admin-only quiz catalog editing (user_id === 1) ────────────────
   Inline edit form on the question card and a persistent add-form
   below it so the catalog can be tended to without a separate admin
   panel. Hidden entirely for non-admin sessions. */
.quiz-admin-edit-toggle {
  position: absolute;
  top: 10px;
  right: 10px;
  font-size: 11px;
  opacity: 0.7;
  z-index: 2;
}
.quiz-admin-edit-toggle:hover { opacity: 1; }

.quiz-admin-form,
.quiz-admin-add-card {
  display: flex;
  flex-direction: column;
  gap: 12px;
  width: 100%;
  text-align: left;
}
.quiz-admin-add-card {
  border: 1px dashed var(--rose-deep, #c97a86);
  background: var(--bg-card);
  padding: 18px 18px 16px;
}
.quiz-admin-form-title {
  font-weight: 600;
  font-size: 14px;
  color: var(--ink);
}
.quiz-admin-field-row {
  display: flex;
  gap: 10px;
}
.quiz-admin-field-row > .quiz-admin-field { flex: 1; min-width: 0; }
.quiz-admin-field {
  display: flex;
  flex-direction: column;
  gap: 4px;
  position: relative;
}
.quiz-admin-label {
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--ink-soft);
}
.quiz-admin-input {
  width: 100%;
  padding: 8px 10px;
  border-radius: 8px;
  border: 1px solid var(--rose);
  background: var(--bg);
  color: var(--ink);
  font: inherit;
  font-size: 14px;
  box-sizing: border-box;
}
.quiz-admin-input:focus {
  outline: none;
  border-color: var(--rose-deep, #c97a86);
}
.quiz-admin-textarea {
  resize: vertical;
  line-height: 1.4;
}
.quiz-admin-combobox-list {
  position: absolute;
  top: 100%;
  left: 0;
  right: 0;
  margin-top: 4px;
  background: var(--bg-card);
  border: 1px solid var(--rose);
  border-radius: 8px;
  box-shadow: 0 6px 18px rgba(0, 0, 0, 0.12);
  max-height: 220px;
  overflow-y: auto;
  z-index: 5;
}
.quiz-admin-combobox-item {
  display: flex;
  justify-content: space-between;
  align-items: center;
  width: 100%;
  padding: 8px 10px;
  background: transparent;
  border: 0;
  text-align: left;
  font: inherit;
  font-size: 13px;
  color: var(--ink);
  cursor: pointer;
}
.quiz-admin-combobox-item:hover,
.quiz-admin-combobox-item:focus {
  background: rgba(0, 0, 0, 0.04);
  outline: none;
}
.quiz-admin-combobox-count {
  font-size: 11px;
  color: var(--ink-soft);
  margin-left: 8px;
}
.quiz-admin-combobox-new {
  padding: 8px 10px;
  font-size: 12px;
  color: var(--ink-soft);
  border-top: 1px dashed var(--rose);
}
.quiz-admin-form-actions {
  display: flex;
  justify-content: flex-end;
  gap: 8px;
}
.quiz-admin-error {
  margin: 0;
  padding: 8px 10px;
  font-size: 13px;
  color: #b3261e;
  background: rgba(179, 38, 30, 0.08);
  border-radius: 8px;
}

/* Admin "view as partner" swap button. Sits under the couple emoji,
   only renders for user_id=1 (so the partner never sees it on her
   own session). */
.admin-assume-btn {
  margin-top: 6px;
  font-size: 11px;
  opacity: 0.6;
}
.admin-assume-btn:hover { opacity: 1; }

/* X (Twitter) tweet embed — auto-rendered inline when an X URL is
   detected in note text (couple_id=1 dogfood only — see
   renderNoteSegments in app.js). Iframe loads platform.twitter.com's
   embed widget; sandboxed cross-origin so X can't read the parent
   page. min-height accommodates video tweets without too much excess
   for text-only ones. */
.x-embed-iframe,
.instagram-embed-iframe {
  width: 100%;
  border: 1px solid var(--rose-deep);
  display: block;
  margin: 8px 0;
  border-radius: 12px;
  background: var(--bg-card);
}
.x-embed-iframe          { min-height: 450px; }
.instagram-embed-iframe  { min-height: 600px; }   /* IG posts run tall — author + media + caption */

/* Larger viewports get more vertical room. */
@media (min-width: 768px) {
  .x-embed-iframe          { min-height: 650px; }
  .instagram-embed-iframe  { min-height: 800px; }
}

/* Solo-release cadence dots — 14-day strip on the dashboard tile.
   Empty days render as faint outlines; hit days fill with the rose
   palette to match the rest of the look-back cluster. Outline color
   uses --ink at low alpha so the empty ring is visible against both
   the cream/light card background and the dark-mode plum-tinted
   card. The previous `--rose` outline at 40% opacity disappeared
   into the dark-mode card surface. */
.solo-cadence-dot {
  width: 16px;
  height: 16px;
  border-radius: 50%;
  background: transparent;
  border: 1.5px solid var(--ink, #4a3a3f);
  flex: 1;
  min-width: 12px;
  max-width: 22px;
  opacity: 0.35;
}
.solo-cadence-dot--hit {
  background: var(--rose-deep, #c97a86);
  border-color: var(--rose-deep, #c97a86);
  opacity: 1;
}

/* ─── Card countdown badge ────────────────────────────────────────────
   Live h:m:s expiry pill on relief-request and tease cards. The
   12-hour window ticks down via the tickSec Alpine state (1s
   interval). Tier classes light up in the last 3/2/1 hours with
   escalating glow — a soft cue that the moment is winding down. */
.card-meta-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  margin-top: -2px;
  font-size: 13px;
  flex-wrap: wrap;
}
.card-countdown {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 3px 9px;
  border-radius: 999px;
  background: rgba(0, 0, 0, 0.04);
  color: var(--ink-soft);
  font-variant-numeric: tabular-nums;
  font-size: 12px;
  font-weight: 500;
  border: 1px solid transparent;
  transition: background 0.3s ease, color 0.3s ease, border-color 0.3s ease;
}
.card-countdown-label {
  font-weight: 500;
  opacity: 0.85;
}
.card-countdown-time {
  font-weight: 700;
  letter-spacing: 0.02em;
}

@keyframes countdown-glow-3h {
  0%, 100% { box-shadow: 0 0 4px 1px rgba(255, 165, 90, 0.30); }
  50%      { box-shadow: 0 0 8px 2px rgba(255, 165, 90, 0.55); }
}
@keyframes countdown-glow-2h {
  0%, 100% { box-shadow: 0 0 6px 2px rgba(255, 130, 80, 0.45); }
  50%      { box-shadow: 0 0 14px 4px rgba(255, 130, 80, 0.75); }
}
@keyframes countdown-glow-1h {
  0%, 100% {
    box-shadow:
      0 0 8px 2px rgba(255, 80, 60, 0.55),
      0 0 16px 4px rgba(255, 100, 70, 0.30);
  }
  50% {
    box-shadow:
      0 0 16px 5px rgba(255, 80, 60, 0.95),
      0 0 30px 8px rgba(255, 100, 70, 0.55);
  }
}
/* Tiered countdown pills — DARK colors as the default (matches the
   root theme model). Light overrides apply on explicit Light or Auto
   when OS is light. Foreground/background/border colors all shift
   per theme so the digits stay readable on whichever background. */
.card-countdown.countdown-tier-3h {
  background: rgba(255, 180, 110, 0.18);
  color: #ffd0a0;
  border-color: rgba(255, 180, 110, 0.55);
  animation: countdown-glow-3h 3s ease-in-out infinite;
}
.card-countdown.countdown-tier-2h {
  background: rgba(255, 145, 95, 0.22);
  color: #ffc0a0;
  border-color: rgba(255, 145, 95, 0.7);
  animation: countdown-glow-2h 2s ease-in-out infinite;
}
.card-countdown.countdown-tier-1h {
  background: rgba(255, 105, 90, 0.28);
  color: #ffd6cc;
  border-color: rgba(255, 105, 90, 0.9);
  animation: countdown-glow-1h 1.1s ease-in-out infinite;
}
.card-countdown.countdown-expired {
  background: rgba(255, 255, 255, 0.06);
  color: var(--ink-soft);
  font-style: italic;
  opacity: 0.7;
}
@media (prefers-reduced-motion: reduce) {
  .card-countdown.countdown-tier-3h,
  .card-countdown.countdown-tier-2h,
  .card-countdown.countdown-tier-1h { animation: none; }
}

/* Light overrides — dark-red digits on translucent warm tints
   against the light --bg-card. */
:root[data-theme="light"] .card-countdown.countdown-tier-3h,
:root[data-theme="auto"]  .card-countdown.countdown-tier-3h {
  background: rgba(255, 165, 90, 0.12);
  color: rgb(180, 95, 25);
  border-color: rgba(255, 165, 90, 0.5);
}
:root[data-theme="light"] .card-countdown.countdown-tier-2h,
:root[data-theme="auto"]  .card-countdown.countdown-tier-2h {
  background: rgba(255, 130, 80, 0.16);
  color: rgb(180, 70, 40);
  border-color: rgba(255, 130, 80, 0.65);
}
:root[data-theme="light"] .card-countdown.countdown-tier-1h,
:root[data-theme="auto"]  .card-countdown.countdown-tier-1h {
  background: rgba(255, 80, 60, 0.18);
  color: rgb(170, 40, 30);
  border-color: rgba(255, 80, 60, 0.85);
}
:root[data-theme="light"] .card-countdown.countdown-expired,
:root[data-theme="auto"]  .card-countdown.countdown-expired {
  background: rgba(0, 0, 0, 0.05);
  color: var(--ink-soft);
}

/* Auto + OS dark → revert to dark-mode countdown colors. */
@media (prefers-color-scheme: dark) {
  :root[data-theme="auto"] .card-countdown.countdown-tier-3h {
    background: rgba(255, 180, 110, 0.18);
    color: #ffd0a0;
    border-color: rgba(255, 180, 110, 0.55);
  }
  :root[data-theme="auto"] .card-countdown.countdown-tier-2h {
    background: rgba(255, 145, 95, 0.22);
    color: #ffc0a0;
    border-color: rgba(255, 145, 95, 0.7);
  }
  :root[data-theme="auto"] .card-countdown.countdown-tier-1h {
    background: rgba(255, 105, 90, 0.28);
    color: #ffd6cc;
    border-color: rgba(255, 105, 90, 0.9);
  }
  :root[data-theme="auto"] .card-countdown.countdown-expired {
    background: rgba(255, 255, 255, 0.06);
    color: var(--ink-soft);
  }
}
/* "Last question" pill — sits just above the question text on the
   final-of-pack item to telegraph that the next tap closes the pack.
   Not a blocker, just a clear "be sure" cue so completion doesn't
   sneak up on the user. */
.quiz-final-question-badge {
  align-self: center;
  font-size: 11px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: white;
  background: linear-gradient(135deg, var(--rose-deep) 0%, var(--gold) 100%);
  padding: 4px 12px;
  border-radius: 999px;
  box-shadow: 0 2px 6px rgba(232, 154, 168, 0.3);
}

/* Reserve three lines of vertical space for the question text so the
   rating row lands at the same vertical position regardless of how
   the prompt wraps on the user's screen. Short questions are
   centered within the reserved block via the inner flex layout —
   keeps the visual rhythm consistent and gives the user a fixed
   tap-target location across the whole pack. */
.quiz-question-text {
  font-size: 22px;
  font-weight: 500;
  color: var(--ink);
  line-height: 1.35;
  max-width: 28em;
  min-height: 4.05em;    /* 3 × line-height */
  display: flex;
  align-items: center;
  justify-content: center;
}

/* Rating buttons — five evenly-spaced cards. The selected one tints
   rose-deep and lifts slightly. Mobile-friendly tap targets (≥ 44px). */
.quiz-rating-row {
  display: grid;
  /* minmax(0, 1fr) forces equal columns even when label text would
     otherwise stretch one column wider than the others — without it,
     "Definitely" makes its column wider than "Maybe" on narrow phones. */
  grid-template-columns: repeat(5, minmax(0, 1fr));
  gap: 6px;
  width: 100%;
  max-width: 480px;
}
.quiz-rating-btn {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  padding: 10px 4px;
  background: var(--bg);
  border: 1px solid var(--rose);
  border-radius: 12px;
  cursor: pointer;
  font: inherit;
  color: var(--ink-soft);
  min-height: 64px;
  transition: transform 0.12s ease, background 0.12s ease, color 0.12s ease, border-color 0.12s ease;
}
.quiz-rating-btn:hover:not(:disabled) {
  border-color: var(--rose-deep);
  color: var(--ink);
}
.quiz-rating-btn:disabled { opacity: 0.5; cursor: not-allowed; }
/* Locked-by-yes-is-sticky state. Stronger visual than plain disabled —
   crossed out + further dimmed so the user understands this isn't
   "loading" but "intentionally blocked." */
.quiz-rating-btn--locked {
  opacity: 0.35;
  text-decoration: line-through;
  text-decoration-color: var(--ink-soft);
  text-decoration-thickness: 1px;
  background: var(--bg);
  cursor: not-allowed;
}
.quiz-rating-btn--selected,
.quiz-rating-btn--selected:hover {
  background: linear-gradient(135deg, var(--rose) 0%, var(--rose-deep) 100%);
  border-color: var(--rose-deep);
  color: white;
  transform: translateY(-1px);
  box-shadow: 0 4px 12px rgba(232, 154, 168, 0.4);
}
.quiz-rating-num {
  font-size: 18px;
  font-weight: 600;
  line-height: 1;
}
.quiz-rating-label {
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  text-align: center;
  /* Allow the label to break inside the (now-equal) column rather than
     forcing the column wider. "Definitely" wraps on narrow screens. */
  min-width: 0;
  word-break: break-word;
}

/* Footer row under the rating buttons: Back affordance on the left,
   helper text in the middle. Back-disabled state matches the rating
   button's disabled treatment so they read as a coherent control set. */
.quiz-question-actions {
  display: flex;
  align-items: center;
  gap: 12px;
  width: 100%;
  max-width: 480px;
  flex-wrap: wrap;
  justify-content: center;
}
.quiz-back-btn {
  font-size: 13px;
  padding: 6px 12px;
  background: transparent;
  border: 1px solid var(--rose);
  border-radius: 999px;
  color: var(--ink-soft);
  cursor: pointer;
  transition: border-color 0.15s ease, color 0.15s ease, background 0.15s ease;
}
.quiz-back-btn:hover:not(:disabled),
.quiz-back-btn:focus-visible {
  border-color: var(--rose-deep);
  color: var(--ink);
  background: var(--bg);
  outline: none;
}
.quiz-back-btn:disabled {
  opacity: 0.4;
  cursor: not-allowed;
}
/* Save Progress — mirrors the back button visually, but pulses subtly
   when there are unsaved changes so the user notices it without it
   feeling like a nag. Disabled when nothing to save. */
.quiz-save-btn {
  font-size: 13px;
  padding: 6px 12px;
  background: transparent;
  border: 1px solid var(--rose);
  border-radius: 999px;
  color: var(--ink-soft);
  cursor: pointer;
  white-space: nowrap;
  transition: border-color 0.15s ease, color 0.15s ease, background 0.15s ease;
}
.quiz-save-btn:hover:not(:disabled),
.quiz-save-btn:focus-visible {
  border-color: var(--rose-deep);
  color: var(--ink);
  background: var(--bg);
  outline: none;
}
.quiz-save-btn:disabled { opacity: 0.4; cursor: not-allowed; }
.quiz-save-btn--has-changes {
  border-color: var(--rose-deep);
  color: var(--rose-deep);
  font-weight: 600;
}
.quiz-save-error {
  color: var(--rose-deep);
  font-weight: 500;
}
.quiz-rating-help {
  margin: 0;
  font-size: 12px;
  color: var(--ink-soft);
  max-width: 28em;
  text-align: center;
}

/* Pack-completed closer — celebratory but not over the top. */
.quiz-completed-card {
  background: linear-gradient(135deg, var(--rose) 0%, var(--bg-card) 100%);
  border: 1px solid var(--gold);
  border-radius: var(--radius);
  padding: 28px 22px;
  text-align: center;
  display: flex;
  flex-direction: column;
  gap: 12px;
  align-items: center;
}
.quiz-completed-emoji { font-size: 40px; line-height: 1; }
.quiz-completed-title {
  font-size: 20px;
  font-weight: 600;
  color: var(--ink);
}
.quiz-completed-body {
  margin: 0;
  font-size: 14px;
  color: var(--ink-soft);
  max-width: 28em;
  line-height: 1.4;
}
.quiz-completed-actions {
  display: flex;
  gap: 10px;
  margin-top: 6px;
  flex-wrap: wrap;
  justify-content: center;
}
/* Top-row primary actions (Start next / Pack list) span the full
   width so they read as the headline next-step. Secondary buttons
   (See matches / Retake / Revise) below sit inline as before. */
.quiz-completed-action--full {
  flex: 1 1 100%;
  min-height: 48px;
}

/* Tier filter tabs in the matches view. Each tier carries its own accent
   so the color reinforces the tier label visually as the user filters. */
.quiz-tier-tabs {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 6px;
  padding: 8px;
}
.quiz-tier-tab {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  padding: 8px 4px;
  border: 1px solid transparent;
  border-radius: 10px;
  background: transparent;
  cursor: pointer;
  font: inherit;
  color: var(--ink-soft);
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  transition: background 0.12s ease, border-color 0.12s ease, color 0.12s ease;
}
.quiz-tier-tab:hover { background: var(--bg); }
.quiz-tier-tab--active {
  background: var(--bg);
  border-color: var(--rose-deep);
  color: var(--ink);
}
.quiz-tier-tab-count {
  font-size: 16px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}
.quiz-tier-tab--perfect.quiz-tier-tab--active { border-color: var(--gold); color: var(--gold); }
.quiz-tier-tab--strong.quiz-tier-tab--active  { border-color: var(--rose-deep); color: var(--rose-deep); }
.quiz-tier-tab--soft.quiz-tier-tab--active    { border-color: var(--ink-soft); color: var(--ink); }

/* Match list — each match is a row with a tier badge on the left, the
   question text, and the pack name below. Soft borders so the list
   reads as a flowing collection rather than a hard table. */
.quiz-matches-list {
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.quiz-match-row {
  background: var(--bg-card);
  border: 1px solid var(--rose);
  border-radius: 12px;
  padding: 14px 16px;
  display: flex;
  flex-direction: column;
  gap: 6px;
  transition: transform 0.12s ease, box-shadow 0.12s ease;
}
.quiz-match-row:hover {
  transform: translateY(-1px);
  box-shadow: 0 4px 12px rgba(232, 154, 168, 0.18);
}
.quiz-match-row--perfect { border-color: var(--gold); }
.quiz-match-row--strong  { border-color: var(--rose-deep); }
.quiz-match-row--soft    { border-color: var(--rose); }

.quiz-match-row-tier-badge {
  align-self: flex-start;
  font-size: 10px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  padding: 3px 8px;
  border-radius: 999px;
  color: white;
}
.quiz-match-row-tier-badge--perfect {
  background: linear-gradient(135deg, var(--rose-deep), var(--gold));
}
.quiz-match-row-tier-badge--strong {
  background: var(--rose-deep);
}
.quiz-match-row-tier-badge--soft {
  background: var(--ink-soft);
}
.quiz-match-row-text {
  font-size: 15px;
  color: var(--ink);
  line-height: 1.35;
}
.quiz-match-row-meta {
  font-size: 11px;
  color: var(--ink-soft);
}
.quiz-match-row-pack {
  text-transform: uppercase;
  letter-spacing: 0.04em;
  font-weight: 600;
}

/* Pack entry-confirm modal. Modeled after the tutorial skip-confirm
   styling but standalone (the tutorial overlay machinery is too
   coupled to the tour state to share). */
.quiz-modal-root {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.55);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 20px;
  z-index: 100;
}
.quiz-modal {
  background: var(--bg-card);
  color: var(--ink);
  border-radius: var(--radius);
  padding: 22px 22px 18px;
  border: 1px solid var(--rose);
  border-top: 4px solid var(--rose-deep);
  box-shadow: 0 16px 48px rgba(0, 0, 0, 0.4);
  max-width: 380px;
  width: 100%;
}
.quiz-modal-title {
  margin: 0 0 10px;
  font-size: 17px;
  font-weight: 600;
}
.quiz-modal-body {
  margin: 0 0 10px;
  font-size: 14px;
  color: var(--ink-soft);
  line-height: 1.45;
}
.quiz-modal-actions {
  display: flex;
  gap: 8px;
  margin-top: 14px;
  justify-content: flex-end;
}

/* Newly-unlocked toast — slides in from the top, auto-dismisses, and
   is tappable to jump straight into the new pack. */
.quiz-unlock-toast {
  position: fixed;
  top: 12px;
  left: 50%;
  transform: translateX(-50%);
  background: linear-gradient(135deg, var(--rose-deep) 0%, var(--gold) 100%);
  color: white;
  padding: 12px 18px;
  border-radius: 999px;
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.25);
  display: flex;
  align-items: center;
  gap: 12px;
  z-index: 200;
  cursor: pointer;
  max-width: calc(100vw - 24px);
  animation: quiz-unlock-slide-in 0.4s cubic-bezier(0.22, 1, 0.36, 1);
}
.quiz-unlock-toast-emoji { font-size: 20px; line-height: 1; }
.quiz-unlock-toast-text {
  display: flex;
  flex-direction: column;
  font-size: 13px;
  line-height: 1.2;
}
.quiz-unlock-toast-text strong { font-weight: 600; }
.quiz-unlock-toast-text span:not(strong) { opacity: 0.92; font-size: 12px; }
@keyframes quiz-unlock-slide-in {
  from { transform: translate(-50%, -120%); opacity: 0; }
  to   { transform: translate(-50%, 0);     opacity: 1; }
}
@media (prefers-reduced-motion: reduce) {
  .quiz-unlock-toast { animation: none; }
}
