/* Compact drawing-board layout for /tinygpt. */
.tinygpt {
  --paper: #0a171b;
  --paper-deep: #061014;
  --ink: #d8e8e6;
  --line: #73b8b5;
  --line-soft: rgba(115, 184, 181, .3);
  --line-faint: rgba(115, 184, 181, .075);
  --accent: #efc75e;
  --good: #72d28b;
  --hot: #ea776d;
  --muted: #88a5a4;
  --bg: var(--paper);
  --panel: var(--paper-deep);
  --fg: var(--ink);
  --edge: var(--line-soft);
  background: transparent;
  color: var(--ink);
  font-size: 20px;
  line-height: 1.4;
  margin: 0;
  padding: 12px;
  position: relative;
}

.tinygpt,
.tinygpt * {
  box-sizing: border-box;
}

.tinygpt::before {
  background-color: var(--paper);
  background-image:
    linear-gradient(var(--line-faint) 1px, transparent 1px),
    linear-gradient(90deg, var(--line-faint) 1px, transparent 1px);
  background-size: 16px 16px;
  border: 1px solid var(--line);
  content: "";
  inset: 12px;
  pointer-events: none;
  position: absolute;
}

.tinygpt > * {
  position: relative;
}

.tinygpt h1,
.tinygpt h2,
.tinygpt p {
  color: inherit;
  font-family: inherit;
  font-weight: 400;
  line-height: inherit;
  margin: 0;
}

.tinygpt a {
  color: var(--accent);
  text-decoration: underline;
  text-underline-offset: 2px;
}

.tinygpt a:hover {
  color: var(--ink);
}

.tg-sr-only,
.tg-lens-results caption {
  clip: rect(0, 0, 0, 0);
  height: 1px;
  margin: -1px;
  overflow: hidden;
  padding: 0;
  position: absolute;
  width: 1px;
}

.tg-masthead {
  align-items: stretch;
  border-bottom: 1px solid var(--line);
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  min-height: 210px;
}

.tg-masthead > * {
  align-items: center;
  border-right: 1px solid var(--line-soft);
  display: flex;
  min-width: 0;
  padding: 16px 20px;
}

.tg-masthead > *:last-child {
  border-right: 0;
}

.tg-overview {
  align-items: flex-start;
  flex-direction: column;
  justify-content: center;
}

.tinygpt h1 {
  color: var(--accent);
  font-size: 18px;
  line-height: 1.4;
  margin-bottom: 7px;
}

.tg-overview-copy {
  column-count: 2;
  column-gap: 28px;
  column-rule: 1px solid var(--line-soft);
  width: 100%;
}

.tg-overview-copy p {
  color: var(--muted);
  font-size: 16px;
  line-height: 1.45;
  margin: 0 0 8px;
}

.tg-key {
  align-items: flex-start;
  color: var(--muted);
  flex-direction: column;
  font-size: 16px;
  gap: 4px;
  justify-content: center;
  white-space: nowrap;
}

/* The only wrapping text in the column, so it sets its own measure rather than
   widening the legend it sits under. */
.tg-key-note {
  font-size: 13px;
  line-height: 1.4;
  margin-top: 2px;
  max-width: 24ch;
  white-space: normal;
}

.tg-mode-lines {
  align-items: center;
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  min-width: 0;
}

/* Loading runs to hundreds of megabytes, so it reports itself rather than
   leaving the page looking broken. */
.tg-mode-status {
  color: var(--muted);
  font-size: 14px;
  line-height: 1.3;
}

.tg-mode-status:empty {
  display: none;
}

.tg-mode {
  align-items: center;
  color: var(--hot);
  display: flex;
  font-size: 17px;
  gap: 10px;
}

.tg-mode-light {
  background: var(--hot);
  display: block;
  height: 24px;
  width: 12px;
}

.tg-model-field {
  border-bottom: 2px solid var(--line);
  height: 210px;
  overflow: hidden;
}

.tg-model-scroll {
  height: 100%;
  overflow: hidden;
}

.tg-model-scroll #modelviz {
  margin: 0 auto;
  max-width: 1000px;
  min-width: 760px;
}

.tinygpt .tg-modeldiagram-svg {
  background: transparent;
  border: 0;
  display: block;
  height: auto;
  width: 100%;
}

/* The drawing scales to its field, so a width in user units lands on a
   fraction of a device pixel and crispEdges rounds it away. Every stroke is
   specified in device pixels instead: one for structure, two for emphasis. */
.tinygpt .tg-modeldiagram-svg :is(line, rect, path) {
  vector-effect: non-scaling-stroke;
}

.tg-md-node rect,
.tg-md-block-frame {
  fill: rgba(6, 16, 20, .88);
  rx: 0;
  ry: 0;
  shape-rendering: crispEdges;
  stroke: var(--line);
  stroke-width: 1;
}

.tg-md-node rect {
  transition: fill 240ms ease-in-out, stroke 240ms ease-in-out,
    stroke-width 240ms ease-in-out;
}

.tg-md-node text,
.tg-md-block text {
  font-family: "TG Technical", monospace;
  pointer-events: none;
  text-anchor: middle;
}

.tg-md-title {
  fill: var(--ink);
  font-size: 18px;
}

.tg-md-qkv-cell {
  fill: rgba(115, 184, 181, .08) !important;
  stroke: var(--line-soft) !important;
  stroke-width: 1 !important;
}

.tg-md-qkv-label {
  fill: var(--ink);
  font-size: 14px;
  text-anchor: middle;
}

.tg-md-detail,
.tg-md-repeat-text,
.tg-md-residual-text,
.tg-md-counter {
  fill: var(--muted);
  font-size: 14px;
}

.tg-md-residual-text {
  fill: var(--muted);
  font-size: 16px;
}

.tg-md-block-frame {
  fill: rgba(10, 23, 27, .55);
  stroke: var(--line-soft);
  transition: stroke 420ms ease-in-out;
}

.tg-modeldiagram-svg.is-running .tg-md-block-frame {
  stroke: var(--line);
}

/* Each of the twelve blocks carries its own pattern of lit grid squares, so
   the repeats read as distinct passes. It sits far enough down in contrast to
   stay behind the drawing, and breathes slowly while the model calculates. */
.tg-md-layer-texture {
  opacity: 0;
  transition: opacity 240ms ease-in-out;
}

.tg-md-layer-texture.is-current {
  opacity: 1;
}

/* Well under the frame stroke at .3: readable as texture, never as a thing to
   look at. */
.tg-md-cell {
  fill: rgba(115, 184, 181, .18);
  stroke: none;
}

.tg-md-cell.is-hatched {
  fill: none;
  stroke: rgba(115, 184, 181, .32);
}

.tg-modeldiagram-svg.is-running .tg-md-texture {
  animation: tg-md-breathe 2600ms ease-in-out infinite;
}

@keyframes tg-md-breathe {
  0%, 100% { opacity: .5; }
  50% { opacity: 1; }
}

.tg-md-wire,
.tg-md-residual,
.tg-md-repeat-path {
  fill: none;
  stroke: var(--line);
  stroke-width: 1;
}

#tg-arrow path {
  fill: var(--line);
}

.tg-md-repeat-path {
  stroke: var(--line-soft);
  stroke-dasharray: 4 3;
}

/* The rail is a legend, so it marks the layer by its stroke: a fill would put a
   solid block behind the number that names it. */
.tg-md-ticks rect {
  fill: transparent;
  shape-rendering: crispEdges;
  stroke: var(--line-soft);
  stroke-width: 1;
  transition: stroke 320ms ease-in-out, stroke-width 320ms ease-in-out;
}

.tg-md-ticks rect.is-active {
  stroke: var(--accent);
  stroke-width: 2;
}

.tg-md-ticks rect.is-complete {
  stroke: var(--line);
}

/* Taken up well inside the stage's own slot and released over a longer ease, so
   the wave down the block keeps a bright head and a fading trail. */
.tg-md-node.is-active rect {
  fill: rgba(239, 199, 94, .18);
  stroke: var(--accent);
  stroke-width: 2;
  transition-duration: 40ms;
}

.tg-md-node.is-active .tg-md-title,
.tg-md-node.is-focused .tg-md-title {
  fill: var(--accent);
}

.tg-md-node.is-complete rect {
  stroke: var(--line);
}

.tg-md-node.is-focused rect {
  stroke: var(--accent);
  stroke-width: 2;
}

.tg-md-answer-box {
  fill: #fff;
  opacity: 0;
  shape-rendering: crispEdges;
  stroke: #fff;
  stroke-width: 1;
}

.tg-modeldiagram-svg.has-answer .tg-md-answer-box {
  opacity: 1;
}

.tg-md-output-token {
  fill: #071417;
  opacity: 0;
  font-family: "TG Technical", monospace;
  font-size: 20px;
  text-anchor: middle;
}

.tg-modeldiagram-svg.has-answer .tg-md-output-token {
  opacity: 1;
}

/* Arriving: the entry animation belongs to the moment a token lands, not to
   the box being occupied, or it would never replay across a run. */
.tg-modeldiagram-svg.is-fresh .tg-md-answer-box,
.tg-modeldiagram-svg.is-fresh .tg-md-output-token {
  animation: tg-answer-in 260ms ease-in-out both;
}

@keyframes tg-answer-in {
  0% { opacity: 0; transform: translateY(5px); }
  50% { opacity: 1; transform: translateY(-2px); }
  100% { opacity: 1; transform: translateY(0); }
}

/* The editable composer is the strongest element below the model horizon. */
/* No fixed height: once the controls wrap — which they do from about 700px
   down — a fixed one pushed the field out of the panel it belongs to. */
.tg-composer {
  background: rgba(6, 16, 20, .72);
  border-bottom: 2px solid var(--accent);
  display: flex;
  flex-direction: column;
  padding: 12px 16px 0;
  position: relative;
}

/* Wraps rather than overflows: between roughly 700 and 1200px the controls
   need two lines, and a fixed height left them sitting on top of the field. */
.tg-composer-head {
  align-items: center;
  column-gap: 16px;
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  min-height: 40px;
  row-gap: 10px;
}

.tg-transport {
  align-items: center;
  display: flex;
  flex-wrap: wrap;
  gap: 14px;
}

.tg-transport label {
  color: var(--muted);
  font-size: 16px;
}

.tg-transport select {
  appearance: none;
  background: var(--paper-deep);
  border: 1px solid var(--line-soft);
  border-radius: 0;
  color: var(--ink);
  cursor: pointer;
  font-size: 17px;
  height: 44px;
  padding: 4px 36px 4px 12px;
}

.tg-transport select:focus-visible {
  border-color: var(--accent);
  outline: 1px solid var(--accent);
  outline-offset: 1px;
}

.tg-composer h2,
.tg-lens h2 {
  color: var(--ink);
  font-size: 18px;
  letter-spacing: .06em;
}

.tg-replay {
  background: transparent;
  border: 1px solid var(--accent);
  border-radius: 0;
  color: var(--accent);
  cursor: pointer;
  font-size: 18px;
  height: 44px;
  padding: 4px 18px;
}

.tg-replay:hover:not(:disabled),
.tg-replay:focus-visible {
  background: var(--accent);
  color: var(--paper-deep);
  outline: 0;
}

.tg-replay:disabled {
  border-color: var(--muted);
  color: var(--muted);
  cursor: wait;
}

.tg-replay.is-stopping {
  border-color: var(--hot);
  color: var(--hot);
}

/* One field now, not a prompt beside an output: for a base model there is only
   text, and the model continues it. */
/* The field is the composer's lower half, not a box inside it. Nested borders
   with a gap between them read as two separate panels, most obviously in a
   narrow viewport where the controls above have already wrapped. */
.tg-composer-body {
  background: var(--paper-deep);
  border-top: 1px solid var(--line-soft);
  flex: 1;
  margin: 10px -16px 0;
  position: relative;
}

.tg-text {
  color: var(--ink);
  font-size: 16px;
  line-height: 1.45;
  min-height: 104px;
  max-height: 30vh;
  overflow: auto;
  overflow-wrap: anywhere;
  padding: 20px 15px 12px;
  white-space: pre-wrap;
}

.tg-text:focus-visible {
  outline: 1px solid var(--accent);
  outline-offset: -1px;
}

.tg-text[contenteditable="false"] {
  cursor: progress;
}

/* Yellow is a claim: this was generated and a reading for it is held. */
.tg-token {
  border-bottom: 1px solid var(--accent);
  color: var(--accent);
  cursor: pointer;
}

.tg-token:hover,
.tg-token.is-selected {
  background: var(--accent);
  color: var(--paper-deep);
}

/* Written by the model, but an edit upstream has invalidated the reading, so
   there is nothing left to point at. Distinct from text the reader typed,
   which carries no mark at all. */
.tg-token.is-stale {
  border-bottom: 1px dotted var(--line-soft);
  color: var(--muted);
  cursor: text;
}

.tg-token.is-stale:hover {
  background: transparent;
  color: var(--muted);
}

.tg-composer-body:focus-within {
  border-top-color: var(--accent);
}

.tg-field-label {
  background: var(--paper-deep);
  color: var(--muted);
  font-size: 14px;
  left: 14px;
  padding: 0 6px;
  position: absolute;
  top: -12px;
  z-index: 1;
}

.tg-placeholder {
  color: var(--muted) !important;
  font-size: 16px !important;
}

.tg-lens {
  display: flex;
  flex-direction: column;
  min-width: 0;
  overflow: hidden;
  position: relative;
  z-index: 1;
}


.tg-logit {
  border-right: 1px solid var(--line);
}

.tg-lens h2 {
  background: rgba(10, 23, 27, .9);
  border-bottom: 1px dashed var(--line-soft);
  height: 44px;
  padding: 10px 16px 7px;
}

.tg-lens-results {
  height: 366px;
  overflow: hidden;
  padding: 6px 0;
}

.tg-lens-results table {
  border-collapse: collapse;
  table-layout: fixed;
  width: 100%;
}

.tg-lens-results tr {
  height: 30px;
}

.tg-lens-results tr .tg-candidate-tracks {
  opacity: 1;
  transform: translateX(0);
  transition: opacity 220ms ease-in-out, transform 260ms ease-in-out;
}

.tg-lens-results tr.is-pending .tg-candidate-tracks {
  opacity: 0;
  transform: translateX(-12px);
}

.tg-lens-results tr.is-arriving th,
.tg-lens-results tr.is-arriving td {
  background: rgba(239, 199, 94, .13);
  color: var(--accent);
}

.tg-lens-results td,
.tg-lens-results th {
  border-bottom: 0;
  font-weight: 400;
  height: 30px;
  line-height: 1;
  overflow: hidden;
  padding: 2px 0;
  text-align: left;
  vertical-align: middle;
  white-space: nowrap;
}

.tg-lens-results tr:last-child td,
.tg-lens-results tr:last-child th {
  border-bottom: 0;
}

.tg-candidate-tracks {
  align-items: center;
  display: grid;
  column-gap: 8px;
  grid-template-columns: repeat(var(--candidate-count), minmax(0, 1fr));
  width: 100%;
}

.tg-token-badge {
  align-items: center;
  background: var(--paper-deep);
  border: 1px solid var(--line-soft);
  border-radius: 0;
  display: inline-flex;
  font-size: 14px;
  height: 24px;
  min-width: 0;
  overflow: hidden;
  vertical-align: middle;
  width: 100%;
}

.tg-token-badge.is-target {
  border-color: var(--accent);
  box-shadow: inset 0 0 0 1px var(--accent);
  opacity: 1 !important;
}

.tg-token-text,
.tg-token-delta,
.tg-token-prob {
  display: block;
  line-height: 10px;
  padding: 0 4px;
  white-space: pre;
}

/* Column capacity is sized so tokens fit; the ellipsis is the visible signal
   for the rare token longer than the cap, in place of a silent mid-word cut. */
.tg-token-text {
  color: var(--ink);
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
}

.tg-token-delta {
  border-left: 1px solid var(--line-soft);
  color: var(--muted);
  min-width: 34px;
  text-align: right;
}

.tg-delta-up {
  color: var(--good);
}

.tg-token-prob {
  background: rgba(115, 184, 181, var(--prob-alpha, .18));
  border-left: 1px solid var(--line-soft);
  color: var(--ink);
  min-width: 46px;
  text-align: right;
}

/* Unlabelled construction geometry links the two projections without turning
   the interface into a literal technical drawing. */
.tg-construction {
  left: 50%;
  pointer-events: none;
  position: absolute;
  top: 0;
  z-index: 2;
}

.tg-origin {
  border: 1px solid var(--line);
  height: 14px;
  left: -6px;
  position: absolute;
  top: -6px;
  width: 14px;
}

.tg-origin::before,
.tg-origin::after {
  background: var(--line);
  content: "";
  left: 6px;
  position: absolute;
  top: 6px;
}

.tg-origin::before {
  height: 1px;
  transform: translateX(-9px);
  width: 36px;
}

.tg-origin::after {
  height: 36px;
  transform: translateY(-9px);
  width: 1px;
}

.tg-ray {
  border-top: 1px dashed var(--line-soft);
  left: 0;
  position: absolute;
  top: 0;
  transform-origin: left center;
  width: 290px;
}

.tg-ray-left {
  transform: rotate(135deg);
}

.tg-ray-right {
  transform: rotate(45deg);
}

.tg-error {
  color: var(--hot) !important;
  padding: 12px;
}

@media (max-width: 1099px) {
  .tg-model-field {
    height: 180px;
  }

  .tg-model-scroll {
    overflow-x: auto;
  }

}

@media (max-width: 760px) {
  .tg-lens {
    border-bottom: 1px solid var(--line);
    border-right: 0;
    min-height: 380px;
  }

  .tg-lens-results {
    overflow-x: auto;
  }

  .tg-construction {
    display: none;
  }
}

@media (max-width: 650px) {
  .tinygpt {
    margin-top: .5rem;
  }

  .tg-masthead,
  .tg-composer-body {
    display: block;
  }

  .tg-overview-copy {
    column-count: 1;
  }

  .tg-masthead > * {
    border-right: 0;
  }

  .tg-key,
  .tg-mode {
    justify-content: flex-start;
  }

  .tg-composer-head {
    align-items: stretch;
    flex-direction: column;
    gap: 12px;
    height: auto;
    min-height: 56px;
  }

  .tg-transport {
    flex-wrap: wrap;
    gap: 8px;
  }

  .tg-transport select {
    flex: 0 1 auto;
    min-width: 0;
  }

  .tg-composer {
    height: auto;
    padding-bottom: 8px;
  }

}

@media (prefers-reduced-motion: reduce) {
  .tinygpt *,
  .tinygpt *::before,
  .tinygpt *::after {
    scroll-behavior: auto !important;
    transition-duration: .01ms !important;
  }

  .tinygpt .tg-md-node rect,
  .tinygpt .tg-md-texture,
  .tinygpt .tg-md-layer-texture,
  .tinygpt .tg-md-answer-box,
  .tinygpt .tg-md-output-token {
    animation: none !important;
    transition: none !important;
  }
}

/* Three-view working field: both readings flank a tall transformer section. */
.tg-primary-field {
  background: rgba(6, 16, 20, .34);
  border-bottom: 2px solid var(--line);
  display: grid;
  grid-template-columns: minmax(0, .9fr) minmax(460px, 1.2fr) minmax(0, .9fr);
  height: 840px;
  position: relative;
}

.tg-primary-field .tg-model-field {
  border-bottom: 0;
  border-left: 1px solid var(--line);
  border-right: 1px solid var(--line);
  height: 840px;
  min-width: 0;
}

.tg-primary-field .tg-model-scroll {
  align-items: stretch;
  display: flex;
  height: 100%;
  justify-content: center;
  overflow: hidden;
}

.tg-primary-field .tg-model-scroll #modelviz {
  height: 100%;
  margin: 0;
  max-width: 520px;
  min-width: 0;
  width: 100%;
}

.tg-primary-field .tg-modeldiagram-svg {
  height: 100%;
  margin: 0 auto;
  max-width: 100%;
  width: auto;
}

.tg-primary-field .tg-lens {
  border: 0;
  min-height: 0;
}

.tg-primary-field .tg-lens h2 {
  height: 44px;
  padding: 10px 16px 7px;
}

.tg-primary-field .tg-lens-results {
  height: calc(100% - 50px);
  overflow: auto;
  padding: 12px;
}

.tg-primary-field .tg-candidate-tracks {
  column-gap: 10px;
}

.tg-primary-field .tg-lens-results tr,
.tg-primary-field .tg-lens-results td {
  height: 30px;
}

.tg-primary-field .tg-token-badge {
  height: 24px;
}

.tg-primary-field .tg-token-text,
.tg-primary-field .tg-token-delta,
.tg-primary-field .tg-token-prob {
  line-height: 22px;
  padding-inline: 4px;
}

.tg-primary-field .tg-construction {
  left: 50%;
  top: 50%;
}

.tg-md-tick-label,
.tg-md-attn-formula,
.tg-md-mlp-formula {
  fill: var(--ink);
  font-family: "TG Technical", monospace;
  font-size: 14px;
  text-anchor: middle;
}

.tg-md-tick-label {
  fill: var(--muted);
  font-size: 13px;
}

.tg-md-residual-text,
.tg-md-repeat-text {
  font-size: 14px;
}

.tg-md-residual-text.vertical,
.tg-md-repeat-text.vertical {
  writing-mode: vertical-rl;
}

.tg-md-answer-box {
  fill: #fff;
}

@media (max-width: 1050px) {
  .tg-primary-field {
    grid-template-columns: minmax(0, .9fr) minmax(380px, 1.15fr) minmax(0, .9fr);
  }

  .tg-primary-field .tg-lens-results {
    padding: 8px;
  }

  .tg-primary-field .tg-candidate-tracks {
    column-gap: 5px;
  }
}

@media (max-width: 760px) {
  .tg-primary-field {
    display: flex;
    flex-direction: column;
    height: auto;
  }

  .tg-primary-field .tg-model-field {
    border: 0;
    border-bottom: 1px solid var(--line);
    height: 760px;
    order: -1;
  }

  .tg-primary-field .tg-lens {
    min-height: 470px;
  }

  .tg-primary-field .tg-lens-results {
    height: 420px;
  }
}

/* Five independent panes sit on the site's grey field. */
.tinygpt {
  display: grid;
  gap: 18px;
  grid-template-areas:
    "masthead masthead"
    "lenses   model"
    "composer composer";
  grid-template-columns: minmax(0, 1.05fr) minmax(430px, .95fr);
  padding: 18px;
}

.tinygpt::before {
  display: none;
}

.tinygpt > .tg-lens-field,
.tinygpt > .tg-model-field,
.tinygpt > .tg-composer,
.tinygpt > .tg-masthead {
  background-color: var(--paper);
  border: 1px solid var(--line);
  min-width: 0;
}

/* One frame, two readings: the logit lens ranks and gets the table, the
   Jacobian lens gets the cloud. Stacked rather than side by side so each keeps
   the full width of the frame — a seven-badge row needs it, and a cloud reads
   better wide than tall. */
.tinygpt > .tg-lens-field {
  align-self: stretch;
  display: grid;
  grid-area: lenses;
  grid-template-columns: minmax(0, 1fr);
  grid-template-rows: auto auto auto;
  min-width: 0;
  overflow: hidden;
}

.tg-lens-field > .tg-lens + .tg-lens {
  border-top: 1px solid var(--line);
}

.tinygpt > .tg-model-field {
  border: 1px solid var(--line);
  grid-area: model;
  height: 840px;
}

.tinygpt > .tg-composer {
  grid-area: composer;
}

.tinygpt > .tg-masthead {
  grid-area: masthead;
}

.tg-lens-field > .tg-lens {
  border: 0;
  min-height: 0;
  overflow: hidden;
}

.tinygpt .tg-lens h2 {
  height: 44px;
  padding: 10px 16px 7px;
}

.tinygpt .tg-lens .tg-lens-results {
  flex: 1;
  min-height: 0;
  overflow: auto;
  padding: 12px;
}

.tinygpt > .tg-model-field .tg-model-scroll {
  align-items: stretch;
  display: flex;
  height: 100%;
  justify-content: center;
  overflow-x: auto;
  overflow-y: hidden;
}

.tinygpt > .tg-model-field #modelviz {
  height: auto;
  margin: 0;
  min-width: 0;
  width: 100%;
}

/* The diagram is a 1000-unit drawing scaled to fit. Sizing it from its width
   rather than the field's height keeps that scale predictable; below roughly
   two-thirds the captions stop being text, so it holds that floor and the
   field scrolls instead of shrinking further. */
.tinygpt > .tg-model-field .tg-modeldiagram-svg {
  aspect-ratio: 10 / 7;
  height: auto;
  margin: 0 auto;
  max-width: 1000px;
  min-width: 640px;
  width: 100%;
}

@media (max-width: 1050px) {
  .tinygpt {
    gap: 12px;
    padding: 12px;
  }

  .tinygpt .tg-lens .tg-lens-results {
    padding: 8px;
  }
}

@media (max-width: 760px) {
  .tinygpt {
    display: grid;
    grid-template-areas:
      "masthead"
      "model"
      "lenses"
      "composer";
    grid-template-columns: minmax(0, 1fr);
  }

  .tinygpt > .tg-model-field {
    height: 760px;
  }

  .tinygpt .tg-lens {
    min-height: 380px;
  }

  .tinygpt .tg-lens .tg-lens-results {
    height: 336px;
  }
}

/* The panes stand the full height of the row they share with the diagram
   rather than floating in it at a fixed shape. */
.tinygpt .tg-lens {
  align-self: stretch;
  min-height: 0;
}

.tinygpt .tg-lens .tg-lens-results {
  overflow: auto;
  padding: 8px;
}

/* Thirteen layers is the whole reading, so the table spends the pane height
   on row spacing rather than leaving the bottom two thirds empty. */
.tinygpt .tg-lens .tg-lens-results table {
  height: 100%;
}

.tinygpt .tg-lens .tg-lens-results tr,
.tinygpt .tg-lens .tg-lens-results td {
  height: auto;
}

.tinygpt .tg-lens .tg-lens-results td {
  padding-block: 0;
}

.tinygpt .tg-lens .tg-candidate-tracks {
  column-gap: 4px;
}

.tinygpt .tg-lens .tg-token-badge {
  font-size: 12px;
  height: 18px;
}

.tinygpt .tg-lens .tg-token-text,
.tinygpt .tg-lens .tg-token-delta,
.tinygpt .tg-lens .tg-token-prob {
  line-height: 16px;
  padding-inline: 3px;
}

.tinygpt .tg-lens .tg-token-delta {
  min-width: 26px;
}

.tinygpt .tg-lens .tg-token-prob {
  min-width: 34px;
}

/* A lens needs two candidates side by side to read as a comparison. On the
   narrowest panes the badge furniture gives up the millimetres so the pair
   still seats its tokens whole. */
@media (max-width: 400px) {
  .tinygpt .tg-lens .tg-token-text,
  .tinygpt .tg-lens .tg-token-delta,
  .tinygpt .tg-lens .tg-token-prob {
    padding-inline: 2px;
  }

  .tinygpt .tg-lens .tg-token-delta {
    min-width: 24px;
  }

  .tinygpt .tg-lens .tg-token-prob {
    min-width: 32px;
  }
}

@media (max-width: 760px) {
  .tinygpt .tg-lens {
    aspect-ratio: auto;
    min-height: 380px;
  }
}

/* Give the three instruments the full browser width. On a large drafting
   surface each lens grows to roughly twice its former constrained size. */
body:has(.tinygpt) .container {
  max-width: none;
  padding-inline: 16px;
}

/* The diagram needs enough width for its captions to render at size; the lens
   frame needs more, because a row of seven badges is the widest thing here. */
.tinygpt {
  grid-template-columns: minmax(0, 1.15fr) minmax(560px, .85fr);
}

.tinygpt > .tg-model-field {
  align-self: center;
  height: auto;
}

/* Side by side stops paying once the model has taken the width it needs: the
   lens frame is left too narrow to seat candidates. Below that the model takes
   a full-width band of its own and the readings sit beneath it. */
@media (max-width: 1599px) {
  .tinygpt {
    grid-template-areas:
      "masthead masthead"
      "lenses   model"
      "composer composer";
    grid-template-columns: minmax(380px, 1fr) minmax(664px, .8fr);
  }

  .tinygpt > .tg-model-field .tg-modeldiagram-svg {
    max-width: 800px;
  }
}

@media (max-width: 760px) {
  body:has(.tinygpt) .container {
    padding-inline: 0;
  }

  .tinygpt {
    grid-template-columns: minmax(0, 1fr);
  }

  .tinygpt > .tg-model-field {
    height: auto;
  }
}

/* ResizeObserver selects a representative subset without changing the data. */
.tg-lens-results .is-adaptively-hidden {
  display: none !important;
}

/* The two readings share one frame. Above the tab width both are stacked in
   it; below, they take turns and the tabs choose. */
.tg-lens-switcher {
  display: none;
}

@media (max-width: 1099px) {
  /* Compose, then watch: stacked, the controls come before the readings they
     produce rather than sitting under everything they made. */
  .tinygpt {
    grid-template-areas:
      "masthead"
      "model"
      "composer"
      "lenses";
    grid-template-columns: minmax(0, 1fr);
  }
}

@media (max-width: 1599px) {
  .tinygpt > .tg-lens-field {
    grid-template-rows: auto 1fr;
  }

  .tg-lens-switcher {
    background: var(--paper);
    border: 1px solid var(--line);
    display: grid;
    gap: 8px;
    grid-template-columns: 1fr 1fr;
    padding: 8px;
  }

  .tg-lens-switcher button {
    background: var(--paper-deep);
    border: 1px solid var(--line-soft);
    border-radius: 0;
    color: var(--muted);
    cursor: pointer;
    font-family: "TG Pixel", monospace;
    font-size: 12px;
    min-height: 44px;
    padding: 8px 10px;
  }

  .tg-lens-switcher button[aria-selected="true"] {
    background: var(--accent);
    border-color: var(--accent);
    color: var(--paper-deep);
  }

  .tg-lens-switcher button:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
  }

  .tinygpt .tg-lens:not(.is-mobile-active) {
    display: none;
  }
}

@media (pointer: coarse) {
  .tg-token {
    align-items: center;
    display: inline-flex;
    justify-content: center;
    min-height: 32px;
    min-width: 24px;
    padding-inline: 2px;
  }
}

/* The checkpoint is a choice, not a status: the sample runs anywhere, the real
   model costs half a gigabyte and a GPU, and nobody should pay that without
   asking for it. */
#checkpoint,
.tg-load {
  background: var(--paper-deep);
  border: 1px solid var(--line-soft);
  color: var(--ink);
  font-family: inherit;
  font-size: 15px;
  padding: 5px 8px;
  text-align: left;
}

/* Room for the native arrow, which otherwise prints over the last glyph. */
#checkpoint {
  padding-right: 22px;
}

.tg-load {
  color: var(--accent);
  cursor: pointer;
}

.tg-load[disabled] {
  color: var(--muted);
  cursor: default;
}

#checkpoint option[disabled] {
  color: var(--muted);
}

/* Red meant "canned, not running". Live inference is not a warning. */
.tg-mode-light.is-live {
  background: var(--good);
}

.tg-field {
  align-items: center;
  color: var(--muted);
  display: flex;
  font-size: 15px;
  gap: 5px;
  white-space: nowrap;
}

.tg-field input {
  background: var(--paper-deep);
  border: 1px solid var(--line-soft);
  color: var(--ink);
  font-family: inherit;
  font-size: 15px;
  padding: 3px 4px;
  width: 4.4rem;
}

/* --- lens statistics ----------------------------------------------------- */


/* --- presentation switch ------------------------------------------------- */

.tg-lens-head {
  align-items: center;
  display: flex;
  gap: 12px;
  justify-content: space-between;
}

.tg-view-switch {
  display: inline-flex;
  flex: none;
  gap: 0;
}

.tg-view-switch button {
  appearance: none;
  background: transparent;
  border: 1px solid var(--line-soft);
  color: var(--muted);
  cursor: pointer;
  font-family: inherit;
  font-size: 13px;
  letter-spacing: .06em;
  padding: 3px 10px;
}

.tg-view-switch button + button {
  border-left: 0;
}

.tg-view-switch button.is-active {
  background: var(--line-soft);
  color: var(--ink);
}

.tg-view-switch button:focus-visible {
  outline: 1px solid var(--accent);
  outline-offset: 1px;
}

/* The table remains the accessible representation in cloud view, so it is
   taken out of the layout without being taken out of the accessibility tree. */
.tg-lens-results.is-visually-hidden {
  clip: rect(0, 0, 0, 0);
  height: 1px;
  overflow: hidden;
  position: absolute;
  width: 1px;
}

/* --- word cloud ---------------------------------------------------------- */

.tg-lens-cloud {
  flex: 1;
  min-height: 180px;
  overflow: hidden;
  position: relative;
}

.tg-lens-cloud[hidden] {
  display: none;
}

/* Out of flow deliberately. resize() writes the viewBox from the host's box,
   and a viewBox gives an SVG an intrinsic aspect ratio — in normal flow, with
   no definite height to resolve against, that ratio sets the host's height,
   which the next resize reads as a larger box. The pane grew on every resize
   and only a reload broke the loop. */
.tg-cloud-svg {
  display: block;
  font-family: "TG Technical", monospace;
  inset: 0;
  position: absolute;
}

.tg-cloud-word {
  dominant-baseline: middle;
  paint-order: stroke fill;
  pointer-events: none;
  user-select: none;
}

/* One line under the cloud's heading, where the logit lens carries its legend:
   the cloud shows no badges, so it needs saying what its sizes mean. */
.tg-lens-note {
  color: var(--muted);
  font-size: 13px;
  line-height: 1.35;
  margin: 0;
}

/* In the lens head the legend reads across rather than down: stacked, it cost
   more height than the rows it explains. */
.tg-lens-head .tg-key {
  align-items: center;
  flex-direction: row;
  flex-wrap: wrap;
  font-size: 14px;
  gap: 4px 10px;
  white-space: normal;
}

.tg-lens-head .tg-key-note {
  font-size: 12px;
  margin: 0;
  max-width: none;
}

/* In a table row a badge fills its track; in the legend it is a specimen and
   must take only the width of the example inside it, or it pushes the labels
   it exists to caption off the row. */
.tg-key .tg-token-badge {
  flex: 0 0 auto;
  width: auto;
}

.tg-key .tg-token-text {
  flex: 0 0 auto;
}

/* With room for three across, the readings take a side each and the diagram
   sits between them: the residual stream is what both are reading, so flanking
   it says more than stacking them beside it. The frame stops being a box —
   display:contents lifts its children into the page grid — so the same markup
   fuses into one tabbed panel the moment the width stops paying for it. */
@media (min-width: 1600px) {
  .tinygpt {
    grid-template-areas:
      "masthead masthead masthead"
      "logit    model    jacobian"
      "composer composer composer";
    grid-template-columns: minmax(0, 1fr) minmax(664px, .8fr) minmax(0, 1fr);
  }

  .tinygpt > .tg-lens-field {
    display: contents;
  }

  .tg-lens-field > .tg-lens {
    align-self: stretch;
    background-color: var(--paper);
    border: 1px solid var(--line);
  }

  .tg-lens-field > .tg-logit {
    grid-area: logit;
  }

  .tg-lens-field > .tg-jacobian {
    border-top: 1px solid var(--line);
    grid-area: jacobian;
  }
}

/* A select is as wide as its longest option, which on a machine without WebGPU
   is a sentence. Capped so the checkpoint cannot dominate the row. */
#checkpoint {
  max-width: 13rem;
}

/* Temperature, top-k and length are set once and left alone, so they read as
   settings rather than controls: smaller, grouped, and out of the way of the
   two things actually used every run. */
.tg-transport-fine {
  align-items: center;
  display: flex;
  flex-wrap: wrap;
  gap: 4px 10px;
}

.tg-transport-fine .tg-field {
  font-size: 12px;
  gap: 4px;
}

.tg-transport-fine .tg-field input {
  font-size: 13px;
  padding: 2px 4px;
  width: 3.2rem;
}

/* Three characters and a decimal point, where the others hold two digits. */
.tg-transport-fine .tg-field input#temperature {
  width: 4rem;
}

/* Small enough to be out of the way, never small enough to miss with a thumb. */
@media (pointer: coarse) {
  .tg-transport-fine .tg-field input {
    font-size: 14px;
    min-height: 32px;
    width: 3.6rem;
  }
}

/* Speed and GENERATE belong together: one says how fast, the other starts it.
   The checkpoint sits at the far end, where a thing you touch once belongs. */
.tg-transport {
  margin-right: auto;
}

.tg-composer-head .tg-mode {
  margin-left: auto;
}

/* On a phone the composer is the only thing on screen, so its own heading says
   nothing the reader does not already know, and the text matters more than the
   settings — it goes first and the controls sit under it. */
@media (max-width: 760px) {
  .tg-composer {
    padding-bottom: 10px;
  }

  .tg-composer h2 {
    clip: rect(0, 0, 0, 0);
    height: 1px;
    margin: -1px;
    overflow: hidden;
    position: absolute;
    width: 1px;
  }

  .tg-composer-body {
    border-bottom: 1px solid var(--line-soft);
    border-top: 0;
    margin: 0 -16px 12px;
    order: -1;
  }

  /* Pushing it to the far end is a wide-layout idea; on its own row it just
     leaves a hole to its left. */
  .tg-composer-head .tg-mode {
    margin-left: 0;
  }
}

/* Temperature, top-k and length are set once a session. On a small phone that
   is three controls' worth of width spent on something rarely touched, so they
   fold behind a gear and the two controls used every run get the room. */
.tg-settings {
  align-items: center;
  background: transparent;
  border: 1px solid var(--line-soft);
  border-radius: 0;
  color: var(--muted);
  cursor: pointer;
  display: none;
  height: 44px;
  justify-content: center;
  padding: 0;
  width: 44px;
}

.tg-settings svg {
  height: 20px;
  width: 20px;
}

.tg-settings svg path {
  fill: none;
  stroke: currentColor;
  stroke-width: 1.4;
}

.tg-settings[aria-expanded="true"] {
  border-color: var(--accent);
  color: var(--accent);
}

.tg-settings:focus-visible {
  outline: 1px solid var(--accent);
  outline-offset: 1px;
}

@media (max-width: 480px) {
  .tg-settings {
    display: inline-flex;
  }

  .tg-transport-fine {
    display: none;
    width: 100%;
  }

  .tg-transport-fine.is-open {
    display: flex;
  }

  /* Smaller type and tighter padding, but never below the 44px a thumb needs. */
  .tg-transport select,
  .tg-replay {
    font-size: 14px;
  }


  .tg-transport select {
    padding: 4px 26px 4px 8px;
  }

  .tg-replay {
    padding: 4px 12px;
  }

  .tg-transport label[for="speed"] {
    font-size: 13px;
  }
}

/* Pace is a preference; GENERATE is the action. The select reads as the
   quieter of the two rather than the widest thing in the row. */
#speed {
  color: var(--muted);
  font-size: 15px;
}

@media (max-width: 480px) {
  /* The options say what they set, so the label is only worth its width to a
     screen reader. */
  .tg-transport label[for="speed"] {
    clip: rect(0, 0, 0, 0);
    height: 1px;
    margin: -1px;
    overflow: hidden;
    position: absolute;
    width: 1px;
  }

  #speed {
    font-size: 14px;
    padding: 4px 22px 4px 7px;
  }
}

/* The explainer is worth reading once and then in the way. Open where there is
   room, folded away on a small phone, and it says what it answers either way. */
.tg-explainer {
  width: 100%;
}

.tg-explainer > summary {
  color: var(--accent);
  cursor: pointer;
  font-size: 15px;
  list-style: none;
  margin-bottom: 8px;
  padding: 4px 0;
}

.tg-explainer > summary::-webkit-details-marker {
  display: none;
}

.tg-explainer > summary::before {
  content: "▸ ";
}

.tg-explainer[open] > summary::before {
  content: "▾ ";
}

.tg-explainer > summary:focus-visible {
  outline: 1px solid var(--accent);
  outline-offset: 2px;
}

@media (pointer: coarse) {
  .tg-explainer > summary {
    padding: 10px 0;
  }
}

/* The masthead's minimum was set when it held three cells side by side. With
   the explainer collapsed it is one line of text, and the minimum showed as an
   empty panel under it. */
@media (max-width: 1099px) {
  .tg-masthead {
    min-height: 0;
  }
}

/* The vertical drawing is far taller than wide, so it is sized from its width
   and allowed its own height rather than being fitted to the field's. */
.tinygpt .tg-modeldiagram-svg.is-vertical {
  aspect-ratio: 38 / 101;
  max-width: 340px;
  min-width: 0;
}

@media (max-width: 480px) {
  .tinygpt > .tg-model-field {
    height: auto;
  }

  .tinygpt > .tg-model-field .tg-model-scroll {
    overflow: visible;
  }
}

/* With nothing to read yet the pane holds one sentence, which belongs in the
   middle of the space it is standing in rather than tucked into its corner. */
.tg-lens-results:has(.tg-placeholder) {
  align-items: center;
  display: flex;
  justify-content: center;
}

.tg-placeholder {
  text-align: center;
}

/* The cloud cannot hold a placeholder of its own — the word cloud owns its
   host's children — so the message sits over it and steps aside once there is
   a reading to show. */
.tg-cloud-stage {
  display: flex;
  flex: 1;
  /* The floor belongs here rather than on the cloud: zeroing the cloud's own
     minimum to let it flex collapsed the pane to a single pixel. */
  min-height: 180px;
  position: relative;
}

.tg-cloud-stage > .tg-lens-cloud {
  flex: 1;
}

.tg-cloud-placeholder {
  align-items: center;
  display: flex;
  inset: 0;
  justify-content: center;
  margin: 0;
  padding: 12px;
  position: absolute;
}

.tg-jacobian.has-reading .tg-cloud-placeholder {
  display: none;
}

/* TEMPORARY: legend and cloud explainer hidden while the layout is judged
   without them. Delete this block to bring both back — nothing else was
   changed, and both remain in the markup and the accessibility tree. */
.tg-lens-head .tg-key,
.tg-lens-note {
  display: none;
}
