/* Chunky Bacon - a Ruby notebook in the 2 CV look.

   The look borrows the design language of Citroen's 2 CV Concept (Paris,
   2026) and redraws it: one warm Infra Red field under a dark cap (the
   masthead, the code, the footer), cream for everything you can press,
   cane-mustard for tasks, light drawn as lines along an edge (the current
   lesson, a focused or running cell). No Citroen marks are used.

   Type: Outfit, heavy and tight, for headings, the masthead and buttons;
   Atkinson Hyperlegible Next for everything the learner reads, and its Mono
   for code, which keeps 0/O and 1/l/I apart for people typing their first
   programs; Shantell Sans for the foxes' speech bubbles.

   The foxes are why the lucky stiff's, from why's (poignant) guide to Ruby
   (CC BY-SA 2.5), traced to SVG in assets/why-foxes/. */

:root {
  /* the 2 CV palette (hex values sampled from press photos) */
  --infra: #ee3f24;       /* Infra Red body paint: big fields only, never text */
  --glow: #ff7131;        /* where the paint flips to orange: light on dark */
  --cap: #1e2229;         /* the black roof and tailgate: masthead, code, footer */
  --bakelite: #ede5d0;    /* the cream of everything you touch: buttons */
  --mustard: #e2b034;     /* the cane-weave door cards: tasks, the tartan */
  --clip: #e4e943;        /* the yellow hub clips: tiny accents on dark only */
  --olive: #998c60;       /* the tartan's dark band: pattern lines only */
  --cap-text: #d3ccb9;    /* secondary text on the cap, 10:1 */

  --paper: #f5f1e8;       /* the pale floor: the page */
  --sheet: #fbf9f4;       /* cells, panels, the bits that sit on the page */
  --karo: transparent;    /* (the exercise book's grid, retired) */
  --margin-line: var(--ink);
  --ink: #1e2229;
  --ink-soft: #4e5560;    /* secondary text, 6.9:1 on paper */
  --rule: #d8d1c1;        /* hairlines inside cells and widgets */
  --fox: var(--infra);    /* the active lesson's edge, the task frame */
  --fox-light: var(--glow);
  --bacon: #bd2d00;       /* links and the run button; 5.2:1 on paper, 4.8:1 on fat */
  --fat: #f6e7be;         /* pale mustard: task boxes */
  --highlight: var(--bakelite); /* the current lesson */
  --code-bg: #ece6d8;     /* inline code, printed examples, output */
  --ok: #276d2b;          /* 5.5:1 on the light green status chips */
  --err: #b3261e;
  --focus: #2f6db5;

  --font-print: "Atkinson Hyperlegible Next", system-ui, -apple-system, "Segoe UI", sans-serif;
  --font-code: "Atkinson Hyperlegible Mono", ui-monospace, "Cascadia Code", Menlo, monospace;
  --font-hand: "Shantell Sans", "Segoe Print", "Comic Sans MS", system-ui, sans-serif;
  --font-display: "Outfit", "Avenir Next", "Futura", system-ui, sans-serif;

  --grid: 24px;
  --page: 74rem;
  --column: 44rem;        /* the lesson column: text ~65ch, cells full width */
}

/* Japanese: the three faces above have no kana or kanji, and a browser left
   to itself may take those from a Chinese font. The platforms' Japanese faces
   follow the Latin ones, so Latin letters keep their shapes and Japanese text
   gets Japanese glyphs - BIZ UD first on Windows, universal design like
   Atkinson. */
:root:lang(ja) {
  --font-print: "Atkinson Hyperlegible Next", "Hiragino Sans", "Hiragino Kaku Gothic ProN", "BIZ UDPGothic", "Yu Gothic UI", "Yu Gothic", Meiryo, "Noto Sans JP", sans-serif;
  --font-code: "Atkinson Hyperlegible Mono", "BIZ UDGothic", "Osaka-Mono", "MS Gothic", "Noto Sans Mono CJK JP", ui-monospace, monospace;
  --font-hand: "Shantell Sans", "Hiragino Maru Gothic ProN", "BIZ UDPGothic", "Yu Gothic UI", "Yu Gothic", Meiryo, "Noto Sans JP", sans-serif;
  --font-display: "Outfit", "Hiragino Maru Gothic ProN", "BIZ UDPGothic", "Yu Gothic UI", "Yu Gothic", Meiryo, "Noto Sans JP", sans-serif;
}

/* Slanted kana and kanji are only a faked oblique; Japanese type marks
   emphasis with weight */
:lang(ja) em { font-style: normal; font-weight: 600; }
:lang(ja) .cell .cm-s-default .cm-comment,
:lang(ja) .irb-note { font-style: normal; }

* { box-sizing: border-box; }

html { background: var(--paper); }

body {
  margin: 0;
  font-family: var(--font-print);
  font-size: 1.0625rem;
  line-height: 1.6;
  color: var(--ink);
  background-color: var(--paper);
}

::selection { background: var(--highlight); }

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

a { color: var(--bacon); text-underline-offset: 0.15em; }
a:hover { color: var(--ink); }

/* for screen readers only: the run status line, the language select's label */
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

/* "Skip to the lesson": out of sight until the keyboard reaches it, then
   beside the sidebar toggle, above the drawer */
.skip-link {
  position: fixed;
  top: -4rem;
  left: 3.5rem;
  z-index: 60;
  padding: 0.4rem 0.8rem;
  border: 2px solid var(--ink);
  border-radius: 6px;
  background: var(--paper);
  color: var(--ink);
}
.skip-link:focus { top: 0.6rem; }
/* the heading takes the focus after a lesson change (shell/app.rb): a ring
   for the keyboard, none after a click */
#lessonBody h2[tabindex="-1"]:focus:not(:focus-visible) { outline: none; }

/* ---------- loading screen: the first thing everyone sees ---------- */
.spinner {
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  padding: 2rem 1rem;
}
.spinner-image { width: min(260px, 70vw); }   /* animates itself */
.spinner h1 {
  font-family: var(--font-display);
  font-weight: 800;
  letter-spacing: -0.035em;
  font-size: clamp(2rem, 6vw, 3.25rem);
  line-height: 1.02;
  margin: 0.8rem 0 0.6rem;
  text-wrap: balance;
}
.spinner p {
  margin: 0 0 1.4rem;
  color: var(--ink-soft);
  max-width: 34rem;
}
/* indeterminate: a strip of bacon sliding through a frame */
.load-bar {
  width: min(16rem, 70vw);
  height: 10px;
  border: 2px solid var(--ink);
  border-radius: 5px;
  overflow: hidden;
  background: var(--cap);
}
.load-bar::before {
  content: "";
  display: block;
  width: calc(100% + 84px);
  height: 100%;
  background: repeating-linear-gradient(90deg,
    var(--infra) 0 18px, var(--glow) 18px 30px, var(--clip) 30px 42px);
  animation: bacon-slide 0.8s linear infinite;
  will-change: transform;
}
@keyframes wobble {
  0%, 100% { transform: rotate(-6deg); }
  50% { transform: rotate(6deg); }
}

/* ---------- masthead ---------- */
/* the column's head; with the sidebar put away it leaves the toggle its place */
header {
  display: flex;
  align-items: center;
  gap: 0.9rem;
  padding: 0.8rem 1.5rem 0.8rem 2.5rem;
  background: var(--cap);
  color: var(--bakelite);
  border-bottom: 2px solid var(--ink);
}
body.sidebar-closed header { padding-left: 4.25rem; }
header .home-link { flex: none; line-height: 0; border-radius: 50%; }
/* the fox in an oval badge, like the one centred on that car's blank nose */
header #mascot {
  width: 50px;
  height: 60px;
  flex: none;
  border-radius: 50%;
  border: 3px solid #adb0b3;
  background: var(--bakelite);
  object-fit: cover;
  object-position: 50% 0;
  padding-top: 6px;
}
header .headerText { flex: 1; min-width: 0; }
header h1 {
  margin: 0;
  font-family: var(--font-display);
  font-weight: 800;
  letter-spacing: -0.03em;
  font-size: 1.75rem;
  line-height: 1.05;
}
header p { margin: 0.2rem 0 0; color: var(--cap-text); font-size: 0.95rem; }
/* the lessons are readable before CRuby has loaded; this says it is coming */
header .kernel-status { font-size: 0.85rem; }
header .kernel-status::before {
  content: "";
  display: inline-block;
  width: 0.55em;
  height: 0.55em;
  margin-right: 0.45em;
  border-radius: 50%;
  background: var(--fox);
  animation: kernel-pulse 1.2s ease-in-out infinite;
}
@keyframes kernel-pulse { 50% { opacity: 0.25; } }
header .kernel-status.is-failed { color: #ff9b8a; }   /* 7.7:1 on the cap */
header .kernel-status.is-failed::before { background: var(--err); animation: none; }
@media (prefers-reduced-motion: reduce) { header .kernel-status::before { animation: none; } }
header select {
  font: 700 0.95rem/1.2 var(--font-display);
  color: var(--ink);
  padding: 0.45rem 0.8rem;
  border: 2px solid var(--bakelite);
  border-radius: 999px;
  background: var(--bakelite);
  cursor: pointer;
}

/* progress: where the work is saved; the dot says whether a folder is on.
   The cheat sheet's button beside it looks the same. */
header .progress-btn,
header .cheat-btn {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  font: 700 0.95rem/1.2 var(--font-display);
  color: var(--ink);
  padding: 0.45rem 0.85rem;
  border: 2px solid var(--bakelite);
  border-radius: 999px;
  background: var(--bakelite);
  box-shadow: none;
}
header .progress-btn:hover,
header .cheat-btn:hover { background: #fff; border-color: #fff; }
/* the sheet is out: the button is pressed, like the index's current lesson */
header .cheat-btn[aria-expanded="true"] { background: var(--highlight); }
/* the masthead measures itself, not the window: a docked cheat sheet or a
   tablet beside the open index leave it ~750 px, where the title wrapped to
   two lines and the subtitle to four. Then the header buttons keep only
   their icons (their names stay in title / aria-label) */
header { container-type: inline-size; }
@container (max-width: 880px) {
  header h1 { font-size: 1.45rem; }
  header .cheat-label, header .progress-label { display: none; }
  header .cheat-btn, header .progress-btn { padding-left: 0.6rem; padding-right: 0.6rem; }
}
@container (max-width: 600px) {
  header #siteSubtitle { display: none; }
}
.progress-icon {
  width: 1.15em;
  height: 1.15em;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.8;
  stroke-linejoin: round;
}
.progress-dot {
  display: none;
  position: absolute;
  top: -5px;
  right: -5px;
  width: 11px;
  height: 11px;
  border-radius: 50%;
  border: 2px solid var(--cap);
}
.progress-btn.is-connected .progress-dot { display: block; background: var(--ok); }
.progress-btn.needs-attention .progress-dot { display: block; background: var(--fox); }

/* ---------- layout: the index in the margin, the lesson on the page ----------
   The sidebar runs from the top of the window to its foot and scrolls on its
   own; the red margin line runs down the page beside it, as in the exercise
   book. Everything else - head, lesson, footer - is the column. */
.layout {
  display: flex;
  align-items: stretch;
  min-height: 100vh;
  min-height: 100dvh;
}
.column {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  border-left: 2px solid var(--margin-line);
}

main {
  flex: 1;
  min-width: 0;
  padding: 2rem 1.5rem 3rem 2.5rem;
}
main > * { max-width: var(--column); }

/* ---------- the sidebar ---------- */
.sidebar {
  width: 17rem;
  flex: none;
  position: sticky;
  top: 0;
  height: 100vh;
  height: 100dvh;
  overflow-y: auto;
  overscroll-behavior: contain;
  scrollbar-width: thin;
  scrollbar-color: var(--rule) transparent;
  display: flex;
  flex-direction: column;
  padding: 0 0.75rem 2rem;
  background: var(--paper);
}
body.sidebar-closed .sidebar { display: none; }

/* Fixed, top left, above the drawer and its scrim: the same spot open or
   closed, wide screen or phone. Whatever sits there makes room - the
   sidebar's head when it shows, the column's head when it does not. */
.sidebar-toggle {
  position: fixed;
  top: 1.1rem;
  left: 0.75rem;
  z-index: 40;
  display: grid;
  place-items: center;
  width: 2.25rem;
  height: 2.25rem;
  padding: 0;
  border: 1.5px solid transparent;
  border-radius: 6px;
  background: transparent;
  color: var(--ink);
}
.sidebar-toggle:hover { background: var(--fat); }
.sidebar-toggle svg {
  width: 18px;
  height: 18px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.6;
}
/* on the squared paper it needs an edge of its own */
body.sidebar-closed .sidebar-toggle { top: 1.6rem; background: var(--paper); border-color: var(--ink); }
body.sidebar-closed .sidebar-toggle:hover { background: var(--fat); }

/* how far the whole course is: a strip of bacon that grows. Sticky, so the
   list scrolls under it and not under the toggle. */
.sidebar-head {
  position: sticky;
  top: 0;
  z-index: 2;
  display: grid;
  grid-template-columns: 1fr auto;
  align-items: baseline;
  column-gap: 0.5rem;
  row-gap: 0.45rem;
  margin: 0 -0.75rem;
  padding: 1.15rem 1rem 0.9rem 3.6rem;
  background: var(--paper);
}
.nav-title {
  font-family: var(--font-display);
  font-weight: 800;
  letter-spacing: -0.02em;
  font-size: 1.25rem;
  line-height: 1.2;
}
.nav-count {
  font-size: 0.82rem;
  font-weight: 600;
  color: var(--ink-soft);
  font-variant-numeric: tabular-nums;
}
/* how far the course is: a row of slots, like the slotted wheel discs,
   filled in Infra Red */
.nav-bar {
  grid-column: 1 / -1;
  display: block;
  height: 10px;
  overflow: hidden;
  background: repeating-linear-gradient(90deg, #c9c2b2 0 5px, transparent 5px 8px);
}
#navBarFill {
  display: block;
  height: 100%;
  width: 0;
  background: repeating-linear-gradient(90deg, var(--infra) 0 5px, transparent 5px 8px);
  transition: width 0.4s ease;
}

/* ---------- the workshop's entry, above the lessons ---------- */
.workshop-link {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.4rem;
  flex: none;
  margin: 0.2rem 0 0.7rem;
  padding: 0.5rem 0.8rem;
  border: 2px solid var(--ink);
  border-radius: 999px;
  background: var(--bakelite);
  color: var(--ink);
  font-family: var(--font-display);
  font-weight: 800;
  font-size: 0.98rem;
  text-decoration: none;
  box-shadow: 0 2px 0 var(--ink);
}
.workshop-link:hover { background: #fff; color: var(--ink); }
.workshop-link.active { background: var(--cap); color: var(--bakelite); }

/* ---------- search ---------- */
/* The magnifier is drawn, not a background image, so it takes the
   placeholder's colour */
.nav-search { position: relative; display: block; flex: none; margin: 0 0 0.3rem; }
.nav-search svg {
  position: absolute;
  left: 0.65rem;
  top: 50%;
  width: 14px;
  height: 14px;
  transform: translateY(-50%);
  fill: none;
  stroke: var(--ink-soft);
  stroke-width: 1.5;
  stroke-linecap: round;
  pointer-events: none;
}
#navSearch {
  width: 100%;
  font: 0.9rem/1.3 var(--font-print);
  padding: 0.45rem 0.6rem 0.45rem 2rem;
  border: 1.5px solid var(--rule);
  border-radius: 6px;
  background: var(--code-bg);
  color: var(--ink);
}
#navSearch:hover { border-color: var(--ink-soft); }
#navSearch:focus { background: var(--paper); border-color: var(--ink); }
#navSearch::placeholder { color: var(--ink-soft); opacity: 1; }

/* ---------- lesson index: one group per course ---------- */
nav#lessonNav { display: flex; flex-direction: column; }
.nav-group-head {
  display: flex;
  align-items: center;
  gap: 0.45rem;
  width: 100%;
  margin: 0;
  padding: 1rem 0.5rem 0.35rem;
  border: 0;
  border-radius: 4px;
  background: transparent;
  color: var(--ink-soft);
  font: 700 0.74rem/1.25 var(--font-print);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  text-align: left;
}
.nav-group-head:hover { background: transparent; color: var(--ink); }
.nav-group-head:active { transform: none; }
.nav-group-name { flex: 1; min-width: 0; }
.nav-group-count {
  flex: none;
  padding: 0.1rem 0.45rem;
  border-radius: 999px;
  background: var(--code-bg);
  font-size: 0.72rem;
  letter-spacing: 0;
  font-variant-numeric: tabular-nums;
}
.nav-group.complete .nav-group-count { background: #e3f2e4; color: #1f5d23; }
.nav-chevron {
  flex: none;
  width: 12px;
  height: 12px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.6;
  stroke-linecap: round;
  stroke-linejoin: round;
  transition: transform 0.15s;
}
.nav-group.closed .nav-chevron { transform: rotate(-90deg); }
.nav-group.closed .nav-group-items { display: none; }

/* a row: the lesson's number in a badge, which turns into a tick once done */
nav#lessonNav a.lesson {
  position: relative;
  display: flex;
  align-items: center;
  gap: 0.6rem;
  margin: 1px 0;
  padding: 0.28rem 0.5rem 0.28rem 0.35rem;
  border-radius: 6px;
  color: var(--ink);
  text-decoration: none;
  font-size: 0.93rem;
  line-height: 1.3;
}
nav#lessonNav a.lesson:hover { background: var(--code-bg); }
nav#lessonNav .num {
  flex: none;
  display: grid;
  place-items: center;
  width: 1.65rem;
  height: 1.65rem;
  border: 1.5px solid var(--rule);
  border-radius: 50%;
  background: var(--paper);
  color: var(--ink-soft);
  font-size: 0.72rem;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}
nav#lessonNav .name { min-width: 0; }
nav#lessonNav a.done .num { font-size: 0; background: var(--ok); border-color: var(--ok); }
nav#lessonNav a.done .num::after { content: "✓"; font-size: 0.8rem; color: #fff; }
/* the current lesson is marked like with a highlighter, and carries the
   fox's orange in the margin beside it */
nav#lessonNav a.active { background: var(--highlight); font-weight: 700; }
nav#lessonNav a.active .num { border-color: var(--ink); color: var(--ink); }
nav#lessonNav a.active::before {
  content: "";
  position: absolute;
  left: -0.75rem;
  top: 0.35rem;
  bottom: 0.35rem;
  width: 4px;
  border-radius: 0 3px 3px 0;
  background: var(--fox);
}
.nav-none { margin: 1rem 0.5rem; font-size: 0.9rem; color: var(--ink-soft); }

/* ---------- gems panel, under the workshop: folded, it is a button like
   the workshop's; open, the chips scroll inside it ---------- */
#gemsPanel {
  flex: none;
  margin: 0 0 0.7rem;
  border: 1.5px solid var(--ink);
  border-radius: 6px;
  background: var(--paper);
}
#gemsPanel summary {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.4rem;
  padding: 0.45rem 0.7rem;
  border-radius: 4px;
  font-size: 0.95rem;
  font-weight: 700;
  cursor: pointer;
  list-style: none;
}
#gemsPanel summary::-webkit-details-marker { display: none; }
#gemsPanel summary::after {
  content: "";
  width: 0.4em;
  height: 0.4em;
  margin-left: 0.15rem;
  border: solid currentColor;
  border-width: 0 2px 2px 0;
  transform: translateY(-0.1em) rotate(45deg);
  transition: transform 0.15s;
}
#gemsPanel[open] summary::after { transform: translateY(0.1em) rotate(-135deg); }
#gemsPanel summary:hover { background: var(--fat); }
#gemsPanel[open] summary { border-bottom: 1.5px solid var(--rule); border-radius: 4px 4px 0 0; }
#gemsPanel[open] > :not(summary) { margin-left: 0.6rem; margin-right: 0.6rem; }
#gemsPanel[open] > #gemsList { margin-top: 0.6rem; }
#gemsPanel[open] > #gemsNote { margin-bottom: 0.6rem; }
#gemsList {
  display: flex;
  flex-wrap: wrap;
  gap: 0.35rem;
  max-height: 11rem;
  overflow-y: auto;
  scrollbar-width: thin;
  margin-bottom: 0.7rem;
}
#gemsList .gem-chip {
  font: 400 0.8rem/1.3 var(--font-code);
  padding: 0.15rem 0.55rem;
  border: 1px solid var(--ink-soft);
  border-radius: 999px;
  background: var(--paper);
  color: var(--ink);
}
#gemsList .gem-chip:hover { background: var(--fat); border-color: var(--ink); }
#gemsList .gem-chip.installed { background: #e3f2e4; border-color: var(--ok); color: #1f5d23; }
.gemsInstallRow { display: flex; gap: 0.4rem; }
.gemsInstallRow input {
  flex: 1;
  min-width: 0;
  font: 0.88rem/1.3 var(--font-code);
  padding: 0.35rem 0.5rem;
  border: 1.5px solid var(--ink-soft);
  border-radius: 6px;
  background: var(--paper);
  color: var(--ink);
}
.gemsInstallRow button { font-size: 0.85rem; padding: 0.3rem 0.7rem; }
#gemsNote { font-size: 0.8rem; line-height: 1.45; color: var(--ink-soft); margin: 0.7rem 0 0; }
#gemsNote code { font-family: var(--font-code); font-size: 0.95em; }

/* ---------- lesson text ---------- */
.lessonText h2 {
  font-family: var(--font-display);
  font-size: 2.2rem;
  font-weight: 700;
  line-height: 1.08;
  letter-spacing: -0.03em;
  margin: 0 0 1.1rem;
  text-wrap: balance;
}
.lessonText h3 { font-family: var(--font-display); font-size: 1.35rem; font-weight: 700; line-height: 1.25; margin: 1.8rem 0 0.5rem; }
.lessonText p, .lessonText ul, .lessonText ol { max-width: 65ch; }
.lessonText p { margin: 0 0 0.9rem; }
.lessonText li { margin: 0.2rem 0; }
.lessonText strong { font-weight: 700; }
.lessonText code, .task code, #chunkyChat code { font-family: var(--font-code); }
/* a long call (DRbObject.new_with_uri(...)) or a URL in the prose wraps
   on a phone instead of widening the page */
.lessonText :not(pre) > code, .lessonText a, .task code { overflow-wrap: anywhere; }
.lessonText img { max-width: 100%; height: auto; }
.lessonText :not(pre) > code { font-size: 0.88em; }
.lessonText p > code, .lessonText li > code, .task code, #chunkyChat code {
  background: var(--code-bg);
  padding: 0.05rem 0.3rem;
  border-radius: 4px;
  font-size: 0.88em;
}
/* printed examples: part of the book, not runnable, so no frame and no button */
.lessonText pre, .offweb pre {
  background: var(--code-bg);
  padding: 0.75rem 1rem;
  border-radius: 6px;
  overflow-x: auto;
  font-size: 0.9rem;
  line-height: 1.5;
}
/* a small two-column cheat sheet in the prose (PyCall: Python -> Ruby);
   on a phone the code wraps, so both columns stay in view */
.lessonText table.cheat {
  border-collapse: collapse;
  margin: 0 0 1.1rem;
  font-size: 0.92rem;
}
.lessonText .cheat th, .lessonText .cheat td {
  text-align: left;
  vertical-align: top;
  padding: 0.3rem 1.2rem 0.3rem 0;
  border-bottom: 1px solid var(--rule);
  overflow-wrap: anywhere;
}
.lessonText .cheat td:last-child, .lessonText .cheat th:last-child { padding-right: 0; }
.lessonText .cheat thead th { border-bottom: 1.5px solid var(--ink-soft); }
.lessonText .cheat td > code {
  background: var(--code-bg);
  padding: 0.05rem 0.3rem;
  border-radius: 4px;
  font-size: 0.88em;
}
.lessonText .cheat em { color: var(--ink-soft); }
/* a table of words, not code (pico-deploy's boards): a word wraps whole,
   never "Boar|d" - the narrow columns give way first */
.lessonText table.cheat.boards th, .lessonText table.cheat.boards td { overflow-wrap: normal; }
/* the first column (the board's name, "Pico 2 W"): one line also in Japanese,
   whose line breaking would split ボード or "Pico W" */
.lessonText table.cheat.boards th:first-child, .lessonText table.cheat.boards td:first-child { white-space: nowrap; }
@media (max-width: 480px) {
  .lessonText table.cheat.boards th, .lessonText table.cheat.boards td { padding-right: 0.6rem; }
}
/* file names with what each is for (pico-deploy's R2P2 files): the name
   stands on a line of its own and never breaks; one longer than the
   column scrolls inside its own line, not the page */
.lessonText dl.files { margin: 0 0 1.1rem; font-size: 0.92rem; }
.lessonText dl.files dt {
  margin: 0.55rem 0 0.15rem;
  overflow-x: auto;
  white-space: nowrap;
}
.lessonText dl.files dt:first-child { margin-top: 0; }
.lessonText dl.files dt > code {
  overflow-wrap: normal;
  background: var(--code-bg);
  padding: 0.05rem 0.3rem;
  border-radius: 4px;
  font-size: 0.95em;
}
.lessonText dl.files dd {
  margin: 0 0 0.3rem 1.2rem;
  padding-bottom: 0.4rem;
  border-bottom: 1px solid var(--rule);
}
kbd {
  font-family: var(--font-code);
  font-size: 0.8em;
  padding: 0.05rem 0.35rem;
  border: 1px solid var(--ink-soft);
  border-bottom-width: 2px;
  border-radius: 4px;
  background: var(--paper);
  white-space: nowrap;
}

.task {
  border-left: 4px solid var(--mustard);
  background: var(--fat);
  padding: 0.75rem 1rem;
  border-radius: 0;
  margin: 1.4rem 0 0.5rem;
}
/* A task statement right above its exercise cell joins it: one orange
   block that says what to do and where to do it. The statement already
   starts with "Aufgabe:", so the cell drops its own label. */
.lessonText:has(+ .cell.exercise) > .task:last-child {
  margin-bottom: 0;
  border: 2px solid var(--ink);
  border-bottom: 0;
  border-radius: 0;
  padding: 0.85rem 1rem 0.8rem;
}
.lessonText:has(> .task:last-child) + .cell.exercise {
  margin-top: 0;
  border-top: 2px solid var(--ink);
  border-top-left-radius: 0;
  border-top-right-radius: 0;
}
.lessonText:has(> .task:last-child) + .cell.exercise::before { display: none; }

/* "on your machine" boxes: things the browser sandbox cannot do */
.offweb {
  border-left: 4px solid var(--ink-soft);
  background: var(--code-bg);
  padding: 0.75rem 1rem;
  border-radius: 0 6px 6px 0;
  margin: 1.4rem 0 0.5rem;
}
.offweb::before { content: "💻\00a0"; }
.offweb pre { background: var(--paper); }

/* "look it up": the official documentation of what the lesson used - the
   pages a Rubyist keeps open next to their code */
.docs {
  border-left: 4px solid var(--focus);
  background: #eef4fb;
  padding: 0.6rem 1rem;
  border-radius: 0 6px 6px 0;
  margin: 1.4rem 0 0.5rem;
}
.docs::before { content: "📖\00a0"; }

/* "📋 cheat sheet" in a lesson's text opens the sheet: a button that reads
   like a link */
.lessonText button.cheat-link {
  font: inherit;
  color: var(--bacon);
  background: none;
  border: 0;
  border-radius: 0;
  padding: 0;
  text-decoration: underline;
  text-underline-offset: 0.15em;
}
.lessonText button.cheat-link:hover { color: var(--ink); background: none; }

/* ---------- notebook cells: worksheets on the page ---------- */
.cell {
  margin: 1rem 0 1.5rem;
  border: 2px solid var(--ink);
  border-radius: 0;
  background: var(--sheet);
  overflow: hidden;
  position: relative;
}
.cell:focus-within, .cell.running { border-left-color: var(--glow); border-right-color: var(--glow); }
.cell.exercise { border: 2px solid var(--ink); border-left-width: 4px; border-right-width: 4px; }
.cell.exercise:focus-within, .cell.exercise.running { border-left-color: var(--glow); border-right-color: var(--glow); }
/* a bonus exercise ("bonus": true): the same frame, its label in mustard */
.cell.exercise.bonus::before { background: var(--mustard); color: var(--ink); }
.cell.exercise::before {
  content: attr(data-label);
  display: block;
  background: var(--cap);
  color: var(--clip);
  font-family: var(--font-hand);
  font-weight: 700;
  font-size: 0.8rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  padding: 0.35rem 0.9rem 0.1rem;
}
/* beat codemirror.css's fixed 300px height regardless of load order */
.cell .CodeMirror {
  height: auto;
  font-family: var(--font-code);
  font-size: 15px;
  line-height: 1.6;
  color: var(--bakelite);
  background: var(--cap);
  padding: 0.45rem 0;
}
.cell .CodeMirror-scroll { min-height: 2.2em; }
.cell .CodeMirror-gutters { background: var(--cap); border-right: 1px solid #3a404a; }
.cell .CodeMirror-linenumber { color: #8c9095; padding: 0 0.5rem 0 0.4rem; }   /* 5.1:1 */
.cell .CodeMirror-cursor { border-left: 2px solid var(--glow); }
.cell .CodeMirror-selected, .cell .CodeMirror-focused .CodeMirror-selected { background: #3a404a; }
.cell .CodeMirror pre.CodeMirror-line { padding: 0 0.7rem; }
/* index.html hangWrappedLines adds a wrapped line's hanging indent to this */
.cell .CodeMirror { --cm-line-pad: 0.7rem; }
/* the editor's input is a hidden textarea: the ring goes on the editor,
   inset, as the cell clips what sticks out */
.cell .CodeMirror-focused { outline: 3px solid var(--clip); outline-offset: -3px; }

/* syntax colours on the cap: all at least 5:1 */
.cell .cm-s-default .cm-keyword { color: var(--glow); }
.cell .cm-s-default .cm-def { color: #fff; font-weight: 700; }
.cell .cm-s-default .cm-string { color: var(--mustard); }
.cell .cm-s-default .cm-string-2 { color: #f0a35e; }
.cell .cm-s-default .cm-number { color: #9db8f7; }
.cell .cm-s-default .cm-atom,
.cell .cm-s-default .cm-builtin { color: #c9a7f0; }
.cell .cm-s-default .cm-variable-2 { color: #7fd1c7; }
.cell .cm-s-default .cm-variable-3,
.cell .cm-s-default .cm-type { color: #7fd1c7; }
.cell .cm-s-default .cm-comment { color: #9aa0a8; font-style: italic; }
.cell .cm-s-default .cm-bracket { color: #9aa0a8; }
.cell .CodeMirror-matchingbracket { color: #fff !important; background: #3a404a; }

/* a Pico lesson's cell with several files (shell/view.rb's tabs_html):
   tabs on the cap, the selected one joined to its editor */
.cell-tabs {
  display: flex;
  flex-wrap: wrap;
  gap: 2px;
  background: #2b3038;
  padding: 0.3rem 0.4rem 0;
}
.cell-tab {
  font-family: var(--font-code);
  font-size: 13px;
  color: var(--cap-text);
  background: transparent;
  border: 0;
  border-radius: 4px 4px 0 0;
  padding: 0.3rem 0.7rem;
  cursor: pointer;
}
.cell-tab[aria-selected="true"] { color: #fff; background: var(--cap); }
.cell-tab:hover { color: #fff; }
.cell-tab:focus-visible { outline: 3px solid var(--clip); outline-offset: -3px; }
.cell-tab-lock { margin-right: 0.3em; font-size: 0.85em; }
/* a read-only tab (a driver to read): no cursor glow, a quieter cap */
.cell .CodeMirror.is-readonly { background: #262a31; }
.cell .CodeMirror.is-readonly .CodeMirror-cursor { border-left-color: transparent; }
/* the bench's slot below a board cell, and the bench (pico_board.js): a
   white breadboard with the Pico 2 W across it, jumper wires, the parts;
   cards for the parts without pins; real controls below the drawing. The
   drawing's colours are the parts' own (a green Pico, gold pads, red
   LED); the mat, the cards and the labels follow the page's light or dark
   scheme through the --pb-* tokens. */
.pico-slot { border-top: 1px solid var(--rule); background: var(--pb-mat); padding: 0.55rem 0.7rem 0.7rem; }
.pico-bench {
  --pb-mat: #efe9db;
  --pb-board: #fbfaf6;
  --pb-board-edge: #d9d3c4;
  --pb-hole: #cfc8b8;
  --pb-card: #fbfaf6;
  --pb-card-edge: #d9d3c4;
  --pb-ink: #1e2229;
  --pb-soft: #5a616b;
  --pb-label-gp: #ffffff;
  --pb-label-gp-ink: #1e2229;
  margin: 0;
  color: var(--pb-ink);
}
.pico-slot { --pb-mat: #efe9db; }
.pico-bar { display: flex; flex-wrap: wrap; align-items: center; gap: 0.4rem 0.6rem; margin-bottom: 0.45rem; }
.pico-bar button { font-size: 0.85rem; padding: 0.25rem 0.75rem; }
.pico-sound[aria-pressed="true"] { background: var(--fat); }
.pico-badge { font-size: 0.85rem; color: var(--pb-soft); }
.pico-badge.is-running { color: var(--ok); font-weight: 600; }
.pico-badge.is-reset { color: var(--err); font-weight: 600; }
.pico-bar button:disabled { opacity: 0.55; cursor: default; }
.pico-webcam[aria-pressed="true"] { background: var(--fat); }
.pico-cam-note { flex-basis: 100%; font-size: 0.8rem; color: var(--pb-soft); margin: 0; }
.pico-svg { display: block; width: 100%; height: auto; margin: 0 auto; user-select: none; -webkit-user-select: none; }
.pico-parts { font-size: 0.8rem; color: var(--pb-soft); margin: 0.35rem 0 0.2rem; }
.pico-controls { display: flex; flex-wrap: wrap; gap: 0.35rem 1.2rem; align-items: center; margin-top: 0.35rem; font-size: 0.9rem; }
.pico-row { display: flex; flex-wrap: wrap; align-items: center; gap: 0.35rem 0.6rem; }
.pico-slider, .pico-line { display: inline-flex; align-items: center; gap: 0.45rem; }
.pico-slider input[type="range"] { width: 8.5rem; accent-color: var(--bacon); }
.pico-slider input[type="range"]:disabled { opacity: 0.55; }
.pico-slider-name { font-weight: 600; }
.pico-slider-value { min-width: 4.4em; font-variant-numeric: tabular-nums; color: var(--pb-soft); }
.pico-scripted { font-size: 0.9rem; cursor: help; }
.pico-press { touch-action: none; min-width: 9rem; }
.pico-press.is-down, .pico-press[aria-pressed="true"] { background: var(--fat); transform: translateY(1px); box-shadow: 0 1px 0 var(--ink); }
.pico-check { display: inline-flex; align-items: center; gap: 0.3rem; }
.pico-moves { display: inline-flex; align-items: center; gap: 0.2rem; }
.pico-moves-name { font-weight: 600; margin-right: 0.2rem; }
.pico-move { min-width: 2rem; padding: 0.15rem 0.4rem; font-size: 0.85rem; }
.pico-press.pico-key { min-width: 2.2rem; padding: 0.15rem 0.45rem; }
.pico-line { max-width: 100%; min-width: 0; }
.pico-line input { box-sizing: border-box; max-width: 100%; min-width: 0; width: 11rem; font: inherit; font-family: var(--font-code); padding: 0.2rem 0.4rem; border: 1px solid var(--ink-soft); border-radius: 4px; background: var(--pb-board); color: var(--pb-ink); }
.pico-say { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
/* a real Pico beside the simulated one (pico_real.js): folded panels under
   the controls; R2P2's terminal as a small black screen */
.pico-real { margin-top: 0.5rem; border-top: 1px dashed var(--pb-card-edge); padding-top: 0.35rem; font-size: 0.9rem; }
.pico-real > summary { cursor: pointer; font-weight: 600; width: fit-content; }
.pico-real > summary:focus-visible { outline: 3px solid var(--clip); outline-offset: 2px; }
.pico-real-intro, .pico-real-note, .pico-real-status { margin: 0.35rem 0; color: var(--pb-soft); font-size: 0.85rem; max-width: 46rem; }
.pico-real-note { color: var(--pb-ink); }
.pico-real-status:empty { display: none; }
.pico-real-buttons { margin: 0.35rem 0; }
.pico-real-buttons button { font-size: 0.85rem; padding: 0.25rem 0.75rem; }
.pico-real-term {
  margin: 0.35rem 0; padding: 0.4rem 0.55rem; max-height: 15.5em; overflow: auto;
  background: #000; color: #e8e8e8; border-radius: 4px;
  font: 12px/1.3 var(--font-code); white-space: pre; tab-size: 8;
}
.pico-real-term:focus-visible { outline: 3px solid var(--clip); outline-offset: 2px; }
.pico-real-type { margin-bottom: 0.2rem; }
.pico-real [hidden] { display: none !important; }

/* the drawing */
.pb-breadboard { fill: var(--pb-board); stroke: var(--pb-board-edge); stroke-width: 1; }
.pb-hole { fill: var(--pb-hole); }
.pb-pcb { fill: url(#pb-pcb); stroke: #0f4b29; stroke-width: 1; }
.pb-pcb-edge { fill: none; stroke: rgba(255, 255, 255, 0.08); stroke-width: 1; }
.pb-pad { fill: #d8b25a; stroke: #a8843a; stroke-width: 0.5; }
.pb-castle { fill: var(--pb-board); stroke: #a8843a; stroke-width: 0.6; }
.pb-ring { fill: #e2c06a; stroke: #a8843a; stroke-width: 0.6; }
.pb-drill { fill: #20262a; }
.pb-mount { fill: #e2c06a; stroke: #a8843a; stroke-width: 0.6; }
.pb-mount-hole { fill: var(--pb-board); stroke: #a8843a; stroke-width: 0.4; }
.pb-silk { fill: #f4f4ef; font: 600 5.5px var(--font-print); }
.pb-silk-tiny { font-size: 4.6px; font-weight: 600; }
.pb-silk-name { font: 700 7.4px var(--font-display); letter-spacing: 0.1px; }
.pb-chip-text { fill: #c9ccd1; font: 700 6.4px var(--font-print); letter-spacing: 0.3px; }
.pb-chip-small { font-size: 4.4px; font-weight: 600; }
.pb-chip-temp { font-size: 5.4px; }
.pb-can-text { fill: #6d747c; font: 700 5px var(--font-print); }
.pb-antenna-zone { fill: rgba(0, 0, 0, 0.12); }
.pb-antenna { fill: none; stroke: #d8b25a; stroke-width: 1.6; stroke-linejoin: round; }
.pb-onboard { fill: #e9f5e4; stroke: #9aa39a; stroke-width: 0.4; }
.pb-onboard.is-on { fill: #c7ff9e; }
.pb-onboard-glow { opacity: 0; transition: opacity 60ms; pointer-events: none; }
/* a pin's label: GP pins white, power red, ground black (the datasheet's colours) */
.pb-label rect { fill: var(--pb-label-gp); stroke: #b9b2a2; stroke-width: 0.5; }
.pb-label-text { fill: var(--pb-label-gp-ink); font: 700 6.3px var(--font-code); }
.pb-label-long { font-size: 5.2px; }
.pb-label-adc { fill: #2e7d4f; font-weight: 700; }
.pb-label-gnd rect { fill: #23262b; stroke: #23262b; }
.pb-label-gnd .pb-label-text { fill: #fff; }
.pb-label-pwr rect { fill: #c62f22; stroke: #c62f22; }
.pb-label-pwr .pb-label-text { fill: #fff; }
.pb-label-ctl rect { fill: #e2b034; stroke: #c09124; }
/* a pin's state: out high red, out low dark, input a blue ring, PWM
   orange (as opaque as its duty), a bus pin violet */
.pb-pinstate { fill: transparent; stroke: transparent; stroke-width: 1.6; }
.pb-pinstate.is-high { fill: rgba(255, 59, 31, 0.85); stroke: #ffd2c4; }
.pb-pinstate.is-low { fill: rgba(20, 22, 26, 0.75); stroke: #6a7078; }
.pb-pinstate.is-in { stroke: #3d8bff; stroke-width: 2; }
.pb-pinstate.is-adc { stroke: #2fbf71; stroke-width: 2; }
.pb-pinstate.is-pwm { fill: #ff8a1f; stroke: #ffe0b8; }
.pb-pinstate.is-bus { fill: rgba(155, 89, 230, 0.8); stroke: #e3d0ff; }
/* jumper wires */
.pb-wire-edge { fill: none; stroke: rgba(0, 0, 0, 0.45); stroke-width: 4.2; stroke-linecap: round; stroke-linejoin: round; }
.pb-wire-core { fill: none; stroke-width: 2.9; stroke-linecap: round; stroke-linejoin: round; }
.pb-wire-shine { fill: none; stroke: rgba(255, 255, 255, 0.28); stroke-width: 0.9; stroke-linecap: round; stroke-linejoin: round; transform: translate(-0.6px, -0.6px); }
.pb-wire-tip { fill: #3a3e44; stroke: #15171a; stroke-width: 0.5; }
.pb-leg { stroke: #a9aeb3; stroke-width: 1.4; stroke-linecap: round; }
.pb-small { fill: var(--pb-soft); font: 600 7.5px var(--font-print); }
.pb-value { fill: var(--pb-ink); font-variant-numeric: tabular-nums; }
/* on the breadboard, white in either scheme */
.pb-col .pb-small { fill: #5a616b; }
.pb-col .pb-value { fill: #1e2229; }
.pb-on-dark { fill: #d8dbe0; }
.pb-shine { fill: rgba(255, 255, 255, 0.55); }
.pb-resistor-body { fill: #e9d3a7; stroke: #b39363; stroke-width: 0.6; }
.pb-led-rim { fill: #b81d10; stroke: #7d1208; stroke-width: 0.6; }
.pb-led-dome { fill: #d8321f; }
.pb-led-dome.is-on { fill: #ff6a4d; }
.pb-led-glow { opacity: 0; pointer-events: none; transition: opacity 60ms; }
.pb-button-body { fill: #3b3f45; stroke: #1e2125; stroke-width: 0.8; }
.pb-button-leg { fill: #b9bec4; }
.pb-button-dot { fill: #23262a; }
.pb-button-cap { fill: #1c1e21; stroke: #5b6067; stroke-width: 1.4; transition: transform 60ms; transform-origin: 27px 30px; }
.pb-button-cap.is-pressed { fill: #2c4f8e; stroke: #8db4ff; transform: scale(0.88); }
.pb-finger path { fill: #c8ccd2; stroke: #6e747c; stroke-width: 0.8; }
.pb-finger-led { fill: #3d8bff; }
.pb-sun { fill: #ffcf33; stroke: #d39b00; stroke-width: 0.8; }
.pb-rays line { stroke: #e7a900; stroke-width: 1.6; stroke-linecap: round; }
.pb-ldr { fill: #f2d7a6; stroke: #b48a46; stroke-width: 0.8; }
.pb-ldr-track { fill: none; stroke: #b8432c; stroke-width: 1.2; }
.pb-buzzer { fill: #202226; stroke: #0e0f11; stroke-width: 0.8; }
.pb-buzzer-ring { fill: none; stroke: #3a3d43; stroke-width: 1; }
.pb-buzzer-hole { fill: #050506; }
.pb-waves path { fill: none; stroke: #8e44ad; stroke-width: 1.6; stroke-linecap: round; }
.pb-waves { opacity: 0; transition: opacity 80ms; }
.pb-env { fill: #1d4e9e; stroke: #123468; stroke-width: 0.8; }
.pb-env-edge { fill: none; stroke: rgba(255, 255, 255, 0.12); }
.pb-env-text { fill: #ffffff; font: 700 8.5px var(--font-code); font-variant-numeric: tabular-nums; }
.pb-header { fill: #1d1f22; stroke: #d8b25a; stroke-width: 0.8; }
.pb-epd-pcb { fill: #1f2124; stroke: #0b0c0d; stroke-width: 0.8; }
.pb-epd-glass { fill: #cfd0cb; stroke: #a9aaa4; stroke-width: 0.8; }
.pb-epd-fpc { fill: #c98a2b; opacity: 0.85; }
.pb-epd-paper { fill: #e8e6df; }
.pb-epd-image { image-rendering: pixelated; image-rendering: crisp-edges; }
.pb-epd-flash { fill: #1d1d1d; opacity: 0; pointer-events: none; }
.pb-epd-flash.is-flashing { animation: pb-epd-full 600ms steps(1, end) infinite; }
.pb-epd-flash.is-fast { animation: pb-epd-full 750ms steps(1, end) 2; }
@keyframes pb-epd-full { 0% { opacity: 1; fill: #1d1d1d; } 50% { opacity: 1; fill: #e8e6df; } }
.pb-busy { fill: #3a2c10; stroke: #8a6a20; stroke-width: 0.6; }
.pb-busy.is-on { fill: #ffbf2f; stroke: #fff2c9; }
/* the Pico-LCD-1.14 and the Pico-RGB-LED */
.pb-lcd-pcb, .pb-rgb-pcb { fill: #1b1d21; stroke: #0b0c0d; stroke-width: 0.8; }
.pb-lcd-bezel { fill: #0c0d0f; stroke: #2c2f34; stroke-width: 0.8; }
.pb-lcd-glass { fill: #040506; }
.pb-lcd-glass.is-backlit { fill: #161a20; }
.pb-lcd-image { image-rendering: pixelated; image-rendering: crisp-edges; transition: opacity 80ms; }
.pb-joy-base { fill: #2b2e33; stroke: #0e0f11; stroke-width: 0.8; }
.pb-key { cursor: pointer; touch-action: none; }
.pb-key-cap { fill: #3a3e45; stroke: #8b9099; stroke-width: 0.8; }
.pb-key-cap.is-pressed { fill: #2c4f8e; stroke: #8db4ff; }
.pb-key-text { fill: #e4e6ea; font: 700 8px var(--font-display); pointer-events: none; }
.pb-key-arrow { font-size: 5px; }
.pb-rgb-led, .pb-robot-led { fill: #6f6c66; stroke: #4a4844; stroke-width: 0.5; }
.pb-rgb-led.is-on, .pb-robot-led.is-on { stroke: rgba(255, 255, 255, 0.75); }
.pb-rgb-glow { opacity: 0; pointer-events: none; }
/* the cards: arena, camera, Wi-Fi, laptop */
.pb-card { fill: var(--pb-card); stroke: var(--pb-card-edge); stroke-width: 1; }
.pb-card-title { fill: var(--pb-ink); font: 700 10px var(--font-display); }
.pb-card-hint { fill: var(--pb-soft); font: 600 7.5px var(--font-print); }
.pb-arena-wall { fill: #8a6a48; }
.pb-arena-floor { fill: #e9e2d0; }
.pb-arena-grid { stroke: rgba(120, 100, 70, 0.18); stroke-width: 0.6; }
.pb-obstacle { fill: #8a6a48; stroke: #5d452c; stroke-width: 0.8; }
.pb-box-card { fill: #c99b5d; stroke: #8a6534; stroke-width: 0.8; }
.pb-box-tape { fill: #e6cf98; }
.pb-drag { cursor: grab; touch-action: none; }
.pb-drag:active { cursor: grabbing; }
.pb-bacon-fat { fill: #f6d9c4; stroke: #c4734f; stroke-width: 0.8; }
.pb-bacon-meat { fill: none; stroke: #c23b22; stroke-width: 2.2; stroke-linecap: round; }
.pb-sonar { stroke: #2a6fdb; stroke-width: 1; stroke-dasharray: 3 2.5; }
.pb-sonar-dot { fill: #2a6fdb; }
.pb-ir { fill: rgba(220, 40, 40, 0.07); stroke: rgba(220, 40, 40, 0.25); stroke-width: 0.5; }
.pb-ir.is-seen { fill: rgba(240, 40, 30, 0.38); stroke: rgba(220, 30, 20, 0.8); }
.pb-robot-body { fill: #24406e; stroke: #111827; stroke-width: 0.8; }
.pb-robot-rim { fill: none; stroke: rgba(255, 255, 255, 0.14); stroke-width: 0.8; }
.pb-robot-well { fill: #142540; }
.pb-robot-caster { fill: url(#pb-metal); stroke: #5d646c; stroke-width: 0.4; }
.pb-robot-usb { fill: #b9bfc6; }
.pb-robot-ir { fill: #3a1c1c; stroke: #120808; stroke-width: 0.3; }
.pb-robot-ir.is-on { fill: #ff3b1f; }
.pb-robot.is-hit .pb-robot-body { stroke: #ff3b1f; stroke-width: 1.6; }
.pb-robot-pico { fill: #23824b; stroke: #0f4b29; stroke-width: 0.4; }
.pb-robot-sonar-board { fill: #1d4e9e; }
.pb-robot-eye { fill: url(#pb-metal); stroke: #5d646c; stroke-width: 0.4; }
.pb-robot-buzz { fill: #111; }
.pb-robot-buzz.is-on { fill: #ffbf2f; }
.pb-wheel { fill: #17191c; }
.pb-tread { stroke: #3d424a; }
.pb-info { font-size: 8.5px; font-variant-numeric: tabular-nums; }
.pb-cam-pcb { fill: #1d4e9e; stroke: #123468; stroke-width: 0.8; }
.pb-cam-barrel { fill: #26292e; stroke: #0b0c0e; stroke-width: 1; }
.pb-photo-frame { fill: #1e2229; }
.pb-photo { image-rendering: pixelated; }
.pb-photo.is-preview { opacity: 0.35; }
/* the webcam's view before a capture: dimmed less, it is a viewfinder */
.pb-photo.is-preview.is-live { opacity: 0.75; }
.pb-photo-flash { fill: #fff; opacity: 0; pointer-events: none; }
.pb-photo-flash.is-flashing { animation: pb-photo-flash 400ms ease-out 1; }
@keyframes pb-photo-flash { 0% { opacity: 0.95; } 100% { opacity: 0; } }
.pb-router { fill: #2f343c; stroke: #15181c; stroke-width: 0.8; }
.pb-router-ant { stroke: #2f343c; stroke-width: 3; stroke-linecap: round; }
.pb-router-led { fill: #3a3f46; }
.pb-router-led.is-on { fill: #5ee05a; }
.pb-wifi-waves path { fill: none; stroke: #2a6fdb; stroke-width: 1.8; stroke-linecap: round; }
.pb-http { fill: #1e2229; }
.pb-http-title { fill: #9aa0a8; font: 700 7px var(--font-code); }
.pb-http-line { fill: #d3ccb9; font: 600 7px var(--font-code); }
.pb-http-line.is-ok { fill: #a6e3a1; }
.pb-http-line.is-bad { fill: #ffb4a6; }
.pb-radio { fill: none; stroke: #2a6fdb; stroke-width: 1.4; stroke-dasharray: 2 4; stroke-linecap: round; opacity: 0.25; }
.pb-radio.is-on { opacity: 0.85; animation: pb-radio 900ms linear infinite; }
@keyframes pb-radio { to { stroke-dashoffset: -12; } }
.pb-laptop-lid { fill: #3a3f47; stroke: #1c1f24; stroke-width: 0.8; }
.pb-laptop-screen { fill: #12151a; }
.pb-laptop-base { fill: #b9bec5; stroke: #858b93; stroke-width: 0.8; }
.pb-laptop-notch { fill: #858b93; }
.pb-term { fill: #d3ccb9; font: 600 8.5px var(--font-code); }
.pb-term-in { fill: #a6e3a1; }
.pb-term-head { fill: #9fc4ff; }
.pb-term-rule { stroke: #343a44; stroke-width: 0.8; }
.pb-ble-dot { fill: #3a3f46; }
.pb-ble-dot.is-adv { fill: #e2b034; }
.pb-ble-dot.is-on { fill: #3d8bff; }
@media (prefers-reduced-motion: reduce) {
  .pb-epd-flash.is-flashing, .pb-photo-flash.is-flashing, .pb-radio.is-on { animation: none; }
  .pb-button-cap, .pb-lcd-image { transition: none; }
}
@media (prefers-color-scheme: dark) {
  .pico-slot { --pb-mat: #23272e; }
  .pico-bench {
    --pb-mat: #23272e;
    --pb-board: #ecebe4;
    --pb-board-edge: #6b6f76;
    --pb-hole: #c4c0b4;
    --pb-card: #2d323a;
    --pb-card-edge: #454b55;
    --pb-ink: #eceae4;
    --pb-soft: #b7bcc4;
  }
  .pico-bench .pb-arena-floor { fill: #d9d2c0; }
  .pico-slot .pico-bench button { color: var(--ink); }
}
/* narrow slots: the controls one under the other */
@media (max-width: 620px) {
  .pico-controls { flex-direction: column; align-items: stretch; }
  .pico-slider input[type="range"] { flex: 1; width: auto; min-width: 6rem; }
  .pico-line input { width: auto; flex: 1; min-width: 4rem; }
  .pico-line { flex: 1; }
}

.cell-toolbar {
  display: flex;
  flex-wrap: wrap;           /* a phone's toolbar takes two rows, not squeezed labels */
  justify-content: flex-end;
  align-items: center;
  row-gap: 0.4rem;
  padding: 0.45rem 0.55rem 0.45rem 0.9rem;
  border-top: 2px solid var(--ink);
  background: var(--sheet);
}
.cell-toolbar > * { white-space: nowrap; }
/* the one strong button: Run, in deep Infra Red */
.cell-toolbar .run-cell {
  font-size: 0.92rem;
  padding: 0.35rem 1.05rem;
  background: var(--bacon);
  color: #fff;
}
.cell-toolbar .run-cell:hover { background: #9e2500; }

/* ---------- 📦 Program (html/compile/compile.js) ---------- */
.cell-toolbar .compile-cell {
  font-size: 0.85rem;
  padding: 0.3rem 0.7rem;
  margin-right: 0.5rem;
  background: transparent;
  color: var(--ink);
  border: 1px solid var(--rule);
  border-radius: 6px;
  cursor: pointer;
}
.compile-panel {
  padding: 0.6rem 0.9rem 0.8rem;
  border-top: 1px solid var(--rule);
  background: var(--paper);
  font-size: 0.9rem;
}
.compile-panel[hidden] { display: none; }
.compile-panel p { margin: 0.3rem 0; }
.compile-targets { display: flex; flex-wrap: wrap; gap: 0.5rem; margin: 0.5rem 0; }
.compile-targets button { font-size: 0.9rem; padding: 0.3rem 0.9rem; }
.compile-panel.busy .compile-targets button { opacity: 0.5; pointer-events: none; }
.compile-detail { max-height: 12rem; overflow: auto; white-space: pre-wrap; font-size: 0.8rem; margin: 0.3rem 0; }
.compile-detail:empty { display: none; }
.compile-note { color: #6f655d; font-size: 0.8rem; }

/* ---------- running a cell ----------
   Ruby blocks the main thread while a cell runs, so everything that moves
   here animates only transform or opacity: the browser runs those off the
   main thread, and they keep moving while Ruby works. The running look
   fades in after a beat, so a cell that finishes instantly does not flash. */
.cell.running::after {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  width: calc(100% + 84px);
  height: 4px;
  background: repeating-linear-gradient(90deg,
    var(--infra) 0 18px, var(--glow) 18px 30px, var(--clip) 30px 42px);
  animation: bacon-slide 0.8s linear infinite, run-fade-in 0.2s ease-out 0.12s backwards;
  will-change: transform, opacity;
  pointer-events: none;
  z-index: 2;
}
/* An animation on its own layer, not a transition: a transition on an
   un-promoted element runs on the main thread and freezes at its start
   value for as long as Ruby is busy. */
.cell.running .CodeMirror {
  animation: run-dim 0.25s ease 0.12s both;
  will-change: opacity;
}
@keyframes run-dim {
  to { opacity: 0.55; }
}
.run-cell[disabled] { cursor: progress; opacity: 1; }
.run-cell .run-fox {
  width: 1.15em;
  height: 1.15em;
  margin-right: 0.4em;
  vertical-align: -0.22em;
  animation: wobble 0.5s ease-in-out infinite, run-fade-in 0.2s ease-out 0.12s backwards;
  will-change: transform, opacity;
}
@keyframes bacon-slide {
  from { transform: translateX(-84px); }
  to { transform: translateX(0); }
}
@keyframes run-fade-in {
  from { opacity: 0; }
}

/* after the run: the output slides in, an error shakes the cell, a solved
   exercise gets a green pulse */
.cell-out.reveal { animation: out-in 0.26s cubic-bezier(0.2, 0.7, 0.3, 1); }
@keyframes out-in {
  from { opacity: 0; transform: translateY(-6px); }
}
.cell.shake { animation: cell-shake 0.34s ease-in-out; }
@keyframes cell-shake {
  20% { transform: translateX(-5px); }
  40% { transform: translateX(5px); }
  60% { transform: translateX(-3px); }
  80% { transform: translateX(2px); }
}
.cell.celebrate { animation: cell-pass 0.9s ease-out; }
@keyframes cell-pass {
  from { box-shadow: 0 0 0 0 rgba(46, 125, 50, 0.55); }
  to { box-shadow: 0 0 0 16px rgba(46, 125, 50, 0); }
}
.run-time {
  margin-right: auto;
  align-self: center;
  font-size: 0.8rem;
  color: var(--ink-soft);
  font-variant-numeric: tabular-nums;
}
.run-time.fresh { animation: out-in 0.3s ease-out; }

/* ⚡ Live, beside ▶: a quiet switch, marked like the highlighter when on;
   paused (a cell too slow for live runs) it is struck through */
.live-toggle {
  margin-right: 0.5rem;
  padding: 0.25rem 0.7rem;
  font: inherit;
  font-size: 0.8rem;
  color: var(--ink-soft);
  background: transparent;
  border: 1.5px dashed var(--ink-soft);
  border-radius: 999px;
  box-shadow: none;
  cursor: pointer;
}
.live-toggle:hover { color: var(--ink); background: transparent; border-color: var(--ink); }
.live-toggle[aria-pressed="true"] {
  color: var(--clip);
  background: var(--cap);
  border: 1.5px solid var(--cap);
}
.live-toggle[aria-pressed="true"]:hover { background: #2c323c; color: var(--clip); }
.live-toggle.is-paused {
  color: var(--ink-soft);
  background: var(--fat);
  text-decoration: line-through;
}
/* a lesson without live runs ("live": false): off for good; a click says why */
.live-toggle.is-off-here {
  color: var(--ink-soft);
  text-decoration: line-through;
  cursor: help;
}
/* what a live run printed is a rehearsal: its errors are fainter, its
   notes ("stopped after a second") quieter still */
.cell-out.is-rehearsal .cell-error { opacity: 0.6; }
.cell-hint {
  font-size: 0.85rem;
  font-style: italic;
  color: var(--ink-soft);
}
/* "Ran cells 1–3 above first": the cells above that ▶ (or a live run) ran
   before this one, at the top of its output (shell/app.rb) */
.cell-earlier {
  margin: 0 0 0.35rem;
  font-size: 0.8rem;
  color: var(--ink-soft);
}
.cell-earlier::before { content: "↑ " / ""; }

/* The JRuby lesson ("engine": "jruby", main.rb add_engine_badge): which Ruby
   ran the cell - JRuby in its worker or the page's CRuby - and how long its
   own run took; under the exercise, what CRuby answered for the same code */
.cell-engine {
  display: inline-block;
  margin: 0 0 0.35rem;
  padding: 0.05rem 0.45rem;
  border-radius: 0.6rem;
  font-size: 0.75rem;
  color: var(--ink-soft);
  border: 1px solid var(--rule);
}
.cell-engine.is-jruby { border-color: #c0392b; }
.cell-engine.is-cruby { border-color: #8d6e00; }
.cell-engine.is-compare { display: block; width: fit-content; margin: 0.4rem 0 0; }
/* a QR code of half blocks (█▀▄): one line on top of the next, no gaps, so
   a phone reads it - dark on light, as a scanner expects */
.cell-stdout.blocks {
  line-height: 1;
  font-size: 0.8rem;
  letter-spacing: 0;
  white-space: pre;
  width: fit-content;
  background: #ffffff;
  color: #000000;
}
/* ...drawn as an SVG instead (main.rb, JRubyCells.blocks_svg): square
   modules whatever the font; the text stays in the page, out of sight */
.cell-blocks { display: block; max-width: 100%; height: auto; margin: 0.3rem 0; background: #ffffff; }
.cell-stdout.blocks.is-drawn {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
}
/* the JRuby lesson's Swing bonus: a preview until the click, then the
   iframe with JRuby on CheerpJ (jruby_lab.js, jruby_swing.html) */
.jruby-swing { margin: 0.8rem 0; }
.jruby-swing-preview { display: block; max-width: 100%; height: auto; border: 1px solid var(--rule); border-radius: 0.4rem; margin-bottom: 0.6rem; }
.lessonText button.jruby-swing-start {
  font: inherit;
  font-weight: 600;
  padding: 0.35rem 1rem;
  border-radius: 0.4rem;
  border: 1px solid #c0392b;
  background: #c0392b;
  color: #ffffff;
  cursor: pointer;
}
.jruby-swing-frame { display: block; width: 100%; height: 640px; border: 1px solid var(--rule); border-radius: 0.4rem; background: var(--paper); }
/* "JRuby is starting ...": the lesson's status line (jruby_lab.js) */
.jruby-status {
  font-size: 0.85rem;
  color: var(--ink-soft);
}
.jruby-status:empty { display: none; }
.jruby-status[data-state="download"]::before,
.jruby-status[data-state="boot"]::before { content: "⏳ " / ""; }
.jruby-status[data-state="ready"]::before { content: "☕ " / ""; }
.jruby-status[data-state="failed"] { color: #b3261e; }

.cell-out {
  border-top: 1px solid var(--rule);
  padding: 0.6rem 0.9rem;
  background: var(--code-bg);
}
.cell-stdout {
  font-family: var(--font-code);
  white-space: pre-wrap;
  margin: 0 0 0.3rem;
  font-size: 0.9rem;
}
/* Terminal colours in the output (html/ansi.rb): the 16 standard ones, as
   a terminal with a light background shows them - "white" is a light grey,
   backgrounds are pale so the ink stays readable on them. Backgrounds come
   first, so a colour set as well wins over their text colour. */
.ansi-bg-0 { background: #1a1208; color: #ffffff; }
.ansi-bg-1 { background: #f2b8b3; }
.ansi-bg-2 { background: #b9dfbb; }
.ansi-bg-3 { background: #f5e08a; }
.ansi-bg-4 { background: #b7cdf2; }
.ansi-bg-5 { background: #e3bdea; }
.ansi-bg-6 { background: #b2e3e6; }
.ansi-bg-7 { background: #dde2e7; }
.ansi-bg-8 { background: #5f6670; color: #ffffff; }
.ansi-bg-9 { background: #f7a399; }
.ansi-bg-10 { background: #9fd6a2; }
.ansi-bg-11 { background: #ffe066; }
.ansi-bg-12 { background: #9cbcf5; }
.ansi-bg-13 { background: #e0a3eb; }
.ansi-bg-14 { background: #8fdde3; }
.ansi-bg-15 { background: #ffffff; }
.ansi-bg-ink { background: var(--ink); color: var(--paper); }
.ansi-fg-0 { color: #1a1208; }
.ansi-fg-1 { color: #b3261e; }
.ansi-fg-2 { color: #2e7d32; }
.ansi-fg-3 { color: #8d6e00; }
.ansi-fg-4 { color: #1f5fbf; }
.ansi-fg-5 { color: #9b2fae; }
.ansi-fg-6 { color: #00838f; }
.ansi-fg-7 { color: #858d95; }
.ansi-fg-8 { color: #5f6670; }
.ansi-fg-9 { color: #d93a2b; }
.ansi-fg-10 { color: #3f9b44; }
.ansi-fg-11 { color: #a67f00; }
.ansi-fg-12 { color: #3b7be0; }
.ansi-fg-13 { color: #c04fd4; }
.ansi-fg-14 { color: #0a96a3; }
.ansi-fg-15 { color: #aab1b8; }
.ansi-fg-paper { color: var(--code-bg); }
.ansi-bold { font-weight: 700; }
.ansi-faint { opacity: 0.6; }
.ansi-italic { font-style: italic; }
.ansi-underline { text-decoration: underline; }
.ansi-strike { text-decoration: line-through; }
.ansi-underline.ansi-strike { text-decoration: underline line-through; }
.cell-result {
  font-family: var(--font-code);
  color: var(--ok);
  font-size: 0.9rem;
  white-space: pre-wrap;   /* a Python repr (a pandas Series) has several lines */
}
/* a pandas DataFrame, as pandas renders it (pycall.rb): a small ruled table
   in the code font, numbers right-aligned, scrolling sideways when wide */
.cell-result.py-table {
  color: var(--ink);
  white-space: normal;
  overflow-x: auto;
}
.py-table table { border: 0; border-collapse: collapse; font-size: 0.85rem; }   /* pandas writes border="1" */
.py-table th, .py-table td { padding: 0.2rem 0.7rem; border: 0; border-bottom: 1px solid var(--rule); text-align: right; }
.py-table thead th { border-bottom: 1.5px solid var(--ink-soft); }
.py-table tbody th { color: var(--ink-soft); font-weight: 400; }
.py-table tbody tr:hover { background: var(--fat); }
/* a Daru table (main.rb daru_result_html) says what it is above, as its inspect does */
.py-table caption { text-align: left; color: var(--ink-soft); padding-bottom: 0.2rem; }
.cell-error {
  font-family: var(--font-code);
  color: var(--err);
  white-space: pre-wrap;
  font-size: 0.9rem;
}
/* An error explained (friendly_errors.rb, main.rb friendly_error): the
   headline in red, the cell's line with a caret on a white slip, what to do
   in print, and Ruby's own message folded away underneath. */
.friendly-error {
  border-left: 3px solid var(--err);
  background: var(--fat);
  padding: 0.5rem 0.75rem;
  font-size: 0.92rem;
  color: var(--ink);
}
.friendly-title {
  font-weight: 700;
  color: var(--err);
  margin-bottom: 0.35rem;
}
.friendly-snippet {
  font-family: var(--font-code);
  font-size: 0.82rem;
  background: var(--paper);
  border: 1px solid var(--rule);
  padding: 0.35rem 0.5rem;
  margin: 0 0 0.4rem;
  overflow-x: auto;
  white-space: pre;
}
.friendly-body { margin: 0; line-height: 1.5; }
.friendly-error code {
  font-family: var(--font-code);
  font-size: 0.95em;
  background: var(--code-bg);
  padding: 0 0.2em;
  border-radius: 3px;
}
.friendly-original {
  margin-top: 0.4rem;
  font-size: 0.8rem;
  color: var(--ink-soft);
}
.friendly-original summary { cursor: pointer; }
.friendly-original pre {
  font-family: var(--font-code);
  white-space: pre-wrap;
  margin: 0.25rem 0 0;
}
/* a live run while typing keeps just the headline (main.rb sets
   .friendly-brief, except when the time limit stopped an endless loop) */
.cell-out.is-rehearsal .friendly-error { opacity: 0.6; }
.friendly-brief .friendly-snippet,
.friendly-brief .friendly-body,
.friendly-brief .friendly-original { display: none; }
.friendly-brief .friendly-title { margin-bottom: 0; }
/* what Ruby's did_you_mean suggests for a misspelt name (main.rb) */
.cell-suggest {
  margin-top: 0.35rem;
  font-size: 0.9rem;
  color: var(--ink);
}
.cell-suggest code { font-family: var(--font-code); font-weight: 700; }
.cell-out.is-rehearsal .cell-suggest { opacity: 0.6; }
.cell-image {
  display: block;
  margin: 0.4rem 0;
  image-rendering: pixelated;
  width: 160px;
  max-width: 100%;
  border: 1px solid var(--rule);
  border-radius: 4px;
  background: #fff;
}
/* an SVG (a drawing, a chart, a diagram) keeps its own size and stays sharp;
   the 160px pixelated thumbnail is for small pixel pictures */
.cell-image[src^="data:image/svg+xml"] {
  width: auto;
  image-rendering: auto;
}
/* The IronRuby lesson ("engine": "ironruby", ironruby_cells.rb): a PNG from
   .NET at its own size (a QR code's modules are its pixels: scaled down
   to 160px a phone may not read it), a Markdown table in a sandboxed frame */
.cell-image.ironruby-png { width: auto; }
.ironruby-html {
  display: block;
  width: 100%;
  height: 14rem;
  margin: 0.4rem 0;
  border: 1px solid var(--rule);
  border-radius: 4px;
  background: #fff;
}
/* a QR code drawn with puts (██): one line on top of the next, no gaps,
   dark on white, so a phone reads it */
.cell-stdout.qr-art {
  line-height: 1;
  letter-spacing: 0;
  white-space: pre;
  width: fit-content;
  background: #ffffff;
  color: #000000;
}
/* "IronRuby is starting ...": the lesson's status line with its bar
   (ironruby_lab.js), and ■ Stop in a cell's output while it runs */
.ironruby-status { font-size: 0.85rem; color: var(--ink-soft); }
.ironruby-status:empty { display: none; }
.ironruby-status progress { display: block; width: 100%; max-width: 22rem; margin-top: 0.3rem; accent-color: #68217a; }
.ironruby-status progress[hidden] { display: none; }
.ironruby-status[data-state="download"] .ironruby-status-text::before,
.ironruby-status[data-state="boot"] .ironruby-status-text::before { content: "⏳ " / ""; }
.ironruby-status[data-state="ready"] .ironruby-status-text::before { content: "✅ " / ""; }
.ironruby-status[data-state="failed"] { color: #b3261e; }
.ironruby-stop {
  font: inherit;
  font-size: 0.85rem;
  padding: 0.15rem 0.7rem;
  border-radius: 0.4rem;
  border: 1px solid #b3261e;
  background: transparent;
  color: #b3261e;
  cursor: pointer;
}
/* show_pdf: the browser's own PDF viewer, about one page tall */
.cell-pdf {
  display: block;
  width: 100%;
  height: 28rem;
  margin: 0.4rem 0;
  border: 1px solid var(--rule);
  border-radius: 4px;
  background: #fff;
}
/* show_audio: the wave (an SVG as an <img>: the whole sound, and 12 ms of
   it magnified) above the browser's own player, both 508px wide */
.cell-audio { margin: 0.4rem 0; max-width: 100%; }
.cell-wave {
  display: block;
  width: 508px;
  max-width: 100%;
  height: auto;
  margin-bottom: 0.25rem;
}
.cell-audio audio,
.ws-audio {
  display: block;
  width: 508px;
  max-width: 100%;
}
/* the line an error points at, in the dark editor: a dark red tint keeps the
   code readable (cream 10:1, every syntax colour 4.7:1 or more), a bright
   underline says where */
.marker { background: #45272a; border-bottom: 2px solid #ff7a6b; }

/* ---------- downloads: files a cell wrote ---------- */
.cell-downloads {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
  margin: 0.4rem 0;
}
.cell-download {
  display: inline-flex;
  align-items: baseline;
  gap: 0.35rem;
  padding: 0.25rem 0.7rem;
  border: 1.5px solid var(--bacon);
  border-radius: 999px;
  color: var(--bacon);
  background: var(--paper);
  font-family: var(--font-code);
  font-size: 0.86rem;
  text-decoration: none;
}
.cell-download:hover { background: var(--bacon); color: #fff; }

/* ---------- widgets: windows drawn in the same ink as the cells ---------- */
.shoes-app, .three-stage, .irb-term, .mini-browser, .file-explorer {
  margin: 0.7rem 0;
  max-width: 100%;
  border: 1.5px solid var(--ink);
  border-radius: 6px;
}

/* Shoes app stage (lacci) */
.shoes-app {
  overflow: auto;
  background: var(--paper);
  font-family: var(--font-print);
  color: var(--ink);
}
.shoes-root { padding: 0.7rem 0.9rem; }
.shoes-stack { display: flex; flex-direction: column; align-items: stretch; gap: 0.35rem; }
.shoes-flow { display: flex; flex-direction: row; flex-wrap: wrap; align-items: baseline; gap: 0.45rem; }
.shoes-text { margin: 0.15rem 0; line-height: 1.35; }
.shoes-app h1.shoes-text { font-size: 1.5rem; margin: 0.2rem 0; }
.shoes-app h2.shoes-text { font-size: 1.25rem; margin: 0.2rem 0; }
.shoes-app h3.shoes-text { font-size: 1.1rem; margin: 0.2rem 0; }
.shoes-button {
  align-self: flex-start;
  padding: 0.3rem 0.8rem;
  border: 1px solid #b9b2a8;
  border-radius: 6px;
  background: linear-gradient(#fff, #efeae4);
  /* the site's own button style is ink-on-orange; a Shoes button is not */
  color: var(--ink);
  font: inherit;
  font-weight: normal;
  box-shadow: none;
  text-shadow: none;
  cursor: pointer;
}
.shoes-button:hover { background: linear-gradient(#fff, #e6dfd6); color: var(--ink); }
.shoes-button:active { background: #e0d8cc; }
.shoes-editline, .shoes-editbox {
  padding: 0.25rem 0.4rem;
  border: 1px solid #b9b2a8;
  border-radius: 5px;
  font: inherit;
}
.shoes-editbox { min-height: 4rem; }
.shoes-link { color: var(--bacon); }
.shoes-image { max-width: 100%; }

/* 3D stage (three-rb) */
.three-stage {
  overflow: hidden;
  background: #151a20;
}
.three-stage canvas {
  display: block;
  width: 100%;
  height: 100%;
  touch-action: none;
}

/* the letter (show_letter, letter.js): an envelope to write a postcode on,
   and below it what Ruby gets from each box - 8x8 grey squares */
.letter-widget { margin: 0.7rem 0; max-width: 480px; }
.letter-canvas {
  display: block;
  width: 100%;
  aspect-ratio: 480 / 300;
  border: 1.5px solid var(--ink);
  border-radius: 6px;
  cursor: crosshair;
  touch-action: none;   /* the pen draws; the page does not scroll */
}
.letter-bar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.4rem 0.7rem;
  margin-top: 0.45rem;
  font-size: 0.85rem;
  color: var(--ink-soft);
}
.letter-clear { font-size: 0.85rem; padding: 0.2rem 0.7rem; }
.letter-previews { display: inline-flex; gap: 0.35rem; }
.letter-preview {
  width: 32px;
  height: 32px;
  image-rendering: pixelated;
  border: 1px solid var(--rule);
}
.letter-error {
  margin-top: 0.4rem;
  font-family: var(--font-code);
  font-size: 0.85rem;
  color: var(--err);
  white-space: pre-wrap;
}

/* A Processing sketch (processing.js): its window's canvas, at the size
   the sketch asked for, narrower if the column is */
.sketch-stage { margin: 0.7rem 0; max-width: 100%; }
.sketch-canvas {
  display: block;
  max-width: 100%;
  height: auto;
  border: 1.5px solid var(--ink);
  border-radius: 6px;
  touch-action: none;
}
.sketch-canvas:focus-visible { outline: 3px solid var(--accent, #c8372d); outline-offset: 2px; }
.sketch-print { max-height: 9rem; overflow-y: auto; margin-top: 0.4rem; }
.sketch-error { margin-top: 0.4rem; }

/* A game (show_game, game.js): a grid of cells on a dark board, a veil
   with "Click to play" while it does not run, the status line below.
   Nothing here animates; the game itself moves only once the learner
   starts it, with a click, Space or Enter. */
.game-widget {
  display: inline-block;
  max-width: 100%;
  margin: 0.7rem 0;
  user-select: none;
  -webkit-user-select: none;
}
.game-widget:focus { outline: none; }
.game-widget:focus-visible { outline: 3px solid var(--focus); outline-offset: 3px; border-radius: 12px; }
.game-board {
  position: relative;
  padding: 6px;
  border-radius: 10px;
  background: #2a2119;
  overflow-x: auto;
}
.game-widget.is-running .game-board { box-shadow: 0 0 0 3px var(--fox-light); }
.game-grid {
  display: grid;
  background: #fbf7ee;
  border-radius: 6px;
  overflow: hidden;
  touch-action: none;   /* a swipe steers; the page does not scroll */
}
.game-grid > div {
  display: flex;
  align-items: center;
  justify-content: center;
  line-height: 1;
  background-size: 90% 90%;
  background-position: center;
  background-repeat: no-repeat;
}
.game-grid > div.alt { box-shadow: inset 0 0 0 100px rgba(80, 60, 30, 0.045); }
/* a ruby2d window (ruby2d.rb): its canvas, at the window's size or the
   column's width */
.game-canvas {
  display: block;
  max-width: 100%;
  border-radius: 6px;
  background: #000;
  touch-action: none;   /* the pointer is the program's */
}
/* white on the veil over the light grid: about 6:1 */
.game-overlay {
  position: absolute;
  inset: 6px;
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
  align-items: center;
  justify-content: center;
  padding: 1rem;
  border-radius: 6px;
  background: rgba(42, 33, 25, 0.72);
  color: #fff;
  font: 600 1.05rem var(--font-hand);
  text-align: center;
  cursor: pointer;
}
.game-overlay[hidden] { display: none; }
.game-overlay small { font: 400 0.85rem var(--font-print); }
.game-bar {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: 0.2rem 1rem;
  min-height: 1.4em;
  padding: 0.3rem 0.2rem 0;
  font-size: 0.9rem;
  color: var(--ink-soft);
}
.game-hint { font-size: 0.8rem; }
.game-log {
  max-height: 6.5em;
  overflow: auto;
  margin: 0.3rem 0 0;
  padding: 0.3rem 0.5rem;
  border-radius: 6px;
  background: var(--code-bg);
  font-size: 0.8rem;
}
.game-log:empty { display: none; }
.game-error { padding-top: 0.3rem; color: var(--err); font-size: 0.9rem; white-space: pre-wrap; }

/* ⏯ Step through (stepper.js, a lesson with "stepper": true): a quieter
   button than ▶ beside it; below the cell the recorded run - the fox's
   sentence, the variables of each frame beside the output so far, a
   slider with buttons. The line about to run is marked in the editor
   itself, as the highlighter marks the current lesson. */
.cell-toolbar .step-cell {
  margin-right: 0.5rem;
  padding: 0.3rem 0.8rem;
  font-size: 0.85rem;
  font-weight: 600;
  background: var(--paper);
}
.cell-toolbar .step-cell:hover { background: var(--fat); }
.cell-toolbar .step-cell[disabled] { opacity: 0.45; cursor: progress; }

/* 💡 Tipp, at the left of the exercise's toolbar: one hint per click, then
   the solution (shell/app.rb reveal_hint); they collect in .cell-hints */
.cell-toolbar .hint-cell {
  margin-right: auto;
  padding: 0.3rem 0.8rem;
  font-size: 0.85rem;
  font-weight: 600;
  background: var(--paper);
}
.cell-toolbar .hint-cell:hover { background: var(--fat); }
.cell-toolbar .hint-cell[disabled] { opacity: 0.45; cursor: default; }
/* after a run the time stamp joins the toolbar (shell/app.rb show_run_time,
   inserted first): 💡 stays at the left edge, the stamp beside it, and the
   stamp's auto margin keeps the buttons on the right - two auto margins
   would park 💡 in the middle */
.cell-toolbar .hint-cell { order: -1; }
.cell-toolbar:has(.hint-cell) .run-time { margin-left: 0.7rem; }
.cell-toolbar:has(.run-time) .hint-cell { margin-right: 0; }
.cell-hints {
  border-top: 2px solid var(--ink);
  background: var(--fat);
  padding: 0.55rem 0.9rem 0.6rem;
  font-size: 0.92rem;
}
.cell-hints .hint-step { margin: 0.3rem 0; }
.cell-hints .hint-step + .hint-step,
.cell-hints .hint-step + .hint-solution { border-top: 1px dashed var(--ink-soft); padding-top: 0.5rem; margin-top: 0.5rem; }
.cell-hints .hint-solution pre {
  margin: 0.35rem 0 0.6rem;
  padding: 0.6rem 0.8rem;
  background: var(--sheet);
  border: 1px solid var(--ink);
  overflow-x: auto;
  font-family: var(--font-code);
  font-size: 0.88rem;
  line-height: 1.45;
  white-space: pre;
}
.cell-hints .hint-apply { font-size: 0.85rem; padding: 0.3rem 0.8rem; }
.cell-hints .hint-apply[disabled] { opacity: 0.6; cursor: default; }
/* the stepper's line, on the dark editor: dark tints under the code
   (cream 10:1, every syntax colour 4.7:1 or more), the kind of step in the
   bar on the left */
.cell .CodeMirror .step-now { background: #3e2d1f; box-shadow: inset 3px 0 0 var(--glow); }
.cell .CodeMirror .step-now.step-call { background: #213149; box-shadow: inset 3px 0 0 #7fa8f0; }
.cell .CodeMirror .step-now.step-return { background: #1f3626; box-shadow: inset 3px 0 0 #6fcf7a; }
.cell .CodeMirror .step-now.step-error { background: #45272a; box-shadow: inset 3px 0 0 #ff7a6b; }
.stepper {
  margin: 0 0 0.7rem;
  padding: 0.6rem 0.7rem 0.4rem;
  border: 1.5px solid var(--ink);
  border-radius: 8px;
  background: var(--paper);
}
.step-say {
  margin: 0 0 0.6rem;
  padding: 0.4rem 0.7rem;
  font: 1rem/1.35 var(--font-hand);
  background: var(--fat);
  border: 1.5px solid var(--ink);
  border-radius: 10px;
}
.step-say.is-error { background: #fbe3e1; }
.step-body {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: 0.7rem;
  align-items: start;
}
.step-frames { display: flex; flex-direction: column; gap: 0.4rem; min-width: 0; }
.step-frame { border: 1px solid var(--rule); border-radius: 6px; overflow: hidden; }
.step-frame.is-top { border-color: var(--ink-soft); }
.step-frame-head {
  display: flex;
  gap: 0.5rem;
  margin: 0;
  padding: 0.15rem 0.5rem;
  font-size: 0.8rem;
  font-weight: 700;
  background: var(--code-bg);
}
.step-pass { margin-left: auto; color: var(--bacon); }
.step-vars { width: 100%; border-collapse: collapse; font: 0.85rem/1.4 var(--font-code); }
.step-vars th, .step-vars td { padding: 0.1rem 0.5rem; border-top: 1px solid #edf0f3; vertical-align: top; text-align: left; }
.step-vars th { width: 1%; white-space: nowrap; font-weight: 400; color: var(--ink-soft); }
.step-vars td { word-break: break-all; }
.step-vars tr.changed td { background: #fff2b8; }
.step-vars tr.earlier th::after { content: " ⟲"; color: var(--ink-soft); }
.step-empty { margin: 0; padding: 0.2rem 0.5rem; font-size: 0.8rem; font-style: italic; color: var(--ink-soft); }
.step-output { min-width: 0; }
.step-out-label { margin: 0 0 0.15rem; font-size: 0.75rem; font-weight: 700; color: var(--ink-soft); }
.step-out {
  margin: 0;
  min-height: 2.2em;
  padding: 0.3rem 0.5rem;
  border-radius: 4px;
  background: var(--code-bg);
  font: 0.85rem/1.4 var(--font-code);
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}
.step-controls {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.4rem 0.6rem;
  margin-top: 0.6rem;
}
.step-btns { display: flex; gap: 0.25rem; }
.step-controls button {
  min-width: 2.2rem;
  padding: 0.2rem 0.45rem;
  font-size: 0.95rem;
  background: var(--paper);
}
.step-controls button:hover { background: var(--fat); }
.step-controls button[aria-disabled="true"] { opacity: 0.35; cursor: default; background: var(--paper); }
.step-controls .step-play { padding: 0.2rem 0.7rem; font-size: 0.85rem; white-space: nowrap; }
.step-slider { flex: 1 1 8rem; min-width: 6rem; margin: 0; accent-color: var(--fox); }
.step-count { font-size: 0.85rem; font-weight: 600; color: var(--ink-soft); font-variant-numeric: tabular-nums; white-space: nowrap; }
.step-note { margin: 0.4rem 0 0; font-size: 0.85rem; color: var(--ink-soft); }
.step-note.is-warn { color: var(--bacon); }
.step-legend { margin: 0.4rem 0 0; font-size: 0.75rem; color: var(--ink-soft); }
@media (max-width: 620px) {
  .step-body { grid-template-columns: minmax(0, 1fr); }
}

/* IRB terminal */
.irb-term {
  background: #211a13;
  padding: 0.75rem 0.95rem;
  font-family: var(--font-code);
  font-size: 0.9rem;
  color: #f2e8da;
}
.irb-history {
  max-height: 260px;
  overflow-y: auto;
  margin-bottom: 0.2rem;
}
.irb-echo { color: #f2e8da; white-space: pre-wrap; }
.irb-stdout { margin: 0; color: #cfe3ff; white-space: pre-wrap; font-family: inherit; }
.irb-result { color: #8ee08f; }
.irb-error { color: #ff9d94; }
.irb-suggest { color: #f5bf4f; }
.irb-note { color: #f5bf4f; font-style: italic; }
.irb-line { display: flex; align-items: center; gap: 0.45rem; }
.irb-prompt { color: #f5bf4f; white-space: nowrap; }
.irb-input {
  flex: 1;
  min-width: 0;
  background: transparent;
  border: none;
  outline: none;
  color: #fff;
  font-family: inherit;
  font-size: inherit;
  caret-color: #8ee08f;
}
.irb-term:focus-within { outline: 3px solid var(--focus); outline-offset: 2px; }

/* Spinel (spinel.js): a program's way through the compiler, and IRB on it */
.spinel-box {
  margin: 0.7rem 0;
  padding: 0.65rem 0.85rem;
  border: 1.5px solid var(--ink);
  border-radius: 6px;
  background: var(--paper);
  font-size: 0.9rem;
}
.spinel-head { display: flex; align-items: baseline; gap: 0.6rem; margin-bottom: 0.35rem; }
.spinel-name { font-weight: 700; }
.spinel-version { color: var(--ink-soft); font-size: 0.8rem; }
.spinel-steps { list-style: none; margin: 0 0 0.4rem; padding: 0; }
.spinel-step {
  display: flex;
  justify-content: space-between;
  gap: 1rem;
  padding: 0.15rem 0;
  border-bottom: 1px solid var(--rule);
}
.spinel-step::before { content: "○"; margin-right: 0.45rem; color: var(--ink-soft); }
.spinel-step.is-busy::before { content: "◌"; color: var(--fox); }
.spinel-step.is-done::before { content: "✓"; color: var(--ok); }
.spinel-step.is-failed::before { content: "✗"; color: var(--err); }
.spinel-step-name { flex: 1; }
.spinel-step-time { font-family: var(--font-code); color: var(--ink-soft); white-space: nowrap; }
.spinel-status { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); }
.spinel-out { margin: 0.45rem 0; padding: 0.4rem 0.55rem; background: var(--code-bg); border-radius: 4px; }
.spinel-problem { margin: 0.45rem 0; padding-left: 0.6rem; border-left: 3px solid var(--err); color: var(--err); }
.spinel-problem pre { margin: 0.25rem 0 0; white-space: pre-wrap; font-family: var(--font-code); font-size: 0.82rem; color: var(--ink); }
.spinel-c { margin: 0.45rem 0; }
.spinel-c summary { cursor: pointer; color: var(--bacon); }
.spinel-c pre {
  max-height: 320px;
  overflow: auto;
  margin: 0.35rem 0 0;
  padding: 0.5rem 0.6rem;
  background: var(--code-bg);
  font-family: var(--font-code);
  font-size: 0.75rem;
  white-space: pre;
}
.spinel-download { display: inline-block; margin: 0.3rem 0; }
.spinel-compare { margin: 0.4rem 0 0; }
.spinel-compare.is-same::before { content: "✓ "; color: var(--ok); }
.spinel-compare.is-different::before { content: "≠ "; color: var(--err); }
.spinel-compare.is-cruby { color: var(--ink-soft); }
.spinel-compare pre { margin: 0.25rem 0 0; }

.spinel-term {
  margin: 0.7rem 0;
  border: 1.5px solid var(--ink);
  border-radius: 6px;
  background: #211a13;
  padding: 0.75rem 0.95rem;
  font-family: var(--font-code);
  font-size: 0.9rem;
  color: #f2e8da;
}
.spinel-term-history { max-height: 320px; overflow-y: auto; margin-bottom: 0.2rem; }
.spinel-term-echo { white-space: pre-wrap; }
.spinel-term-out { margin: 0; color: #cfe3ff; white-space: pre-wrap; font-family: inherit; }
.spinel-term-result { color: #8ee08f; white-space: pre-wrap; }
.spinel-term-error { color: #ff9d94; white-space: pre-wrap; }
.spinel-term-note { color: #f5bf4f; font-style: italic; }
.spinel-term-time { color: #a89a8a; font-size: 0.78rem; }
.spinel-term-line { display: flex; align-items: center; gap: 0.45rem; }
.spinel-term-prompt { color: #f5bf4f; white-space: nowrap; }
.spinel-term-input {
  flex: 1;
  min-width: 0;
  background: transparent;
  border: none;
  outline: none;
  color: #fff;
  font-family: inherit;
  font-size: inherit;
  caret-color: #8ee08f;
}
.spinel-term-input:disabled { opacity: 0.5; }
.spinel-term:focus-within { outline: 3px solid var(--focus); outline-offset: 2px; }
:lang(ja) .spinel-term-note { font-style: normal; }

/* mini browser */
.mini-browser {
  overflow: hidden;
  background: var(--paper);
}
.mb-chrome, .fe-title {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.4rem 0.6rem;
  background: var(--code-bg);
  border-bottom: 1px solid var(--rule);
}
.mb-dots, .fe-dots { display: flex; gap: 0.28rem; }
.mb-dots span, .fe-dots span {
  width: 11px;
  height: 11px;
  border-radius: 50%;
  display: inline-block;
  border: 1px solid rgba(26, 18, 8, 0.35);
}
.mb-dots span:nth-child(1), .fe-dots span:nth-child(1) { background: #f56d5c; }
.mb-dots span:nth-child(2), .fe-dots span:nth-child(2) { background: #f5bf4f; }
.mb-dots span:nth-child(3), .fe-dots span:nth-child(3) { background: #59c837; }
.mb-scheme {
  font-family: var(--font-code);
  font-size: 0.85rem;
  color: var(--ink-soft);
  white-space: nowrap;
}
.mb-url {
  flex: 1;
  min-width: 0;
  font-family: var(--font-code);
  font-size: 0.88rem;
  padding: 0.25rem 0.5rem;
  border: 1px solid #b9c3cd;
  border-radius: 5px;
  background: var(--paper);
  color: var(--ink);
}
button.mb-go {
  font-size: 0.85rem;
  padding: 0.25rem 0.8rem;
}
.mb-status {
  font-family: var(--font-code);
  font-size: 0.82rem;
  font-weight: 700;
  padding: 0.1rem 0.5rem;
  border-radius: 999px;
  min-width: 2.6em;
  text-align: center;
}
.mb-status.ok { background: #e3f2e4; color: var(--ok); }
.mb-status.err { background: #fde3df; color: var(--err); }
.mb-view {
  padding: 0.8rem 1rem;
  min-height: 60px;
  background: var(--paper);
}
/* the page inside .mb-view is in a shadow root: its styles are
   MB_PAGE_STYLE in main.rb */
/* what the app printed while answering (a Rails app logs every request) */
.mb-log {
  margin: 0;
  max-height: 9.5rem;
  overflow: auto;
  padding: 0.4rem 0.6rem;
  border-top: 1px solid var(--rule);
  background: var(--code-bg);
  color: var(--ink-soft);
  font-family: var(--font-code);
  font-size: 0.78rem;
  line-height: 1.4;
  white-space: pre-wrap;
}
.mb-log[hidden] { display: none; }

/* file explorer (simulated filesystem) */
.file-explorer {
  overflow: hidden;
  background: var(--paper);
  font-size: 0.92rem;
}
.fe-title { font-weight: 700; color: var(--ink-soft); }
button.fe-refresh {
  margin-left: auto;
  font-size: 0.9rem;
  padding: 0.05rem 0.55rem;
  background: var(--paper);
  color: var(--ink);
  border: 1px solid #b9c3cd;
}
button.fe-refresh:hover { background: var(--fat); }
.fe-list { list-style: none; margin: 0; padding: 0.3rem 0; max-height: 220px; overflow-y: auto; }
.fe-list li {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.25rem 0.9rem;
  font-family: var(--font-code);
}
.fe-list li.fe-file { cursor: pointer; }
.fe-list li.fe-file:hover { background: var(--fat); }
.fe-list li.fe-dir { color: var(--ink-soft); }
.fe-name { flex: 1; }
.fe-size { color: var(--ink-soft); font-size: 0.8rem; }
.fe-preview {
  margin: 0;
  border-top: 1px solid var(--rule);
  background: var(--code-bg);
  padding: 0.6rem 0.9rem;
  font-family: var(--font-code);
  font-size: 0.86rem;
  white-space: pre-wrap;
  max-height: 200px;
  overflow-y: auto;
}

/* ---------- buttons: cream means pressable, like every control in that car
   (Bakelite cream); the one strong action is deep Infra Red ---------- */
.buttons { margin: 0.7rem 0; display: flex; gap: 0.6rem; }
button {
  font-family: var(--font-display);
  font-size: 1rem;
  font-weight: 700;
  line-height: 1.25;
  padding: 0.5rem 1.2rem;
  border-radius: 999px;
  border: 2px solid var(--ink);
  cursor: pointer;
  background: var(--bakelite);
  color: var(--ink);
  box-shadow: 0 2px 0 var(--ink);
}
button:hover { background: #fff; }
button:active { transform: translateY(1px); box-shadow: 0 1px 0 var(--ink); }
button.reset {
  background: transparent;
  font-weight: 600;
  font-size: 0.92rem;
  padding: 0.4rem 0.95rem;
  border-color: var(--ink-soft);
  color: var(--ink-soft);
  box-shadow: none;
}
button.reset:hover { background: var(--paper); color: var(--ink); border-color: var(--ink); }

/* ---------- the workshop: files beside a program editor ---------- */
.in-workshop main > * { max-width: 64rem; }
.workshop {
  display: grid;
  grid-template-columns: 12.5rem minmax(0, 1fr);
  gap: 1.2rem;
  align-items: start;
  margin-top: 1.2rem;
}
/* until the editor has room beside it, the file panel sits above it */
@media (max-width: 1100px) {
  .workshop { grid-template-columns: minmax(0, 1fr); }
}
.ws-files h3 { margin: 0; font-size: 0.95rem; font-weight: 700; }
.ws-where { margin: 0.1rem 0 0.6rem; font-size: 0.82rem; color: var(--ink-soft); overflow-wrap: anywhere; }
.ws-locked {
  font-size: 0.85rem;
  margin: 0 0 0.6rem;
  padding: 0.5rem 0.6rem;
  background: var(--fat);
  border-left: 3px solid var(--fox);
  border-radius: 0 4px 4px 0;
}
.ws-list { list-style: none; margin: 0 0 0.6rem; padding: 0; }
.ws-list li { display: flex; align-items: center; border-radius: 4px; }
.ws-list li:hover { background: var(--code-bg); }
.ws-list li.is-open {
  background: linear-gradient(100deg,
    transparent 0.2rem, var(--highlight) 0.2rem,
    var(--highlight) calc(100% - 0.2rem), transparent calc(100% - 0.2rem));
}
.ws-files button.ws-file {
  flex: 1;
  min-width: 0;
  text-align: left;
  font: 400 0.86rem/1.35 var(--font-code);
  padding: 0.3rem 0.5rem;
  background: transparent;
  border: 0;
  color: var(--ink);
  overflow-wrap: anywhere;
}
.ws-list li.is-open button.ws-file { font-weight: 700; }
.ws-files button.ws-del, .ws-files button.ws-ren {
  flex: none;
  font-size: 1rem;
  line-height: 1;
  padding: 0.2rem 0.45rem;
  background: transparent;
  border: 0;
  color: var(--ink-soft);
  opacity: 0.8;           /* 4.5:1; full at hover and focus */
}
.ws-list li:hover button.ws-del, .ws-files button.ws-del:focus-visible,
.ws-list li:hover button.ws-ren, .ws-files button.ws-ren:focus-visible { opacity: 1; }
.ws-files button.ws-del:hover { color: var(--err); background: transparent; }
.ws-files button.ws-ren:hover { color: var(--ink); background: transparent; }
.ws-list li.is-renaming { padding: 0.15rem 0; }
.ws-files button.ws-action, .progress-dialog button.pd-secondary {
  font-size: 0.85rem;
  font-weight: 600;
  padding: 0.3rem 0.7rem;
  background: var(--paper);
  color: var(--ink);
  border: 1.5px solid var(--ink-soft);
}
.ws-files button.ws-action:hover, .progress-dialog button.pd-secondary:hover { background: var(--fat); border-color: var(--ink); }
.ws-files button.ws-action:disabled { opacity: 0.45; cursor: not-allowed; }
.ws-newname {
  width: 100%;
  font: 0.86rem/1.3 var(--font-code);
  padding: 0.35rem 0.5rem;
  border: 1.5px solid var(--ink);
  border-radius: 6px;
}
.ws-error { font-size: 0.8rem; color: var(--err); margin: 0.4rem 0 0; }
.ws-actions { display: flex; flex-wrap: wrap; gap: 0.4rem; margin-top: 0.8rem; }

.cell.ws-editor { margin: 0; }
.ws-tab {
  font: 700 0.85rem/1.3 var(--font-code);
  padding: 0.4rem 0.9rem;
  background: var(--code-bg);
  border-bottom: 1px solid var(--rule);
  overflow-wrap: anywhere;
}
.ws-editor .CodeMirror-scroll { min-height: 20em; }
/* a picture or PDF from the project, in place of the editor */
.ws-editor.is-preview .CodeMirror, .ws-editor.is-preview .ws-stdin { display: none; }
.ws-preview {
  padding: 1rem;
  background:
    repeating-conic-gradient(var(--code-bg) 0 25%, var(--paper) 0 50%) 0 0 / 16px 16px;
  text-align: center;
}
.ws-preview img {
  display: inline-block;
  max-width: 100%;
  vertical-align: middle;
  border: 1px solid var(--rule);
  background: #fff;
}
/* ChunkyPNG's 8x8 pixels, drawn big enough to see */
.ws-preview img.is-tiny { width: 160px; image-rendering: pixelated; }
/* a SQLite database: a few words in place of the editor */
.ws-preview p.ws-database {
  display: inline-block;
  max-width: 34rem;
  margin: 1.5rem 0;
  padding: 0.9rem 1.1rem;
  text-align: left;
  background: var(--paper);
  border: 1px solid var(--rule);
  border-radius: 6px;
}
.ws-preview iframe.ws-pdf {
  display: block;
  width: 100%;
  height: 28rem;
  border: 1px solid var(--rule);
  background: #fff;
}
.ws-stdin { border-top: 1px solid var(--rule); }
.ws-stdin summary {
  cursor: pointer;
  font-size: 0.85rem;
  font-weight: 600;
  color: var(--ink-soft);
  padding: 0.35rem 0.9rem;
}
.ws-stdin textarea {
  display: block;
  width: calc(100% - 1.8rem);
  margin: 0 0.9rem 0.6rem;
  font: 0.88rem/1.45 var(--font-code);
  padding: 0.4rem 0.5rem;
  border: 1px solid var(--rule);
  border-radius: 4px;
  resize: vertical;
}
/* a text file cannot be run (not the running state, which is disabled too) */
.run-cell[disabled]:not(:has(.run-fox)) { opacity: 0.45; cursor: not-allowed; }

/* ---------- the cheat sheet: a loose page lying beside the exercise book ----------
   shell/cheatsheet.rb. From 1280 px (Cheatsheet::OVERLAY) it stands to the
   right of the lesson and the page makes room for it, so a learner reads
   it while typing; below that it lies over the page. */
.cheatsheet {
  --cheat-width: 26rem;
  position: fixed;
  top: 0;
  right: 0;
  bottom: 0;
  z-index: 45;   /* over the index's toggle too, where a phone gives it the whole width */
  width: min(var(--cheat-width), 100vw);
  overflow-y: auto;
  overscroll-behavior: contain;
  scrollbar-width: thin;
  scrollbar-color: var(--rule) transparent;
  padding: 0 1.1rem 2.5rem;
  background: var(--paper);
  border-left: 2px solid var(--ink);
  box-shadow: 0 0 28px rgba(26, 18, 8, 0.25);
}
.cheatsheet[hidden] { display: none; }
@media (min-width: 1280px) {
  body.cheat-open .layout { margin-right: 26rem; }
  .cheatsheet { box-shadow: none; }
}
.cheat-head {
  position: sticky;
  top: 0;
  z-index: 2;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  margin: 0 -1.1rem;
  padding: 1.1rem 1.1rem 0.5rem;
  background: var(--paper);
}
.cheat-head h2 {
  margin: 0;
  font-family: var(--font-hand);
  font-weight: 700;
  font-variation-settings: "INFM" 50, "BNCE" 15;
  font-size: 1.45rem;
  line-height: 1.2;
}
.cheatsheet button.cheat-close {
  font-size: 1.4rem;
  line-height: 1;
  padding: 0.1rem 0.45rem;
  background: transparent;
  border: 0;
  color: var(--ink-soft);
}
.cheatsheet button.cheat-close:hover { color: var(--ink); background: var(--code-bg); }
.cheat-intro { margin: 0 0 0.6rem; font-size: 0.88rem; line-height: 1.45; color: var(--ink-soft); }
.cheat-search { position: relative; display: block; }
.cheat-search svg {
  position: absolute;
  left: 0.6rem;
  top: 50%;
  width: 14px;
  height: 14px;
  transform: translateY(-50%);
  fill: none;
  stroke: var(--ink-soft);
  stroke-width: 1.5;
  pointer-events: none;
}
#cheatSearch {
  width: 100%;
  font: 0.92rem/1.3 var(--font-print);
  color: var(--ink);
  padding: 0.42rem 0.6rem 0.42rem 1.9rem;
  border: 1.5px solid var(--rule);
  border-radius: 6px;
  background: var(--code-bg);
}
#cheatSearch:focus { background: var(--paper); border-color: var(--ink); }
#cheatSearch::placeholder { color: var(--ink-soft); opacity: 1; }
.cheat-toc { display: flex; flex-wrap: wrap; gap: 0.3rem; margin: 0.7rem 0 0.4rem; }
.cheatsheet button.cheat-chip {
  font-size: 0.78rem;
  font-weight: 600;
  padding: 0.12rem 0.55rem;
  border: 1px solid var(--rule);
  border-radius: 999px;
  background: var(--paper);
}
.cheatsheet button.cheat-chip:hover { background: var(--fat); border-color: var(--ink); }
.cheat-section { margin-top: 1.2rem; scroll-margin-top: 4rem; }
.cheat-section h3 {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.6rem;
  margin: 0 0 0.2rem;
  padding-bottom: 0.2rem;
  border-bottom: 1.5px solid var(--ink);
  font-size: 1.02rem;
  font-weight: 800;
}
.cheat-lessons { font-size: 0.78rem; font-weight: 600; color: var(--ink-soft); white-space: nowrap; }
.cheatsheet table.cheat { width: 100%; border-collapse: collapse; font-size: 0.85rem; line-height: 1.45; }
.cheatsheet .cheat td {
  padding: 0.32rem 0.6rem 0.32rem 0;
  border-bottom: 1px solid var(--rule);
  vertical-align: top;
  overflow-wrap: anywhere;
}
.cheatsheet .cheat td:last-child { padding-right: 0; }
.cheatsheet .cheat td:first-child { width: 47%; }
.cheatsheet .cheat code { font-family: var(--font-code); font-size: 0.95em; }
/* the code column keeps its lines; code inside the explanation flows */
.cheatsheet .cheat td:first-child code { white-space: pre-wrap; }
.cheatsheet .cheat td:last-child code {
  background: var(--code-bg);
  padding: 0.02rem 0.25rem;
  border-radius: 3px;
}
/* a longer example: the code as a block over the whole width, the
   explanation below it (Cheatsheet.row_html) */
.cheatsheet .cheat tr.cheat-wide td { width: auto; }
.cheatsheet .cheat tr.cheat-wide td > code {
  display: block;
  margin-bottom: 0.25rem;
  padding: 0.3rem 0.5rem;
  white-space: pre;
  overflow-x: auto;
  background: var(--code-bg);
  border-radius: 4px;
}
.cheat-docs { margin: 0.45rem 0 0; font-size: 0.82rem; }
.cheat-docs::before { content: "📖\00a0"; }
.cheat-docs span { font-weight: 700; }
.cheat-none { color: var(--ink-soft); font-size: 0.9rem; }

/* ---------- the progress dialog ---------- */
.progress-dialog {
  width: min(34rem, calc(100vw - 2rem));
  max-height: calc(100vh - 2rem);
  overflow: auto;
  padding: 1.3rem 1.5rem 1.1rem;
  border: 2px solid var(--ink);
  border-radius: 8px;
  background: var(--paper);
  color: var(--ink);
}
.progress-dialog::backdrop { background: rgba(26, 18, 8, 0.35); }
.pd-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 1rem; }
.pd-head h2 { margin: 0; font-size: 1.45rem; font-weight: 800; line-height: 1.2; }
.progress-dialog button.pd-close {
  font-size: 1.4rem;
  line-height: 1;
  padding: 0.1rem 0.45rem;
  background: transparent;
  border: 0;
  color: var(--ink-soft);
}
.progress-dialog button.pd-close:hover { color: var(--ink); background: var(--code-bg); }
.pd-intro { color: var(--ink-soft); margin: 0.5rem 0 0.4rem; }
.progress-dialog section { border-top: 1px solid var(--rule); margin-top: 1rem; padding-top: 0.2rem; }
.progress-dialog h3 { font-size: 1rem; font-weight: 700; margin: 0.8rem 0 0.2rem; }
.progress-dialog p { margin: 0.3rem 0 0.6rem; }
.pd-actions { display: flex; flex-wrap: wrap; gap: 0.5rem; }
.progress-dialog button.pd-primary { font-size: 0.95rem; padding: 0.45rem 1rem; }
.pd-connected { font-weight: 600; }
.pd-error, .pd-message.is-error { color: var(--err); }
.pd-busy { color: var(--ink-soft); font-style: italic; }   /* the offline copy being saved */
/* Python in the offline copy or not */
.pd-check { display: flex; align-items: flex-start; gap: 0.5rem; margin: 0.2rem 0 0.8rem; cursor: pointer; }
.pd-check input { margin: 0.25rem 0 0; accent-color: var(--fox); flex: none; }
.pd-message { min-height: 1.6em; margin: 1rem 0 0; font-weight: 600; }
.pd-message.is-ok { color: var(--ok); }

/* ---------- the foxes speak: a comic speech bubble, hand-lettered.
   The foxes have no names; "chunky bacon" is what they shout. ---------- */
#chunkyChat {
  --bubble: var(--paper);
  display: none;
  gap: 1.2rem;
  align-items: flex-end;
  margin: 2.2rem 0 1.2rem;
}
#chunkyChat.pass { --bubble: #e6f4e7; }
#chunkyChat.fail { --bubble: #fde6dc; }
#chunkyChat .chat-avatar { flex: none; }
#chunkyChat .chat-avatar img { width: 84px; display: block; }
/* a pass: both foxes shout it (why's chunky bacon strip, animated) */
#chunkyChat .chat-avatar .chat-cheer { display: none; }
#chunkyChat.pass .chat-avatar .chat-fox { display: none; }
#chunkyChat.pass .chat-avatar .chat-cheer { display: block; width: 190px; }
#chunkyChat .chat-message {
  position: relative;
  flex: 1;
  min-width: 0;
  margin-bottom: 1.6rem;
  padding: 0.9rem 1.3rem;
  background: var(--bubble);
  border: 2.5px solid var(--ink);
  /* uneven corners: drawn by hand, not by a box model */
  border-radius: 30px 22px 28px 18px / 22px 28px 20px 30px;
  font-family: var(--font-hand);
  font-variation-settings: "INFM" 35;
  font-size: 1.05rem;
  line-height: 1.55;
  animation: bubble-pop 0.3s cubic-bezier(0.3, 1.4, 0.5, 1);
  transform-origin: 0 100%;
}
/* a different name per state restarts the pop when the verdict changes */
#chunkyChat.pass .chat-message { animation-name: bubble-pop-pass; }
#chunkyChat.fail .chat-message { animation-name: bubble-pop-fail; }
@keyframes bubble-pop { from { opacity: 0; transform: scale(0.9); } }
@keyframes bubble-pop-pass { from { opacity: 0; transform: scale(0.9); } }
@keyframes bubble-pop-fail { from { opacity: 0; transform: scale(0.9); } }
/* the tail, pointing down to the fox's mouth: ink triangle, bubble inside */
#chunkyChat .chat-message::before,
#chunkyChat .chat-message::after {
  content: "";
  position: absolute;
  border-style: solid;
}
#chunkyChat .chat-message::before {
  left: -22px;
  bottom: 10px;
  border-width: 0 0 16px 26px;
  border-color: transparent transparent var(--ink) transparent;
  transform: skewY(-18deg);
}
#chunkyChat .chat-message::after {
  left: -15px;
  bottom: 13.5px;
  border-width: 0 0 11px 19px;
  border-color: transparent transparent var(--bubble) transparent;
  transform: skewY(-18deg);
}
#chunkyChat .chat-message p { margin: 0; }
#chunkyChat .chat-message strong { font-weight: 700; }
#chunkyChat .chat-message small { font-size: 0.9em; color: var(--ink-soft); }
#chunkyChat .chat-message a { color: var(--ink); font-weight: 700; text-decoration-color: var(--fox); text-decoration-thickness: 3px; }
#chunkyChat kbd { font-size: 0.78em; }

/* the dark tail, under a strip of the seats' yellow tartan */
footer {
  position: relative;
  margin-top: 3rem;
  text-align: center;
  padding: 4.5rem 1.5rem 3rem;
  background: var(--cap);
  color: var(--cap-text);
  font-size: 0.88rem;
}
footer::before {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  top: 0;
  height: 2.5rem;
  background-color: var(--mustard);
  background-image:
    repeating-linear-gradient(0deg, transparent 0 14px, rgba(255, 250, 235, 0.6) 14px 16px, transparent 16px 22px, rgba(153, 140, 96, 0.5) 22px 30px, transparent 30px 44px),
    repeating-linear-gradient(90deg, transparent 0 14px, rgba(255, 250, 235, 0.6) 14px 16px, transparent 16px 22px, rgba(153, 140, 96, 0.5) 22px 30px, transparent 30px 44px);
}
footer p { margin: 0.4rem auto; max-width: 40rem; }
footer a { color: var(--bakelite); }

/* ---------- small screens: the lesson fills the page, the index is a drawer
   that slides in over it (shell/app.rb: App::NARROW is this width) ---------- */
@media (max-width: 820px) {
  header, body.sidebar-closed header { gap: 0.6rem; padding: 0.6rem 1rem 0.6rem 3.5rem; }
  header #mascot { width: 40px; height: 48px; padding-top: 4px; }
  header h1 { font-size: 1.3rem; }
  header p { font-size: 0.85rem; }
  header select { padding: 0.35rem 0.5rem; }

  /* no margin strip on a small screen: a sliver of paper and a line
     beside the page read as a rendering glitch, and the room is short */
  .column { margin-left: 0; border-left: 0; }
  main { padding: 1.4rem 1rem 2rem 1rem; }
  .sidebar-toggle { top: 0.85rem; left: 0.6rem; }
  body.sidebar-closed .sidebar-toggle { background: transparent; border-color: transparent; }
  /* here the toggle sits on the dark masthead, or on the drawer when it is out */
  .sidebar-toggle, body.sidebar-closed .sidebar-toggle { color: var(--bakelite); }
  .sidebar-toggle:hover, body.sidebar-closed .sidebar-toggle:hover { background: #2c323c; }
  body.sidebar-open .sidebar-toggle { color: var(--ink); }
  body.sidebar-open .sidebar-toggle:hover { background: var(--fat); }
  /* visibility follows the slide, so a closed drawer leaves the tab order
     instead of taking the keyboard somewhere nobody can see */
  .sidebar,
  body.sidebar-closed .sidebar {
    display: flex;
    position: fixed;
    left: 0;
    top: 0;
    bottom: 0;
    z-index: 30;
    width: min(19rem, 86vw);
    height: auto;
    transform: translateX(-100%);
    visibility: hidden;
    transition: transform 0.18s, visibility 0s 0.18s;
  }
  body.sidebar-open .sidebar {
    transform: none;
    visibility: visible;
    transition: transform 0.18s;
    border-right: 2px solid var(--ink);
    box-shadow: 0 0 28px rgba(26, 18, 8, 0.25);
  }
  .sidebar-head { padding-top: 0.95rem; }
  /* the page behind an open drawer steps back; a tap on it closes the drawer */
  body.sidebar-open::after {
    content: "";
    position: fixed;
    inset: 0;
    z-index: 25;
    background: rgba(26, 18, 8, 0.35);
  }
  nav#lessonNav a.lesson { padding-top: 0.42rem; padding-bottom: 0.42rem; }
  .lessonText h2 { font-size: 1.7rem; }
  .cell .CodeMirror { font-size: 14px; }
  .workshop { grid-template-columns: minmax(0, 1fr); }
  .ws-editor .CodeMirror-scroll { min-height: 14em; }
  #chunkyChat { gap: 0.9rem; }
  #chunkyChat .chat-avatar img { width: 60px; }
  #chunkyChat.pass .chat-avatar .chat-cheer { width: 130px; }
}
/* phones: the title is the brand, the subtitle is the first thing to go */
@media (max-width: 520px) {
  header { gap: 0.5rem; }
  header #mascot { width: 36px; height: 44px; }
  header p { display: none; }
  header h1 { font-size: 1.05rem; }
  header .progress-btn, header .cheat-btn { padding: 0.35rem 0.5rem; }
  header .progress-label, header .cheat-label { display: none; }
}
/* narrow phones: three buttons need the room, the fox still waves from
   the speech bubble below */
@media (max-width: 420px) {
  header .home-link { display: none; }
  header h1 { font-size: 1rem; }
}

@media (prefers-reduced-motion: reduce) {
  .spinner-image,
  .load-bar::before,
  .cell.running::after,
  .run-cell .run-fox,
  .cell-out.reveal,
  .cell.shake,
  .cell.celebrate,
  .run-time.fresh,
  #chunkyChat .chat-message { animation: none; }
  .cell.running .CodeMirror { animation: none; opacity: 0.55; }
  button:active { transform: none; }
  .sidebar, body.sidebar-open .sidebar, #navBarFill, .nav-chevron, #gemsPanel summary::after { transition: none; }
}

/* The Ruboto lesson's phone (ruboto_phone.js, docs/HANDOVER.md §6u): an
   Android 14 phone in a dark bezel, 360 x 640 dp at 1 dp = 1 px (scaled down
   in a narrow column), logcat beside it. Inside the screen the colours are
   Android's Material 3 light theme, not the course's: it is the app's look.
   Outside it - the bar, logcat - the course's light or dark scheme. */
.ruboto-slot { border-top: 1px solid var(--rule); padding: 0.55rem 0.7rem 0.7rem; background: var(--sheet); overflow-x: auto; }
/* what the toasts say, for screen readers only */
.rp-live { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
.ruboto-slot { position: relative; }
.rp-bar { display: flex; flex-wrap: wrap; align-items: center; gap: 0.4rem 0.8rem; margin-bottom: 0.5rem; }
.rp-bar button { font-size: 0.85rem; padding: 0.25rem 0.75rem; }
.rp-badge { font-size: 0.8rem; color: var(--ink-soft); }
.rp-stage { display: flex; flex-wrap: wrap; gap: 0.8rem; align-items: flex-start; }
.rp-holder { position: relative; flex: none; }
.rp-device {
  position: absolute; left: 0; top: 0; transform-origin: 0 0;
  background: #17191d; border-radius: 28px; padding: 12px; box-shadow: 0 2px 10px rgba(0, 0, 0, 0.25);
}
.rp-screen {
  --md-primary: #6750a4; --md-on-primary: #ffffff; --md-surface: #fef7ff; --md-on-surface: #1d1b20;
  --md-variant: #49454f; --md-outline: #79747e; --md-container: #f3edf7; --md-secondary: #e8def8;
  position: relative; display: flex; flex-direction: column; overflow: hidden;
  background: var(--md-surface); color: var(--md-on-surface); border-radius: 16px;
  font-family: Roboto, "Segoe UI", system-ui, -apple-system, sans-serif; font-size: 14px; line-height: 1.3;
}
.rp-device.is-portrait .rp-screen { width: 360px; height: 712px; }
.rp-device.is-landscape .rp-screen { width: 640px; height: 432px; }
/* the keyboard's place is always visible inside the screen */
.rp-screen button:focus-visible, .rp-screen [role="button"]:focus-visible, .rp-screen select:focus-visible,
.rp-screen input[type="range"]:focus-visible { outline: 3px solid var(--focus); outline-offset: -3px; }
.rp-screen:focus { outline: none; }
/* the course's buttons (cream, 2 px ink, Outfit) stop at the screen's edge */
.rp-screen button, .rp-screen button:where(:hover, :active) {
  font-family: inherit; font-weight: inherit; line-height: inherit; border: none; box-shadow: none; transform: none;
  background: transparent; color: inherit; border-radius: 0;
}
.rp-screen .rp-status { flex: none; height: 24px; display: flex; justify-content: space-between; align-items: center; padding: 0 14px; font-size: 12px; background: var(--md-container); }
.rp-screen .rp-body { position: relative; flex: 1 1 auto; display: flex; flex-direction: column; min-height: 0; }
.rp-screen .rp-nav { flex: none; height: 48px; display: flex; justify-content: space-around; align-items: center; background: #f0eaf3; }
.rp-screen .rp-nav button { background: none; border: none; box-shadow: none; color: var(--md-variant); font-size: 18px; width: 64px; height: 40px; border-radius: 20px; padding: 0; cursor: pointer; }
.rp-screen .rp-nav button:hover:not(:disabled) { background: rgba(103, 80, 164, 0.1); }
.rp-screen .rp-nav button:disabled { opacity: 0.35; cursor: default; }
.rp-screen .rp-appbar { flex: none; height: 56px; display: flex; align-items: center; gap: 4px; padding: 0 4px 0 16px; background: var(--md-container); }
.rp-screen .rp-title { flex: 1 1 auto; font-size: 20px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.rp-screen .rp-action, .rp-screen .rp-more { background: none; border: none; box-shadow: none; color: var(--md-primary); font-weight: 500; font-size: 14px; line-height: 1; padding: 10px 12px; border-radius: 20px; cursor: pointer; }
.rp-screen .rp-more { color: var(--md-variant); font-size: 20px; padding: 8px 14px; }
.rp-screen .rp-content { position: relative; flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column; overflow: auto; }
.rp-screen .rp-content.is-dialog-theme { background: rgba(0, 0, 0, 0.32); justify-content: center; align-items: center; }
.rp-screen .rp-root { flex: 1 1 auto; min-height: 0; }
.rp-screen .rp-v { box-sizing: border-box; min-width: 0; }
.rp-screen .rp-linear { display: flex; }
.rp-screen .rp-linear.is-vertical { flex-direction: column; }
.rp-screen .rp-linear.is-horizontal { flex-direction: row; }
.rp-screen .rp-frame { display: grid; grid-template: 1fr / 1fr; }
.rp-screen .rp-frame > * { grid-area: 1 / 1; }
.rp-screen .rp-scroll { overflow: auto; }
.rp-screen .rp-text { display: flex; flex-direction: column; white-space: pre-wrap; overflow-wrap: anywhere; }
.rp-screen .rp-text.is-single span { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.rp-screen .rp-button {
  margin: 4px; min-height: 40px; padding: 8px 20px; border: none; border-radius: 20px; box-shadow: none;
  background: var(--md-primary); color: var(--md-on-primary); font-weight: 500; font-size: 14px; line-height: 1.2; cursor: pointer;
}
.rp-screen .rp-button:hover:not(:disabled) { filter: brightness(1.1); }
.rp-screen .rp-button:disabled, .rp-screen .rp-button.is-disabled { background: rgba(29, 27, 32, 0.12); color: rgba(29, 27, 32, 0.38); cursor: default; }
.rp-screen .rp-edit {
  margin: 4px 0; padding: 8px 4px; border: none; border-bottom: 1px solid var(--md-outline); border-radius: 0;
  background: transparent; color: inherit; font: inherit; outline: none; box-shadow: none; width: auto;
}
.rp-screen .rp-edit:focus { border-bottom: 2px solid var(--md-primary); }
.rp-screen .rp-check { display: flex; align-items: center; gap: 10px; min-height: 40px; cursor: pointer; }
.rp-screen .rp-check input { width: 18px; height: 18px; accent-color: var(--md-primary); margin: 0; }
/* Android 14's switch: the text first, a track with a thumb at the end */
.rp-screen .rp-check.is-switch { flex-direction: row-reverse; justify-content: space-between; }
.rp-screen .rp-check.is-switch input {
  appearance: none; -webkit-appearance: none; position: relative; flex: none; width: 52px; height: 32px; border-radius: 16px;
  border: 2px solid var(--md-outline); background: var(--md-container); box-sizing: border-box; cursor: pointer;
}
.rp-screen .rp-check.is-switch input::after {
  content: ""; position: absolute; top: 6px; left: 6px; width: 16px; height: 16px; border-radius: 50%;
  background: var(--md-outline); transition: left 0.1s, width 0.1s, height 0.1s, top 0.1s;
}
.rp-screen .rp-check.is-switch input:checked { background: var(--md-primary); border-color: var(--md-primary); }
/* disabled, as Android draws it: 38 % */
.rp-screen .rp-check.is-disabled { opacity: 0.38; cursor: default; }
.rp-screen .rp-check.is-disabled input { cursor: default; }
.rp-screen .rp-check.is-switch input:checked::after { left: 22px; top: 2px; width: 24px; height: 24px; background: var(--md-on-primary); }
.rp-screen .rp-check.is-switch input:focus-visible, .rp-screen .rp-check input:focus-visible { outline: 3px solid var(--focus); outline-offset: 2px; }
.rp-screen .rp-seek { margin: 8px 4px; accent-color: var(--md-primary); }
.rp-screen .rp-progress { margin: 8px 4px; accent-color: var(--md-primary); }
.rp-screen .rp-image { position: relative; overflow: hidden; display: flex; }
.rp-screen .rp-image img { position: absolute; inset: 0; width: 100%; height: 100%; display: block; }
.rp-screen .rp-image:not(.is-wrap) { min-height: 48px; }
.rp-screen .rp-image.is-wrap img { position: static; height: auto; max-height: 100%; }
.rp-screen .rp-list { display: flex; flex-direction: column; overflow: auto; }
.rp-screen .rp-li { display: flex; flex-direction: column; }
.rp-screen .rp-row { display: block; text-align: left; padding: 12px 16px; min-height: 48px; box-sizing: border-box; font-size: 16px; line-height: 1.5; color: inherit; background: none; border: none; border-bottom: 1px solid #e7e0ec; border-radius: 0; box-shadow: none; }
.rp-screen button.rp-row { cursor: pointer; }
.rp-screen button.rp-row:hover { background: rgba(103, 80, 164, 0.08); }
.rp-screen .rp-spinner { margin: 4px 0; padding: 8px; font: inherit; color: inherit; background: var(--md-surface); border: 1px solid var(--md-outline); border-radius: 4px; }
.rp-screen .rp-v.is-clickable:not(button) { cursor: pointer; }
.rp-screen .rp-menu-layer { position: absolute; inset: 0; z-index: 3; }
.rp-screen .rp-menu { position: absolute; right: 8px; top: 8px; min-width: 160px; background: var(--md-container); border-radius: 4px; box-shadow: 0 3px 10px rgba(0, 0, 0, 0.3); padding: 8px 0; display: flex; flex-direction: column; }
.rp-screen .rp-menu-item { text-align: left; background: none; border: none; box-shadow: none; border-radius: 0; padding: 12px 16px; font-size: 14px; color: inherit; cursor: pointer; }
.rp-screen .rp-menu-item:hover { background: rgba(103, 80, 164, 0.08); }
.rp-screen .rp-scrim { position: absolute; inset: 0; z-index: 4; background: rgba(0, 0, 0, 0.32); display: flex; align-items: center; justify-content: center; }
.rp-screen .rp-dialog { background: var(--md-container); color: var(--md-on-surface); border-radius: 28px; padding: 24px; width: 280px; max-height: 80%; overflow: auto; box-shadow: 0 6px 24px rgba(0, 0, 0, 0.3); display: flex; flex-direction: column; gap: 12px; }
.rp-screen .rp-dialog-activity { padding: 16px; }
.rp-screen .rp-dialog-title { font-size: 22px; line-height: 1.25; }
.rp-screen .rp-dialog-message { color: var(--md-variant); }
.rp-screen .rp-dialog-item { text-align: left; background: none; border: none; box-shadow: none; border-radius: 8px; padding: 10px 8px; font-size: 16px; color: inherit; cursor: pointer; }
.rp-screen .rp-dialog-item:hover { background: rgba(103, 80, 164, 0.08); }
.rp-screen .rp-dialog-buttons { display: flex; justify-content: flex-end; gap: 8px; }
.rp-screen .rp-dialog-button { background: none; border: none; box-shadow: none; color: var(--md-primary); font-weight: 500; font-size: 14px; padding: 10px 12px; border-radius: 20px; cursor: pointer; }
.rp-screen .rp-dialog-button:hover { background: rgba(103, 80, 164, 0.08); }
.rp-screen .rp-toasts { position: absolute; left: 0; right: 0; bottom: 64px; display: flex; justify-content: center; pointer-events: none; z-index: 5; }
.rp-screen .rp-toast { max-width: 80%; background: #313033; color: #f4eff4; border-radius: 20px; padding: 10px 16px; font-size: 14px; box-shadow: 0 2px 6px rgba(0, 0, 0, 0.3); animation: rp-toast-in 0.15s ease-out; }
@keyframes rp-toast-in { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: none; } }
.rp-screen .rp-home { flex: 1 1 auto; display: flex; flex-direction: column; align-items: center; gap: 36px; padding-top: 56px; background: linear-gradient(160deg, #3b2f63, #6a4e8c 55%, #c58b6a); color: #fff; }
.rp-screen .rp-home-clock { font-size: 56px; font-weight: 300; letter-spacing: 1px; }
.rp-screen .rp-home-empty { opacity: 0.7; }
.rp-screen .rp-app-icon { display: flex; flex-direction: column; align-items: center; gap: 6px; background: none; border: none; box-shadow: none; color: #fff; font-size: 13px; cursor: pointer; padding: 8px; border-radius: 16px; }
.rp-screen .rp-app-icon img { width: 56px; height: 56px; border-radius: 50%; background: #fef7ff; padding: 6px; box-sizing: border-box; }
.rp-screen .rp-app-icon:hover { background: rgba(255, 255, 255, 0.12); }
.rp-logcat { flex: 1 1 12rem; min-width: 11rem; max-width: 100%; display: flex; flex-direction: column; border: 1px solid var(--rule); border-radius: 6px; background: var(--cap); color: var(--cap-text); align-self: stretch; max-height: 40rem; }
.rp-logcat-head { display: flex; justify-content: space-between; align-items: center; padding: 0.3rem 0.5rem; border-bottom: 1px solid #343a44; }
.rp-logcat-title { font-family: var(--font-display); font-weight: 700; color: #fff; }
.rp-logcat-clear { font-size: 0.75rem; padding: 0.1rem 0.5rem; }
.rp-logcat-lines { flex: 1 1 auto; overflow: auto; padding: 0.35rem 0.5rem; font: 0.75rem/1.45 var(--font-code); min-height: 8rem; }
.rp-log { white-space: pre-wrap; overflow-wrap: anywhere; }
.rp-log-time { opacity: 0.6; margin-right: 0.5em; }
.rp-log-level { font-weight: 700; margin-right: 0.4em; }
.rp-log.is-D .rp-log-level { color: #7cc4ff; }
.rp-log.is-I .rp-log-level { color: #8fd18f; }
.rp-log.is-W .rp-log-level { color: var(--clip); }
.rp-log.is-E, .rp-log.is-A { color: #ff9b8a; }
.rp-log-tag { color: #fff; }
.ruboto-slot.is-stale .rp-stage { opacity: 0.45; pointer-events: none; filter: grayscale(0.6); }
.rp-note { margin-top: 0.5rem; font-size: 0.9rem; color: var(--ink-soft); }
@media (prefers-reduced-motion: reduce) {
  .rp-toast { animation: none; }
  .rp-screen .rp-check.is-switch input::after { transition: none; }
}
@media (max-width: 620px) {
  .rp-stage { flex-direction: column; }
  .rp-logcat { flex-basis: auto; width: 100%; max-height: 16rem; }
}
