/* Level 1: Vagueness has a cost
   Three surfaces, three treatments: the brief is a paper job ticket, the output is devices
   on a steel bench, and the builder is a dark composing rail that type tiles slot into.
   Colours come from the Okabe-Ito colourblind-safe set; no state relies on hue alone. */

@font-face {
  font-family: "Atkinson Hyperlegible Next";
  src: url("../fonts/AtkinsonHyperlegibleNext-latin.woff2") format("woff2");
  font-weight: 200 800;
  font-style: normal;
  font-display: swap;
}

:root {
  color-scheme: light;

  --ground: #dce2e7;
  --panel: #f6f8f9;
  --line: #b7c1cb;
  --ink: #18222d;
  --ink-soft: #4a5866;

  --rail: #26313c;
  --trough: #1b242d;
  --trough-edge: #5b6b7c;
  --rail-ink: #c9d3dc;

  --tile: #ffffff;
  --tile-edge: #8795a3;

  --ticket: #fff3b0;
  --ticket-line: #d8c35a;

  --accent: #0072b2; /* Okabe-Ito blue: picked up, focus */
  --accent-wash: #e1eef7;
  --go: #f0e442; /* Okabe-Ito yellow: Run, drop targets on the dark rail */
  --go-edge: #b8ae1e;
  --met: #0072b2; /* Okabe-Ito blue: met, on the reveal; always with a glyph, a word and a solid edge */
  --unmet: #d55e00; /* Okabe-Ito vermillion: not met, or behind a miss; always with a glyph and a dashed edge */
  --unmet-ink: #b34700; /* the same, darkened for marks on the light ground and the ticket (3:1 and 4.5:1) */

  --focus: var(--accent);
  --font: "Atkinson Hyperlegible Next", "Atkinson Hyperlegible", system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  --gutter: 16px;
  --tile-radius: 7px;
}

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

html {
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
  /* The frames are sized from the pane width; a scrollbar that comes and goes would make them flicker. */
  scrollbar-gutter: stable;
  /* main.js measures whichever bar is pinned, so focus never scrolls in underneath it. */
  scroll-padding-bottom: calc(var(--pinned-bottom, 0px) + 20px);
}

body {
  margin: 0;
  background: var(--ground);
  color: var(--ink);
  font: 400 1rem/1.45 var(--font);
  -webkit-font-smoothing: antialiased;
}

h1,
h2,
p,
ul,
figure {
  margin: 0;
}

button {
  font: inherit;
  color: inherit;
}

:focus-visible {
  outline: 3px solid var(--focus);
  outline-offset: 3px;
}

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

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

.app {
  max-width: 1440px;
  margin: 0 auto;
  padding: var(--gutter);
  display: grid;
  gap: 16px;
  grid-template-columns: minmax(0, 1fr);
  grid-template-areas:
    "head"
    "brief"
    "output"
    "builder";
}

.masthead {
  grid-area: head;
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  column-gap: 14px;
  row-gap: 2px;
}

.level-number {
  font-size: 0.9375rem;
  font-weight: 700;
  color: var(--ink-soft);
}

.level-title {
  font-size: 1.5rem;
  font-weight: 800;
  line-height: 1.1;
  letter-spacing: -0.01em;
}

.pane {
  min-width: 0;
}

.pane--brief {
  grid-area: brief;
}

.pane--output {
  grid-area: output;
}

.pane--builder {
  grid-area: builder;
}

.pane-heading {
  margin-bottom: 10px;
  font-size: 0.9375rem;
  font-weight: 700;
  color: var(--ink-soft);
}

@media (min-width: 1000px) {
  .app {
    --gutter: 24px;
    gap: 20px 24px;
  }

  .level-title {
    font-size: 1.75rem;
  }
}

/* Wide: the brief owns the left column and stays pinned beside both the output and the
   builder, so it is in view while composing and while checking. It starts at 1360, not 1280:
   below that the column would leave the builder's rail so narrow that chips wrap word by word,
   and a 1280 laptop is better served by the full-width builder. */
@media (min-width: 1360px) {
  .app {
    grid-template-columns: 270px minmax(0, 1fr);
    grid-template-areas:
      "head head"
      "brief output"
      "brief builder";
    align-items: start;
  }
}

@media (min-width: 1440px) {
  .app {
    grid-template-columns: 300px minmax(0, 1fr);
  }
}

/* ---------------------------------------------------------------- client brief: a paper job ticket */

.pane--brief {
  filter: drop-shadow(0 6px 10px rgba(24, 34, 45, 0.18));
}

@media (min-width: 1360px) {
  .pane--brief {
    position: sticky;
    top: var(--gutter);
  }

  /* Taller than the window (a short one, after the reveal), it moves with the page (main.js). */
  .pane--brief.is-too-tall {
    position: static;
  }
}

.ticket {
  position: relative;
  padding: 20px 18px 14px;
  background: var(--ticket);
  border-radius: 0 0 3px 3px;
  /* perforated top edge, torn off a pad */
  -webkit-mask: radial-gradient(circle at 8px 0, transparent 4.5px, #000 5px) 0 0 / 16px 100% repeat-x;
  mask: radial-gradient(circle at 8px 0, transparent 4.5px, #000 5px) 0 0 / 16px 100% repeat-x;
}

.ticket-heading {
  margin-bottom: 8px;
}

.ticket-client {
  font-size: 1.25rem;
  font-weight: 800;
  line-height: 1.2;
}

.ticket-job {
  margin-top: 2px;
  color: var(--ink-soft);
}

.reqs {
  margin-top: 14px;
  padding: 0;
  list-style: none;
}

.req {
  padding: 10px 0;
  border-top: 1px solid var(--ticket-line);
  font-weight: 600;
  line-height: 1.3;
}

/* After a Run each item becomes a tick box; after Check it shows the truth beside the call. */
.pane--brief [data-state="plain"] .req-box,
.pane--brief [data-state="plain"] .brief-footer {
  display: none;
}

.req-label {
  display: flex;
  align-items: flex-start;
  gap: 10px;
}

.pane--brief [data-state="marking"] .req-label {
  cursor: pointer;
}

.req-box {
  flex: none;
  display: grid;
  place-content: center;
  width: 22px;
  height: 22px;
  margin: 0;
  background: #ffffff;
  border: 2px solid var(--ink);
  border-radius: 5px;
  -webkit-appearance: none;
  appearance: none;
}

.req-box::before {
  content: "✓";
  visibility: hidden;
  color: #ffffff;
  font-size: 15px;
  font-weight: 800;
  line-height: 1;
}

.req-box:checked {
  background: var(--ink);
}

.req-box:checked::before {
  visibility: visible;
}

.pane--brief [data-state="waiting"] .req-box {
  opacity: 0.45;
}

.req-outcome {
  margin-top: 4px;
  font-size: 0.875rem;
}

.req-outcome:empty,
.req-seen:empty {
  display: none;
}

.req-glyph {
  font-weight: 800;
}

.req.is-met .req-glyph {
  color: var(--met);
}

.req.is-unmet .req-glyph {
  color: var(--unmet-ink);
}

.req-call {
  margin-left: 4px;
  font-weight: 800;
}

/* The lesson's moment: ticked, but not met. */
.req[data-call="missed"] .req-call {
  /* Longhands: Safari before 26.2 drops a shorthand that includes the thickness. */
  text-decoration-line: underline;
  text-decoration-thickness: 3px;
  text-decoration-color: var(--unmet-ink);
  text-underline-offset: 3px;
}

.req-seen {
  color: var(--ink-soft);
  font-size: 0.875rem;
  font-weight: 400;
}

.req.is-met,
.req.is-unmet {
  padding-left: 10px;
  border-left: 4px solid var(--met);
}

.req.is-unmet {
  border-left-style: dashed;
  border-left-color: var(--unmet);
}

.brief-footer {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 8px 12px;
  margin-top: 8px;
  padding-top: 10px;
  border-top: 1px solid var(--ticket-line);
}

.brief-say {
  font-weight: 700;
  line-height: 1.25;
}

/* Approved: stamped on the ticket in the met colour. */
.brief-say.is-approved {
  padding: 2px 10px;
  border: 3px solid var(--met);
  border-radius: 6px;
  color: var(--met);
  font-size: 1.25rem;
  font-weight: 800;
  transform: rotate(-4deg);
}

.brief-action {
  min-height: 44px;
  padding: 0 18px;
  background: var(--ink);
  border: 0;
  border-radius: 9px;
  color: #ffffff;
  font-weight: 800;
  cursor: pointer;
}

.brief-action[aria-disabled="true"] {
  background: transparent;
  box-shadow: inset 0 0 0 2px var(--ink-soft);
  color: var(--ink-soft);
  cursor: not-allowed;
}

/* On one column the brief becomes a compact strip pinned to the top, so it stays in view.
   (On very short screens, e.g. phone landscape at high zoom, it scrolls rather than fill them.) */
@media (max-width: 1359.98px) {
  html {
    scroll-padding-top: calc(var(--pinned-top, 0px) + 8px);
  }

  .pane--brief {
    z-index: 20;
    margin-inline: calc(var(--gutter) * -1);
    padding: 0 var(--gutter) 6px;
    background: var(--ground);
  }

  .ticket {
    padding: 14px 14px 8px;
  }

  .ticket-heading {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    overflow: hidden;
    clip: rect(0 0 0 0);
    white-space: nowrap;
  }

  .ticket-client {
    font-size: 1rem;
  }

  /* Once there is a build to check, the strip keeps its room for the checklist. */
  .pane--brief [data-state]:not([data-state="plain"]) .ticket-client,
  .pane--brief [data-state]:not([data-state="plain"]) .ticket-job {
    display: none;
  }

  .reqs {
    margin-top: 4px;
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    column-gap: 14px;
  }

  .req {
    padding: 6px 0;
    font-size: 0.9375rem;
    line-height: 1.25;
  }

  .req-outcome,
  .req-seen {
    font-size: 0.8125rem;
    line-height: 1.2;
  }

  .req-outcome {
    margin-top: 2px;
  }

  .brief-footer {
    margin-top: 4px;
    padding-top: 6px;
  }

  /* Until something is built, empty frames would only push the builder off screen. */
  .app[data-built="false"] .frame {
    display: none;
  }

  .app[data-built="false"] .assistant {
    margin-top: 0;
  }
}

@media (max-width: 1359.98px) and (min-height: 300px) {
  .pane--brief {
    position: sticky;
    top: 0;
  }

  /* Pinned, a brief that has grown past half the screen (a small phone, after the reveal)
     would leave too little to scroll through, so it moves with the page (main.js). */
  .pane--brief.is-too-tall {
    position: static;
  }
}

@media (min-width: 720px) and (max-width: 1359.98px) {
  .reqs {
    grid-template-columns: repeat(4, minmax(0, 1fr));
  }
}

/* ---------------------------------------------------------------- build output: devices on the bench */

.frames {
  display: flex;
  align-items: flex-start;
  column-gap: 20px; /* renderer.js GAP */
  row-gap: 16px;
}

/* Stacked (narrow screens): the phone comes first, then what the AI said about it, then the
   desktop. So the player reads the report straight after watching the build. */
.output-body[data-layout="stack"] {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 16px;
}

.output-body[data-layout="stack"] .frames {
  display: contents;
}

/* Too narrow to share a line: the words go above the bar, so the bar never changes width. */
.output-body[data-layout="stack"] .build-status {
  align-self: stretch;
  grid-template-columns: minmax(0, 1fr);
  gap: 6px;
  margin-bottom: 0;
}

.output-body[data-layout="stack"] .frame--phone {
  order: 1;
}

.output-body[data-layout="stack"] .assistant {
  order: 2;
  align-self: stretch;
  margin-top: 0;
}

.output-body[data-layout="stack"] .frame--desktop {
  order: 3;
}

.frame {
  --s: 0.5;
  --fw: 0;
  --fh: 0;
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.frame-box {
  width: calc(var(--fw) * var(--s) * 1px);
  height: calc(var(--fh) * var(--s) * 1px);
}

.frame-scaler {
  width: calc(var(--fw) * 1px);
  height: calc(var(--fh) * 1px);
  transform: scale(var(--s));
  transform-origin: 0 0;
}

.frame-caption {
  font-size: 0.875rem;
  font-weight: 600;
  color: var(--ink-soft);
}

.frame--desktop .frame-scaler {
  overflow: hidden;
  background: #fff;
  border-radius: 10px;
  box-shadow: 0 0 0 1px var(--line), 0 22px 36px -22px rgba(24, 34, 45, 0.55);
}

.browser-bar {
  display: flex;
  align-items: center;
  height: 36px;
  padding: 0 16px;
  background: #e7ebef;
  border-bottom: 1px solid #d3dae0;
}

.browser-address {
  flex: 0 1 460px;
  margin: 0 auto;
  padding: 3px 16px;
  overflow: hidden;
  background: #fff;
  border-radius: 999px;
  color: #5c6975;
  font-size: 13px;
  text-align: center;
  white-space: nowrap;
  text-overflow: ellipsis;
}

.phone-body {
  --focus: var(--go); /* the screen's focus ring sits on the dark bezel */
  padding: 10px; /* renderer.js DEVICES.phone.bezel */
  background: var(--rail);
  border-radius: 36px;
  box-shadow: 0 22px 36px -22px rgba(24, 34, 45, 0.6);
}

/* Each viewport is a size container, so the built page's own responsive rules answer to the
   device's width, not the window's. */
.viewport {
  position: relative;
  overflow: hidden;
  background: #fff;
  container-type: inline-size;
}

/* A page wider than the phone scrolls sideways, as in a phone browser. Its own scroll bar is
   hidden and drawn instead by .phone-cue, so it looks the same everywhere and always shows. */
.viewport--phone {
  overflow-x: auto;
  overflow-y: hidden;
  overscroll-behavior-x: contain;
  scrollbar-width: none;
  border-radius: 26px;
}

/* The frame is scaled down, and its focus ring with it: undo the scale so the ring stays 3px,
   drawn inside the bezel. */
.viewport--phone:focus-visible {
  outline-width: calc(3px / var(--s, 1));
  outline-offset: calc(2px / var(--s, 1));
}

.viewport--phone::-webkit-scrollbar {
  display: none;
}

.phone-body {
  position: relative;
}

.phone-cue {
  display: none;
  position: absolute;
  inset: 10px; /* renderer.js DEVICES.phone.bezel */
  overflow: hidden;
  border-radius: 26px;
  pointer-events: none;
}

.frame--phone.is-wider .phone-cue {
  display: block;
}

/* A shadow on the cut edge, gone once scrolled to the end. */
.phone-cue::before,
.phone-cue::after {
  content: "";
  position: absolute;
}

.phone-cue::before {
  top: 0;
  right: 0;
  bottom: 0;
  width: 26px;
  background: linear-gradient(to right, rgba(24, 34, 45, 0), rgba(24, 34, 45, 0.3));
  opacity: calc((1 - var(--at, 0) - var(--seen, 1)) * 30);
}

/* The scroll bar: a faint track, and a thumb as wide as the share of the page in view. */
.phone-cue::after {
  right: 22px;
  bottom: 6px;
  left: 22px;
  height: 4px;
  background: linear-gradient(to right,
    rgba(24, 34, 45, 0.14) calc(var(--at, 0) * 100%),
    rgba(24, 34, 45, 0.55) 0 calc((var(--at, 0) + var(--seen, 1)) * 100%),
    rgba(24, 34, 45, 0.14) 0);
  border-radius: 2px;
}

/* Stacked, the foot of the phone screen can sit below the fold, so the bar rides just under the
   part a page can reach (renderer.js DEVICES.phone.page of .height). */
.output-body[data-layout="stack"] .phone-cue::after {
  bottom: calc(100% * (640 - 560) / 640 + 6px);
}

.viewport-empty {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  color: var(--ink-soft);
  font-size: 18px;
}

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

.build-status {
  display: grid;
  grid-template-columns: minmax(0, 17em) minmax(60px, 1fr);
  align-items: center;
  gap: 12px;
  margin-bottom: 14px;
}

.app[data-built="false"] .build-status {
  display: none;
}

.build-step {
  overflow: hidden;
  color: var(--ink-soft);
  font-size: 0.9375rem;
  font-weight: 600;
  white-space: nowrap;
  text-overflow: ellipsis;
}

.build-track {
  height: 8px;
  overflow: hidden;
  background: #ffffff;
  border-radius: 999px;
  box-shadow: inset 0 0 0 1px #6b7a89;
}

.build-fill {
  display: block;
  height: 100%;
  background: var(--accent);
  transform: scaleX(var(--p, 0));
  transform-origin: 0 50%;
  transition: transform 400ms linear;
}

/* ---------------------------------------------------------------- the exchange: your prompt, the AI's report */

.assistant {
  display: grid;
  gap: 14px;
  max-width: 70ch;
  margin-top: 18px;
}

.who {
  font-size: 0.9375rem;
  font-weight: 700;
}

.exchange--you .said {
  font-weight: 600;
}

.said-idle {
  color: var(--ink-soft);
}

.exchange--ai .said,
.said-reserve {
  display: grid;
  gap: 6px;
}

.exchange--ai .said {
  position: relative;
}

.said-reserve {
  visibility: hidden;
}

.said-building {
  position: absolute;
  top: 0;
  left: 0;
}

.report {
  display: grid;
  gap: 4px;
  margin: 0;
  padding-left: 1.25em;
}

/* A report line behind a miss: where the AI's own default stood. */
.report-line.is-blamed {
  position: relative;
  list-style: none;
  border-radius: 4px;
  outline: 2px dashed var(--unmet-ink);
  outline-offset: 2px;
}

.report-line.is-blamed::before {
  content: "✗";
  content: "✗" / "";
  position: absolute;
  left: -1.25em;
  color: var(--unmet-ink);
  font-weight: 800;
}

/* ---------------------------------------------------------------- the page the AI built
   Drawn inside a device viewport, in the device's CSS pixels; the frame scales the lot.
   The page is the client's website, not the game, so it uses the system font. */

.site {
  min-height: 100%;
  background: #ffffff;
  color: #1c2b22;
  font: 400 15px/1.4 system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
}

/* The build reveals each part in turn: a short fade and rise, nothing that flashes. */
.site .part {
  transition: opacity 280ms ease-out, transform 280ms ease-out, visibility 280ms;
}

.site.is-building .part:not(.is-in) {
  visibility: hidden;
  opacity: 0;
  transform: translateY(10px);
}

.site-header {
  background: #245a3c;
  color: #ffffff;
}

.site-bar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px 22px;
  padding: 14px 24px;
}

.site-logo {
  margin-right: auto;
  font-size: 17px;
  font-weight: 700;
  white-space: nowrap;
}

.site-nav {
  display: flex;
  gap: 18px;
  font-size: 14px;
  white-space: nowrap;
}

.site-menu {
  display: none;
  padding: 3px 10px;
  border: 1px solid currentColor;
  border-radius: 6px;
  font-size: 13px;
  font-weight: 600;
}

.site-main {
  padding: 22px 24px 24px;
}

.form-heading {
  margin-bottom: 14px;
  font-size: 26px;
  font-weight: 700;
  line-height: 1.15;
}

.form-note {
  margin: -6px 0 12px;
  color: #56625b;
  font-size: 13px;
}

.form-fields {
  display: grid;
  gap: 12px 20px;
}

.field {
  display: grid;
  gap: 4px;
  min-width: 0;
}

.field-label {
  font-size: 15px;
  font-weight: 600;
}

.field-box {
  display: block;
  height: 38px;
  background: #ffffff;
  border: 1px solid #a9b4ae;
  border-radius: 4px;
}

.field--multiline .field-box {
  height: 72px;
}

.form-actions {
  display: flex;
  margin-top: 18px;
}

.form-button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 42px;
  padding: 0 22px;
  background: #245a3c;
  color: #ffffff;
  border-radius: 4px;
  font-weight: 700;
  white-space: nowrap;
}

/* The fixed layouts are as wide as their content, so a phone scrolled sideways still sees
   the header and page background behind it. */
.layout--grid-fixed,
.layout--hero-split {
  width: max-content;
  min-width: 100%;
}

/* Layout: the AI's default. A fixed 640px page with the fields in two columns and the
   button on the right. Fine on a desktop; on a phone it runs off the right-hand edge,
   which cuts through the second column. */
.layout--grid-fixed .site-bar,
.layout--grid-fixed .site-main {
  width: 640px;
  margin-inline: auto;
}

.layout--grid-fixed .form-fields {
  grid-template-columns: 1fr 1fr;
}

.layout--grid-fixed .field--multiline {
  grid-column: 1 / -1;
}

.layout--grid-fixed .form-actions {
  justify-content: flex-end;
}

/* Layout: "make it nice", as the AI reads it. A fixed 840px split with a big picture
   on the left. On a phone the picture fills the screen and the form is cut off beside it. */
.layout--hero-split .site-bar,
.layout--hero-split .site-main {
  width: 840px;
  margin-inline: auto;
}

.layout--hero-split .site-main {
  display: grid;
  grid-template-columns: 250px minmax(0, 1fr);
  gap: 32px;
  align-items: start;
}

.hero {
  position: relative;
  height: 400px;
  overflow: hidden;
  border-radius: 22px;
  background:
    radial-gradient(140% 70% at 15% 105%, #4d9a5f 0 50%, transparent 50.5%),
    radial-gradient(120% 60% at 95% 110%, #2f7448 0 50%, transparent 50.5%),
    linear-gradient(180deg, #7fb8f0 0%, #d6ebff 75%);
}

.hero-tagline {
  position: absolute;
  right: 22px;
  bottom: 22px;
  left: 22px;
  color: #ffffff;
  font-size: 26px;
  font-weight: 800;
  line-height: 1.1;
  text-shadow: 0 2px 10px rgba(0, 0, 0, 0.35);
}

/* Layout: "keep it simple". One narrow column, which happens to fit a phone. */
.layout--stacked .site-main {
  max-width: 480px;
}

/* Layout: "mobile friendly". Two columns when there is room, one when there is not. */
.layout--fluid .site-main {
  max-width: 760px;
  margin-inline: auto;
}

@container (min-width: 600px) {
  .layout--fluid .form-fields {
    grid-template-columns: 1fr 1fr;
  }

  .layout--fluid .field--multiline {
    grid-column: 1 / -1;
  }

  .layout--fluid .form-actions {
    justify-content: flex-end;
  }
}

@container (max-width: 599.98px) {
  .layout--fluid .site-nav {
    display: none;
  }

  .layout--fluid .site-menu {
    display: block;
  }

  .layout--fluid .form-button {
    flex: 1;
  }
}

/* Theme: "make it nice", as the AI reads it. Gradients, pills and soft shadows. */
.theme--fancy {
  background: linear-gradient(150deg, #f4efff 0%, #e8f7f3 100%);
  color: #2a1d5c;
  font-family: "Trebuchet MS", "Segoe UI", system-ui, sans-serif;
}

.theme--fancy .site-header {
  background: linear-gradient(90deg, #5b3cc4, #b8408a);
}

.theme--fancy .site-form {
  padding: 18px;
  background: rgba(255, 255, 255, 0.78);
  border-radius: 22px;
  box-shadow: 0 18px 40px -22px rgba(67, 36, 159, 0.55);
}

.theme--fancy .form-heading {
  font-size: 30px;
  font-weight: 800;
  letter-spacing: -0.02em;
  color: #43249f;
}

.theme--fancy .field-box {
  border-color: #d9cdfa;
  border-radius: 12px;
  box-shadow: inset 0 1px 3px rgba(67, 36, 159, 0.12);
}

.theme--fancy .form-button {
  min-height: 46px;
  padding: 0 28px;
  background: linear-gradient(90deg, #5b3cc4, #b8408a);
  border-radius: 999px;
  box-shadow: 0 10px 22px -10px rgba(184, 64, 138, 0.9);
}

/* Theme: "keep it simple", as the AI reads it. More or less the browser's own defaults. */
.theme--bare {
  color: #000000;
  font: 400 16px/1.25 "Times New Roman", Times, serif;
}

.theme--bare .site-header {
  background: none;
  color: #000000;
}

.theme--bare .site-bar {
  gap: 4px 12px;
  padding: 8px;
}

.theme--bare .site-logo {
  font-size: 18px;
}

.theme--bare .site-link {
  color: #0000ee;
  text-decoration: underline;
}

.theme--bare .site-menu {
  border-radius: 0;
}

.theme--bare .site-main {
  padding: 8px;
}

.theme--bare .form-heading {
  margin: 8px 0 12px;
  font-size: 24px;
}

.theme--bare .field {
  max-width: 260px;
}

.theme--bare .field-label {
  font-weight: 400;
}

.theme--bare .field-box {
  height: 24px;
  border-color: #767676;
  border-radius: 2px;
}

/* No level-1 prompt reaches this ("keep it simple" drops the long fields), but any legal build must draw right. */
.theme--bare .field--multiline .field-box {
  height: 56px;
}

.theme--bare .form-actions {
  margin-top: 12px;
}

.theme--bare .form-button {
  min-height: 0;
  padding: 2px 8px;
  background: #efefef;
  color: #000000;
  border: 1px solid #767676;
  border-radius: 3px;
  font: 400 15px/1.3 system-ui, sans-serif;
}

/* ---------------------------------------------------------------- prompt builder: the composing rail */

.pane--builder {
  padding: 16px;
  background: var(--panel);
  border-radius: 14px;
  container-type: inline-size;
}

.slots {
  display: flex;
  flex-direction: column;
  gap: 14px;
}

.slot {
  min-width: 0;
}

.slot-rail {
  --focus: var(--go);
  padding: 10px 10px 8px;
  background: var(--rail);
  border-radius: 12px;
}

.sockets {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}

.socket {
  position: relative;
  flex: 1 1 110px;
  min-width: 0;
  min-height: 62px;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  align-content: center;
  gap: 4px;
  padding: 6px;
  background: var(--trough);
  border: 2px dashed var(--trough-edge);
  border-radius: 10px;
}

.socket.is-filled {
  border-style: solid;
  border-color: var(--trough);
}

.socket.is-target,
.socket.is-droppable {
  border-style: dashed;
  border-color: var(--go);
}

.socket.is-drop-hover {
  border-style: solid;
  background: #34424f;
}

.socket.is-target {
  cursor: pointer;
}

/* A full socket that can take the picked-up chip says so; screen readers get "Press to swap in …" instead.
   The label sits on the socket's top edge, out of the flow, so a chip that fills the socket
   cannot push it onto a line of its own and make the rail grow under the pointer. */
.socket.is-filled.is-target::after,
.socket.is-filled.is-droppable::after {
  content: "Swap here";
  content: "Swap here" / "";
  position: absolute;
  top: -0.8em;
  right: 6px;
  padding: 0 6px;
  background: var(--rail);
  border-radius: 6px;
  color: var(--go);
  font-size: 0.8125rem;
  font-weight: 700;
  line-height: 1.4;
}

.socket-empty {
  flex: 1;
  align-self: stretch;
  min-height: 44px;
  padding: 0 8px;
  background: none;
  border: 0;
  border-radius: 6px;
  color: var(--go);
  text-align: left;
  cursor: default;
}

.socket.is-target .socket-empty {
  cursor: pointer;
}

.socket-hint {
  visibility: hidden;
  font-weight: 700;
}

.socket.is-target .socket-hint,
.socket.is-droppable .socket-hint {
  visibility: visible;
}

.socket > .chip {
  max-width: 100%;
}

.slot-name {
  display: block;
  margin-top: 6px;
  padding-left: 4px;
  color: var(--rail-ink);
  font-size: 0.875rem;
}

.tray {
  display: flex;
  flex-wrap: wrap;
  align-content: flex-start;
  gap: 8px;
  padding: 12px 2px 2px;
}

/* A chip's home in the tray keeps its shape while the chip is out, so nothing reflows. */
.tray-home {
  display: inline-grid;
  max-width: 100%;
  border-radius: var(--tile-radius);
}

.tray-home > * {
  grid-area: 1 / 1;
}

.tray-home.is-empty {
  outline: 2px dashed var(--line);
  outline-offset: -2px;
}

/* ---------------------------------------------------------------- tiles */

.tile {
  display: block;
  min-height: 44px;
  max-width: 100%;
  padding: 10px 14px 8px;
  border: 1px solid var(--tile-edge);
  border-bottom-width: 4px; /* the base of a type slug: reads as a physical tile */
  border-radius: var(--tile-radius);
  font: 700 1rem/1.25 var(--font);
  text-align: left;
}

.tile--ghost {
  visibility: hidden;
}

.chip {
  background: var(--tile);
  color: var(--ink);
  cursor: grab;
  -webkit-user-select: none;
  user-select: none;
  -webkit-touch-callout: none;
  -webkit-tap-highlight-color: transparent;
  touch-action: manipulation;
  transition: transform 120ms ease, box-shadow 120ms ease;
}

.chip:hover {
  border-color: var(--ink-soft);
}

.chip[aria-pressed="true"] {
  background: var(--accent-wash);
  border-color: var(--accent);
  box-shadow: 0 0 0 2px var(--accent);
  transform: translateY(-3px);
}

.chip.is-lifted {
  opacity: 0.35;
}

.chip--ghost {
  position: fixed;
  top: 0;
  left: 0;
  z-index: 1000;
  margin: 0;
  pointer-events: none;
  box-shadow: 0 16px 26px -10px rgba(24, 34, 45, 0.5);
  transition: none;
}

.is-dragging-chip,
.is-dragging-chip * {
  cursor: grabbing !important;
  -webkit-user-select: none;
  user-select: none;
}

/* Locked (mid-build): the tray chips stay readable but plainly out of reach. The placed
   chips keep full strength, since they are the prompt being built. */
.is-locked .chip {
  cursor: default;
}

/* A chip behind a miss, after a check: a dashed ring and a cross, until it moves or the next build. */
.chip.is-blamed {
  position: relative;
}

.chip.is-blamed::after {
  content: "";
  position: absolute;
  inset: -6px;
  border: 2px dashed var(--unmet);
  border-radius: 11px;
  pointer-events: none;
}

.chip.is-blamed::before {
  content: "✗";
  content: "✗" / "";
  position: absolute;
  top: -12px;
  right: -10px;
  display: grid;
  place-items: center;
  width: 20px;
  height: 20px;
  background: var(--unmet);
  border-radius: 50%;
  color: #ffffff;
  font-size: 12px;
  font-weight: 800;
  line-height: 1;
}

.is-locked .chip:hover {
  border-color: var(--tile-edge);
}

.is-locked .tray .chip {
  opacity: 0.5;
}

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

.run-area {
  --focus: var(--go);
  z-index: 5;
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 12px;
  padding: 10px;
  background: var(--rail);
  border-radius: 12px;
  box-shadow: 0 10px 22px -12px rgba(24, 34, 45, 0.6);
}

.run-hint {
  margin-right: auto;
  color: var(--rail-ink);
  font-size: 0.9375rem;
  line-height: 1.3;
}

.run-hint:empty {
  display: none;
}

.run {
  min-width: 120px;
  min-height: 52px;
  padding: 0 26px;
  background: var(--go);
  border: 0;
  border-bottom: 4px solid var(--go-edge);
  border-radius: 9px;
  color: var(--ink);
  font-size: 1.125rem;
  font-weight: 800;
  cursor: pointer;
}

.run[aria-disabled="true"] {
  background: #3a4754;
  border-bottom-color: #2e3945;
  color: #aab6c1;
  cursor: not-allowed;
}

/* Stacked builder: Run rides along the bottom of the screen while you scroll the slots,
   when the screen is tall enough to spare the room. */
@media (min-height: 540px) {
  .run-area {
    position: sticky;
    bottom: 12px;
  }
}

/* Wide builder: one continuous rail reads left to right like the sentence it builds,
   and each slot's tiles wait in a column directly beneath their socket. */
@container (min-width: 860px) {
  .slots {
    display: grid;
    grid-template-columns:
      minmax(0, 0.7fr) minmax(0, 1fr) minmax(0, 1.2fr) minmax(0, 2.3fr) max(8rem, 112px);
    grid-template-rows: auto auto;
    column-gap: 10px;
    row-gap: 0;
    padding-inline: 12px;
    isolation: isolate;
  }

  .slots::before {
    content: "";
    grid-column: 1 / -1;
    grid-row: 1;
    margin-inline: -12px;
    background: var(--rail);
    border-radius: 12px;
    z-index: -1;
  }

  .slot {
    display: grid;
    grid-row: 1 / span 2;
    grid-template-rows: subgrid;
  }

  .slot:nth-child(1) { grid-column: 1; }
  .slot:nth-child(2) { grid-column: 2; }
  .slot:nth-child(3) { grid-column: 3; }
  .slot:nth-child(4) { grid-column: 4; }

  .slot-rail {
    padding: 12px 0 10px;
    background: none;
    border-radius: 0;
  }

  .tray {
    padding-top: 14px;
  }

  .run-area {
    grid-column: 5;
    grid-row: 1;
    position: static;
    flex-direction: column;
    align-items: stretch;
    justify-content: center;
    gap: 6px;
    padding: 12px 0 10px 4px;
    background: none;
    box-shadow: none;
  }

  .run-hint {
    order: 2;
    max-width: 10em;
    min-height: 2.6em; /* two lines kept for it ("Add an action and a thing to run"), so Run stays put */
    margin: 0;
    font-size: 0.875rem;
  }

  .run-hint:empty {
    display: block;
  }
}

@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    transition-duration: 0.01ms !important;
    animation-duration: 0.01ms !important;
  }
}

/* ---------------------------------------------------------------- Windows Contrast mode
   Forced colours drop backgrounds and shadows, which is how the frames, the progress bar, Run
   and the picked-up chip are drawn: give each an edge in system colours instead. The built
   page is the client's website, the thing being judged, so it keeps its own colours. */

@media (forced-colors: active) {
  .viewport,
  .phone-cue {
    forced-color-adjust: none;
  }

  .frame--desktop .frame-scaler,
  .phone-body {
    border: 1px solid CanvasText;
  }

  .build-track {
    border: 1px solid CanvasText;
  }

  .build-fill {
    background: Highlight;
    forced-color-adjust: none;
  }

  .run,
  .brief-action {
    border: 2px solid ButtonText;
  }

  .run[aria-disabled="true"],
  .brief-action[aria-disabled="true"] {
    border-color: GrayText;
    color: GrayText;
  }

  .chip[aria-pressed="true"] {
    outline: 3px solid Highlight;
    outline-offset: 2px;
  }

  .socket.is-target,
  .socket.is-droppable {
    border-color: Highlight;
  }
}

/* Written into the builder by index.html and replaced when the game starts. It shows only if
   the game never starts, after a wait long enough that a normal load never flashes it. */
.boot-failed {
  padding: 16px;
  font-weight: 700;
  opacity: 0;
  animation: boot-failed 0s 3s forwards;
}

@keyframes boot-failed {
  to { opacity: 1; }
}
