/* ---------------------------------------------------------------------------
   graderoot. — the teacher application.

   One stylesheet, no build step, no framework. The product's promise is a
   self-contained artifact that runs anywhere; an app that needed a bundler to
   render a login page would not believe its own thesis.

   Palette note: every incumbent in refs/incumbents is purple, indigo or
   primary blue. This is deliberately neither — a deep teal that reads as a
   considered instrument rather than a toy, with amber reserved for the one
   thing on screen the teacher should act on.
--------------------------------------------------------------------------- */

:root {
  --ink:        #14161a;
  --ink-2:      #3d434e;
  --ink-3:      #666e7d;
  /* Decorative fills only: dots, rules, glyphs. At 2.4:1 on the page ground
     this is not a text colour, and it kept drifting into text roles — the
     progress rail, passage source refs, speaker labels, placeholders. Text
     that needs to look quiet takes --ink-3 (4.8:1). */
  --ink-4:      #9aa2b1;
  --line:       #e3ded6;
  --line-2:     #efeae2;
  --paper:      #faf7f2;
  --surface:    #ffffff;
  --surface-2:  #f5f2ec;

  --accent:     #0f5c57;
  --accent-ink: #0a3f3b;
  --accent-2:   #e8f0ee;
  --accent-3:   #cfe0dd;

  --warm:       #b4560f;
  --warm-2:     #fdf0e3;

  --good:       #1c6b3f;
  --good-2:     #e8f3ec;
  --bad:        #a02121;
  --bad-2:      #fdecec;


  /* The type scale. Every font-size in this file is one of these.
     Before it existed there were 37 sizes across two unit systems, five of
     them inside a 0.64px band — differences nobody chose and nobody can see,
     which is what makes a screen read as assembled rather than composed. */
  --t-2xs: 0.6875rem;    /* 11px */
  --t-xs: 0.75rem;     /* 12px */
  --t-sm: 0.8125rem;     /* 13px */
  --t-base: 0.875rem;   /* 14px */
  --t-md: 0.9375rem;     /* 15px */
  --t-lg: 1.0625rem;     /* 17px */
  --t-xl: 1.25rem;     /* 20px */
  --t-2xl: 1.625rem;    /* 26px */
  --t-3xl: 2rem;    /* 32px */
  --t-4xl: 2.375rem;    /* 38px */

  /* The spacing scale: 4px, doubling and halving. Every margin, padding and
     gap snaps to it. There used to be 37 values, including every integer from
     1 to 20. */
  --s-05: 2px;  --s-1: 4px;   --s-15: 6px;  --s-2: 8px;
  --s-3: 12px;  --s-4: 16px;  --s-5: 20px;  --s-6: 24px;
  --s-8: 32px;  --s-10: 40px; --s-12: 48px; --s-16: 64px;

  --r-sm: 6px;
  --r:    10px;
  --r-lg: 16px;
  --r-xl: 22px;

  --shadow-1: 0 1px 2px rgba(20,22,26,.05), 0 1px 3px rgba(20,22,26,.04);
  --shadow-2: 0 2px 4px rgba(20,22,26,.04), 0 8px 20px -6px rgba(20,22,26,.10);
  --shadow-3: 0 4px 8px rgba(20,22,26,.05), 0 24px 48px -12px rgba(20,22,26,.16);

  --sans: ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  --serif: ui-serif, Georgia, "Iowan Old Style", "Times New Roman", serif;
  --mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;

  --wrap: 1120px;
}

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

html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  font-family: var(--sans);
  font-size: var(--t-md);
  line-height: 1.55;
  color: var(--ink);
  background: var(--paper);
  -webkit-font-smoothing: antialiased;
}

h1, h2, h3, h4 { margin: 0 0 .4em; line-height: 1.18; letter-spacing: -0.014em; font-weight: 600; }
h1 { font-size: var(--t-3xl); }
h2 { font-size: var(--t-xl); }
h3 { font-size: var(--t-lg); }
p { margin: 0 0 1em; }
p:last-child { margin-bottom: 0; }

a { color: var(--accent-ink); text-underline-offset: 2px; }
/* Links, not buttons. A button that happens to be an <a> carries its own
   foreground in --btn-fg, and a bare `a:hover` outranks the `.btn` rule that
   reads it — so every link-shaped primary button turned accent-on-accent the
   moment a pointer touched it. */
a:not(.btn):hover { color: var(--accent); }

:focus-visible {
  outline: 3px solid var(--accent);
  outline-offset: 2px;
  border-radius: 3px;
}

.skip {
  position: absolute; left: -9999px; top: 0; z-index: 100;
  background: var(--ink); color: #fff; padding: 12px 16px; border-radius: 0 0 var(--r) 0;
}
.skip:focus { left: 0; }

/* ------------------------------------------------------------------ layout */

.wrap { max-width: var(--wrap); margin: 0 auto; padding: 0 24px; }
.wrap-narrow { max-width: 760px; margin: 0 auto; padding: 0 24px; }

.appbar {
  background: var(--surface);
  border-bottom: 1px solid var(--line);
  position: sticky; top: 0; z-index: 20;
}
/* 48px, not 62.
   On a 1366x768 laptop — which is what a school laptop is — every row of
   chrome is a row the work does not get. The bar holds a wordmark and three
   controls; it does not need the height of a hero. The controls keep their own
   size, so nothing here shrinks a tap target: only the space around them goes. */
.appbar .wrap { display: flex; align-items: center; gap: 18px; height: 48px; }

.brand {
  display: inline-flex; align-items: center; gap: 7px;
  font-family: var(--serif); font-size: var(--t-lg); font-weight: 600;
  letter-spacing: -.02em; color: var(--ink); text-decoration: none;
}
.brand .mark {
  width: 22px; height: 22px; border-radius: 7px;
  background: var(--accent); position: relative; flex: none;
}
.brand .mark::after {
  content: ""; position: absolute; inset: 6px 6px auto auto;
  width: 7px; height: 7px; border-radius: 50%; background: #f0c46a;
}
.appbar nav { margin-left: auto; display: flex; align-items: center; gap: 6px; }
.appbar nav a {
  padding: 6px 10px; border-radius: var(--r-sm); color: var(--ink-2);
  text-decoration: none; font-size: var(--t-md); font-weight: 500;
}
/* Buttons living in a bar sit to its height rather than setting it. The base
   .btn is min-height 42, which in a 48px bar leaves three pixels of air and
   reads as cramped — and padding cannot fix a min-height. 34 is the same size
   the small button already uses, and well clear of the 24px target floor. */
.appbar nav .btn, .studio-bar .btn { min-height: 34px; padding: 0 14px; }
.appbar nav a:hover { background: var(--surface-2); color: var(--ink); }
.appbar nav a[aria-current="page"] { color: var(--ink); background: var(--surface-2); }
/* A .btn inside the nav is still a button: the nav-link colour rules above are
   more specific than .btn's, and would otherwise paint dark text on the accent
   fill. Restate the button's own colours at matching specificity. */
.appbar nav a.btn { color: var(--btn-fg); background: var(--btn-bg); }
.appbar nav a.btn-primary:hover { color: #fff; background: var(--accent-ink); }

main { padding: 32px 0 96px; }

.page-head { display: flex; align-items: flex-end; gap: 20px; margin-bottom: 20px; flex-wrap: wrap; }
.page-head h1 { margin: 0; }
.page-head .sub { color: var(--ink-3); margin: 6px 0 0; }
.page-head .spacer { flex: 1; }

/* ------------------------------------------------------------------ blocks */

.card {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-1);
}
.card-pad { padding: 20px 24px; }

.stack > * + * { margin-top: 12px; }
.stack-lg > * + * { margin-top: 24px; }
.row { display: flex; gap: 12px; align-items: center; flex-wrap: wrap; }
.row-tight { display: flex; gap: 8px; align-items: center; }
.spread { display: flex; gap: 16px; align-items: center; justify-content: space-between; flex-wrap: wrap; }
.muted { color: var(--ink-3); }
.small { font-size: var(--t-base); }
.tiny { font-size: var(--t-sm); }
.mono { font-family: var(--mono); font-size: .86em; }
.center { text-align: center; }

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

.btn {
  --btn-bg: var(--surface);
  --btn-fg: var(--ink);
  --btn-line: var(--line);
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  min-height: 42px; padding: 0 16px;
  font: inherit; font-size: var(--t-md); font-weight: 600;
  color: var(--btn-fg); background: var(--btn-bg);
  border: 1px solid var(--btn-line); border-radius: var(--r);
  cursor: pointer; text-decoration: none;
  transition: background .12s ease, border-color .12s ease, transform .06s ease;
}
/* The variable, not the property.
 *
 * This said `background: var(--surface-2)`, and every other button sets its
 * colour through `--btn-bg` — which `.btn { background: var(--btn-bg) }` reads.
 * A declaration on `.btn:hover` outranks one on `.btn`, so the moment a pointer
 * touched a primary button its dark green was replaced by pale cream while the
 * text stayed white: 7.81:1 at rest, 1.12:1 hovered. Every primary button on
 * every page, for everyone.
 *
 * Changing the variable instead lets `.btn-primary:hover` win on source order,
 * which is what it was always written expecting to do. */
.btn:hover { --btn-bg: var(--surface-2); }
.btn:active { transform: translateY(1px); }


.btn-primary {
  --btn-bg: var(--accent); --btn-fg: #fff; --btn-line: var(--accent);
  box-shadow: var(--shadow-1);
}
.btn-primary:hover { --btn-bg: var(--accent-ink); --btn-line: var(--accent-ink); }

.btn-warm { --btn-bg: var(--warm); --btn-fg: #fff; --btn-line: var(--warm); }
.btn-warm:hover { --btn-bg: #96470c; --btn-line: #96470c; }

.btn-ghost { --btn-line: transparent; --btn-bg: transparent; color: var(--ink-2); }
.btn-ghost:hover { --btn-bg: var(--surface-2); }

.btn-danger { --btn-fg: var(--bad); }
.btn-danger:hover { --btn-bg: var(--bad-2); --btn-line: #f0c9c9; }

.btn-lg { min-height: 50px; padding: 0 24px; font-size: var(--t-lg); border-radius: var(--r); }
.btn-sm { min-height: 34px; padding: 0 12px; font-size: var(--t-base); }
.btn-block { width: 100%; }

/* ------------------------------------------------------------------- forms */

label { display: block; font-weight: 600; font-size: var(--t-md); margin-bottom: 6px; }
.hint { color: var(--ink-3); font-size: var(--t-base); font-weight: 400; margin: -2px 0 6px; }

input[type=text], input[type=email], input[type=password], input[type=search], textarea, select {
  width: 100%; font: inherit; color: var(--ink);
  background: var(--surface);
  border: 1px solid var(--line); border-radius: var(--r);
  padding: 12px 12px;
  transition: border-color .12s ease, box-shadow .12s ease;
}
input:hover, textarea:hover { border-color: #d3cbc0; }
input:focus, textarea:focus, select:focus {
  outline: none; border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-3);
}
textarea { resize: vertical; min-height: 108px; line-height: 1.5; }
::placeholder { color: var(--ink-4); }

.field + .field { margin-top: 16px; }
.field-error { color: var(--bad); font-size: var(--t-base); margin-top: 4px; display: block; }
.field.is-error input, .field.is-error textarea { border-color: var(--bad); }

.checkline { display: flex; gap: 8px; align-items: flex-start; }
.checkline input[type=checkbox] { width: 18px; height: 18px; margin: 2px 0 0; accent-color: var(--accent); flex: none; }
.checkline label { margin: 0; font-weight: 400; }

/* ------------------------------------------------------------------ notice */

.notice {
  border: 1px solid var(--line); border-left: 3px solid var(--ink-4);
  background: var(--surface); border-radius: var(--r);
  padding: 12px 16px; font-size: var(--t-md);
}
.notice-warn { border-left-color: var(--warm); background: var(--warm-2); border-color: #f0dcc4; }
.notice-bad  { border-left-color: var(--bad); background: var(--bad-2); border-color: #f2d3d3; }
.notice-good { border-left-color: var(--good); background: var(--good-2); border-color: #cde3d5; }
.notice-info { border-left-color: var(--accent); background: var(--accent-2); border-color: var(--accent-3); }
.notice strong { font-weight: 600; }

/* ------------------------------------------------------------------- chips */

.chip {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 2px 8px; border-radius: 999px;
  background: var(--surface-2); border: 1px solid var(--line);
  font-size: var(--t-xs); font-weight: 600; color: var(--ink-2); white-space: nowrap;
}
.chip-accent { background: var(--accent-2); border-color: var(--accent-3); color: var(--accent-ink); }
.chip-warm   { background: var(--warm-2); border-color: #f0dcc4; color: #8a4109; }
.chip-good   { background: var(--good-2); border-color: #cde3d5; color: var(--good); }
.chip-bad    { background: var(--bad-2); border-color: #f2d3d3; color: var(--bad); }
.chip-dot { width: 6px; height: 6px; border-radius: 50%; background: currentColor; flex: none; }

/* -------------------------------------------------------------- dashboard */

.grid-spaces { display: grid; gap: 16px; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); }

.space-card {
  display: flex; flex-direction: column;
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-1);
  transition: box-shadow .15s ease, transform .15s ease, border-color .15s ease;
  /* Not `overflow: hidden`. The card used to clip its own contents to keep the
     thumbnail inside the rounded corner, which also clipped the menu that opens
     out of it — the last line of "nothing is deleted, you can unarchive it" was
     cut off by the card's bottom edge, on the one control that needs its
     consequences read. The thumbnail rounds its own corners instead. */
  position: relative;
}
.space-card:has(.menu[open]) { z-index: 30; }
.space-card:hover { box-shadow: var(--shadow-2); transform: translateY(-1px); border-color: #d8d1c6; }
.space-card .thumb {
  height: 118px; background: var(--surface-2);
  border-bottom: 1px solid var(--line-2);
  border-radius: calc(var(--r-lg) - 1px) calc(var(--r-lg) - 1px) 0 0;
  display: flex; align-items: center; justify-content: center;
  position: relative; overflow: hidden;
}
.space-card .thumb .glyph { font-family: var(--serif); font-size: var(--t-4xl); color: var(--accent); opacity: .32; }
.space-card .body { padding: 16px 16px 16px; display: flex; flex-direction: column; flex: 1; gap: 8px; }
.space-card h3 { margin: 0; font-size: var(--t-lg); }
.space-card h3 a { color: inherit; text-decoration: none; }
.space-card h3 a:hover { text-decoration: underline; }
.space-card .meta { color: var(--ink-3); font-size: var(--t-sm); }
.space-card .foot { margin-top: auto; display: flex; gap: 8px; align-items: center; }

.empty {
  border: 1px dashed var(--line); border-radius: var(--r-lg);
  background: var(--surface); padding: 48px 32px; text-align: center;
}
.empty h2 { font-family: var(--serif); font-weight: 600; }
.empty p { max-width: 46ch; margin-inline: auto; color: var(--ink-3); }

/* ----------------------------------------------------------------- steps */

.steps { display: flex; gap: 0; margin-bottom: 24px; border-bottom: 1px solid var(--line); }
.steps li {
  list-style: none; display: flex; align-items: center; gap: 8px;
  padding: 0 16px 12px; color: var(--ink-3); font-size: var(--t-base); font-weight: 500;
  position: relative;
}
.steps li:first-child { padding-left: 0; }
.steps li .n {
  width: 23px; height: 23px; border-radius: 50%; flex: none;
  display: grid; place-items: center; font-size: var(--t-xs); font-weight: 600;
  background: var(--surface-2); color: var(--ink-3); border: 1px solid var(--line);
}
.steps li[data-state="current"] { color: var(--ink); }
.steps li[data-state="current"] .n { background: var(--accent); color: #fff; border-color: var(--accent); }
.steps li[data-state="current"]::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: -1px; height: 2px; background: var(--accent);
}
.steps li:first-child[data-state="current"]::after { left: 0; }
.steps li[data-state="done"] { color: var(--ink-2); }
.steps li[data-state="done"] .n { background: var(--accent-2); color: var(--accent-ink); border-color: var(--accent-3); }

/* The card menu. A <details>, so the browser owns the hard parts. */
.space-card__top { display: flex; align-items: flex-start; gap: 8px; }
.space-card__top h3 { flex: 1; min-width: 0; margin: 0; }

.menu { position: relative; flex: none; }
.menu > summary {
  list-style: none; cursor: pointer; user-select: none;
  width: 28px; height: 24px; border-radius: var(--r-sm);
  display: grid; place-items: center;
  color: var(--ink-3); font-size: var(--t-base); line-height: 1;
}
.menu > summary::-webkit-details-marker { display: none; }
.menu > summary:hover { background: var(--surface-2); color: var(--ink); }
.menu > summary:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.menu[open] > summary { background: var(--surface-2); color: var(--ink); }

.menu__panel {
  position: absolute; right: 0; top: calc(100% + 4px); z-index: 20;
  width: min(280px, 78vw); padding: 12px;
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--r); box-shadow: var(--shadow-2, 0 12px 28px -8px rgba(20,22,26,.22));
}
.menu__form { display: flex; gap: 6px; }
.menu__form input {
  flex: 1; min-width: 0; padding: 6px 8px; font: inherit; font-size: var(--t-sm);
  border: 1px solid var(--line); border-radius: var(--r-sm); background: var(--surface-2);
}
.menu__form input:focus-visible { outline: 2px solid var(--accent); outline-offset: -1px; }
.menu__panel hr { border: 0; border-top: 1px solid var(--line-2); margin: 12px 0 10px; }
.menu__note { margin: 8px 0 0; font-size: var(--t-xs); color: var(--ink-3); line-height: 1.45; }

/* ---------------------------------------------------------------- upload */

.dropzone {
  border: 2px dashed var(--line); border-radius: var(--r-lg);
  background: var(--surface); padding: 48px 24px; text-align: center;
  transition: border-color .14s ease, background .14s ease;
}
.dropzone.is-over { border-color: var(--accent); background: var(--accent-2); }
.dropzone h2 { font-size: var(--t-lg); }
.dropzone .formats { color: var(--ink-3); font-size: var(--t-base); margin-top: 8px; }

.filelist { list-style: none; margin: 16px 0 0; padding: 0; }
.filelist li {
  display: flex; gap: 12px; align-items: center;
  padding: 12px 12px; border: 1px solid var(--line); border-radius: var(--r);
  background: var(--surface); margin-top: 8px;
}
.filelist .ext {
  width: 34px; height: 34px; border-radius: var(--r-sm); flex: none;
  display: grid; place-items: center; font-size: var(--t-2xs); font-weight: 700;
  background: var(--surface-2); color: var(--ink-2); letter-spacing: .03em;
}
.filelist .name { font-weight: 500; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.filelist .size { color: var(--ink-3); font-size: var(--t-sm); }

/* -------------------------------------------------------------- passages */

.passages { display: grid; gap: 8px; }
.passage {
  display: grid; grid-template-columns: 26px 1fr; gap: 12px;
  padding: 12px 12px; border: 1px solid var(--line); border-radius: var(--r);
  background: var(--surface);
}
.passage.is-out { background: var(--surface-2); border-style: dashed; }
.passage.is-out .ptext { color: var(--ink-3); text-decoration: line-through; text-decoration-thickness: 1px; }
.passage input[type=checkbox] { width: 18px; height: 18px; margin-top: 2px; accent-color: var(--accent); }
.passage .phead { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; margin-bottom: 4px; }
.passage .ptext { font-size: var(--t-md); color: var(--ink); white-space: pre-wrap; }
.passage .pref { font-size: var(--t-xs); color: var(--ink-3); font-family: var(--mono); }

.filegroup { margin-top: 24px; }
.filegroup > header {
  display: flex; gap: 12px; align-items: center; flex-wrap: wrap;
  padding-bottom: 8px; margin-bottom: 12px; border-bottom: 1px solid var(--line);
}
.filegroup > header h3 { margin: 0; }

/* ---------------------------------------------------------------- prompt */

.promptbox { position: relative; }
.promptbox textarea {
  min-height: 168px; font-size: var(--t-lg); line-height: 1.55;
  padding: 16px 16px; border-radius: var(--r-lg);
}
.examples { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 12px; }
.examples button {
  font: inherit; font-size: var(--t-sm); text-align: left;
  padding: 6px 12px; border-radius: 999px;
  border: 1px solid var(--line); background: var(--surface); color: var(--ink-2);
  cursor: pointer;
}
.examples button:hover { border-color: var(--accent-3); background: var(--accent-2); color: var(--accent-ink); }

/* ------------------------------------------------------------------- wait */

.wait { display: grid; gap: 20px; }
.stagelist { list-style: none; margin: 0; padding: 0; display: grid; gap: 2px; }
.stagelist li {
  display: grid; grid-template-columns: 26px 1fr auto; gap: 12px; align-items: start;
  padding: 12px 4px; border-bottom: 1px solid var(--line-2);
}
.stagelist li:last-child { border-bottom: 0; }
.stagelist .dot {
  width: 20px; height: 20px; border-radius: 50%; margin-top: 2px;
  border: 2px solid var(--line); background: var(--surface); display: grid; place-items: center;
  font-size: var(--t-2xs); color: #fff; font-weight: 700;
}
.stagelist li[data-state="done"] .dot { background: var(--accent); border-color: var(--accent); }
.stagelist li[data-state="done"] .dot::after { content: "✓"; }
.stagelist li[data-state="active"] .dot { border-color: var(--accent); box-shadow: 0 0 0 4px var(--accent-2); }
.stagelist li[data-state="active"] .dot::after {
  content: ""; width: 8px; height: 8px; border-radius: 50%; background: var(--accent);
  animation: pulse 1.4s ease-in-out infinite;
}
.stagelist li[data-state="failed"] .dot { background: var(--bad); border-color: var(--bad); }
.stagelist li[data-state="failed"] .dot::after { content: "!"; }
.stagelist .label { font-weight: 600; }
.stagelist li[data-state="todo"] .label { color: var(--ink-4); }
.stagelist .detail { color: var(--ink-3); font-size: var(--t-base); }
.stagelist .elapsed { color: var(--ink-4); font-size: var(--t-sm); font-family: var(--mono); }

@keyframes pulse { 0%,100% { opacity: 1; transform: scale(1); } 50% { opacity: .45; transform: scale(.75); } }
@media (prefers-reduced-motion: reduce) { * { animation: none !important; transition: none !important; } }

.progressbar { height: 5px; background: var(--line-2); border-radius: 999px; overflow: hidden; }
.progressbar i { display: block; height: 100%; background: var(--accent); border-radius: 999px; transition: width .5s ease; }

/* ---------------------------------------------------------------- preview */

.preview-shell {
  display: grid; grid-template-columns: minmax(0,1fr) 340px; gap: 20px; align-items: start;
}
@media (max-width: 940px) { .preview-shell { grid-template-columns: 1fr; } }

.device {
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-lg);
  overflow: hidden; box-shadow: var(--shadow-2);
}
.device > header {
  display: flex; gap: 8px; align-items: center;
  padding: 8px 12px; border-bottom: 1px solid var(--line); background: var(--surface-2);
}
.device .dots { display: flex; gap: 4px; }
.device .dots i { width: 9px; height: 9px; border-radius: 50%; background: #d8d2c8; display: block; }
.device .fakeurl {
  flex: 1; font-family: var(--mono); font-size: var(--t-xs); color: var(--ink-3);
  background: var(--surface); border: 1px solid var(--line); border-radius: 999px;
  padding: 2px 12px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.device iframe { display: block; width: 100%; height: 620px; border: 0; background: #fff; }
.device[data-size="phone"] iframe { width: 390px; height: 720px; margin: 16px auto; border: 1px solid var(--line); border-radius: 22px; }

.sizeswitch { display: inline-flex; border: 1px solid var(--line); border-radius: var(--r); overflow: hidden; }
.sizeswitch button {
  font: inherit; font-size: var(--t-sm); font-weight: 600; padding: 6px 12px;
  background: var(--surface); border: 0; border-right: 1px solid var(--line); cursor: pointer; color: var(--ink-2);
}
.sizeswitch button:last-child { border-right: 0; }
.sizeswitch button[aria-pressed="true"] { background: var(--accent); color: #fff; }

.sharebox {
  display: flex; gap: 8px; align-items: center;
  border: 1px solid var(--accent-3); background: var(--accent-2);
  border-radius: var(--r); padding: 8px 8px 8px 12px;
}
.sharebox code {
  flex: 1; font-family: var(--mono); font-size: var(--t-base); color: var(--accent-ink);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

.deflist { display: grid; gap: 0; margin: 0; }
.deflist > div { display: flex; gap: 12px; justify-content: space-between; padding: 8px 0; border-bottom: 1px solid var(--line-2); }
.deflist > div:last-child { border-bottom: 0; }
.deflist dt { color: var(--ink-3); font-size: var(--t-base); margin: 0; }
.deflist dd { margin: 0; font-size: var(--t-base); font-weight: 500; text-align: right; }

.versions { list-style: none; margin: 0; padding: 0; }
.versions li { display: flex; gap: 12px; align-items: center; padding: 12px 0; border-bottom: 1px solid var(--line-2); }
.versions li:last-child { border-bottom: 0; }
.versions .vn {
  width: 30px; height: 30px; border-radius: 50%; flex: none; display: grid; place-items: center;
  background: var(--surface-2); border: 1px solid var(--line); font-size: var(--t-sm); font-weight: 600;
}
.versions li[data-live="1"] .vn { background: var(--accent); color: #fff; border-color: var(--accent); }
.versions .vprompt { flex: 1; font-size: var(--t-base); color: var(--ink-2); }

/* ---------------------------------------------------------------- landing */

.hero { padding: 64px 0 40px; }
.hero h1 {
  font-family: var(--serif); font-size: clamp(2.1rem, 4.6vw, 3.3rem);
  font-weight: 600; letter-spacing: -.026em; max-width: 15ch; margin-bottom: .28em;
}
.hero .lede { font-size: var(--t-lg); color: var(--ink-2); max-width: 54ch; margin-bottom: 24px; }
.hero .lede strong { color: var(--ink); font-weight: 600; }

.demo-prompt {
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-lg);
  box-shadow: var(--shadow-2); overflow: hidden;
}
.demo-prompt header {
  padding: 8px 16px; border-bottom: 1px solid var(--line-2);
  font-size: var(--t-sm); color: var(--ink-3); display: flex; gap: 8px; align-items: center;
}
.demo-prompt .typed {
  padding: 16px 16px 16px; font-size: var(--t-lg); line-height: 1.5; color: var(--ink);
  min-height: 92px;
}
.demo-prompt .caret { display: inline-block; width: 2px; height: 1.1em; background: var(--accent); vertical-align: -.18em; animation: blink 1.1s steps(2) infinite; }
@keyframes blink { 50% { opacity: 0; } }
.demo-prompt footer { padding: 12px 16px; border-top: 1px solid var(--line-2); background: var(--surface-2); display: flex; gap: 8px; align-items: center; }

.hero-grid { display: grid; grid-template-columns: 1.05fr .95fr; gap: 48px; align-items: center; }
@media (max-width: 900px) { .hero-grid { grid-template-columns: 1fr; gap: 32px; } .hero { padding-top: 32px; } }

.proof { display: grid; grid-template-columns: repeat(3, 1fr); gap: 16px; margin-top: 40px; }
@media (max-width: 760px) { .proof { grid-template-columns: 1fr; } }
.proof .card-pad h3 { font-size: var(--t-md); }
.proof p { font-size: var(--t-base); color: var(--ink-3); margin: 0; }

.section { padding: 48px 0; border-top: 1px solid var(--line); }
.section h2 { font-family: var(--serif); font-weight: 600; font-size: var(--t-2xl); letter-spacing: -.02em; }

footer.site { border-top: 1px solid var(--line); padding: 32px 0 48px; color: var(--ink-3); font-size: var(--t-base); }

/* ------------------------------------------------------------------ misc */

.auth-shell { max-width: 420px; margin: 64px auto 0; }
.auth-shell .card-pad { padding: 24px; }

.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: 0;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

table.plain { width: 100%; border-collapse: collapse; font-size: var(--t-base); }
table.plain th, table.plain td { text-align: left; padding: 8px 8px; border-bottom: 1px solid var(--line-2); }
table.plain th { color: var(--ink-3); font-weight: 600; font-size: var(--t-sm); }

/* ---------------------------------------------------------------------------
   The studio.

   Two states of one page. Before a Space exists the conversation is the whole
   width and the prompt box sits in the middle of it; once it exists the
   conversation becomes a column and the Space takes the main panel. The swap
   is `.has-space` on one element, so it costs no navigation and no reflow of
   anything already drawn.
--------------------------------------------------------------------------- */

.studio-body { height: 100vh; overflow: hidden; }
/* The studio owns the whole viewport.
   The site-wide `main { padding: 32px 0 96px}` is right for a document page
   and wrong here: the header lives INSIDE main, so subtracting its height a
   second time left a 96px dead band under the composer and a 34px gap above
   the panels. A flex column with the bar fixed and the panels taking the rest
   is the shape this screen actually wants. */
.studio-body main {
  height: 100vh; padding: 0; margin: 0;
  display: flex; flex-direction: column; min-height: 0;
}
.studio-body .studio-bar { flex: none; }
.studio-body .studio { flex: 1; min-height: 0; }

.studio-bar {
  display: flex; align-items: center; justify-content: space-between; gap: 16px;
  height: 48px; padding: 0 14px;
  background: var(--surface); border-bottom: 1px solid var(--line);
}
.studio-bar__left { display: flex; align-items: center; gap: 12px; min-width: 0; }
.studio-bar__right { display: flex; align-items: center; gap: 8px; flex: none; }
.studio-bar__sep { width: 1px; height: 20px; background: var(--line); }
.studio-bar__title { display: flex; flex-direction: column; gap: 0; min-width: 0; }
.studio-bar__title strong {
  font-size: var(--t-md); line-height: 1.15;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.studio-bar__title .small { font-size: var(--t-2xs); line-height: 1.25; }
/* The home link in the studio bar. Padded to 24px because that is the floor
   for something you press (WCAG 2.2 target size, minimum) — trimming the bar
   took it to 23, which is the kind of pixel that only shows up when measured. */
.brand-sm {
  font-size: var(--t-md);
  display: inline-flex; align-items: center; gap: 6px;
  padding: 3px 4px; margin: -3px -4px; border-radius: var(--r-sm);
}
.brand-sm:hover { background: var(--surface-2); }

.pubstate { font-size: var(--t-xs); color: var(--ink-3); }
.pubstate[data-published="1"][data-draft="0"] { color: var(--good); }
.pubstate[data-draft="1"] { color: var(--warm); }
.is-hidden { display: none !important; }

.studio { display: grid; grid-template-columns: 1fr; height: 100%; min-height: 0; }
.studio .studio-stage { display: none; }
.studio.has-space { grid-template-columns: minmax(340px, 30%) 1fr; }
.studio.has-space .studio-stage { display: flex; }

.studio-chat {
  display: flex; flex-direction: column; min-height: 0; min-width: 0;
  border-right: 1px solid var(--line); background: var(--paper);
}
.studio:not(.has-space) .studio-chat { border-right: 0; }
.studio:not(.has-space) .thread,
.studio:not(.has-space) .composer,
.studio:not(.has-space) .composer-rail,
.studio:not(.has-space) .hints,
.studio:not(.has-space) .studio-opener { max-width: 720px; margin-inline: auto; width: 100%; }
.studio:not(.has-space) .studio-opener { padding: 48px 24px 4px; }
.studio-opener h1 { margin: 0 0 8px; }

.thread { flex: 1; min-height: 0; overflow-y: auto; padding: 20px 20px 8px; }
.studio.has-space .thread { padding: 16px 16px 8px; }

.msg { margin-bottom: 16px; }
.msg__who { font-size: var(--t-2xs); letter-spacing: .04em; text-transform: uppercase; color: var(--ink-4); margin-bottom: 4px; }
.msg__body { font-size: var(--t-base); line-height: 1.55; }
.msg-you .msg__body {
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--r); padding: 8px 12px; white-space: pre-wrap;
}

/* The result of a build, tied to the steps that produced it.
   A run's rail ends at its last step; this picks the thread back up on the
   same left edge, so what came out reads as the end of what happened rather
   than as loose text underneath it. */
.msg-ai { border-left: 2px solid var(--accent-3); padding-left: 14px; margin-left: 4px; }
.msg-ai.is-superseded { border-left-color: var(--line); }
.run + .msg-ai { margin-top: -8px; }

.msg-bad .msg__body { border-left: 3px solid var(--bad); padding-left: 12px; }
.msg-bad { border-left: 0; padding-left: 0; margin-left: 0; }

/* A run, and the steps inside it.

   The steps used to be a flat append-only list, and it read as one: three rows
   saying "Checking it" because the check ran three times, no line tying any of
   them together, and a body that vanished with `display: none` the instant the
   next step began. It looked bolted on because it was — a log with headings.

   What it is now is a pipeline. Each build is one run; each stage has ONE row
   inside it, in a fixed order, which re-opens and counts its attempts when the
   build comes back to it. A rail connects them so the eye reads a process
   rather than a stack, and the body opens and shuts on a height transition
   instead of blinking.

   The body still holds everything the step produced — the streamed file, the
   checks, the frames of the activity being played — so folding a step puts
   forty kilobytes of HTML away with it. Without that, a finished build is a
   wall of everything that ever happened, and you scroll past the making of the
   thing to reach the thing. */
.run { position: relative; margin: 2px 0 20px; }

/* The rail: one line from the first dot to the last, so a build reads as a
   thread with two ends rather than a stack of separate rows. Behind the dots,
   which mask it with the page ground. */
.run::before {
  content: ""; position: absolute; left: 5px; top: 15px; bottom: 15px;
  width: 1px; background: var(--line);
}
.run[data-single]::before { display: none; }

/* Positioned, so the dots paint over the rail rather than under it. A
   positioned pseudo-element paints above static siblings whatever the source
   order, which drew the line straight through the middle of every dot. */
.step { margin: 0; position: relative; }

/* A grid, not negative margins. The dot used to be pulled left out of its own
   box and was clipped by the thread's scroll container — visibly half a
   circle. Column one is the rail's column; the dot centres in it. */
.step__head {
  display: grid; grid-template-columns: 11px 1fr 12px; gap: 10px; align-items: start;
  width: 100%; padding: 7px 6px 7px 0; margin: 0;
  background: none; border: 0; border-radius: var(--r-sm);
  font: inherit; color: inherit; text-align: left; cursor: pointer;
  transition: background .12s ease;
}
.step__head:hover { background: var(--surface-2); }
.step__head:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }

.step__dot {
  width: 9px; height: 9px; margin: 5px 0 0 1px; border-radius: 50%;
  background: var(--accent);
  box-shadow: 0 0 0 3px var(--paper), 0 0 0 5px var(--accent-2);
  transition: background .2s ease, box-shadow .2s ease, border-color .2s ease;
}
/* Running: filled and breathing. Finished: a quiet ring that stops asking for
   attention but still marks its place on the rail. */
.step:not([data-done]) .step__dot { animation: sp-pulse 1.7s ease-in-out infinite; }
.step[data-done] .step__dot {
  /* --ink-3, not --ink-4: this is a graphic, and 3:1 is the floor for one.
     A hollow ring rather than a filled dot, so a finished step marks its place
     on the rail without competing with the one that is running. */
  background: var(--paper); border: 1.5px solid var(--ink-3);
  box-shadow: 0 0 0 3px var(--paper);
}
@keyframes sp-pulse {
  0%, 100% { box-shadow: 0 0 0 3px var(--paper), 0 0 0 5px var(--accent-2); }
  50%      { box-shadow: 0 0 0 3px var(--paper), 0 0 0 9px transparent; }
}

.step__titles { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 1px; }
.step__label {
  font-size: var(--t-base); font-weight: 600; line-height: 1.35;
  transition: color .18s ease, font-weight .18s ease;
}
.step[data-done] .step__label { font-weight: 500; color: var(--ink-2); }
.step__summary { font-size: var(--t-xs); color: var(--ink-3); line-height: 1.4; }
.step__summary:empty { display: none; }

.step__chev {
  width: 10px; height: 10px; margin-top: 6px; justify-self: end; opacity: .45;
  border-right: 1.5px solid var(--ink-4); border-bottom: 1.5px solid var(--ink-4);
  transform: rotate(45deg) translate(-2px, -2px);
  transition: transform .18s ease, opacity .12s ease;
}
.step__head:hover .step__chev { opacity: 1; }
.step[data-open="0"] .step__chev { transform: rotate(-45deg) translate(-3px, 1px); }

/* Open and shut as a height, not as an on/off.

   `grid-template-rows: 0fr -> 1fr` is the one way to transition to a height
   nobody has measured, and the content here is never a known height: it is
   however much HTML the model streamed. The inner element carries the
   `overflow: hidden` that makes the row cap actually clip. */
.step__fold {
  display: grid; grid-template-rows: 1fr;
  transition: grid-template-rows .22s cubic-bezier(.4, 0, .2, 1);
}
.step[data-open="0"] .step__fold { grid-template-rows: 0fr; }
.step__body {
  min-height: 0; overflow: hidden;
  padding-left: 21px;              /* aligns with the label, past the rail */
  transition: opacity .18s ease;
}
.step[data-open="0"] .step__body { opacity: 0; }
.step__body > * { margin-top: 2px; }
.step__body > :last-child { margin-bottom: 10px; }
.step__note { font-size: var(--t-xs); color: var(--ink-3); line-height: 1.45; margin-bottom: 8px; }

/* What was read, shown inside the step that read it. This replaced a whole
   screen, so it has to carry the one thing that screen was really for: saying
   out loud when a file could not be read. */
.matcard {
  border: 1px solid var(--line); border-radius: var(--r); background: var(--surface);
  padding: 10px 12px; margin: 2px 0 8px; font-size: var(--t-sm);
}
.matcard__files { list-style: none; margin: 0; padding: 0; }
.matcard__files li {
  display: flex; gap: 8px; align-items: baseline; justify-content: space-between;
  padding: 2px 0; min-width: 0;
}
.matcard__name { font-weight: 500; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.matcard__pages { color: var(--ink-3); font-size: var(--t-xs); flex: none; }
.matcard__counts {
  margin-top: 6px; padding-top: 6px; border-top: 1px solid var(--line-2);
  color: var(--ink-3); font-size: var(--t-xs);
}
.matcard__warn {
  margin-top: 8px; padding-left: 10px; border-left: 2px solid var(--warm, #c2721f);
  color: var(--ink-2); font-size: var(--t-xs); line-height: 1.45;
}
.matcard__link {
  display: inline-block; margin-top: 8px;
  font-size: var(--t-xs); color: var(--accent-ink);
}

@media (prefers-reduced-motion: reduce) {
  .step__dot { animation: none !important; }
  .step__chev, .step__fold, .step__body, .step__head { transition: none; }
}

.codewrap {
  border: 1px solid var(--line); border-radius: var(--r);
  background: #12161c; overflow: hidden; margin: 0 0 8px;
}
.codewrap__head {
  display: flex; justify-content: space-between; align-items: baseline; gap: 8px;
  padding: 6px 12px; font-size: var(--t-2xs); letter-spacing: .03em; text-transform: uppercase;
  color: #9fb0c4; background: #1a2029; border-bottom: 1px solid #262e39;
}
.codewrap__count { color: #7f8fa3; }
.code {
  margin: 0; padding: 8px 12px; max-height: 220px; overflow: auto;
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: var(--t-2xs); line-height: 1.5; color: #cfd8e3;
  white-space: pre-wrap; word-break: break-word;
}

/* An edit, shown as the small change it is. A revision that rewrites the whole
   file and a revision that changes four lines look identical as a wall of
   streaming HTML; only one of them is what the teacher asked for. */
.edit {
  border: 1px solid var(--line); border-radius: var(--r);
  background: var(--surface); overflow: hidden; margin: 0 0 8px;
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: var(--t-2xs); line-height: 1.55;
}
.edit__head {
  padding: 6px 8px; font-family: inherit; font-size: var(--t-xs);
  color: var(--ink-2); background: var(--surface-2); border-bottom: 1px solid var(--line-2);
}
.edit__line { display: flex; gap: 8px; padding: 2px 8px; white-space: pre-wrap; word-break: break-word; }
.edit__line--out { background: var(--bad-2); color: #6d1a1a; }
.edit__line--in { background: var(--good-2); color: #12492c; }
.edit__sign { flex: none; opacity: .6; }

/* The activity being played. "We tested it" is a claim; this is the evidence. */
.play { margin: 0 0 8px; }
.play__stage {
  border: 1px solid var(--line); border-radius: var(--r); overflow: hidden;
  background: var(--surface);
}
.play__shot { display: block; width: 100%; height: auto; }
.play__caption { font-size: var(--t-xs); color: var(--ink-2); line-height: 1.45; margin: 6px 2px 6px; }
.play__strip { display: flex; gap: 4px; overflow-x: auto; padding-bottom: 2px; scrollbar-width: thin; }
.play__thumb {
  flex: none; width: 62px; padding: 0; border: 1px solid var(--line); border-radius: 4px;
  background: var(--surface); overflow: hidden; cursor: pointer; line-height: 0;
}
.play__thumb[data-on="1"] { border-color: var(--accent); box-shadow: 0 0 0 2px var(--accent-2); }
.play__thumb:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.play__thumb img { width: 100%; height: auto; display: block; }

.tests { margin: 0 0 16px; }
.tests__head { font-size: var(--t-sm); font-weight: 600; margin-bottom: 6px; }
.tests__list { list-style: none; margin: 0; padding: 0; display: grid; gap: 2px; }
.tests__list li { display: flex; gap: 8px; align-items: baseline; font-size: var(--t-sm); line-height: 1.45; }
.tests__mark { flex: none; width: 14px; font-weight: 700; }
.tests__list li[data-state="pass"] .tests__mark { color: var(--good); }
.tests__list li[data-state="warn"] .tests__mark { color: var(--warm); }
.tests__list li[data-state="fail"] .tests__mark { color: var(--bad); }
.tests__list li[data-state="pass"] .tests__label { color: var(--ink-2); }
.tests__note { color: var(--ink-3); font-size: var(--t-xs); }

.composer {
  flex: none; display: flex; gap: 8px; align-items: flex-end;
  padding: 12px 16px 16px; background: var(--paper); border-top: 1px solid var(--line-2);
}
.studio:not(.has-space) .composer { border-top: 0; padding-bottom: 40px; }
.composer textarea {
  flex: 1; resize: none; min-height: 44px; max-height: 200px; padding: 12px 12px;
  /* The placeholder is the only instruction on this control, so it must not be
     clipped. At the 30% column width a long one wraps past the collapsed
     height and the last words are cut mid-air. */
  text-overflow: ellipsis;
  font: inherit; font-size: var(--t-base); line-height: 1.45;
  border: 1px solid var(--line); border-radius: var(--r); background: var(--surface);
}
.composer textarea:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.composer button { flex: none; height: 44px; }

.studio-stage { flex-direction: column; min-width: 0; min-height: 0; background: var(--surface-2); }
.stage-head {
  flex: none; display: flex; align-items: center; justify-content: space-between; gap: 8px;
  padding: 8px 12px; border-bottom: 1px solid var(--line);
}
.stage-frame { flex: 1; min-height: 0; padding: 12px; }
.stage-frame iframe {
  width: 100%; height: 100%; border: 1px solid var(--line); border-radius: var(--r);
  background: #fff; box-shadow: 0 1px 3px rgba(20,22,26,.06);
}

/* A native <dialog>: showModal() gives focus containment, Escape, the backdrop
   and inertness of the page behind. The hand-rolled version claimed all of
   that via aria-modal and delivered none of it — four tabs walked out of the
   dialog and onto controls the teacher could not see. */
.modal {
  border: 0; padding: 20px; max-height: 100%;
  background: transparent; color: inherit; overflow: visible;

  /* A definite width, and that is the whole fix for a dialog that opened
     403px left of centre.
     
     A <dialog> is centred by the user agent with `margin: auto` against
     `inset-inline: 0`, which only centres the box it is given. Sized by its
     content it took the full viewport width — the long paragraphs inside
     contribute their unwrapped width, and `width: min(520px, 100%)` on the
     card cannot constrain its own parent — so the dialog was centred
     correctly and the card sat at its left edge. Stating the width means the
     box being centred is the box you can see. */
  width: min(560px, calc(100% - 24px));
}
.modal::backdrop { background: rgba(20,22,26,.42); }
.modal:not([open]) { display: none; }
.modal__actions { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.modal__spacer { flex: 1; }
.modal__card {
  width: 100%; background: var(--surface); border-radius: var(--r-lg);
  padding: 20px 20px 16px; box-shadow: 0 16px 48px rgba(20,22,26,.2);
}
.modal__card h2 { margin: 0 0 4px; font-size: var(--t-xl); }
.modal__card h3 { margin: 0 0 2px; }
.modal__card hr { border: 0; border-top: 1px solid var(--line-2); margin: 16px 0 12px; }
.linkrow { display: flex; gap: 8px; margin-top: 12px; }
.linkrow input {
  flex: 1; min-width: 0; padding: 8px 12px; font: inherit; font-size: var(--t-sm);
  border: 1px solid var(--line); border-radius: var(--r-sm); background: var(--surface-2);
}

/* A laptop at 1280 still gets both panels; below that the Space wins the width
   and the conversation goes under it, because a teacher on a small screen is
   there to look at the activity, not at how it was made. */
@media (max-width: 900px) {
  .studio-body, .studio-body main { height: auto; overflow: visible; }
  .studio.has-space { grid-template-columns: 1fr; }
  .studio.has-space .studio-stage { order: -1; height: 62vh; }
  .studio-chat { border-right: 0; }
  .thread { max-height: none; }
}

.mini-select {
  font: inherit; font-size: var(--t-xs); padding: 4px 6px;
  border: 1px solid var(--line); border-radius: var(--r-sm); background: var(--surface);
}
.mini-select:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }

/* Suggestions above the composer. In reach, always legible, and they survive
   the column narrowing — which a placeholder does not. */
.hints { display: flex; flex-wrap: wrap; gap: 6px; padding: 0 16px 8px; }
.hints button {
  font: inherit; font-size: var(--t-xs); line-height: 1.3; padding: 4px 8px;
  color: var(--ink-2); background: var(--surface); cursor: pointer;
  border: 1px solid var(--line); border-radius: 999px;
}
.hints button:hover { border-color: var(--accent-3); background: var(--accent-2); color: var(--accent-ink); }
.hints button:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }

/* A failure the teacher can see, beside the control that failed. */
.header-error {
  font-size: var(--t-xs); line-height: 1.35; color: var(--bad);
  background: var(--bad-2); border: 1px solid #f3cdcd;
  border-radius: var(--r-sm); padding: 4px 8px; max-width: 320px;
}

/* Rotate revokes a link that may already be in front of a class, so it must
   not look like the button beside it that only closes a dialog. The file
   already defines .btn-danger as a quiet outline; this is the loud variant for
   the one control that destroys something. */
.btn-danger--solid { --btn-bg: var(--bad); --btn-fg: #fff; --btn-line: var(--bad); }
.btn-danger--solid:hover { --btn-bg: #8c1c1c; --btn-line: #8c1c1c; }

/* How long this has been running. Elapsed plus the honest typical figure —
   not a progress bar, because we do not know the fraction and a made-up one
   is how a wait stops being believed. */
.composer-rail { flex: none; }
.build-clock {
  margin: 0; padding: 4px 16px 0;
  font-size: var(--t-xs); color: var(--ink-3); font-variant-numeric: tabular-nums;
}
.composer-note {
  flex: none; margin: 0; padding: 0 16px 8px; min-height: 0;
  font-size: var(--t-xs); line-height: 1.4; color: var(--warm);
}
.composer-note:empty { display: none; }
.hints button[disabled] { color: var(--ink-4); border-color: var(--line-2); cursor: default; }
.hints button[disabled]:hover { border-color: var(--line); background: var(--surface); color: var(--ink-2); }

/* Disabled buttons get their own palette, never an opacity multiplier.
   Dimming the whole element dims its label too: the primary button's text fell
   from 7.8:1 to 2.7:1 when disabled, and "Building…" is what the teacher looks
   at for the entire build — the lowest-contrast text on screen at the moment
   they most want to read it. */
.btn[disabled],
.btn:disabled {
  cursor: default;
  opacity: 1;
  box-shadow: none;
}
.btn[disabled]:not(.btn-primary),
.btn:disabled:not(.btn-primary) {
  --btn-fg: var(--ink-3); --btn-bg: var(--surface-2); --btn-line: var(--line);
}
/* White on #3c7570 is 4.7:1 — still reads as "busy", still readable. */
.btn-primary[disabled],
.btn-primary:disabled { --btn-bg: #3c7570; --btn-line: #3c7570; --btn-fg: #fff; }

/* --ink-4 (#9aa2b1) is 2.4:1 on the page ground. It is a decorative fill, not
   a text colour, and it had drifted into four text and graphic roles. */
.msg__who { color: var(--ink-3); }
.step__chev { border-right-color: var(--ink-3); border-bottom-color: var(--ink-3); }
::placeholder { color: var(--ink-3); opacity: 1; }

/* Ghost buttons on the tinted stage panel had no visible hover: their hover
   fill was the panel's own background. */
.stage-head .btn-ghost:hover { background: var(--surface); border-color: var(--line); color: var(--ink); }

/* A result card carries the version it produced and when. Two near-identical
   cards with different item counts is how a teacher comes to believe an older
   build is the activity currently on the right. */
.msg__stamp { font-weight: 400; color: var(--ink-3); letter-spacing: 0; text-transform: none; }
/* A superseded card recedes by weight and colour, not by opacity. Dimming the
   element dims its text too, and axe caught this one at 3.9:1 — the same
   mistake the disabled buttons were making three commits ago. */
.msg-ai.is-superseded .msg__body { color: var(--ink-2); }
.msg-ai.is-superseded .msg__body strong { font-weight: 500; color: var(--ink-2); }


/* Below 900px the two panels stack, so the header must stay put: it holds
   Publish and Share, and it used to scroll away with the page. */
@media (max-width: 900px) {
  .studio-bar { position: sticky; top: 0; z-index: 30; }
}

/* At phone width the bar overflowed by about 68px and Share sat off-screen. */
@media (max-width: 560px) {
  .studio-bar { flex-wrap: wrap; height: auto; min-height: 57px; padding: 8px 12px; gap: 8px; }
  .studio-bar__right { flex: 1 1 100%; justify-content: flex-end; }
}

/* The plan, shown while it is still worth reading — early enough that the
   teacher can press Stop if we misheard them. BAR.md §5 asks for exactly this;
   it was being computed, stored, and never shown. */
.plan {
  border: 1px solid var(--accent-3); border-radius: var(--r);
  background: var(--accent-2); padding: 12px; margin: 0 0 12px;
}
.plan__title { font-weight: 600; font-size: var(--t-base); line-height: 1.35; }
.plan__facts { font-size: var(--t-xs); color: var(--accent-ink); margin-top: 2px; }
.plan__heard {
  display: block; margin-top: 8px; font-size: var(--t-xs); line-height: 1.5; color: var(--ink-2);
}
.plan__heard-label {
  display: block; font-size: var(--t-2xs); text-transform: uppercase; letter-spacing: .04em;
  color: var(--accent-ink); margin-bottom: 2px;
}
.plan__mech {
  margin-top: 8px; font-size: var(--t-xs); color: var(--accent-ink); font-weight: 500;
}
.plan__groups { list-style: none; margin: 8px 0 0; padding: 0; font-size: var(--t-xs); color: var(--ink-2); }
.plan__groups li { padding: 1px 0; }
.plan__assume { margin-top: 6px; font-size: var(--t-xs); color: var(--ink-3); line-height: 1.45; }

.tests__rest { font-size: var(--t-xs); color: var(--ink-3); margin: 0 0 4px; }
.step__warn {
  font-size: var(--t-xs); line-height: 1.45; color: var(--warm);
  background: var(--warm-2); border-radius: var(--r-sm);
  padding: 6px 8px; margin: 0 0 8px;
}

/* The check that outranks the others. BAR.md §0 makes misreading the material
   a total failure regardless of how the activity looks, and the result used to
   go only into a log — the teacher, whose class would have found the invented
   fact, never saw it. */
.fidelity {
  border: 1px solid var(--good); border-left-width: 3px; border-radius: var(--r);
  background: var(--good-2); padding: 8px 12px; margin: 0 0 12px;
}
.fidelity.is-bad { border-color: var(--bad); background: var(--bad-2); }
.fidelity__head { font-size: var(--t-sm); font-weight: 600; color: var(--good); }
.fidelity.is-bad .fidelity__head { color: var(--bad); }
.fidelity__line { font-size: var(--t-xs); color: var(--ink-2); line-height: 1.45; margin-top: 2px; }
.fidelity__problem { font-size: var(--t-xs); color: var(--bad); line-height: 1.45; margin-top: 4px; }

/* A check nobody ran is not a check that passed. It reads as neither. */
.tests__list li[data-state="skipped"] .tests__mark { color: var(--ink-4); }
.tests__list li[data-state="skipped"] .tests__label { color: var(--ink-3); }
