/* Hands-on Computer Science
   Overview first, details on request. One accent (Hochschulblau), topic
   colours only as small dots, lists instead of tiles. */

@font-face {
  font-family: 'Atkinson Next';
  src: url('fonts/sans.woff2') format('woff2-variations');
  font-weight: 200 800;
  font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Atkinson Next';
  src: url('fonts/sans-ext.woff2') format('woff2-variations');
  font-weight: 200 800;
  font-display: swap;
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'Atkinson Next';
  src: url('fonts/sans-italic.woff2') format('woff2-variations');
  font-weight: 200 800;
  font-style: italic;
  font-display: swap;
}
@font-face {
  font-family: 'Atkinson Mono';
  src: url('fonts/mono.woff2') format('woff2-variations');
  font-weight: 200 800;
  font-display: swap;
}

:root {
  --paper: #ffffff;
  --paper-2: #f3f6f9;
  --ink: #1b2430;
  --quiet: #5f6b78;
  --rule: #e3e7ec;
  --accent: #009ee3;
  --accent-ink: #0072a8;
  --done: #23a565;
  --warn: #f5a623;
  --error: #c2185b;
  --sans: 'Atkinson Next', system-ui, sans-serif;
  --mono: 'Atkinson Mono', ui-monospace, monospace;
  --measure: 44rem;
  --wide: 64rem;
  --ease: cubic-bezier(0.2, 0.7, 0.2, 1);
  --tok-keyword: #0b6fa4;
  --tok-string: #2b7a3d;
  --tok-number: #b3541e;
  --tok-builtin: #7c3aed;
  --tok-function: #0e7490;
  color-scheme: light;
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme='light']) {
    --paper: #15202b;
    --paper-2: #1c2a37;
    --ink: #e6edf3;
    --quiet: #93a1af;
    --rule: #2a3a4a;
    --accent-ink: #5cc6f2;
    --done: #3fcf86;
    --tok-keyword: #5cc6f2;
    --tok-string: #8fd8a8;
    --tok-number: #f5a66b;
    --tok-builtin: #c4a1ff;
    --tok-function: #6fd6e8;
    color-scheme: dark;
  }
}
:root[data-theme='dark'] {
  --paper: #15202b;
  --paper-2: #1c2a37;
  --ink: #e6edf3;
  --quiet: #93a1af;
  --rule: #2a3a4a;
  --accent-ink: #5cc6f2;
  --done: #3fcf86;
  --tok-keyword: #5cc6f2;
  --tok-string: #8fd8a8;
  --tok-number: #f5a66b;
  --tok-builtin: #c4a1ff;
  --tok-function: #6fd6e8;
  color-scheme: dark;
}

/* --- base ---------------------------------------------------------------- */

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

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

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font: 400 1.0625rem/1.6 var(--sans);
  font-optical-sizing: auto;
  min-height: 100vh;
  display: flex;
  flex-direction: column;
}

/* The bit field behind everything (client/backdrop.js), faded out where the
   text column is, so it only shows at the edges. */
.backdrop {
  position: fixed;
  inset: 0;
  width: 100%;
  height: 100%;
  z-index: -1;
  pointer-events: none;
  mask-image: linear-gradient(90deg, #000 0%, rgb(0 0 0 / 0.55) 22%, rgb(0 0 0 / 0.12) 38%, rgb(0 0 0 / 0.12) 62%, rgb(0 0 0 / 0.55) 78%, #000 100%);
}

@media print {
  .backdrop {
    display: none;
  }
}

/* The licence under every page, quiet so that it does not compete with the content. */
.site-foot {
  width: 100%;
  max-width: var(--wide);
  margin: 0 auto;
  padding: 1.25rem 1.25rem 2rem;
  border-top: 1px solid var(--rule);
  color: var(--quiet);
  font-size: 0.85rem;
}

.site-foot p {
  max-width: var(--measure);
  margin: 0;
}

.site-foot a {
  color: inherit;
}

main {
  flex: 1;
  width: 100%;
  max-width: var(--wide);
  margin: 0 auto;
  padding: 2.5rem 1.25rem 5rem;
}

h1,
h2,
h3 {
  font-weight: 700;
  line-height: 1.15;
  letter-spacing: -0.01em;
  text-wrap: balance;
  margin: 0;
}

:is(h1, h2, h3, h4) code {
  white-space: nowrap;
}

/* on a phone a long piece of code would stick out, so there it may wrap */
@media (max-width: 40rem) {
  :is(h1, h2, h3, h4) code {
    white-space: normal;
  }
}

h1 {
  font-size: clamp(2rem, 1.4rem + 2.4vw, 2.75rem);
  font-weight: 750;
  letter-spacing: -0.02em;
}

h2 {
  font-size: 1.3rem;
}

h3 {
  font-size: 1.05rem;
}

a {
  color: var(--accent-ink);
  text-decoration-thickness: 1px;
  text-underline-offset: 0.18em;
}

a:hover {
  text-decoration-thickness: 2px;
}

code,
kbd {
  font-family: var(--mono);
  font-size: 0.9em;
}

button,
input,
select {
  font: inherit;
  color: inherit;
}

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

.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

.skip {
  position: absolute;
  left: -999px;
}

.skip:focus {
  left: 1rem;
  top: 0.5rem;
  z-index: 20;
  background: var(--paper);
  padding: 0.5rem 0.75rem;
}

.lead {
  font-size: 1.2rem;
  line-height: 1.5;
  color: var(--quiet);
  max-width: var(--measure);
  margin: 0.75rem 0 0;
  text-wrap: pretty;
}

.section {
  margin-top: 3rem;
}

.section > h2 {
  margin-bottom: 0.75rem;
}

/* --- header -------------------------------------------------------------- */

.bar {
  display: flex;
  align-items: center;
  gap: 0.5rem 1.5rem;
  max-width: var(--wide);
  width: 100%;
  margin: 0 auto;
  padding: 1rem 1.25rem 0;
  flex-wrap: wrap;
}

.wordmark {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  color: var(--ink);
  text-decoration: none;
  font-weight: 700;
  letter-spacing: -0.01em;
}

.wordmark .mark {
  color: var(--accent);
}

.nav {
  display: flex;
  gap: 1.5rem;
}

.nav a {
  color: var(--quiet);
  text-decoration: none;
  padding: 0.35rem 0;
  border-bottom: 2px solid transparent;
}

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

.nav a[aria-current='page'] {
  color: var(--ink);
  border-bottom-color: var(--accent);
}

.tools {
  display: flex;
  align-items: center;
  gap: 0.25rem;
  margin-left: auto;
}

.tool {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  min-height: 2.5rem;
  padding: 0 0.6rem;
  border: 0;
  border-radius: 999px;
  background: none;
  color: var(--quiet);
  text-decoration: none;
  cursor: pointer;
  font-size: 0.95rem;
}

.tool:hover,
.tool[aria-current='page'] {
  color: var(--ink);
  background: var(--paper-2);
}

.tool kbd {
  border: 1px solid var(--rule);
  border-radius: 4px;
  padding: 0 0.35rem;
  font-size: 0.75rem;
  line-height: 1.4;
}

.tool.lang {
  font-size: 0.85rem;
  font-weight: 600;
}

/* The progress ring next to "My learning": share of all items done. */
.ring {
  --p: 0;
  width: 1.1rem;
  height: 1.1rem;
  border-radius: 50%;
  background: conic-gradient(var(--done) calc(var(--p) * 1%), var(--rule) 0);
  mask: radial-gradient(circle, transparent 0.32rem, #000 0.33rem);
  transition: --p 0.6s var(--ease);
}

@property --p {
  syntax: '<number>';
  inherits: true;
  initial-value: 0;
}

/* --- start page ---------------------------------------------------------- */

.hero {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: 2rem 3rem;
  align-items: center;
  min-height: 26rem;
}

.hero-text {
  container-type: inline-size;
}

/* Two lines, the area name never broken: the size follows the column so that
   the longest name, "Artificial Intelligence" (about 9.6em), fits, and is the
   same on every start page. */
.hero-title {
  font-size: min(clamp(2.4rem, 1.4rem + 4vw, 3.6rem), 10.4cqi);
  line-height: 1.02;
  letter-spacing: -0.035em;
}

.hero-title span {
  display: block;
  white-space: nowrap;
}

.ways {
  list-style: none;
  margin: 2rem 0 0;
  padding: 0;
  display: grid;
  gap: 0.25rem;
  max-width: 28rem;
}

.way {
  display: grid;
  gap: 0.1rem;
  padding: 0.75rem 1rem;
  border-radius: 0.75rem;
  text-decoration: none;
  color: var(--ink);
}

.way:hover {
  background: var(--paper-2);
}

.way-title {
  font-weight: 700;
  font-size: 1.15rem;
}

.way-text {
  color: var(--quiet);
}

.way-main {
  background: var(--accent);
  color: #fff;
}

.way-main:hover {
  background: var(--accent-ink);
}

.way-main .way-text {
  color: rgb(255 255 255 / 0.85);
}

/* --- rows: the list form used everywhere ---------------------------------- */

.rows {
  list-style: none;
  margin: 0;
  padding: 0;
  border-top: 1px solid var(--rule);
}

.row {
  position: relative;
  display: flex;
  align-items: center;
  gap: 0.75rem;
  border-bottom: 1px solid var(--rule);
}

.row-link {
  flex: 1;
  min-width: 0;
  display: grid;
  gap: 0.15rem;
  padding: 0.85rem 0.5rem 0.85rem 2.1rem;
  color: var(--ink);
  text-decoration: none;
  position: relative;
}

/* The type icon: its shape says what kind of item it is. */
.type-icon {
  width: 1.15rem;
  height: 1.15rem;
  flex: none;
  color: var(--accent-ink);
}

.row-link > .type-icon {
  position: absolute;
  left: 0.25rem;
  top: 1.05rem;
}

.row-link:hover {
  background: var(--paper-2);
}

.row-head {
  display: flex;
  align-items: baseline;
  gap: 0.25rem 1rem;
  flex-wrap: wrap;
}

.row-title {
  font-weight: 650;
  font-size: 1.1rem;
  margin-right: auto;
}

.row-link:hover .row-title {
  color: var(--accent-ink);
}

.row-meta {
  display: flex;
  gap: 0.75rem;
  color: var(--quiet);
  font-size: 0.875rem;
  white-space: nowrap;
}

.row-optional {
  font-style: italic;
}

/* on the site with drafts (build/drafts.mjs): a strip on top, a tag at each draft */
.draft-tag {
  padding: 0 0.45rem;
  border: 1px solid var(--warn);
  border-radius: 999px;
  color: var(--ink);
  font-size: 0.78rem;
  font-weight: 600;
}

.drafts-banner {
  margin: 0;
  padding: 0.45rem 1rem;
  background: color-mix(in srgb, var(--warn) 22%, var(--paper));
  color: var(--ink);
  font-size: 0.9rem;
  text-align: center;
}

.drafts-banner a {
  color: inherit;
  font-weight: 600;
}

.row-summary {
  color: var(--quiet);
  font-size: 0.95rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  max-width: 48rem;
}

.row-done {
  width: 1.5rem;
  flex: none;
  color: var(--done);
  opacity: 0;
  transform: scale(0.6);
  transition: opacity 0.3s, transform 0.3s var(--ease);
}

.row.is-done .row-done {
  opacity: 1;
  transform: none;
}

/* a done check takes its mark back on click (site.js) */
.row.is-done :is(.row-step, .row-done) {
  cursor: pointer;
}

.row.is-done :is(.row-step, .row-done):hover {
  filter: brightness(1.12);
}

.row.is-done :is(.row-step, .row-done):focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: 50%;
}

.undo-pop {
  position: fixed;
  inset: auto;
  margin: 0;
  padding: 0.3rem;
  border: 1px solid var(--rule);
  border-radius: 0.6rem;
  background: var(--paper);
  color: var(--ink);
  box-shadow: 0 8px 24px rgb(0 0 0 / 0.18);
}

.undo-pop button {
  font: inherit;
  font-size: 0.92rem;
  padding: 0.45rem 0.75rem;
  border: 0;
  border-radius: 0.4rem;
  background: none;
  color: inherit;
  cursor: pointer;
  white-space: nowrap;
}

.undo-pop button:hover,
.undo-pop button:focus-visible {
  background: color-mix(in srgb, var(--accent) 14%, transparent);
  outline: none;
}

/* numbered steps of a path or course section */
.trail {
  border-top: 0;
  counter-reset: step;
}

.trail .row {
  border-bottom: 0;
}

.trail .row-link {
  padding-left: 2.6rem;
  border-radius: 0.6rem;
}

.trail .row-link > .type-icon {
  left: 0.85rem;
}

.row-step {
  position: relative;
  flex: none;
  width: 2rem;
  height: 2rem;
  border-radius: 50%;
  border: 2px solid var(--accent);
  display: grid;
  place-items: center;
  font-weight: 700;
  font-size: 0.9rem;
  background: var(--paper);
  z-index: 1;
}

.row-step .icon {
  display: none;
}

.trail .row + .row::after {
  content: '';
  position: absolute;
  left: calc(1rem - 1px);
  bottom: calc(50% + 1rem);
  height: calc(100% - 2rem);
  width: 2px;
  background: color-mix(in srgb, var(--quiet) 45%, transparent);
}

.trail .row.is-done .row-step {
  background: var(--done);
  border-color: var(--done);
  color: #fff;
}

.trail .row.is-done .row-step span {
  display: none;
}

.trail .row.is-done .row-step .icon {
  display: block;
}

.row-path {
  flex-wrap: wrap;
}

.row-path .meter {
  width: 100%;
  margin: -0.4rem 0 0.8rem 2.1rem;
}

[data-view='cards'] .switchable .row-path .meter {
  margin: 0 1.1rem 1rem;
}

[data-view='cards'] .switchable .row-path .meter-track {
  flex: 1;
}

[data-view='cards'] .switchable .row-path .meter-label {
  white-space: nowrap;
}

/* --- item, path and course pages ----------------------------------------- */

/* The column stands in the middle, so that a page zoomed on a projector stays
   centred (Nicolas, 2026-11-01). */
.page {
  max-width: var(--measure);
  margin-inline: auto;
}

.page-head {
  margin-bottom: 2rem;
}

/* The table of contents of a long item stands in the left margin and travels
   along while reading; it needs a window wide enough for a margin (Nicolas,
   2026-11-02). Its rail runs the height of the article, the list sticks. */
/* a section reached from the table of contents keeps a little air above */
.prose :is(h2, h3)[id] {
  scroll-margin-top: 1.5rem;
}

.toc {
  display: none;
}

@media (min-width: 80rem) {
  .item {
    position: relative;
  }

  .toc {
    display: block;
    position: absolute;
    top: 0;
    bottom: 0;
    right: calc(100% + 3rem);
    width: 13rem;
  }

  .toc-inner {
    position: sticky;
    top: 2rem;
    max-height: calc(100vh - 4rem);
    overflow-y: auto;
    overscroll-behavior: contain;
    scrollbar-width: thin;
  }
}

.toc-title {
  margin: 0 0 0.6rem;
  color: var(--quiet);
  font-size: 0.75rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

.toc ol {
  margin: 0;
  padding: 0;
  list-style: none;
  border-left: 2px solid var(--rule);
}

.toc a {
  display: block;
  margin-left: -2px;
  padding: 0.3rem 0 0.3rem 0.85rem;
  border-left: 2px solid transparent;
  color: var(--quiet);
  font-size: 0.85rem;
  line-height: 1.35;
  text-decoration: none;
  transition:
    color 0.2s var(--ease),
    border-color 0.2s var(--ease);
}

.toc .toc-3 a {
  padding-left: 1.7rem;
  font-size: 0.8rem;
}

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

/* Subsections fold away unless their section is the one being read
   (client/toc.js sets .is-folding and marks the open ones); the height
   follows from a grid row that grows from 0fr to 1fr, with no minimum, so
   that the padding of a folded entry leaves no gap. */
.toc.is-folding .toc-3 {
  display: grid;
  grid-template-rows: minmax(0, 0fr);
  overflow: hidden;
  opacity: 0;
  visibility: hidden;
  transition:
    grid-template-rows 0.25s var(--ease),
    opacity 0.2s var(--ease),
    visibility 0s linear 0.25s;
}

.toc.is-folding .toc-3 > a {
  min-height: 0;
  overflow: hidden;
}

.toc.is-folding .toc-3.is-open {
  grid-template-rows: minmax(0, 1fr);
  opacity: 1;
  visibility: visible;
  transition:
    grid-template-rows 0.25s var(--ease),
    opacity 0.2s var(--ease) 0.05s,
    visibility 0s;
}

@media (prefers-reduced-motion: reduce) {
  .toc.is-folding .toc-3,
  .toc.is-folding .toc-3.is-open {
    transition: none;
  }
}

.toc a[aria-current] {
  border-left-color: var(--accent);
  color: var(--accent-ink);
  font-weight: 700;
}

.kicker {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 0.3rem 1rem;
  margin: 0 0 0.75rem;
  color: var(--quiet);
  font-size: 0.95rem;
}

.kicker .type-icon {
  margin-right: -0.5rem;
}

.before {
  margin: 1.25rem 0 0;
  padding: 0.6rem 0.9rem;
  border-left: 3px solid var(--accent);
  background: var(--paper-2);
  border-radius: 0 0.5rem 0.5rem 0;
}

.notice {
  padding: 0.75rem 1rem;
  border-left: 3px solid var(--warn);
  background: color-mix(in srgb, var(--warn) 10%, var(--paper));
  border-radius: 0 0.5rem 0.5rem 0;
}

.prose {
  max-width: var(--measure);
  font-size: 1.0625rem;
  line-height: 1.7;
}

.prose h2 {
  margin: 2.25rem 0 0.75rem;
}

.prose p,
.prose ul,
.prose ol {
  margin: 0 0 1rem;
}

.prose li + li {
  margin-top: 0.25rem;
}

/* Tasks in an experiment (Nicolas, 2026-11-05: they got lost in the text
   around them). Every numbered list at the top level of an experiment is a
   group of tasks: a quiet band in the colour of the area with a line on its
   left, and the numbers in that colour, so that the eye finds the next task
   while scrolling without the page turning loud. */
.prose.has-tasks > ol {
  margin: 1.25rem 0 1.5rem;
  padding: 0.7rem 1rem 0.7rem 2.75rem;
  border-left: 3px solid var(--accent);
  border-radius: 0 0.5rem 0.5rem 0;
  background: color-mix(in srgb, var(--accent) 7%, transparent);
}

.prose.has-tasks > ol > li::marker {
  color: var(--accent-ink);
  font-weight: 700;
}

.prose.has-tasks > ol > li + li {
  margin-top: 0.5rem;
}

.prose table {
  border-collapse: collapse;
  margin: 1.25rem 0;
  display: block;
  overflow-x: auto;
  font-size: 0.95rem;
}

.prose th,
.prose td {
  padding: 0.45rem 1.25rem 0.45rem 0;
  border-bottom: 1px solid var(--rule);
  text-align: left;
}

.prose th {
  font-weight: 650;
}

/* a result table to fill in (client/tables.js) */
.prose td:has(> .table-input) {
  padding-top: 0.2rem;
  padding-bottom: 0.2rem;
}

.table-input {
  width: 6rem;
  font: inherit;
  color: inherit;
  padding: 0.25rem 0.45rem;
  border: 1px solid var(--rule);
  border-radius: 0.35rem;
  background: var(--paper-2);
}

.table-input:focus {
  outline: 2px solid var(--accent);
  outline-offset: 1px;
}

.prose code {
  background: var(--paper-2);
  padding: 0.1em 0.35em;
  border-radius: 0.3em;
}

.prose img {
  max-width: 100%;
  height: auto;
}

.prose blockquote {
  margin: 1.25rem 0;
  padding-left: 1rem;
  border-left: 3px solid var(--rule);
  color: var(--quiet);
}

/* --- code ---------------------------------------------------------------------- */

.prose pre.code,
.runner-output {
  margin: 0;
  padding: 0.9rem 1rem;
  font: 400 0.92rem/1.55 var(--mono);
  white-space: pre-wrap;
  overflow-wrap: anywhere;
  tab-size: 4;
}

/* Code keeps its lines: a long one scrolls sideways instead of breaking
   (Nicolas, 2026-11-02). Program output still wraps. */
.prose pre.code {
  white-space: pre;
  overflow-wrap: normal;
  overflow-x: auto;
  scrollbar-width: thin;
  margin: 1.25rem 0;
  background: var(--paper-2);
  border-radius: 0.75rem;
}

.prose pre.code code {
  background: none;
  padding: 0;
  font-size: inherit;
}

/* Copy button (client/copy.js): in the corner of every code block, shown on
   hover or keyboard focus, always on touch screens. */
.code-block {
  position: relative;
  margin: 1.25rem 0;
}

.prose .code-block > pre {
  margin: 0;
}

/* The name of a block that is a file, and the button that saves it
   (client/copy.js), as a tab on top of the block. */
.code-file {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
  margin: 1.25rem 0 0;
  padding: 0.35rem 0.45rem 0.35rem 1rem;
  border-bottom: 1px solid var(--rule);
  border-radius: 0.75rem 0.75rem 0 0;
  background: var(--paper-2);
}

.prose .code-file code {
  background: none;
  padding: 0;
  color: var(--quiet);
  font-size: 0.85rem;
}

.code-file + .code-block {
  margin-top: 0;
}

.prose .code-file + .code-block > pre {
  border-radius: 0 0 0.75rem 0.75rem;
}

.code-file-save {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  padding: 0.3rem 0.55rem;
  border: 1px solid var(--rule);
  border-radius: 0.5rem;
  background: var(--paper);
  color: var(--quiet);
  font: 500 0.8rem/1 var(--sans);
  cursor: pointer;
}

.code-file-save:hover,
.code-file-save:focus-visible {
  color: var(--accent-ink);
}

.copy-code {
  position: absolute;
  top: 0.45rem;
  right: 0.45rem;
  z-index: 1;
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  padding: 0.35rem;
  border: 1px solid var(--rule);
  border-radius: 0.5rem;
  background: var(--paper);
  color: var(--quiet);
  font: 500 0.8rem/1 var(--sans);
  cursor: pointer;
  opacity: 0;
  transition: opacity 0.15s, color 0.15s;
}

:is(.code-block, .runner-code):hover .copy-code,
.copy-code:focus-visible,
.copy-code[data-copied] {
  opacity: 1;
}

@media (hover: none) {
  .copy-code {
    opacity: 1;
  }
}

.copy-code:hover {
  color: var(--accent-ink);
}

.copy-code[data-copied] {
  color: var(--done);
}

.copy-code-done {
  display: none;
}

.prose .copy-code[data-copied] .copy-code-done {
  display: inline;
  padding-right: 0.15rem;
}

.tok-keyword {
  color: var(--tok-keyword);
  font-weight: 650;
}

.tok-string {
  color: var(--tok-string);
}

.tok-number {
  color: var(--tok-number);
}

.tok-builtin {
  color: var(--tok-builtin);
}

.tok-op {
  color: var(--tok-keyword);
  font-weight: 650;
}

.tok-function {
  color: var(--tok-function);
}

.tok-comment {
  color: var(--quiet);
  font-style: italic;
}

/* A runnable block (client/runner.js): the highlighted code lies under a
   transparent textarea with exactly the same font and padding, so typing
   happens in the textarea and the colours show through. */
.runner {
  margin: 1.5rem 0;
  border: 1px solid var(--rule);
  border-radius: 0.75rem;
  background: var(--paper);
  overflow: hidden;
  transition: border-color 0.2s, box-shadow 0.2s;
}

.runner > pre.code {
  margin: 0;
  border-radius: 0;
}

.runner-code {
  position: relative;
  background: var(--paper-2);
}

.prose .runner-code pre.code {
  margin: 0;
  border-radius: 0;
  background: none;
  overflow: visible;
}

/* The highlighted code and the textarea over it lie on one sheet as wide as
   the longest line, and that sheet scrolls sideways under the copy button. */
.runner-scroll {
  overflow-x: auto;
  scrollbar-width: thin;
}

.runner-sheet {
  position: relative;
  width: max-content;
  min-width: 100%;
}

.runner-input {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  margin: 0;
  padding: 0.9rem 1rem;
  border: 0;
  outline: 0;
  resize: none;
  overflow: hidden;
  background: transparent;
  color: transparent;
  caret-color: var(--ink);
  font: 400 0.92rem/1.55 var(--mono);
  white-space: pre;
  overflow-wrap: normal;
  tab-size: 4;
}

.runner-input::selection {
  background: color-mix(in srgb, var(--accent) 30%, transparent);
  color: transparent;
}

/* While someone types, the whole box is outlined, along its rounded edge. */
.runner:has(.runner-input:focus) {
  border-color: var(--accent);
  box-shadow: 0 0 0 1px var(--accent);
}

.runner-bar {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 0.5rem 0.75rem;
  padding: 0.6rem 0.75rem;
  border-top: 1px solid var(--rule);
}

.runner-bar .button {
  min-height: 2.25rem;
  padding: 0 1rem;
  font-size: 0.92rem;
}

.runner-bar .runner-run {
  display: inline-grid;
}

.runner-label {
  grid-area: 1 / 1;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
}

.runner-run[data-state='run'] [data-label='stop'],
.runner-run[data-state='stop'] [data-label='run'] {
  visibility: hidden;
}

.runner-status {
  color: var(--quiet);
  font-size: 0.875rem;
}

.runner-output {
  border-top: 1px solid var(--rule);
  min-height: 2.5rem;
  animation: fade 0.25s var(--ease);
}

/* R prints tables in columns, which break apart when lines wrap */
/* Suggestions while typing R or Python (client/complete.js): a small list at the
   cursor, the name on the left, its type or signature on the right, and for a
   function the package it comes from. */
.complete {
  position: fixed;
  z-index: 50;
  min-width: 14rem;
  max-width: min(30rem, calc(100vw - 1rem));
  /* about eight entries; more, as after dplyr::, scroll */
  max-height: 15.5rem;
  overflow-y: auto;
  overscroll-behavior: contain;
  margin: 0;
  padding: 0.25rem;
  list-style: none;
  background: var(--paper);
  border: 1px solid var(--rule);
  border-radius: 0.5rem;
  box-shadow: 0 0.5rem 1.5rem rgb(0 0 0 / 0.18);
  font: 400 0.85rem/1.4 var(--mono);
}

.complete-item {
  display: flex;
  gap: 1rem;
  justify-content: space-between;
  padding: 0.25rem 0.5rem;
  border-radius: 0.3rem;
  cursor: pointer;
}

.complete-item[aria-selected='true'] {
  background: color-mix(in srgb, var(--accent) 18%, transparent);
}

.complete-name {
  white-space: nowrap;
}

.complete-item.kind-column .complete-name {
  font-weight: 650;
}

.complete-detail {
  flex: 1;
  overflow: hidden;
  color: var(--quiet);
  text-align: right;
  white-space: nowrap;
  text-overflow: ellipsis;
}

.complete-package {
  flex-shrink: 0;
  color: var(--accent);
  white-space: nowrap;
}

/* a hidden copy of the editor text, to find where the cursor is */
.complete-mirror {
  position: absolute;
  inset: 0 auto auto 0;
  visibility: hidden;
  pointer-events: none;
}

/* a plot from R, drawn large and scaled to the width of the box */
.runner-plot {
  display: block;
  width: 100%;
  max-width: 40rem;
  height: auto;
  margin: 0.25rem 0;
  border-radius: 0.5rem;
  background: #fff;
}

/* a file the R code wrote, offered for download */
.runner-file {
  color: var(--accent-ink);
}

.runner[data-lang='r'] .runner-output {
  white-space: pre;
  overflow-wrap: normal;
  overflow-x: auto;
  font-size: 0.85rem;
}

.out-in {
  color: var(--accent-ink);
  font-weight: 650;
}

.out-err {
  color: var(--error);
}

.out-note {
  color: var(--quiet);
  font-style: italic;
}

.runner-answer {
  min-width: min(16rem, 100%);
  padding: 0.1rem 0.4rem;
  border: 0;
  border-bottom: 2px solid var(--accent);
  background: color-mix(in srgb, var(--accent) 8%, transparent);
  color: var(--accent-ink);
  font: 650 0.92rem/1.55 var(--mono);
  outline: 0;
}

.runner-hint {
  margin: 0 !important;
  padding: 0.6rem 1rem 0.8rem;
  border-top: 1px dashed var(--rule);
  font-size: 0.92rem;
  color: var(--ink);
  box-shadow: inset 3px 0 0 var(--warn);
}

/* the simulated LED of a program that uses lifi_hardware (client/led-view.js):
   the LED and the time on the left, the timeline beside it; the bars take the
   LED's own colours, since they stand for light */
.led-view {
  padding: 0.9rem 1rem 0.8rem;
  border-top: 1px solid var(--rule);
  animation: fade 0.25s var(--ease);
}

.led-row {
  display: flex;
  align-items: flex-start;
  gap: 1rem;
}

.led-device {
  display: grid;
  justify-items: center;
  gap: 0.3rem;
  flex: none;
  width: 4.2rem;
}

.led-lamp {
  width: 2.6rem;
  height: 2.6rem;
  border-radius: 50%;
  background: var(--paper-2);
  border: 1px solid var(--rule);
  transition: box-shadow 0.15s;
}

.led-lamp.is-on {
  box-shadow: 0 0 1.1rem 0.15rem color-mix(in srgb, var(--glow) 55%, transparent);
}

.led-clock,
.led-axis {
  font: 400 0.8rem/1 var(--mono);
  color: var(--quiet);
  font-variant-numeric: tabular-nums;
}

.led-timeline {
  flex: 1;
  min-width: 0;
  padding: 0.35rem 0.6rem 0 0.2rem;
}

.led-track {
  position: relative;
  height: 1.9rem;
  border-radius: 0.3rem;
  background: var(--paper-2);
  box-shadow: inset 0 0 0 1px var(--rule);
}

.led-bars {
  position: absolute;
  inset: 0;
}

.led-bar {
  position: absolute;
  top: 0.25rem;
  bottom: 0.25rem;
  margin-left: 1px;
  border-radius: 0.2rem;
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--ink) 15%, transparent);
}

/* a colour that lasts no time at all, as when two set_color follow without a pause */
.led-bar.is-instant {
  width: 3px;
  margin-left: 0;
}

.led-head {
  position: absolute;
  top: -0.2rem;
  bottom: -0.2rem;
  width: 2px;
  margin-left: -1px;
  background: var(--ink);
  opacity: 0;
}

.led-view.is-playing .led-head {
  opacity: 0.7;
}

.led-axis {
  position: relative;
  height: 1.2rem;
  margin-top: 0.35rem;
}

.led-axis span {
  position: absolute;
  transform: translateX(-50%);
  white-space: nowrap;
}

.led-axis span:first-child {
  transform: none;
}

.led-axis span:last-child {
  transform: translateX(-100%);
}

.led-note {
  margin: 0.5rem 0 0 !important;
  font-size: 0.92rem;
  color: var(--quiet);
}

.led-again {
  margin-top: 0.5rem;
  min-height: 2rem;
  padding: 0 0.9rem;
  font-size: 0.88rem;
}

/* .button sets a display of its own, which would override the attribute */
.led-again[hidden],
.runner-reset[hidden] {
  display: none;
}

/* --- callouts ------------------------------------------------------------------- */

.callout {
  margin: 1.5rem 0;
  padding: 0.9rem 1.1rem;
  background: var(--paper-2);
  border-radius: 0.75rem;
}

.callout p:last-child {
  margin-bottom: 0;
}

.callout-title {
  font-weight: 700;
  margin-bottom: 0.25rem !important;
  color: var(--accent-ink);
}

.callout-tip .callout-title {
  color: var(--done);
}

/* Hints and solutions stay folded until someone asks for them. */
details.callout > summary {
  list-style: none;
  cursor: pointer;
  margin-bottom: 0 !important;
}

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

details.callout > summary::before {
  content: '▸';
  display: inline-block;
  width: 0.65em;
  transition: transform 0.2s var(--ease);
}

details.callout[open] > summary {
  margin-bottom: 0.5rem !important;
}

details.callout[open] > summary::before {
  transform: rotate(90deg);
}

.callout-hint .callout-title {
  color: var(--done);
}

.callout-solution .callout-title {
  color: var(--accent-ink);
}

.path-section {
  margin-top: 1.75rem;
}

.path-section > summary h2 {
  margin: 0;
  font-size: 1.2rem;
}

/* Every title stands in capitals after the icon of its kind; warnings are not
   red, which marks errors, but in the lead colour. */
.callout-title {
  display: flex;
  align-items: center;
  gap: 0.45em;
  letter-spacing: 0.06em;
}

.callout-icon {
  flex: none;
  width: 1.15em;
  height: 1.15em;
}

.finish {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.75rem;
  margin: 2.5rem 0 0;
  padding-top: 1.5rem;
  border-top: 1px solid var(--rule);
}

/* --- what you learn: a folded box beside the content (pages.mjs) --------------- */

.objectives {
  max-width: var(--measure);
  margin: 1.5rem 0;
  border: 1px solid var(--rule);
  border-radius: 0.75rem;
  background: color-mix(in srgb, var(--accent) 4%, var(--paper));
}

.objectives > summary {
  list-style: none;
  display: flex;
  align-items: center;
  gap: 0.6rem;
  padding: 0.7rem 1rem;
  cursor: pointer;
  color: var(--quiet);
  font-weight: 600;
}

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

.objectives > summary::after {
  content: '';
  width: 0.45rem;
  height: 0.45rem;
  margin-left: auto;
  border-right: 2px solid currentColor;
  border-bottom: 2px solid currentColor;
  transform: rotate(45deg);
  transition: transform 0.25s var(--ease);
}

.objectives[open] > summary::after {
  transform: rotate(225deg);
}

.objectives > summary:hover {
  color: var(--ink);
}

.objectives-icon {
  width: 1.1rem;
  height: 1.1rem;
  color: var(--accent);
}

.objectives::details-content {
  block-size: 0;
  overflow: hidden;
  transition: block-size 0.35s var(--ease), content-visibility 0.35s allow-discrete;
}

.objectives[open]::details-content {
  block-size: auto;
}

.objectives-body {
  padding: 0 1rem 0.9rem 2.7rem;
  font-size: 0.95rem;
}

.objectives-body p {
  margin: 0 0 0.4rem;
}

.objectives-body ul {
  margin: 0;
  padding-left: 1.1rem;
}

.objectives-body li + li {
  margin-top: 0.25rem;
}

/* --- details on request --------------------------------------------------- */

.more {
  margin-top: 2rem;
  border-top: 1px solid var(--rule);
}

.more > summary {
  list-style: none;
  display: flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.85rem 0;
  cursor: pointer;
  color: var(--quiet);
  font-weight: 600;
}

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

.more > summary::before {
  content: '';
  width: 0.5rem;
  height: 0.5rem;
  border-right: 2px solid currentColor;
  border-bottom: 2px solid currentColor;
  transform: rotate(-45deg);
  transition: transform 0.25s var(--ease);
  margin: 0 0.25rem;
}

.more[open] > summary::before {
  transform: rotate(45deg);
}

.more > summary:hover {
  color: var(--ink);
}

.more::details-content {
  block-size: 0;
  overflow: hidden;
  transition: block-size 0.35s var(--ease), content-visibility 0.35s allow-discrete;
}

.more[open]::details-content {
  block-size: auto;
}

.more-body {
  padding: 0 0 1rem;
}

.more-body h3 {
  margin: 1rem 0 0.5rem;
  color: var(--quiet);
  font-size: 0.95rem;
}

.facts {
  display: grid;
  grid-template-columns: max-content 1fr;
  gap: 0.4rem 1.5rem;
  margin: 0.5rem 0 0;
}

.facts div {
  display: contents;
}

.facts dt {
  color: var(--quiet);
}

.facts dd {
  margin: 0;
}

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

.button {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  min-height: 2.75rem;
  padding: 0 1.25rem;
  border: 2px solid var(--accent);
  border-radius: 999px;
  background: var(--accent);
  color: #fff;
  font-weight: 650;
  text-decoration: none;
  cursor: pointer;
  transition: background 0.2s, color 0.2s, border-color 0.2s;
}

.button:hover {
  background: var(--accent-ink);
  border-color: var(--accent-ink);
}

.links {
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem;
  margin-top: 2rem;
}

.button-quiet {
  background: transparent;
  color: var(--accent-ink);
}

/* filled on hover like every button, so the text takes the page colour */
.button-quiet:hover {
  color: var(--paper);
}

.done-toggle .icon {
  stroke-dasharray: 24;
  stroke-dashoffset: 24;
  width: 0;
  transition: stroke-dashoffset 0.4s var(--ease) 0.1s, width 0.2s;
}

.done-toggle[aria-pressed='true'] {
  background: transparent;
  border-color: var(--done);
  color: var(--done);
}

.done-toggle[aria-pressed='true'] .icon {
  width: 18px;
  stroke-dashoffset: 0;
}

.text-button {
  border: 0;
  background: none;
  padding: 0;
  color: var(--accent-ink);
  text-decoration: underline;
  text-underline-offset: 0.18em;
  cursor: pointer;
}

.danger {
  color: var(--error);
}

.chips {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  border: 0;
  padding: 0;
  margin: 1rem 0 0;
}

.chip {
  position: relative;
}

.chip input {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  margin: 0;
  opacity: 0;
  pointer-events: none;
}

.chip > span {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  min-height: 2.25rem;
  padding: 0 0.95rem;
  border: 1px solid var(--rule);
  border-radius: 999px;
  cursor: pointer;
  font-size: 0.95rem;
  transition: background 0.2s, border-color 0.2s, color 0.2s;
}

.chip .type-icon {
  width: 1rem;
  height: 1rem;
  color: inherit;
  margin-left: -0.2rem;
}

.chip > span:hover {
  border-color: var(--quiet);
}

.chip input:checked + span {
  background: var(--ink);
  border-color: var(--ink);
  color: var(--paper);
}

.chip input:focus-visible + span {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

.field {
  display: grid;
  gap: 0.25rem;
  font-size: 0.875rem;
  color: var(--quiet);
}

.field[hidden] {
  display: none;
}

.field select,
.share-actions input {
  min-height: 2.5rem;
  padding: 0 0.75rem;
  border: 1px solid var(--rule);
  border-radius: 0.5rem;
  background: var(--paper);
  color: var(--ink);
}

/* --- content list ---------------------------------------------------------- */

.finder {
  margin: 1.5rem 0 0;
}

.finder-search {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  padding: 0 1rem;
  border: 2px solid var(--rule);
  border-radius: 999px;
  max-width: 36rem;
  transition: border-color 0.2s;
}

.finder-search:focus-within {
  border-color: var(--accent);
}

.finder-search .icon {
  color: var(--quiet);
  flex: none;
}

.finder-search input {
  flex: 1;
  min-width: 0;
  border: 0;
  outline: 0;
  background: none;
  min-height: 3rem;
  font-size: 1.1rem;
}

.finder-more {
  margin-top: 0.5rem;
  border-top: 0;
}

.finder-more > summary {
  padding: 0.5rem 0;
  font-size: 0.95rem;
}

.fields {
  display: flex;
  flex-wrap: wrap;
  gap: 1rem;
}

.count {
  color: var(--quiet);
  font-size: 0.9rem;
  margin: 1rem 0 0.5rem;
}

.list-bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 0.5rem 1rem;
}

.list-bar .count {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 0.5rem 0.75rem;
}

.list-tools {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  margin-left: auto;
}

.group-by {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  font-size: 0.875rem;
  color: var(--quiet);
}

.group-by select {
  min-height: 2rem;
  padding: 0 0.5rem;
  border: 1px solid var(--rule);
  border-radius: 0.5rem;
  background: var(--paper);
  color: var(--ink);
  font-size: 0.875rem;
}

/* Shown as soon as any filter is set: one click back to everything. */
.clear-filters {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  min-height: 1.9rem;
  padding: 0 0.7rem 0 0.5rem;
  border: 1px solid var(--rule);
  border-radius: 999px;
  background: var(--paper);
  color: var(--ink);
  font-size: 0.85rem;
  cursor: pointer;
  animation: rise 0.25s var(--ease);
  transition: border-color 0.2s;
}

.clear-filters[hidden] {
  display: none;
}

.clear-filters:hover {
  border-color: var(--quiet);
}

.clear-filters .icon {
  width: 0.95rem;
  height: 0.95rem;
}

.filter-badge {
  display: inline-grid;
  place-items: center;
  min-width: 1.25rem;
  height: 1.25rem;
  margin-left: 0.4rem;
  padding: 0 0.3rem;
  border-radius: 999px;
  background: var(--accent);
  color: #fff;
  font-size: 0.75rem;
  font-weight: 650;
}

.filter-badge[hidden] {
  display: none;
}

/* Group headings inside the content list. */
.rows .group-head {
  list-style: none;
  border-bottom: 1px solid var(--rule);
}

.rows .group-head:not(:first-child) {
  margin-top: 1.75rem;
}

.group-head h2 {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  margin: 0;
  padding: 0.5rem 0.25rem;
  font-size: 0.8rem;
  font-weight: 650;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--quiet);
}

.group-head .icon {
  width: 1rem;
  height: 1rem;
}

.group-count {
  font-weight: 400;
  letter-spacing: 0;
}

.group-count::before {
  content: '· ';
}

.rows:has(> .group-head) {
  border-top: 0;
}

[data-view='cards'] .switchable .group-head {
  grid-column: 1 / -1;
  border-bottom: 0;
}

[data-view='cards'] .switchable .group-head:not(:first-child) {
  margin-top: 0.75rem;
}

[data-view='cards'] .switchable .group-head h2 {
  padding-bottom: 0;
}

.view-switch {
  display: flex;
  border: 1px solid var(--rule);
  border-radius: 999px;
  padding: 2px;
}

.view-switch button {
  display: grid;
  place-items: center;
  width: 2.25rem;
  height: 1.9rem;
  border: 0;
  border-radius: 999px;
  background: none;
  color: var(--quiet);
  cursor: pointer;
  transition: background 0.2s, color 0.2s;
}

.view-switch button:hover {
  color: var(--ink);
}

.view-switch button[aria-pressed='true'] {
  background: var(--paper-2);
  color: var(--ink);
}

/* The same rows as cards: icon on top, summary in full, meta at the foot. */
[data-view='cards'] .rows.switchable {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(15rem, 100%), 1fr));
  gap: 1rem;
  border-top: 0;
  margin-top: 0.25rem;
}

[data-view='cards'] .switchable .row {
  align-items: stretch;
  border: 1px solid var(--rule);
  border-radius: 0.75rem;
  overflow: hidden;
  transition: border-color 0.2s;
}

[data-view='cards'] .switchable .row:hover {
  border-color: color-mix(in srgb, var(--accent) 60%, var(--rule));
}

[data-view='cards'] .switchable .row-link {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  padding: 1.1rem 1.1rem 1rem;
}

[data-view='cards'] .switchable .row-link > .type-icon {
  position: static;
  width: 1.6rem;
  height: 1.6rem;
  margin-bottom: 0.15rem;
}

[data-view='cards'] .switchable .row-head {
  display: contents;
}

[data-view='cards'] .switchable .row-title {
  margin-right: 0;
}

[data-view='cards'] .switchable .row-summary {
  order: 1;
  white-space: normal;
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
}

/* topics only in the card view; the list stays one quiet line */
.row-topics {
  display: none;
}

[data-view='cards'] .switchable .row-topics {
  order: 2;
  display: flex;
  flex-wrap: wrap;
  gap: 0.3rem;
  margin-top: auto;
  padding-top: 0.25rem;
}

[data-view='cards'] .switchable .row-topics .topic-tag {
  min-height: 1.5rem;
  padding: 0 0.55rem;
  font-size: 0.78rem;
}

[data-view='cards'] .switchable .row-meta {
  order: 3;
  margin-top: auto;
  padding-top: 0.25rem;
}

[data-view='cards'] .switchable .row-link:has(.row-topics) .row-meta {
  margin-top: 0;
}

[data-view='cards'] .switchable .row-done {
  position: absolute;
  top: 1rem;
  right: 1rem;
}

.empty {
  color: var(--quiet);
  padding: 1rem 0;
}

.row[hidden] {
  display: none;
}

/* --- course ---------------------------------------------------------------- */

.course-section {
  margin-top: 1.5rem;
}

/* A foldable path part or module section; fold.js remembers closed ones. */
.fold > summary {
  list-style: none;
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 0.25rem 1rem;
  cursor: pointer;
  padding: 0.5rem 0;
}

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

.fold > summary::before {
  content: '';
  align-self: center;
  width: 0.55rem;
  height: 0.55rem;
  border-right: 2px solid var(--quiet);
  border-bottom: 2px solid var(--quiet);
  transform: rotate(-45deg);
  transition: transform 0.25s var(--ease);
  /* turned down, the arrow reaches about 2px past its box on the left, which
     the clipped content of an open section would cut off */
  margin-left: 0.15rem;
  margin-right: 0.25rem;
}

.fold[open] > summary::before {
  transform: rotate(45deg);
}

.fold::details-content {
  block-size: 0;
  overflow: hidden;
  transition: block-size 0.35s var(--ease), content-visibility 0.35s allow-discrete;
}

.fold[open]::details-content {
  block-size: auto;
}

.fold-restoring .fold::details-content,
.fold-restoring .fold > summary::before {
  transition: none;
}

.fold-meta {
  color: var(--quiet);
  font-size: 0.9rem;
}

.course-path-part {
  margin-top: 0.75rem;
}

.course-path-part > summary h3 {
  margin: 0;
  font-size: 0.95rem;
  color: var(--quiet);
}

.course-path-link {
  margin: 0.75rem 0 0;
  font-size: 0.92rem;
}

.course-path-link a {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
}

.course-section-title {
  font-weight: 700;
  font-size: 1.3rem;
}

/* --- topics of an item ---------------------------------------------------------- */

.topic-tags {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
  margin: 0.9rem 0 0;
}

.topic-tag {
  display: inline-flex;
  align-items: center;
  min-height: 1.75rem;
  padding: 0 0.7rem;
  border-radius: 999px;
  background: color-mix(in srgb, var(--accent) 12%, transparent);
  color: var(--accent-ink);
  font-size: 0.85rem;
  font-weight: 600;
  text-decoration: none;
  transition: background 0.2s;
}

.topic-tag:hover {
  background: color-mix(in srgb, var(--accent) 22%, transparent);
}

/* --- institution of a course ------------------------------------------------ */

/* one card per module; two or three side by side, smaller, wrapping on a phone */
.institutions {
  display: grid;
  gap: 0.75rem;
  margin-top: 1.25rem;
}

.institutions.is-several {
  grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr));
}

.institution {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 0.75rem 1.25rem;
  padding: 0.9rem 1.1rem;
  border: 1px solid var(--rule);
  border-radius: 0.75rem;
}

.is-several .institution {
  align-items: flex-start;
  flex-direction: column;
  gap: 0.6rem;
  padding: 0.75rem 0.9rem;
  font-size: 0.9rem;
}

.is-several .institution-logo img {
  width: 96px;
}

.institution-logo img {
  display: block;
  width: 140px;
  height: auto;
}

/* the logo is black; on a dark page it shows white */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme='light']) .institution-logo img {
    filter: invert(1);
  }
}
:root[data-theme='dark'] .institution-logo img {
  filter: invert(1);
}

.institution-text {
  display: grid;
  gap: 0.15rem;
}

.institution-module {
  color: var(--ink);
  font-weight: 650;
}

.institution-name {
  color: var(--quiet);
  text-decoration: none;
}

.institution-handbook {
  font-size: 0.92rem;
}

/* --- meter ----------------------------------------------------------------- */

.meter {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  margin-top: 1.25rem;
  font-size: 0.875rem;
  color: var(--quiet);
}

.meter-track {
  flex: 0 1 14rem;
  height: 6px;
  border-radius: 999px;
  background: var(--rule);
  overflow: hidden;
}

.meter-fill {
  height: 100%;
  width: var(--progress, 0%);
  background: var(--done);
  border-radius: inherit;
  transition: width 0.6s var(--ease);
}

/* --- quiz ------------------------------------------------------------------ */

.quiz-question {
  margin: 0 0 2.5rem;
}

/* A question is a sentence, not a title: fill the lines instead of
   balancing them, and never break inside a piece of code. */
.quiz-question h2 {
  text-wrap: pretty;
  font-size: 1.15rem;
  margin: 0 0 0.75rem;
}

.quiz-options {
  list-style: none;
  padding: 0;
  margin: 0 0 0.75rem;
  display: grid;
  gap: 0.5rem;
  counter-reset: option;
}

.prose .quiz-option {
  position: relative;
  margin: 0;
  padding: 0.65rem 1rem 0.65rem 3.1rem;
  border: 1px solid var(--rule);
  border-radius: 0.75rem;
  cursor: pointer;
  user-select: none;
  transition: border-color 0.2s, background 0.2s;
  counter-increment: option;
}

/* The mark carries the option's letter, which is also its key (quiz.js): a
   circle for one answer, a square for several. */
.quiz-option::before {
  content: counter(option, upper-alpha);
  position: absolute;
  left: 0.85rem;
  top: 50%;
  translate: 0 -50%;
  display: grid;
  place-items: center;
  box-sizing: border-box;
  width: 1.6rem;
  height: 1.6rem;
  border: 2px solid var(--quiet);
  border-radius: 50%;
  color: var(--quiet);
  font-size: 0.8rem;
  font-weight: 700;
  line-height: 1;
  transition: background 0.2s, border-color 0.2s, color 0.2s;
}

.quiz-options[data-multiple='true'] .quiz-option::before {
  border-radius: 0.25rem;
}

.quiz-option:hover {
  border-color: var(--quiet);
}

.quiz-option[aria-checked='true'] {
  border-color: var(--accent);
}

.quiz-option[aria-checked='true']::before {
  border-color: var(--accent);
  background: var(--accent);
  color: var(--paper);
}

.is-checked .quiz-option {
  cursor: default;
}

.is-checked .quiz-option[data-correct='true'] {
  border-color: var(--done);
  background: color-mix(in srgb, var(--done) 9%, transparent);
}

.is-checked .quiz-option[data-correct='false'][aria-checked='true'] {
  border-color: var(--error);
  background: color-mix(in srgb, var(--error) 8%, transparent);
}

.quiz-actions {
  display: flex;
  align-items: center;
  gap: 1rem;
}

.quiz-actions button {
  min-height: 2.5rem;
  padding: 0 1.1rem;
  border: 2px solid var(--accent);
  border-radius: 999px;
  background: none;
  color: var(--accent-ink);
  font-weight: 650;
  cursor: pointer;
  transition: opacity 0.2s;
}

.quiz-actions button:disabled {
  opacity: 0.4;
  cursor: default;
}

.quiz-verdict {
  margin: 0 !important;
  font-weight: 700;
}

.is-correct .quiz-verdict {
  color: var(--done);
}

.is-wrong .quiz-verdict {
  color: var(--error);
}

.quiz-explanation {
  margin-top: 0.75rem;
  padding: 0.75rem 1rem;
  background: var(--paper-2);
  border-radius: 0.75rem;
  animation: reveal 0.35s var(--ease);
}

.quiz-explanation p {
  margin: 0;
}

/* The reason for one option, inside it, shown after answering. The mark
   then stays beside the first line instead of the middle of the block. */
.quiz-option:has(.quiz-reason)::before {
  top: calc(0.65rem + 0.8em);
}

.prose .quiz-reason {
  margin-top: 0.4rem;
  font-size: 0.92rem;
  color: var(--quiet);
  animation: reveal 0.35s var(--ease);
}

.quiz-reason p {
  margin: 0;
}

/* One question at a time (client/quiz.js): a bar with one segment per
   question on top, Back and Next below, a short result at the end. */
.quiz-bar {
  display: grid;
  grid-template-columns: auto 1fr auto;
  align-items: center;
  gap: 0.75rem;
  margin: 0.5rem 0 1.75rem;
}

.quiz-bar .button {
  justify-content: center;
  min-width: 8.5rem;
  min-height: 2.25rem;
  padding: 0 1rem;
  font-size: 0.92rem;
}

@media (max-width: 40rem) {
  .quiz-bar {
    gap: 0.5rem;
  }

  .quiz-bar .button {
    min-width: 6.5rem;
    padding: 0 0.7rem;
  }
}

.quiz-position {
  white-space: nowrap;
  text-align: center;
  font-size: 0.875rem;
  color: var(--quiet);
}

.quiz-steps {
  grid-column: 1 / -1;
}

.quiz-steps {
  display: flex;
  gap: 6px;
}

.quiz-step {
  flex: 1;
  height: 8px;
  padding: 0;
  border: 0;
  border-radius: 999px;
  background: var(--rule);
  cursor: pointer;
  transition: background 0.3s, transform 0.3s var(--ease);
}

.quiz-step.was-correct {
  background: color-mix(in srgb, var(--done) 35%, var(--rule));
}

.quiz-step.is-correct {
  background: var(--done);
}

.quiz-step.is-wrong {
  background: var(--error);
}

.quiz-step[aria-current='step'] {
  transform: scaleY(1.6);
  box-shadow: inset 0 0 0 1px var(--accent);
}

.quiz-step[aria-current='step']:not(.is-correct, .is-wrong) {
  background: var(--accent);
}

.quiz-stepper .quiz-question {
  margin-bottom: 0;
}

.quiz-stepper .quiz-question h2:focus,
.quiz-result h2:focus {
  outline: 0;
}

/* on the result, Next keeps its place in the grid but is not shown */
.quiz-bar .quiz-next[hidden] {
  display: inline-flex;
  visibility: hidden;
}

.quiz-back:disabled {
  opacity: 0.35;
  pointer-events: none;
}

/* the arrows that stand for Back and Next in focus mode */
.quiz-arrow {
  display: none;
}

/* In focus mode the bar saves room (Nicolas, 2026-11-05): Back and Next are
   thin arrows without a frame, in one row with a thin bar of segments and
   the position, followed by the focus symbol. */
.in-focus > .quiz-bar {
  grid-template-columns: auto minmax(0, 1fr) auto auto auto;
  grid-template-areas: 'back steps position next tools';
  gap: 0.6rem;
  margin: 0 0 1.25rem;
}

.in-focus > .quiz-bar .quiz-back {
  grid-area: back;
}

.in-focus > .quiz-bar .quiz-next {
  grid-area: next;
}

.in-focus > .quiz-bar .quiz-position {
  grid-area: position;
  font-size: 0.8rem;
}

.in-focus > .quiz-bar .quiz-steps {
  grid-area: steps;
  gap: 4px;
}

.in-focus > .quiz-bar .quiz-tools {
  grid-area: tools;
  margin: 0;
}

.in-focus > .quiz-bar .quiz-step {
  height: 3px;
}

.in-focus > .quiz-bar .button {
  min-width: 0;
  min-height: 0;
  width: 1.9rem;
  height: 1.9rem;
  padding: 0;
  border: 1px solid transparent;
  border-radius: 0.4rem;
  background: none;
  box-shadow: none;
  color: var(--quiet);
}

.in-focus > .quiz-bar .button:hover {
  border-color: var(--rule);
  color: var(--accent-ink);
}

.in-focus > .quiz-bar .quiz-label {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

.in-focus > .quiz-bar .quiz-arrow {
  display: block;
}

.quiz-result h2 {
  font-size: 1.15rem;
  margin: 0 0 0.5rem;
}

.prose .quiz-score {
  font-size: 1.5rem;
  font-weight: 700;
  margin: 0 0 1.25rem;
}

/* --- my learning ------------------------------------------------------------ */

.form-error {
  color: var(--error);
}

.share {
  display: flex;
  flex-wrap: wrap;
  gap: 1.5rem;
  align-items: flex-start;
  margin-top: 1rem;
}

.qr {
  width: 11rem;
  background: #fff;
  border-radius: 0.75rem;
  padding: 0.5rem;
}

.qr svg {
  display: block;
  width: 100%;
  height: auto;
}

.share-actions {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 0.75rem;
  flex: 1 1 16rem;
  min-width: 0;
}

.share-actions input {
  width: 100%;
  font-family: var(--mono);
  font-size: 0.8rem;
}

.actions {
  display: flex;
  gap: 1rem;
  align-items: center;
}

.row-course .meter {
  margin: 0;
  flex: none;
}

.row-course .meter-track {
  flex: none;
  width: 6rem;
}

.row-course .text-button {
  font-size: 0.875rem;
  margin-right: 0.5rem;
}

.rows .muted {
  padding: 0.85rem 0;
  color: var(--quiet);
}

/* --- map ----------------------------------------------------------------- */

.map {
  margin: 0;
  position: relative;
}

.map-canvas {
  display: block;
  width: 100%;
  height: 100%;
  overflow: visible;
}

.hero-map {
  aspect-ratio: 1 / 0.85;
}

.map-full {
  height: min(68vh, 40rem);
  margin-top: 1.5rem;
  border-top: 1px solid var(--rule);
  border-bottom: 1px solid var(--rule);
}

.map-link {
  stroke: color-mix(in srgb, var(--quiet) 40%, transparent);
  stroke-width: 1.25;
  transition: stroke 0.2s, opacity 0.3s;
}

.map-node {
  cursor: pointer;
  transform-box: view-box;
  transition: opacity 0.3s;
}

.map-node text {
  fill: var(--ink);
  font-size: 12.5px;
  font-weight: 600;
  paint-order: stroke;
  stroke: var(--paper);
  stroke-width: 4px;
  stroke-linejoin: round;
  pointer-events: none;
}

.map-node text tspan.code {
  font-family: var(--mono);
}

/* a topic: a filled circle with its number of items, its name above the ring */
.map-topic circle {
  fill: var(--accent);
  stroke: var(--paper);
  stroke-width: 3;
  transition: r 0.2s var(--ease);
}

.map-topic .map-count {
  fill: #fff;
  stroke: none;
  font-size: 14px;
  font-weight: 700;
}

.map-topic .map-topic-name {
  font-size: 14px;
  font-weight: 700;
}

.map-topic:hover circle,
.map-topic:focus-visible circle {
  r: 25;
}

/* an item: a small ring with its type icon; its title on request */
.map-item circle {
  fill: var(--paper);
  stroke: var(--accent);
  stroke-width: 1.5;
  transition: r 0.2s var(--ease), stroke 0.2s;
}

.map-item.is-done circle {
  stroke: var(--done);
  stroke-width: 2.5;
}

.map-icon {
  color: var(--accent-ink);
  pointer-events: none;
}

.map-item text {
  opacity: 0;
  transition: opacity 0.2s;
}

.map-item:hover circle,
.map-item:focus-visible circle {
  r: 12;
}

.map-item:hover text,
.map-item:focus-visible text,
.map.is-focused .map-item.is-near text,
.map.has-topic:not(.is-focused) .map-item.is-chosen text {
  opacity: 1;
}

.map.is-focused .map-node:not(.is-near),
.map.is-focused .map-link:not(.is-near) {
  opacity: 0.15;
}

.map-link.is-near {
  stroke: var(--accent);
}

.map-node:focus-visible {
  outline: none;
}

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

.search {
  width: min(40rem, calc(100vw - 2rem));
  max-height: min(34rem, calc(100vh - 6rem));
  margin: 12vh auto auto;
  padding: 0;
  border: 1px solid var(--rule);
  border-radius: 1rem;
  background: var(--paper);
  color: var(--ink);
  box-shadow: 0 24px 60px -20px rgb(15 30 45 / 0.35);
  overflow: hidden;
  display: none;
  flex-direction: column;
}

.search[open] {
  display: flex;
  animation: rise 0.22s var(--ease);
}

.search::backdrop {
  background: rgb(15 30 45 / 0.35);
  backdrop-filter: blur(2px);
  animation: fade 0.22s;
}

.search-form {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  padding: 0.5rem 0.5rem 0.5rem 1.1rem;
  border-bottom: 1px solid var(--rule);
}

.search-form > .icon {
  color: var(--quiet);
  flex: none;
}

.search-form input {
  flex: 1;
  min-width: 0;
  border: 0;
  outline: 0;
  background: none;
  min-height: 3rem;
  font-size: 1.15rem;
}

.search-results {
  list-style: none;
  margin: 0;
  padding: 0.5rem;
  overflow-y: auto;
}

.search-results:empty {
  display: none;
}

.search-results a {
  display: grid;
  grid-template-columns: auto 1fr auto;
  align-items: baseline;
  gap: 0 0.75rem;
  padding: 0.6rem 0.75rem;
  border-radius: 0.6rem;
  color: var(--ink);
  text-decoration: none;
}

.search-results .r-icon {
  align-self: center;
  display: flex;
}

.search-results a[aria-selected='true'],
.search-results a:hover {
  background: var(--paper-2);
}

.search-results .r-title {
  font-weight: 650;
}

.search-results .r-meta {
  color: var(--quiet);
  font-size: 0.85rem;
}

.search-results .r-summary {
  grid-column: 2 / -1;
  color: var(--quiet);
  font-size: 0.9rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.search-results li {
  animation: rise 0.25s var(--ease) backwards;
  animation-delay: calc(var(--i, 0) * 25ms);
}

.search-hint {
  margin: 0;
  padding: 0.6rem 1.1rem;
  border-top: 1px solid var(--rule);
  color: var(--quiet);
  font-size: 0.85rem;
}

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

@view-transition {
  navigation: auto;
}

::view-transition-old(root),
::view-transition-new(root) {
  animation-duration: 0.25s;
}

::view-transition-group(*) {
  animation-duration: 0.4s;
  animation-timing-function: var(--ease);
}

@keyframes rise {
  from {
    opacity: 0;
    transform: translateY(6px);
  }
}

@keyframes fade {
  from {
    opacity: 0;
  }
}

@keyframes reveal {
  from {
    opacity: 0;
    transform: translateY(-4px);
  }
}

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

/* --- small screens ---------------------------------------------------------- */

@media (max-width: 960px) {
  .tool-label,
  .tool kbd {
    display: none;
  }

  .bar {
    column-gap: 1rem;
  }

  .nav {
    gap: 1.1rem;
  }
}

@media (max-width: 800px) {
  .hero {
    grid-template-columns: 1fr;
    min-height: 0;
  }

  .hero-map {
    aspect-ratio: 1 / 0.8;
    max-height: 22rem;
  }
}

@media (max-width: 640px) {
  body {
    font-size: 1rem;
  }

  main {
    padding: 1.5rem 1rem 4rem;
  }

  .bar {
    padding: 0.75rem 1rem 0;
    gap: 0.25rem 0.75rem;
  }

  .wordmark span {
    font-size: 0.9rem;
  }

  .nav {
    order: 3;
    width: 100%;
    gap: 1.25rem;
    overflow-x: auto;
    scrollbar-width: none;
  }

  .tool-label,
  .tool kbd {
    display: none;
  }

  .tool {
    padding: 0 0.45rem;
  }

  /* on phones the system setting decides; the header keeps one row */
  .tool.theme {
    display: none;
  }

  .tools {
    gap: 0;
  }

  .row-summary {
    white-space: normal;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
  }

  .search {
    margin-top: 1rem;
    max-height: calc(100vh - 2rem);
  }
}

.map-node.is-dim,
.map-link.is-dim {
  opacity: 0.12;
}

/* --- projects ----------------------------------------------------------------- */

.phase {
  scroll-margin-top: 1rem;
}

.challenge::details-content {
  block-size: 0;
  overflow: hidden;
  transition: block-size 0.35s var(--ease), content-visibility 0.35s allow-discrete;
}

.challenge[open]::details-content {
  block-size: auto;
}

.challenge {
  margin-top: 0.75rem;
  border-left: 3px solid var(--rule);
  scroll-margin-top: 1rem;
}

.challenge[open] {
  border-left-color: var(--accent);
}

.challenge > summary {
  list-style: none;
  cursor: pointer;
  padding: 0.5rem 0.9rem;
  font-weight: 650;
}

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

.challenge > summary:hover {
  color: var(--accent-ink);
}

.challenge-body {
  padding: 0 0.9rem 0.5rem;
}

/* --- photos with captions ---------------------------------------------------- */

.photo-row {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr));
  gap: 1rem;
  margin: 1.5rem 0;
}

.photo {
  margin: 0;
}

/* Photos always show as 16:9 and fill their column (Nicolas, 2026-11-02);
   the files are cut to that format, and cover only catches one that is not. */
.photo img {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 16 / 9;
  object-fit: cover;
}

/* Every picture on a page sits in the frame of a drawing (.drawing-canvas),
   a thin line with room inside and round corners (Nicolas, 2026-11-02). */
.photo img,
.prose p > img:only-child {
  display: block;
  padding: 0.75rem;
  background: var(--paper);
  border: 1px solid var(--rule);
  border-radius: 0.75rem;
}

/* a drawing keeps its own height, so a wide flat one does not stand in empty
   space (Nicolas, 2026-11-02); the margin keeps it apart from the text */
.photo img[src$='.svg'] {
  aspect-ratio: auto;
  margin: 1rem 0 0.25rem;
}

.photo figcaption {
  margin-top: 0.4rem;
  color: var(--quiet);
  font-size: 0.875rem;
}

/* --- 3D models (client/model.js) -------------------------------------------- */

.model-row {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr));
  gap: 1rem;
  margin: 1.5rem 0 0.5rem;
}

.model {
  margin: 0;
  /* the printed case is black; a little lighter, so its shape shows */
  --model-colour: #59626d;
}

.model-canvas {
  height: 20rem;
  border-radius: 0.75rem;
  background: var(--paper-2);
  display: grid;
  place-items: center;
  color: var(--quiet);
  font-size: 0.9rem;
  text-align: center;
  padding: 0;
  overflow: hidden;
}

.model-canvas canvas {
  width: 100%;
  height: 100%;
  display: block;
  cursor: grab;
  touch-action: pan-y;
}

.model-canvas canvas:active {
  cursor: grabbing;
}

.model-canvas canvas:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: -2px;
}

.model figcaption,
.model-hint {
  margin-top: 0.4rem;
  color: var(--quiet);
  font-size: 0.875rem;
}

.model-hint {
  margin: 0 0 1.5rem;
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme='light']) .model {
    --model-colour: #8794a2;
  }
}

:root[data-theme='dark'] .model {
  --model-colour: #8794a2;
}

/* --- slide decks --------------------------------------------------------------- */

/* A data set: download, size and the lines that load it, above the text. */
.dataset {
  margin: 0 0 2.5rem;
  padding: 1.25rem 1.25rem 0.25rem;
  border: 1px solid var(--rule);
  border-radius: 0.75rem;
}

.dataset-file {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.75rem 1.25rem;
  margin-bottom: 1.25rem;
}

.script-file {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem 0.9rem;
  max-width: var(--measure);
  margin: 0 0 1.5rem;
}

.quiz-files {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.2rem 0.45rem;
  margin: 0 0 1.5rem;
  color: var(--quiet);
  font-size: 0.85rem;
}

.quiz-files .icon {
  width: 14px;
  height: 14px;
}

.quiz-files a {
  color: var(--quiet);
  text-decoration: underline;
  text-decoration-color: color-mix(in srgb, currentColor 40%, transparent);
  text-underline-offset: 0.2em;
  transition: color 0.2s;
}

.quiz-files a:hover {
  color: var(--accent-ink);
}

.script-note {
  color: var(--quiet);
  font-size: 0.9rem;
}

.prose .dataset-facts {
  display: flex;
  flex-wrap: wrap;
  gap: 0.25rem 1rem;
  margin: 0;
  color: var(--quiet);
  font-size: 0.95rem;
}

.prose .dataset-lang {
  margin: 1.25rem 0 -0.75rem;
  color: var(--quiet);
  font-size: 0.85rem;
  font-weight: 600;
}

.deck {
  margin: 0 0 2rem;
}

.deck-frame {
  position: relative;
  aspect-ratio: 16 / 9;
  border-radius: 0.75rem;
  overflow: hidden;
  border: 1px solid var(--rule);
  background: #000;
}

.deck-frame iframe {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  border: 0;
}

.deck-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.75rem 1.25rem;
  margin-top: 0.75rem;
}

/* Material inside a lesson (build/embed.mjs): a quiz or a demo in a quiet
   frame, named above with its type and a link to its own page, so that the
   lesson reads on around it; other material is its row. */
.embedded {
  max-width: var(--measure);
  margin: 0.5rem 0 2rem;
}

.embedded-quiz,
.embedded-demo {
  padding: 1rem 1.25rem 0.25rem;
  border: 1px solid var(--rule);
  border-radius: 0.75rem;
}

.embedded-slides {
  max-width: none;
}

.embedded-head {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.25rem 0.5rem;
  margin: 0 0 0.75rem;
  color: var(--quiet);
  font-size: 0.875rem;
}

.embedded-head .type-icon {
  width: 16px;
  height: 16px;
}

.embedded-head a {
  color: inherit;
  font-weight: 600;
  text-decoration: underline;
  text-decoration-color: color-mix(in srgb, currentColor 40%, transparent);
  text-underline-offset: 0.2em;
  transition: color 0.2s;
}

.embedded-head a:hover {
  color: var(--accent-ink);
}

.embedded .prose {
  max-width: none;
}

.row-material {
  color: var(--quiet);
}

/* A video sits in the same frame as a deck; until it is played, the frame
   holds only a play button and the note that YouTube is asked then. */
.video {
  margin: 0 0 2rem;
}

.video-frame {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 1rem;
  aspect-ratio: 16 / 9;
  padding: 1.5rem;
  border-radius: 0.75rem;
  overflow: hidden;
  border: 1px solid var(--rule);
  background: #000;
  color: #fff;
  text-align: center;
}

.video-play {
  display: inline-flex;
  align-items: center;
  gap: 0.6rem;
  padding: 0.7rem 1.2rem;
  border: 1px solid rgb(255 255 255 / 0.35);
  border-radius: 999px;
  background: rgb(255 255 255 / 0.08);
  color: inherit;
  font: inherit;
  font-weight: 600;
  cursor: pointer;
  transition: background 0.15s;
}

.video-play:hover,
.video-play:focus-visible {
  background: rgb(255 255 255 / 0.18);
}

.video-play svg {
  width: 1.6rem;
  height: 1.6rem;
}

.video-note {
  max-width: 34rem;
  margin: 0;
  color: rgb(255 255 255 / 0.65);
  font-size: 0.85rem;
}

.video-frame iframe {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  border: 0;
}

/* --- back to where the learner came from ---------------------------------------- */

/* A small, quiet arrow at the end of the kicker, and a button beside "mark
   as done"; both name their place in a tooltip (Nicolas, 2026-11-02). */
.back-icon {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.6rem;
  height: 1.6rem;
  margin-left: -0.4rem;
  border-radius: 999px;
  color: var(--quiet);
  opacity: 0.75;
  transition: color 0.2s, opacity 0.2s, background-color 0.2s;
}

.back-icon .icon {
  width: 16px;
  height: 16px;
  transition: transform 0.2s var(--ease);
}

.back-icon:hover,
.back-icon:focus-visible {
  background: var(--paper-2);
  color: var(--ink);
  opacity: 1;
}

.back-icon:hover .icon {
  transform: translateX(-2px);
}

.back-button {
  position: relative;
}

.back-button .icon {
  width: 18px;
  height: 18px;
}

.back-icon::after,
.back-button::after {
  content: attr(data-tip);
  position: absolute;
  left: 50%;
  top: calc(100% + 0.4rem);
  z-index: 5;
  padding: 0.3rem 0.55rem;
  border-radius: 0.4rem;
  background: var(--ink);
  color: var(--paper);
  font: 500 0.78rem/1.3 var(--sans);
  letter-spacing: normal;
  text-transform: none;
  white-space: nowrap;
  pointer-events: none;
  opacity: 0;
  transform: translate(-50%, -2px);
  transition: opacity 0.15s, transform 0.15s var(--ease);
}

/* the button stands at the end of the page, so its tooltip opens upwards */
.back-button::after {
  top: auto;
  bottom: calc(100% + 0.4rem);
  transform: translate(-50%, 2px);
}

:is(.back-icon, .back-button):is(:hover, :focus-visible)::after {
  opacity: 1;
  transform: translate(-50%, 0);
  transition-delay: 0.3s;
}

/* --- areas: one accent colour each ---------------------------------------------- */

:root {
  --cs: #009ee3;
  --cs-ink: #0072a8;
  /* data science: a darker Hochschulblau (Nicolas, 2026-10-30) */
  --ds: #0b6aa2;
  --ds-ink: #08527e;
  /* artificial intelligence: pink, the colour data science had before */
  --ai: #db2777;
  --ai-ink: #be185d;
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme='light']) {
    --cs-ink: #5cc6f2;
    --ds: #3d8fc4;
    --ds-ink: #8ab9e0;
    --ai-ink: #f472b6;
  }
}
:root[data-theme='dark'] {
  --cs-ink: #5cc6f2;
  --ds: #3d8fc4;
  --ds-ink: #8ab9e0;
  --ai-ink: #f472b6;
}

/* The page of an area takes its colour; so does each card on the entrance.
   Artificial intelligence also gets a plain red for errors, apart from its pink. */
:root[data-area='ai'],
[data-area='ai']:not(:root) {
  --accent: var(--ai);
  --accent-ink: var(--ai-ink);
  --error: #dc2626;
}

:root[data-area='ds'],
[data-area='ds']:not(:root) {
  --accent: var(--ds);
  --accent-ink: var(--ds-ink);
}

:root[data-area='cs'],
[data-area='cs']:not(:root) {
  --accent: var(--cs);
  --accent-ink: var(--cs-ink);
}

/* The title opens a menu with the other area, in that area's colour. */
.area-menu {
  position: relative;
}

.area-menu > summary {
  list-style: none;
  cursor: pointer;
  border-radius: 0.5rem;
}

.area-menu > summary::-webkit-details-marker {
  display: none;
}

.area-menu > summary .icon {
  color: var(--quiet);
  transition: transform 0.25s var(--ease);
}

.area-menu[open] > summary .icon {
  transform: rotate(180deg);
}

.area-menu-panel {
  position: absolute;
  z-index: 20;
  top: calc(100% + 0.6rem);
  left: 0;
  display: grid;
  gap: 0.25rem;
  width: min(22rem, calc(100vw - 2rem));
  padding: 0.5rem;
  border: 1px solid var(--rule);
  border-radius: 0.9rem;
  background: var(--paper);
  box-shadow: 0 12px 32px rgb(0 0 0 / 0.18);
  animation: rise 0.2s var(--ease);
}

.area-option {
  display: grid;
  gap: 0.2rem;
  padding: 0.75rem 0.85rem;
  border-radius: 0.6rem;
  border-left: 4px solid var(--accent);
  color: var(--ink);
  text-decoration: none;
  transition: background 0.2s;
}

.area-option:hover {
  background: color-mix(in srgb, var(--accent) 10%, transparent);
}

.area-option-name {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  font-weight: 700;
  color: var(--accent-ink);
}

.area-option-text {
  color: var(--quiet);
  font-size: 0.88rem;
  line-height: 1.45;
}

.area-home {
  padding: 0.5rem 0.85rem;
  border-radius: 0.6rem;
  color: var(--quiet);
  font-size: 0.88rem;
  text-decoration: none;
}

.area-home:hover {
  color: var(--ink);
  background: var(--paper-2);
}

.area-dot {
  width: 0.65rem;
  height: 0.65rem;
  border-radius: 50%;
  background: var(--accent);
}

.hero-empty {
  align-self: center;
  color: var(--quiet);
}

/* the entrance */
.chooser {
  max-width: 64rem;
  padding-top: 4rem;
}

.chooser-tools {
  display: flex;
  justify-content: flex-end;
  align-items: center;
  gap: 0.25rem;
  margin: -2.5rem 0 2rem;
}

.chooser-title {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  font-size: clamp(1.8rem, 4vw, 2.6rem);
}

.chooser-title .mark {
  color: var(--cs);
}

.chooser-areas {
  list-style: none;
  margin: 2rem 0 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(16rem, 100%), 1fr));
  gap: 1.25rem;
}

.area-card {
  position: relative;
  display: grid;
  gap: 0.6rem;
  height: 100%;
  padding: 1.75rem 1.5rem 3.25rem;
  border: 1px solid var(--rule);
  border-top: 6px solid var(--accent);
  border-radius: 1rem;
  color: var(--ink);
  text-decoration: none;
  transition: border-color 0.2s, transform 0.2s var(--ease);
}

.area-card:hover {
  border-color: var(--accent);
  transform: translateY(-2px);
}

.area-name {
  font-size: 1.5rem;
  font-weight: 700;
  color: var(--accent-ink);
}

.area-summary {
  color: var(--quiet);
}

.area-go {
  position: absolute;
  right: 1.5rem;
  bottom: 1.1rem;
  font-size: 1.4rem;
  color: var(--accent);
  transition: transform 0.2s var(--ease);
}

.area-card:hover .area-go {
  transform: translateX(4px);
}

/* Below the cards the content of all areas. Each row carries the colour of
   its area as a bar, half and half for an item in two; the type icons stay
   quiet, so that the colour means only the area. */
.chooser-items {
  margin-top: 3.5rem;
}

.row.has-area::before {
  content: '';
  position: absolute;
  left: 0;
  top: 0.7rem;
  bottom: 0.7rem;
  width: 4px;
  border-radius: 2px;
  background: linear-gradient(var(--mark-a) 50%, var(--mark-b) 50%);
}

.row.has-area .row-link {
  padding-left: 2.6rem;
}

.row.has-area .row-link > .type-icon {
  left: 0.75rem;
  color: var(--quiet);
}

[data-view='cards'] .switchable .row.has-area::before {
  inset: 0 0 auto;
  width: auto;
  height: 6px;
  border-radius: 0;
  background: linear-gradient(to right, var(--mark-a) 50%, var(--mark-b) 50%);
}

[data-view='cards'] .switchable .row.has-area .row-link {
  padding-top: 1.4rem;
  padding-left: 1.1rem;
}

/* --- subtitle of a course ---------------------------------------------------------- */

.page-head .subtitle {
  margin: 0.4rem 0 0;
  font-size: 1.15rem;
  font-weight: 600;
  color: var(--accent-ink);
}

.row-subtitle {
  color: var(--accent-ink);
  font-size: 0.92rem;
  font-weight: 600;
}

[data-view='cards'] .switchable .row-subtitle {
  order: 0;
}

/* --- the frame of every demonstrator (client/demo-frame.js) ---------------------------- */

/* Above each demonstrator two quiet buttons, for its shortcuts and for full
   screen. In full screen the demonstrator keeps the width it had on the page
   (--demo-width) and is scaled up with zoom, so the layout stays the same,
   only larger. */
.demo {
  position: relative;
  margin: 1.5rem 0;
}

/* the frame takes the focus for the shortcuts, without a ring of its own */
.demo:focus {
  outline: none;
}

.demo > :last-child {
  margin: 0;
}

/* A text that changes in a place of a fixed size (demo-frame.js, steady()):
   all its forms lie in one grid cell, only the live one is seen. */
.demo-steady {
  display: grid;
}

.demo-steady > * {
  grid-area: 1 / 1;
  visibility: hidden;
}

.demo-steady > .demo-steady-live {
  visibility: visible;
}

.demo-bar {
  display: flex;
  justify-content: flex-end;
  gap: 0.25rem;
  margin-bottom: 0.35rem;
}

.demo-tool {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  padding: 0.2rem 0.5rem;
  border: 1px solid transparent;
  border-radius: 0.4rem;
  background: none;
  color: var(--quiet);
  font: inherit;
  font-size: 0.8rem;
  cursor: pointer;
  transition: color 0.15s, border-color 0.15s;
}

.demo-tool:hover,
.demo-tool:focus-visible,
.demo-tool[aria-expanded='true'] {
  border-color: var(--rule);
  color: var(--accent-ink);
}

/* The shortcuts: a small card under the buttons, over the demonstrator. */
.demo-keys {
  position: absolute;
  top: 2.1rem;
  right: 0;
  z-index: 2;
  width: min(24rem, 100%);
  padding: 0.85rem 1rem;
  border: 1px solid var(--rule);
  border-radius: 0.6rem;
  background: var(--paper);
  box-shadow: 0 8px 24px rgb(0 0 0 / 0.18);
  font-size: 0.85rem;
}

.prose .demo-keys p {
  margin: 0;
}

.prose .demo-keys-head {
  margin-bottom: 0.5rem;
  font-weight: 650;
}

.demo-keys dl {
  display: grid;
  grid-template-columns: max-content 1fr;
  gap: 0.35rem 0.85rem;
  margin: 0;
}

.demo-keys dt {
  white-space: nowrap;
  color: var(--quiet);
}

.demo-keys dd {
  margin: 0;
}

.demo-keys kbd {
  display: inline-block;
  min-width: 1.6em;
  padding: 0.05rem 0.35rem;
  border: 1px solid var(--rule);
  border-bottom-width: 2px;
  border-radius: 0.3rem;
  background: var(--paper-2);
  color: var(--ink);
  font: 600 0.8rem/1.4 var(--mono);
  text-align: center;
}

.prose .demo-keys-note {
  margin-top: 0.6rem;
  color: var(--quiet);
  font-size: 0.8rem;
}

/* on the projector the card is read from the back of the room */
.demo:fullscreen > .demo-keys,
.demo.is-fullscreen > .demo-keys {
  top: 3.5rem;
  right: 0.75rem;
  width: min(36rem, 90vw);
  font-size: 1.2rem;
}

.demo:fullscreen .demo-keys kbd,
.demo.is-fullscreen .demo-keys kbd,
.demo:fullscreen .demo-keys-note,
.demo.is-fullscreen .demo-keys-note {
  font-size: 1.1rem;
}

.demo:fullscreen,
.demo.is-fullscreen {
  display: flex;
  align-items: center;
  justify-content: center;
  margin: 0;
  padding: 1rem;
  overflow: auto;
  background: var(--paper);
  color: var(--ink);
}

/* without the Fullscreen API: a layer over the page */
.demo.is-fullscreen {
  position: fixed;
  inset: 0;
  z-index: 1000;
}

.has-fullscreen-demo {
  overflow: hidden;
}

.demo:fullscreen > .demo-bar,
.demo.is-fullscreen > .demo-bar {
  position: absolute;
  top: 0.75rem;
  right: 0.75rem;
  margin: 0;
}

.demo:fullscreen > :last-child,
.demo.is-fullscreen > :last-child {
  flex: none;
  width: var(--demo-width);
}

/* --- focus mode (client/focus.js) ---------------------------------------------------- */

/* A code box, a result table or a quiz alone on the screen: its parts keep
   the width they have on the page and are enlarged to the width of the
   screen, and what does not fit scrolls. It starts at the top, so that
   nothing moves when an explanation or an output opens below. */
/* The button is a small, quiet symbol in the header row of what it shows,
   like the tools of a demonstrator (.demo-tool). */
.focus-toggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.9rem;
  height: 1.9rem;
  padding: 0;
  border: 1px solid transparent;
  border-radius: 0.4rem;
  background: none;
  color: var(--quiet);
  cursor: pointer;
  transition: color 0.15s, border-color 0.15s;
}

.focus-toggle:hover,
.focus-toggle:focus-visible {
  border-color: var(--rule);
  color: var(--accent-ink);
}

.runner-bar .focus-toggle {
  margin-left: auto;
}

.table-tools,
.quiz-tools {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 0.5rem;
}

.table-tools {
  margin: -0.5rem 0 1.25rem;
}

.table-tools .button {
  min-height: 2rem;
  padding: 0 0.8rem;
  font-size: 0.85rem;
}

/* in a quiz of several questions, the symbol ends the row of Back, the
   position and Next */
.quiz-bar:has(> .quiz-tools) {
  grid-template-columns: auto 1fr auto auto;
}

.quiz-bar > .quiz-tools {
  margin-left: -0.25rem;
}

.focusable.in-focus {
  display: flex;
  flex-direction: column;
  margin: 0;
  padding: 3.5rem 0 2.5rem;
  overflow: auto;
  border: 0;
  border-radius: 0;
  box-shadow: none;
  background: var(--paper);
  color: var(--ink);
}

.focusable.in-focus > * {
  flex: none;
  box-sizing: border-box;
  width: var(--focus-width);
  margin-inline: auto;
  zoom: var(--focus-zoom, 1);
}

/* the header row of a quiz keeps the size of the first fit (client/focus.js) */
.focusable.in-focus > .quiz-bar {
  width: var(--steady-width, var(--focus-width));
  zoom: var(--steady-zoom, var(--focus-zoom, 1));
}

/* a table only as wide as its columns, in the middle */
.table-box.in-focus > table {
  width: max-content;
  max-width: var(--focus-width);
}

/* the buttons of a table or a quiz stay in the corner, at their usual size */
.in-focus > .table-tools,
.in-focus > .quiz-tools {
  position: fixed;
  top: 0.75rem;
  right: 0.75rem;
  width: auto;
  margin: 0;
  zoom: 1;
}

/* the suggestions of a code box in focus (client/complete.js), as large as the code */
.focusable.in-focus > .complete {
  width: auto;
  margin: 0;
}

/* a copy of what must fit, to measure how large a quiz or a code box may be
   in focus (client/quiz.js, client/runner.js) */
.focusable.in-focus > .focus-measure {
  position: absolute;
  visibility: hidden;
  display: flow-root;
  zoom: 1;
}

/* The stage of a presentation of code boxes (client/present.js): the heading
   and the paragraph above a code box and the box itself, scaled together like
   an element in focus, with small buttons to the previous and the next box in
   the corner. Not shown until a box is presented; it covers the page as a
   layer, and full screen, where the browser grants it, comes on top. */
.code-stage {
  display: none;
}

.code-stage.is-open {
  position: fixed;
  inset: 0;
  z-index: 1000;
  display: flex;
  flex-direction: column;
  padding: 3rem 0 3.5rem;
  overflow: auto;
  /* room for the scrollbar from the start, so that a tall box does not shift */
  scrollbar-gutter: stable;
  background: var(--paper);
  color: var(--ink);
}

.code-stage > .stage-context,
.code-stage > .stage-box {
  flex: none;
  box-sizing: border-box;
  width: var(--focus-width);
  max-width: none;
  margin-inline: auto;
  zoom: var(--focus-zoom, 1);
}

.code-stage > .stage-context > :first-child {
  margin-top: 0;
}

/* every heading at one size, whatever its level on the page */
.code-stage > .stage-context > :is(h1, h2, h3, h4) {
  font-size: 1.3rem;
}

.code-stage > .stage-context > :last-child {
  margin-bottom: 1rem;
}

.code-stage .runner {
  position: relative;
  margin: 0;
}

/* On the stage, the output of a code box has a fixed room of eight lines,
   which counts when the box is fitted; longer output scrolls inside it. */
.runner.on-stage .runner-output {
  box-sizing: border-box;
  height: calc(8 * 1.55 * 0.92rem + 1.8rem);
  overflow: auto;
}

.stage-nav {
  position: fixed;
  top: 0.75rem;
  right: 0.75rem;
  display: flex;
  align-items: center;
  gap: 0.25rem;
  font: 0.8rem/1 var(--mono);
  color: var(--quiet);
}

.stage-nav .focus-toggle:disabled {
  opacity: 0.3;
  cursor: default;
}

.stage-position {
  min-width: 3.5rem;
  text-align: center;
  font-variant-numeric: tabular-nums;
}

/* without the Fullscreen API: a layer over the page */
.focusable.is-focus {
  position: fixed;
  inset: 0;
  z-index: 1000;
}

.has-focus {
  overflow: hidden;
}

/* --- the dplyr demonstrator ------------------------------------------------------------ */

.verb-demo {
  margin: 1.5rem 0;
  padding: 1rem;
  border: 1px solid var(--rule);
  border-radius: 0.75rem;
}

.verb-demo-buttons {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
}

.chip-button {
  min-height: 2rem;
  padding: 0 0.75rem;
  border: 1px solid var(--rule);
  border-radius: 999px;
  background: none;
  color: var(--ink);
  font: 600 0.85rem/1 var(--mono);
  cursor: pointer;
  transition: border-color 0.2s, background 0.2s;
}

.chip-button:hover {
  border-color: var(--accent);
}

.chip-button[aria-pressed='true'] {
  border-color: var(--accent);
  background: color-mix(in srgb, var(--accent) 14%, transparent);
  color: var(--accent-ink);
}

.prose .verb-demo-code {
  margin: 0.9rem 0 0.75rem;
}

.verb-demo-frame {
  overflow: auto hidden;
}

.prose .verb-demo-table {
  display: table;
  margin: 0;
  overflow: visible;
  border-collapse: collapse;
  font: 400 0.88rem/1.4 var(--mono);
}

.prose .verb-demo-table th,
.prose .verb-demo-table td {
  padding: 0.3rem 0.9rem 0.3rem 0.4rem;
  border-bottom: 1px solid var(--rule);
  text-align: left;
  white-space: nowrap;
}

.prose .verb-demo-table th {
  font-weight: 700;
  color: var(--accent-ink);
}

.verb-demo-table td.na {
  color: var(--quiet);
  font-style: italic;
}

.prose .verb-demo-foot {
  margin: 0.75rem 0 0;
  font-size: 0.88rem;
  color: var(--quiet);
}

/* The game with three doors (client/monty-hall.js): three doors that swing
   open in place, the choice between staying and switching, a bar per
   decision for how often it won, and the rounds as R code on request. Every
   part that changes during a round has a place of a fixed size, so that
   nothing moves while playing. */
.monty-hall {
  margin: 1.5rem 0;
  padding: 1rem;
  border: 1px solid var(--rule);
  border-radius: 0.75rem;
}

.prose .mh-status {
  min-height: 1lh;
  margin: 0 0 1rem;
  font-weight: 650;
}

.mh-stage {
  display: grid;
  grid-template-columns: minmax(0, 22.5rem) minmax(0, 1fr);
  gap: 1.25rem 2rem;
  align-items: start;
}

.mh-doors {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 7rem));
  gap: 0.75rem;
}

/* the doorway: what is behind the door, and the door itself on top */
.mh-door {
  position: relative;
  aspect-ratio: 3 / 4;
  padding: 0;
  border: 2px solid var(--rule);
  border-radius: 0.5rem 0.5rem 0.2rem 0.2rem;
  background: color-mix(in srgb, var(--ink) 10%, var(--paper));
  color: var(--ink);
  perspective: 30rem;
  cursor: pointer;
  transition: border-color 0.2s, box-shadow 0.2s;
}

.mh-behind {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  font-size: 2.4rem;
}

.mh-panel {
  position: absolute;
  inset: -2px;
  display: grid;
  place-items: center;
  border: 2px solid color-mix(in srgb, var(--accent) 45%, var(--rule));
  border-radius: inherit;
  background: color-mix(in srgb, var(--accent) 14%, var(--paper));
  font: 700 1.6rem/1 var(--mono);
  transform-origin: left center;
  transition: transform 0.7s var(--ease) var(--delay, 0s), border-color 0.2s;
}

/* the handle */
.mh-panel::after {
  content: '';
  position: absolute;
  top: 55%;
  right: 0.6rem;
  width: 0.45rem;
  height: 0.45rem;
  border-radius: 50%;
  background: color-mix(in srgb, var(--accent) 60%, var(--ink));
}

.mh-door:hover:not(:disabled) .mh-panel {
  border-color: var(--accent);
}

.mh-door[data-chosen='true'] {
  border-color: var(--accent);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 30%, transparent);
}

.mh-door[data-chosen='true'] .mh-panel {
  border-color: var(--accent);
}

.mh-door[data-open='true'] .mh-panel {
  z-index: 1;
  transform: rotateY(78deg);
}

.mh-door[data-open='true'] .mh-behind {
  animation: mh-reveal 0.45s ease-out var(--delay, 0s) backwards;
}

.mh-door:disabled {
  cursor: default;
}

@keyframes mh-reveal {
  from {
    opacity: 0;
    transform: scale(0.6);
  }
}

/* stay and switch side by side, next round in the place of stay; buttons
   that are not needed keep their place and are only not shown */
.mh-actions {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  grid-auto-rows: 2.75rem;
  gap: 0.5rem;
  margin-top: 1rem;
}

.mh-actions .button {
  justify-content: center;
  min-height: 0;
  padding: 0 0.75rem;
  line-height: 1.15;
  text-align: center;
}

.mh-actions [data-act='stay'],
.mh-actions [data-act='next'] {
  grid-area: 1 / 1;
}

.mh-actions [data-act='switch'] {
  grid-area: 1 / 2;
}

.mh-actions .is-gone {
  visibility: hidden;
}

.mh-stats {
  display: grid;
  gap: 0.7rem;
}

.prose .mh-stats-head {
  margin: 0;
  font-weight: 650;
}

.mh-stat {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: 0.3rem 0.75rem;
  font-size: 0.85rem;
}

.mh-stat-label {
  color: var(--quiet);
}

.mh-stat-value {
  font-variant-numeric: tabular-nums;
}

.mh-bar {
  grid-column: 1 / -1;
  height: 0.75rem;
  overflow: hidden;
  border: 1px solid var(--rule);
  border-radius: 999px;
  background: var(--paper-2);
}

.mh-bar-fill {
  display: block;
  width: 0;
  height: 100%;
  background: var(--done);
  transition: width 0.6s var(--ease);
}

.prose .mh-tools {
  display: flex;
  flex-wrap: wrap;
  gap: 0.25rem 1rem;
  margin: 0.25rem 0 0;
  font-size: 0.85rem;
}

.mh-link {
  padding: 0;
  border: none;
  background: none;
  color: var(--accent-ink);
  font: inherit;
  text-decoration: underline;
  cursor: pointer;
}

/* nothing to save before the first round */
.mh-link:disabled {
  color: var(--quiet);
  text-decoration: none;
  cursor: default;
}

.mh-record {
  margin-top: 1rem;
}

.prose .mh-record pre {
  margin: 0;
}

/* on a phone the statistics go under the doors, and the status and a button
   may need two lines, so the room for two is kept from the start */
@media (max-width: 40rem) {
  .mh-stage {
    grid-template-columns: minmax(0, 1fr);
  }

  .prose .mh-status {
    min-height: 2lh;
  }

  .mh-actions {
    grid-auto-rows: 3.25rem;
  }
}

@media (prefers-reduced-motion: reduce) {
  .mh-panel,
  .mh-bar-fill,
  .mh-door[data-open='true'] .mh-behind {
    transition: none;
    animation: none;
  }
}

/* The table to record observations in (client/data-table.js): its size and
   "start over" above, a table that scrolls sideways when it grows wider than
   the text, a name and a type in the head of every column, a quiet + for
   another column and another row, and below it the two exports with the R
   code that reads them. A cell that does not fit its type gets a yellow
   outline, which warns without blocking. */
.data-table {
  margin: 1.5rem 0;
  padding: 1rem;
  border: 1px solid var(--rule);
  border-radius: 0.75rem;
}

.dt-tools,
.dt-export {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem 0.75rem;
}

.data-table .button:disabled {
  opacity: 0.45;
  cursor: default;
  pointer-events: none;
}

.dt-size {
  color: var(--quiet);
  font-size: 0.85rem;
  font-variant-numeric: tabular-nums;
}

.dt-link {
  margin-left: auto;
  padding: 0;
  border: none;
  background: none;
  color: var(--accent-ink);
  font: inherit;
  font-size: 0.85rem;
  text-decoration: underline;
  cursor: pointer;
}

.prose .dt-empty {
  margin: 1rem 0 0;
  color: var(--quiet);
}

.dt-scroll {
  margin-top: 1rem;
  overflow-x: auto;
}

.prose .dt-table {
  display: table;
  margin: 0;
  overflow: visible;
  border-collapse: collapse;
  font-size: 0.9rem;
}

.prose .dt-table th,
.prose .dt-table td {
  padding: 0.3rem 0.35rem;
  border-bottom: 1px solid var(--rule);
  vertical-align: top;
}

.prose .dt-table thead th {
  border-bottom: 2px solid var(--rule);
  font-weight: 400;
}

/* the row numbers stay in view when the table scrolls sideways */
.prose .dt-table .dt-corner,
.prose .dt-table .dt-rowhead {
  position: sticky;
  left: 0;
  z-index: 1;
  background: var(--paper);
  color: var(--quiet);
  font: 400 0.8rem/1 var(--mono);
  text-align: right;
  white-space: nowrap;
}

.prose .dt-table .dt-corner {
  vertical-align: bottom;
  padding-bottom: 0.6rem;
}

.dt-rowhead .dt-index {
  display: inline-block;
  min-width: 1.5em;
  padding-top: 0.55rem;
}

.dt-col {
  min-width: 10rem;
}

.dt-head,
.dt-type-line {
  display: flex;
  align-items: center;
  gap: 0.25rem;
}

.dt-type-line {
  margin-top: 0.3rem;
}

.dt-name,
.dt-cell,
.dt-type {
  min-width: 0;
  padding: 0.3rem 0.45rem;
  border: 1px solid var(--rule);
  border-radius: 0.35rem;
  background: var(--paper-2);
  color: inherit;
  font: inherit;
}

.dt-name {
  flex: 1;
  width: 8rem;
  font: 650 0.9rem/1.3 var(--mono);
}

.dt-type {
  flex: 1;
  width: 7rem;
  font-size: 0.8rem;
}

.dt-cell {
  width: 100%;
  min-width: 6rem;
  font-family: var(--mono);
}

.dt-name:focus,
.dt-cell:focus,
.dt-type:focus {
  outline: 2px solid var(--accent);
  outline-offset: 1px;
}

.dt-name.is-warn {
  border-color: var(--warn);
}

.dt-cell.is-misfit {
  border-color: var(--warn);
  box-shadow: inset 0 0 0 1px var(--warn);
  background: color-mix(in srgb, var(--warn) 12%, var(--paper-2));
}

.dt-abbr {
  color: var(--accent-ink);
  font: 400 0.8rem/1 var(--mono);
  white-space: nowrap;
}

.dt-abbr.is-inferred {
  color: var(--quiet);
  font-style: italic;
}

.dt-note,
.dt-levels {
  max-width: 14rem;
  margin-top: 0.3rem;
  color: var(--quiet);
  font-size: 0.78rem;
  line-height: 1.35;
  white-space: normal;
}

.dt-note.is-warn {
  color: var(--ink);
  padding-left: 0.4rem;
  border-left: 3px solid var(--warn);
}

.dt-note .dt-link {
  margin-left: 0.25rem;
  font-size: inherit;
}

.dt-levels {
  font-family: var(--mono);
}

.dt-delete {
  display: inline-grid;
  place-items: center;
  flex: none;
  width: 1.75rem;
  height: 1.75rem;
  padding: 0;
  border: none;
  border-radius: 0.35rem;
  background: none;
  color: var(--quiet);
  cursor: pointer;
  vertical-align: top;
}

.dt-delete:hover,
.dt-delete:focus-visible {
  background: color-mix(in srgb, var(--error) 12%, transparent);
  color: var(--error);
}

/* A quiet + right of the last column and below the last row. It shows when
   the pointer or the focus is in the table, always on touch screens, and
   always when the table has no column left. */
.prose .dt-table .dt-add,
.prose .dt-table .dt-add-row {
  border-bottom: none;
}

.prose .dt-table thead .dt-add {
  vertical-align: top;
  padding-top: 0.45rem;
}

.dt-plus {
  display: inline-grid;
  place-items: center;
  width: 1.75rem;
  height: 1.75rem;
  padding: 0;
  border: 1px dashed var(--rule);
  border-radius: 50%;
  background: none;
  color: var(--quiet);
  cursor: pointer;
  opacity: 0;
  transition: opacity 0.15s, color 0.15s, border-color 0.15s;
}

.dt-scroll:hover .dt-plus,
.data-table:focus-within .dt-plus,
.data-table.is-blank .dt-plus {
  opacity: 1;
}

.dt-plus:hover,
.dt-plus:focus-visible {
  border-style: solid;
  border-color: var(--accent);
  color: var(--accent-ink);
}

@media (hover: none) {
  .dt-plus {
    opacity: 1;
  }
}

@media (prefers-reduced-motion: reduce) {
  .dt-plus {
    transition: none;
  }
}

/* In full screen the table sets the width: everything else wraps within it
   (inline-size containment leaves them out of the measure), the table no
   longer scrolls, and the frame scales the whole to fit the screen. */
.demo:fullscreen > .data-table,
.demo.is-fullscreen > .data-table {
  width: max-content;
  min-width: 34rem;
}

.demo:fullscreen .data-table > :not(.dt-scroll),
.demo.is-fullscreen .data-table > :not(.dt-scroll) {
  contain: inline-size;
}

.demo:fullscreen .dt-scroll,
.demo.is-fullscreen .dt-scroll {
  overflow: visible;
}

/* Full screen is for filling in, so saving and the R code below the table
   stay on the page and leave the room to the table. */
.demo:fullscreen .data-table > :is(.dt-export, .dt-code-head, .code-block),
.demo.is-fullscreen .data-table > :is(.dt-export, .dt-code-head, .code-block) {
  display: none;
}

.prose .dt-misfits {
  margin: 0.75rem 0 0;
  padding-left: 0.5rem;
  border-left: 3px solid var(--warn);
  font-size: 0.85rem;
}

.dt-export {
  margin-top: 1.25rem;
}

.prose .dt-code-head {
  margin: 1rem 0 0.4rem;
  font-size: 0.85rem;
  color: var(--quiet);
}

.prose .data-table .code-block {
  margin: 0;
}

@media (max-width: 40rem) {
  .data-table {
    padding: 0.75rem;
  }

  .dt-export .button {
    flex: 1 1 12rem;
    justify-content: center;
  }
}

/* The byte switchboard (client/byte-switchboard.js): eight switches with
   their place values, the sum, and the byte in four readings. A switch that
   is on takes the accent colour, like a chosen quiz option. */
.byte-switchboard {
  margin: 1.5rem 0;
  padding: 1rem;
  border: 1px solid var(--rule);
  border-radius: 0.75rem;
}

.bs-bits {
  display: grid;
  grid-template-columns: repeat(8, minmax(0, 1fr));
  gap: 0.4rem;
}

.bs-col {
  display: grid;
  justify-items: center;
  gap: 0.15rem;
  font-family: var(--mono);
}

.bs-power {
  font-size: 0.75rem;
  color: var(--quiet);
}

.bs-weight {
  font-size: 0.9rem;
  color: var(--quiet);
  transition: color 0.2s;
}

.bs-col.is-on .bs-weight {
  color: var(--accent-ink);
  font-weight: 700;
}

.bs-bit {
  width: 100%;
  aspect-ratio: 1;
  margin-top: 0.25rem;
  border: 2px solid var(--rule);
  border-radius: 0.5rem;
  background: var(--paper-2);
  color: var(--quiet);
  font: 700 clamp(1.1rem, 4vw, 1.7rem) / 1 var(--mono);
  cursor: pointer;
  touch-action: none;
  transition: background 0.15s, border-color 0.15s, color 0.15s, transform 0.1s;
}

.bs-bit:hover {
  border-color: var(--accent);
}

.bs-bit[aria-pressed='true'] {
  border-color: var(--accent);
  background: var(--accent);
  color: #fff;
  transform: translateY(2px);
}

.bs-bit.is-flipped {
  animation: bs-flip 0.4s var(--ease);
}

@keyframes bs-flip {
  from {
    box-shadow: 0 0 0 4px color-mix(in srgb, var(--warn) 55%, transparent);
  }
}

.prose .bs-sum {
  min-height: 1.5em;
  margin: 1rem 0;
  font: 650 1.1rem var(--mono);
  color: var(--accent-ink);
  text-align: center;
}

/* Each reading takes the width it needs and the gaps are equal, so the short
   decimal and hex values do not stand in a column as wide as the binary one;
   the text takes the rest, its note may wrap there. The values reserve room
   for their longest form, so counting does not move the columns. */
.bs-outputs {
  display: grid;
  grid-template-columns: max-content max-content max-content minmax(8rem, 1fr);
  gap: 0.75rem 2.5rem;
  padding-top: 1rem;
  border-top: 1px solid var(--rule);
}

.bs-out {
  display: grid;
  align-content: start;
  gap: 0.1rem;
}

.bs-label,
.bs-note {
  font-size: 0.8rem;
  color: var(--quiet);
}

.bs-value {
  font: 700 1.6rem/1.2 var(--mono);
}

.bs-value[data-out='bin'] {
  min-width: 9ch;
}

/* Decimal, hex and text are fields that look like the other readings; a
   line under them shows on hover that they can be typed in, and the field
   being edited gets a light ground. */
.bs-field {
  box-sizing: content-box;
  width: 3ch;
  margin: 0 -0.2rem;
  padding: 0 0.2rem;
  border: 0;
  border-radius: 4px;
  background: transparent;
  color: inherit;
  box-shadow: inset 0 -2px 0 transparent;
  cursor: text;
  transition: box-shadow 0.15s, background-color 0.15s;
}

.bs-field[data-out='hex'],
.bs-field[data-out='text'] {
  width: 2ch;
}

.bs-field:hover {
  box-shadow: inset 0 -2px 0 var(--rule);
}

.bs-field:focus {
  outline: none;
  background: color-mix(in srgb, var(--accent) 12%, transparent);
  box-shadow: inset 0 -2px 0 var(--accent);
}

.bs-field[aria-invalid='true'] {
  background: color-mix(in srgb, var(--warn) 18%, transparent);
  box-shadow: inset 0 -2px 0 var(--warn);
}

/* on a phone two readings per row */
@media (max-width: 40rem) {
  .bs-outputs {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 0.75rem 1.25rem;
  }
}

.prose .bs-message {
  min-height: 1.5em;
  margin: 0.75rem 0 0;
  color: var(--quiet);
}

.bs-controls {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem;
  margin-top: 0.75rem;
}

@media (prefers-reduced-motion: reduce) {
  .bs-bit,
  .bs-bit.is-flipped {
    transition: none;
    animation: none;
  }
}

/* The photo and the audio digitiser (client/photo-digitiser.js,
   client/audio-digitiser.js): the same two cuts for a picture and for a
   sound, built alike. Above, what is digitised; in the middle, the original
   and its digital version; below, the arithmetic of the file size and the
   two knobs, one per cut. The texts that change keep their place
   (demo-frame.js, steady()), so nothing moves while the knobs turn. */
.photo-digitiser,
.audio-digitiser {
  margin: 1.5rem 0;
  padding: 1rem;
  border: 1px solid var(--rule);
  border-radius: 0.75rem;
}

.dg-label {
  font-size: 0.8rem;
  color: var(--quiet);
}

.dg-choices {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.4rem;
}

.dg-choices .dg-label {
  margin-right: 0.25rem;
}

.dg-calc {
  margin-top: 1rem;
  padding-top: 0.75rem;
  border-top: 1px solid var(--rule);
  font-family: var(--mono);
  text-align: center;
  color: var(--quiet);
}

.prose .dg-calc p {
  margin: 0;
}

.dg-calc .dg-size {
  font-size: 1.15rem;
  font-weight: 650;
  color: var(--ink);
}

.dg-calc .dg-time {
  color: var(--ink);
}

.dg-panel {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
  gap: 1rem 2rem;
  margin-top: 1rem;
  padding-top: 1rem;
  border-top: 1px solid var(--rule);
}

.dg-knob {
  display: grid;
  align-content: start;
  gap: 0.35rem;
}

.dg-range {
  width: 100%;
  margin: 0.35rem 0;
  accent-color: var(--accent);
  cursor: pointer;
}

.dg-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 0.35rem;
}

.prose .dg-reading {
  margin: 0;
  font-size: 0.85rem;
  color: var(--quiet);
}

.prose .dg-note {
  margin: 1rem 0 0;
  font-size: 0.75rem;
  color: var(--quiet);
}

/* the photo: the pictures to choose from as small buttons, then the original
   and the digital picture side by side, at every width */
.pd-motif {
  width: 2.75rem;
  height: 2.75rem;
  padding: 2px;
  border: 2px solid var(--rule);
  border-radius: 0.5rem;
  background: var(--paper-2);
  cursor: pointer;
  transition: border-color 0.2s;
}

.pd-motif img {
  display: block;
  width: 100%;
  height: 100%;
  border-radius: 0.3rem;
  object-fit: cover;
}

.pd-motif:hover,
.pd-motif[aria-pressed='true'] {
  border-color: var(--accent);
}

.pd-pair {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  align-items: end; /* a caption on two lines keeps the pictures level */
  gap: 1rem;
  margin-top: 1rem;
}

.pd-caption {
  display: block;
  margin-bottom: 0.35rem;
}

.pd-frame {
  position: relative;
}

/* the outline as a shadow, so that the canvas is exactly the picture and the
   cell over it lines up */
.pd-canvas {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 1;
  border-radius: 0.4rem;
  background: var(--paper-2);
  box-shadow: 0 0 0 1px var(--rule);
  cursor: crosshair;
}

.pd-pixelated {
  image-rendering: pixelated;
}

.pd-cell {
  position: absolute;
  display: none;
  box-sizing: border-box;
  border: 2px solid var(--warn);
  pointer-events: none;
}

.pd-cell.is-on {
  display: block;
}

/* the value of the pixel pointed at: where, its bits, its numbers; three
   lines from the start */
.prose .pd-pixel {
  margin: 0.75rem 0 0;
  font: 0.85rem/1.5 var(--mono);
  text-align: center;
  color: var(--quiet);
}

/* the first line is a steady place (a grid), the other two plain lines */
.pd-pixel > span {
  min-height: 1.5em;
}

.pd-pixel > span:not(.demo-steady) {
  display: block;
}

.pd-pixel [data-out='where'] {
  font-family: var(--sans);
}

.pd-pixel .pd-bits {
  font-weight: 650;
  color: var(--accent-ink);
}

/* the sound: a window of the wave, the analog one quiet, the digital
   staircase in the accent colour, as in the legend */
.prose .ad-window {
  margin: 1rem 0 0.35rem;
}

.ad-wave {
  display: block;
  width: 100%;
  height: 13rem;
  border: 1px solid var(--rule);
  border-radius: 0.5rem;
  background: var(--paper-2);
}

.prose .ad-legend {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 0.25rem 1.5rem;
  margin: 0.5rem 0 0;
  font-size: 0.8rem;
  color: var(--quiet);
}

.ad-swatch {
  display: inline-block;
  width: 1.1rem;
  height: 3px;
  margin-right: 0.4rem;
  border-radius: 2px;
  vertical-align: middle;
}

.ad-swatch-analog {
  background: var(--quiet);
}

.ad-swatch-digital {
  background: var(--accent);
}

.ad-players {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 0.5rem;
  margin-top: 0.75rem;
}

.audio-digitiser .chip-button:disabled {
  opacity: 0.5;
  cursor: wait;
}

@media (max-width: 40rem) {
  .photo-digitiser,
  .audio-digitiser {
    padding: 0.75rem;
  }

  .pd-pair {
    gap: 0.6rem;
  }

  .ad-wave {
    height: 10rem;
  }

  .dg-calc {
    font-size: 0.75rem;
  }

  .dg-calc .dg-size {
    font-size: 0.85rem;
  }

  .ad-players .button {
    flex: 1 1 100%;
    justify-content: center;
  }
}

/* The prefix trap (client/prefix-trap.js): the sender with the code words
   and the word on the left, the bits on the wire in the middle, the receiver
   with every reading it could cut on the right, and below them the code words
   that start others. The list of readings keeps room for all it shows, so
   that nothing below it moves while typing; on a phone the three stand one
   below the other. */
.prefix-trap {
  margin: 1.5rem 0;
  padding: 1rem;
  border: 1px solid var(--rule);
  border-radius: 0.75rem;
}

.pt-link {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 0.8fr) minmax(0, 1.3fr);
  gap: 1rem;
  align-items: start;
}

.prose .pt-link p {
  margin: 0;
}

.pt-side {
  padding: 0.75rem;
  border: 1px solid var(--rule);
  border-radius: 0.5rem;
  background: var(--paper-2);
}

.prose .pt-sub {
  margin: 0.6rem 0 0.35rem;
  font-size: 0.8rem;
  color: var(--quiet);
}

.pt-book {
  display: grid;
  grid-template-columns: auto auto minmax(0, 1fr);
  gap: 0.4rem 0.5rem;
  align-items: center;
}

.pt-ch {
  font: 1.1rem var(--mono);
}

.pt-eq {
  font-family: var(--mono);
  color: var(--quiet);
}

.pt-field {
  width: 100%;
  box-sizing: border-box;
  padding: 0.3rem 0.5rem;
  border: 1px solid var(--rule);
  border-radius: 0.35rem;
  background: var(--paper);
  color: var(--ink);
  font: 1.05rem var(--mono);
  letter-spacing: 0.12em;
}

.pt-field:focus {
  outline: 2px solid var(--accent);
  border-color: var(--accent);
}

.pt-field[aria-invalid='true'] {
  border-color: var(--error);
  color: var(--error);
}

.pt-wire {
  padding-top: 0.75rem;
  text-align: center;
}

.prose .pt-wire .pt-bits {
  margin-top: 1.4rem;
  font: 700 1.15rem/1.3 var(--mono);
  letter-spacing: 0.08em;
  color: var(--accent-ink);
  word-break: break-all;
}

.pt-line {
  position: relative;
  margin: 0.6rem 0;
  border-top: 1px solid var(--quiet);
}

.pt-line::after {
  content: '';
  position: absolute;
  top: -5px;
  right: -1px;
  border-top: 5px solid transparent;
  border-bottom: 5px solid transparent;
  border-left: 9px solid var(--quiet);
}

.prose .pt-note {
  font-size: 0.8rem;
  color: var(--quiet);
}

.prose .pt-verdict {
  margin: 0.35rem 0 0.6rem;
  font-weight: 650;
  color: var(--error);
}

.prefix-trap[data-result='one'] .pt-verdict {
  color: var(--done);
}

.prose .pt-readings {
  display: grid;
  gap: 0.35rem;
  align-content: start;
  /* room for the six readings it shows */
  min-height: calc(6 * 2.1rem + 5 * 0.35rem);
  margin: 0;
  padding: 0;
  list-style: none;
}

.prose .pt-readings li {
  display: flex;
  flex-wrap: wrap;
  gap: 0.15rem 0.75rem;
  align-items: baseline;
  justify-content: space-between;
  min-height: 2.1rem;
  box-sizing: border-box;
  margin: 0;
  padding: 0.3rem 0.6rem;
  border: 1px solid var(--rule);
  border-radius: 0.35rem;
  background: var(--paper);
  font-family: var(--mono);
}

.prefix-trap[data-result='one'] .pt-readings li {
  border-color: var(--done);
}

/* a long reading breaks between code words, never inside one, and the
   letters then move to a line of their own, aligned to the right */
.pt-chunk {
  display: inline-block;
  white-space: nowrap;
}

.pt-chunk.is-alt {
  color: var(--quiet);
}

.pt-chunk + .pt-chunk {
  margin-left: 0.4em;
}

.pt-read {
  margin-left: auto;
  white-space: nowrap;
  font-weight: 650;
  color: var(--accent-ink);
}

.pt-why {
  min-height: 3rem;
  margin-top: 1rem;
  font-size: 0.9rem;
  color: var(--quiet);
}

.prose .pt-why p,
.prose .pt-why ul {
  margin: 0;
}

.prose .pt-why li {
  margin: 0.15rem 0;
  font-family: var(--mono);
  color: var(--error);
}

.prose .pt-why .pt-free {
  color: var(--done);
}

.pt-end {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.75rem;
  margin-top: 0.75rem;
}

.prose .pt-end .dg-note {
  margin: 0;
}

@media (max-width: 40rem) {
  .pt-link {
    grid-template-columns: minmax(0, 1fr);
  }

  .prose .pt-wire .pt-bits {
    margin-top: 0.4rem;
  }
}

/* The hex viewer (client/hex-viewer.js): the choice between a typed text and
   a file, then the dump with the offset, the bytes in hex and the same bytes
   as characters, sixteen to a row and eight on a phone. The signature of the
   file type is blue, bytes from 80 up yellow and control characters grey; a
   selected byte is marked in both columns, together with the other bytes of
   its character. The dump always has sixteen rows, so the page does not move
   while paging. */
.hex-viewer {
  --hx-high: #b45309;
  margin: 1.5rem 0;
  padding: 1rem;
  border: 1px solid var(--rule);
  border-radius: 0.75rem;
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme='light']) .hex-viewer {
    --hx-high: #f5a623;
  }
}

:root[data-theme='dark'] .hex-viewer {
  --hx-high: #f5a623;
}

.hex-viewer.is-dragging {
  border-color: var(--accent);
  background: var(--paper-2);
}

.hx-source {
  display: grid;
  gap: 0.6rem;
}

.hx-text {
  width: 100%;
  box-sizing: border-box;
  padding: 0.4rem 0.6rem;
  border: 1px solid var(--rule);
  border-radius: 0.35rem;
  background: var(--paper);
  color: var(--ink);
  font: 1rem/1.4 var(--mono);
  resize: vertical;
}

.hx-text:focus {
  outline: 2px solid var(--accent);
  border-color: var(--accent);
}

.hx-drop {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem 0.75rem;
  min-height: 2.6rem;
  padding: 0.5rem 0.75rem;
  border: 1px dashed var(--quiet);
  border-radius: 0.35rem;
  color: var(--quiet);
  font-size: 0.9rem;
  cursor: pointer;
}

.hx-drop[hidden] {
  display: none;
}

.hx-file {
  max-width: 100%;
  font: inherit;
}

.prose .hx-summary,
.prose .hx-info {
  margin: 0.75rem 0 0.5rem;
  font-size: 0.9rem;
}

.prose .hx-info {
  min-height: 2.6em;
  color: var(--quiet);
}

.hx-dump {
  display: grid;
  gap: 0.1rem;
  padding: 0.6rem 0.75rem;
  overflow-x: auto;
  border: 1px solid var(--rule);
  border-radius: 0.5rem;
  background: var(--paper-2);
  font: 0.9rem/1.5 var(--mono);
}

.hx-dump:focus-visible {
  outline: 2px solid var(--accent);
}

.hx-row {
  display: flex;
  gap: 1.2ch;
  white-space: pre;
}

.hx-off {
  flex: none;
  color: var(--quiet);
}

/* eight bytes to a row, and a smaller type so that both columns fit a phone */
.hex-viewer.is-narrow .hx-dump {
  padding: 0.5rem;
  font-size: 0.78rem;
}

.hex-viewer.is-narrow .hx-row {
  gap: 1ch;
}

.hx-bytes {
  display: grid;
  grid-template-columns: repeat(var(--per-row, 16), 2ch);
  column-gap: 0.6ch;
  flex: none;
}

/* a little more room after every eighth byte, as in every hex editor */
.hx-byte:nth-child(8n):not(:last-child) {
  margin-right: 0.6ch;
}

.hx-chars {
  display: grid;
  grid-template-columns: repeat(var(--per-row, 16), 1ch);
  flex: none;
  padding-left: 0.6ch;
  border-left: 1px solid var(--rule);
}

.hx-byte,
.hx-char {
  border-radius: 0.2rem;
  text-align: center;
  cursor: pointer;
}

.hx-byte.is-empty,
.hx-char.is-empty {
  cursor: default;
}

.hx-byte.is-ctrl,
.hx-char.is-ctrl {
  color: var(--quiet);
}

.hx-byte.is-high,
.hx-char.is-high {
  color: var(--hx-high);
}

.hx-byte.is-magic,
.hx-char.is-magic {
  color: var(--accent-ink);
  font-weight: 700;
}

.hx-byte.is-mate,
.hx-char.is-mate {
  box-shadow: inset 0 0 0 1px var(--accent);
}

.hx-byte.is-sel,
.hx-char.is-sel {
  background: var(--accent);
  color: var(--paper);
}

.hx-pager {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  align-items: center;
  gap: 0.75rem;
  font-size: 0.85rem;
  color: var(--quiet);
}

.hx-range {
  text-align: center;
}

.hx-pager .chip-button:disabled {
  opacity: 0.4;
  cursor: default;
}

.prose .hx-legend {
  display: flex;
  flex-wrap: wrap;
  gap: 0.35rem 1.2rem;
  margin: 0.75rem 0 0;
}

.hx-key::before {
  content: '';
  display: inline-block;
  width: 0.7em;
  height: 0.7em;
  margin-right: 0.4em;
  border-radius: 0.15rem;
  background: currentColor;
}

.hx-key.is-magic {
  color: var(--accent-ink);
}

.hx-key.is-high {
  color: var(--hx-high);
}

.hx-key.is-ctrl {
  color: var(--quiet);
}

/* The question game (client/question-game.js): the sixteen faces as buttons,
   eight in a row and four on a phone, then the questions, the panel that
   writes out what the last answer gained, the bits collected and the log.
   It borrows the labels, chips and panel of the digitisers. */
.question-game {
  margin: 1.5rem 0;
  padding: 1rem;
  border: 1px solid var(--rule);
  border-radius: 0.75rem;
}

.qg-faces {
  display: grid;
  grid-template-columns: repeat(8, minmax(0, 1fr));
  gap: 0.4rem;
}

.qg-face {
  display: grid;
  gap: 0.2rem;
  padding: 0.2rem 0.2rem 0.15rem;
  border: 1px solid var(--rule);
  border-radius: 0.5rem;
  background: #000;
  color: #c9d1d9;
  font: 0.72rem/1.2 var(--mono);
  text-align: center;
  cursor: pointer;
  transition: border-color 0.2s, opacity 0.2s;
}

.qg-face img {
  display: block;
  width: 100%;
  height: auto;
  border-radius: 0.35rem;
}

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

.qg-face.is-out {
  opacity: 0.15;
  filter: grayscale(1);
  cursor: default;
}

.qg-face.is-found {
  border-color: var(--done);
  box-shadow: 0 0 0 2px var(--done);
}

.prose .qg-hint {
  margin: 1rem 0 0.5rem;
}

.question-game .chip-button:disabled {
  opacity: 0.35;
  cursor: default;
}

.qg-answer {
  display: grid;
  gap: 0.2rem;
}

.qg-meter {
  margin-top: 1rem;
}

.prose .qg-meter p {
  margin: 0 0 0.35rem;
  font-variant-numeric: tabular-nums;
}

.qg-track {
  height: 0.6rem;
  border: 1px solid var(--rule);
  border-radius: 999px;
  overflow: hidden;
}

.qg-fill {
  width: 0;
  height: 100%;
  background: var(--accent);
  transition: width 0.25s var(--ease);
}

.prose .qg-stats {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 0.75rem 1rem;
  margin: 1rem 0 0;
}

.qg-stats dt {
  font-size: 0.8rem;
  font-weight: 400;
  color: var(--quiet);
}

.prose .qg-stats dd {
  margin: 0;
  font: 650 1.5rem/1.2 var(--mono);
}

.prose .qg-stats .qg-note {
  font: 400 0.72rem/1.3 var(--sans);
  color: var(--quiet);
}

.prose .qg-log {
  height: 6.5rem;
  margin: 1rem 0 0;
  padding: 0.5rem 0 0;
  border-top: 1px solid var(--rule);
  overflow-y: auto;
  list-style: none;
  font: 0.8rem/1.5 var(--mono);
  color: var(--quiet);
}

.prose .qg-log li {
  margin: 0;
  padding: 0;
}

.prose .qg-log li:first-child {
  color: var(--ink);
}

.qg-end {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem 1rem;
  margin-top: 1rem;
}

.prose .qg-end .dg-note {
  margin: 0;
}

@media (max-width: 40rem) {
  .question-game {
    padding: 0.75rem;
  }

  .qg-faces {
    grid-template-columns: repeat(4, minmax(0, 1fr));
  }

  .prose .qg-stats {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

/* The folder tree and command line (client/shell-demo.js): the tree above,
   with "du bist hier" on the current folder and the way a typed path takes
   in the accent colour, the terminal below. Rows are 30px high and indented
   22px per level, the numbers shell-demo.js draws the lines with. Tree,
   terminal and the line below keep their height from the start. */
.shell-demo {
  margin: 1.5rem 0;
  padding: 1rem;
  border: 1px solid var(--rule);
  border-radius: 0.75rem;
}

.sd-top {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.75rem;
  margin-bottom: 0.75rem;
}

.sd-os {
  display: inline-flex;
  border: 1px solid var(--rule);
  border-radius: 999px;
  padding: 0.15rem;
}

.sd-os-button {
  padding: 0.25rem 0.85rem;
  border: 0;
  border-radius: 999px;
  background: none;
  color: var(--quiet);
  font: inherit;
  font-size: 0.9rem;
  cursor: pointer;
}

.sd-os-button[aria-pressed='true'] {
  background: var(--accent);
  color: #fff;
}

.sd-predict {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  font-size: 0.9rem;
  color: var(--quiet);
  cursor: pointer;
}

.sd-reset {
  margin-left: auto;
  min-height: 2rem;
  padding: 0 0.8rem;
  font-size: 0.85rem;
}

.sd-tree {
  position: relative;
  height: calc(var(--sd-rows, 10) * 30px + 0.5rem + 2px);
  overflow-y: auto;
  overscroll-behavior: contain;
  padding: 0.25rem 0;
  border: 1px solid var(--rule);
  border-radius: 0.5rem 0.5rem 0 0;
}

.sd-canvas {
  position: relative;
  min-height: 100%;
}

.sd-edges {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  pointer-events: none;
  transition: opacity 0.15s;
}

.sd-edges.is-moving {
  opacity: 0;
}

.sd-lines path,
.sd-route path {
  fill: none;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.sd-lines path {
  stroke: var(--rule);
  stroke-width: 1.5;
}

.sd-route path {
  stroke: var(--accent);
  stroke-width: 3;
}

.sd-here,
.sd-row {
  position: absolute;
  left: 0;
  right: 0;
  height: 30px;
  transform: translateY(calc(var(--i, 0) * 30px));
  transition: transform 0.3s var(--ease), opacity 0.3s;
}

.sd-here {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  padding-right: 0.75rem;
  /* yellow, as the current folder in the drawings of the lessons */
  background: color-mix(in srgb, var(--yellow) 14%, transparent);
  border-left: 3px solid var(--yellow);
  color: var(--yellow);
  font-size: 0.8rem;
}

.sd-row {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  width: fit-content;
  max-width: 100%;
  padding: 0 0.4rem 0 calc(8px + var(--depth, 0) * 22px);
  border: 0;
  border-radius: 0.3rem;
  background: none;
  color: var(--ink);
  font: 400 0.92rem/1 var(--mono);
  white-space: nowrap;
  cursor: pointer;
}

.sd-row:hover .sd-name,
.sd-row:focus-visible .sd-name {
  text-decoration: underline;
}

.sd-icon {
  flex: none;
  color: var(--quiet);
}

.sd-row.is-dir .sd-icon {
  color: var(--accent);
}

.sd-row.on-route .sd-name {
  color: var(--accent-ink);
  font-weight: 700;
}

.sd-row.is-target .sd-name,
.sd-row.gets-new .sd-name {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
  border-radius: 2px;
}

.sd-badge {
  margin-left: 0.4rem;
  font: 400 0.8rem/1 var(--sans);
  color: var(--accent-ink);
}

.sd-row.has-problem .sd-badge {
  color: var(--error);
}

.sd-row.is-entering,
.sd-row.is-leaving {
  opacity: 0;
}

.sd-row.is-new .sd-name {
  animation: sd-new 1.4s var(--ease);
}

@keyframes sd-new {
  from {
    background: color-mix(in srgb, var(--accent) 35%, transparent);
  }
}

.sd-term {
  height: calc(12 * 1.45 * 0.85rem + 1.5rem);
  overflow-y: auto;
  padding: 0.75rem 1rem;
  border: 1px solid var(--rule);
  border-top: 0;
  border-radius: 0 0 0.5rem 0.5rem;
  background: var(--paper-2);
  font: 400 0.85rem/1.45 var(--mono);
  cursor: text;
}

.sd-out {
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}

.sd-err {
  color: var(--error);
}

.sd-note {
  color: var(--quiet);
  font-family: var(--sans);
}

.sd-line {
  display: flex;
}

.sd-prompt {
  flex: none;
  white-space: pre;
  color: var(--quiet);
}

.sd-input {
  flex: 1;
  min-width: 4rem;
  padding: 0;
  border: 0;
  outline: 0;
  background: none;
  color: var(--ink);
  font: inherit;
}

.sd-status {
  min-height: calc(2 * 1.45em);
  margin: 0.6rem 0 0;
  color: var(--quiet);
  font-size: 0.9rem;
  line-height: 1.45;
}

.sd-show {
  margin-left: 0.4rem;
  padding: 0.1rem 0.6rem;
  border: 1px solid var(--accent);
  border-radius: 999px;
  background: none;
  color: var(--accent-ink);
  font: inherit;
  cursor: pointer;
}

@media (prefers-reduced-motion: reduce) {
  .sd-here,
  .sd-row,
  .sd-edges {
    transition: none;
  }
  .sd-row.is-new .sd-name {
    animation: none;
  }
}

/* Figures from the decks are drawn for a light page (stagekit figures.py
   --theme light); on a dark page they keep a light ground of their own
   inside their frame. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme='light']) .prose img[src$='.svg'] {
    background: #fff;
  }
}
:root[data-theme='dark'] .prose img[src$='.svg'] {
  background: #fff;
}

/* --- drawings on content pages (docs/figures.md) --------------------------
   stagekit's draw.js reads its colours from these names on :root, so the
   eight roles of a deck get the colours of the page: blue is the colour of
   the area, the ground is the page, and yellow, green and red keep their
   meaning in a shade that reads on light and on dark. */
:root {
  --bg: var(--paper);
  --white: var(--ink);
  --gray-light: #4a5259;
  --gray: #7d868f;
  --gray-dark: #c5ccd3;
  --blue: var(--accent-ink);
  --green: #15803d;
  --yellow: #b45309;
  --red: #c2185b;
  --font: var(--sans);
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme='light']) {
    --gray-light: #b6bec6;
    --gray-dark: #3a4b5c;
    --green: #3fcf86;
    --yellow: #ffd23f;
    --red: #ff4d6d;
  }
}
:root[data-theme='dark'] {
  --gray-light: #b6bec6;
  --gray-dark: #3a4b5c;
  --green: #3fcf86;
  --yellow: #ffd23f;
  --red: #ff4d6d;
}

/* A drawing is 1600 units wide and as wide as the text column, so that 48
   units read like the text and 32 units like a caption; on a phone it keeps
   560 px and scrolls sideways. */
.drawing {
  max-width: var(--measure);
  margin: 2rem 0;
}
.drawing-canvas {
  overflow-x: auto;
  padding: 0.75rem;
  background: var(--paper);
  border: 1px solid var(--rule);
  border-radius: 0.75rem;
}
.drawing-canvas svg {
  display: block;
  width: 100%;
  min-width: 560px;
  height: auto;
}
.drawing figcaption {
  margin-top: 0.6rem;
  max-width: var(--measure);
  color: var(--quiet);
  font-size: 0.95rem;
  line-height: 1.5;
}
.drawing-bar {
  display: flex;
  align-items: center;
  gap: 0.5rem 0.75rem;
  margin-top: 0.6rem;
}
.drawing-back {
  padding-inline: 0.6rem;
}
.drawing-back:disabled {
  opacity: 0.4;
  pointer-events: none;
}
.drawing-step {
  color: var(--quiet);
  font-size: 0.95rem;
}

/* The language model by counting (client/next-word.js): the corpus in a text
   field with the button that trains, the choice of context, the sentence as
   words in the code font with its context marked, the line that says what
   the distribution is based on and what the last roll drew, and the
   distribution as bars with probability and count. The sentence and the bars
   have places of a fixed height, so that nothing moves while it grows. */
.next-word {
  margin: 1.5rem 0;
  padding: 1rem;
  border: 1px solid var(--rule);
  border-radius: 0.75rem;
}

.nw-corpus {
  display: grid;
  gap: 0.4rem;
}

.nw-text {
  width: 100%;
  min-height: 9.5rem;
  padding: 0.5rem 0.65rem;
  border: 1px solid var(--rule);
  border-radius: 0.5rem;
  background: var(--paper);
  color: var(--ink);
  font: 0.85rem/1.45 var(--mono);
  resize: vertical;
}

.nw-train {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem 0.75rem;
}

.prose .nw-train .dg-reading {
  flex: 1 1 16rem;
}

.nw-settings {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 0.6rem 1.5rem;
  margin-top: 1rem;
  padding-top: 1rem;
  border-top: 1px solid var(--rule);
}

.nw-temperature {
  display: flex;
  align-items: center;
  gap: 0.5rem;
}

.nw-temperature input {
  width: 9rem;
  accent-color: var(--accent);
  cursor: pointer;
}

.nw-temperature output {
  font: 600 0.9rem/1 var(--mono);
  font-variant-numeric: tabular-nums;
}

.nw-sentence {
  display: flex;
  flex-wrap: wrap;
  align-content: flex-start;
  gap: 0.35rem;
  height: 4.6rem;
  margin-top: 0.75rem;
  padding: 0.5rem;
  overflow-y: auto;
  border: 1px solid var(--rule);
  border-radius: 0.5rem;
}

.nw-word {
  padding: 0.2rem 0.55rem;
  border: 1px solid var(--rule);
  border-radius: 0.4rem;
  font: 600 0.9rem/1.3 var(--mono);
}

.nw-word.is-mark {
  border-style: dashed;
  font-weight: 400;
  color: var(--quiet);
}

.nw-word.is-context {
  border-color: var(--accent);
  background: color-mix(in srgb, var(--accent) 14%, transparent);
  color: var(--accent-ink);
}

.nw-empty {
  align-self: center;
  font-size: 0.85rem;
  color: var(--quiet);
}

.nw-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
  margin-top: 0.75rem;
}

.nw-calc {
  display: grid;
  gap: 0.2rem;
}

.prose .nw-bars {
  height: 17.5rem;
  margin: 0.75rem 0 0;
  padding: 0;
  overflow-y: auto;
  list-style: none;
}

.prose .nw-bars li {
  margin: 0;
  padding: 0;
}

.nw-bar {
  display: grid;
  grid-template-columns: 6.5rem minmax(0, 1fr) 3rem 3.75rem;
  align-items: center;
  gap: 0.6rem;
  width: 100%;
  min-height: 1.75rem;
  padding: 0.1rem 0.4rem;
  border: 1px solid transparent;
  border-radius: 0.4rem;
  background: none;
  color: var(--ink);
  font: 0.85rem/1.2 var(--mono);
  text-align: left;
  cursor: pointer;
}

.nw-bar:hover {
  border-color: var(--accent);
}

.nw-label {
  overflow: hidden;
  font-weight: 600;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.nw-track {
  height: 0.85rem;
  border-radius: 0.2rem;
  background: color-mix(in srgb, var(--rule) 45%, transparent);
  overflow: hidden;
}

.nw-fill {
  display: block;
  height: 100%;
  background: var(--accent);
}

.nw-p,
.nw-n {
  text-align: right;
  font-variant-numeric: tabular-nums;
}

.nw-n {
  color: var(--quiet);
}

.prose .nw-more {
  padding: 0.2rem 0.4rem;
  font-size: 0.8rem;
  color: var(--quiet);
}

/* The bag of the context: one card per occurrence, a fixed place, so that
   bags of different sizes and the lines about temperature take the same
   room; a bag larger than its place scrolls, on a phone, to the drawn card.
   While a card is drawn, a few light up and the drawn one stays. */
.nw-bag {
  display: grid;
  grid-template-rows: auto 4.2rem;
  gap: 0.35rem;
  margin-top: 0.75rem;
}

.prose .nw-bag-caption {
  margin: 0;
  font-size: 0.85rem;
  color: var(--quiet);
}

.nw-cards {
  position: relative;
  display: flex;
  flex-wrap: wrap;
  align-content: flex-start;
  gap: 0.2rem;
  overflow-y: auto;
}

.nw-card {
  padding: 0.05rem 0.35rem;
  border: 1px solid var(--rule);
  border-radius: 0.25rem;
  font: 0.75rem/1.3 var(--mono);
  transition: background-color 60ms, border-color 60ms;
}

.nw-card.is-drawn {
  border-color: var(--accent);
  background: var(--accent);
  color: var(--paper);
}

.nw-verdict {
  display: grid;
  gap: 0.2rem;
  margin-top: 0.75rem;
  padding-top: 0.75rem;
  border-top: 1px solid var(--rule);
}

.prose .nw-verdict p {
  margin: 0;
}

.prose .nw-verdict p:first-child {
  font-weight: 600;
}

.nw-size {
  margin-top: 0.75rem;
  font-size: 0.9rem;
}

.nw-size summary {
  cursor: pointer;
  color: var(--quiet);
}

.prose .nw-size table {
  margin: 0.6rem 0;
  font-variant-numeric: tabular-nums;
}

.prose .nw-size td:not(:first-child),
.prose .nw-size th:not(:first-child) {
  text-align: right;
}

.prose .nw-size p {
  margin: 0.4rem 0 0;
}

@media (max-width: 30rem) {
  .nw-bar {
    grid-template-columns: 5rem minmax(0, 1fr) 2.75rem 3.25rem;
    gap: 0.4rem;
  }
}

/* Guessing the next word (client/word-guess.js): the score above, the
   sentence as words in the code font with a mark under each for you and the
   model (green where the guess was right), the field for the guess and the
   lines that say what the word was and what both guessed. The sentence has
   a place of a fixed height, so that nothing moves while it grows. */
.word-guess {
  margin: 1.5rem 0;
  padding: 1rem;
  border: 1px solid var(--rule);
  border-radius: 0.75rem;
}

.wg-head {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: 0.4rem 1rem;
}

.prose .wg-head p {
  margin: 0;
}

.prose .wg-score {
  font: 650 1rem/1.3 var(--mono);
}

.wg-sentence {
  display: flex;
  flex-wrap: wrap;
  align-content: flex-start;
  gap: 0.5rem 0.4rem;
  height: 5.6rem;
  margin-top: 0.75rem;
  padding: 0.5rem;
  overflow-y: auto;
  border: 1px solid var(--rule);
  border-radius: 0.5rem;
}

.wg-word {
  display: grid;
  justify-items: center;
  gap: 0.15rem;
}

.wg-marks {
  display: flex;
  gap: 0.2rem;
}

.wg-mark {
  padding: 0 0.25rem;
  border-radius: 0.2rem;
  font: 0.65rem/1.4 var(--mono);
  color: var(--quiet);
  text-decoration: line-through;
}

.wg-mark.is-hit {
  background: color-mix(in srgb, var(--green) 24%, transparent);
  color: var(--ink);
  text-decoration: none;
}

.wg-form {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem;
  margin-top: 0.75rem;
}

.wg-form .dg-label {
  flex-basis: 100%;
}

.wg-input {
  flex: 1 1 12rem;
  min-width: 0;
  padding: 0.45rem 0.65rem;
  border: 1px solid var(--rule);
  border-radius: 0.5rem;
  background: var(--paper);
  color: var(--ink);
  font: 600 1rem/1.3 var(--mono);
}

.wg-input:disabled {
  opacity: 0.5;
}

/* --- a map of meaning (client/meaning-map.js) ----------------------------------- */

/* Before the model is loaded a short note and one button; afterwards the map
   of the cards on the left and the search on the right, below both the
   comparison of two texts. Places keep their size while results change. */
.meaning-map {
  margin: 1.5rem 0;
  padding: 1rem;
  border: 1px solid var(--rule);
  border-radius: 0.75rem;
}

.prose .mm-start p {
  margin: 0 0 0.75rem;
}

.prose .mm-start .mm-status {
  margin: 0.75rem 0 0;
  color: var(--quiet);
  font-size: 0.9rem;
}

.mm-main {
  display: grid;
  grid-template-columns: minmax(14rem, 1fr) minmax(16rem, 1.15fr);
  gap: 1rem 1.25rem;
  align-items: start;
}

.mm-main[hidden] {
  display: none;
}

@media (max-width: 44rem) {
  .mm-main {
    grid-template-columns: 1fr;
  }
}

.mm-map {
  display: block;
  width: 100%;
  aspect-ratio: 1;
  border: 1px solid var(--rule);
  border-radius: 0.5rem;
  background: var(--paper);
}

.mm-dot circle {
  fill: var(--paper);
  stroke: var(--quiet);
  stroke-width: 0.5;
}

.mm-dot text {
  font: 650 3.6px/1 var(--mono);
  fill: var(--ink);
  pointer-events: none;
}

.mm-dot:hover circle,
.mm-dot:focus circle,
.mm-dot.is-near circle {
  fill: var(--accent);
  stroke: var(--accent);
}

.mm-dot:hover text,
.mm-dot:focus text,
.mm-dot.is-near text {
  fill: var(--paper);
}

.mm-dot:focus {
  outline: none;
}

.mm-lines line {
  stroke: var(--accent);
  stroke-width: 0.5;
  stroke-dasharray: 1.2 1;
}

.mm-query polygon {
  fill: var(--yellow, #f5c518);
  stroke: var(--ink);
  stroke-width: 0.4;
}

.prose .mm-caption,
.prose .mm-hover {
  margin: 0.4rem 0 0;
  font-size: 0.8rem;
  color: var(--quiet);
}

.prose .mm-hover {
  min-height: 2.6em;
  color: var(--ink);
}

.mm-form {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem;
}

.mm-form .dg-label {
  flex-basis: 100%;
  margin: 0;
}

.mm-input {
  flex: 1 1 12rem;
  min-width: 0;
  padding: 0.45rem 0.65rem;
  border: 1px solid var(--rule);
  border-radius: 0.5rem;
  background: var(--paper);
  color: var(--ink);
  font: 500 0.95rem/1.3 var(--sans);
}

.prose .mm-results {
  display: grid;
  gap: 0.35rem;
  min-height: 12.5rem;
  margin: 0.75rem 0 0;
  padding: 0;
  list-style: none;
}

.prose .mm-result {
  display: grid;
  grid-template-columns: 2.6rem 3rem 1.6rem 1fr;
  align-items: center;
  gap: 0 0.4rem;
  margin: 0;
  font-size: 0.85rem;
  line-height: 1.3;
}

.mm-value {
  font: 650 0.85rem/1 var(--mono);
  text-align: right;
}

.mm-bar {
  height: 0.55rem;
  border-radius: 0.2rem;
  background: linear-gradient(to right, var(--accent) calc(var(--share) * 100%), color-mix(in srgb, var(--rule) 60%, transparent) 0);
}

.mm-card {
  display: grid;
  place-items: center;
  width: 1.5rem;
  height: 1.5rem;
  border: 1px solid var(--quiet);
  border-radius: 50%;
  font: 650 0.7rem/1 var(--mono);
}

.mm-shared {
  grid-column: 4;
  font-size: 0.75rem;
  color: var(--quiet);
}

.prose .mm-keyword,
.prose .mm-numbers {
  margin: 0.6rem 0 0;
  font-size: 0.85rem;
}

.prose .mm-numbers {
  min-height: 2.6em;
  font: 0.75rem/1.4 var(--mono);
  color: var(--quiet);
  overflow-wrap: anywhere;
}

.mm-compare {
  grid-column: 1 / -1;
  padding-top: 0.75rem;
  border-top: 1px solid var(--rule);
}

.prose .mm-similarity {
  flex-basis: 100%;
  margin: 0;
  font: 650 1rem/1.3 var(--mono);
}

/* The tokens a text is cut into, as small chips in code type; the two markers
   the model adds at the start and the end are quiet. Each line keeps its
   height, so that the results below do not jump. */
.prose .mm-tokens {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.25rem;
  flex-basis: 100%;
  min-height: 1.6rem;
  margin: 0.5rem 0 0;
  font-size: 0.8rem;
}

.mm-token-label {
  margin-right: 0.2rem;
  color: var(--quiet);
}

.mm-token {
  padding: 0.05rem 0.35rem;
  border: 1px solid var(--accent);
  border-radius: 0.3rem;
  font: 600 0.8rem/1.3 var(--mono);
  white-space: pre;
}

.mm-token.is-marker {
  border-color: var(--rule);
  color: var(--quiet);
}

.mm-main.is-words {
  grid-template-columns: 1fr;
}

.mm-main.is-words .mm-compare {
  padding-top: 0;
  border-top: 0;
}

/* --- arithmetic with meaning (client/word-arithmetic.js) ------------------------ */

/* Three words with two signs in one row that wraps on a phone, below it the
   shadow of the calculation on the left and the nearest words on the right.
   It shares the start, the inputs and the bars with the map of meaning. */
.word-arithmetic {
  margin: 1.5rem 0;
  padding: 1rem;
  border: 1px solid var(--rule);
  border-radius: 0.75rem;
}

.wa-main[hidden] {
  display: none;
}

.wa-form {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem;
}

.wa-term {
  flex: 1 1 6.5rem;
}

.wa-sign {
  width: 2.2rem;
  height: 2.2rem;
  border: 1px solid var(--accent);
  border-radius: 50%;
  background: transparent;
  color: var(--accent-ink);
  font: 700 1.15rem/1 var(--mono);
  cursor: pointer;
}

.wa-sign:hover,
.wa-sign:focus-visible {
  background: color-mix(in srgb, var(--accent) 12%, transparent);
}

.wa-result {
  display: grid;
  grid-template-columns: minmax(14rem, 1.3fr) minmax(12rem, 1fr);
  gap: 1rem 1.25rem;
  align-items: start;
  margin-top: 1rem;
}

@media (max-width: 44rem) {
  .wa-result {
    grid-template-columns: 1fr;
  }
}

.prose .wa-figure {
  margin: 0;
}

.wa-plot {
  display: block;
  width: 100%;
  aspect-ratio: 200 / 130;
  border: 1px solid var(--rule);
  border-radius: 0.5rem;
  background: var(--paper);
}

.wa-lines .is-arrow {
  stroke: var(--accent);
  stroke-width: 1.2;
  fill: var(--accent);
}

.wa-lines .is-side {
  stroke: var(--quiet);
  stroke-width: 0.6;
  stroke-dasharray: 2 1.6;
}

.wa-dot {
  fill: var(--ink);
}

.wa-pass {
  fill: var(--quiet);
}

.wa-word-label,
.wa-step {
  font: 600 7px/1 var(--sans);
  fill: var(--ink);
}

.wa-step {
  font-weight: 500;
  fill: var(--quiet);
}

.wa-best {
  font: 700 7px/1 var(--sans);
  fill: var(--accent-ink);
}

.wa-star {
  fill: var(--yellow, #f5c518);
  stroke: var(--ink);
  stroke-width: 0.4;
}

.prose .wa-results {
  display: grid;
  gap: 0.35rem;
  min-height: 11rem;
  margin: 0.5rem 0;
  padding: 0;
  list-style: none;
}

.prose .wa-row {
  display: grid;
  grid-template-columns: 2.6rem 4rem 1fr;
  align-items: center;
  gap: 0 0.5rem;
  margin: 0;
  font-size: 0.9rem;
}

.wa-row.is-first .wa-word {
  font-weight: 700;
  color: var(--accent-ink);
}

/* --- a gallery of pictures (client/gallery.js) ---------------------------------- */

/* Small pictures in a grid, in groups with a quiet title, and above them the
   link that downloads all of them as one file. A click opens a picture large
   in a view over the page that keeps one size for every picture. */
.prose .gallery {
  margin: 1.5rem 0;
}

.prose .gallery-bar {
  margin: 0 0 0.75rem;
}

.gallery-download {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  font-weight: 600;
}

.prose .gallery-title {
  margin: 1rem 0 0.4rem;
  font-size: 0.85rem;
  color: var(--quiet);
}

.prose .gallery-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(5.5rem, 1fr));
  gap: 0.6rem;
  margin: 0;
  padding: 0;
  list-style: none;
}

.prose .gallery-grid li {
  margin: 0;
  padding: 0;
}

.gallery-item {
  display: block;
  border: 1px solid var(--rule);
  border-radius: 0.4rem;
  overflow: hidden;
  background: #fff;
  transition: border-color 0.15s, transform 0.15s var(--ease);
}

.gallery-item:hover,
.gallery-item:focus-visible {
  border-color: var(--accent);
  transform: translateY(-2px);
}

.prose .gallery-item img {
  display: block;
  width: 100%;
  aspect-ratio: 105 / 148.5;
  object-fit: contain;
}

.gallery-view {
  width: 100%;
  max-width: none;
  height: 100%;
  max-height: none;
  margin: 0;
  padding: 3.5rem 1rem 1rem;
  border: 0;
  background: var(--paper);
  color: var(--ink);
}

.gallery-view[open] {
  display: grid;
  grid-template-rows: minmax(0, 1fr) auto;
  justify-items: center;
  gap: 0.75rem;
}

.gallery-view::backdrop {
  background: transparent;
}

.gallery-stage {
  width: min(100%, 40rem);
  height: 100%;
  min-height: 0;
}

.gallery-stage img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: contain;
}

.gallery-view .gallery-caption {
  min-height: 2.6em;
  margin: 0;
  max-width: 40rem;
  text-align: center;
  font-size: 0.95rem;
  color: var(--quiet);
}

@media (prefers-reduced-motion: reduce) {
  .gallery-item {
    transition: none;
  }
}
