/* RunCoach interface (F5).
 *
 * The design language came out of Google Stitch; the implementation did not.
 * Stitch emitted Tailwind from a CDN, two Google Fonts and four WebGL canvases
 * driving the microphone rings. All three were dropped:
 *
 *   - the Tailwind CDN build is documented as development-only, and F7 deploys
 *     this, so a runtime dependency on someone else's host is a liability;
 *   - external fonts block first paint and fail offline, and the type scale
 *     survives fine on the system stack;
 *   - a ring that pulses is a CSS animation. Four WebGL contexts each running
 *     requestAnimationFrame forever is battery on a phone, for the same picture.
 *
 * What was kept is the part worth keeping: the palette, the type scale, the
 * shapes, and the visual treatment of the four voice states.
 */

:root {
  /* Palette, straight from the generated design. */
  --bg: #131313;
  --surface-lowest: #0e0e0e;
  --surface-low: #1c1b1b;
  --surface: #201f1f;
  --surface-high: #2a2a2a;
  --surface-highest: #353534;

  --text: #e5e2e1;
  --text-muted: #c2cab0;
  --outline: #424936;
  --outline-strong: #8c947c;

  --accent: #ccff80;
  --accent-strong: #a3e635;
  --accent-dim: #98da27;
  /* The coach speaking reads warm, so it is never confused with listening. */
  --warm: #e6b366;
  --user-surface: #3e495d;
  --error: #ffb4ab;

  /* Type scale. */
  --step-label: 0.875rem;
  --step-body: 1rem;
  --step-body-lg: 1.125rem;
  --step-stat: 1.5rem;
  --step-headline: 2rem;

  --sp-xs: 4px;
  --sp-sm: 8px;
  --sp-md: 16px;
  --sp-lg: 24px;
  --sp-xl: 32px;

  --mic: 140px;
  --radius: 12px;
  --radius-bubble: 24px;
}

* {
  box-sizing: border-box;
}

/* The browser's own [hidden] rule sets display:none, but any class here that
   sets display beats it: author styles win over user-agent styles at equal
   specificity. That left the "Conectar Telegram" button sitting above the words
   "Telegram conectado". Hiding something has to mean hiding it. */
[hidden] {
  display: none !important;
}

html,
body {
  height: 100%;
}

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font-family: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  font-size: var(--step-body);
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;

  display: grid;
  /* minmax(0, 1fr), not 1fr. A grid item defaults to min-width: auto, so the
     column refuses to shrink below its contents - and the profile strip is a
     row of chips that is deliberately wider than the screen, scrolling inside
     its own overflow. Without this the strip stretches the column, the column
     stretches the page, and every heading gets clipped off the left edge on a
     phone. The wide layout already had it; this one did not. */
  grid-template-columns: minmax(0, 1fr);
  grid-template-areas: "topbar" "profile" "stage";
  grid-template-rows: auto auto 1fr;
  min-height: 100dvh;

  /* Nothing here is ever meant to scroll sideways. If something does, it is a
     bug, and this makes it visible as clipping rather than as a page that
     drifts. */
  overflow-x: hidden;
}

.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

/* --- header ---------------------------------------------------------------- */

.topbar {
  grid-area: topbar;
  padding: var(--sp-lg) var(--sp-lg) var(--sp-sm);
}

.wordmark {
  margin: 0;
  font-size: var(--step-stat);
  font-weight: 800;
  letter-spacing: -0.03em;
  text-transform: uppercase;
  color: var(--accent);
}

.tagline {
  margin: 2px 0 0;
  font-size: var(--step-label);
  color: var(--text-muted);
}

/* --- profile --------------------------------------------------------------- */

.profile {
  grid-area: profile;
  padding: 0 var(--sp-lg) var(--sp-md);
  min-width: 0; /* same reason as the column: let the chip strip scroll, not stretch */
}

.profile-title,
.profile-hint {
  margin: 0;
}

.profile-title {
  font-size: var(--step-body);
  font-weight: 700;
  color: var(--accent);
}

.profile-hint {
  margin-top: var(--sp-xs);
  font-size: var(--step-label);
  color: var(--text-muted);
}

/* Hidden once anything is known: it explains an empty panel, and an empty
   panel is the only thing it needs to explain. */
.profile[data-known="true"] .profile-hint {
  display: none;
}

.fields {
  margin: var(--sp-md) 0 0;
  padding: 0;
  display: flex;
  gap: var(--sp-sm);
}

.field {
  background: var(--surface-high);
  border: 1px solid var(--outline);
  border-radius: var(--radius);
  padding: var(--sp-sm) var(--sp-md);
}

.field dt {
  font-size: 0.6875rem;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-muted);
}

.field dd {
  margin: 2px 0 0;
  font-size: var(--step-body);
  font-weight: 700;
}

.field .countdown {
  margin-top: var(--sp-xs);
  font-size: var(--step-label);
  font-weight: 600;
  color: var(--accent);
}

/* Nothing known yet: say so on a wide screen, take no room on a narrow one. */
.field[data-empty="true"] {
  border-style: dashed;
}

.field[data-empty="true"] dd {
  font-weight: 400;
  font-style: italic;
  color: var(--outline-strong);
}

/* On narrow screens the panel becomes a scrolling strip of chips, and empty
   fields disappear rather than filling the strip with "sin registrar". */
@media (max-width: 63.99rem) {
  .fields {
    overflow-x: auto;
    scrollbar-width: none;
    padding-bottom: var(--sp-xs);
  }
  .fields::-webkit-scrollbar {
    display: none;
  }
  .field {
    flex: 0 0 auto;
    border-radius: 999px;
    display: flex;
    align-items: baseline;
    gap: var(--sp-sm);
  }
  .field dd,
  .field .countdown {
    margin-top: 0;
  }
  .profile[data-known="true"] .field[data-empty="true"] {
    display: none;
  }
}

/* --- stage ----------------------------------------------------------------- */

.stage {
  grid-area: stage;
  display: flex;
  flex-direction: column;
  gap: var(--sp-md);
  padding: 0 var(--sp-lg) var(--sp-lg);
  min-height: 0; /* lets .log scroll instead of stretching the page */
  min-width: 0;  /* and lets a long unbroken word wrap instead of widening it */
}

/* --- the voice control ----------------------------------------------------- */

.voice {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--sp-md);
}

.mic-wrap {
  position: relative;
  width: var(--mic);
  height: var(--mic);
  display: grid;
  place-items: center;
}

.mic {
  position: relative;
  z-index: 1;
  width: var(--mic);
  height: var(--mic);
  border-radius: 50%;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--sp-xs);
  font: inherit;
  font-size: var(--step-label);
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--accent);
  background: var(--surface-highest);
  border: 2px solid var(--accent-strong);
  box-shadow: 0 0 24px rgba(163, 230, 53, 0.22);
  cursor: pointer;
  transition: transform 0.15s ease, border-color 0.2s ease, color 0.2s ease,
    box-shadow 0.2s ease;
}

.mic:hover {
  transform: scale(1.03);
}

.mic:active {
  transform: scale(0.96);
}

.mic:focus-visible {
  outline: 3px solid var(--accent);
  outline-offset: 4px;
}

.mic svg {
  width: 44px;
  height: 44px;
  fill: currentColor;
}

.mic .mic-off {
  display: none;
}

.status {
  margin: 0;
  min-height: 1.5rem;
  font-size: var(--step-label);
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-muted);
  text-align: center;
}

/* The rings. Each state shows exactly one, and none of them animate unless
   their state is active, so an idle page costs nothing. */
.ring {
  position: absolute;
  inset: -14px;
  border-radius: 50%;
  opacity: 0;
  pointer-events: none;
}

.ring-pulse {
  border: 2px solid var(--accent-strong);
}

.ring-sweep {
  border: 2px solid transparent;
  border-top-color: var(--outline-strong);
  border-right-color: var(--outline-strong);
}

.ring-wave {
  border: 2px solid var(--warm);
}

@keyframes pulse {
  0% { transform: scale(0.94); opacity: 0.85; }
  70% { transform: scale(1.12); opacity: 0; }
  100% { transform: scale(1.12); opacity: 0; }
}

@keyframes sweep {
  to { transform: rotate(360deg); }
}

@keyframes wave {
  0%, 100% { transform: scale(0.97); opacity: 0.9; }
  50% { transform: scale(1.06); opacity: 0.35; }
}

/* Listening. */
.voice[data-state="listening"] .ring-pulse {
  opacity: 1;
  animation: pulse 1.8s ease-out infinite;
}
.voice[data-state="listening"] .status {
  color: var(--accent);
}

/* Thinking. */
.voice[data-state="thinking"] .ring-sweep {
  opacity: 1;
  animation: sweep 1.1s linear infinite;
}
.voice[data-state="thinking"] .mic {
  border-color: var(--outline-strong);
  color: var(--text);
  box-shadow: none;
}

/* Speaking: warm, and moving outwards rather than pulsing inwards. */
.voice[data-state="speaking"] .ring-wave {
  opacity: 1;
  animation: wave 1.4s ease-in-out infinite;
}
.voice[data-state="speaking"] .mic {
  border-color: var(--warm);
  color: var(--warm);
  box-shadow: 0 0 24px rgba(230, 179, 102, 0.25);
}
.voice[data-state="speaking"] .status {
  color: var(--warm);
}

/* Unavailable: struck through, dimmed, and not inviting a click. */
.voice[data-state="unavailable"] .mic {
  border-color: var(--outline);
  color: var(--outline-strong);
  background: var(--surface-high);
  box-shadow: none;
  cursor: not-allowed;
}
.voice[data-state="unavailable"] .mic .mic-on {
  display: none;
}
.voice[data-state="unavailable"] .mic .mic-off {
  display: block;
}
.voice[data-state="unavailable"] .status {
  color: var(--error);
}

@media (prefers-reduced-motion: reduce) {
  .ring {
    animation: none !important;
  }
  .voice[data-state="listening"] .ring-pulse,
  .voice[data-state="thinking"] .ring-sweep,
  .voice[data-state="speaking"] .ring-wave {
    opacity: 0.6;
  }
}

/* --- conversation ---------------------------------------------------------- */

.log {
  flex: 1;
  min-height: 8rem;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: var(--sp-sm);
  padding-right: var(--sp-xs);
}

.bubble {
  max-width: 85%;
  padding: var(--sp-md);
  border-radius: var(--radius-bubble);
  font-size: var(--step-body-lg);
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}

.bubble.coach {
  align-self: flex-start;
  background: var(--surface);
  border: 1px solid rgba(66, 73, 54, 0.5);
  border-bottom-left-radius: var(--sp-xs);
}

.bubble.user {
  align-self: flex-end;
  background: var(--user-surface);
  border-bottom-right-radius: var(--sp-xs);
}

/* Still being spoken, so visibly not final. */
.bubble.partial {
  opacity: 0.55;
}

/* A degraded reply is the app talking, not the coach. Calmer, so a rate limit
   does not read as a failure. */
.bubble.notice {
  align-self: flex-start;
  background: var(--surface-low);
  border: 1px solid var(--outline);
  border-bottom-left-radius: var(--sp-xs);
  color: var(--text-muted);
  font-size: var(--step-body);
  font-style: italic;
}

.divider {
  display: flex;
  align-items: center;
  gap: var(--sp-md);
  margin: var(--sp-sm) 0;
  font-size: 0.6875rem;
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--outline-strong);
}

.divider::before,
.divider::after {
  content: "";
  flex: 1;
  height: 1px;
  background: var(--outline);
}

/* --- composer -------------------------------------------------------------- */

.composer {
  display: flex;
  gap: var(--sp-sm);
  align-items: center;
  padding: var(--sp-xs);
  background: var(--surface-low);
  border: 1px solid var(--outline);
  border-radius: var(--radius);
}

.composer input {
  flex: 1;
  min-width: 0;
  padding: var(--sp-sm) var(--sp-md);
  font: inherit;
  color: var(--text);
  background: transparent;
  border: none;
}

.composer input::placeholder {
  color: var(--outline-strong);
}

.composer input:focus {
  outline: none;
}

.composer:focus-within {
  border-color: var(--accent-strong);
}

.composer button {
  padding: var(--sp-sm) var(--sp-lg);
  font: inherit;
  font-size: var(--step-label);
  font-weight: 600;
  letter-spacing: 0.05em;
  color: var(--accent);
  background: var(--surface-high);
  border: 1px solid rgba(66, 73, 54, 0.5);
  border-radius: var(--radius);
  cursor: pointer;
}

.composer button:hover {
  background: var(--surface-highest);
}

.composer button:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

/* --- narrow screens -------------------------------------------------------- */

/* The microphone moves to the bottom half, where a thumb reaches it, and the
   conversation scrolls above. */
@media (max-width: 63.99rem) {
  .stage {
    display: flex;
    flex-direction: column;
  }
  .voice {
    order: 2;
    padding-top: var(--sp-sm);
  }
  .log {
    order: 1;
  }
  .composer {
    order: 3;
    margin-bottom: env(safe-area-inset-bottom, 0);
  }
  .bubble {
    max-width: 90%;
    font-size: var(--step-body);
  }
}

/* --- wide screens ---------------------------------------------------------- */

@media (min-width: 64rem) {
  body {
    /* The reading column is capped rather than elastic: a conversation is text,
       and text stops being readable long before it stops fitting. Capping the
       track itself and centring the pair is what keeps a wide monitor growing
       the outer margins instead of the gap between the conversation and the
       panel next to it. An elastic 1fr column left roughly 700px of dead space
       between the two on a 1900px screen. */
    grid-template-columns: minmax(0, 52rem) 320px;
    grid-template-areas:
      "topbar profile"
      "stage  profile";
    grid-template-rows: auto 1fr;
    justify-content: center;
    column-gap: var(--sp-xl);
    padding-inline: var(--sp-xl);
  }

  .topbar,
  .stage {
    padding-inline: 0;
  }

  .profile {
    align-self: start;
    position: sticky;
    top: var(--sp-lg);
    padding: var(--sp-lg);
    margin-top: var(--sp-lg);
    background: var(--surface-low);
    border: 1px solid var(--outline);
    border-radius: var(--radius);
  }

  .profile-title {
    font-size: var(--step-stat);
    letter-spacing: -0.01em;
  }

  .fields {
    flex-direction: column;
  }

  .field {
    padding: var(--sp-md);
  }

  .field dd {
    font-size: var(--step-stat);
  }
}

/* --- telegram -------------------------------------------------------------- */

.telegram {
  margin-top: var(--sp-md);
  padding-top: var(--sp-md);
  border-top: 1px solid var(--outline);
}

.telegram-link {
  display: inline-block;
  padding: var(--sp-sm) var(--sp-md);
  font-size: var(--step-label);
  font-weight: 600;
  letter-spacing: 0.05em;
  color: var(--bg);
  background: var(--accent-strong);
  border-radius: var(--radius);
  text-decoration: none;
}

.telegram-link:hover {
  background: var(--accent);
}

.telegram-link:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

.telegram-note {
  margin: var(--sp-sm) 0 0;
  font-size: var(--step-label);
  color: var(--text-muted);
}

/* On a narrow screen the panel is a strip of chips, and this sits under it
   rather than trying to become one. */
@media (max-width: 63.99rem) {
  .telegram {
    display: flex;
    align-items: center;
    gap: var(--sp-md);
    flex-wrap: wrap;
  }
  .telegram-note {
    margin: 0;
    flex: 1 1 12rem;
    min-width: 0;
  }
}
