/* ============================================
   Murmur Onboarding Prototype
   Responsive: iPhone (390×844) → iPad Mini (744×1133)
   ============================================ */

@font-face {
  font-family: 'Sofia Pro';
  font-weight: 300;
  font-style: normal;
  font-display: swap;
  src: url('assets/fonts/sofiaprolight_1.otf') format('opentype');
}

@font-face {
  font-family: 'Sofia Pro';
  font-weight: 400;
  font-style: normal;
  font-display: swap;
  src: url('assets/fonts/sofiaproregular.otf') format('opentype');
}

@font-face {
  font-family: 'Sofia Pro';
  font-weight: 700;
  font-style: normal;
  font-display: swap;
  src: url('assets/fonts/sofiaprobold_1.otf') format('opentype');
}

/* Sofia Pro has no ExtraBold; Black stands in at 800 so existing rules hold */
@font-face {
  font-family: 'Sofia Pro';
  font-weight: 800;
  font-style: normal;
  font-display: swap;
  src: url('assets/fonts/sofiaproblack.otf') format('opentype');
}

:root {
  --bg: transparent;
  --surface: rgba(255, 255, 255, 0.92);
  --surface-solid: #FFFFFF;
  --text-primary: #F5F5F5;
  --text-secondary: #A1A1AA;
  --text-muted: #71717A;
  --border: rgba(255, 255, 255, 0.12);
  --border-light: rgba(255, 255, 255, 0.06);

  /* Phase palette */
  --phase1: #D5347A;
  --phase1-light: #FFF0F6;
  --phase2: #22C55E;
  --phase2-light: #F0FDF4;
  --phase3: #4ECDC4;
  --phase3-light: #F0FDFA;

  /* Current phase — set via JS */
  --phase-color: var(--phase1);
  --phase-bg: var(--phase1-light);

  /* Actions */
  --nah: #9CA3AF;
  --tweak: #D5347A;
  --thatsus: #22C55E;

  /* AI */
  --ai-purple: #A78BFA;
  --ai-purple-dim: rgba(167, 139, 250, 0.15);

  /* Brand */
  --brand: #EC4899;

  /* System */
  --radius: 14px;
  --radius-sm: 10px;
  --radius-pill: 999px;
  --shadow-card: 0 2px 12px rgba(0,0,0,0.06);
  --shadow-elevated: 0 8px 32px rgba(0,0,0,0.12);

  /* Responsive layout tokens — mobile-first defaults (375px+) */
  --app-max-width: 480px;
  --card-gap: 24px;
  --card-padding-x: 16px;
  --card-padding-top: 32px;
  --action-btn-size: 80px;
  --action-btn-gap: 32px;
  --action-btn-emoji: 24px;
  --action-btn-label: 11px;
  --action-bar-pad-top: 12px;
  --action-bar-pad-bottom: 24px;
  --card-area-bottom-pad: 32px;
}

* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
  -webkit-tap-highlight-color: transparent;
}

html {
  height: 100%;
  overflow: hidden;
}

body {
  font-family: 'Sofia Pro', -apple-system, BlinkMacSystemFont, 'SF Pro Text', 'Helvetica Neue', sans-serif;
  background: #080808;
  color: var(--text-primary);
  height: 100dvh;
  overflow: hidden;
  overscroll-behavior: none;
  -webkit-overflow-scrolling: auto;
  position: fixed;
  inset: 0;
  width: 100%;
}

/* ---- Brand Glow ----
   A deliberate brand moment: a bottom-anchored bloom of the phase accent
   (--phase-color: pink in sandbox, purple in copilot) that owns the lower stage
   and reaches up behind the first reply card, filling the negative space the
   opaque cards don't cover.

   Z-STACK (bottom -> top):
     galaxy nebula (#galaxyBg sibling, z-index -2)
     galaxy starfield (#galaxyBg, z-index -1, prepended to <body>)
     .brand-glow (z-index -1, sits AFTER galaxy in DOM -> paints above it)
     app content (header z55, murmur-pulse line z30, cards paint above -1)

   It's its OWN fixed layer sized to the viewport (Decision Log "Full-bleed
   background effects", 2026-06-27) — NOT a ::before on the padded content
   container, whose padding would trap it and leave dead margins. */
.brand-glow {
  position: fixed;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  opacity: 0;                 /* dark until a phase turns it on */
  transition: opacity 600ms ease;
  background: radial-gradient(
    ellipse 140% 90% at 50% 100%,
    color-mix(in srgb, var(--phase-color) 66%, transparent) 0%,
    color-mix(in srgb, var(--phase-color) 44%, transparent) 22%,
    color-mix(in srgb, var(--phase-color) 24%, transparent) 45%,
    color-mix(in srgb, var(--phase-color) 10%, transparent) 66%,
    transparent 86%
  );
}

/* On during sandbox/copilot (body.brand-glow-on toggled in updateChrome).
   Pulse OPACITY only — never transform/scale. Scaling a bottom-anchored
   gradient slides its bands out of position (Decision Log "Full-bleed
   background effects", 2026-06-27); opacity keeps the bloom seated. A slow,
   calm breathe between a fuller and a softer glow — both clearly visible. */
body.brand-glow-on .brand-glow {
  opacity: 1;
  animation: brand-glow-breathe 3600ms ease-in-out infinite;
}

@keyframes brand-glow-breathe {
  0%, 100% { opacity: 1; }
  50%      { opacity: 0.72; }
}

@media (prefers-reduced-motion: reduce) {
  /* No pulse; hold at the fuller glow. */
  body.brand-glow-on .brand-glow {
    animation: none;
    opacity: 1;
  }
}

/* ---- Welcome Screen (pre-sandbox primer) ---- */

.welcome-screen {
  position: fixed;
  inset: 0;
  z-index: 300;
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: 64px 32px 32px;
  padding-top: max(64px, calc(env(safe-area-inset-top) + 48px));
  padding-bottom: max(32px, env(safe-area-inset-bottom));
  background: transparent;
  font-family: 'Sofia Pro', -apple-system, BlinkMacSystemFont, sans-serif;
  color: var(--text-primary);
  opacity: 1;
  transition: opacity 0.4s ease;
}

.welcome-screen.hidden {
  opacity: 0;
  pointer-events: none;
}

.welcome-logo {
  flex-shrink: 0;
  width: 180px;
  max-width: 45%;
  color: var(--text-primary);
  margin-bottom: 18vh;
}

.welcome-logo svg {
  display: block;
  width: 100%;
  height: auto;
}

/* Empty logo placeholder: collapse reserved spacing until a future logo fills it */
.welcome-logo:empty {
  margin-bottom: 0;
}

.welcome-body {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 18px;
  text-align: center;
  max-width: 520px;
}

.welcome-brand-logo {
  display: block;
  width: 200px;
  height: auto;
  aspect-ratio: 376.321 / 234.24;
  margin-bottom: 25px;
  opacity: 1;
}

/* The Murmur EKG draws itself on every fresh load of the welcome screen (and,
   via the pageshow hook in welcome-underline.js, on bfcache back-restores too).
   Inlined + CSS-driven rather than an <img> so the draw fires deterministically
   each time instead of the browser painting a cached, already-finished frame. */
.welcome-ekg {
  stroke-dasharray: 1;
  stroke-dashoffset: 1;
  animation: welcomeEkgDraw 1.75s cubic-bezier(0.6, -0.05, 0.01, 0.99) forwards;
}

@keyframes welcomeEkgDraw {
  from { stroke-dashoffset: 1; }
  to   { stroke-dashoffset: 0; }
}

@media (prefers-reduced-motion: reduce) {
  .welcome-ekg {
    animation: none;
    stroke-dashoffset: 0;
  }
}

.welcome-heading {
  font-family: 'Sofia Pro', sans-serif;
  font-weight: 800;
  font-size: clamp(36px, 8vw, 56px);
  line-height: 1.05;
  letter-spacing: -0.02em;
  color: #FFFFFF;
  margin: 0;
}

.welcome-greeting {
  position: relative;
  font-family: 'Sofia Pro', sans-serif;
  font-weight: 800;
  font-size: clamp(28px, 5vw, 35px);
  line-height: 1.15;
  letter-spacing: -0.01em;
  color: #FFFFFF;
  margin: 0;
}

.welcome-subhead {
  font-family: 'Sofia Pro', sans-serif;
  font-weight: 300;
  font-size: clamp(17px, 4vw, 22px);
  line-height: 1.5;
  color: rgba(245, 245, 245, 0.6);
  margin: 0;
  max-width: 28ch;
}

.welcome-underline {
  position: absolute;
  left: 0;
  bottom: -0.4em;
  width: 100%;
  height: 0.4em;
  z-index: 0;
  pointer-events: none;
  transform: rotate(-1deg);
  transform-origin: left center;

  /* Initial state: fully clipped, invisible */
  clip-path: inset(0 100% 0 0);
  opacity: 1;
}

.welcome-greeting.is-measured .welcome-underline {
  animation:
    welcome-underline-draw 800ms cubic-bezier(0.4, 0, 0.2, 1) 200ms forwards,
    welcome-underline-fade 1400ms ease-out 1600ms forwards;
}

@keyframes welcome-underline-draw {
  0% {
    clip-path: inset(0 100% 0 0);
  }
  100% {
    clip-path: inset(0 0% 0 0);
  }
}

@keyframes welcome-underline-fade {
  0% { opacity: 1; }
  100% { opacity: 0.35; }
}

@media (prefers-reduced-motion: reduce) {
  .welcome-underline { display: none; }
}

.welcome-start {
  margin-top: 48px;
  padding: 18px 35px;
  border: none;
  border-radius: 14px;
  background: #D5347A;
  color: #FFFFFF;
  font-family: 'Sofia Pro', sans-serif;
  font-weight: 800;
  font-size: 22px;
  letter-spacing: 0.01em;
  cursor: pointer;
  box-shadow: 0 4px 18px rgba(213, 52, 122, 0.25);
  transition: transform 0.15s ease, box-shadow 0.2s ease, background 0.2s ease;
}

.welcome-start:hover {
  background: #E14C8D;
  box-shadow: 0 6px 22px rgba(213, 52, 122, 0.35);
}

.welcome-start:active {
  transform: scale(0.97);
}

.welcome-footer {
  margin-top: auto;
  padding-top: 32px;
  font-family: 'Sofia Pro', sans-serif;
  font-weight: 300;
  font-size: 15px;
  color: rgba(245, 245, 245, 0.31);
  letter-spacing: 0.02em;
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: 5px;
}

/* Welcome screen: center the content block in the canvas (equal auto-margin
   slack above the body and below the button), footer pinned at the bottom. */
#welcomeScreen .welcome-body { order: 1; margin-top: auto; }
#welcomeScreen .welcome-start { order: 2; }
#welcomeScreen .welcome-logo {
  order: 3;
  margin-top: auto;
  margin-bottom: 16px;
  width: 140px;
}
#welcomeScreen .welcome-footer {
  order: 4;
  margin-top: 0;
  padding-top: 0;
  /* Match the wrapup footer's type treatment (06 #beat-label): tiny uppercase,
     wide tracking, muted slate at low opacity. Scoped to #welcomeScreen so the
     graduation-beat's "handle it" footer (shares .welcome-footer) is untouched. */
  font-size: 10px;
  font-weight: 400;
  letter-spacing: 1.5px;
  text-transform: uppercase;
  color: #94A3B8;
  opacity: 0.4;
}

/* ---- Sandbox → Copilot Transition (quiet held moment) ---- */

.sandbox-transition {
  position: fixed;
  inset: 0;
  z-index: 290;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: 32px;
  background: #080808;
  opacity: 0;
  transition: opacity 0.4s ease;
}

.sandbox-transition.visible {
  opacity: 1;
}

.sandbox-transition.fading {
  opacity: 0;
  transition: opacity 0.6s ease;
}

.transition-emoji {
  font-size: 48px;
  line-height: 1;
  margin-bottom: 24px;
}

.transition-headline {
  font-family: 'Sofia Pro', sans-serif;
  font-weight: 800;
  font-size: clamp(24px, 6vw, 36px);
  line-height: 1.15;
  color: #FFF8F0;
  text-align: center;
  margin: 0 0 16px;
}

.transition-subline {
  font-family: 'Sofia Pro', sans-serif;
  font-weight: 300;
  font-size: clamp(15px, 3.5vw, 19px);
  line-height: 1.35;
  color: rgba(255, 248, 240, 0.55);
  text-align: center;
  max-width: 28ch;
  margin: 0;
}

.transition-cta {
  margin-top: 48px;
  padding: 18px 48px;
  border: none;
  border-radius: 14px;
  background: var(--ai-purple);
  color: #FFFFFF;
  font-family: 'Sofia Pro', sans-serif;
  font-weight: 700;
  font-size: 20px;
  letter-spacing: 0.01em;
  cursor: pointer;
  box-shadow: 0 4px 18px rgba(167, 139, 250, 0.25);
  transition: transform 0.15s ease, box-shadow 0.2s ease;
}

.transition-cta:active {
  transform: scale(0.97);
}

/* Confidence meter pulse during transition */
.confidence-meter.confidence-pulse .confidence-fill {
  animation: confidencePulse 0.8s ease-in-out 2;
}

@keyframes confidencePulse {
  0%, 100% { box-shadow: 0 0 4px rgba(213, 52, 122, 0.3); }
  50% { box-shadow: 0 0 16px rgba(213, 52, 122, 0.7); }
}

/* ---- Graduation Beat (sandbox -> copilot) ---- */

.graduation-beat {
  position: fixed;
  inset: 0;
  z-index: 290;
  display: none;
  flex-direction: column;
  align-items: center;
  padding: 64px 32px 32px;
  padding-top: max(64px, calc(env(safe-area-inset-top) + 48px));
  padding-bottom: max(32px, env(safe-area-inset-bottom));
  background: #080808;
  font-family: 'Sofia Pro', -apple-system, BlinkMacSystemFont, sans-serif;
  color: var(--text-primary);
  opacity: 0;
  transition: opacity 0.4s ease;
}

.graduation-beat.active {
  display: flex;
  opacity: 1;
}

.graduation-dots {
  display: flex;
  gap: 14px;
  margin-top: 48px;
}

.graduation-dots span {
  width: 12px;
  height: 12px;
  border-radius: 50%;
  background: #D5347A;
  opacity: 0.3;
  animation: graduation-pulse 1.2s ease-in-out infinite;
}

.graduation-dots span:nth-child(2) { animation-delay: 0.2s; }
.graduation-dots span:nth-child(3) { animation-delay: 0.4s; }

@keyframes graduation-pulse {
  0%, 80%, 100% { opacity: 0.3; transform: scale(0.9); }
  40% { opacity: 1; transform: scale(1.15); }
}

.graduation-status {
  min-height: 1.4em;
  transition: opacity 0.25s ease;
}

.graduation-status.fading {
  opacity: 0;
}

/* ---- Phase Frame (viewport border + label) ---- */

.phase-frame {
  position: fixed;
  inset: 0;
  border: none;
  pointer-events: none;
  z-index: 200;
}

.phase-frame-label { display: none; }

/* ---- App Shell ---- */

.app {
  display: flex;
  flex-direction: column;
  height: 100dvh;
  width: 100%;
  overflow: hidden;
}

/* ---- Header ---- */

.header {
  padding: 12px 20px;
  padding-top: max(12px, env(safe-area-inset-top));
  display: flex;
  align-items: center;
  justify-content: space-between;
  background: transparent;
  flex-shrink: 0;
  z-index: 55;
  width: 100%;
  max-width: var(--app-max-width);
  margin: 0 auto;
}

.header-left {
  display: flex;
  flex-direction: column;
  gap: 1px;
}

.header-brand {
  font-size: 15px;
  font-weight: 700;
  color: var(--text-primary);
  display: flex;
  align-items: center;
  gap: 8px;
  background: transparent;
  border: none;
  padding: 0;
  font-family: inherit;
  cursor: pointer;
  transition: opacity 0.15s ease;
}

.header-brand:hover { opacity: 0.75; }
.header-brand:active { opacity: 0.6; }

.brand-logo {
  height: 18.4px;
  width: auto;
  filter: brightness(0) invert(1);
  opacity: 0.65;
}


.header-right {
  display: flex;
  align-items: center;
  gap: 10px;
}

.dev-menu-btn {
  width: 32px;
  height: 32px;
  border-radius: 6px;
  border: none;
  background: transparent;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  color: var(--text-muted);
  position: relative;
  z-index: 60;
}

.dev-menu-btn .hamburger-line {
  display: block;
  width: 18px;
  height: 2px;
  background: currentColor;
  border-radius: 1px;
  transition: transform 0.25s ease, opacity 0.2s ease;
  position: absolute;
}

.dev-menu-btn .hamburger-line:nth-child(1) { transform: translateY(-5px); }
.dev-menu-btn .hamburger-line:nth-child(2) { transform: translateY(0); }
.dev-menu-btn .hamburger-line:nth-child(3) { transform: translateY(5px); }

.dev-menu-btn.active .hamburger-line:nth-child(1) { transform: rotate(45deg); }
.dev-menu-btn.active .hamburger-line:nth-child(2) { opacity: 0; }
.dev-menu-btn.active .hamburger-line:nth-child(3) { transform: rotate(-45deg); }
.dev-menu-btn.active { color: var(--text-primary); z-index: 60; }

/* ---- Voice Confidence Meter ---- */

.confidence-meter {
  padding: 12px 0 0;
  background: transparent;
  flex-shrink: 0;
  width: 100%;
}

.confidence-row {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  max-width: var(--app-max-width);
  margin: 0 auto;
  padding: 0 20px;
  box-sizing: border-box;
}

.murmur-pulse {
  position: fixed;
  bottom: 0;
  left: 0;
  right: 0;
  height: 6px;
  background-color: #D5347A;
  z-index: 30;
  pointer-events: none;

  /* Initial: clipped from both sides toward center (invisible) */
  clip-path: inset(0 50% 0 50%);
  opacity: 1;
  animation:
    murmur-pulse-draw 800ms cubic-bezier(0.4, 0, 0.2, 1) 200ms forwards,
    murmur-pulse-breathe 2400ms ease-in-out 1600ms infinite;
}

.confidence-meter.copilot .murmur-pulse {
  background-color: var(--ai-purple);
}

@keyframes murmur-pulse-draw {
  0% { clip-path: inset(0 50% 0 50%); }
  100% { clip-path: inset(0 0% 0 0%); }
}

@keyframes murmur-pulse-breathe {
  0%   { opacity: 1; }
  50%  { opacity: 0.35; }
  100% { opacity: 1; }
}

@media (prefers-reduced-motion: reduce) {
  .murmur-pulse {
    clip-path: none;
    opacity: 0.35;
    animation: none;
  }

}

.confidence-label {
  font-size: 11px;
  font-weight: 600;
  color: var(--text-muted);
  white-space: nowrap;
}

.confidence-track {
  flex: 1;
  height: 10px;
  background: rgba(255, 255, 255, 0.1);
  border-radius: 5px;
  overflow: hidden;
}

.confidence-fill {
  height: 100%;
  border-radius: 5px;
  background: #D5347A;
  transition: width 0.6s cubic-bezier(0.4, 0, 0.2, 1);
  width: 0%;
}

.confidence-picks {
  font-size: 12px;
  font-weight: 600;
  color: var(--text-muted);
  font-variant-numeric: tabular-nums;
  margin-left: -4px;
}

.confidence-meter.copilot .confidence-picks {
  color: var(--ai-purple);
}

.confidence-pct {
  font-size: 13px;
  font-weight: 700;
  color: #D5347A;
  font-variant-numeric: tabular-nums;
  min-width: 32px;
  text-align: right;
}

.confidence-meter.copilot .confidence-fill {
  background: var(--ai-purple);
}

.confidence-meter.copilot .confidence-pct {
  color: var(--ai-purple);
}

.confidence-meter.copilot .confidence-label {
  color: var(--ai-purple);
}

/* ---- AI Learning Bar (Copilot) — inside action bar ---- */

.ai-learning-bar {
  display: none;
  flex-direction: column;
  gap: 6px;
  width: 100%;
  margin-bottom: 20px;
  flex-shrink: 0;
}

.ai-learning-bar.visible {
  display: flex;
}

.ai-learning-label {
  font-size: 11px;
  font-weight: 600;
  color: var(--ai-purple);
  text-align: center;
  letter-spacing: 0.02em;
}

.ai-learning-track {
  width: 100%;
  height: 6px;
  border-radius: var(--radius-pill);
  background: rgba(167, 139, 250, 0.15);
  overflow: hidden;
}

.ai-learning-fill {
  height: 100%;
  border-radius: var(--radius-pill);
  background: var(--ai-purple);
  transition: width 0.6s cubic-bezier(0.4, 0, 0.2, 1);
}

/* ---- Card Area ---- */

.card-area {
  flex: 1;
  display: flex;
  flex-direction: column;
  justify-content: flex-start;
  padding: var(--card-padding-top) var(--card-padding-x) 0;
  padding-bottom: calc(var(--card-area-bottom-pad) + env(safe-area-inset-bottom));
  overflow-y: auto;
  overflow-x: hidden;
  -webkit-overflow-scrolling: touch;
  width: 100%;
  max-width: var(--app-max-width);
  margin: 0 auto;
}

/* Persistent brand footer for the card phases (sandbox/copilot). EKG mark above
   the copyright, both centered and muted to sit as quiet chrome below the cards.
   flex-shrink:0 keeps it pinned while .card-area (flex:1) scrolls above it. */
.phase-footer {
  flex-shrink: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  width: 100%;
  max-width: var(--app-max-width);
  margin: 0 auto;
  padding: 8px var(--card-padding-x) calc(14px + env(safe-area-inset-bottom));
}

/* Murmur signature mark — 30x19 (376x234 source ratio), white at 35% to match
   Murmur's chrome. Static (no draw-on): this is persistent footer chrome, not a
   reveal beat like the wrapup mark. */
.phase-footer-mark {
  display: block;
  width: 30px;
  height: 19px;
  opacity: 0.35;
}

.phase-footer-mark path {
  stroke: #fff;
}

/* Copyright — mirrors the welcome screen footer: tiny uppercase, wide tracking,
   muted slate at low opacity. */
.phase-footer-version {
  font-family: 'Sofia Pro', -apple-system, BlinkMacSystemFont, sans-serif;
  font-size: 10px;
  font-weight: 400;
  letter-spacing: 1.5px;
  text-transform: uppercase;
  color: #94A3B8;
  opacity: 0.4;
  text-align: center;
}

/* ---- Comment Card ---- */

.comment-card {
  width: 100%;
  display: flex;
  flex-direction: column;
  gap: var(--card-gap);
  animation: cardIn 0.3s cubic-bezier(0.4, 0, 0.2, 1);
  position: relative;
  touch-action: pan-y;
  --swipe-tint: transparent;
}

.comment-bubble-container {
  background: rgba(255, 255, 255, 0.06);
  backdrop-filter: blur(20px);
  -webkit-backdrop-filter: blur(20px);
  border: 1px solid rgba(255, 255, 255, 0.1);
  border-radius: var(--radius);
  box-shadow: 0 4px 24px rgba(0, 0, 0, 0.3);
  overflow: hidden;
}

@keyframes cardIn {
  from { opacity: 0; transform: translateY(12px); }
  to { opacity: 1; transform: translateY(0); }
}

.comment-card.exit {
  animation: cardOut 0.35s cubic-bezier(0.4, 0, 0.2, 1) forwards;
}

@keyframes cardOut {
  to { opacity: 0; transform: translateY(-20px) scale(0.97); }
}

/* Swipe gesture tint overlay */
.comment-card::after {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: var(--radius);
  pointer-events: none;
  z-index: 2;
  background: var(--swipe-tint);
}

/* Directional entry after swipe */
.comment-card.enter-from-left {
  animation: cardInFromLeft 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}

.comment-card.enter-from-right {
  animation: cardInFromRight 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}

@keyframes cardInFromLeft {
  from { opacity: 0; transform: translateX(-40px); }
  to { opacity: 1; transform: translateX(0); }
}

@keyframes cardInFromRight {
  from { opacity: 0; transform: translateX(40px); }
  to { opacity: 1; transform: translateX(0); }
}

/* Live badge (copilot) */
.card-live-badge {
  padding: 8px 16px;
  font-size: 11px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--ai-purple);
  background: transparent;
  display: flex;
  align-items: center;
  gap: 6px;
}

.card-live-badge .live-dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--ai-purple);
  animation: livePulse 1.5s infinite;
}

@keyframes livePulse {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.3; }
}

/* Comment content */
.comment-bubble {
  padding: 16px 16px 12px;
  display: flex;
  gap: 12px;
  align-items: flex-start;
}

.comment-avatar {
  width: 40px;
  height: 40px;
  border-radius: 50%;
  flex-shrink: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 15px;
  font-weight: 600;
  color: white;
  position: relative;
}

.comment-avatar .platform-icon {
  position: absolute;
  bottom: -2px;
  right: -4px;
  width: 18px;
  height: 18px;
  background: white;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  box-shadow: 0 1px 3px rgba(0,0,0,0.15);
}

.comment-avatar .platform-icon img {
  width: 12px;
  height: 12px;
}

.comment-body { flex: 1; min-width: 0; }

.comment-username {
  font-size: 14px;
  font-weight: 600;
  color: var(--text-primary);
  margin-bottom: 3px;
}

.comment-text {
  font-size: 16px;
  line-height: 1.45;
  color: var(--text-primary);
}

/* ---- Reply Options (Sandbox) ---- */

.reply-options {
  display: flex;
  flex-direction: column;
  gap: 20px;
}

/* Section-label row: the "CHOOSE YOUR REPLY" / "✦ AI-DRAFTED REPLIES" label on
   the left, the persistent phase disclaimer pinned to the right on the same
   baseline. At narrow widths the disclaimer wraps below the label (still in this
   gap, still right-aligned) rather than colliding with it. */
.reply-options-header {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 6px;
}

.reply-options-label {
  font-size: 11px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--text-muted);
  padding: 0;
}

/* Persistent phase disclaimer, left-aligned and stacked directly under the
   section label (the header is now a column). Was a right-pinned item on the
   label's line; now it reads as a second line beneath the instruction. Emoji +
   text + white color unchanged. */
.phase-note-inline {
  text-align: left;
  font-size: 13px;
  font-weight: 500;
  line-height: 1.3;
  color: #FFFFFF;
}

.reply-option {
  width: 100%;
  padding: 20px 20px;
  border-radius: var(--radius);
  border: 1.5px solid rgba(255, 255, 255, 0.12);
  background: rgba(255, 255, 255, 0.06);
  backdrop-filter: blur(20px);
  -webkit-backdrop-filter: blur(20px);
  box-shadow: 0 2px 12px rgba(0, 0, 0, 0.15);
  display: grid;
  grid-template-columns: 1fr;
  align-items: center;
  transition: all 0.15s ease;
  text-align: left;
  font-family: inherit;
  color: var(--text-primary);
}

.reply-option:active {
  transform: scale(0.98);
}

/* Desktop grab affordance. The instruction copy says "click and drag" on fine
   pointers, so the cursor has to agree with it. Body class is set for the life of
   an active drag, which also suppresses text selection across the reply copy. */
@media (hover: hover) and (pointer: fine) {
  .reply-option { cursor: grab; }
  .reply-option:active { cursor: grabbing; }
  .reply-option .reply-option-pencil { cursor: pointer; }
}

body.is-dragging-row {
  user-select: none;
  -webkit-user-select: none;
  cursor: grabbing;
}

/* One gesture, one pick: the stack stops taking input from the moment a swipe
   commits until the next card mounts. Also blocks the pencil, which shouldn't be
   reachable on a row that's already on its way out. */
.reply-options.beat-locked { pointer-events: none; }

/* Confirmation beat. Sits in the footprint the swiped row just vacated and holds
   for CONFIRM_HOLD before the next card mounts, so the action gets a receipt in
   the place it happened. No border, no fill — the empty space IS the message, and
   a box there would read as another live card competing with the one still up.
   Off-white for both outcomes: this is a receipt, not a judgement, and green/red
   would put a moral weight on "dismissed" that the gesture doesn't carry. */
.swipe-confirm {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  padding: 20px;
  color: rgba(245, 245, 245, 0.72);
  font-size: 13px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  /* Entry is a keyframe, not a class-toggled transition. A toggle would have to be
     scheduled off requestAnimationFrame, which is paused in a backgrounded tab —
     the beat would then hold its full duration at opacity 0 and the user would
     see nothing but a gap. An animation runs on its own clock, so the worst case
     is that it plays unseen rather than never playing. */
  animation: swipe-confirm-in 0.18s cubic-bezier(0.33, 0, 0.2, 1) both;
}

/* The label is centered for both outcomes; the only thing the direction still
   drives is which side it drifts in from, which echoes the gesture without
   re-stating it. */
.swipe-confirm.approved  { --confirm-from: 10px; }
.swipe-confirm.dismissed { --confirm-from: -10px; }

/* Drifts in from the side the gesture went, settling centered. */
@keyframes swipe-confirm-in {
  from { opacity: 0; transform: translateX(var(--confirm-from, 0px)); }
  to   { opacity: 1; transform: translateX(0); }
}

@media (prefers-reduced-motion: reduce) {
  .swipe-confirm { --confirm-from: 0px; }
}

/* AI DRAFT pill — 06's `.ai-tag` badge, matched property for property. Every
   value below is 06's; the ONE difference is that the colour derives from
   --phase-color instead of being hard-coded violet, so the pill carries the
   sandbox magenta and the copilot indigo in turn. Copilot's --phase-color IS
   #A78BFA, the same token 06 uses, so the copilot pill's fill and border come
   out byte-identical to 06's badge.
   BOTH cards carry it, sandbox and copilot alike: both are AI drafts and they
   share one signature. No A/B letters — two stacked cards with their own
   controls already read as two options. The body is a flex column, so
   align-self:flex-start makes the pill hug its text instead of stretching. */
.reply-option-label {
  align-self: flex-start;
  font-size: 9px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.8px;
  color: color-mix(in srgb, var(--phase-color) 40%, white);
  background: color-mix(in srgb, var(--phase-color) 22%, transparent);
  border: 1px solid color-mix(in srgb, var(--phase-color) 55%, transparent);
  border-radius: 8px;
  padding: 3px 8px;
  margin-bottom: 4px;
  white-space: nowrap;            /* the badge is one unit — never break "AI DRAFT ✦" */
}

/* Decorative. It also opts out of the uppercase tracking: the trailing 0.8px
   would land inside the pill's right padding and throw the badge off balance. */
.reply-option-label-sparkle {
  letter-spacing: 0;
  margin-left: 1px;
}

.reply-option-text {
  font-size: 14px;
  line-height: 1.4;
  color: var(--text-primary);
  /* flow-root contains the right-floated EDIT (.reply-option-tools) so that,
     when a long last line forces EDIT to drop to its own line, the card grows
     to hold it instead of the float spilling over the next card. Curated drafts
     top out ~5 lines, so the old 8-line clamp is retired here. */
  display: flow-root;
}

.reply-option.editing .reply-option-text {
  display: block;
  -webkit-line-clamp: unset;
  overflow: visible;
}

.reply-option-body {
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 4px;
}

/* EDIT tools now ride the last line of the draft text: floated right, so the
   last line flows ragged-left and EDIT pins to the text column's right edge on
   the same row. If the last line is too long to share the row, the float drops
   to its own line (collision fallback), staying inside the flow-root text box. */
.reply-option-tools {
  float: right;
  display: inline-flex;
  align-items: center;
  gap: 12px;
  margin-left: 14px;
}

.reply-option-more,
.reply-option-pencil {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 2px 0;
  /* Tap target held at its old height. The mark shrank from an 18px Material
     icon to 06's 12px ✎, but the hit area must not shrink with it — this is the
     old (18px icon + 2px x 2 padding) box, with the content re-centring inside. */
  min-height: 22px;
  font-family: inherit;
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.02em;
  color: rgba(255, 255, 255, 0.5);
  background: transparent;
  border: none;
  cursor: pointer;
  line-height: 1;
}

.reply-option-more:hover,
.reply-option-pencil:hover {
  color: rgba(255, 255, 255, 0.85);
}

/* Edit mark — one Unicode glyph (✎, U+270E) in the app font, matching 06's
   per-draft control (.ai-draft-edit .btn-icon). No icon-font dependency, and it
   sits AFTER the label rather than before it, so the control reads EDIT ✎.
   Sized to the label: one type size, one optical weight. */
.reply-option-pencil .btn-icon {
  font-size: 12px;
  line-height: 1;
}

.reply-option-pencil-label {
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

/* Bottom sheet for full reply text */
.reply-sheet-backdrop {
  position: fixed;
  inset: 0;
  z-index: 99;
  background: rgba(0, 0, 0, 0.5);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.2s ease;
}

.reply-sheet-backdrop.open {
  opacity: 1;
  pointer-events: auto;
}

.reply-sheet {
  position: fixed;
  bottom: 0;
  left: 0;
  right: 0;
  z-index: 100;
  max-width: var(--app-max-width);
  margin: 0 auto;
  background: #141414;
  border-top-left-radius: 22px;
  border-top-right-radius: 22px;
  padding: 12px 24px 24px;
  padding-bottom: calc(24px + env(safe-area-inset-bottom));
  max-height: 70vh;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  transform: translateY(100%);
  transition: transform 0.28s cubic-bezier(0.4, 0, 0.2, 1);
  box-shadow: 0 -12px 40px rgba(0, 0, 0, 0.5);
  touch-action: pan-y;
}

.reply-sheet.open {
  transform: translateY(0);
}

.reply-sheet-handle {
  width: 36px;
  height: 4px;
  border-radius: 2px;
  background: rgba(255, 255, 255, 0.18);
  margin: 0 auto 16px;
}

/* Original comment context — read-only, the post the reply answers. Kept
   legible (not too dim) so the user can genuinely compare while editing. */
.reply-sheet-context {
  background: rgba(255, 255, 255, 0.04);
  border: 1px solid rgba(255, 255, 255, 0.1);
  border-radius: 12px;
  padding: 10px 12px;
  margin-bottom: 16px;
}
.reply-sheet-context-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 8px;
  margin-bottom: 5px;
}
.reply-sheet-context-user { font-size: 12px; font-weight: 700; color: var(--text-primary); }
.reply-sheet-context-text { font-size: 13px; line-height: 1.5; color: rgba(255, 255, 255, 0.72); }

/* Both blocks in the sheet are labelled the same way (06 addendum 56): outside
   the container, above it, on the same left rail. The reply label carries the
   phase color — sandbox magenta by default, copilot indigo scoped below. */
.reply-sheet-label {
  font-size: 10px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: #D5347A;
  margin-bottom: 8px;
}
/* The ONE thing that must not carry across is the AI signature: the original
   comment goes muted (and the markup omits the ✦). A human's words never wear
   the AI author mark. */
.reply-sheet-label.plain {
  color: var(--text-muted);
}

.reply-sheet.ai-drafted .reply-sheet-label:not(.plain) {
  color: var(--ai-purple);
}

.reply-sheet-text {
  font-size: 15px;
  line-height: 1.5;
  color: var(--text-primary);
  width: 100%;
  min-height: 140px;
  background: rgba(255, 255, 255, 0.04);
  border: 1px solid rgba(255, 255, 255, 0.12);
  border-radius: 12px;
  padding: 12px 14px;
  font-family: inherit;
  resize: vertical;
  outline: none;
  box-sizing: border-box;
  transition: border-color 0.15s ease;
}

/* Focus ring tracks the phase's CTA color: sandbox pink by default, copilot
   AI-purple (scoped below). */
.reply-sheet-text:focus {
  border-color: #D5347A;
}

.reply-sheet.ai-drafted .reply-sheet-text:focus {
  border-color: var(--ai-purple);
}

.reply-sheet-actions {
  display: flex;
  gap: 10px;
  margin-top: 14px;
}

.reply-sheet-btn {
  flex: 1;
  padding: 13px 16px;
  border-radius: 12px;
  border: none;
  font-family: inherit;
  font-size: 15px;
  font-weight: 700;
  cursor: pointer;
  letter-spacing: 0.01em;
  transition: transform 0.1s ease, background 0.2s ease;
}

.reply-sheet-btn:active {
  transform: scale(0.98);
}

.reply-sheet-btn.close {
  background: rgba(255, 255, 255, 0.06);
  color: var(--text-primary);
}

.reply-sheet-btn.save {
  background: #D5347A;
  color: #FFFFFF;
}

/* Copilot (AI-drafted) edit sheet: Save carries the AI-purple accent instead of
   the sandbox pink. */
.reply-sheet.ai-drafted .reply-sheet-btn.save {
  background: var(--ai-purple);
}

/* Save starts disabled in the copilot sheet until the user taps into the draft
   (openReplySheet + the textarea focus/input listener manage the state). */
.reply-sheet-btn.save:disabled {
  opacity: 0.4;
  cursor: not-allowed;
  transform: none;
}

/* Inline edit mode (Tweak) */
.reply-edit-field {
  width: 100%;
  font-family: inherit;
  font-size: 14px;
  line-height: 1.4;
  color: var(--text-primary);
  background: transparent;
  border: none;
  outline: none;
  resize: none;
  overflow: hidden;
}

.reply-option.editing {
  border-color: var(--tweak);
  background: rgba(213, 52, 122, 0.08);
  border-style: dashed;
}

.reply-edit-confirm {
  display: none;
  margin-top: 10px;
  padding: 10px 18px;
  border-radius: var(--radius-pill);
  background: var(--tweak);
  color: white;
  font-family: inherit;
  font-size: 13px;
  font-weight: 600;
  cursor: pointer;
  text-align: center;
  -webkit-user-select: none;
  user-select: none;
}

.reply-edit-confirm:active {
  transform: scale(0.96);
}

.reply-option.editing .reply-edit-confirm {
  display: inline-block;
}

/* ---- AI Draft (Copilot) ---- */

.ai-draft {
  background: rgba(255, 255, 255, 0.06);
  backdrop-filter: blur(20px);
  -webkit-backdrop-filter: blur(20px);
  border: 1px solid rgba(255, 255, 255, 0.1);
  border-radius: var(--radius);
  box-shadow: 0 2px 12px rgba(0, 0, 0, 0.15);
  padding: 14px 16px;
}

.ai-draft-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0 0 8px;
}

.ai-draft-label {
  font-size: 11px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--ai-purple);
}

.ai-draft-confidence {
  font-size: 11px;
  font-weight: 600;
  padding: 2px 8px;
  border-radius: var(--radius-pill);
  background: var(--ai-purple-dim);
  color: var(--ai-purple);
}

.ai-draft-text {
  font-size: 14px;
  line-height: 1.45;
  color: var(--text-primary);
  padding: 14px 16px;
  background: rgba(255, 255, 255, 0.06);
  border-radius: var(--radius);
  border: 1.5px solid rgba(255, 255, 255, 0.12);
}

.ai-draft-edit {
  display: block;
  width: 100%;
  font-family: inherit;
  font-size: 14px;
  line-height: 1.45;
  color: var(--text-primary);
  padding: 14px 14px 14px 16px;
  background: rgba(213, 52, 122, 0.08);
  border-radius: var(--radius-sm);
  border: none;
  border-left: 3px solid var(--tweak);
  outline: none;
  resize: none;
  overflow: hidden;
  box-sizing: border-box;
}

.ai-draft-sent {
  font-size: 14px;
  line-height: 1.45;
  color: var(--text-primary);
  padding: 14px;
  background: var(--phase3-light);
  border-radius: var(--radius-sm);
  border: 1.5px solid rgba(78, 205, 196, 0.3);
  display: flex;
  align-items: flex-start;
  gap: 8px;
}

.ai-draft-sent .sent-check {
  color: var(--phase3);
  font-size: 16px;
  flex-shrink: 0;
}

/* ---- Action Buttons (fixed bottom, revealed on selection) ---- */

.action-bar {
  position: fixed;
  bottom: 0;
  left: 0;
  right: 0;
  z-index: 40;
  background: transparent;
  border-top: none;
  padding: var(--action-bar-pad-top) var(--card-padding-x) var(--action-bar-pad-bottom);
  padding-bottom: calc(var(--action-bar-pad-bottom) + env(safe-area-inset-bottom));
  max-width: var(--app-max-width);
  margin: 0 auto;
  transform: translateY(100%);
  transition: transform 0.25s cubic-bezier(0.4, 0, 0.2, 1);
  display: flex;
  flex-direction: column;
  align-items: center;
}

.action-bar.visible {
  transform: translateY(0);
}

.action-pick-counter {
  text-align: center;
  font-size: 12px;
  color: var(--text-muted);
  font-variant-numeric: tabular-nums;
  margin-top: 22px;
}

.action-pick-counter strong {
  color: #FFFFFF;
}

.action-pick-progress {
  margin: 10px auto 0;
  max-width: 180px;
  height: 6px;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.08);
  overflow: hidden;
}

.action-pick-progress-fill {
  height: 100%;
  background: #D5347A;
  border-radius: inherit;
  transition: width 0.4s cubic-bezier(0.4, 0, 0.2, 1);
}

.action-buttons {
  display: flex;
  gap: var(--action-btn-gap);
  justify-content: center;
  align-items: center;
  flex-shrink: 0;
  width: 100%;
}

.action-btn {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  width: var(--action-btn-size);
  height: var(--action-btn-size);
  border-radius: 50%;
  border: none;
  cursor: pointer;
  font-family: inherit;
  font-size: var(--action-btn-label);
  font-weight: 600;
  gap: 2px;
  transition: all 0.15s ease;
  box-shadow: var(--shadow-card);
}

.action-btn .action-emoji {
  font-size: var(--action-btn-emoji);
  line-height: 1;
}

.action-btn .action-label {
  font-size: var(--action-btn-label);
  font-weight: 600;
  letter-spacing: 0.02em;
}

.action-btn:active { transform: scale(0.95); }

.action-btn:disabled {
  opacity: 1;
}


.btn-nah {
  background: rgba(255, 255, 255, 0.08);
  color: var(--text-secondary);
}

.btn-tweak {
  background: rgba(213, 52, 122, 0.15);
  color: #D5347A;
}

.btn-thatsus {
  background: var(--thatsus);
  color: white;
}

/* ---- Hover States (desktop) ---- */

@media (hover: hover) {
  .action-btn:hover:not(:disabled) {
    transform: translateY(-1px);
    box-shadow: 0 3px 10px rgba(0,0,0,0.08);
  }

  .btn-nah:hover:not(:disabled) {
    background: rgba(255, 255, 255, 0.14);
    color: var(--text-primary);
  }

  .btn-tweak:hover:not(:disabled) {
    background: rgba(213, 52, 122, 0.25);
  }

  .btn-thatsus:hover:not(:disabled) {
    background: #16A34A;
  }

  .reply-option:hover {
    border-color: rgba(255, 255, 255, 0.25);
    background: rgba(255, 255, 255, 0.08);
  }

  .dev-menu-btn:hover {
    border-color: rgba(255,255,255,0.3);
    color: var(--text-primary);
  }

  .dev-btn:hover {
    border-color: rgba(255,255,255,0.25);
    color: var(--text-primary);
    background: rgba(255,255,255,0.1);
  }

  .dev-panel-close:hover {
    background: rgba(255,255,255,0.15);
    color: var(--text-primary);
  }

  .phase-transition-btn:hover {
    opacity: 0.9;
    transform: translateY(-1px);
  }

  .vitals-alert-action:hover {
    text-decoration: underline;
    cursor: pointer;
  }

  .feedback-reason:hover {
    border-color: rgba(255,255,255,0.2);
    background: rgba(255,255,255,0.06);
  }
}

/* ---- Footer (inside toolbar) ---- */

.phase-footer {
  text-align: center;
  padding-top: 8px;
}

.phase-footer-label {
  font-size: 10px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--phase-color);
  transition: color 0.4s ease;
}

.phase-footer-sub {
  font-size: 10px;
  color: var(--text-muted);
  margin-top: 1px;
}

/* ---- Vitals Dashboard (Phase 3) ---- */

.vitals-screen {
  display: none;
  position: fixed;
  inset: 0;
  z-index: 200;
  background: transparent;
  align-items: center;
  justify-content: center;
  padding: 32px;
}

.vitals-screen.active { display: flex; }

.vitals-placeholder {
  color: #FFF8F0;
  font-family: 'Sofia Pro', -apple-system, BlinkMacSystemFont, sans-serif;
  font-size: 22px;
  font-weight: 300;
  line-height: 1.4;
  text-align: center;
  width: 100%;
  height: 100%;
  position: relative;
}

.vitals-placeholder-center {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 56px;
  width: 100%;
}

.orbital-map {
  width: clamp(260px, 68vw, 460px);
  aspect-ratio: 1 / 1;
  opacity: 0;
  animation: orbital-emerge 1.6s ease 0.2s forwards;
}

.orbital-map svg {
  display: block;
  width: 100%;
  height: 100%;
  overflow: visible;
}

.orbital-tilt {
  transform-origin: 300px 300px;
  transform-box: fill-box;
  transform: scaleY(0.62);
}

.orbit {
  transform-origin: 300px 300px;
  transform-box: fill-box;
  animation: orbit-spin 45s linear infinite;
}

.orbit-path {
  fill: none;
  stroke: rgba(255, 248, 240, 0.07);
  stroke-width: 1;
}

.satellite {
  fill: rgba(225, 220, 230, 0.78);
}

.orbital-center {
  fill: rgba(255, 248, 240, 0.95);
  filter: drop-shadow(0 0 14px rgba(255, 248, 240, 0.22));
}

@keyframes orbit-spin {
  to { transform: rotate(360deg); }
}

@keyframes orbital-emerge {
  to { opacity: 1; }
}

.vitals-foot {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 18px;
}

.vitals-brand-health {
  font-family: 'Sofia Pro', -apple-system, BlinkMacSystemFont, sans-serif;
  font-size: 36px;
  font-weight: 700;
  letter-spacing: -0.01em;
  line-height: 1;
  color: rgba(255, 248, 240, 0.95);
  margin: 0;
  opacity: 0;
  animation: orbital-emerge 1.4s ease 1.1s forwards;
}

@media (prefers-reduced-motion: reduce) {
  .orbit { animation: none; }
  .orbital-map,
  .vitals-brand-health {
    animation: none;
    opacity: 1;
  }
}

/* ---- Wrap Screen (customer-tied brand orbit) ---- */

.wrap-screen {
  display: none;
  position: fixed;
  inset: 0;
  z-index: 200;
  background: transparent;
  align-items: center;
  justify-content: center;
  padding: 32px;
}

.wrap-screen.active { display: flex; }

.wrap-placeholder {
  color: #FFF8F0;
  font-family: 'Sofia Pro', -apple-system, BlinkMacSystemFont, sans-serif;
  text-align: center;
  width: 100%;
  height: 100%;
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: space-between;
  padding: clamp(24px, 5vh, 56px) 24px clamp(20px, 3vh, 40px);
}

.wrap-footer {
  position: relative;
  z-index: 2;
}

/* Wrap screen atmosphere — a scoped replica of 06-murmur-brand-health's sky
   (#sky-layer): 06's exact #0D1117 floor + gradient glow, with DOM stars
   injected by app.js (renderWrapStars). Scoped to the wrap screen so the global
   #080808 WebGL galaxy stays untouched. It's opaque, so it occludes the global
   galaxy only while the wrap frame is up — that's what lets the #0D1117 floor be
   scoped here without a global change. */
.wrap-sky {
  position: fixed;
  inset: 0;
  z-index: 0;
  overflow: hidden;
  pointer-events: none;
  background:
    radial-gradient(circle at 22% 28%, rgba(120, 132, 165, 0.08), transparent 62%),
    radial-gradient(circle at 78% 58%, rgba(90, 100, 130, 0.06), transparent 55%),
    radial-gradient(circle at 50% 85%, rgba(70, 78, 105, 0.05), transparent 50%),
    #0D1117;
}
.wrap-sky .star {
  position: absolute;
  border-radius: 50%;
  opacity: 0;
  will-change: opacity;
}
.wrap-sky .star.on {
  animation: wrapStarReveal 800ms ease-out forwards,
             wrapTwinkle var(--tw, 3s) ease-in-out infinite 800ms;
}
@keyframes wrapStarReveal { from { opacity: 0; } to { opacity: var(--so, 0.6); } }
@keyframes wrapTwinkle {
  0%, 100% { opacity: var(--so, 0.6); }
  50%      { opacity: calc(var(--so, 0.6) * 0.35); }
}

/* L'Oréal wordmark as top-left chrome — 06 .brand-mark (body.brand-loreal):
   real white asset, low opacity, monitoring context. Not centered in the orbit.
   Doubles as Home (restarts at the welcome screen), so it's a real button. */
.wrap-brand-chrome {
  position: fixed;
  top: calc(env(safe-area-inset-top, 0px) + 16px);
  left: calc(env(safe-area-inset-left, 0px) + 18px);
  z-index: 3;
  width: 92px;
  height: 24px;
  padding: 0;
  border: none;
  background: url('img/brand-logo-loreal-white.svg') left center / contain no-repeat;
  opacity: 0.82;
  cursor: pointer;
  transition: opacity 150ms ease;
}
.wrap-brand-chrome:hover { opacity: 1; }
.wrap-brand-chrome:focus-visible {
  outline: 2px solid rgba(255, 255, 255, 0.7);
  outline-offset: 6px;
  opacity: 1;
}

/* Centered reassurance — sits at the orbit center like 06 #headline.centered,
   the healthy bubbles ring it. 06 type tokens exactly (line 21px/600/0.2px,
   sub 13px muted). 05's kept brand-level copy, no underline. */
.wrap-center {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 200px;
  transform: translate(-50%, -50%);
  text-align: center;
  z-index: 1;
  pointer-events: none;
  opacity: 0;
  animation: orbital-emerge 1.4s ease 0.4s forwards;
}
.wrap-center-line {
  font-family: 'Sofia Pro', -apple-system, BlinkMacSystemFont, sans-serif;
  font-size: 21px;
  font-weight: 600;
  letter-spacing: 0.2px;
  line-height: 1.25;
  color: #F9FAFB;
}
.wrap-center-sub {
  font-family: 'Sofia Pro', -apple-system, BlinkMacSystemFont, sans-serif;
  font-size: 13px;
  line-height: 1.5;
  color: #94A3B8;
  margin: 8px auto 0;
  max-width: 180px;
}

/* The stage is the region above the footer. The map + headline center inside it
   (not the whole viewport), and container-type: size lets the orbit be sized to
   the stage via cq units — so the bubbles always clear the footer, on any surface. */
.wrap-stage {
  position: relative;
  flex: 1 1 auto;
  width: 100%;
  min-height: 0;
  container-type: size;
}

.orbital-map--wrap {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  /* Horizontal reach is sized off the viewport (90vw), not the padded stage, so
     the orbit reclaims the side room that .wrap-screen / .wrap-placeholder padding
     otherwise wastes — the map is absolutely centered, so it can overflow the
     stage horizontally and stay symmetric. The vertical term (92cqh) still caps it
     to the stage height so the orbit never reaches the footer. vw/vh line is the
     fallback where container units aren't supported. */
  width: min(560px, 90vw, 88vh);
  width: min(560px, 90vw, 92cqh);
  aspect-ratio: 1 / 1;
  flex-shrink: 0;
  opacity: 0;
  animation: orbital-emerge 1.6s ease 0.2s forwards;
  z-index: 1;
  pointer-events: none;
}

.wrap-footer {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: clamp(8px, 1.4vh, 18px);
  width: 100%;
  /* The footer is the only in-flow child now (sky, chrome, map and the
     centered headline are all positioned out of flow), so push it to the
     bottom of the column rather than letting it park at the top. */
  margin-top: auto;
}

.wrap-footer .wrap-version,
.wrap-footer .vitals-placeholder-logo {
  position: static;
  transform: none;
  inset: auto;
}

/* Footer signature mark — the draw-on Murmur EKG, inlined so a CSS animation-delay
   can hold it undrawn while the composition emerges, then draw it on as the closing
   beat. White at 35% to match Murmur's chrome. 30x19 (376x234 source ratio),
   centered directly above the copyright. Doubles as the restart control here, so
   it keeps pointer events and the hover/active fade (base 0.35 lives on the button
   so hover brightens it, not the mark). */
.wrap-footer .wrap-logo-btn {
  width: 30px;
  height: 19px;
  opacity: 0.35;
  position: relative;   /* anchors the expanded hit area below */
}

/* Demo re-fire affordance: 30x19 mark is too thin to tap on mobile, so expand
   the clickable area to ~54x45 with an invisible overlay. No layout shift (the
   mark stays 30x19); clicks in the padded zone still resolve to the button. */
.wrap-footer .wrap-logo-btn::after {
  content: "";
  position: absolute;
  inset: -13px -12px;
}

.wrap-logo-mark {
  display: block;
  width: 100%;
  height: 100%;
}

/* The mark sits undrawn (dasharray/offset 1 = fully hidden) from mount, holds
   through the delay, then draws over 1.75s — firing ~1.8s in, once the orbit
   (0.2s+1.6s) and headline (0.4s+1.4s) have settled. 'both' fill keeps it hidden
   during the delay and drawn after. Heartbeat easing matches the source SVG. */
.wrap-logo-ekg {
  stroke: #fff;
  stroke-dasharray: 1;
  stroke-dashoffset: 1;
  animation: wrapLogoDraw 1.75s cubic-bezier(0.6, -0.05, 0.01, 0.99) 1.8s both;
}

@keyframes wrapLogoDraw {
  from { stroke-dashoffset: 1; }
  to   { stroke-dashoffset: 0; }
}

@media (prefers-reduced-motion: reduce) {
  .wrap-logo-ekg {
    animation: none;
    stroke-dashoffset: 0;
  }
}

.orbital-map--wrap svg {
  display: block;
  width: 100%;
  height: 100%;
  overflow: visible;
}

.brand-orbit {
  /* Rotate around the viewBox center (300,300), not the formation's bbox corner.
     With transform-box: fill-box the 300,300 origin was measured from the group's
     bounding box, which sits off-center, so the whole cluster swung in a circle
     (drifting right). view-box anchors the pivot to the true center = the copy's
     center, so it rotates in place. */
  transform-origin: 300px 300px;
  transform-box: view-box;
  animation: orbit-spin var(--orbit-duration, 50s) linear infinite;
  animation-direction: var(--orbit-direction, normal);
}

/* One formation, drifting as a single rigid body — 06's orbitDrift/orbitCounter
   pace exactly: 180s per revolution, layer normal + bubbles counter so the icons
   and the X glyph stay upright. */
.brand-orbit--calm { --orbit-duration: 180s; --orbit-direction: normal; --counter-direction: reverse; }

.brand-satellite-counter {
  transform-origin: center;
  transform-box: fill-box;
  animation: orbit-spin var(--orbit-duration, 50s) linear infinite;
  animation-direction: var(--counter-direction, reverse);
}

/* Channel bubbles — 06 calm read (its sentColor for a Cool/low-negative
   channel): Cool blue #3B82F6 with a soft same-hue glow (06 uses
   box-shadow 0 0 12px rgba(59,130,246,0.28); drop-shadow is the SVG
   equivalent). Full opacity, white platform icon. No rings. Per-bubble
   radius lives on each <circle> so the channel sizes vary like 06's. */
.orbital-map--wrap .brand-satellite {
  opacity: 1;
}
.orbital-map--wrap .satellite-body {
  fill: #3B82F6;
  stroke: none;
  filter: drop-shadow(0 0 7px rgba(59, 130, 246, 0.45));
}

/* X has no committed icon in 06 — it renders as a white text glyph. */
.orbital-map--wrap .wrap-sat-label {
  font-family: 'Sofia Pro', -apple-system, BlinkMacSystemFont, sans-serif;
  font-size: 15px;
  font-weight: 600;
  fill: #FFFFFF;
  user-select: none;
  pointer-events: none;
}


/* Platform logos on the brand-orbit satellites — the same monochrome read as
   06's mask + white-fill treatment.

   These used to be external colored SVGs loaded via <image> and flattened with
   filter: brightness(0) invert(1). That works in desktop Chrome but not on iOS
   Safari, where saturated P3 brand colors (Instagram #E1306C, YouTube #FF0000,
   Facebook #1877F2) survive brightness(0) and come through tinted — the logos
   read colored on touch devices only. The markup is now inlined in app.js as
   real SVG geometry, so white is a paint value rather than a filter result and
   every surface renders the same. Don't reintroduce <image href> here. */
.wrap-sat-icon {
  pointer-events: none;
  user-select: none;
  fill: #FFFFFF;
  stroke: #FFFFFF;
}

@media (prefers-reduced-motion: reduce) {
  .brand-orbit,
  .brand-satellite-counter {
    animation: none;
  }
  .orbital-map--wrap,
  .wrap-center {
    animation: none;
    opacity: 1;
  }
  /* Stars fade in via animation; with motion reduced, show them at rest
     opacity instead of leaving them stranded at 0. */
  .wrap-sky .star {
    animation: none;
    opacity: var(--so, 0.6);
  }
}

/* ---- Shared bottom wordmark ---- */

.vitals-placeholder-logo {
  position: absolute;
  bottom: 0;
  left: 50%;
  transform: translateX(-50%);
  width: 180px;
  color: var(--text-primary);
}

button.vitals-placeholder-logo {
  background: none;
  border: none;
  padding: 0;
  display: block;
  cursor: pointer;
  transition: opacity 0.3s ease;
}

button.vitals-placeholder-logo:hover {
  opacity: 0.7;
}

button.vitals-placeholder-logo:active {
  opacity: 0.55;
}

.wrap-version {
  position: absolute;
  bottom: 70px;
  left: 50%;
  transform: translateX(-50%);
  /* Type treatment mirrors 06-murmur-brand-health #beat-label at 10px:
     uppercase, 1.5px tracking, muted slate at 0.4 opacity. */
  font-family: 'Sofia Pro', -apple-system, BlinkMacSystemFont, sans-serif;
  font-size: 10px;
  letter-spacing: 1.5px;
  text-transform: uppercase;
  color: #94A3B8;
  opacity: 0.4;
  white-space: nowrap;
  text-align: center;
  line-height: 1.5;
  margin: 0;
  pointer-events: none;
}

.vitals-placeholder-logo svg {
  display: block;
  width: 100%;
  height: auto;
}

.vitals-health-ring {
  width: 160px;
  height: 160px;
  border-radius: 50%;
  background: rgba(78, 205, 196, 0.08);
  border: 4px solid var(--phase3);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  margin: 8px auto;
  box-shadow: 0 0 0 10px rgba(78, 205, 196, 0.06);
}

.vitals-health-label {
  font-size: 22px;
  font-weight: 700;
  color: var(--phase3);
}

.vitals-health-sub {
  font-size: 11px;
  color: var(--text-secondary);
  margin-top: 2px;
}

.vitals-channels {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 10px;
}

.vitals-channel {
  background: rgba(255,255,255,0.06);
  border: 1px solid rgba(255,255,255,0.08);
  border-radius: var(--radius-sm);
  padding: 14px;
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.vitals-channel-header {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: 12px;
  font-weight: 600;
  color: var(--text-primary);
  text-transform: capitalize;
}

.vitals-channel-header img {
  width: 14px;
  height: 14px;
}

.vitals-channel-stat {
  font-size: 11px;
  color: var(--text-muted);
}

.vitals-channel-stat strong {
  color: var(--text-primary);
}

.vitals-status-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  flex-shrink: 0;
}

.vitals-status-dot.healthy { background: var(--phase3); }
.vitals-status-dot.concern { background: #D5347A; }
.vitals-status-dot.critical { background: #EF4444; }

/* Alert Cards */
.vitals-alerts {
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.vitals-alert {
  background: rgba(255,255,255,0.06);
  border: 1px solid rgba(255,255,255,0.08);
  border-radius: var(--radius-sm);
  padding: 14px;
  display: flex;
  gap: 10px;
  align-items: flex-start;
}

.vitals-alert-body { flex: 1; }

.vitals-alert-title {
  font-size: 14px;
  font-weight: 600;
  color: var(--text-primary);
  margin-bottom: 3px;
  display: flex;
  align-items: center;
  gap: 6px;
}

.vitals-alert-title img {
  width: 14px;
  height: 14px;
}

.vitals-alert-desc {
  font-size: 12px;
  color: var(--text-secondary);
  line-height: 1.4;
  margin-bottom: 6px;
}

.vitals-alert-status {
  font-size: 11px;
  color: var(--text-muted);
}

.vitals-alert-action {
  font-size: 12px;
  font-weight: 600;
  color: var(--phase3);
  white-space: nowrap;
  align-self: center;
}

/* Recent auto-replies */
.vitals-recent {
  background: rgba(255,255,255,0.06);
  border: 1px solid rgba(255,255,255,0.08);
  border-radius: var(--radius);
  padding: 14px;
}

.vitals-recent-header {
  font-size: 12px;
  font-weight: 600;
  color: var(--text-primary);
  margin-bottom: 10px;
}

.vitals-recent-item {
  padding: 8px 0;
  border-top: 1px solid rgba(255,255,255,0.06);
  font-size: 12px;
  line-height: 1.4;
}

.vitals-recent-item:first-of-type { border-top: none; }

.vitals-recent-comment {
  color: var(--text-secondary);
  margin-bottom: 3px;
}

.vitals-recent-reply {
  color: var(--text-primary);
  padding-left: 10px;
  border-left: 2px solid var(--phase3);
}

/* ---- Phase Transition ---- */

.phase-transition {
  position: fixed;
  inset: 0;
  background: rgba(0,0,0,0.5);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  display: none;
  align-items: center;
  justify-content: center;
  z-index: 100;
  padding: 20px;
}

.phase-transition.active { display: flex; }

.phase-transition-card {
  background: rgba(20, 20, 28, 0.95);
  backdrop-filter: blur(20px);
  -webkit-backdrop-filter: blur(20px);
  border: 1px solid rgba(255,255,255,0.1);
  border-radius: clamp(20px, 2vw, 26px);
  padding: clamp(32px, 4vw, 44px) clamp(24px, 3vw, 36px);
  text-align: center;
  /* Overlay sibling to the welcome/wrapup screens, so it grows on the same
     vw-clamp system instead of holding phone size in a desktop viewport. Every
     floor equals the old fixed px, so phone rendering is unchanged; the ceiling
     lands the modal ~1.4x larger on a 1200px+ window, in step with the app. */
  max-width: clamp(320px, 42vw, 480px);
  width: 100%;
  box-shadow: 0 12px 40px rgba(0,0,0,0.5);
  animation: transIn 0.4s cubic-bezier(0.4, 0, 0.2, 1);
}

@keyframes transIn {
  from { transform: scale(0.92) translateY(16px); opacity: 0; }
  to { transform: scale(1) translateY(0); opacity: 1; }
}

.phase-transition-icon { font-size: clamp(40px, 4.6vw, 56px); margin-bottom: clamp(12px, 1.4vw, 16px); }

.phase-transition-title {
  font-size: clamp(18px, 2.2vw, 26px);
  font-weight: 700;
  color: var(--text-primary);
  margin-bottom: clamp(8px, 1vw, 12px);
}

.phase-transition-desc {
  font-size: clamp(14px, 1.7vw, 19px);
  color: var(--text-secondary);
  line-height: 1.5;
  margin-bottom: clamp(20px, 2.4vw, 28px);
}

.phase-transition-btn {
  padding: clamp(12px, 1.5vw, 16px) clamp(28px, 3.4vw, 40px);
  border-radius: var(--radius-pill);
  border: none;
  font-family: inherit;
  font-size: clamp(14px, 1.6vw, 17px);
  font-weight: 600;
  color: white;
  cursor: pointer;
  background: var(--phase-color);
}

.phase-transition-btn:active { transform: scale(0.96); }

/* Round Notice meter */
.round-notice-meter {
  width: 100%;
  height: clamp(8px, 1vw, 11px);
  background: rgba(255, 255, 255, 0.1);
  border-radius: 999px;
  overflow: hidden;
  margin: clamp(16px, 2vw, 22px) 0 clamp(6px, 0.8vw, 9px);
}

.round-notice-meter-fill {
  height: 100%;
  border-radius: 999px;
  background: var(--phase-color);
  transition: width 0.6s cubic-bezier(0.4, 0, 0.2, 1);
}

.round-notice-meter-label {
  font-size: clamp(12px, 1.4vw, 15px);
  font-weight: 600;
  color: var(--phase-color);
  margin-bottom: clamp(20px, 2.4vw, 28px);
}

/* ---- Review Overlay (sent-while-away) ---- */

.review-overlay {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.85);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
  display: none;
  flex-direction: column;
  z-index: 50;
  padding: 80px 20px 40px;
}

.review-overlay.open { display: flex; }

.review-panel {
  width: 100%;
  max-width: var(--app-max-width);
  margin: 0 auto;
  flex: 1;
  display: flex;
  flex-direction: column;
  gap: 20px;
  min-height: 0;
}

.review-panel-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-shrink: 0;
}

.review-panel-title {
  font-size: 18px;
  font-weight: 700;
  color: var(--text-primary);
}

.review-panel-close {
  width: 32px;
  height: 32px;
  border-radius: 50%;
  border: none;
  background: rgba(255,255,255,0.08);
  color: var(--text-primary);
  font-size: 14px;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
}

.review-list {
  flex: 1;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  display: flex;
  flex-direction: column;
  gap: 16px;
  padding-bottom: 20px;
}

.review-item {
  background: rgba(255,255,255,0.04);
  border: 1px solid rgba(255,255,255,0.08);
  border-radius: var(--radius);
  padding: 16px;
  display: flex;
  flex-direction: column;
  gap: 12px;
  transition: opacity 0.2s ease;
}

.review-item.overridden {
  opacity: 0.5;
}

.review-item-comment {
  display: flex;
  gap: 10px;
  align-items: flex-start;
}

.review-avatar {
  width: 32px;
  height: 32px;
  border-radius: 50%;
  flex-shrink: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 12px;
  font-weight: 600;
  color: white;
  position: relative;
}

.review-avatar .platform-icon {
  position: absolute;
  bottom: -2px;
  right: -4px;
  width: 14px;
  height: 14px;
  background: white;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  box-shadow: 0 1px 3px rgba(0,0,0,0.15);
}

.review-avatar .platform-icon img {
  width: 9px;
  height: 9px;
}

.review-comment-body { flex: 1; min-width: 0; }

.review-username {
  font-size: 12px;
  font-weight: 600;
  color: var(--text-primary);
  margin-bottom: 2px;
}

.review-comment-text {
  font-size: 13px;
  line-height: 1.4;
  color: var(--text-secondary);
}

.review-item-reply {
  padding: 10px 12px;
  background: rgba(167, 139, 250, 0.08);
  border-left: 3px solid var(--ai-purple);
  border-radius: var(--radius-sm);
}

.review-reply-label {
  font-size: 10px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--ai-purple);
  margin-bottom: 4px;
}

.review-reply-text {
  font-size: 13px;
  line-height: 1.4;
  color: var(--text-primary);
}

.review-disagree {
  align-self: flex-start;
  padding: 8px 14px;
  border-radius: var(--radius-pill);
  border: 1px solid rgba(236, 72, 153, 0.4);
  background: rgba(236, 72, 153, 0.12);
  color: var(--brand);
  font-family: inherit;
  font-size: 12px;
  font-weight: 600;
  cursor: pointer;
}

.review-disagree:disabled {
  cursor: default;
  opacity: 0.6;
}

/* ---- Dev Panel ---- */

.dev-overlay {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.85);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
  display: none;
  flex-direction: column;
  z-index: 50;
  padding: 120px 32px 80px;
}

.dev-overlay.open { display: flex; }

/* Shares the app shell's box AND its 20px gutter, so the panel's content edges
   line up with the brand mark and the menu button in .header above it. Without
   the padding the panel matched the shell's outer box but overhung the chrome
   by 20px a side, which read as the bars being too wide. */
.dev-panel {
  width: 100%;
  max-width: var(--app-max-width);
  padding: 0 20px;
  margin: 0 auto;
  flex: 1;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 32px;
}

.dev-panel-title {
  font-size: 18px;
  font-weight: 800;
  color: var(--text-primary);
}

.dev-signals {
  display: flex;
  flex-direction: column;
  gap: 16px;
}

.dev-signal {
  display: flex;
  align-items: center;
  gap: 8px;
}

/* Wide enough for the longest label ("Voice Confidence") to sit on one line —
   the bars must stay aligned to a common left edge, so this is a fixed column,
   not a hug. Re-measure if a label gets longer. */
.dev-signal-label {
  font-size: 13px;
  font-weight: 400;
  color: var(--text-secondary);
  width: 132px;
  flex-shrink: 0;
}

.dev-signal-bar {
  flex: 1;
  height: 8px;
  background: rgba(255,255,255,0.08);
  border-radius: 4px;
  overflow: hidden;
}

/* width: 0 is a FAIL-SAFE, not a starting value. The fill is a block child of
   a flex-sized bar, so with no width declared it computes `auto` and fills the
   whole track — meaning any invalid inline width (a NaN% from a missing
   denominator, say) silently renders a FULL bar at zero progress: the exact
   opposite of the truth. Declaring 0 makes the failure mode empty. The inline
   style overrides it in every healthy case. */
.dev-signal-fill {
  width: 0;
  height: 100%;
  border-radius: 3px;
  background: var(--phase-color);
  transition: width 0.5s ease;
}

.dev-signal-fill.met { background: var(--thatsus); }

.dev-signal-value {
  font-size: 14px;
  font-weight: 400;
  font-variant-numeric: tabular-nums;
  width: 48px;
  text-align: right;
  color: var(--text-primary);
}

.dev-buttons {
  display: flex;
  gap: 10px;
  flex-wrap: wrap;
}

.dev-btn {
  padding: 10px 20px;
  border-radius: var(--radius-pill);
  border: 1px solid rgba(255,255,255,0.12);
  background: rgba(255,255,255,0.06);
  font-family: inherit;
  font-size: 13px;
  font-weight: 400;
  cursor: pointer;
  color: var(--text-secondary);
}

.dev-btn:active { transform: scale(0.96); }

/* Reset Phase is the primary action in the dev panel, so it carries the stage's
   own colour (pink in sandbox, indigo in copilot, teal in vitals) rather than a
   fixed pink. It resets the stage you're in, and it should look like it. */
#devResetPhase {
  background: var(--phase-color);
  border-color: var(--phase-color);
  color: #FFFFFF;
}

/* Restart is the secondary escape hatch back to the welcome screen; the heavier
   border keeps it legible next to the filled primary. Padding drops 1px per side
   so both pills keep the same outer size. */
#devRestart {
  border-width: 2px;
  padding: 9px 19px;
}

/* ============================================
   Breakpoint System
   --bp-mobile-sm: 375px (iPhone SE, minimum supported)
   --bp-mobile:    390px (iPhone 14/15, primary target)
   --bp-tablet-sm: 744px (iPad Mini portrait)
   --bp-tablet:    834px (iPad Air portrait)
   Above 834px: cap at max-width, center in viewport.
   ============================================ */

/* 390px+ — iPhone 14/15 primary target */
@media (min-width: 390px) {
  :root {
    --action-btn-size: 88px;
    --action-btn-gap: 40px;
    --action-btn-emoji: 26px;
    --card-gap: 28px;
    --card-area-bottom-pad: 40px;
  }
}

/* 744px+ — iPad Mini portrait */
@media (min-width: 744px) {
  :root {
    --app-max-width: 560px;
    --action-btn-size: 96px;
    --action-btn-gap: 48px;
    --action-btn-emoji: 28px;
    --action-btn-label: 12px;
    --card-gap: 32px;
    --card-padding-x: 24px;
    --card-padding-top: 48px;
    --action-bar-pad-top: 16px;
    --card-area-bottom-pad: 48px;
  }
}

/* 834px+ — iPad Air portrait and above */
@media (min-width: 834px) {
  :root {
    --app-max-width: 600px;
    --card-padding-x: 28px;
    --card-padding-top: 56px;
  }
}

/* ============================================
   1200px+ — Desktop / laptop tier

   Everything above this line is a phone growing up: one column, phone type
   sizes, capped at 600px. On a 1440px window that reads as a narrow strip with
   the viewport doing nothing around it.

   This tier treats desktop as its own size, not a wider phone. Two moves:

   1. WIDTH. --app-max-width is the single lever — .header, .confidence-row,
      .card-area, .phase-footer and .action-bar all inherit it, so one token
      widens the whole stage in register. 860px, not 1100: the cards hold
      2-4 lines of conversational copy and past ~70ch a reply stops reading as
      a message and starts reading as a paragraph. 860 minus 40px gutters
      leaves 780px of card, which at the desktop body size below lands around
      62ch. Wide enough to fill the window, short enough to still feel spoken.

   2. TYPE. All type in this file is fixed px tuned for a phone at arm's
      length. On a laptop at 60cm those sizes are legible but weak — they read
      as a mobile screenshot pasted onto a monitor. Everything on the card
      stages steps up roughly 1.25x, and the paddings that frame it step with
      it so the ratio of ink to air holds. Welcome and transition already
      scale on vw clamps and are not listed. Wrapup's orbit SVG scales on cq
      units, but the HTML sitting on top of it (the orbit-center copy and the
      sentiment alert) is fixed px — both are listed at the end of this block.

   Gated at 1200px, not 1024: a 1024-1199px window is a small laptop or a
   landscape tablet where 600px still reads fine and the extra width would just
   stretch the lines.
   ============================================ */
@media (min-width: 1200px) {
  :root {
    --app-max-width: 860px;
    --card-padding-x: 40px;
    --action-btn-size: 104px;
    --action-btn-gap: 56px;
    --action-btn-emoji: 32px;
    --action-btn-label: 13px;
    --action-bar-pad-top: 20px;

    /* Vertical rhythm is elastic here, horizontal is not. A wide window is
       always wide, but "desktop" covers both a 13" laptop with ~720px of
       content height and a studio display with 1300px. Bigger type costs
       vertical space, and on the short end that's space the window doesn't
       have — the deck would overflow and .card-area would start scrolling,
       which this stage is built never to do.

       So the type holds its size and the AIR gives way instead: each value
       tracks vh between a floor tuned to the 720px case and the ceiling the
       tall case wants. Legibility is the thing worth protecting; padding is
       not. Keep the floors — dropping below them collapses the card rhythm
       into a list. */
    --card-gap: clamp(20px, 3.4vh, 32px);
    --card-padding-top: clamp(28px, 5vh, 56px);
    --card-area-bottom-pad: clamp(24px, 4.6vh, 48px);
  }

  /* Header + meter — chrome, so it steps up less than the content it frames. */
  .header { padding: 16px 24px; }
  .header-brand { font-size: 18px; }
  .brand-logo { height: 22px; }

  .confidence-row { padding: 0 24px; gap: 14px; }
  .confidence-label { font-size: 13px; }
  .confidence-picks { font-size: 14px; }
  .confidence-pct { font-size: 15px; min-width: 40px; }
  .confidence-track { height: 12px; }

  /* Comment card — the thing being replied to. It carries the largest body
     size on the stage because it's the subject; the drafts answer it. */
  .card-live-badge { font-size: 13px; padding: clamp(9px, 1.5vh, 12px) 24px; }
  .comment-bubble { padding: clamp(16px, 2.8vh, 22px) 24px clamp(14px, 2.3vh, 18px); gap: 16px; }
  .comment-avatar { width: 52px; height: 52px; font-size: 19px; }
  .comment-avatar .platform-icon { width: 22px; height: 22px; }
  .comment-avatar .platform-icon img { width: 15px; height: 15px; }
  .comment-username { font-size: 16px; margin-bottom: 5px; }
  .comment-text { font-size: 21px; }

  /* Section label + the persistent phase disclaimer under it. */
  .reply-options { gap: clamp(16px, 2.6vh, 24px); }
  .reply-options-header { gap: 8px; }
  .reply-options-label { font-size: 13px; }
  .phase-note-inline { font-size: 16px; }

  /* Reply cards (both stages share .reply-option). */
  .reply-option { padding: clamp(18px, 3vh, 26px) 28px; }
  .reply-option-label { font-size: 11px; padding: 4px 10px; border-radius: 9px; }
  .reply-option-text { font-size: 18px; }
  .reply-option-pencil { font-size: 14px; }
  .reply-option-pencil-label { font-size: 14px; }
  .reply-option-pencil .btn-icon { font-size: 14px; }   /* tracks the label, as at base */

  /* Inline AI-draft block. */
  .ai-draft-label { font-size: 13px; }
  .ai-draft-confidence { font-size: 13px; padding: 3px 10px; }
  .ai-draft-text,
  .ai-draft-edit,
  .ai-draft-sent { font-size: 18px; padding: 18px 20px; }
  .ai-draft-sent .sent-check { font-size: 20px; }

  .action-pick-counter { font-size: 14px; }
  .swipe-confirm { font-size: 15px; }

  .phase-footer-mark { width: 36px; height: 23px; }
  .phase-footer-version { font-size: 11px; }

  /* Wrapup orbit centre. The SVG around it is sized in cq units, so the bubbles
     grow with the stage while this copy stayed at phone size — on a laptop it
     read as a caption floating inside a large formation. Same ~1.25x as the
     card stages. The box widens with the type (200 -> 320) so the headline
     still breaks to two lines instead of four; the sub tracks it at 280. */
  .wrap-center { width: 320px; }
  .wrap-center-line { font-size: 27px; }
  .wrap-center-sub { font-size: 16px; margin-top: 10px; max-width: 280px; }

  /* The sentiment alert's desktop step-up is NOT here — its base block is
     defined further down this file, and a media query carries no extra
     specificity, so anything written here would lose on source order. It lives
     in its own 1200px query at the very bottom instead. */
}

/* Desktop / laptop — center the whole UI as one block.
   On a phone the 100dvh column is right: chrome pins to the edges because the
   edges are close. On a 1100px-tall window that same column stretches and all the
   slack pools between the cards and the footer, which is what read as top-heavy.

   So cap the app's height and center THAT box, rather than centering the cards
   inside a full-height column. Two reasons it has to be this way round:
   - Header, meter, cards and footer travel together, so the UI still reads as one
     designed object instead of chrome flung at the viewport edges.
   - The frame is a fixed height, so a taller or shorter card can't re-center the
     block. Centering the cards themselves would nudge everything up or down on
     every mount — exactly the jitter the confirmation beat was built to avoid.

   Gated on height too: a short laptop window has no slack to redistribute, and
   capping there would only invent scroll. The three atmosphere layers (galaxy,
   nebula, brand glow) are all position:fixed, so they stay full-bleed and are
   untouched by any of this. */
@media (hover: hover) and (pointer: fine) and (min-height: 800px) {
  body {
    display: flex;
    align-items: center;
    justify-content: center;
  }

  /* 760px is measured, not picked: the tallest card in the deck is 488px (a
     copilot card, which carries the Live badge sandbox cards don't), plus 141px
     of chrome and 104px of card-area padding = 733px. The 27px left over is
     headroom. Sized to the worst case so the frame never scrolls, and no larger,
     so typical cards don't sit above a pool of dead space. Re-measure this if the
     card gains a row or the copy gets longer. */
  .app {
    height: min(100dvh, 760px);
  }
}

/* The 760px above was measured against phone type. The 1200px+ tier runs type
   and padding ~1.25x taller, so the same deck overflows that frame and the card
   area starts scrolling. Re-measured for desktop: tallest copilot card ~620px,
   plus 172px of chrome and 104px of card-area padding = 896, rounded to 900.
   Must stay AFTER the rule above — same specificity, so source order decides.
   Keep the two numbers in step: if one is re-measured, re-measure the other. */
@media (min-width: 1200px) and (hover: hover) and (pointer: fine) and (min-height: 900px) {
  .app {
    height: min(100dvh, 900px);
  }
}

/* ---- Utility ---- */

.hidden { display: none !important; }

/* ============================================
   Sentiment Alert — the end-of-onboarding handoff beat
   A Murmur object, docked into the wrapup screen between
   the brand orbit and the Restart pill (in-flow, shares
   the ending — never overlays it). Every value resolves
   to an existing token; the only literal is the sentiment
   red (#EF4444 = .vitals-status-dot.critical).
   ============================================ */
.sent-alert {
  position: relative;          /* in-flow: docked, not floating */
  z-index: 4;                  /* above orbit/headline (z1) + footer (z2) */
  flex: 0 0 auto;
  width: 100%;                 /* full container width — wrap-placeholder's 24px padding is the only inset */
  max-width: 475px;            /* ceiling on wide viewports; below 475px the card stays full width */
  margin: 0 auto clamp(12px, 3vh, 28px);  /* centers once the cap engages; clears the Restart pill below */

  display: flex;
  gap: 10px;
  align-items: flex-start;
  padding: 14px;
  padding-top: 18px;           /* clears the accent rail */
  text-align: left;            /* wrap-placeholder centers text; the card reads left */

  /* Alert-card DNA (.vitals-alert), elevated to a floating surface */
  background: rgba(255, 255, 255, 0.06);
  backdrop-filter: blur(20px);
  -webkit-backdrop-filter: blur(20px);
  /* Sentiment red at low alpha instead of the neutral white hairline — the whole
     card edge now carries the severity the rail already signals. */
  border: 1px solid rgb(255 7 7 / 35%);
  border-radius: var(--radius);
  box-shadow: var(--shadow-elevated);
  overflow: hidden;

  cursor: pointer;
  opacity: 0;
  transform: translateY(10px);
  /* App's sheet-settle spring (styles.css:777) — a gentle dock into the calm ending */
  transition:
    opacity 460ms cubic-bezier(0.32, 0.72, 0.28, 1),
    transform 460ms cubic-bezier(0.32, 0.72, 0.28, 1);
}

.sent-alert.is-shown {
  opacity: 1;
  transform: translateY(0);
}

.sent-alert.is-leaving {
  opacity: 0;
  transform: translateY(6px);
}

/* Top severity rail — sentiment red, signals without tinting the headline.
   Subtle ~1.8s breathe (opacity + glow bleeding inward, since the card clips
   overflow) so it reads as a live alert, not a static line. Held solid under
   prefers-reduced-motion below. */
.sent-alert-rail {
  position: absolute;
  left: 0;
  right: 0;
  top: 0;
  height: 4px;
  background: #EF4444;
  animation: sentRailPulse 1.8s ease-in-out infinite;
}

@keyframes sentRailPulse {
  0%, 100% { opacity: 0.6; box-shadow: 0 0 6px 1px rgba(239, 68, 68, 0.4); }
  50%      { opacity: 1;   box-shadow: 0 0 14px 3px rgba(239, 68, 68, 0.7); }
}

.sent-alert-icon {
  flex-shrink: 0;
  width: 22px;
  height: 22px;
  display: flex;
  align-items: center;
  justify-content: center;
}

.sent-alert-icon img {
  width: 18px;
  height: 18px;
  display: block;
  /* White TikTok mark, to match the wrapup brand-channel logos. Colored SVG
     loaded via <img>, so recolor with the same flatten-then-lift filter.
     The alert only fires during wrapup, so this stays phase-scoped. */
  filter: brightness(0) invert(1);
}

.sent-alert-body {
  flex: 1;
  min-width: 0;
}

.sent-alert-title {
  font-size: 14px;
  font-weight: 600;
  color: var(--text-primary); /* headline stays bold white */
  margin-bottom: 3px;
  display: flex;
  align-items: center;
  gap: 6px;
}

.sent-alert-desc {
  font-size: 12px;
  line-height: 1.4;
  color: var(--text-secondary);
}

.sent-alert-dismiss {
  flex-shrink: 0;
  width: 26px;
  height: 26px;
  margin: -4px -4px 0 0;
  display: flex;
  align-items: center;
  justify-content: center;
  background: transparent;
  border: none;
  border-radius: var(--radius-pill);
  color: var(--text-muted);
  cursor: pointer;
  transition: color 0.15s ease, background 0.15s ease;
}

.sent-alert-dismiss:hover {
  color: var(--text-primary);
  background: rgba(255, 255, 255, 0.06);
}

.sent-alert-dismiss .material-symbols-rounded {
  font-size: 18px;
}

@media (prefers-reduced-motion: reduce) {
  .sent-alert {
    transition: opacity 0.3s ease;
    transform: translateY(0);
  }
  .sent-alert.is-leaving { transform: translateY(0); }
  /* No breathe — solid red rail, no glow */
  .sent-alert-rail {
    animation: none;
    opacity: 1;
    box-shadow: none;
  }
}

/* 1200px+ — the alert's share of the desktop tier (see the main block above).
   It's the one interactive object on the ending, so it holds the same ~1.25x
   step-up as the card stages rather than shrinking away next to the orbit.
   Padding and the dismiss target step with the type; the rail is chrome and
   doesn't. Must stay AFTER the base .sent-alert block — a media query adds no
   specificity, so writing these in the main 1200px block loses on source
   order and silently does nothing. */
@media (min-width: 1200px) {
  .sent-alert { max-width: 560px; padding: 18px; padding-top: 24px; gap: 13px; }
  .sent-alert-icon { width: 26px; height: 26px; }
  .sent-alert-icon img { width: 22px; height: 22px; }
  .sent-alert-title { font-size: 17px; margin-bottom: 4px; }
  .sent-alert-desc { font-size: 16px; }
  .sent-alert-dismiss { width: 30px; height: 30px; }
  .sent-alert-dismiss .material-symbols-rounded { font-size: 21px; }
}
