/* Page chrome: the bar the dial sits under, and the share control in it. */
html, body { min-height: 100%; }
#stage { min-height: 100vh; }

.tone-bar {
  position: sticky; top: 0; z-index: 20;
  display: flex; align-items: center; justify-content: space-between;
  height: 64px; padding: 0 24px;
}
.tone-bar .wordmark img { display: block; }
.tone-bar-right { display: flex; align-items: center; gap: 18px; font-size: 14px; }

.tone-share {
  display: inline-flex; align-items: center; gap: 8px;
  /* The share control is the button's outline sibling: same height, same pill. */
  height: var(--btn-h); padding: 0 var(--btn-px);
  border: 1px solid rgba(19,19,18,0.16); border-radius: 999px;
  font: inherit; font-size: 14px; font-weight: 500; color: var(--ink-1); background: transparent;
  cursor: pointer; transition: background var(--dur-1) var(--ease-swift), scale 150ms ease-out;
}
.tone-share:hover { background: var(--paper-1); }
.tone-share:active { scale: 0.97; }
.tone-share:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 2px; }

@media (prefers-reduced-motion: reduce) { .tone-share { transition: none; } }

/* ═══ The Dial: a single screen. Cause on the left, cost on the right, evidence in a slide-out. ═══ */
.body-dial { background: var(--dial-bg, var(--paper-1)); overflow: hidden; }
.v-dial { --page-max: 1160px; --page-pad: 28px; --type-lg: clamp(26px, 3.4vw, 48px); --type-figure: clamp(46px, 5.4vw, 78px); --dial-bg: var(--paper-1); background: var(--dial-bg); color: var(--ink-1); transition: background 200ms linear; height: 100vh; overflow: hidden; }
.dial-bar { background: var(--dial-bg); transition: background 200ms linear; padding: 0 var(--page-pad); max-width: var(--page-max); margin: 0 auto; height: 64px; }
.dial-bar-link { border: 0; background: transparent; font: inherit; font-size: 14px; font-weight: 500; color: var(--ink-2); cursor: pointer; padding: 6px 4px; border-radius: var(--r-2); }
.dial-bar-link:hover { color: var(--ink-1); text-decoration: underline; text-underline-offset: 3px; }
.dial-bar-link:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 2px; }

.dial-hero { position: relative; height: calc(100vh - 64px); overflow: hidden; cursor: ew-resize; user-select: none; -webkit-user-select: none; touch-action: pan-y; }
.dial-hero.is-dragging { cursor: grabbing; }
.dial-field { position: absolute; inset: 0; pointer-events: none; }
.dial-grid {
  position: relative; z-index: 1; height: 100%;
  display: grid; grid-template-columns: minmax(0, var(--col-left, 60fr)) minmax(0, var(--col-right, 40fr));
  grid-template-rows: auto auto; align-content: start;
  /* Anchored to this tier's short edge, like the tier below. 941px is where the
     heading goes to 48px and the row gap to 64, so the tier is at its tightest
     the moment it opens: 20px there, growing with the height. */
  gap: 64px 64px; padding: clamp(20px, calc(20px + (100vh - 941px) * 0.40), 104px) var(--page-pad) 24px;
  max-width: var(--page-max); margin: 0 auto;
}
/* The heading spans both columns: the right column's first row is empty, and
   a 48px display line inside the 624px left column gets only about 26
   characters, which breaks the opening sentence across lines.
   The measure is on the h1 and in em, so it tracks the type size: a px cap
   tuned at 48px goes ragged again as soon as the clamp drops the heading to
   36px, and the line count then holds at every width we support rather than
   drifting with the viewport. 15em is the knee for this copy: the narrowest
   measure that still breaks evenly. It sets four lines of 638 / 705 / 653 /
   591 at 48px, last line at 84% of the longest. 16em breaks identically in a
   wider box, so it only wastes the room. 17 to 19em leave a stub of 26 to 36%.
   14em and below go to five lines. 20em would fit three, but at 960px the
   measure is wider than the headline wants to be read at.
   Retune it whenever the copy changes. The opening claim does not own line one
   on its own, but it is in ink against grey, so it still reads as its own
   unit. */
.dial-head { grid-column: 1 / -1; grid-row: 1; max-width: 1000px; }
.dial-instrument { grid-column: 1; grid-row: 2; }
.dial-right { grid-column: 2; grid-row: 2; }
.dial-instrument {
  display: flex; flex-direction: column; gap: 19px; align-self: start; max-width: 760px;
  padding: 22px 32px 22px; background: var(--paper-0); border-radius: 20px;
  border: 1px solid rgba(19,19,18,0.06);
  box-shadow: 0 1px 2px rgba(0,0,0,0.03), 0 8px 24px rgba(0,0,0,0.05), 0 24px 48px rgba(0,0,0,0.04);
}
/* A grid rather than a column of flex items, laid out the same: rows from the
   top, the slack of a pinned height at the bottom. The grid lets the unseen
   copy of the conversation share the real one's row (see html() in tone.js). */
.dial-instrument { display: grid; grid-template-columns: minmax(0, 1fr); align-content: start; }
.dial-config { grid-row: 1; }
.dial-instrument > .dial-chat { grid-row: 2; grid-column: 1; align-self: start; }
.dial-ghost { visibility: hidden; pointer-events: none; }
/* While the copy is there, the row is its height alone, as the pinned card is:
   reserveBubble() measures its phrasings without the ids that space the tone
   lines, which the copy also lacks, and the result can come out shorter than
   the conversation on show. The real conversation still lays out in full. */
.dial-instrument:has(> .dial-ghost) > .dial-chat:not(.dial-ghost) { margin-bottom: -9999px; }
.dial-ghost .dial-ghost-prompts { display: grid; grid-template-columns: minmax(0, 1fr); justify-items: end; }
.dial-ghost-prompts > .dial-bubble { grid-area: 1 / 1; }
@media (min-width: 961px) { .dial-ghost .dial-tax { display: none; } }
.dial-right { display: flex; flex-direction: column; justify-content: flex-start; align-items: flex-start; min-width: 0; }
/* Two columns: the readout centres on the card it shares a row with. tone.js
   measures the same centre and its margin then comes to nothing, so the page
   holds that position before the script runs and if it never does. */
@media (min-width: 961px) { .dial-right { justify-content: center; } }


/* ——— entrance ———
   Once per session, gated in tone.js. Everything rises a little and fades in,
   in reading order, while the field blooms behind it. Transform and opacity
   only, so nothing reflows, and `both` holds the first frame so no element
   flashes at full size before its turn. Keyframes rather than transitions
   because this runs once and always to completion. */
@keyframes dial-rise {
  from { opacity: 0; transform: translateY(14px); }
  to { opacity: 1; transform: none; }
}
.v-dial[data-intro] .tone-bar,
.v-dial[data-intro] .dial-head,
.v-dial[data-intro] .dial-instrument,
.v-dial[data-intro] .dial-tax,
.v-dial[data-intro] .dial-field-foot {
  animation: dial-rise 520ms cubic-bezier(0.32, 0.72, 0, 1) both;
}
.v-dial[data-intro] .tone-bar { animation-duration: 420ms; }
.v-dial[data-intro] .dial-head { animation-delay: 60ms; }
.v-dial[data-intro] .dial-instrument { animation-delay: 150ms; }
.v-dial[data-intro] .dial-tax { animation-delay: 240ms; }
.v-dial[data-intro] .dial-field-foot { animation-delay: 330ms; }
@media (prefers-reduced-motion: reduce) {
  .v-dial[data-intro] .tone-bar,
  .v-dial[data-intro] .dial-head,
  .v-dial[data-intro] .dial-instrument,
  .v-dial[data-intro] .dial-tax,
  .v-dial[data-intro] .dial-field-foot { animation: none; }
}

/* Type roles, and only these: display (the number), h1, body 16, small 14,
   mono label 13 uppercase. Everything on the page maps to one of them. */
/* One block: the claim in ink, the evidence continuing in grey at the same
   size, so it reads as a sentence rather than a headline and a caption. */
.dial-head h1 {
  font-size: var(--type-lg); line-height: var(--display-leading); letter-spacing: -0.02em;
  font-weight: 400; color: var(--ink-3); max-width: var(--head-measure, 15em); text-wrap: pretty;
}
.dial-head h1 b { font-weight: var(--heading-weight); color: var(--ink-1); }
.dial-head-mobile { display: none; }

/* config sentence with chips */
.dial-config { display: flex; flex-wrap: wrap; align-items: center; gap: 10px 12px; font-size: 16px; color: var(--ink-2); cursor: default; }
/* Wraps only when it has to. Held at nowrap, the row kept its full width in
   the narrow half of the two-column range and spilled out of the card and over
   the readout beside it. */
.dial-config-line { display: flex; flex-wrap: wrap; align-items: center; gap: 10px 12px; white-space: nowrap; }
.dial-chip-wrap { position: relative; }
.dial-chip {
  display: inline-flex; align-items: center; gap: 10px; height: 42px; padding: 0 14px;
  border: 1px solid rgba(19,19,18,0.16); border-radius: var(--r-pill); background: var(--paper-0);
  font: inherit; font-size: 16px; font-weight: 500; color: var(--ink-1); cursor: pointer; white-space: nowrap;
  box-shadow: 0 1px 2px rgba(0,0,0,0.04);
  transition: background var(--dur-1) var(--ease-swift), border-color var(--dur-1) var(--ease-swift), scale 150ms ease-out;
}
.dial-chip:hover { background: var(--paper-2); border-color: rgba(19,19,18,0.28); }
.dial-chip:active { scale: 0.98; }
.dial-chip[aria-expanded="true"] { border-color: var(--ink-1); }
.dial-chip:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 2px; }
.dial-chip-inner { display: inline-flex; align-items: center; gap: 10px; }
.dial-chev { width: 9px; height: 9px; margin-left: 2px; border-right: 1.5px solid currentColor; border-bottom: 1.5px solid currentColor; transform: translateY(-3px) rotate(45deg); opacity: 0.7; }
.dial-logo { display: inline-block; width: 18px; height: 18px; flex: none; background: currentColor; -webkit-mask: var(--logo) center / contain no-repeat; mask: var(--logo) center / contain no-repeat; }
.dial-logo-text { background: none; font-family: var(--font-mono); font-size: 12px; line-height: 18px; text-align: center; border: 1px solid currentColor; border-radius: 50%; }

.dial-menu {
  position: absolute; top: calc(100% + 8px); left: 0; z-index: 30; min-width: 280px; margin: 0; padding: 6px; list-style: none;
  background: var(--paper-0); border: 1px solid rgba(19,19,18,0.10); border-radius: 14px;
  box-shadow: 0 1px 2px rgba(0,0,0,0.04), 0 8px 24px rgba(0,0,0,0.10), 0 24px 48px rgba(0,0,0,0.08);
  transform-origin: top left; opacity: 0; transform: scale(0.96) translateY(-4px);
  transition: opacity 120ms ease-in, transform 120ms ease-in;
}
.dial-menu[data-open] { opacity: 1; transform: none; transition: opacity 160ms cubic-bezier(0.32, 0.72, 0, 1), transform 160ms cubic-bezier(0.32, 0.72, 0, 1); }
.dial-menu li[role=option] { display: grid; grid-template-columns: 18px 1fr auto; align-items: center; gap: 12px; height: 44px; padding: 0 12px; border-radius: 9px; font-size: 16px; font-weight: 500; color: var(--ink-1); cursor: pointer; }
.dial-menu li[role=option]:not(:has(.dial-logo)) { grid-template-columns: 1fr auto; }
.dial-menu li small { font-size: 14px; font-weight: 400; color: var(--ink-3); }
.dial-menu li[role=option]:hover, .dial-menu li[role=option]:focus-visible { background: var(--paper-1); outline: none; }
.dial-menu li[aria-selected="true"] { background: var(--ink-1); color: var(--paper-1); }
.dial-menu li[aria-selected="true"] small { color: rgba(244,242,236,0.6); }
.dial-menu-foot { margin-top: 4px; padding-top: 6px; border-top: 1px solid rgba(19,19,18,0.08); }
.dial-menu-foot-btn { width: 100%; height: 36px; border: 0; background: transparent; border-radius: 8px; font: inherit; font-size: 14px; color: var(--ink-2); text-align: left; padding: 0 12px; cursor: pointer; }
.dial-menu-foot-btn:hover { background: var(--paper-1); color: var(--ink-1); }

/* chat with the dial as its hinge */
.dial-chat { display: flex; flex-direction: column; gap: 18px; cursor: auto; }
.dial-turn { display: flex; gap: 12px; align-items: flex-start; }
.dial-turn-you { justify-content: flex-end; }
.dial-prompt-reserve { display: none; }
.dial-bubble { max-width: 88%; margin: 0; padding: 14px 18px; background: var(--paper-1); color: var(--ink-1); border-radius: 18px 18px 4px 18px; font-size: 16px; line-height: 1.5; transition: background 220ms var(--ease-swift); }
.dial-bubble[data-tone="polite"] { background: rgba(14,91,58,0.07); }
.dial-bubble[data-tone="angry"] { background: rgba(222,42,27,0.07); }
.dial-turn-model { padding-right: 8%; }
.dial-avatar { flex: none; width: 28px; height: 28px; margin-top: 2px; border-radius: 50%; background: var(--ink-1); color: var(--paper-1); display: grid; place-items: center; }
.dial-avatar .dial-logo { width: 14px; height: 14px; }
.dial-avatar .dial-logo-text { border: 0; font-size: 12px; }
.dial-turn-body { min-width: 0; }
.dial-reply-name { display: block; font-size: 14px; font-weight: 500; color: var(--ink-2); margin: 2px 0 6px; }
.dial-reply-text { margin: 0; font-size: 16px; line-height: 1.5; white-space: pre-line; color: var(--ink-1); overflow: hidden; }
/* The other answer sits unseen in the same cell, so the box is the taller of
   the two from the first paint: the height tone.js pins it to once it runs. */
.dial-reply-stack { display: grid; }
.dial-reply-stack > .dial-reply-text { grid-area: 1 / 1; }
.dial-reply-reserve { visibility: hidden; }
/* The tone sentence sits on its own line, above and below the task, rather than
   running inline with it. Inline, changing the prefix from 72 characters to 0
   re-wrapped the whole paragraph: across the seven phrasings the task sentence
   started in 411px of different horizontal positions and moved between three
   and four lines. That re-flow, not the fade, was what made a scrub feel rough.
   Each line reserves its tallest phrasing, so nothing below it moves either. */
.dial-wrap-line {
  display: grid; grid-template-rows: 0fr;
  transition: grid-template-rows 220ms var(--ease-swift);
}
.dial-wrap-line[data-open] { grid-template-rows: 1fr; }
.dial-wrap-clip { overflow: hidden; min-height: 0; }
#dial-pre-line .dial-wrap { display: inline-block; margin-bottom: 6px; }
#dial-post-line .dial-wrap { display: inline-block; margin-top: 6px; }
.dial-task-line { display: block; }
@media (prefers-reduced-motion: reduce) { .dial-wrap-line { transition: none; } }

/* The highlight is paired with the bubble's wash, so it takes the same
   duration and curve, and its own opacity transition is what swapInto drives. */
.dial-wrap {
  color: inherit; border-radius: 3px; padding: 0 2px; background: rgba(19,19,18,0.08);
  transition: background 220ms var(--ease-swift), opacity 170ms var(--ease-swift);
}
.dial-reply-text { transition: opacity 170ms var(--ease-swift); }
@media (prefers-reduced-motion: reduce) {
  .dial-wrap, .dial-reply-text, .dial-bubble { transition: none; }
}
.dial-bubble[data-tone="polite"] .dial-wrap { background: rgba(14,91,58,0.2); }
.dial-bubble[data-tone="angry"] .dial-wrap { background: rgba(222,42,27,0.2); }
.dial-wrap[hidden] { display: none; }

/* slider: a plain dot that opens into a face on hover.
   Entering elements ease out; the hover cue is gated to real pointers. */
.dial-control { position: relative; z-index: 2; cursor: default; padding: 2px 0 0; }
.dial-ticks {
  display: flex; justify-content: space-between; margin-bottom: 10px;
  font-family: var(--font-mono); font-size: 13px; letter-spacing: 0.08em;
  text-transform: uppercase; color: var(--ink-2);
}
.dial-tick-polite { color: var(--green-600); }
.dial-tick-angry { color: var(--red-600); }

.dial-track-wrap { position: relative; height: 44px; }
/* The rail and the filled portion are drawn here; the input stays for input. */
.dial-track-wrap::before,
.dial-track-wrap::after {
  content: ''; position: absolute; top: 50%; left: 0; height: 3px;
  margin-top: -1.5px; border-radius: 2px; pointer-events: none;
}
.dial-track-wrap::before { right: 0; background: rgba(19,19,18,0.12); z-index: 0; }
.dial-track-wrap::after {
  width: calc(var(--t) * 100%); background: var(--tone, var(--ink-1));
  transition: background 200ms linear; z-index: 0;
}

.dial-control input[type="range"] {
  position: relative; z-index: 1; width: 100%; height: 44px; margin: 0;
  background: transparent; -webkit-appearance: none; appearance: none; cursor: grab;
}
.dial-control input[type="range"]:active { cursor: grabbing; }
.dial-control input[type="range"]::-webkit-slider-runnable-track { height: 3px; background: transparent; }
.dial-control input[type="range"]::-moz-range-track { height: 3px; background: transparent; }
.dial-control input[type="range"]::-webkit-slider-thumb { -webkit-appearance: none; width: 44px; height: 44px; margin-top: -20.5px; border-radius: 50%; background: transparent; border: 0; }
.dial-control input[type="range"]::-moz-range-thumb { width: 44px; height: 44px; border-radius: 50%; background: transparent; border: 0; }
.dial-control input[type="range"]:focus { outline: none; }

.dial-face {
  position: absolute; top: 50%; width: 44px; height: 44px; z-index: 2;
  transform: translate(-50%, -50%); pointer-events: none;
}
/* box-shadow, not drop-shadow. The circle is r21 plus a 1px stroke inside a
   44x44 svg, so the artwork is flush with the element box on all four sides,
   and a filter's region is that box: the blur had nowhere to go and came out
   sliced flat into a square. A box-shadow is painted outside the border box
   and is never clipped, and at border-radius 50% on a 44x44 box its edge is
   the same r22 circle the artwork draws. */
.dial-face-inner {
  display: block; transform: scale(0.34); transform-origin: center;
  border-radius: 50%; box-shadow: 0 2px 6px rgba(0,0,0,0.16);
  transition: transform 220ms cubic-bezier(0.32, 0.72, 0, 1);
}
.dial-face svg { display: block; overflow: visible; }
.dial-face circle#dial-face-bg { stroke: var(--paper-0); stroke-width: 2; }
.dial-face circle:not(#dial-face-bg) { fill: var(--paper-0); }
.dial-face path { fill: none; stroke: var(--paper-0); stroke-width: 2.6; stroke-linecap: round; }
.dial-face-features { opacity: 0; transition: opacity 140ms ease; }

/* Open the face on hover, on keyboard focus, and for the length of a drag. */
@media (hover: hover) and (pointer: fine) {
  .dial-control:hover .dial-face-inner { transform: scale(1); }
  .dial-control:hover .dial-face-features { opacity: 1; }
}
@media (hover: none) {
  .dial-face-inner { transform: scale(1); }
  .dial-face-features { opacity: 1; }
}
.dial-control.is-live .dial-face-inner,
.dial-control:focus-within .dial-face-inner { transform: scale(1); }
.dial-control.is-live .dial-face-features,
.dial-control:focus-within .dial-face-features { opacity: 1; }
.dial-control input[type="range"]:focus-visible ~ .dial-face .dial-face-inner { outline: 2px solid var(--focus-ring); outline-offset: 3px; }

/* right column: the cost */
/* The readout holds a constant width, so the clearing cut out of the dot field
   never resizes as the figure gains or loses a column. Everything is left
   aligned, so a column arriving on the right never nudges the ones already there. */
.dial-tax { display: flex; flex-direction: column; gap: 6px; align-items: flex-start; text-align: left; }
.dial-tax-label { font-size: 16px; font-weight: 400; color: var(--ink-1); margin-bottom: 2px; }
/* Rolling digits, ported from the anatomy at numbers.sfinterface.com.
   Each column is a strip behind a one-cell window; the veil lives in the
   overhang, so the window bleeds past the glyph and the box is pulled back. */
.dial-tax-num {
  /* Capped so the widest bill in the data, ten cells, still fits the column. */
  font-size: var(--type-figure); line-height: 1; letter-spacing: -0.04em;
  font-variant-numeric: tabular-nums; display: flex; align-items: center;
  justify-content: flex-start;
}
.num-dim { opacity: 0; }
.num-small { font-size: 0.55em; align-self: flex-end; margin-bottom: 0.14em; }
/* The window is sized to the glyph, not to the cell: at display sizes the
   published 1.94em overhang shows the neighbouring digits outright. */
.num { --num-cell: 1.4em; --num-window: 1.04em; --num-fade: 0.11em; }
.num-col {
  display: inline-block; width: 1ch; height: var(--num-window);
  overflow: hidden; vertical-align: top;
  -webkit-mask-image: linear-gradient(transparent 0, #000 var(--num-fade), #000 calc(100% - var(--num-fade)), transparent 100%);
  mask-image: linear-gradient(transparent 0, #000 var(--num-fade), #000 calc(100% - var(--num-fade)), transparent 100%);
}
.num-strip { display: block; white-space: pre; line-height: var(--num-cell); will-change: transform; }
.num-sym { display: inline-block; width: 1ch; text-align: center; }
/* The glyphs are generated, so the page's text is the readable figure in the
   readout's visually hidden label, not thirty digits a column (numbers.js).
   Three decades, so a turn can run either way without leaving the strip. */
.num-strip::before { content: "0\A 1\A 2\A 3\A 4\A 5\A 6\A 7\A 8\A 9\A 0\A 1\A 2\A 3\A 4\A 5\A 6\A 7\A 8\A 9\A 0\A 1\A 2\A 3\A 4\A 5\A 6\A 7\A 8\A 9"; }
.num-sym::before { content: attr(data-ch); }
.dial-tax-num[data-sign="up"] { color: var(--red-600); }
.dial-tax-num[data-sign="down"] { color: var(--green-600); }
.dial-tax-sub { font-size: 16px; line-height: 1.5; color: var(--ink-2); max-width: 44ch; }
.dial-tax-label, .dial-tax-runs { align-self: flex-start; }
.dial-tax-sub .mono { font-family: inherit; }
.dial-tax-sub .mono { color: var(--ink-1); }
.dial-tax-runs { display: inline-flex; align-items: center; gap: 8px; margin-top: 4px; font-size: 16px; color: var(--ink-3); }
.dial-chip-small { height: 32px; padding: 0 12px; font-size: 16px; font-weight: 400; gap: 6px; border-radius: var(--r-pill); box-shadow: none; background: transparent; }
.dial-chip-small .dial-chev { width: 7px; height: 7px; }
.dial-chip-wrap-small .dial-menu { min-width: 200px; }
.dial-field-foot { display: flex; flex-direction: column; gap: 6px; text-align: right; z-index: 2; }
.dial-callout {
  padding: 20px; border-radius: 14px; background: var(--paper-1);
  border: 1px solid rgba(19,19,18,0.06); margin-top: 28px;
}
.dial-callout h4 { margin: 0 0 8px; font-size: 17px; font-weight: 500; letter-spacing: -0.015em; color: var(--ink-1); }
.dial-callout p { margin: 0; font-size: 14px; line-height: 1.5; color: var(--ink-2); }
.dial-callout .btn { margin-top: 14px; }
.dial-field-foot { position: absolute; right: max(var(--page-pad), calc((100% - var(--page-max)) / 2 + var(--page-pad))); bottom: 24px; align-items: flex-end; }
/* One size for the whole block. It was three: a 14px legend, a 13px scale and
   a 14px underlined sentence, stacked, which is more typographic variety than
   four lines of small print can carry. */
.dial-field-caption { display: inline-flex; align-items: center; gap: 6px; font-size: 13.5px; color: var(--ink-2); }
.dial-field-note { display: inline-flex; align-items: center; gap: 14px; font-size: 13.5px; color: var(--ink-3); }
.dial-dot { display: inline-block; width: 9px; height: 9px; border-radius: 50%; }
.dial-dot-ink { background: var(--ink-1); }
.dial-dot-tone { background: var(--red-600); margin-left: 8px; }
/* The underline runs under the arrow as well as the word, so the whole control
   reads as one link rather than a link with something parked beside it. */
.dial-inline-link { display: inline-flex; align-items: center; gap: 6px; border: 0; background: transparent; padding: 0; font: inherit; color: var(--ink-3); text-decoration: underline; text-underline-offset: 3px; cursor: pointer; }
.dial-inline-link::after {
  content: ''; flex: none; width: 0.9em; height: 0.8em;
  background-color: currentColor;
  -webkit-mask: var(--icon-arrow-right) center / contain no-repeat;
  mask: var(--icon-arrow-right) center / contain no-repeat;
}
.dial-inline-link:hover { color: var(--ink-1); }
.dial-inline-link:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 2px; }

/* slide-out */
.dial-scrim { position: fixed; inset: 0; z-index: 40; background: rgba(19,19,18,0.28); opacity: 0; transition: opacity 200ms ease-in; }
.dial-scrim[data-open] { opacity: 1; transition: opacity 260ms cubic-bezier(0.32, 0.72, 0, 1); }
.dial-scrim[hidden] { display: none; }
.dial-panel {
  position: fixed; top: 0; right: 0; bottom: 0; z-index: 41; width: min(480px, 100vw);
  background: var(--paper-0); color: var(--ink-1); display: flex; flex-direction: column;
  box-shadow: -1px 0 0 rgba(19,19,18,0.06), -24px 0 48px rgba(0,0,0,0.12);
  transform: translateX(100%); transition: transform 200ms ease-in; cursor: auto;
}
.dial-panel[data-open] { transform: none; transition: transform 260ms cubic-bezier(0.32, 0.72, 0, 1); }
.dial-panel[hidden] { display: none; }
.dial-panel-head { display: flex; align-items: center; justify-content: space-between; padding: 22px 32px 18px; }
.dial-panel-head h2 { font-size: 24px; font-weight: 500; letter-spacing: -0.02em; }
.dial-panel-close { width: 36px; height: 36px; border: 1px solid rgba(19,19,18,0.14); border-radius: 50%; background: transparent; font: inherit; font-size: 22px; line-height: 1; color: var(--ink-1); cursor: pointer; }
.dial-panel-close:hover { background: var(--paper-1); }
.dial-panel-close:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 2px; }
.dial-panel-body { overflow-y: auto; padding: 0 32px 32px; display: flex; flex-direction: column; gap: 20px; flex: 1; }
.dial-sec[hidden] { display: none; }
.dial-sec-lead { font-size: 15px; color: var(--ink-2); }
.dial-sec-caption { font-size: 13.5px; color: var(--ink-3); margin: 14px 0 0; }
.dial-sec-foot { margin-top: 14px; font-size: 13px; color: var(--ink-3); }
.dial-model-list { display: flex; flex-direction: column; margin-top: 12px; }
.dial-model-row { display: grid; grid-template-columns: 1fr 64px 96px; align-items: center; gap: 12px; height: 44px; border-bottom: 1px solid rgba(19,19,18,0.08); font-size: 15px; }
.dial-model-row[data-priced="false"] { color: var(--ink-3); }
.dial-model-row[data-priced="false"] .dial-logo { opacity: 0.5; }
.dial-model-row-pct, .dial-model-row-tax { text-align: right; font-variant-numeric: tabular-nums; font-size: 14px; }
.dial-model-row[data-priced="true"] .dial-model-row-pct[data-sign="up"] { color: var(--red-600); }
.dial-row-name { display: inline-flex; align-items: center; gap: 12px; min-width: 0; }
.dial-figs { display: flex; flex-direction: column; margin-top: 20px; }
.dial-fig { display: grid; grid-template-columns: 124px 1fr; gap: 16px; align-items: baseline; padding: 16px 0; border-bottom: 1px solid rgba(19,19,18,0.08); }
.dial-fig .mono { font-size: 25px; letter-spacing: -0.03em; font-variant-numeric: tabular-nums; color: var(--ink-1); white-space: nowrap; }
.dial-fig span:last-child { font-size: 14px; line-height: 1.45; color: var(--ink-2); }

@media (max-width: 1180px) {
  .dial-grid { gap: 30px 48px; }
  .dial-field-foot { right: var(--page-pad); }
}

/* One column. The page scrolls here; a phone cannot hold the whole instrument. */
/* Short laptop screens. The page is one screen with no scroll, so when the
   viewport is not tall enough for the card to clear the fold it gives back
   the air it can spare. The 64px under the heading is the first thing to go:
   a clipped answer costs the reader more than the breathing room does. */
/* The narrow half of the two-column range. The card is under 560px here, so
   it gets a larger share of the row and the readout keeps only what the widest
   bill needs. */
@media (min-width: 961px) and (max-width: 1120px) {
  .v-dial { --col-left: 65fr; --col-right: 35fr; }
  .dial-grid { gap: 36px 40px; }
  .dial-instrument { padding: 20px 24px; }
}

/* The top padding here is anchored to the tier's SHORT edge and opens up from
   there. The tier is tightest at its own floor: at 821px tall the card clears
   the fold by about 26px, and a flat value generous enough to give the heading
   air at 900px would push the model's answer through it. So the tier holds a
   modest 26px where it has to and spends the height on the gap where the height
   exists. */
@media (min-width: 961px) and (max-height: 940px) {
  .v-dial { --type-lg: clamp(26px, 3.4vw, 42px); }
  .dial-grid { gap: 36px 64px; padding-top: clamp(26px, calc(26px + (100vh - 821px) * 0.40), 58px); }
  .dial-instrument { gap: 17px; padding: 20px 28px; }
  .dial-bubble { padding: 12px 16px; }
}

/* Below about 881px tall the four-line headline stops fitting, so these
   windows widen the measure rather than shrink the type. The measure is the
   only lever that changes the LINE COUNT: at 20em this copy sets in three
   rather than four, and the line it buys back is worth more here than the
   tighter measure is. The same trade the height tiers already make, height
   bought with width. Inherited by both tiers below. */
@media (min-width: 961px) and (max-height: 880px) {
  .v-dial { --head-measure: 20em; }
}

/* Shorter still: 1280x720 and 1366x768 are common and leave 60px less than
   the tier above needs, so the heading and the card give up another notch
   rather than push the answer through the fold. Padding anchored to the tier's
   own floor like the tiers above: at 741px tall there are 17px between the card
   and the fold and the gap has to stay at 16px, but by 800 the headline has set
   in three lines and there is 90px going spare, so it buys air under the nav.
   Capped at 64px. */
@media (min-width: 961px) and (max-height: 820px) {
  .v-dial { --type-lg: clamp(24px, 3.4vw, 36px); }
  .dial-grid { gap: 28px 64px; padding-top: clamp(16px, calc(16px + (100vh - 741px) * 0.72), 64px); padding-bottom: 16px; }
  .dial-instrument { gap: 16px; padding: 16px 24px; }
}

/* Narrow AND short has no room to buy air with: up to about 1180 wide the
   card is at or past the fold at 768 to 800 tall before any padding is added.
   The bound is 1180 rather than the 1120 column breakpoint because just above
   that breakpoint the columns widen back to 60/40 and the card grows taller
   again, which is the tightest point of all. These windows keep the flat 16px.
   Placed after the tier above so it wins, and before the tier below so the
   shortest windows still get their own smaller value. */
@media (min-width: 961px) and (max-width: 1180px) and (max-height: 820px) {
  .dial-grid { padding-top: 16px; }
}

/* The shortest windows we still lay out in two columns. Below this the page
   stops being one screen and the narrow layout takes over. */
@media (min-width: 961px) and (max-height: 740px) {
  .v-dial { --type-lg: clamp(22px, 3.4vw, 32px); }
  .dial-grid { gap: 20px 64px; padding-top: clamp(14px, calc(14px + (100vh - 710px) * 0.35), 26px); padding-bottom: 12px; }
  .dial-instrument { gap: 10px; padding: 12px 22px; }
  .dial-bubble { padding: 8px 14px; line-height: 1.45; }
}

/* Below this a window genuinely cannot hold the card above the fold, so the
   page stops pretending to be one screen and scrolls, which is the same escape
   the stacked layout takes. Clipping the model's answer is the worse outcome. */
@media (min-width: 961px) and (max-height: 709px) {
  .body-dial, .v-dial { overflow: visible; height: auto; }
  .dial-hero { height: auto; min-height: 0; padding-bottom: 28px; }
}

@media (max-width: 960px) {
  .v-dial { --type-lg: clamp(26px, 6.9vw, 33px); --type-figure: clamp(40px, 11vw, 60px); }
  .body-dial, .v-dial { overflow: visible; height: auto; }
  .dial-hero { height: auto; min-height: 0; cursor: default; padding-bottom: 32px; }
  .dial-grid {
    grid-template-columns: 1fr; grid-template-rows: auto auto auto; align-content: start;
    padding: 24px var(--page-pad) 32px; gap: 24px 0;
  }
  .v-dial { --page-pad: 24px; }
  .dial-head, .dial-instrument, .dial-right { grid-column: 1; }
  .dial-head { grid-row: 1; } .dial-instrument { grid-row: 2; } .dial-right { grid-row: 3; }
  .dial-head h1 { max-width: none; }

  .dial-instrument { padding: 20px; gap: 20px; border-radius: 16px; }
  .dial-config { font-size: 15px; }
  .dial-config-line { flex-wrap: wrap; white-space: normal; gap: 8px 10px; }
  .dial-chip { height: 44px; font-size: 15px; padding: 0 12px; }
  .dial-menu-foot-btn { min-height: 44px; }
  .dial-bar-link { min-height: 44px; }
  .dial-inline-link {
    min-height: 44px; padding: 0 12px; border: 1px solid rgba(19,19,18,0.16);
    border-radius: 999px; text-decoration: none;
  }
  .dial-panel-close { width: 44px; height: 44px; flex-shrink: 0; }
  /* Text reflows on a narrow card, so the fixed heights have to go. */
  .dial-bubble { max-width: 100%; padding: 12px 14px; }
  /* Reserve all phrasings so the range stays under a thumb during a drag. */
  .dial-turn-you:has(> .dial-prompt-reserve) { display: grid; grid-template-columns: minmax(0, 1fr); }
  .dial-turn-you > .dial-bubble, .dial-prompt-reserve { grid-area: 1 / 1; }
  .dial-turn-you > .dial-bubble { align-self: stretch; }
  /* A collapsing text mask can cut through wrapped lines while scrubbing. */
  .dial-wrap-line { transition: none; }
  .dial-prompt-reserve { display: grid; grid-template-columns: minmax(0, 1fr); visibility: hidden; pointer-events: none; }
  .dial-prompt-reserve > .dial-bubble { grid-area: 1 / 1; }
  .dial-prompt-reserve .dial-wrap, .dial-ghost-prompts .dial-wrap { display: inline-block; }
  .dial-prompt-reserve .dial-wrap-line:first-child .dial-wrap,
  .dial-ghost-prompts .dial-wrap-line:first-child .dial-wrap { margin-bottom: 6px; }
  .dial-prompt-reserve .dial-wrap-line:last-child .dial-wrap,
  .dial-ghost-prompts .dial-wrap-line:last-child .dial-wrap { margin-top: 6px; }
  .dial-turn-model { padding-right: 0; }
  .dial-chat { gap: 18px; }

  /* The readout moves inside the card, under the dial, so a drag is visible. */
  .dial-tax { margin: 2px 0 0 !important; gap: 2px; align-items: flex-start; text-align: left; }
  .dial-tax-num { justify-content: flex-start; }
  .dial-tax-label, .dial-tax-runs { align-self: flex-start; }
  .dial-tax-label { order: 2; font-size: 16px; color: var(--ink-3); font-weight: 400; margin: 0; }
  .dial-tax-num { order: 1; }
  .dial-tax-sub { order: 3; max-width: none; font-size: 16px; margin-top: 2px; }
  .dial-tax-runs { order: 4; }

  /* The field becomes one full-bleed band under the card, on its own. */
  /* The field's band. It has to be deep enough to carry the busiest call at a
     readable scale: on a short band every dot has to stand for a dozen tokens
     and the field thins out to nothing. */
  .dial-right { min-height: min(58vh, 560px); justify-content: flex-end; }
  .dial-field-foot { position: static; align-items: flex-start; text-align: left; margin: 0; padding: 0; background: none; }

  .dial-menu { min-width: 240px; }
  .dial-panel { width: 100%; }
  .dial-panel-head { padding: 18px 20px 14px; }
  .dial-panel-body { padding: 0 20px 24px; }
  .dial-model-row { grid-template-columns: 1fr 56px 78px; font-size: 14px; }
  .dial-fig { grid-template-columns: 100px 1fr; gap: 12px; }
  .dial-fig .mono { font-size: 21px; }
}

/* The bar runs out of room before the page does. Below this the two panel
   links go: the panels are still reachable from the model chip's footer and
   from the probe link under the field, and the call to action is not. */
@media (max-width: 420px) {
  .dial-bar-link { display: none; }
  .tone-bar { padding: 0 16px; }
  .tone-bar-right { gap: 12px; }
}

@media (max-width: 560px) {
  .v-dial { --page-pad: 16px; }
  .tone-bar-right { gap: 10px; font-size: 13px; }
  .tone-bar .wordmark img { width: 104px; height: auto; }
  .tone-bar .btn { height: 44px; padding: 0 12px; font-size: 13px; }
  .dial-bar-link { font-size: 13px; padding: 6px 2px; }
  .dial-grid { padding-top: 16px; padding-bottom: 28px; }
  .dial-head h1 { font-size: 26px; line-height: 1.18; }
  .dial-head-evidence { display: none; }
  .dial-head-mobile {
    display: block; margin: 10px 0 0; font-size: 18px; line-height: 1.45;
    color: var(--ink-3);
  }
  .dial-instrument { padding: 16px; }
  .dial-bubble, .dial-reply-text { font-size: 15px; }
  .dial-ticks { font-size: 12px; }
  .dial-tax-label, .dial-tax-sub { font-size: 15px; }
}

@media (prefers-reduced-motion: reduce) {
  .dial-face-inner, .dial-face-features, .dial-track-wrap::after { transition: none; }
  .v-dial, .dial-bar, .dial-chip, .dial-menu, .dial-menu[data-open], .dial-panel, .dial-panel[data-open], .dial-scrim, .dial-scrim[data-open] { transition: none; }
}
