/*
 * base.css — IMMUTABLE chrome.
 *
 * Holds only:
 *   - Stage container (the chaos zone)
 *   - Floating HUD (controls always reachable)
 *   - Appear / vanish animations for tree nodes
 *   - Reduced-motion fallback
 *
 * Everything visual inside the stage is set by inline styles from the
 * DOM-tree genome. That's where the chaos lives.
 */

* { box-sizing: border-box; }

:root {
  color-scheme: dark;
  --good: #6ee7b7;
  --bad: #fb7185;
  --warn: #fbbf24;
  --mono: ui-monospace, "SF Mono", "Cascadia Mono", "JetBrains Mono", Menlo, Consolas, monospace;
  --sans: system-ui, -apple-system, "Segoe UI", Inter, Roboto, sans-serif;
}

html, body {
  margin: 0;
  padding: 0;
  height: 100%;
  width: 100%;
  background: hsl(218, 36%, 7%);
  color: #e8e8f0;
  font-family: var(--sans);
  -webkit-font-smoothing: antialiased;
  overflow: hidden;
}

/* Stage --------------------------------------------------------------- */

#stage {
  position: fixed;
  inset: 0;
  width: 100vw;
  height: 100vh;
  overflow: hidden;
  z-index: 0;
  contain: layout paint;
  background:
    radial-gradient(circle at 22% 18%, hsla(218, 80%, 24%, 0.55) 0%, transparent 42%),
    radial-gradient(circle at 82% 84%, hsla(300, 86%, 30%, 0.4) 0%, transparent 55%),
    hsl(218, 36%, 7%);
}

/* Smooth morphing between generations.
 *
 * Mutations change inline styles every tick; the transition makes each
 * property glide to its new value instead of snapping. New elements
 * fade in via opacity (set up in apply.js — render with opacity:0,
 * restore on next frame); the transition handles the actual fade.
 * Removed elements get the .is-vanishing class which overrides
 * everything below with its own animation.
 *
 * `transition: all` is broad on purpose — chaos drifts across many
 * properties (transform, filter, color, layout, typography...) and we
 * want them all to morph. Browsers handle this fine for ~200 nodes.
 */
#stage > * {
  transition:
    opacity         600ms cubic-bezier(0.22, 1, 0.36, 1),
    transform       600ms cubic-bezier(0.22, 1, 0.36, 1),
    filter          600ms ease,
    color           600ms ease,
    background      600ms ease,
    background-color 600ms ease,
    border-color    600ms ease,
    border-width    600ms ease,
    border-radius   600ms ease,
    padding         600ms ease,
    margin          600ms ease,
    top             600ms cubic-bezier(0.22, 1, 0.36, 1),
    left            600ms cubic-bezier(0.22, 1, 0.36, 1),
    right           600ms cubic-bezier(0.22, 1, 0.36, 1),
    bottom          600ms cubic-bezier(0.22, 1, 0.36, 1),
    width           600ms ease,
    height          600ms ease,
    max-width       600ms ease,
    min-width       600ms ease,
    max-height      600ms ease,
    min-height      600ms ease,
    font-size       600ms ease,
    font-weight     600ms ease,
    letter-spacing  600ms ease,
    line-height     600ms ease,
    text-shadow     600ms ease,
    box-shadow      600ms ease;
  max-width: 100vw;
}

#stage > .is-vanishing {
  animation: vanish-fade 500ms ease forwards !important;
  pointer-events: none;
}

@keyframes vanish-fade {
  to { opacity: 0; transform: scale(0.92); filter: blur(4px); }
}

/* Speaking node — set on whichever element (at any depth) carries the
 * text the TTS layer is currently reading aloud. The class is
 * added/removed by js/tts.js via [data-gid="..."] lookup; while it's
 * present the evolver also has the matching id locked (so the node
 * won't be removed, replaced, wrapped, or text-mutated under live
 * speech).
 *
 * Outline + layered box-shadow so the highlight reads through the
 * chaos zone regardless of what inline styles the genome layered on.
 * The high `z-index` lifts the spoken element above every other
 * top-level node — useful because random style mutations can give
 * neighbours absurd z-indexes that would otherwise obscure the
 * spoken text. !important is required to beat per-node inline
 * z-index from the genome.
 *
 * Foreground guarantee: the genome's opMutateStyle / opAddStyle can
 * land on opacity / filter / mix-blend-mode / visibility, all of
 * which can hide the spoken element entirely. We override those four
 * with !important on BOTH the speaker and its top-level ancestor —
 * the ancestor matters because when the speaker is nested in an
 * opWrapNode wrapper, the wrapper's opacity/filter cascade visually
 * to the inner element. We deliberately leave color, background,
 * fonts, transform, and letter-spacing alone so the spoken text
 * still looks like the chaotic thing the user is hearing.
 */
#stage .is-speaking,
#stage > .has-speaking-descendant {
  opacity: 1 !important;
  filter: none !important;
  mix-blend-mode: normal !important;
  visibility: visible !important;
}

#stage .is-speaking {
  outline: 3px solid var(--good);
  outline-offset: 6px;
  /* Three-layer shadow: tight inner ring (constant), animated soft
     halo (driven by @keyframes speak-pulse below), and a dark drop-
     shadow that backs the element so the text reads against any
     chaos painted behind it. */
  box-shadow:
    0 0 0 2px rgba(110, 231, 183, 0.55),
    0 0 36px 6px hsla(150, 76%, 67%, 0.55),
    0 12px 48px rgba(0, 0, 0, 0.65);
  /* !important because opMutateStyle can mutate text-shadow directly
     and overwrite our legibility halo. */
  text-shadow:
    0 0 2px rgba(0, 0, 0, 0.95),
    0 2px 6px rgba(0, 0, 0, 0.85) !important;
  animation: speak-pulse 1.2s ease-in-out infinite;
  z-index: 9999 !important;
  position: relative;
}

/* When the spoken element is nested (e.g. inside a wrapper created by
 * opWrapNode), z-index alone on the descendant only stacks within its
 * parent's box. To keep "spoken text always on top" we ALSO lift the
 * entire top-level ancestor subtree, so the outlined element wins
 * against neighbouring top-level nodes regardless of their inline
 * z-index. The class is toggled on the top-level ancestor by
 * js/tts.js whenever the speaker is at depth >= 1. */
#stage > .has-speaking-descendant {
  z-index: 9999 !important;
}

/* Pulse only the middle (soft halo) shadow layer; the inner ring and
 * the dark backing stay constant so the silhouette doesn't strobe. */
@keyframes speak-pulse {
  0%, 100% {
    box-shadow:
      0 0 0 2px rgba(110, 231, 183, 0.55),
      0 0 0 0 hsla(150, 76%, 67%, 0.7),
      0 12px 48px rgba(0, 0, 0, 0.65);
  }
  50% {
    box-shadow:
      0 0 0 2px rgba(110, 231, 183, 0.55),
      0 0 0 28px hsla(150, 76%, 67%, 0),
      0 12px 48px rgba(0, 0, 0, 0.65);
  }
}

/* HUD ----------------------------------------------------------------- */

.hud {
  position: fixed;
  bottom: 16px;
  right: 16px;
  z-index: 9999;
  width: min(380px, calc(100vw - 32px));
  background: rgba(10, 10, 16, 0.78);
  backdrop-filter: blur(14px);
  border: 1px solid rgba(255, 255, 255, 0.1);
  border-radius: 14px;
  padding: 12px 14px;
  display: flex;
  flex-direction: column;
  gap: 10px;
  font-family: var(--sans);
  color: #e8e8f0;
  box-shadow: 0 16px 48px rgba(0, 0, 0, 0.5);
  user-select: none;
}

.hud.is-collapsed .hud-body { display: none; }
.hud.is-collapsed { padding-bottom: 12px; }

.hud-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
}

.hud-title {
  display: flex;
  align-items: center;
  gap: 8px;
}

.hud-title h2 {
  margin: 0;
  font-size: 12px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  font-weight: 700;
  color: #cdd6f4;
}

.dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--good);
  box-shadow: 0 0 12px currentColor;
  animation: blink 1.6s ease-in-out infinite;
}
.dot.is-paused { background: var(--warn); animation: none; }

@keyframes blink {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.35; }
}

.btn-mini {
  appearance: none;
  width: 22px;
  height: 22px;
  display: grid;
  place-items: center;
  font-size: 16px;
  line-height: 1;
  background: transparent;
  color: #9aa0b4;
  border: 1px solid rgba(255, 255, 255, 0.1);
  border-radius: 6px;
  cursor: pointer;
}
.btn-mini:hover { background: rgba(255, 255, 255, 0.08); color: #fff; }

.hud-body {
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.hud-stats {
  display: flex;
  gap: 10px;
  font-family: var(--mono);
  font-size: 11px;
  color: #9aa0b4;
}
.hud-stats b { color: #fff; font-weight: 600; }

/* Controls ------------------------------------------------------------ */

.ctrl-row {
  display: grid;
  grid-template-columns: 44px 1fr 56px;
  align-items: center;
  gap: 10px;
  font-family: var(--mono);
  font-size: 11px;
  color: #9aa0b4;
}

.ctrl-row input[type="range"] {
  -webkit-appearance: none;
  appearance: none;
  width: 100%;
  height: 4px;
  background: rgba(255, 255, 255, 0.1);
  border-radius: 2px;
  outline: none;
}

.ctrl-row input[type="range"]::-webkit-slider-thumb {
  -webkit-appearance: none;
  appearance: none;
  width: 14px;
  height: 14px;
  border-radius: 50%;
  background: var(--good);
  border: 0;
  cursor: pointer;
  box-shadow: 0 0 10px currentColor;
}
.ctrl-row input[type="range"]::-moz-range-thumb {
  width: 14px;
  height: 14px;
  border-radius: 50%;
  background: var(--good);
  border: 0;
  cursor: pointer;
  box-shadow: 0 0 10px currentColor;
}

.ctrl-row output {
  font-variant-numeric: tabular-nums;
  text-align: right;
  color: #fff;
}

.btn-row { display: flex; flex-wrap: wrap; gap: 6px; }

.btn {
  appearance: none;
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  background: rgba(255, 255, 255, 0.06);
  color: #e8e8f0;
  border: 1px solid rgba(255, 255, 255, 0.1);
  border-radius: 8px;
  padding: 7px 11px;
  cursor: pointer;
  transition: background 160ms ease, border-color 160ms ease, transform 80ms ease;
  flex: 1 1 auto;
  min-width: 0;
}
.btn:hover { background: rgba(255, 255, 255, 0.12); }
.btn:active { transform: translateY(1px); }
.btn-primary { background: rgba(110, 231, 183, 0.14); border-color: rgba(110, 231, 183, 0.45); color: #d3ffe9; }
.btn-good    { color: var(--good); border-color: rgba(110, 231, 183, 0.35); }
.btn-bad     { color: var(--bad);  border-color: rgba(251, 113, 133, 0.4); }
.btn-ghost   { background: transparent; }
.btn.is-flash { animation: btnflash 600ms ease; }

@keyframes btnflash {
  0%   { background: rgba(255, 255, 255, 0.3); }
  100% { background: rgba(255, 255, 255, 0.06); }
}

/* Cataclysm: brief screen shake when fired */
body.is-cataclysm { animation: shake 600ms ease; }
@keyframes shake {
  0%, 100% { transform: translate(0, 0); }
  10% { transform: translate(-6px, 4px); }
  20% { transform: translate(7px, -3px); }
  30% { transform: translate(-5px, 5px); }
  40% { transform: translate(6px, -2px); }
  50% { transform: translate(-4px, 3px); }
  60% { transform: translate(3px, -4px); }
  70% { transform: translate(-3px, 2px); }
  80% { transform: translate(2px, -2px); }
  90% { transform: translate(-1px, 1px); }
}

/* TTS controls (collapsible <details>, sibling of the mutation log) */

.tts-wrap {
  margin-top: 4px;
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.tts-wrap > summary {
  cursor: pointer;
  font-family: var(--mono);
  font-size: 11px;
  color: #9aa0b4;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}
.tts-wrap[open] > summary { color: #fff; }

.ctrl-toggle {
  display: flex;
  align-items: center;
  gap: 8px;
  font-family: var(--mono);
  font-size: 11px;
  color: #9aa0b4;
  cursor: pointer;
}
.ctrl-toggle input[type="checkbox"] {
  accent-color: var(--good);
  cursor: pointer;
  width: 14px;
  height: 14px;
}

.tts-wrap .ctrl-row select {
  width: 100%;
  /* Span the value + readout columns of the 3-col .ctrl-row grid so the
   * dropdown lines up with the slider tracks above instead of leaving
   * the readout column empty. */
  grid-column: 2 / 4;
  font-family: var(--mono);
  font-size: 11px;
  background: rgba(255, 255, 255, 0.06);
  color: #e8e8f0;
  border: 1px solid rgba(255, 255, 255, 0.1);
  border-radius: 6px;
  padding: 4px 6px;
  cursor: pointer;
}
.tts-wrap .ctrl-row select:hover { background: rgba(255, 255, 255, 0.1); }

/* Force the popup option list into the same dark palette. Without
 * these, the open dropdown falls back to the OS-native renderer
 * (white background, black text) and breaks the HUD's vibe. Most
 * Chromium/Firefox/Edge versions honor option-level color/background;
 * Safari uses the system theme regardless. */
.tts-wrap .ctrl-row select option {
  background: hsl(218, 36%, 9%);
  color: #e8e8f0;
}
.tts-wrap .ctrl-row select option:checked,
.tts-wrap .ctrl-row select option:hover {
  background: rgba(110, 231, 183, 0.2);
  color: #ffffff;
}

/* Mutation log (collapsed by default via <details>) */

.log-wrap {
  margin-top: 4px;
}
.log-wrap summary {
  cursor: pointer;
  font-family: var(--mono);
  font-size: 11px;
  color: #9aa0b4;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}
.log-wrap[open] summary { color: #fff; }

.mut-log {
  max-height: 180px;
  overflow: auto;
  margin: 8px 0 0;
  padding: 6px 10px 6px 24px;
  list-style: decimal;
  background: rgba(0, 0, 0, 0.3);
  border: 1px solid rgba(255, 255, 255, 0.08);
  border-radius: 8px;
  font-family: var(--mono);
  font-size: 10.5px;
  color: #cdd6f4;
}

.mut-log li { padding: 1px 0; }
.mut-log .gen-tag { color: #6c7086; margin-right: 6px; }
.mut-log .op-name { color: #cba6f7; margin-right: 6px; }
.mut-log .summary { color: #cdd6f4; }

/* Scrollbars */
.mut-log::-webkit-scrollbar { width: 6px; }
.mut-log::-webkit-scrollbar-thumb {
  background: rgba(255, 255, 255, 0.12);
  border-radius: 3px;
}

/* Focus */
:focus-visible {
  outline: 2px solid rgba(110, 231, 183, 0.7);
  outline-offset: 2px;
  border-radius: 4px;
}

/* Reduced motion */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
  }
}
