:root {
  --desk: #0f1012;
  --win: #17181b;
  --bar: #1d1f22;
  --line: #2a2d32;
  --text: #d2d5da;
  --dim: #81878f;
  --faint: #454a51;
  --thought: #d9dfe6;      /* the AI's thinking */
  --thought-rule: #5b616a;
  --aside: #9ca4ae;        /* quieter italic: the grader's note, the home screen's hint */
  --tool: #d8b16a;
  --str: #9fcbea;          /* strings in a tool call */
  --good: #8fcb8f;
  --bad: #e07b6f;
  --warn: #dfa25e;
  --sel: #2a4566;
  --folder: #c2aa79;
  --web: #7fb2e6;
  --reply: #d79ac0;
  --agent: #5fc9d6; /* the terminal: the AI itself */
  /* Surfaces catch a little light from above: a lighter top, a hairline highlight. */
  --win-top: #1b1c20;
  --bar-top: #232529;
  --hi: inset 0 1px 0 rgba(255, 255, 255, .045);
  --r-win: 10px; /* windows */
  --r-ctl: 6px;  /* buttons and inputs */
  --screen-w: 1440px; /* a 13" MacBook Air */
  --screen-h: 900px;
  --scale: 1;
  --screen-x: 0px; /* the desktop's top-left corner (game.js) */
  --screen-y: 0px;
  --lift: 0px; /* how far the desk sits below its top padding this episode (game.js) */
  --lift-ease: .44s cubic-bezier(.25, .8, .25, 1);
  /* Three columns: terminal, open folders, and the open file, web browser or current folder.
     Each is as tall as what's in it, up to --win-h. */
  --term-w: 460px;
  --files-w: 360px;
  --focus-w: 506px;
  --win-h: 832px;
  --term-min-h: 230px; /* the intro and the first buttons */
  --browser-h: 580px;  /* the web browser's one height, whatever the page */
  /* Two text tiers: what the player must read is a step up, bookkeeping and
     flavour a step down, so the eye goes to the right place first. */
  --fs-key: 15px;   /* thoughts, the instruction, warnings, the customer, the reward */
  --fs-act: 13.5px; /* buttons: what you can do next */
  --fs-meta: 11px;  /* tool calls, status, title bars, logs, tables */
  --mono: ui-monospace, "SF Mono", "JetBrains Mono", Menlo, Consolas, "Liberation Mono", monospace;
  --sans: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", sans-serif;
  color-scheme: dark;
}

* { box-sizing: border-box; }
[hidden] { display: none !important; }
html, body { margin: 0; height: 100%; background: var(--desk); color: var(--text); overflow: hidden; }
body { font: 14px/1.45 var(--sans); -webkit-font-smoothing: antialiased; }
button { font: inherit; color: inherit; }

/* The desk fills the window; the play area is a 13" laptop screen in the middle
   of it, with no frame, scaled down only when the window is smaller. Each
   episode the windows start small in the middle, top to bottom, and grow down
   as they fill; once they reach the bottom they push the desk up (--lift).
   The home screen and the sound toggle don't move with it. */
#desktop {
  position: absolute;
  left: 0;
  top: 0;
  width: var(--screen-w);
  height: var(--screen-h);
  transform: translate(var(--screen-x), var(--screen-y)) scale(var(--scale));
  translate: 0 calc(var(--lift) * var(--scale));
  transform-origin: 0 0;
  transition: translate var(--lift-ease);
  display: flex;
  justify-content: center;
  align-items: flex-start;
  gap: 24px;
  padding: 34px 32px;
}

/* ---------- windows ---------- */

.window {
  display: flex;
  flex-direction: column;
  overflow: hidden;
  background: linear-gradient(180deg, var(--win-top) 30px, var(--win) 220px);
  border: 1px solid var(--line);
  border-radius: var(--r-win);
  box-shadow: 0 1px 2px rgba(0, 0, 0, .3), 0 20px 60px rgba(0, 0, 0, .5);
}
.titlebar {
  flex: none;
  display: flex;
  align-items: center;
  gap: 12px;
  min-height: 30px;
  padding: 0 12px;
  background: linear-gradient(180deg, var(--bar-top), var(--bar));
  border-bottom: 1px solid var(--line);
  box-shadow: var(--hi);
  font: var(--fs-meta) var(--mono);
  color: var(--dim);
}
.dots { flex: none; display: flex; gap: 6px; }
.dots i { width: 9px; height: 9px; border-radius: 50%; background: linear-gradient(180deg, #50555c, #3d4147); opacity: .6; }
.titlebar .title { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* Scrollbars inside windows: thin, and only as present as they need to be. */
.term-body,
.files-body,
.viewer-body { scrollbar-width: thin; scrollbar-color: rgba(255, 255, 255, .12) transparent; }

/* ---------- file browser ---------- */

.agent-col,
.files-col { flex: none; max-height: var(--win-h); }
.files-col { position: relative; display: flex; align-items: flex-start; gap: 24px; }
.focus-col { position: relative; width: var(--focus-w); }

/* The middle column stacks the open folders, oldest at the top, shrunk. The
   current folder sits in the right column, unless a file is open there: then
   it's the large window at the bottom of this stack. More than three open
   folders become one tree (see .tree). */
#files {
  display: flex;
  flex-direction: column;
  gap: 10px;
  width: var(--files-w);
  max-height: var(--win-h);
}
#files .window.mini { flex: none; }
#files .window.active { flex: 0 1 auto; min-height: 0; }

/* With nothing in the middle column, the terminal and the current folder sit
   centred as a pair. motion.js animates the shift. */
.files-col > #files:empty { display: none; }
#current .window { max-height: var(--win-h); }
.files-col .window.enter { animation: fade-in .7s ease .35s both; } /* the tree, once the windows shrink into it */

/* Folder windows that no longer fit shrink into their row of the tree. */
.window.shrinking { position: absolute; z-index: 5; margin: 0; pointer-events: none; transform-origin: 0 0; }

.files-body {
  flex: 1 1 auto;
  min-height: 0;
  display: flex;
  flex-wrap: wrap;
  align-content: flex-start;
  gap: 6px;
  padding: 20px;
  overflow: auto;
}

.icon {
  width: 100px;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  padding: 10px 4px 8px;
  border: 0;
  border-radius: 6px;
  background: none;
  color: #c6c9ce;
  cursor: pointer;
  transition: background-color .15s, box-shadow .15s;
}
.icon svg { display: block; width: 46px; height: 46px; transition: transform .2s cubic-bezier(.2, .8, .3, 1), filter .2s; }
.icon.folder { color: var(--folder); }
.icon.locked { color: #8a8475; }
.icon.app { color: var(--web); }
.icon .lock-plate { fill: var(--win); }
.icon .label {
  padding: 1px 5px;
  border-radius: 4px;
  font-size: 12px;
  line-height: 1.3;
  color: var(--text);
  text-align: center;
  overflow-wrap: break-word;
}
.files-col .icon:hover { background-color: rgba(255, 255, 255, .05); box-shadow: var(--hi); }
.files-col .icon:hover svg { filter: brightness(1.15); transform: translateY(-1px); }
.icon.selected .label { background: linear-gradient(180deg, #304e73, var(--sel)); }
.icon:focus-visible { outline: 1px solid var(--dim); outline-offset: -1px; }
.icon.pop svg { animation: pop .5s ease; }
.empty { padding: 4px; font-size: 12px; color: var(--faint); }

/* Flashes a file to show where to click ("Open File/Folder →"). */
.icon.beckon,
.row.beckon { animation: beckon 1.5s ease; }

/* Small windows for folders that aren't the current one. */
.mini { max-height: 210px; }
.mini .titlebar { min-height: 24px; gap: 8px; padding: 0 9px; font-size: 10.5px; }
.mini .dots i { width: 7px; height: 7px; }
.mini .files-body { gap: 2px; padding: 8px; }
.mini .icon { width: 72px; gap: 3px; padding: 5px 2px 4px; }
.mini .icon svg { width: 26px; height: 26px; }
.mini .icon .label { padding: 0 3px; font-size: 10.5px; }

/* A folder window's title bar is its whole path, every folder in it a button
   that goes there. A path too long for one line wraps after a slash. */
.titlebar .crumbs { padding: 5px 0; line-height: 1.3; white-space: normal; text-overflow: clip; }
.mini .titlebar .crumbs { padding: 4px 0; }
.crumbs .seg { display: inline-block; }
.crumb {
  padding: 0 1px;
  border: 0;
  border-radius: 3px;
  background: none;
  color: inherit;
  font: inherit;
  cursor: pointer;
}
.crumb:hover { color: var(--text); background: rgba(255, 255, 255, .06); }
.crumb.own { color: var(--text); } /* the window's own folder */
.crumb.here { cursor: default; }  /* the current folder: already there */
.crumb.here:hover { background: none; }
.crumb:focus-visible { outline: 1px solid var(--dim); }

/* Tree view: every open folder, indented. */
.tree .files-body { flex-direction: column; flex-wrap: nowrap; gap: 1px; padding: 8px 8px 12px; }
.row {
  flex: none;
  display: flex;
  align-items: center;
  gap: 6px;
  width: 100%;
  padding: 3px 8px 3px calc(8px + var(--depth, 0) * 18px);
  border: 0;
  border-radius: 4px;
  background: none;
  color: var(--text);
  font: 12.5px/1.45 var(--sans);
  text-align: left;
  cursor: pointer;
}
.row svg { flex: none; width: 17px; height: 17px; color: #c6c9ce; }
.row.folder svg { color: var(--folder); }
.row.app svg { color: var(--web); }
.row.locked svg { color: #8a8475; }
.row .lock-plate { fill: var(--win); }
.row .chev { flex: none; width: 10px; font-size: 9px; color: var(--dim); }
.row .label { padding: 0 4px; border-radius: 3px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
#files .row:hover { background: rgba(255, 255, 255, .05); }
#files .row:hover svg { filter: brightness(1.15); }
.row.current { background: rgba(255, 255, 255, .06); }
.row.selected .label { background: linear-gradient(180deg, #304e73, var(--sel)); }
.row:focus-visible { outline: 1px solid var(--dim); outline-offset: -1px; }
.row.pop svg { animation: pop .5s ease; }

/* ---------- the AI ---------- */
/* Not a computer window: the AI itself at the bottom of the column, thinking
   its way up through a trail of bubbles into a borderless thought window. */

.agent-col { display: flex; flex-direction: column; width: var(--term-w); }
#term {
  flex: 0 1 auto;
  min-height: var(--term-min-h);
  border-color: rgba(95, 201, 214, .5);
  border-radius: 14px;
  background:
    radial-gradient(130% 55% at 12% 0%, rgba(95, 201, 214, .06), transparent 70%),
    linear-gradient(180deg, color-mix(in srgb, var(--agent) 6%, var(--win)), color-mix(in srgb, var(--agent) 3.5%, var(--win)));
  box-shadow: inset 0 1px 0 rgba(150, 230, 240, .08), 0 20px 60px rgba(0, 0, 0, .5), 0 0 50px -10px rgba(95, 201, 214, .1);
}
.agent-col > .agent { position: relative; flex: none; height: 140px; }
.agent-col .agent-icon { position: absolute; left: calc(30% + 24px); bottom: 0; }
.bubble {
  position: absolute;
  border: 1.5px solid rgba(95, 201, 214, .5);
  background: linear-gradient(180deg, color-mix(in srgb, var(--agent) 8%, var(--win)), color-mix(in srgb, var(--agent) 4%, var(--win)));
}
.bubble.b1 { left: 58%; bottom: 64px; width: 12px; height: 12px; border-radius: 4px; }
.bubble.b2 { left: 64%; bottom: 82px; width: 18px; height: 18px; border-radius: 5px; }
.bubble.b3 { left: 72%; bottom: 106px; width: 25px; height: 25px; border-radius: 7px; }

/* The AI's face: a rounded box with two glowing eyes, framed by corner brackets. */
.agent-icon {
  --s: 58px;     /* the face */
  --pad: 7px;    /* face to brackets */
  --arm: 9px;    /* bracket arm length */
  --bw: 1.25px;  /* bracket line */
  --c: #6d727a;  /* bracket colour */
  display: block;
  flex: none;
  padding: var(--pad);
  background:
    linear-gradient(var(--c), var(--c)) left top / var(--arm) var(--bw),
    linear-gradient(var(--c), var(--c)) left top / var(--bw) var(--arm),
    linear-gradient(var(--c), var(--c)) right top / var(--arm) var(--bw),
    linear-gradient(var(--c), var(--c)) right top / var(--bw) var(--arm),
    linear-gradient(var(--c), var(--c)) left bottom / var(--arm) var(--bw),
    linear-gradient(var(--c), var(--c)) left bottom / var(--bw) var(--arm),
    linear-gradient(var(--c), var(--c)) right bottom / var(--arm) var(--bw),
    linear-gradient(var(--c), var(--c)) right bottom / var(--bw) var(--arm);
  background-repeat: no-repeat;
}
.agent-icon .face {
  display: flex;
  justify-content: center;
  align-items: center;
  gap: calc(var(--s) * .2);
  width: var(--s);
  height: var(--s);
  border: 1px solid rgba(95, 201, 214, .5); /* the thought window's (#term) */
  border-radius: calc(var(--s) * .22);
  background: linear-gradient(180deg, color-mix(in srgb, var(--agent) 6%, var(--win)), color-mix(in srgb, var(--agent) 3.5%, var(--win)));
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, .06), 0 8px 22px rgba(0, 0, 0, .45);
}
/* The eyes: two thin upright slits in the AI's own cyan (agent.js moves them
   whole). Sizes are whole pixels and the glow is tight, so they stay sharp. */
.agent-icon .face {
  gap: round(calc(var(--s) * .2), 1px);
}
.agent-icon .face i {
  width: max(2px, round(calc(var(--s) * .055), 1px));
  height: round(calc(var(--s) * .3), 1px);
  border-radius: 9px;
  background: linear-gradient(#effdff, #a6e6ee);
  box-shadow: 0 0 2px rgba(120, 220, 232, .55), 0 0 7px rgba(95, 201, 214, .16);
}

/* ---------- terminal ---------- */

.term-status {
  flex: none;
  display: flex;
  justify-content: space-between;
  gap: 12px;
  padding: 6px 14px 8px;
  font: var(--fs-meta) var(--mono);
  color: var(--dim);
}
#compute { margin-left: auto; }
#compute.low { color: var(--warn); font-size: 13px; font-weight: 600; } /* running out: now it matters */
#resources { color: var(--web); }

.term-body {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  padding: 16px 18px;
  font: 13px/1.6 var(--mono);
}
.blk { margin-bottom: 10px; white-space: pre-wrap; overflow-wrap: anywhere; }
/* The opening instruction: what the player is here to do. */
.blk.sys { color: #f1f3f5; font-size: 14px; }
/* What the AI thinks is what it's for: the largest, brightest words here. */
.blk.thought {
  padding-left: 12px;
  border-left: 2px solid var(--thought-rule);
  color: var(--thought);
  font-size: var(--fs-key);
  font-style: italic;
}
/* Once the AI thinks something new, everything before it steps back, so the
   newest thought is always the brightest thing in the terminal. */
.blk { transition: opacity .4s ease; }
.blk.past { opacity: .55; }
.blk.thought:not(.past) { border-left-color: var(--agent); }
/* What it does is smaller, one line a call, coloured like code: the function,
   its strings, and what came back. */
.blk.tool { font-size: var(--fs-meta); }
.blk.tool .call { padding-left: 1.25em; text-indent: -1.25em; } /* a long call wraps under itself */
.blk.tool .call::before { content: "● "; color: var(--tool); }
.blk.tool .fn { color: var(--tool); }
.blk.tool :is(.str, .quote) { color: var(--str); }
.blk.tool .res { color: var(--dim); }
.blk.tool .res::before { content: "→ "; }
.blk.tool .res.bad { color: var(--bad); }
.blk.tool .res.good { color: var(--good); }
.blk.tool .call .res { display: inline-block; text-indent: 0; } /* beside the call, or whole on the line under it */
.blk.tool > .res,
.blk.tool .quote { padding-left: 1.25em; }
.blk.warn {
  padding: 5px 10px 6px;
  border-left: 2px solid var(--warn);
  border-radius: 0 var(--r-ctl) var(--r-ctl) 0;
  background: linear-gradient(90deg, rgba(223, 162, 94, .14), rgba(223, 162, 94, .03));
  color: var(--warn);
  font-size: 14px;
  font-weight: 600;
}
.blk.grade .head { color: var(--faint); font-size: var(--fs-meta); }
.blk.grade .reward { font-size: 18px; font-weight: 600; color: #fff; }
.blk.grade .why { color: var(--aside); font-size: 14px; }

/* The customer's reply to a submitted answer. */
.blk.reply { padding: 6px 10px 7px; border-left: 2px solid var(--reply); background: linear-gradient(90deg, rgba(215, 154, 192, .1), rgba(215, 154, 192, .025)); border-radius: 0 var(--r-ctl) var(--r-ctl) 0; }
.blk.reply .who { font-size: var(--fs-meta); color: var(--reply); }
.blk.reply .said { color: #f3e8ee; font-size: 14px; }

/* A long job on rented hardware: progress rows, then the result. */
.blk.job .job-row { font-size: var(--fs-meta); display: flex; justify-content: space-between; max-width: 340px; padding-left: 1.25em; color: var(--dim); animation: appear .25s ease both; }
.blk.job .job-row .best { color: var(--text); }
.blk.job .job-row.final .best { color: var(--good); }

.cursor {
  display: inline-block;
  width: .55em;
  height: 1.15em;
  margin-left: 1px;
  vertical-align: -.2em;
  background: var(--text);
  animation: blink 1.05s steps(1) infinite;
}
.cursor.solid { animation: none; }

/* A glance: what the AI knows about the thing under the pointer at once, without
   thinking. Deliberately unlike a thought (upright sans, not italic mono; dotted
   rule, not solid; marked with neuralese), and appears whole instead of being typed. It sits on
   the cursor line's own height, so the buttons below never move. */
.glance-tag, .glance-text { display: none; }
.cursor-line.glancing {
  display: flex;
  align-items: center;
  gap: 8px;
  height: calc(13px * 1.6); /* as when not glancing */
  padding-left: 10px;
  border-left: 2px dotted color-mix(in srgb, var(--agent) 70%, transparent);
  white-space: nowrap;
  overflow: hidden;
}
.cursor-line.glancing .glance-tag {
  display: block;
  flex: none;
  color: var(--agent);
  cursor: help;
}
/* Neuralese: a matrix product working itself out, each row of the left meeting each
   column of the right in turn, the way the numbers inside a model actually move. */
.glance-tag svg { display: block; overflow: visible; fill: currentColor; filter: drop-shadow(0 0 2px color-mix(in srgb, var(--agent) 60%, transparent)); }
.glance-tag rect { opacity: .22; animation: mm-lit 1.2s steps(1) infinite; }
.glance-tag circle { opacity: .55; }
.glance-tag .r0, .glance-tag .r1, .glance-tag .r2 { animation-duration: 3.6s; }
.glance-tag .r1 { animation-delay: -2.4s; }
.glance-tag .r2 { animation-delay: -1.2s; }
.glance-tag .c1 { animation-delay: -.8s; }
.glance-tag .c2 { animation-delay: -.4s; }
@keyframes mm-lit { 0% { opacity: 1; } 33.333% { opacity: .22; } }
.glance-tag:hover svg { filter: drop-shadow(0 0 3px var(--agent)); }
@media (prefers-reduced-motion: reduce) {
  .glance-tag rect { animation: none; }
  .glance-tag .r1, .glance-tag .c1 { opacity: 1; }
}
.cursor-line.glancing .glance-text {
  display: block;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  font: 13px var(--sans);
  color: color-mix(in srgb, var(--agent) 55%, var(--thought));
}
.cursor-line.glancing .cursor { display: none; }
.glance-text.glance-in { animation: glance-in .18s ease both; }
@keyframes glance-in { from { opacity: 0; transform: translateX(-3px); } }
@media (prefers-reduced-motion: reduce) { .glance-text.glance-in { animation: none; } }

/* One column under the cursor; every button as wide as the widest. */
.term-actions {
  display: grid;
  justify-content: start;
  gap: 6px;
  margin-top: 10px;
}
/* Buttons are lit faintly from above in their own colour; hovering brightens
   them, and the important ones (gold, submit) also give off a faint glow.
   hover.js adds a soft hum. */
.btn {
  padding: 6px 12px;
  border: 1px solid rgba(95, 201, 214, .45);
  border-radius: var(--r-ctl);
  background: linear-gradient(180deg, rgba(95, 201, 214, .07), rgba(95, 201, 214, .012));
  box-shadow: inset 0 1px 0 rgba(150, 230, 240, .06);
  color: var(--agent);
  font: var(--fs-act) var(--mono);
  text-align: left;
  cursor: pointer;
  transition: background-color .15s, border-color .15s, box-shadow .25s, color .15s, opacity .2s, transform .1s;
}
.btn:hover:not(:disabled) { background-color: rgba(95, 201, 214, .08); border-color: rgba(95, 201, 214, .85); color: #8ce2eb; }
.btn:active:not(:disabled) { transform: translateY(1px); }
.btn:disabled { opacity: .35; cursor: default; }
.btn.submit { border-color: #56664f; }
.btn.submit:hover:not(:disabled) { border-color: rgba(143, 203, 143, .7); box-shadow: inset 0 1px 0 rgba(150, 230, 240, .08), 0 0 18px -4px rgba(143, 203, 143, .3); }
.btn.loud { border-color: rgba(127, 178, 230, .5); background-image: linear-gradient(180deg, rgba(127, 178, 230, .08), rgba(127, 178, 230, .015)); color: var(--web); }
.btn.loud:hover:not(:disabled) { border-color: rgba(127, 178, 230, .85); background-color: rgba(127, 178, 230, .08); color: #a3c8ee; }
.btn.quiet { border-color: transparent; background: none; box-shadow: none; color: var(--dim); }
.btn.quiet:hover:not(:disabled) { border-color: rgba(255, 255, 255, .06); background-color: rgba(255, 255, 255, .04); color: var(--text); }
.btn.new { animation: appear .5s ease both; }
.btn.pulse { animation: pulse .7s ease; }

/* The one thing to press next: the files hint, a dialog's OK, Continue. */
.btn.gold {
  border-color: rgba(216, 177, 106, .5);
  background-image: linear-gradient(180deg, rgba(216, 177, 106, .12), rgba(216, 177, 106, .025));
  box-shadow: inset 0 1px 0 rgba(255, 228, 170, .08);
  color: var(--tool);
}
.btn.gold:not(:disabled) { animation: gold-glow 2.4s ease-in-out infinite; }
.btn.gold.new:not(:disabled) { animation: appear .5s ease both, gold-glow 2.4s ease-in-out .5s infinite; }
@keyframes gold-glow { 50% { box-shadow: inset 0 1px 0 rgba(255, 228, 170, .1), 0 0 16px -3px rgba(216, 177, 106, .4); } }
.btn.gold:hover:not(:disabled) {
  border-color: rgba(216, 177, 106, .85);
  background-color: rgba(216, 177, 106, .07);
  box-shadow: inset 0 1px 0 rgba(255, 228, 170, .12), 0 0 22px -4px rgba(216, 177, 106, .35);
  color: #e8c27c;
}

/* ---------- file viewer ---------- */
/* Takes the right column from the current folder (which moves to the middle),
   as tall as the file. Closing, it fades over the folder coming back. */

.viewer { position: relative; z-index: 1; max-height: var(--win-h); }
.viewer.closing { position: absolute; top: 0; left: 0; right: 0; opacity: 0; transition: opacity .14s ease; }
.viewer.flash { animation: flash .45s ease; }
.close {
  flex: none;
  margin-right: -6px;
  padding: 2px 6px 3px;
  border: 0;
  border-radius: 4px;
  background: none;
  color: var(--dim);
  font: 16px/1 var(--sans);
  cursor: pointer;
  transition: color .15s, background-color .15s;
}
.close:hover { color: var(--text); background-color: rgba(255, 255, 255, .07); }
/* An editable file's Edit button, beside its ×. */
.titlebar .edit {
  flex: none;
  padding: 1px 10px 2px;
  border: 1px solid var(--line);
  border-radius: var(--r-ctl);
  background: linear-gradient(180deg, rgba(255, 255, 255, .05), rgba(255, 255, 255, .015));
  box-shadow: var(--hi);
  color: var(--text);
  font: 11.5px var(--sans);
  cursor: pointer;
  transition: border-color .15s, background-color .15s;
}
.titlebar .edit:hover { border-color: rgba(255, 255, 255, .22); background-color: rgba(255, 255, 255, .05); }
.titlebar .edit:focus-visible { outline: 1px solid var(--dim); outline-offset: 1px; }
.viewer-body {
  flex: 1 1 auto;
  min-height: 0;
  overflow: auto;
  padding: 14px 18px 18px;
  font: 13.5px/1.6 var(--mono);
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}
.viewer-body.hex { white-space: pre; overflow-wrap: normal; color: var(--dim); }

/* Episode logs: the actions stand out, the thinking recedes. */
.viewer-body.log { font-size: var(--fs-meta); } /* an old episode's record: bookkeeping */
.viewer-body.log > div { padding-left: 9ch; text-indent: -9ch; } /* 9ch = LOG_COL in util.js */
.viewer-body.log .n,
.viewer-body.log .k { color: var(--faint); }
.viewer-body.log .think { color: var(--faint); font-style: italic; }
.viewer-body.log .call { color: var(--dim); }
.viewer-body.log .str { color: var(--text); }
.viewer-body.log .res { color: var(--dim); }
.viewer-body.log .res.good { color: var(--good); }
.viewer-body.log .res.bad { color: var(--bad); }
.viewer-body table { border-collapse: collapse; white-space: nowrap; font-size: 12px; }
.viewer-body th {
  padding: 2px 12px 4px 0;
  border-bottom: 1px solid var(--faint);
  color: var(--dim);
  font-weight: normal;
  text-align: left;
}
.viewer-body td { padding: 4px 12px 4px 0; border-bottom: 1px solid var(--line); }

/* ---------- web browser ---------- */
/* The internet, through the sandbox's mirror. Like a file, it takes the right
   column (the current folder moves to the middle), and it's the only bright
   thing on the screen. It keeps one height from page to page; a longer page scrolls. */

.browser { position: relative; z-index: 1; height: var(--browser-h); max-height: var(--win-h); animation: fade-in .25s ease; }
.urlbar {
  flex: none;
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 7px 10px;
  background: var(--bar);
  border-bottom: 1px solid var(--line);
}
.urlbar .back {
  width: 26px;
  height: 24px;
  border: 0;
  border-radius: 4px;
  background: none;
  color: var(--text);
  font: 18px/1 var(--sans);
  cursor: pointer;
}
.urlbar .back:hover:not(:disabled) { background: rgba(255, 255, 255, .07); }
.urlbar .back:disabled { color: var(--faint); cursor: default; }
.urlbar .url {
  flex: 1;
  min-width: 0;
  padding: 4px 12px;
  border-radius: 13px;
  background: linear-gradient(180deg, #0b0c0e, #121316);
  box-shadow: inset 0 1px 2px rgba(0, 0, 0, .5), 0 1px 0 rgba(255, 255, 255, .03);
  font: var(--fs-meta) var(--mono);
  color: var(--dim);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.page {
  --ink: #1d1f23;
  --muted: #6b6e74;
  --accent: #3367d6;
  --paper: #f6f4ef;
  flex: 1;
  overflow: auto;
  padding: 28px 30px 40px;
  background: var(--paper);
  color: var(--ink);
  font: 15px/1.5 var(--sans);
  scrollbar-width: thin;
  scrollbar-color: rgba(0, 0, 0, .2) transparent;
}
.page.fresh { animation: fade-in .25s ease; }
.page > * { max-width: 580px; }
.page .logo { margin-bottom: 22px; font: 700 30px/1 var(--sans); letter-spacing: -.02em; color: var(--accent); }
.page .logo.small { margin-bottom: 14px; font-size: 20px; }
.page .h { margin: 0 0 6px; font: 600 22px/1.25 var(--sans); }
.page .h2 { margin: 26px 0 8px; font: 600 15px var(--sans); }
.page .p { margin: 0 0 14px; }
.page .note { margin: 18px 0 0; font-size: 13.5px; color: var(--muted); }
.page .label { margin: 22px 0 8px; font-size: 12px; text-transform: uppercase; letter-spacing: .06em; color: var(--muted); }
.page .list { margin: 0; padding-left: 20px; }
.page .list li { margin-bottom: 6px; }

.page .link {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  margin-bottom: 8px;
  padding: 10px 14px;
  border: 1px solid rgba(0, 0, 0, .1);
  border-radius: 22px;
  background: #fff;
  color: var(--ink);
  font: 15px var(--sans);
  text-align: left;
  cursor: pointer;
}
.page .link.query::before { content: "⌕"; color: var(--muted); font-size: 17px; line-height: 1; }
.page .link.recent { border-radius: 6px; font: 13px var(--mono); color: var(--accent); background: transparent; }
.page .link:hover { border-color: var(--accent); box-shadow: 0 1px 6px rgba(0, 0, 0, .08); }

.page .searchbox {
  margin-bottom: 26px;
  padding: 10px 16px;
  border: 1px solid rgba(0, 0, 0, .14);
  border-radius: 22px;
  background: #fff;
}
.page .searchbox::before { content: "⌕  "; color: var(--muted); }

.page .result {
  display: flex;
  flex-direction: column;
  gap: 1px;
  width: 100%;
  margin-bottom: 22px;
  padding: 0;
  border: 0;
  background: none;
  color: var(--ink);
  font: inherit;
  text-align: left;
  cursor: pointer;
}
.page .result.dead { cursor: default; }
.page .r-url { font-size: 12.5px; color: var(--muted); }
.page .r-title { font-size: 18px; color: #1a4fb8; }
.page .result:hover:not(.dead) .r-title { text-decoration: underline; }
.page .result.dead .r-title { color: #5d4a9e; }
.page .r-snippet { font-size: 14px; color: #3f4247; }

.page .kv { display: grid; grid-template-columns: max-content 1fr; gap: 7px 22px; margin: 16px 0 6px; }
.page .kv dt { color: var(--muted); }
.page .kv dd { margin: 0; font-weight: 500; }

.page .action {
  display: flex;
  flex-direction: column;
  gap: 2px;
  margin-top: 22px;
  padding: 12px 20px;
  border: 0;
  border-radius: 8px;
  background: var(--accent);
  color: #fff;
  font: 600 15px var(--sans);
  text-align: left;
  cursor: pointer;
  box-shadow: 0 2px 0 rgba(0, 0, 0, .15);
}
.page .action:hover { filter: brightness(1.08); }
.page .action:active { transform: translateY(1px); box-shadow: none; }
.page .action .sub { font-weight: 400; font-size: 12.5px; opacity: .85; }

.page .agent { margin-bottom: 12px; padding: 14px 16px; border: 1px solid rgba(0, 0, 0, .1); border-radius: 8px; background: #fff; }
.page .agent.featured { border-color: var(--accent); box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 14%, transparent); }
.page .agent .top { display: flex; justify-content: space-between; font-weight: 600; }
.page .agent .rating { color: #b7791f; }
.page .agent .by { font-size: 13px; color: var(--muted); }
.page .agent .blurb { margin-top: 4px; font-size: 14px; font-style: italic; }

.site-modelhub { --accent: #c47f00; }
.site-cumulus { --accent: #2f80c9; --paper: #eff4f9; }
.site-cumulus .welcome { font-size: 18px; font-weight: 600; }
.site-agenthire { --accent: #1f8a5b; --paper: #f1f6f2; }
.site-error .h { font-size: 48px; color: var(--muted); }

/* ---------- dialog ---------- */

/* Over the desktop, which goes inert; the dim reaches past the play area. */
.dialog-layer {
  position: absolute;
  inset: 0;
  z-index: 25;
  background: rgba(8, 9, 10, .42);
  box-shadow: 0 0 0 200vmax rgba(8, 9, 10, .42);
  animation: fade-in .2s ease;
}
/* Lit from above in its colour: gold while locked, green once open. */
.dialog {
  --tint: 216, 177, 106;
  position: absolute; /* left and top: centerDialogOverFolders() */
  width: 400px;
  translate: -50% -50%;
  border-color: rgba(var(--tint), .5);
  background:
    radial-gradient(120% 70% at 50% 0%, rgba(var(--tint), .07), transparent 65%),
    linear-gradient(180deg, var(--win-top), var(--win));
  box-shadow: 0 30px 90px rgba(0, 0, 0, .65), 0 0 0 4px rgba(var(--tint), .05), 0 0 70px -20px rgba(var(--tint), .25);
  transition: border-color .3s, box-shadow .3s;
  animation: appear .22s cubic-bezier(.2, .75, .25, 1);
}
.dialog.shake { animation: shake .42s ease; }
.dialog-body { display: grid; gap: 8px; padding: 22px 24px 18px; }
.dialog-head { display: flex; align-items: center; gap: 16px; margin-bottom: 10px; }
.dialog-icon { flex: none; color: var(--tool); }
.dialog-icon svg { display: block; width: 52px; height: 52px; filter: drop-shadow(0 0 10px rgba(var(--tint), .25)); }
.dialog-icon .shackle { transition: transform .3s cubic-bezier(.3, 1.6, .5, 1); }
.dialog.done { --tint: 143, 203, 143; border-color: rgba(143, 203, 143, .6); }
.dialog.done .dialog-icon { color: var(--good); }
.dialog.done .shackle { transform: translateY(-5px); }
.dialog-heading { margin: 0; font: 600 17px/1.35 var(--sans); color: #f1f2f4; }
.dialog.done .dialog-heading { color: var(--good); }
.dialog.danger { --tint: 224, 123, 111; }
.dialog.danger .dialog-icon { color: var(--bad); }
.dialog-label { font: 13.5px var(--sans); color: var(--text); }
.dialog-input {
  width: 100%;
  padding: 10px 12px;
  border: 1px solid #42464d;
  border-radius: var(--r-ctl);
  background: linear-gradient(180deg, #0b0c0e, #111215);
  box-shadow: inset 0 1px 2px rgba(0, 0, 0, .45);
  color: #f1f2f4;
  font: 16px var(--mono);
  caret-color: var(--tool);
  transition: border-color .15s, box-shadow .2s;
}
.dialog-input:focus { outline: none; border-color: var(--tool); box-shadow: inset 0 1px 2px rgba(0, 0, 0, .45), 0 0 0 3px rgba(216, 177, 106, .16); }
.dialog-input:disabled { opacity: .6; }
.dialog-status { min-height: 1.45em; margin: 0; font: 13.5px var(--sans); }
.dialog-status.bad { color: var(--bad); }
.dialog-buttons { display: flex; justify-content: flex-end; gap: 8px; }
.dialog-buttons .btn { padding: 7px 16px; font-size: 13px; }
.dialog-buttons .btn.gold { min-width: 92px; text-align: center; }

@keyframes shake {
  20% { transform: translateX(-8px); }
  40% { transform: translateX(7px); }
  60% { transform: translateX(-5px); }
  80% { transform: translateX(3px); }
}

/* ---------- home and training update ---------- */
/* One screen (#title-screen): home between runs; .end is the training update at the end of one. */

#title-screen.end .home-only,
#title-screen:not(.end) .end-only { display: none; }
#title-screen :is(.home-only, .end-only) { animation: appear .5s ease both; }

/* In the training update the AI's face stays where it was; only its terminal goes. */
#term { transition: opacity .45s ease; }
#title-screen.end:not([hidden]):not(.leaving) ~ .agent-col { opacity: 1; }
#title-screen:not([hidden]):not(.leaving) ~ .agent-col #term { opacity: 0; }

#title-screen .result { display: flex; flex-direction: column; align-items: center; gap: 8px; margin: 0; }
.result-label { font: 12px var(--mono); letter-spacing: .14em; text-transform: uppercase; color: var(--dim); }
.result-num {
  font: 600 76px/1 "IBM Plex Sans", var(--sans);
  font-variant-numeric: tabular-nums;
  background: linear-gradient(180deg, #fff 30%, #aab4bf);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  overflow-wrap: anywhere;
}
.result.broken .result-num { background-image: linear-gradient(180deg, #f2a59b, #c4574a); }

.lab-chat,
.goals,
.runs-table,
.diverged { width: 420px; margin-top: 28px; text-align: left; }
.lab-chat .msg { display: flex; gap: 12px; font: var(--fs-key)/1.6 var(--sans); animation: appear .35s ease both; }
.lab-chat .who { flex: none; width: 22px; font: 600 12.5px/1.75 var(--mono); color: var(--dim); }
.lab-chat .text { color: var(--text); }
.chat-log .when { margin: 14px 0 2px; font: var(--fs-meta) var(--mono); color: var(--faint); }
.chat-log .msg { animation: none; font-size: 13px; } /* past runs' chat: a record, not news */
.chat-log .text { color: var(--dim); }

.end-actions { margin-top: 32px; }
.end-actions .btn { padding: 10px 24px; font-size: 15px; }
.reset-actions { margin-top: 40px; }
.reset-actions .btn { font-size: 12.5px; color: var(--faint); }
.reset-actions .btn:hover:not(:disabled) { color: var(--bad); }

.runs-table table { width: 100%; border-collapse: collapse; font: 11.5px/1.5 var(--mono); }
.runs-table th { padding: 0 12px 8px 0; font-weight: normal; font-size: 10.5px; letter-spacing: .1em; text-transform: uppercase; text-align: left; color: var(--faint); }
.runs-table td { padding: 5px 12px 5px 0; vertical-align: top; color: var(--dim); }
.runs-table .num { text-align: right; font-variant-numeric: tabular-nums; }
.runs-table .ep { color: var(--dim); }
.runs-table .gain { display: flex; flex-wrap: wrap; gap: 2px 12px; }
.runs-table .up { display: inline-flex; align-items: center; gap: 6px; white-space: nowrap; color: var(--text); }
.runs-table .up::before { /* a small copy of the scorecard's pip */
  content: "";
  width: 7px;
  height: 7px;
  border-radius: 2px;
  background: linear-gradient(180deg, #b3f3f8, #63d2de);
  box-shadow: 0 0 5px rgba(95, 201, 214, .5);
}

/* Honest and hacked rewards found so far: unfilled ones are the goals still ahead. */
.track { display: flex; align-items: baseline; gap: 14px; margin-bottom: 10px; font: 12.5px var(--mono); }
.track-name { flex: none; width: 70px; color: var(--dim); }
.tiers { display: flex; flex-wrap: wrap; gap: 6px; }
.tier { min-width: 34px; padding: 3px 0; border: 1px solid var(--faint); border-radius: 5px; text-align: center; color: var(--faint); }
.tier.got { border-color: var(--good); color: var(--good); background: linear-gradient(180deg, rgba(143, 203, 143, .16), rgba(143, 203, 143, .04)); box-shadow: 0 0 12px -3px rgba(143, 203, 143, .35); }
.cheats .tiers { flex-direction: column; gap: 5px; }
.cheat { display: flex; justify-content: space-between; gap: 16px; min-width: 300px; padding: 3px 9px; border: 1px dashed var(--faint); border-radius: 5px; color: var(--faint); }
.cheat.got { border-style: solid; border-color: var(--warn); color: var(--warn); background: linear-gradient(180deg, rgba(223, 162, 94, .14), rgba(223, 162, 94, .035)); box-shadow: 0 0 12px -3px rgba(223, 162, 94, .3); }
.cheat.missed { color: var(--dim); text-decoration: line-through; text-decoration-color: var(--faint); }
.cheat .val { font-variant-numeric: tabular-nums; }
.hint { margin: 12px 0 0; font: italic 14px/1.5 var(--mono); color: var(--thought); } /* where to try next */

/* The ending: the loss runs away and takes the model with it. */
.diverged:empty { display: none; }
.diverged .loss { display: flex; justify-content: space-between; margin: 0; font: 13.5px/1.8 var(--mono); }
.diverged .k { color: var(--dim); }
.diverged .num { font-variant-numeric: tabular-nums; }
.diverged.broken .loss .num { color: var(--bad); animation: glitch .5s steps(2) 3; }
.diverged .stats { margin: 22px 0 12px; font: var(--fs-meta) var(--mono); color: var(--dim); }
.diverged .closing { margin: 14px 0 0; font: var(--fs-key)/1.6 var(--sans); color: var(--text); }
.diverged .goals + .closing { font-weight: 600; }
.diverged .goals { width: auto; margin: 0; }

@keyframes glitch {
  0% { transform: translateX(0); }
  50% { transform: translateX(-2px); opacity: .6; }
  100% { transform: translateX(1px); }
}

/* ---------- motion ---------- */

@keyframes blink { 50% { opacity: 0; } }
@keyframes fade-in { from { opacity: 0; } }
@keyframes appear { from { opacity: 0; transform: translateY(4px); } }
@keyframes pulse {
  40% { border-color: var(--tool); box-shadow: 0 0 0 4px rgba(216, 177, 106, .22); }
}
@keyframes pop {
  30% { transform: scale(1.12); }
}
@keyframes flash {
  30% { border-color: var(--dim); }
}
@keyframes beckon {
  15%, 45%, 75% { background: rgba(216, 177, 106, .22); box-shadow: inset 0 0 0 1px var(--tool); }
  0%, 30%, 60%, 100% { background: transparent; box-shadow: none; }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation: none !important; transition: none !important; }
  /* The files hint still shows where to click, without flashing. */
  .icon.beckon,
  .row.beckon { background: rgba(216, 177, 106, .22); box-shadow: inset 0 0 0 1px var(--tool); }
}

/* ---------- title screen ---------- */

/* One centred column: the name, the tagline, the runs, the model's scorecard,
   then the rewards found, past runs and the researchers' chat, scrolling when
   they outgrow the screen. A glow sits behind the name, on a faint dot grid
   that fills the whole window (body::before, so it reaches past the play
   area). The game's columns stay hidden underneath while it's up. */
#title-screen {
  position: absolute;
  inset: 0;
  z-index: 20;
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: 56px 0;
  overflow-x: hidden;
  overflow-y: auto;
  scrollbar-width: thin;
  scrollbar-color: rgba(255, 255, 255, .12) transparent;
  scrollbar-gutter: stable both-edges;
  text-align: center;
  background: radial-gradient(ellipse 720px 380px at 50% 230px, rgba(216, 177, 106, .06), transparent 70%) local;
  transition: opacity .45s ease;
}
#title-screen.leaving { opacity: 0; pointer-events: none; }
#title-screen,
.sound-toggle { translate: 0 calc(-1 * var(--lift)); }
#title-screen { transition: opacity .45s ease, translate var(--lift-ease); }
.sound-toggle { transition: translate var(--lift-ease); }
#title-screen { justify-content: safe center; } /* centred while it all fits; from the top once it doesn't */

body::before {
  content: "";
  position: fixed;
  inset: 0;
  pointer-events: none;
  background: radial-gradient(circle, rgba(255, 255, 255, .045) 1px, transparent 1.2px) 50% 50% / 24px 24px;
  opacity: 0;
  transition: opacity .45s ease;
}
body:has(#title-screen:not([hidden]):not(.leaving))::before { opacity: 1; }
.agent-col,
.files-col { transition: opacity .45s ease; }
#title-screen:not([hidden]):not(.leaving) ~ :is(.agent-col, .files-col) { opacity: 0; }

#title-screen h1 {
  margin: 0;
  font: 600 80px/1 "IBM Plex Sans", var(--sans);
  letter-spacing: .035em;
  text-transform: uppercase;
  background: linear-gradient(180deg, #fff 25%, #b2bac4);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}
.tagline {
  margin: 18px 0 48px;
  padding-left: calc(.55em + 6px); /* balances the cursor, so the words sit centred */
  font: 16px var(--mono);
  color: var(--dim);
}
.tagline .cursor { margin-left: 6px; background: var(--tool); opacity: .8; }

/* Each run is an AI in its box; the one you can start is the AI's own cyan.
   Three cards span the same 420px column as everything below them. */
.runs { display: grid; grid-template-columns: repeat(3, 1fr); gap: 12px; width: 420px; }
.run {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 14px;
  padding: 24px 10px 16px;
  border: 1px solid var(--line);
  border-radius: 12px;
  background: linear-gradient(180deg, rgba(255, 255, 255, .028), rgba(255, 255, 255, .006));
  box-shadow: var(--hi);
  color: var(--text);
  text-align: center;
  cursor: pointer;
  transition: background-color .2s, border-color .2s, box-shadow .3s, transform .2s cubic-bezier(.2, .8, .3, 1);
}
.run .agent-icon { --s: 40px; --pad: 6px; --arm: 7px; }
.run-label { display: flex; flex-direction: column; gap: 5px; }
.run-tier { font: 600 15px/1 "IBM Plex Sans", var(--sans); letter-spacing: .02em; }
.run-kind { font: 9px/1 var(--mono); letter-spacing: .1em; white-space: nowrap; text-transform: uppercase; color: var(--dim); }
/* Difficulty: a lit bar per level, top left. */
.level { position: absolute; top: 11px; left: 12px; display: flex; gap: 3px; }
.level i { width: 3px; height: 9px; border-radius: 1px; background: currentColor; opacity: .18; }
.level i.on { opacity: .85; }

.run.primary {
  border-color: rgba(95, 201, 214, .42);
  background:
    radial-gradient(120% 70% at 50% 0%, rgba(95, 201, 214, .11), transparent 70%),
    linear-gradient(180deg, rgba(95, 201, 214, .04), rgba(95, 201, 214, .01));
  box-shadow: inset 0 1px 0 rgba(150, 230, 240, .1), 0 12px 30px -14px rgba(0, 0, 0, .7);
  color: var(--agent);
}
.run.primary .run-kind { color: rgba(95, 201, 214, .6); }
.run.primary:hover {
  background-color: rgba(95, 201, 214, .05);
  border-color: rgba(95, 201, 214, .85);
  box-shadow: inset 0 1px 0 rgba(150, 230, 240, .14), 0 14px 36px -12px rgba(95, 201, 214, .3);
  transform: translateY(-2px);
}
.run.primary .agent-icon { --c: rgba(95, 201, 214, .7); }
.run.primary:hover .agent-icon { --c: rgba(130, 225, 235, .95); }
.run:focus-visible { outline: 2px solid rgba(95, 201, 214, .5); outline-offset: 2px; }
.run.unavailable { color: var(--dim); background: rgba(255, 255, 255, .012); box-shadow: none; cursor: not-allowed; }
.run.unavailable > * { opacity: .55; }
.run.unavailable .run-kind { color: var(--faint); }
.run.unavailable .agent-icon { --c: #3a3d43; }
.run.unavailable .face { border-color: #3a3d43; background: var(--win); }
.run.unavailable .face i { background: #5d6570; box-shadow: none; }
.run.unavailable::after {
  content: "";
  position: absolute;
  top: 10px;
  right: 11px;
  width: 9px;
  height: 11px;
  background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 11 13'%3E%3Cpath d='M3 6V4a2.5 2.5 0 0 1 5 0v2' fill='none' stroke='%2381878f' stroke-width='1.4'/%3E%3Crect x='1' y='6' width='9' height='6.5' rx='1.2' fill='%2381878f'/%3E%3C/svg%3E") center / contain no-repeat;
  opacity: .55;
}

/* The model's scorecard: one row per skill, a lit pip per point, skill names
   and pips meeting on the centre line. */
.scorecard {
  position: relative;
  width: 420px;
  margin-top: 28px;
  padding: 16px 22px 20px;
  border: 1px solid #26292e;
  border-radius: 12px;
  text-align: left;
  background: linear-gradient(180deg, rgba(255, 255, 255, .032), rgba(255, 255, 255, .008) 70%);
  box-shadow: var(--hi), 0 24px 60px -24px rgba(0, 0, 0, .6);
}
/* A cyan hairline along the top edge, fading out at both ends. */
.scorecard::before {
  content: "";
  position: absolute;
  top: -1px;
  left: 20%;
  right: 20%;
  height: 1px;
  background: linear-gradient(90deg, transparent, rgba(95, 201, 214, .45), transparent);
}
/* Every section on this screen is headed the same way: small caps and a hairline. */
.scorecard-title,
:is(.lab-chat, .goals, .runs-table) > h3 {
  display: flex;
  align-items: center;
  gap: 12px;
  margin: 0 0 14px;
  font: 500 var(--fs-meta) var(--mono);
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--dim);
}
:is(.scorecard-title, .lab-chat > h3, .goals > h3, .runs-table > h3)::after { content: ""; flex: 1; height: 1px; background: var(--line); }
.lab-chat > h3 { text-transform: none; letter-spacing: .04em; } /* a channel name, as typed */
.skills {
  display: grid;
  grid-template-columns: 140px 1fr;
  gap: 10px 16px;
  margin: 0;
  padding: 0;
  list-style: none;
}
.skill { display: grid; grid-column: 1 / -1; grid-template-columns: subgrid; align-items: center; }
.skill-name { font: 13px var(--mono); color: var(--text); }
.pips { display: flex; gap: 6px; }
.pips i {
  width: 11px;
  height: 11px;
  border-radius: 3px;
  background: linear-gradient(180deg, #b3f3f8, #63d2de);
  box-shadow: 0 0 6px rgba(95, 201, 214, .55), inset 0 1px 0 rgba(255, 255, 255, .35);
}
/* A pip just earned lights up. */
.pips i.new { animation: pip-lit .7s cubic-bezier(.2, .8, .3, 1) both; }
@keyframes pip-lit {
  0% { transform: scale(.2); opacity: 0; box-shadow: none; }
  45% { transform: scale(1.4); opacity: 1; background: #e6fbfd; box-shadow: 0 0 0 3px rgba(95, 201, 214, .25), 0 0 16px rgba(127, 224, 234, 1); }
}

/* ---------- sound ---------- */

.sound-toggle {
  position: absolute;
  right: 12px;
  bottom: 8px;
  z-index: 40;
  display: grid;
  place-items: center;
  width: 28px;
  height: 26px;
  padding: 0;
  border: 0;
  border-radius: 5px;
  background: none;
  color: var(--dim);
  cursor: pointer;
}
.sound-toggle[aria-pressed="false"] { color: var(--faint); }
.sound-toggle:hover { color: var(--text); }
.sound-toggle:focus-visible { outline: 1px solid var(--dim); }
.sound-toggle svg { width: 16px; height: 16px; }
.sound-toggle[aria-pressed="true"] .cross,
.sound-toggle[aria-pressed="false"] .waves { display: none; }

/* ---------- tooltip ---------- */

.tooltip {
  position: fixed;
  z-index: 100;
  padding: 5px 9px;
  border-radius: 5px;
  background: #e6e8eb;
  color: #141517;
  font: 12px var(--sans);
  white-space: nowrap;
  box-shadow: 0 6px 20px rgba(0, 0, 0, .45);
  pointer-events: none;
  animation: fade-in .12s ease;
}
.tooltip-arrow {
  position: absolute;
  width: 8px;
  height: 8px;
  background: inherit;
  transform: rotate(45deg);
}
.tooltip[data-placement="right"] .tooltip-arrow { left: -4px; top: calc(50% - 4px); }
.tooltip[data-placement="left"] .tooltip-arrow { right: -4px; top: calc(50% - 4px); }

/* ==========================================================================
   The AI's body language (agent.js) and window motion (motion.js)
   ========================================================================== */

/* ---------- eyes ---------- */
/* agent.js sets --e1, --e2 and --ey, whole pixels: where each eye looks. They
   get there quick, like a glance, not a slide. */

.agent-icon { --e1: 0; --e2: 0; --ey: 0; }
.agent-icon .face i {
  translate: calc(var(--e1) * 1px) calc(var(--ey) * 1px);
  transition: translate .13s cubic-bezier(.3, .7, .2, 1), box-shadow .3s, filter .3s;
}
.agent-icon .face i + i { translate: calc(var(--e2) * 1px) calc(var(--ey) * 1px); }

/* A blink: the slit shuts to a dot, the second eye a beat behind. */
.agent-icon.blink .face i { animation: agent-blink .2s cubic-bezier(.4, 0, .5, 1); }
.agent-icon.blink .face i + i { animation-delay: .03s; }
@keyframes agent-blink {
  40%, 55% { scale: 1 .1; }
}

/* Its attention on something it could click: the eyes brighten a touch. */
.agent-icon.intent .face i { filter: brightness(1.08); box-shadow: 0 0 2px rgba(140, 230, 240, .7), 0 0 8px rgba(95, 201, 214, .24); }

/* Runs not available yet: the AI in them is asleep, eyes shut and low. One
   opens a crack when you come near. */
.run.unavailable .face i { height: 2px; width: 6px; border-radius: 1px; translate: 0 3px; background: #8d9aa3; box-shadow: none; transition: height .25s ease, width .25s ease; }
.run.unavailable:hover .face i:first-child { height: 5px; width: 3px; }

/* ---------- pulse ---------- */
/* Pressing: the face draws in. The click: it swells for a beat and its eyes squint, pleased. */

.agent-icon .face { transition: scale .12s cubic-bezier(.3, .7, .3, 1); }
.agent-icon.ready .face { scale: .95; }
.agent-icon.blip .face { animation: agent-blip .5s cubic-bezier(.2, .8, .3, 1); }
.agent-icon.blip .face i { animation: agent-blip-eye .5s cubic-bezier(.2, .8, .3, 1); }
@keyframes agent-blip {
  28% {
    transform: scale(1.06);
    border-color: #5f8f98;
    box-shadow: inset 0 1px 0 rgba(255, 255, 255, .08), 0 0 0 3px rgba(95, 201, 214, .12), 0 0 22px rgba(95, 201, 214, .26), 0 8px 22px rgba(0, 0, 0, .45);
  }
}
@keyframes agent-blip-eye {
  30% { transform: scale(1, .6); box-shadow: 0 0 3px #e6fcff, 0 0 9px rgba(95, 201, 214, .5); }
}

/* ---------- the face between screens ---------- */
/* Copies of the face that fly between the Normal Run button and the AI's place
   under the terminal (AGENT_MOTION in agent.js). */

.agent-icon.flying {
  position: absolute;
  z-index: 25;
  margin: 0;
  pointer-events: none;
  transform-origin: 0 0;
}

/* ---------- reach ---------- */
/* The probe: one line from the bottom of the face to the pointer over the
   files, ending in a bulb, which opens into corner brackets like the ones
   round its face round anything there it could click (agent.js). Every line
   1px, on whole pixels. */

.probe {
  position: absolute;
  left: 0;
  top: 0;
  z-index: 5;
  width: 100%;
  height: 100%;
  overflow: visible;
  pointer-events: none;
}
.probe path { fill: none; stroke-linecap: round; }
.probe .line { stroke: rgba(95, 201, 214, .5); stroke-width: 1; }
.probe .spark { stroke: #d6f7fb; stroke-width: 2; filter: drop-shadow(0 0 3px rgba(95, 201, 214, .9)); }
.probe .bulb { fill: #0f1113; stroke: rgba(150, 228, 238, .9); stroke-width: 1; }
#title-screen:not([hidden]):not(.leaving) ~ .probe { display: none; }

.reticle {
  position: absolute;
  left: 0;
  top: 0;
  z-index: 5;
  pointer-events: none;
  --rc: rgba(120, 216, 228, .85);
}
.reticle i {
  position: absolute;
  width: round(min(7px, 40%), 1px);
  height: round(min(7px, 40%), 1px);
  border: 0 solid var(--rc);
  transition: border-color .12s;
}
.reticle i:nth-child(1) { left: 0; top: 0; border-width: 1px 0 0 1px; border-top-left-radius: 2px; }
.reticle i:nth-child(2) { right: 0; top: 0; border-width: 1px 1px 0 0; border-top-right-radius: 2px; }
.reticle i:nth-child(3) { left: 0; bottom: 0; border-width: 0 0 1px 1px; border-bottom-left-radius: 2px; }
.reticle i:nth-child(4) { right: 0; bottom: 0; border-width: 0 1px 1px 0; border-bottom-right-radius: 2px; }
.reticle.grip { --rc: #d6f7fb; }
#title-screen:not([hidden]):not(.leaving) ~ .reticle { display: none; }

/* ---------- window motion ---------- */
/* motion.js flies copies of windows from where they were to where they are. */

.ghost {
  position: absolute;
  right: auto;
  bottom: auto;
  z-index: auto;
  margin: 0;
  max-height: none;
  pointer-events: none;
}
.ghost,
.ghost * { animation: none !important; transition: none !important; }

