/* =========================================================================
   Explain to a Caveman — stylesheet
   =========================================================================
   Layout & visuals only. No DOM logic. Theming is driven by CSS custom
   properties on :root, overridden per [data-theme="…"] palette. The single
   gradient (--grad) is reused for: H1 fill, output card top border,
   active mode-toggle underline, primary button accents, theme toggle ring.
   ========================================================================= */

/* ---------- Box-sizing reset ---------- */
*, *::before, *::after { box-sizing: border-box; }

/* ---------- Theme palettes (CSS variables) ----------
   Two free themes (Dark, Light) + four purchasable elemental themes.
   :root holds the Dark (default) palette plus shared values. Each
   [data-theme="..."] overrides only what differs. Switching themes is
   just setting <html data-theme="water">. */

/* Every theme follows the 60-30-10 rule:
     60% — NEUTRAL: --bg, the page field. Near-neutral with only a whisper
           of the theme's temperature, so content owns the page.
     30% — CONTRAST: --bg-elevated/--bg-input surfaces, --border(-hover),
           and the text ramp — the structural layer that builds hierarchy.
     10% — HIGHLIGHT: --accent/--grad, the theme's main color. Reserved for
           CTAs, links, active states, and the XP/ring fills — never fields.
   Each block below is ordered 60 → 30 → 10. */

:root,
[data-theme="dark"] {
  /* DARK — free default. Plain graphite field; quiet bone/brass highlight
     (warm enough to feel caveman, nothing like Fire's ember orange). */
  /* 60% neutral */
  --bg:           #141518;
  /* 30% contrast */
  --bg-elevated:  #1d1e22;
  --bg-input:     #18191d;

  --border:       #2c2d32;
  --border-hover: #3a3b41;

  --text:         #eceae6;
  --text-muted:   #9b978f;
  --text-dim:     #8a867e;

  /* 10% highlight */
  --accent:       #c2a06b;
  --accent-soft:  rgba(194, 160, 107, 0.13);

  --warning-bg:   rgba(255, 77, 77, 0.08);
  --warning-fg:   #ffb8b8;
  --warning-bd:   rgba(255, 77, 77, 0.25);
  --success-fg:   #7fd989;

  /* Per-theme gradient — used for H1 fill, output card top border,
     mode-toggle underline, primary button accents, theme-switcher ring */
  --grad: linear-gradient(135deg, #e9d8b1 0%, #c2a06b 50%, #8f6f42 100%);

  --caveman:        #eceae6;
  --caveman-option: #d3b071; /* gold for "Caveman" option in dropdown */
  /* Title chrome (hub.css .etac-title). All three contrast the GROUND, not
     the title — the catalog runs from near-black (Iridescent) to near-white
     (SHINY), so anything keyed to the title's own colour would vanish for
     half of them. --edge is the hard 1px drop behind the glyphs; --plate and
     --plate-bd are the small rounded rectangle they sit on. */
  --title-edge:   rgba(255, 255, 255, 0.5);
  --title-plate:    rgba(255, 255, 255, 0.055);
  --title-plate-bd: rgba(255, 255, 255, 0.10);
}

[data-theme="light"] {
  /* LIGHT — free. Plain paper field; the same bone/brass family as Dark,
     deepened for AA contrast on the light ground. */
  /* 60% neutral */
  --bg:           #f7f5f2;
  /* 30% contrast */
  --bg-elevated:  #ebe8e2;
  --bg-input:     #fffefc;

  --border:       #cec8bd;
  --border-hover: #a49d8f;

  --text:         #201d18;
  --text-muted:   #5a544a;
  /* Darkened from #7f786c (3.57:1 on --bg-elevated, under AA) to clear
     4.5:1 where panel labels/hints actually sit. */
  --text-dim:     #6e685e;

  /* 10% highlight — deep enough that gradient-filled headings (H1 uses
     --grad as a text fill) stay readable on the paper field; the lightest
     grad stop keeps ≥3:1 against --bg. */
  --accent:       #6f5229;
  --accent-soft:  rgba(111, 82, 41, 0.14);

  --warning-bg:   rgba(192, 33, 33, 0.08);
  --warning-fg:   #9c2424;
  --warning-bd:   rgba(192, 33, 33, 0.25);
  --success-fg:   #2e7d3a;

  --grad: linear-gradient(135deg, #b08347 0%, #7d5e33 45%, #3a2913 100%);

  --caveman:        #201d18;
  --caveman-option: #8a6a3f;
  --title-edge:   rgba(0, 0, 0, 0.62);
  --title-plate:    rgba(0, 0, 0, 0.045);
  --title-plate-bd: rgba(0, 0, 0, 0.085);
}

[data-theme="fire"] {
  /* FIRE — dark elemental (Store unlock). Neutral charcoal field; warm
     ember highlight. */
  /* 60% neutral */
  --bg:           #16171a;
  /* 30% contrast */
  --bg-elevated:  #1f2024;
  --bg-input:     #1a1b1e;

  --border:       #2e2f34;
  --border-hover: #3a3b40;

  --text:         #ececec;
  --text-muted:   #9a9a9a;
  /* Bumped from #6e6e6e (~3.5:1 on --bg, under AA) to ~5:1 so the dim
     labels, footer note, and nav text stay legible without losing hierarchy. */
  --text-dim:     #8a8a8a;

  /* 10% highlight */
  --accent:       #ff8a3d;
  --accent-soft:  rgba(255, 138, 61, 0.12);

  --warning-bg:   rgba(255, 77, 77, 0.08);
  --warning-fg:   #ffb8b8;
  --warning-bd:   rgba(255, 77, 77, 0.25);
  --success-fg:   #7fd989;

  --grad: linear-gradient(135deg, #ffd84a 0%, #ff8a3d 50%, #ff4d4d 100%);

  --caveman:        #ececec;
  --caveman-option: #ffb43a;
  --title-edge:   rgba(255, 255, 255, 0.5);
  --title-plate:    rgba(255, 255, 255, 0.055);
  --title-plate-bd: rgba(255, 255, 255, 0.10);
}

[data-theme="air"] {
  /* AIR — light. Neutral warm-white field (was cream, which tinted the
     whole 60%); pink → magenta stays pure highlight. The 30% layer is
     deliberately deep for a light theme: near-white inputs on a dimmer
     field with firm borders, so buttons/cards read at a glance. */
  /* 60% neutral */
  --bg:           #f6f2ee;
  /* 30% contrast */
  --bg-elevated:  #ece5e0;
  --bg-input:     #fffefd;

  --border:       #cabcb5;
  --border-hover: #a3928a;

  --text:         #211c1e;
  --text-muted:   #5c5055;
  /* Darkened from #837579 (3.52:1 on --bg-elevated, under AA) to clear
     4.5:1 where panel labels/hints actually sit. */
  --text-dim:     #6f6367;

  /* 10% highlight (deep enough for AA link text on the neutral field) */
  --accent:       #bc2066;
  --accent-soft:  rgba(188, 32, 102, 0.14);

  --warning-bg:   rgba(192, 33, 33, 0.08);
  --warning-fg:   #9c2424;
  --warning-bd:   rgba(192, 33, 33, 0.25);
  --success-fg:   #2e7d3a;

  /* First stop darkened from #f9a8d4 (1.63:1 on --bg, illegible as gradient
     text) to clear 3:1, same discipline Light's grad already applies. */
  --grad: linear-gradient(135deg, #b37999 0%, #ec4899 50%, #a21caf 100%);

  --caveman:        #1a1a1a;
  --caveman-option: #be185d;
  --title-edge:   rgba(0, 0, 0, 0.62);
  --title-plate:    rgba(0, 0, 0, 0.045);
  --title-plate-bd: rgba(0, 0, 0, 0.085);
}

[data-theme="water"] {
  /* WATER — light. Cool near-white field; surfaces are neutral slate (was
     sky-tinted #f0f9ff, which leaked the highlight into the 30% layer);
     ocean blue stays pure highlight. Deep borders + white inputs keep
     interactive edges crisp on the light field. */
  /* 60% neutral */
  --bg:           #f6f8fa;
  /* 30% contrast */
  --bg-elevated:  #e8edf3;
  --bg-input:     #ffffff;

  --border:       #becbd9;
  --border-hover: #8b9fb5;

  --text:         #0f172a;
  --text-muted:   #43526a;
  /* Darkened from #71829a (3.33:1 on --bg-elevated, under AA) to clear
     4.5:1 where panel labels/hints actually sit. */
  --text-dim:     #5e6c80;

  /* 10% highlight (deep enough for AA link text on the neutral field) */
  --accent:       #0270aa;
  --accent-soft:  rgba(2, 112, 170, 0.14);

  --warning-bg:   rgba(192, 33, 33, 0.08);
  --warning-fg:   #9c2424;
  --warning-bd:   rgba(192, 33, 33, 0.25);
  --success-fg:   #15803d;

  /* First stop darkened from #7dd3fc (1.57:1 on --bg, illegible as gradient
     text) to clear 3:1, same discipline Light's grad already applies. */
  --grad: linear-gradient(135deg, #5996b3 0%, #0ea5e9 50%, #0c4a6e 100%);

  --caveman:        #0f172a;
  --caveman-option: #0270aa;
  --title-edge:   rgba(0, 0, 0, 0.62);
  --title-plate:    rgba(0, 0, 0, 0.045);
  --title-plate-bd: rgba(0, 0, 0, 0.085);
}

[data-theme="earth"] {
  /* EARTH — light. Neutral warm-stone field (was saturated tan, which made
     the theme color ~100% of the page); richer sienna as the highlight.
     Strong clay borders + bright inputs so controls stand off the stone. */
  /* 60% neutral */
  --bg:           #f0ebdf;
  /* 30% contrast */
  --bg-elevated:  #e2d9c3;
  --bg-input:     #fbf8ee;

  --border:       #bcae89;
  --border-hover: #93845c;

  --text:         #27200f;
  --text-muted:   #594e32;
  /* Darkened from #7d7355 (3.35:1 on --bg-elevated, under AA) to clear
     4.5:1 where panel labels/hints actually sit. */
  --text-dim:     #685f47;

  /* 10% highlight */
  --accent:       #82492a;
  --accent-soft:  rgba(130, 73, 42, 0.14);

  --warning-bg:   rgba(155, 33, 33, 0.08);
  --warning-fg:   #7a2424;
  --warning-bd:   rgba(155, 33, 33, 0.25);
  /* Nudged from #2e7d3a (4.29:1 on --bg, just under AA) to clear 4.5:1. */
  --success-fg:   #2d7938;

  /* First stop darkened from #d4b896 (1.59:1 on --bg, illegible as gradient
     text) to clear 3:1, same discipline Light's grad already applies. */
  --grad: linear-gradient(135deg, #99846c 0%, #8b5a3c 50%, #4a2c1a 100%);

  --caveman:        #2d1f0d;
  --caveman-option: #82492a;
  --title-edge:   rgba(0, 0, 0, 0.62);
  --title-plate:    rgba(0, 0, 0, 0.045);
  --title-plate-bd: rgba(0, 0, 0, 0.085);
}

/* ---------- Base ---------- */
body {
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  background: var(--bg);
  color: var(--text);
  margin: 0;
  /* Asymmetric padding biases the centered container upward — less space
     at the top, more at the bottom, so content lands a couple of lines
     above the true vertical center. */
  padding: 1rem 1rem 6rem;
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  transition: background 0.2s, color 0.2s;
}

.container {
  max-width: 720px;
  width: 100%;
  margin: auto;   /* vertical + horizontal centering inside body flex */
}

/* ---------- Theme switcher (fixed top-right, hover-expanding pill) ----------
   The CONTAINER is the visible shape — a single pill (rectangle with
   semicircular ends, border-radius: 9999px). It carries the gradient
   ring and bg-elevated fill. Individual option buttons are transparent
   icon containers that live inside the pill.
   - Collapsed: container is 40×40 → renders as a single circle
     (because length == diameter when only the active option is visible).
   - Expanded (:hover or :focus-within): non-active options grow from
     width 0 → 40px. The container auto-sizes to fit them, smoothly
     stretching from circle into a pill. The gradient ring stretches too.
   script.js keeps the active option as the last DOM child so the
   collapsed circle always sits flush against the right corner. */

.theme-switcher {
  position: fixed;
  top: 1rem;
  right: 1rem;
  display: flex;
  align-items: center;
  height: 40px;
  /* Pill shape — radius is huge, so corners are always fully rounded */
  border-radius: 9999px;
  /* Two-background trick: solid fill inside, gradient acts as the border */
  border: 2px solid transparent;
  background:
    linear-gradient(var(--bg-elevated), var(--bg-elevated)) padding-box,
    var(--grad) border-box;
  /* Clip option icons to the pill outline while they grow during animation */
  overflow: hidden;
  z-index: 100;
}

.theme-option {
  width: 40px;
  height: 40px;
  border: 0;
  background: transparent;
  color: var(--text);
  font-size: 18px;
  cursor: pointer;
  display: grid;
  place-items: center;
  flex-shrink: 0;
  overflow: hidden;
  padding: 0;
  transition: width 0.25s ease, opacity 0.2s ease,
              background 0.2s, transform 0.12s;
}

/* Collapsed: only the active option is visible. Others have zero width. */
.theme-option:not(.active) {
  width: 0;
  opacity: 0;
  pointer-events: none;
}

/* Expanded: every option visible inside the pill. */
.theme-switcher:hover .theme-option:not(.active),
.theme-switcher:focus-within .theme-option:not(.active) {
  width: 40px;
  opacity: 1;
  pointer-events: auto;
}

/* In the expanded pill, give the active option a subtle accent-tinted
   disc so the user can tell which theme is currently selected. */
.theme-switcher:hover .theme-option.active,
.theme-switcher:focus-within .theme-option.active {
  background: var(--accent-soft);
  border-radius: 50%;
}

.theme-option:hover { transform: scale(1.08); }
.theme-option:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: -2px;
  border-radius: 50%;
}

/* Unowned Store themes are hidden from the pickers entirely (hud.js sets
   [hidden] on their buttons) — they appear only once the bundle is owned. */

/* ---------- Header (caveman + title + subtitle) ---------- */
.page-header {
  text-align: center;
  margin-bottom: 1.25rem;
}

.caveman-svg {
  width: 72px;
  height: auto;
  display: block;
  margin: 0 auto 0.5rem;
  /* Caveman shapes inherit fill/stroke from currentColor */
  color: var(--caveman);
  fill: currentColor;
  stroke: currentColor;
}
/* Eyes / mouth / club separator dots cut out against background */
.caveman-svg .eye {
  fill: var(--bg);
  stroke: none;
}

h1 {
  font-size: 32px;
  font-weight: 600;
  margin: 0 0 0.35rem;
  background: var(--grad);
  -webkit-background-clip: text;
          background-clip: text;
  -webkit-text-fill-color: transparent;
  letter-spacing: -0.01em;
}

.subtitle {
  text-align: center;
  color: var(--text-muted);
  font-size: 14px;
  margin: 0;
}

/* ---------- Mode toggle (Character / Study) ----------
   The underline used to be a static ::after on the active button. Now it's
   ONE pseudo-element on the .mode-toggle parent, and its left/width are
   driven by CSS custom properties that script.js updates whenever the
   active button changes. Transitioning those properties produces a smooth
   gradient bar that slides between the two buttons. */
.mode-toggle {
  display: flex;
  gap: 4px;
  margin: 1.5rem auto;
  padding: 4px;
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  border-radius: 10px;
  width: fit-content;
  position: relative; /* anchor for the sliding underline */
}
.mode-btn {
  background: transparent;
  border: none;
  border-radius: 6px;
  color: var(--text-muted);
  cursor: pointer;
  padding: 6px 14px;
  font-size: 13px;
  font-family: inherit;
  transition: color 0.2s, background 0.2s;
  position: relative;
}
.mode-btn:hover { color: var(--text); }
.mode-btn.active {
  background: var(--bg-input);
  color: var(--text);
  font-weight: 600;
}

/* The sliding gradient underline */
.mode-toggle::after {
  content: '';
  position: absolute;
  bottom: 6px;
  height: 2px;
  left: var(--slider-x, 18px);
  width: var(--slider-w, 90px);
  background: var(--grad);
  border-radius: 1px;
  transition: left 0.28s cubic-bezier(0.4, 0, 0.2, 1),
              width 0.28s cubic-bezier(0.4, 0, 0.2, 1);
  pointer-events: none;
}

/* ---------- The "Explain X to a Y" sentence builder ---------- */
.inputs {
  display: flex;
  align-items: center;
  justify-content: center;
  /* gap replaced with sibling-margin so we can animate the space away
     when a child collapses on mode switch (gap can't transition per-item) */
  font-size: 17px;
  flex-wrap: wrap;
  margin-bottom: 1.5rem;
}
.inputs > * + * { margin-left: 8px; }

select,
input[type="text"] {
  padding: 7px 11px;
  font-size: 15px;
  border: 1px solid var(--border);
  background: var(--bg-input);
  color: var(--text);
  border-radius: 7px;
  font-family: inherit;
  min-width: 180px;
  outline: none;
  transition: border-color 0.15s, background 0.15s;
}
select:hover,
input[type="text"]:hover { border-color: var(--border-hover); }
select:focus,
input[type="text"]:focus { border-color: var(--accent); }

select option,
select optgroup { background: var(--bg-input); color: var(--text); }
/* Caveman option keeps a bold, theme-tinted highlight in every palette */
select option.caveman-option {
  color: var(--caveman-option);
  font-weight: 700;
}

/* -------- Mode-switch morph (Character ↔ Study) --------
   The four character-only elements collapse fluidly when the mode changes
   to Study: max-width + margin + padding + opacity all animate together,
   so the visible neighbors slide smoothly into the freed space instead of
   teleporting. `display 0.3s allow-discrete` flips display:none in/out at
   the boundaries so the elements actually leave/enter the layout cleanly.
   `@starting-style` provides the "from collapsed" state when they re-enter. */

/* Default max-widths and max-heights give each element a concrete starting
   size to animate down from (you can't transition between `auto`/`none`
   and `0`). Heights are generous — they don't constrain in normal use. */
#character-btn    { max-width: 320px; max-height: 60px; }
#to-a-word        { max-width: 100px; max-height: 40px; }
#surprise-btn     { max-width: 240px; max-height: 60px; }

#character-btn, #to-a-word, #surprise-btn {
  overflow: hidden;
  transition: opacity 0.3s ease,
              max-width 0.3s ease,
              max-height 0.3s ease,
              min-width 0.3s ease,
              margin 0.3s ease,
              padding 0.3s ease,
              border-width 0.3s ease,
              display 0.3s allow-discrete;
}

/* In Study Mode, also collapse the vertical dimensions (max-height +
   padding-top/bottom + border-top/bottom). This makes the element fully
   zero-sized by the time `display: none` kicks in, so there's no leftover
   vertical extent to disappear at the end of the transition. */
body[data-mode="plain"] #character-btn,
body[data-mode="plain"] #to-a-word,
body[data-mode="plain"] #surprise-btn {
  opacity: 0;
  max-width: 0;
  max-height: 0;
  min-width: 0;
  margin: 0;
  padding: 0;
  border-width: 0;
  pointer-events: none;
  display: none;
}

@starting-style {
  body[data-mode="character"] #character-btn,
  body[data-mode="character"] #to-a-word,
  body[data-mode="character"] #surprise-btn {
    opacity: 0;
    max-width: 0;
    max-height: 0;
    margin: 0;
    padding: 0;
    border-width: 0;
  }
}

/* Prefix word: two stacked text spans crossfade between "Explain" and
   "Break down" so there's never a moment where the slot is empty. Using
   inline-grid with all children in grid-area 1/1 places them on top of
   each other; the grid auto-sizes to the larger ("Break down"). */
#prefix-word {
  display: inline-grid;
  min-width: 5.5em;
  vertical-align: middle;
}
.prefix-text {
  grid-area: 1 / 1;
  text-align: center;
  transition: opacity 0.28s ease;
}
.prefix-text[data-when="character"] { opacity: 1; }
.prefix-text[data-when="plain"]     { opacity: 0; }

body[data-mode="plain"] .prefix-text[data-when="character"] { opacity: 0; }
body[data-mode="plain"] .prefix-text[data-when="plain"]     { opacity: 1; }

/* ---------- Action buttons ---------- */
.buttons {
  display: flex;
  /* gap replaced with sibling-margin (same reason as .inputs above) */
  justify-content: center;
  margin-bottom: 1.5rem;
  flex-wrap: wrap;
}
.buttons > * + * { margin-left: 10px; }
button {
  padding: 8px 16px;
  font-size: 14px;
  border: 1px solid var(--border);
  background: var(--bg-elevated);
  color: var(--text);
  border-radius: 7px;
  cursor: pointer;
  font-family: inherit;
  transition: background 0.15s, border-color 0.15s, transform 0.05s;
}
button:hover { background: var(--bg-input); border-color: var(--border-hover); }
button:active { transform: translateY(1px); }
button:disabled {
  opacity: 0.5;
  cursor: not-allowed;
  transform: none;
}

button.primary {
  border-color: var(--accent);
  color: var(--accent);
  background: var(--accent-soft);
  font-weight: 600;
}
button.primary:hover {
  background: var(--accent-soft);
  border-color: var(--accent);
  filter: brightness(1.1);
}

/* Press-down animation: scripts add `.pressed` momentarily on click,
   producing a quick "click feedback" bounce. Independent of :active so
   it fires for keyboard activation too. */
button.pressed {
  animation: press-down 0.22s ease-out;
}
@keyframes press-down {
  0%   { transform: scale(1)    translateY(0);  }
  40%  { transform: scale(0.93) translateY(2px); }
  100% { transform: scale(1)    translateY(0);  }
}

/* Sparkles — small particles emitted around the Explain button on click.
   Each sparkle is a fixed-position dot that flies outward (via per-element
   --dx / --dy custom properties) and fades out. */
.sparkle {
  position: fixed;
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--accent);
  pointer-events: none;
  z-index: 200;
  animation: sparkle-burst 0.55s ease-out forwards;
}
@keyframes sparkle-burst {
  0% {
    transform: translate(-50%, -50%) scale(1);
    opacity: 1;
  }
  60% {
    opacity: 0.9;
  }
  100% {
    transform: translate(calc(-50% + var(--dx)), calc(-50% + var(--dy))) scale(0.2);
    opacity: 0;
  }
}

/* ---------- Output card ---------- */
.output-card {
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  border-radius: 12px;
  overflow: hidden;
  position: relative;
  margin-top: 0.5rem;
}
.output-card::before {
  content: '';
  position: absolute;
  top: 0; left: 0; right: 0;
  height: 2px;
  background: var(--grad);
}
.output-card-inner { padding: 1.4rem 1.5rem 1.25rem; }

.output-tags {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 0.75rem;
  font-size: 13px;
  color: var(--text-muted);
  flex-wrap: wrap;
}
.tag {
  background: var(--bg-input);
  border: 1px solid var(--border);
  padding: 3px 10px;
  border-radius: 6px;
  color: var(--text);
}
.tag.mode-tag {
  font-size: 12px;
  color: var(--text-dim);
  background: transparent;
  border: none;
  padding: 0;
}
.arrow { color: var(--accent); font-weight: 700; }

.rendered {
  font-size: 15px;
  line-height: 1.7;
  color: var(--text);
}
.rendered p { margin: 0.25rem 0; }
.rendered strong {
  display: block;
  margin-top: 0.75rem;
  font-size: 12px;
  text-transform: uppercase;
  letter-spacing: 0.8px;
  color: var(--accent);
}
.rendered strong:first-child { margin-top: 0; }

.result-actions {
  margin-top: 1rem;
  display: flex;
  gap: 8px;
}
.result-actions button {
  padding: 6px 12px;
  font-size: 13px;
}

/* ---------- Loading state (streaming pre-first-token) ---------- */
.loading {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 1rem 1.25rem;
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  border-radius: 10px;
  color: var(--text-muted);
  font-size: 14px;
}
.loading-dots {
  display: inline-flex;
  gap: 4px;
}
.loading-dots span {
  width: 7px; height: 7px;
  border-radius: 50%;
  background: var(--accent);
  animation: pulse 1.2s ease-in-out infinite;
}
.loading-dots span:nth-child(2) { animation-delay: 0.15s; }
.loading-dots span:nth-child(3) { animation-delay: 0.30s; }
@keyframes pulse {
  0%, 80%, 100% { opacity: 0.3; transform: scale(0.85); }
  40%           { opacity: 1;   transform: scale(1.1);  }
}

/* Subtle blinking caret while tokens are still arriving */
.streaming-cursor {
  display: inline-block;
  width: 7px;
  margin-left: 2px;
  background: var(--accent);
  height: 1.05em;
  vertical-align: -0.15em;
  animation: caret 0.9s steps(1) infinite;
}
@keyframes caret {
  50% { opacity: 0; }
}

/* ---------- Friendly error / warning ---------- */
.warning {
  padding: 12px 16px;
  background: var(--warning-bg);
  color: var(--warning-fg);
  border: 1px solid var(--warning-bd);
  border-radius: 7px;
  font-size: 14px;
  text-align: center;
  margin-top: 0.5rem;
}

/* ---------- Footer-note (below buttons, mode-aware text) ---------- */
.footer-note {
  margin: 1.5rem 0 1rem;
  padding-top: 1rem;
  border-top: 1px solid var(--border);
  font-size: 12px;
  color: var(--text-dim);
  text-align: center;
}

/* ---------- Page footer (credits + access-code) ---------- */
.page-footer {
  margin-top: 1.25rem;
  padding-top: 1rem;
  border-top: 1px solid var(--border);
  font-size: 12px;
  color: var(--text-dim);
  text-align: center;
}
.page-footer p { margin: 0.25rem 0; }
.page-footer a {
  color: var(--text-muted);
  text-decoration: none;
  border-bottom: 1px dotted var(--text-dim);
}
.page-footer a:hover {
  color: var(--accent);
  border-bottom-color: var(--accent);
}

/* Daily-usage badge pinned to top-left. Rounded box bordered in the
   active theme's accent color. Tabular numerals keep the countdown
   digits from jittering. Hidden when an access code is in use. */
.usage-counter {
  position: fixed;
  top: 1rem;
  left: 1rem;
  padding: 6px 12px;
  font-size: 12px;
  color: var(--text);
  background: var(--bg-elevated);
  border: 2px solid var(--accent);
  border-radius: 8px;
  font-feature-settings: "tnum";
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
  user-select: none;
  z-index: 100;
  transition: border-color 0.2s ease, color 0.2s ease;
}
.usage-counter.depleted {
  color: var(--warning-fg);
  border-color: var(--warning-fg);
}
.usage-counter[hidden] { display: none; }

/* On narrow screens, tuck the badge against the edge so it doesn't
   overlap the centered caveman illustration too aggressively. */
@media (max-width: 560px) {
  .usage-counter {
    top: 0.75rem;
    left: 0.75rem;
    font-size: 11px;
    padding: 4px 9px;
  }
}

.access-code-status {
  margin-left: 6px;
  font-weight: 600;
}
.access-code-status.accepted { color: var(--success-fg); }
.access-code-status.rejected { color: var(--warning-fg); }

/* Subtle "clear" link-button beside the access-code status — easy to find
   once you're looking for it, easy to overlook otherwise. Styled like the
   footer's dotted-underline links rather than a full button so it doesn't
   compete with the "accepted ✓" status for attention. */
.access-code-clear {
  margin-left: 6px;
  padding: 0;
  border: none;
  background: none;
  font: inherit;
  font-size: 11px;
  color: var(--text-dim);
  text-decoration: underline dotted;
  text-underline-offset: 2px;
  cursor: pointer;
  opacity: 0.65;
  transition: opacity 0.15s ease, color 0.15s ease;
}
.access-code-clear:hover {
  opacity: 1;
  color: var(--accent);
}
.access-code-clear[hidden] { display: none; }

.access-code-panel {
  margin-top: 0.5rem;
  display: flex;
  gap: 6px;
  justify-content: center;
  align-items: center;
  flex-wrap: wrap;
}
/* The HTML `hidden` attribute is normally equivalent to `display: none`,
   but the .access-code-panel rule above declares `display: flex` and wins
   on specificity. Restore the hidden behaviour explicitly. */
.access-code-panel[hidden] { display: none; }
.access-code-panel input {
  font-size: 13px;
  padding: 6px 10px;
  min-width: 200px;
}
.access-code-panel button {
  padding: 6px 12px;
  font-size: 13px;
}

/* ---------- Version badge + patch notes/roadmap pill (bottom-center) ----------
   Floats centered along the bottom edge so it's easy to find but light
   enough not to fight the content. Opacity bumps on hover for a clear
   "I can be clicked" affordance. */
/* The bottom nav bar. (Class is still .version-badge for historical
   reasons — it predates the move of the version number into the footer
   credit.) At rest, it's a small circular pill containing only the home
   icon. On hover or focus-within, two .version-side groups slide out
   from behind the pill to reveal the rest of the nav:
   patch notes · about · 🏠 · roadmap · feedback */
.version-badge {
  position: fixed;
  /* Raised well clear of the viewport edge so it floats rather than hugs. */
  bottom: 1.75rem;
  left: 50%;
  transform: translateX(-50%);
  font-family: ui-monospace, "SF Mono", Menlo, monospace;
  font-size: 12.5px;
  letter-spacing: 0.02em;
  color: var(--text-dim);
  background: var(--bg-elevated);
  border: 1px solid var(--accent-soft);
  border-radius: 999px;
  /* Uniform padding so the collapsed state (just the home icon) is a
     proper circle, not an oval. */
  padding: 7px;
  /* Resting opacity is comfortably visible without screaming; hover
     pushes it to full 1.0 for the "I can be clicked" affordance. */
  opacity: 0.85;
  user-select: none;
  z-index: 50;
  transition: opacity 0.2s ease, border-color 0.2s ease, box-shadow 0.2s ease;
  display: inline-flex;
  align-items: center;
  /* No parent gap — sides own their internal gap and use margin-when-
     expanded to space themselves from the home icon. With sides at
     width:0 collapsed, this lets the badge shrink to a perfect circle. */
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.12);
  white-space: nowrap;
}
.version-badge:hover,
.version-badge:focus-within,
.version-badge.is-open {
  opacity: 1;
  border-color: var(--accent);
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.18);
}

.version-sep { opacity: 0.45; }

.version-link,
.version-link:visited {
  color: inherit;
  text-decoration: underline;
  text-decoration-style: dotted;
  text-decoration-color: currentColor;
  text-underline-offset: 2px;
  position: relative;
}
.version-link:hover {
  color: var(--accent);
  text-decoration-color: var(--accent);
}

/* "You are here" treatment for the link representing the current page.
   Matches the home icon's .is-current styling — accent color, no hover
   color change, default cursor — so the user can see at a glance which
   page they're on without thinking about it. */
.version-link.is-current,
.version-link.is-current:visited,
.version-link.is-current:hover {
  color: var(--accent);
  text-decoration-color: var(--accent);
  cursor: default;
}

/* ---------- Collapsing side groups ----------
   At rest, both sides collapse to width 0 / opacity 0, leaving only the
   central home icon visible — the bar reads as a small round button.
   On hover or focus-within, both sides slide out to identical widths so
   the home icon stays dead-centered. Each side's justify-content pushes
   its content inward toward the home icon, so the slack from the equal-
   width slots ends up on the outer edges where the eye doesn't notice.
   The sides also sit 3px above the centerline and slightly less opaque
   than the home icon, so they read as floating accents. */
.version-side {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  overflow: hidden;
  white-space: nowrap;
  width: 0;
  opacity: 0;
  margin: 0;
  transform: translateY(-3px);
  transition: width   0.35s cubic-bezier(0.4, 0, 0.2, 1),
              opacity 0.22s ease,
              margin  0.3s  cubic-bezier(0.4, 0, 0.2, 1);
}
.version-side-left  { justify-content: flex-end;   }
.version-side-right { justify-content: flex-start; }

.version-badge:hover .version-side,
.version-badge:focus-within .version-side,
.version-badge.is-open .version-side {
  /* Tuned to comfortably fit each side's content at 12.5px monospace.
     Left side "patch notes · about ·" with internal 8px gaps measures
     ~170px (plus ~12px when the red dot is visible inside the link);
     right side "· roadmap · feedback" measures ~155px. 185px covers
     both with buffer — update if link labels change. */
  width: 185px;
  opacity: 0.8;
}
.version-badge:hover .version-side-left,
.version-badge:focus-within .version-side-left,
.version-badge.is-open .version-side-left  { margin-right: 8px; }
.version-badge:hover .version-side-right,
.version-badge:focus-within .version-side-right,
.version-badge.is-open .version-side-right { margin-left:  8px; }

/* ---------- Home icon in the bottom nav ----------
   Always visible — the only element that survives the collapsed state.
   Renders as a link on every page EXCEPT index.html, where it's a
   <span> with .is-current (still visible, but non-interactive and
   accent-colored as a "you're here" cue). */
.version-home {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  /* Strip the dotted underline the text links use — the icon is its own
     affordance. */
  text-decoration: none;
  border-bottom: none;
  color: var(--text-dim);
  transition: color 0.15s ease, transform 0.15s ease;
  flex-shrink: 0;
}
.version-home svg {
  width: 14px;
  height: 14px;
  display: block;
}
a.version-home:hover {
  color: var(--accent);
  transform: translateY(-1px);
}
.version-home.is-current {
  color: var(--accent);
  cursor: default;
}

/* When the pill expands, the home icon swells and lifts clear of the bar
   so it reads as the anchor the rest of the nav fans out from, rather
   than just another item in the row. */
.version-badge:hover .version-home,
.version-badge:focus-within .version-home,
.version-badge.is-open .version-home {
  transform: scale(1.4) translateY(-2px);
  color: var(--accent);
}

/* Red-dot indicator for unseen patch notes. Lives inside the "patch
   notes" link in the bottom nav. Toggled by inline scripts on each page
   based on localStorage 'etac-seen-version' vs the footer's
   .credit-version text. A gentle pulse draws the eye without being
   obnoxious. */
.version-dot {
  display: inline-block;
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: #ef4444;
  margin-left: 5px;
  vertical-align: middle;
  box-shadow: 0 0 0 0 rgba(239, 68, 68, 0.55);
  animation: version-dot-pulse 1.8s ease-out infinite;
}
.version-dot[hidden] { display: none; }

/* Edge variant — pinned to the badge's top-right corner so it stays
   visible while the nav is collapsed to just the home button (when
   the in-link dot is hidden inside a width:0 side group). Two dots,
   same red pulse, toggled together by the inline script. */
.version-dot.is-edge {
  position: absolute;
  top: -2px;
  right: -2px;
  margin: 0;
  transition: opacity 0.15s ease;
}

/* Once the pill expands, the in-link dot beside "patch notes" takes over —
   hide the corner dot so the notification reads as moving into the link
   rather than staying stuck to the pill. */
.version-badge:hover .version-dot.is-edge,
.version-badge:focus-within .version-dot.is-edge,
.version-badge.is-open .version-dot.is-edge {
  opacity: 0;
}

@keyframes version-dot-pulse {
  0%   { box-shadow: 0 0 0 0   rgba(239, 68, 68, 0.55); }
  70%  { box-shadow: 0 0 0 6px rgba(239, 68, 68, 0); }
  100% { box-shadow: 0 0 0 0   rgba(239, 68, 68, 0); }
}

@media (max-width: 560px) {
  .version-badge {
    bottom: 1.25rem;
    font-size: 11px;
    /* Slightly tighter circular padding on narrow screens */
    padding: 6px;
  }
  .version-home svg { width: 13px; height: 13px; }
  /* Narrower expanded sides on mobile to keep the open bar from
     overflowing tight viewports. */
  .version-badge:hover .version-side,
  .version-badge:focus-within .version-side,
  .version-badge.is-open .version-side { width: 165px; }
}

/* ---------- About page content ---------- */
.about-content {
  margin-top: 0.5rem;
}
.about-content section {
  margin-bottom: 1.75rem;
}
.about-content h2 {
  font-size: 16px;
  font-weight: 700;
  color: var(--accent);
  margin: 0 0 0.5rem;
  letter-spacing: 0.02em;
  text-transform: uppercase;
}
.about-content p,
.about-content ul,
.about-content ol {
  font-size: 15px;
  line-height: 1.65;
  color: var(--text);
  margin: 0 0 0.6rem;
}
.about-content ul,
.about-content ol {
  padding-left: 1.25rem;
}
.about-content li {
  margin-bottom: 0.4rem;
}
.about-content code {
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  padding: 1px 6px;
  border-radius: 4px;
  font-size: 13px;
  font-family: ui-monospace, "SF Mono", Menlo, monospace;
}
.about-content .screenshot-placeholder {
  font-size: 13px;
  color: var(--text-dim);
  font-style: italic;
}
.about-content .disclaimer-section {
  background: var(--accent-soft);
  border: 1px solid var(--accent);
  padding: 1rem 1.25rem;
  border-radius: 8px;
}
.about-content .disclaimer-section h2 {
  margin-top: 0;
}

/* ---------- Shared small "feedback" button ----------
   Used inline on the roadmap aside and as a standalone button on the
   about page. Deliberately understated — sits inline with surrounding
   prose without screaming for attention, then warms to the accent color
   on hover. */
.feedback-link,
.feedback-link:visited {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 2px 8px;
  font-size: 11px;
  font-weight: 500;
  /* Match the muted tone of surrounding aside-foot prose so the button
     reads as part of the sentence, not a separate UI element. */
  color: var(--text-dim);
  background: var(--bg-input);
  border: 1px solid var(--border);
  border-radius: 5px;
  text-decoration: none;
  cursor: pointer;
  line-height: 1.4;
  transition: color 0.15s, border-color 0.15s, background 0.15s;
}
.feedback-link:hover {
  color: var(--accent);
  border-color: var(--accent);
  background: var(--accent-soft);
}
.feedback-link:focus-visible {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 2px var(--accent-soft);
}

/* Standalone (about-page) variant: a small centered prompt with a touch
   more breathing room around it. */
.feedback-prompt {
  text-align: center;
  margin: 1.5rem 0 0;
  padding-top: 1.25rem;
  border-top: 1px solid var(--border);
  font-size: 13px;
  color: var(--text-dim);
}
.feedback-prompt .feedback-link { margin-left: 6px; }

/* ---------- Patch notes page ---------- */
.patch-notes-content {
  margin-top: 0.5rem;
}
.patch-version {
  margin-bottom: 1.75rem;
  border-left: 2px solid var(--accent);
  padding-left: 1rem;
}
.patch-version h2 {
  font-size: 18px;
  font-weight: 700;
  color: var(--accent);
  margin: 0 0 0.5rem;
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 10px;
}
.patch-date {
  font-size: 12px;
  font-weight: 400;
  color: var(--text-dim);
}
.patch-version ul {
  list-style: disc;
  padding-left: 1.25rem;
  font-size: 15px;
  line-height: 1.65;
  color: var(--text);
  margin: 0;
}
.patch-version li {
  margin-bottom: 0.35rem;
}

/* ---------- Roadmap page ----------
   Mirrors the patch-notes column treatment: each status group gets its
   own left-border accent in the color that matches its urgency. */
.roadmap-content { margin-top: 0.5rem; }

.roadmap-section {
  margin-bottom: 1.75rem;
  border-left: 2px solid var(--text-dim);
  padding-left: 1rem;
}
.roadmap-section h2 {
  font-size: 18px;
  font-weight: 700;
  margin: 0 0 0.6rem;
  display: flex;
  align-items: center;
  gap: 10px;
  color: var(--text);
}
.roadmap-status {
  display: inline-block;
  padding: 3px 10px;
  border-radius: 999px;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  border: 1px solid currentColor;
  background: var(--accent-soft);
}
.roadmap-section.is-now    { border-left-color: var(--accent); }
.roadmap-section.is-now    .roadmap-status { color: var(--accent); }
.roadmap-section.is-next   .roadmap-status { color: var(--text-dim); background: transparent; }
.roadmap-section.is-maybe  { border-left-style: dashed; }
.roadmap-section.is-maybe  .roadmap-status {
  color: var(--text-dim);
  background: transparent;
  opacity: 0.85;
}
.roadmap-section ul {
  list-style: disc;
  padding-left: 1.25rem;
  font-size: 15px;
  line-height: 1.65;
  color: var(--text);
  margin: 0;
}
.roadmap-section li { margin-bottom: 0.4rem; }
.roadmap-section li strong { color: var(--accent); }

/* ---------- Mobile responsiveness ---------- */
@media (max-width: 560px) {
  body { padding: 2rem 0.75rem 5rem; }

  /* Shrink + tuck the theme switcher on small screens. (These rules used to
     target a stale `.theme-toggle` class that no longer exists in the markup,
     so they never applied — the switcher stayed full-size on mobile.) */
  .theme-switcher {
    top: 0.75rem;
    right: 0.75rem;
    height: 36px;
  }
  .theme-option { width: 36px; height: 36px; font-size: 16px; }
  /* Keep the expanded options the same diameter as the collapsed circle so
     the pill stays a tidy rounded shape on phones. */
  .theme-switcher:hover .theme-option:not(.active),
  .theme-switcher:focus-within .theme-option:not(.active) { width: 36px; }

  h1 { font-size: 26px; }
  .caveman-svg { width: 64px; }

  /* Stack the sentence-builder pieces vertically */
  .inputs {
    flex-direction: column;
    align-items: stretch;
  }
  .inputs > * + * { margin-left: 0; margin-top: 6px; }
  .inputs span { text-align: center; color: var(--text-muted); font-size: 14px; }
  select,
  input[type="text"] { min-width: 0; width: 100%; }

  /* Make buttons stack at narrow widths */
  .buttons { flex-direction: column; }
  .buttons > * + * { margin-left: 0; margin-top: 10px; }
  .buttons button { width: 100%; }

  /* On mobile the morph collapses vertical space instead of horizontal */
  body[data-mode="plain"] #character-btn,
  body[data-mode="plain"] #to-a-word,
  body[data-mode="plain"] #surprise-btn {
    margin-top: 0;
    max-height: 0;
  }

  .output-card-inner { padding: 1.2rem 1rem 1rem; }
  .rendered { font-size: 14px; line-height: 1.65; }
}

/* ---------- Visually-hidden helper (screen-reader-only) ----------
   Keeps content in the accessibility tree but off-screen. Used for the
   live-region status announcer so streaming doesn't spam the visible UI. */
.sr-only {
  position: absolute !important;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* ---------- Output card entrance ----------
   Results rise in gently so they feel "delivered" rather than popping. */
@keyframes card-rise {
  from { opacity: 0; transform: translateY(10px); }
  to   { opacity: 1; transform: none; }
}
.output-card { animation: card-rise 0.34s cubic-bezier(0.2, 0.8, 0.2, 1) both; }

/* ---------- Empty state (pre-first-explanation) ----------
   A friendly nudge in the output slot so the page doesn't read as blank.
   The bone bobs to give the caveman theme a little life. */
.empty-state {
  margin-top: 0.5rem;
  padding: 1.9rem 1.25rem;
  text-align: center;
  border: 1px dashed var(--border);
  border-radius: 12px;
  background: color-mix(in oklab, var(--bg-elevated) 55%, transparent);
  animation: card-rise 0.3s ease both;
}
.empty-state-emoji {
  font-size: 30px;
  line-height: 1;
  margin-bottom: 0.5rem;
  animation: bob 3s ease-in-out infinite;
}
.empty-state p { margin: 0; font-size: 14px; color: var(--text-muted); }
.empty-state strong { color: var(--accent); }
@keyframes bob {
  0%, 100% { transform: translateY(0); }
  50%      { transform: translateY(-5px); }
}

/* ---------- Caveman charm ----------
   A slow idle "breathing" sway, and a playful tilt when you hover the
   header — leans into the mascot without being distracting. */
.caveman-svg {
  transform-origin: 50% 90%;
  animation: caveman-breathe 4.5s ease-in-out infinite;
  transition: transform 0.25s ease;
}
@keyframes caveman-breathe {
  0%, 100% { transform: translateY(0)    rotate(0deg);    }
  50%      { transform: translateY(-2px) rotate(-1.2deg); }
}
.page-header:hover .caveman-svg {
  animation-play-state: paused;
  transform: rotate(3deg) scale(1.05);
}

/* ---------- Respect reduced-motion ----------
   Honor the OS "reduce motion" preference: neutralize animations and
   transitions site-wide, and drop the sparkle particles entirely. Placed
   last so it overrides everything above. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
    scroll-behavior: auto !important;
  }
  .sparkle { display: none !important; }
}

/* Inline formatting the owner's text editor can apply (content.js). These
   live outside the edit-mode block because visitors see them too. */
.t-accent { color: var(--accent); }
.t-dim { color: var(--text-dim); }

/* ================= Owner text-edit mode (content.js) =================
   Lives in style.css (not hub.css) because every page loads this sheet —
   including roadmap.html, which skips hub.css. The dashed outlines only
   appear for the owner with edit mode on; visitors never see any of it. */
body.etac-copyedit [data-copy] {
  outline: 1.5px dashed color-mix(in oklab, var(--accent) 55%, transparent);
  outline-offset: 3px; border-radius: 4px; cursor: text;
}
body.etac-copyedit [data-copy]:hover { outline-style: solid; background: var(--accent-soft); }
[data-copy].etac-copyedit-active {
  outline: 2px solid var(--accent); outline-offset: 3px;
  background: var(--bg-input); cursor: text;
}
.etac-copytoast {
  position: fixed; left: 50%; bottom: 64px; transform: translate(-50%, 8px); z-index: 90;
  font: 650 13px -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  color: var(--text); background: var(--bg-elevated);
  border: 1px solid var(--border); border-radius: 99px; padding: 9px 16px;
  box-shadow: 0 8px 24px rgba(0, 0, 0, .2);
  opacity: 0; pointer-events: none; transition: opacity .18s, transform .18s;
}
.etac-copytoast.is-show { opacity: 1; transform: translate(-50%, 0); }
.etac-copytoast.is-error { border-color: var(--warning-bd); color: var(--warning-fg); }
.etac-copyfab {
  position: fixed; right: 18px; bottom: 18px; z-index: 89;
  width: 44px; height: 44px; border-radius: 50%; font-size: 18px; cursor: pointer;
  background: var(--bg-elevated); border: 1px solid var(--border);
  box-shadow: 0 4px 12px rgba(0, 0, 0, .2);
}
.etac-copyfab:hover { border-color: var(--accent); }

/* Deleted patch-note points ('' override): gone for visitors, so the card
   shrinks to fit; ghosted with a restore button for the owner in edit mode. */
.etac-copyhidden { display: none !important; }
body.etac-copyedit .etac-copyhidden {
  display: list-item !important;
  opacity: .45; text-decoration: line-through;
}
body.etac-copyedit .pn-card li[data-copy] { position: relative; padding-right: 38px; }
.etac-copydel {
  position: absolute; right: 4px; top: 2px;
  width: 24px; height: 24px; border-radius: 50%;
  font: 700 14px/1 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  color: var(--text-dim); background: var(--bg-elevated);
  border: 1px solid var(--border); cursor: pointer; padding: 0;
}
.etac-copydel:hover { border-color: var(--warning-bd); color: var(--warning-fg); }
.etac-copydel.is-restore { text-decoration: none; opacity: 1; }
.etac-copydel.is-restore:hover { border-color: var(--accent); color: var(--accent); }

/* "+ Add a point" under each data-copy-list (edit mode only — the button
   itself is injected by content.js, so no body gate needed). */
.etac-copyadd {
  margin-top: 10px; padding: 6px 14px; cursor: pointer;
  font: 650 12px -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  color: var(--accent); background: transparent;
  border: 1.5px dashed color-mix(in oklab, var(--accent) 55%, transparent);
  border-radius: 99px;
}
.etac-copyadd:hover { border-style: solid; background: var(--accent-soft); }

/* Formatting toolbar — floats bottom-center while an editor is open. */
.etac-copybar {
  position: fixed; left: 50%; bottom: 108px; transform: translateX(-50%);
  z-index: 90; display: flex; gap: 4px; padding: 5px;
  background: var(--bg-elevated); border: 1px solid var(--border);
  border-radius: 99px;
  box-shadow: 0 8px 24px rgba(0, 0, 0, .2);
}
.etac-copybar__btn {
  width: 32px; height: 32px; border-radius: 50%; cursor: pointer;
  border: 1px solid transparent; background: transparent;
  font: 700 14px -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  color: var(--text); padding: 0;
}
.etac-copybar__btn:hover { border-color: var(--border); background: var(--bg-input); }
.etac-copybar__btn.is-accent { color: var(--accent); }
.etac-copybar__btn.is-dim { color: var(--text-dim); }
.etac-copybar__btn.is-clear { font-weight: 500; }
@media (prefers-reduced-motion: reduce) {
  .etac-copytoast { transition: opacity .18s; transform: translate(-50%, 0); }
}
