/*
 * Styles for the unified demo only.
 *
 * A separate file rather than more of styles.css, deliberately. The spike pages
 * link styles.css too, and several class names are shared between the two
 * surfaces (.panel, .btn, .control, .log, .turn). spike.css happens to redefine
 * most of them and therefore wins on those pages — but not all of them, and the
 * spikes are verified working artifacts whose appearance should not shift because
 * the demo needed a new rule. So the demo's styles live where they cannot reach
 * them. styles.css stays the shared shell: body, header, footer, banner.
 *
 * Mobile-first throughout: base rules target a phone and the media query widens
 * for a desktop. Web AI on a phone is the interesting half of this project, so
 * the phone layout is the one that gets to be the default rather than the
 * afterthought.
 */

:root {
  --color-good: #059669;
  --color-good-bg: rgba(16, 185, 129, 0.1);
  --color-warn: #d97706;
  --color-warn-bg: rgba(245, 158, 11, 0.12);
  --color-bad: #dc2626;
  --color-bad-bg: rgba(220, 38, 38, 0.09);
  --mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, monospace;
}

/* ---------------------------------------------------------------- panels */

.panel {
  background: var(--color-surface);
  border-radius: var(--radius);
  box-shadow: var(--shadow-sm);
  padding: 14px;
  margin-bottom: 10px;
}

.panel-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 8px;
}

.panel-title {
  font-size: 0.75rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--color-text-muted);
  margin-bottom: 8px;
}

.panel-docs {
  font-size: 0.8rem;
  color: var(--color-accent-dark);
  text-decoration: none;
  white-space: nowrap;
}

.panel-note {
  margin: 0 0 12px;
  font-size: 0.85rem;
  line-height: 1.5;
  color: var(--color-text-muted);
}

/* ---------------------------------------------------------------- picker */

/*
 * A scroll-snapping row on a phone and a grid on a desktop. The cards carry the
 * device badge because that is the thing worth seeing before you commit — four
 * of the five will happily begin a download that cannot finish on a phone.
 */
.picker {
  display: grid;
  grid-auto-flow: column;
  /* Wide enough that the next card peeks and the row reads as scrollable, capped
     so it stays a card. Uncapped at 74% a phone held sideways gave one card and a
     third of the next across the whole screen. */
  grid-auto-columns: clamp(200px, 74%, 300px);
  gap: 8px;
  overflow-x: auto;
  /* Without this, swiping the row past its last card hands the gesture to the
     browser and navigates back. Same for the comparison table below. */
  overscroll-behavior-x: contain;
  scroll-snap-type: x mandatory;
  padding-bottom: 6px;
  margin: 0 -14px;
  padding-left: 14px;
  padding-right: 14px;
}

.picker-card {
  scroll-snap-align: start;
  display: grid;
  grid-template-columns: 1fr auto;
  grid-template-areas: "name badge" "tag tag";
  gap: 4px 8px;
  text-align: left;
  background: var(--color-bg);
  border: 2px solid transparent;
  border-radius: var(--radius);
  padding: 10px;
  cursor: pointer;
  font: inherit;
  color: inherit;
  transition:
    border-color 0.15s,
    background 0.15s;
}

.picker-card:hover:not(:disabled) {
  border-color: var(--color-border);
}

.picker-card--active {
  border-color: var(--color-accent);
  background: var(--color-surface);
  box-shadow: var(--shadow-sm);
}

.picker-card:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

.picker-name {
  grid-area: name;
  font-weight: 600;
  font-size: 0.92rem;
}

.picker-tagline {
  grid-area: tag;
  font-size: 0.75rem;
  line-height: 1.4;
  color: var(--color-text-muted);
}

.picker-badge {
  grid-area: badge;
  align-self: start;
  font-size: 0.62rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  padding: 2px 6px;
  border-radius: 10px;
  white-space: nowrap;
}

.picker-badge--good {
  background: var(--color-good-bg);
  color: var(--color-good);
}
.picker-badge--warn {
  background: var(--color-warn-bg);
  color: var(--color-warn);
}
.picker-badge--bad {
  background: var(--color-bad-bg);
  color: var(--color-bad);
}

/* ---------------------------------------------------------------- status */

.status {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 0.85rem;
  font-weight: 500;
  padding: 8px 10px;
  border-radius: 6px;
  background: var(--color-bg);
  margin-bottom: 10px;
}

.status-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--color-text-muted);
  flex-shrink: 0;
}

.status--loading .status-dot {
  background: var(--color-warn);
  animation: pulse 1.1s ease-in-out infinite;
}
.status--loaded .status-dot {
  background: var(--color-good);
}
.status--error .status-dot,
.status--unavailable .status-dot {
  background: var(--color-bad);
}

.status--loaded {
  background: var(--color-good-bg);
  color: var(--color-good);
}
.status--error,
.status--unavailable {
  background: var(--color-bad-bg);
  color: var(--color-bad);
}

.status-text {
  min-width: 0;
  overflow-wrap: anywhere;
}

@keyframes pulse {
  50% {
    opacity: 0.35;
  }
}

/* -------------------------------------------------------------- progress */

.progress {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 12px;
}

.progress-track {
  flex: 1;
  height: 6px;
  background: var(--color-border);
  border-radius: 3px;
  overflow: hidden;
}

.progress-bar {
  height: 100%;
  background: var(--color-accent);
  transition: width 0.2s;
}

.progress-text {
  font-family: var(--mono);
  font-size: 0.7rem;
  color: var(--color-text-muted);
  white-space: nowrap;
}

/* -------------------------------------------------------------- controls */

.control {
  display: block;
  margin-bottom: 14px;
}

.control--inline {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 0.85rem;
  margin-bottom: 4px;
}

.control-label {
  display: block;
  font-size: 0.78rem;
  font-weight: 600;
  margin-bottom: 5px;
}

.control-label code,
.control-note code,
.panel-note code {
  font-family: var(--mono);
  font-size: 0.9em;
  background: var(--color-bg);
  padding: 1px 4px;
  border-radius: 3px;
  font-weight: 400;
}

.control-flag {
  display: inline-block;
  margin-left: 6px;
  font-size: 0.62rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  padding: 1px 5px;
  border-radius: 9px;
  background: var(--color-warn-bg);
  color: var(--color-warn);
  vertical-align: middle;
}

.control-flag--ok {
  background: var(--color-good-bg);
  color: var(--color-good);
}
.control-flag--bad {
  background: var(--color-bad-bg);
  color: var(--color-bad);
}

.control-input {
  width: 100%;
  font: inherit;
  /* 16px, out of step with the rest of the phone type, and deliberately. Below
     16px iOS Safari zooms the page in when a field takes focus and does not zoom
     back out when it loses it, so one tap on the prompt box leaves every
     subsequent tap on a page wider than the screen. The desktop query at the
     bottom puts this back to 0.88rem, where no such rule applies. */
  font-size: 16px;
  padding: 8px;
  border: 1px solid var(--color-border);
  border-radius: 6px;
  background: var(--color-surface);
  color: var(--color-text);
}

.control-input--area {
  resize: vertical;
  line-height: 1.5;
}

.control-input:disabled {
  background: var(--color-bg);
  color: var(--color-text-muted);
}

.control-row {
  display: flex;
  align-items: center;
  gap: 10px;
}

.control-range {
  flex: 1;
  min-width: 0;
  accent-color: var(--color-accent-dark);
}

.control-value {
  font-family: var(--mono);
  font-size: 0.78rem;
  color: var(--color-text);
  white-space: nowrap;
}

/* A read-only context value is a sentence, not a number. nowrap is right for the
   figure beside a slider and wrong here — it ran straight off the panel edge on
   both a phone and a desktop. */
.control-value--prose {
  white-space: normal;
  overflow-wrap: anywhere;
  display: block;
  margin: 0;
}

.control-note {
  margin: 5px 0 0;
  font-size: 0.75rem;
  line-height: 1.45;
  color: var(--color-text-muted);
}

.control-note--bad {
  color: var(--color-bad);
}

/* --------------------------------------------------------------- buttons */

.btn-row {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
  margin-top: 4px;
}

.btn {
  font: inherit;
  font-size: 0.88rem;
  font-weight: 500;
  padding: 9px 16px;
  border: 1px solid var(--color-border);
  border-radius: 6px;
  background: var(--color-surface);
  color: var(--color-text);
  cursor: pointer;
  transition:
    background 0.15s,
    border-color 0.15s;
}

.btn:hover:not(:disabled) {
  border-color: var(--color-accent);
}

.btn--primary {
  background: var(--color-accent);
  border-color: var(--color-accent);
  font-weight: 600;
}

.btn--primary:hover:not(:disabled) {
  background: var(--color-accent-dark);
  border-color: var(--color-accent-dark);
}

.btn--small {
  font-size: 0.78rem;
  padding: 5px 10px;
}

.btn:disabled {
  opacity: 0.45;
  cursor: not-allowed;
}

/*
 * Finger-sized hit areas, keyed on the pointer rather than the width: what makes
 * a 32px button hard to press is the finger, and a phone held sideways is 852px
 * wide. Only the box grows — the type and the padding stay as they are, so
 * nothing moves on a desktop and nothing looks inflated on a phone.
 */
@media (pointer: coarse) {
  .btn {
    min-height: 44px;
  }

  .btn--small {
    min-height: 40px;
  }

  /* Inline targets that are a line of text tall. Padding rather than min-height:
     they sit in flowing text, so a fixed box would break the line they are on —
     vertical padding on an inline box does not move the line it sits in. */
  .panel-docs,
  .wire-open,
  .intro a,
  .panel-note a,
  .control-note a {
    padding-block: 8px;
  }

  /* The GitHub mark is a fixed 32px circle, so it has nowhere to put padding
     without becoming a bigger circle. Extend the hit area past the paint
     instead. */
  .intro a.intro-github-link {
    position: relative;
  }

  .intro a.intro-github-link::after {
    content: "";
    position: absolute;
    inset: -6px;
  }

  .panel > details > summary,
  .compare-details > summary,
  .detail-block > summary {
    padding-block: 10px;
  }
}

/* ------------------------------------------------------------------- chat */

.output {
  background: var(--color-bg);
  border-radius: 6px;
  padding: 10px;
  margin-bottom: 12px;
  min-height: 96px;
  /* dvh so the box is measured against the screen the reader actually has, and a
     px floor because 55% of a phone held sideways is not enough to be worth
     scrolling. Same shape for the diagnostics boxes below. */
  max-height: max(200px, 55dvh);
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  justify-content: center;
}

/* Once there is a conversation it should grow from the top, not stay centered —
   the centering exists only so the empty-state line sits in the middle. */
.output:has(.turn) {
  justify-content: flex-start;
}

.output-empty {
  margin: 0;
  font-size: 0.85rem;
  color: var(--color-text-muted);
  text-align: center;
  padding: 16px 0;
}

.turn {
  margin-bottom: 12px;
}

.turn:last-child {
  margin-bottom: 0;
}

.turn-role {
  font-size: 0.68rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--color-text-muted);
  margin-bottom: 4px;
}

.turn-text {
  font-size: 0.88rem;
  line-height: 1.55;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}

.turn--user .turn-text {
  background: var(--color-surface);
  border-radius: 6px;
  padding: 8px 10px;
}

.turn--model .turn-text {
  border-left: 3px solid var(--color-accent);
  padding-left: 10px;
}

.turn-flag {
  display: inline-block;
  margin-left: 6px;
  font-size: 0.6rem;
  padding: 1px 5px;
  border-radius: 9px;
  background: var(--color-border);
  color: var(--color-text-muted);
  letter-spacing: 0.03em;
}

.turn-flag--live {
  background: var(--color-accent);
  color: #04241a;
}

.turn-error {
  margin-top: 6px;
  font-size: 0.8rem;
  color: var(--color-bad);
  font-family: var(--mono);
  overflow-wrap: anywhere;
}

/* JSON mode verdict: did it actually come back as JSON, and was anything
   enforcing that. The distinction is the point of the toggle. */
.json-verdict {
  font-size: 0.7rem;
  font-weight: 600;
  padding: 4px 8px;
  border-radius: 4px;
  margin-bottom: 6px;
  display: inline-block;
}

.json-verdict--ok {
  background: var(--color-good-bg);
  color: var(--color-good);
}
.json-verdict--bad {
  background: var(--color-bad-bg);
  color: var(--color-bad);
}

/* -------------------------------------------------------------- run stats */

.run-stats {
  display: flex;
  flex-wrap: wrap;
  gap: 4px 10px;
  margin-top: 8px;
  padding-left: 13px;
}

.run-stat {
  font-family: var(--mono);
  font-size: 0.68rem;
  color: var(--color-text-muted);
  white-space: nowrap;
}

.run-stat-key::after {
  content: " ";
}

.run-stat-val {
  color: var(--color-text);
  font-weight: 600;
}

.run-stat--flag {
  color: var(--color-warn);
  font-weight: 600;
}

.run-stat--bad {
  color: var(--color-bad);
  font-weight: 600;
  white-space: normal;
}

/* --------------------------------------------------------------- guidance */

.axes {
  margin: 0;
}

.axis {
  padding: 8px 0;
  border-top: 1px solid var(--color-border);
}

.axis:first-child {
  border-top: none;
}

.axis dt {
  font-size: 0.78rem;
  font-weight: 600;
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 8px;
}

.axis dd {
  margin: 4px 0 0;
  font-size: 0.75rem;
  line-height: 1.45;
  color: var(--color-text-muted);
}

.axis-value {
  font-family: var(--mono);
  font-size: 0.7rem;
  font-weight: 600;
  padding: 1px 6px;
  border-radius: 9px;
  background: var(--color-bg);
  color: var(--color-text-muted);
  white-space: nowrap;
  text-align: right;
}

.axis-value--good {
  background: var(--color-good-bg);
  color: var(--color-good);
}
.axis-value--warn {
  background: var(--color-warn-bg);
  color: var(--color-warn);
}
.axis-value--bad {
  background: var(--color-bad-bg);
  color: var(--color-bad);
}

/* ------------------------------------------------------- comparison table */

.compare-details,
.detail-block {
  margin-top: 12px;
  border-top: 1px solid var(--color-border);
  padding-top: 10px;
}

.compare-details summary,
.detail-block summary {
  font-size: 0.8rem;
  font-weight: 600;
  cursor: pointer;
  color: var(--color-accent-dark);
}

/* Wide content scrolls inside its own box; the page body never scrolls
   sideways. On a phone this table is five columns of prose and that is fine —
   it scrolls. */
.table-scroll {
  overflow-x: auto;
  overscroll-behavior-x: contain;
  margin-top: 10px;
}

.compare {
  border-collapse: collapse;
  font-size: 0.72rem;
  min-width: 640px;
}

.compare th,
.compare td {
  padding: 6px 8px;
  text-align: left;
  vertical-align: top;
  border: 1px solid var(--color-border);
}

.compare thead th {
  font-size: 0.7rem;
  background: var(--color-bg);
  white-space: nowrap;
}

.compare tbody th {
  font-weight: 600;
  background: var(--color-bg);
  white-space: nowrap;
}

.compare .compare-active {
  outline: 2px solid var(--color-accent);
  outline-offset: -2px;
}

.cell--good {
  background: var(--color-good-bg);
  color: var(--color-good);
}
.cell--warn {
  background: var(--color-warn-bg);
  color: var(--color-warn);
}
.cell--bad {
  background: var(--color-bad-bg);
  color: var(--color-bad);
}

/* ------------------------------------------------------------ diagnostics */

.probe,
.log-data {
  font-family: var(--mono);
  font-size: 0.68rem;
  line-height: 1.4;
  background: var(--color-bg);
  border-radius: 6px;
  padding: 8px;
  margin: 8px 0 0;
  max-height: max(200px, 40dvh);
  overflow: auto;
  white-space: pre;
}

.log {
  max-height: max(220px, 45dvh);
  overflow-y: auto;
  margin-top: 8px;
}

.log-line {
  font-size: 0.74rem;
  padding: 4px 0;
  border-top: 1px solid var(--color-border);
}

.log-line:first-child {
  border-top: none;
}

.log-time {
  font-family: var(--mono);
  font-size: 0.66rem;
  color: var(--color-text-muted);
  margin-right: 6px;
}

.log-msg {
  overflow-wrap: anywhere;
}

.log-line--warn .log-msg {
  color: var(--color-warn);
}

.log-line--error .log-msg {
  color: var(--color-bad);
  font-weight: 600;
}

.log-data {
  max-height: 200px;
}

/* --------------------------------------------------------------- recovery */

.recovery {
  background: var(--color-bad-bg);
  border: 1px solid var(--color-bad);
  border-radius: var(--radius);
  padding: 12px 14px;
  margin-bottom: 10px;
}

.recovery-title {
  font-weight: 700;
  font-size: 0.9rem;
  color: var(--color-bad);
  margin-bottom: 6px;
}

.recovery-detail {
  font-size: 0.78rem;
  line-height: 1.45;
  color: var(--color-text);
  margin-bottom: 8px;
}

.recovery-detail:last-child {
  margin-bottom: 0;
}

/* ------------------------------------------------------------- wider than
   a phone. Two columns, because on a desktop the conversation and the
   settings are read together rather than scrolled between. */

@media (min-width: 900px) {
  .panel {
    padding: 18px 20px;
  }

  .picker {
    grid-auto-flow: row;
    grid-template-columns: repeat(5, 1fr);
    overflow-x: visible;
    margin: 0;
    padding: 0;
  }

  .picker-card {
    grid-auto-columns: auto;
  }

  /*
   * Two columns: settings and guidance on the left, the conversation on the
   * right at full height. Named areas rather than nth-of-type placement — the
   * crash banner is a <section> too, so every index shifts when it appears.
   * An area with no item in it collapses to zero height, so the banner row
   * costs nothing when there is no banner.
   *
   * The conversation spans all three left-hand rows deliberately: it is the
   * thing you actually use, and letting it share a row with the settings panel
   * left a large void under it whenever the settings were taller.
   */
  .content {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1.1fr);
    grid-template-areas:
      "banner banner"
      "picker picker"
      "runtime chat"
      "guidance chat"
      "diagnostics chat";
    gap: 0 12px;
    align-items: start;
  }

  .recovery {
    grid-area: banner;
  }
  .panel--picker {
    grid-area: picker;
  }
  .panel--runtime {
    grid-area: runtime;
  }
  .panel--guidance {
    grid-area: guidance;
  }
  .panel--diagnostics {
    grid-area: diagnostics;
  }

  .panel--chat {
    grid-area: chat;
    /* Sticky so a long settings or guidance column does not scroll the
       conversation off the screen. */
    position: sticky;
    top: 8px;
  }

  .panel--chat .output {
    max-height: 60vh;
  }

  /* No iOS focus-zoom rule on a desktop, so the fields can match the type around
     them again. See .control-input. */
  .control-input {
    font-size: 0.88rem;
  }

  .compare {
    font-size: 0.75rem;
  }
}

/* The JSON toggle belongs with the send controls rather than above the prompt,
   so it rides at the trailing edge of the button row. .btn-row already wraps, so
   on a narrow screen it drops to its own line instead of squeezing the buttons. */
.json-toggle {
  margin-left: auto;
  margin-bottom: 0;
  font-size: 0.8rem;
}

.json-toggle input {
  margin: 0;
}

/* A <details> opening a panel carries the heading itself, so it should not also
   carry the separator that divides one disclosure from the next. */
.panel > details:first-child,
.panel > .panel-title + details {
  margin-top: 0;
  border-top: none;
  padding-top: 0;
}

/* -------------------------------------------------------- verbatim panel */

/* The conversation shows a processed reply — web-llm strips a think block, and
   every runtime sends a history the reader never sees. This opens the unedited
   pair. Understated on purpose: it is a diagnostic, not a feature. */
.wire-open {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  margin-top: 6px;
  margin-left: 13px;
  padding: 2px 6px;
  border: 1px solid var(--color-border);
  border-radius: 4px;
  background: none;
  color: var(--color-text-muted);
  font-family: var(--mono);
  font-size: 0.66rem;
  cursor: pointer;
}

.wire-open:hover {
  color: var(--color-text);
  border-color: var(--color-text-muted);
}

.wire {
  width: min(96vw, 760px);
  max-height: 88dvh;
  overflow-y: auto;
  padding: 16px;
  border: 1px solid var(--color-border);
  border-radius: 8px;
  background: var(--color-bg);
  color: var(--color-text);
}

.wire::backdrop {
  background: rgba(0, 0, 0, 0.5);
}

.wire-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 10px;
}

.wire-title {
  font-size: 0.8rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

.wire-note {
  font-size: 0.72rem;
  line-height: 1.5;
  color: var(--color-text-muted);
  margin: 0 0 12px;
}

.wire-section {
  margin-bottom: 14px;
}

.wire-label {
  font-size: 0.66rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--color-text-muted);
  margin-bottom: 4px;
}

/* Wide payloads scroll inside the block rather than widening the dialog. */
.wire-body {
  margin: 0;
  padding: 8px 10px;
  max-height: max(160px, 34dvh);
  overflow: auto;
  background: var(--color-surface);
  border-radius: 6px;
  font-family: var(--mono);
  font-size: 0.7rem;
  line-height: 1.5;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}

.wire-empty {
  margin: 0;
  font-size: 0.72rem;
  color: var(--color-text-muted);
}

/* ------------------------------------------------------------ tool calling */

/* The tool editor sits under the button row, in the same place the JSON note
   does, and only while the toggle is on. Indented to the same gutter as the run
   stats so it reads as belonging to the controls above it rather than to the
   prompt. */
.tool-editor {
  margin-top: 10px;
  padding-top: 10px;
  border-top: 1px solid var(--color-border);
}

/* A function is code, so it gets the mono face and no autocorrect. Tabs would
   leave the field, so indentation here is whatever was typed or pasted. */
.control-input--code {
  font-family: var(--mono);
  font-size: 0.78rem;
  line-height: 1.5;
  white-space: pre;
  overflow-x: auto;
}

.tool-schema {
  margin: 6px 0 2px;
}

.tool-schema > summary {
  font-size: 0.72rem;
  color: var(--color-text-muted);
  cursor: pointer;
}

/* The calls themselves, between the question and the answer. One row each, and
   deliberately noisy: the whole point of the toggle is whether this block
   appears at all. */
.tool-calls {
  display: flex;
  flex-direction: column;
  gap: 4px;
  margin-bottom: 6px;
}

.tool-call {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 6px;
  font-family: var(--mono);
  font-size: 0.72rem;
  padding: 5px 8px;
  border-radius: 4px;
  background: var(--color-good-bg);
  color: var(--color-good);
  /* An argument list can be long; it wraps rather than pushing the result and
     the timing off the edge on a phone. */
  overflow-wrap: anywhere;
}

.tool-call--bad {
  background: var(--color-bad-bg);
  color: var(--color-bad);
}

.tool-call-name {
  font-weight: 600;
}

.tool-call-arrow {
  opacity: 0.6;
}

.tool-call-result {
  font-weight: 600;
}

.tool-call-ms {
  margin-left: auto;
  color: var(--color-text-muted);
  font-weight: 400;
}
