/* Token Visualizer — instrument-panel interior, scoped to .token-visualizer-page
   Composition: a quiet input region (model + textarea), then a single bonded
   result slab containing the hero count, a load-bearing trust strip, the
   colored token tape (page texture), and the context-window gauge.

   Dimensional discipline: all layout spacing comes from the --space-* scale
   (4/8/12/16/24/32…). The only literal sizes are component specs defined
   once below (gauge track, token chip, legend swatch) — never ad-hoc px. */
.token-visualizer-page {
  --tv-accent: #9d8bf5;
  --tv-accent-strong: #b3a4ff;
  --tv-accent-soft: rgba(157, 139, 245, 0.13);

  /* token category palette — colour carries linguistic meaning */
  --tv-cat-word: rgba(125, 128, 240, 0.26);
  --tv-cat-cjk: rgba(240, 178, 108, 0.26);
  --tv-cat-number: rgba(80, 210, 178, 0.26);
  --tv-cat-punct: rgba(150, 170, 205, 0.22);
  --tv-cat-space: rgba(150, 170, 205, 0.13);

  /* component specs — fixed by role, defined once, reused */
  --tv-control-h: 44px;     /* every interactive control in the rail */
  --tv-track-h: 14px;       /* gauge track */
  --tv-swatch: 12px;        /* legend colour swatch */
  --tv-pill-pad: 4px 8px;   /* badges + drop hint */
}

[data-cat="word"]   { --tv-cat: var(--tv-cat-word); }
[data-cat="cjk"]    { --tv-cat: var(--tv-cat-cjk); }
[data-cat="number"] { --tv-cat: var(--tv-cat-number); }
[data-cat="punct"]  { --tv-cat: var(--tv-cat-punct); }
[data-cat="space"]  { --tv-cat: var(--tv-cat-space); }

/* ── the bench: two regions — composer above, the bonded result slab below. */
.tv-bench {
  display: grid;
  gap: var(--space-5);
}

/* ── region 1 — compose: the quiet input area ──────────────────────────── */
.tv-compose {
  display: grid;
  gap: var(--space-4);
}
.tv-rail {
  display: flex;
  align-items: flex-end;
  gap: var(--space-3);
  flex-wrap: wrap;
}
.tv-field {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  min-width: 0;
}
.tv-field__label {
  font-size: var(--text-xs);
  text-transform: uppercase;
  color: var(--color-text-faint);
}
.tv-select,
.tv-input-num {
  appearance: none;
  background: var(--color-surface-2);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
  color: var(--color-text);
  font: inherit;
  font-size: var(--text-sm);
  padding: 0 var(--space-3);
  min-height: var(--tv-control-h);
  transition: border-color var(--transition-fast);
}
.tv-select:hover,
.tv-input-num:hover { border-color: var(--color-surface-3); }
.tv-select {
  min-width: 200px;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6' fill='none' stroke='%238ca0c0' stroke-width='1.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M1 1l4 4 4-4'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right var(--space-3) center;
  padding-right: var(--space-6);
  cursor: pointer;
}
.tv-input-num { width: 160px; -moz-appearance: textfield; }
.tv-input-num::-webkit-outer-spin-button,
.tv-input-num::-webkit-inner-spin-button {
  -webkit-appearance: none;
  margin: 0;
}
.tv-select:focus-visible,
.tv-input-num:focus-visible,
.tv-textarea:focus-visible,
.tv-clear-inline:focus-visible {
  outline: 2px solid var(--tv-accent);
  outline-offset: 2px;
}

/* input well */
.tv-input-wrap { position: relative; }
.tv-textarea {
  display: block;
  width: 100%;
  box-sizing: border-box;
  min-height: 120px;
  resize: vertical;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  background: var(--color-bg-deep);
  color: var(--color-text);
  font-family: var(--font-mono);
  font-size: var(--text-sm);
  line-height: var(--line-base);
  padding: var(--space-4);
  padding-right: calc(var(--space-4) + 32px); /* room for inline × button */
  transition: border-color var(--transition-fast), background var(--transition-fast);
}
.tv-textarea::placeholder { color: var(--color-text-faint); }
.tv-textarea:hover { border-color: var(--color-surface-3); }
.tv-textarea:disabled { opacity: 0.5; }
.tv-input-wrap.is-dragover .tv-textarea {
  border-color: var(--tv-accent);
  background: var(--tv-accent-soft);
}

/* inline × clear button — sits in the textarea's top-right corner */
.tv-clear-inline {
  position: absolute;
  top: var(--space-3);
  right: var(--space-3);
  width: 28px;
  height: 28px;
  min-height: 0;
  padding: 0;
  border-radius: var(--radius-sm);
  background: var(--color-surface-2);
  border: 1px solid var(--color-border);
  color: var(--color-text-muted);
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  transition: color var(--transition-fast), border-color var(--transition-fast),
              background var(--transition-fast);
}
.tv-clear-inline:hover {
  color: var(--color-text);
  border-color: var(--color-surface-3);
  background: var(--color-surface-3);
}
.tv-clear-inline[hidden] { display: none; }

.tv-drop-hint {
  position: absolute;
  right: var(--space-3);
  bottom: var(--space-3);
  margin: 0;
  padding: var(--tv-pill-pad);
  border-radius: var(--radius-pill);
  background: var(--color-surface-2);
  border: 1px solid var(--color-border);
  font-size: var(--text-xs);
  color: var(--color-text-faint);
  pointer-events: none;
  transition: opacity var(--transition-fast);
}
/* fade the hint out once there is text, so it never overlaps content */
.tv-textarea:not(:placeholder-shown) ~ .tv-drop-hint { opacity: 0; }
.tv-input-wrap.is-dragover .tv-drop-hint {
  opacity: 1;
  color: var(--color-on-primary);
  background: var(--tv-accent);
  border-color: var(--tv-accent);
}

/* status line — reserves a line so showing/clearing it never shifts layout */
.tv-status {
  margin: 0;
  min-height: 1.4em;
  font-size: var(--text-sm);
  color: var(--color-text-muted);
}
.tv-status[data-tone="error"] { color: var(--color-danger); }
.tv-status[data-tone="success"] { color: var(--color-success); }
.tv-status[data-tone="warn"] { color: var(--color-warning); }
.tv-status[data-tone="loading"] { color: var(--color-text-faint); }

/* ── region 2 — the result slab: count + trust + tape + gauge bonded ─────
   One panel, four stacked sections. Surface depth marks it as the payoff —
   no gradient, no chrome — just a single lifted surface against the page. */
.tv-result {
  background: var(--color-surface-2);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  padding: var(--space-5);
  display: grid;
  gap: var(--space-5);
}
/* Empty bench: hide the slab visually but keep `.tv-result` in the
   accessibility tree so its `aria-live="polite"` wiring continues to
   work across empty→has-text transitions. (If we used `display: none`
   on the outer element, some screen readers wouldn't announce the
   first token count after the panel becomes visible again — the
   live-region was outside the tree at the moment of the content
   change.) The visual collapse is identical: padding/border/bg gone,
   children hidden. */
.tv-result.tv-result--empty {
  background: transparent;
  border-color: transparent;
  padding: 0;
  gap: 0;
}
.tv-result.tv-result--empty > * { display: none; }
.tv-result__head {
  display: flex;
  align-items: flex-start;
  gap: var(--space-4);
  flex-wrap: wrap;
}

/* headline count — clearly the dominant figure on the panel */
.tv-readout {
  flex: 1 1 auto;
  min-width: 0;
}
.tv-readout__count {
  display: flex;
  align-items: baseline;
  gap: var(--space-3);
  flex-wrap: wrap;
}
.tv-readout__num {
  font-family: var(--font-mono);
  font-weight: 600;
  letter-spacing: -0.02em;
  line-height: 1;
  color: var(--color-text);
  font-variant-numeric: tabular-nums;
  /* sized by digit count via data-len on the parent — see below */
  font-size: clamp(3.5rem, 2.5rem + 4vw, 5rem);
}
/* discrete size classes so a 9-digit count (e.g. 1,050,000) never overflows
   on mobile — picked by JS from the length of the formatted count string. */
.tv-readout__count[data-len="mid"]  .tv-readout__num { font-size: clamp(3rem, 2.25rem + 3vw, 4.25rem); }
.tv-readout__count[data-len="long"] .tv-readout__num { font-size: clamp(2.25rem, 1.75rem + 2vw, 3.25rem); }
.tv-readout__unit-word {
  font-size: var(--text-sm);
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--color-text-muted);
}

/* meta line — char / word counts as a quiet annotation, not a side panel */
.tv-meta {
  margin: var(--space-3) 0 0;
  font-family: var(--font-mono);
  font-size: var(--text-sm);
  color: var(--color-text-muted);
  font-variant-numeric: tabular-nums;
}
.tv-meta__num { color: var(--color-text); font-weight: 600; }
.tv-meta__sep { color: var(--color-text-faint); margin: 0 var(--space-2); }

/* copy-result action — sits at the end of the head row, top-aligned with
   the count baseline so it never competes with the hero number's mass */
.tv-copy-btn {
  margin-left: auto;
  align-self: flex-start;
  flex-shrink: 0;
  min-height: var(--tv-control-h);
  padding: 0 var(--space-4);
  font-size: var(--text-sm);
}
.tv-copy-btn:disabled {
  opacity: 0.45;
  cursor: default;
}
.tv-copy-btn.is-copied {
  color: var(--color-success);
  border-color: var(--color-success);
}

/* ── trust strip — load-bearing element under the headline number ─────────
   For "exact" it states the source. For "estimate" it states the error bar
   and the methodology — so "is this number trustworthy" answers itself
   in the same visual lane as the answer, not in a footnote.

   Block flow (not flex) so when the strip wraps on narrow viewports it
   continues as a single paragraph instead of breaking into a tag-chip on
   row 1 and body copy on row 2. The dot is inline-block, label/detail
   are inline spans — one sentence whether it fits in one line or three. */
.tv-trust {
  display: block;
  margin: 0;
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-sm);
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  line-height: 1.55;
}
.tv-trust[hidden] { display: none; }
.tv-trust[data-kind="exact"] {
  background: rgba(79, 212, 181, 0.10);
  color: var(--color-success);
}
.tv-trust[data-kind="estimate"] {
  background: rgba(246, 168, 95, 0.12);
  color: var(--color-warning);
}
.tv-trust__dot {
  display: inline-block;
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: currentColor;
  margin-right: var(--space-2);
  vertical-align: 0.1em;
}
/* Reads as a confident note, not a status pill template. Label leads in
   bold, then an inline word-break, then the detail flows as ordinary prose
   in the muted text colour. No artificial em-dash — that just collided
   with the ±10–20% range and the in-sentence punctuation. */
.tv-trust__label { font-weight: 600; margin-right: 0.4em; }
.tv-trust__detail { color: var(--color-text-muted); font-weight: 400; }

/* the data-currency note under the gauge */
.tv-gauge__note {
  margin: 0;
  font-size: var(--text-xs);
  color: var(--color-text-faint);
}

/* ── context-window gauge ──────────────────────────────────────────────── */
.tv-gauge {
  display: grid;
  gap: var(--space-2);
  padding-top: var(--space-4);
  border-top: 1px solid var(--color-border);
}
.tv-gauge__head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-3);
  flex-wrap: wrap;
}
.tv-gauge__title {
  margin: 0;
  font-size: var(--text-sm);
  font-weight: 600;
  color: var(--color-text);
}
.tv-gauge__model {
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  color: var(--color-text-muted);
}
.tv-gauge__track {
  position: relative;
  height: var(--tv-track-h);
  margin-top: var(--space-1);
  border-radius: var(--radius-pill);
  background: var(--color-bg-deep);
  border: 1px solid var(--color-border);
  overflow: hidden;
}
.tv-gauge__fill {
  position: absolute;
  inset: 0;
  transform-origin: left center;
  transform: scaleX(0);
  transition: transform 0.5s cubic-bezier(0.34, 0.72, 0.26, 1),
              background var(--transition-base);
}
.tv-gauge__track[data-state="idle"] .tv-gauge__fill { background: transparent; }
.tv-gauge__track[data-state="ok"] .tv-gauge__fill { background: var(--tv-accent); }
.tv-gauge__track[data-state="warn"] .tv-gauge__fill { background: var(--color-warning); }
.tv-gauge__track[data-state="over"] .tv-gauge__fill { background: var(--color-danger); }
.tv-gauge__track[data-state="over"] { border-color: var(--color-danger); }
.tv-gauge__tick {
  position: absolute;
  top: 0;
  bottom: 0;
  width: 1px;
  background: rgba(234, 242, 255, 0.22);
  pointer-events: none;
}
/* scale labels positioned to sit exactly under the track + ticks */
.tv-gauge__scale {
  position: relative;
  height: 1.2em;
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  color: var(--color-text-faint);
}
.tv-gauge__scale span { position: absolute; top: 0; }
.tv-gauge__scale span:nth-child(1) { left: 0; }
.tv-gauge__scale span:nth-child(2) { left: 25%; transform: translateX(-50%); }
.tv-gauge__scale span:nth-child(3) { left: 50%; transform: translateX(-50%); }
.tv-gauge__scale span:nth-child(4) { left: 75%; transform: translateX(-50%); }
.tv-gauge__scale span:nth-child(5) { left: 100%; transform: translateX(-100%); }
.tv-gauge__readout {
  margin: 0;
  font-size: var(--text-sm);
  line-height: var(--line-base);
  color: var(--color-text-muted);
}
.tv-gauge__track[data-state="warn"] ~ .tv-gauge__readout { color: var(--color-warning); }
.tv-gauge__track[data-state="over"] ~ .tv-gauge__readout { color: var(--color-danger); }

/* ── token tape — now inside the result slab, above the gauge ─────────────
   The colored chips are the page texture; tape font-size bumped so they
   read as the product's signature, not as a sub-result. */
.tv-tape-block {
  display: grid;
  gap: var(--space-3);
}
.tv-block__head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-3) var(--space-4);
  flex-wrap: wrap;
}
.tv-block__title {
  margin: 0;
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--color-text-muted);
}
.tv-legend {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2) var(--space-3);
  margin: 0;
  padding: 0;
  list-style: none;
}
.tv-legend__item {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  font-size: var(--text-xs);
  color: var(--color-text-muted);
}
.tv-legend__sw {
  width: var(--tv-swatch);
  height: var(--tv-swatch);
  border-radius: var(--radius-xs);
  background: var(--tv-cat);
  /* bright outline so even the faint categories (space) read as a swatch */
  border: 1px solid rgba(234, 242, 255, 0.34);
}
.tv-tape {
  padding: var(--space-4);
  background: var(--color-bg-deep);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  font-family: var(--font-mono);
  font-size: var(--text-md);
  line-height: 2;
  word-break: break-word;
}
.tv-token {
  display: inline-block;
  vertical-align: middle;
  white-space: pre-wrap;
  line-height: 1.4;
  padding: 2px 6px;
  margin: 0 var(--space-1) var(--space-1) 0;
  border-radius: var(--radius-xs);
  background: var(--tv-cat);
  outline: 1px solid transparent;
  transition: outline-color var(--transition-fast);
}
.tv-token[data-cat="space"] { color: var(--color-text-faint); }
.tv-token:hover { outline-color: rgba(234, 242, 255, 0.3); }
.tv-tape__empty {
  margin: 0;
  padding: var(--space-5) var(--space-4);
  text-align: center;
  background: var(--color-bg-deep);
  border: 1px dashed var(--color-border);
  border-radius: var(--radius-md);
  font-size: var(--text-sm);
  color: var(--color-text-faint);
}
.tv-tape__empty[hidden] { display: none; }
.tv-tape__note {
  margin: 0;
  font-size: var(--text-xs);
  color: var(--color-text-faint);
}
/* Reminder that the chips are always GPT o200k, even when an estimate model
   is selected — quiet inline caveat, not a banner. */
.tv-tape__provider-note {
  margin: 0;
  font-size: var(--text-xs);
  line-height: 1.5;
  color: var(--color-warning);
  opacity: 0.85;
}
.tv-tape__provider-note[hidden] { display: none; }

/* ── motion ────────────────────────────────────────────────────────────── */
@media (prefers-reduced-motion: reduce) {
  .tv-gauge__fill { transition: background var(--transition-base); }
}

/* ── responsive ────────────────────────────────────────────────────────── */
@media (max-width: 720px) {
  .tv-bench { gap: var(--space-4); }
  .tv-result { padding: var(--space-4); gap: var(--space-4); }
  .tv-tape { font-size: var(--text-sm); line-height: 1.9; }
}
@media (max-width: 600px) {
  .tv-rail { gap: var(--space-3) var(--space-2); }
  .tv-select { min-width: 0; width: 100%; }
  .tv-field { width: 100%; }
  .tv-field--custom .tv-input-num { width: 100%; }
  .tv-result__head { gap: var(--space-3); }
  /* On a phone the Copy button is a secondary action, not a rival to the
     hero number — small, quiet, right-aligned. */
  .tv-copy-btn {
    margin-left: auto;
    width: auto;
    align-self: flex-start;
    padding: 0 var(--space-3);
    font-size: var(--text-xs);
    min-height: 32px;
  }
}
