/* AI Literacy module - shared styles.
 *
 * Light and dark are both first-class. Self-study material gets read late at
 * night on whatever device is to hand, and a page that only works in one scheme
 * is a page that is uncomfortable to read half the time.
 */

:root, [data-theme="dark"] {
  color-scheme: dark;

  --ink: #e6edf3;
  --muted: #9198a1;
  --line: #30363d;
  --bg: #0d1117;
  --panel: #161b22;
  --accent: #2dd4bf;
  --accent-soft: #10352f;
  --accent-ink: #5eead4;
  --warn: #e3b341;
  --warn-soft: #3a2d0f;
  --ok: #3fb950;
  --ok-soft: #12291a;
  --on-accent: #06231f;

  --signal: #e5484d;
  --signal-soft: #2a1215;
  --signal-ink: #ff6b6b;
}

[data-theme="light"] {
  color-scheme: light;

  --ink: #1b1f23;
  --muted: #57606a;
  --line: #d8dee4;
  --bg: #f6f8fa;
  --panel: #ffffff;
  --accent: #0f766e;
  --accent-soft: #d9f0ec;
  --accent-ink: #075b53;
  --warn: #8a5a00;
  --warn-soft: #fff4d6;
  --ok: #15703a;
  --ok-soft: #dcf5e3;
  --on-accent: #ffffff;

  --signal: #c0272d;
  --signal-soft: #fbe9ea;
  --signal-ink: #8f1d22;
}

* {
  box-sizing: border-box;
}

/* `hidden` must win over a class that sets display: the scripts hide the bar's
 * Next link at the end of a route and the animation pause button until it can
 * work, and `.chrome-step { display: block }` used to override it. */
[hidden] {
  display: none !important;
}

body {
  margin: 0;
  font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  color: var(--ink);
  background: var(--bg);
  line-height: 1.55;
}

/* No maximum width: a reader who has made their window wide has done so on
   purpose, and a reader who wants shorter lines can narrow it. */
header,
main,
footer {
  width: 92%;
  margin: 0 auto;
}

.storage-notice {
  border: 2px solid var(--warn);
  background: var(--warn-soft);
  padding: 14px 16px;
  margin: 14px 0 20px;
}

.storage-notice h3 {
  margin-top: 0;
}

.storage-state {
  font-weight: 600;
}

.learner-work,
.unit-record,
.unit-checkpoints {
  margin-top: 32px;
  padding-top: 20px;
  border-top: 2px solid var(--line);
}

.learner-work textarea {
  display: block;
  box-sizing: border-box;
  width: 100%;
  margin-top: 8px;
  padding: 10px;
  border: 1px solid var(--line);
  border-radius: 4px;
  background: var(--bg);
  color: var(--ink);
  font: inherit;
  resize: vertical;
}

.completion-check {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  margin: 18px 0 8px;
}

.record-settings {
  display: grid;
  grid-template-columns: minmax(10rem, 16rem) minmax(0, 28rem);
  gap: 10px 16px;
  align-items: center;
}

.record-settings input,
.record-settings select {
  box-sizing: border-box;
  width: 100%;
  padding: 7px 9px;
  border: 1px solid var(--line);
  border-radius: 4px;
  background: var(--bg);
  color: var(--ink);
  font: inherit;
}

.progress-list article {
  padding: 12px 0;
  border-bottom: 1px solid var(--line);
}

.progress-list article h3,
.progress-list article p {
  margin: 4px 0;
}

.progress-list article[data-state="complete"] {
  border-left: 5px solid var(--ok);
  padding-left: 12px;
}

.record-actions {
  display: grid;
  gap: 12px;
  max-width: 48rem;
}

.record-actions fieldset,
#import-conflicts fieldset {
  display: grid;
  gap: 8px;
  border: 1px solid var(--line);
  padding: 12px;
}

.record-actions button,
#import-preview button {
  width: fit-content;
  padding: 7px 14px;
  border: 1px solid var(--accent);
  border-radius: 4px;
  background: var(--accent);
  color: var(--on-accent);
  font: inherit;
  cursor: pointer;
}

.record-actions button:disabled {
  cursor: not-allowed;
  opacity: 0.65;
}

.export-actions {
  grid-template-columns: repeat(2, fit-content(22rem));
}

#import-preview {
  margin-top: 18px;
  padding: 16px;
  border: 1px solid var(--line);
}

@media (max-width: 600px) {
  .record-settings {
    grid-template-columns: 1fr;
    gap: 6px;
  }

  .export-actions {
    grid-template-columns: 1fr;
  }
}

.storage-notice button {
  padding: 7px 14px;
  border: 1px solid var(--accent);
  border-radius: 4px;
  background: var(--accent);
  color: var(--on-accent);
  font: inherit;
  cursor: pointer;
}

.storage-notice button:disabled {
  cursor: default;
  opacity: 0.75;
}

header {
  padding: 40px 0 20px;
}

h1 {
  margin: 0 0 8px;
  font-size: clamp(1.9rem, 5vw, 3.2rem);
  line-height: 1.1;
}

h2 {
  margin: 32px 0 12px;
  font-size: 1.35rem;
}

.crumb {
  margin: 0 0 12px;
  font-size: 0.92rem;
}

/* The persistent navigation bar. Small on purpose: it has to say where you are
   and where you can go without taking space from what you came to read. It
   wraps rather than scrolls, so the 320px reflow check passes, and it collapses
   to the unit number plus a menu on a narrow screen. */
.chrome {
  position: sticky;
  top: 0;
  z-index: 10;
  /* Fixed proportions, not a wrapping row. A long unit title used to push the
     step links onto a second line and double the height of the bar; now each
     column truncates its own text and the order never changes. The columns are
     assigned explicitly, so a missing "Previous" at Unit 0 leaves its space
     rather than shifting "Next" into it. */
  display: grid;
  grid-template-columns: 50% 25% 25%;
  align-items: baseline;
  width: 92%;
  margin: 0 auto;
  padding: 8px 0;
  background: var(--bg);
  border-bottom: 1px solid var(--line);
  font-size: 0.92rem;
}

.chrome > * {
  min-width: 0;
}

/* At phone widths a quarter of the bar is narrower than "Previous" (47px for
 * 54px at 320px); the menu, which truncates by design, gives up the room. */
@media (max-width: 380px) {
  .chrome {
    grid-template-columns: 40% 30% 30%;
  }
}

.chrome-menu {
  position: relative;
  grid-column: 1;
}

.chrome-prev {
  grid-column: 2;
}

.chrome-next {
  grid-column: 3;
  text-align: right;
}

.chrome-step,
.chrome-menu > summary {
  display: block;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
  padding-right: 12px;
}

.chrome-unit {
  font-weight: 600;
}

.chrome-title::before {
  content: " \00B7 ";
}

.chrome-title,
.chrome-position {
  color: var(--muted);
}

.chrome-position::before {
  content: " \00B7 ";
}

.chrome-end {
  color: var(--muted);
}

/* A disclosure triangle of our own, so the label reads as a menu rather than as
   an ordinary link: the native marker is inconsistent between browsers and is
   hidden by the ellipsis rules above. The arrow turns when the menu opens, so
   the state is shown by shape and position, never by colour alone. */
.chrome-menu > summary {
  cursor: pointer;
  color: var(--accent-ink);
  list-style: none;
}

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

/* At the start, not the end: the label truncates with an ellipsis, so a trailing
   marker is the first thing to be cut -- exactly when a long unit title makes it
   least obvious that the label opens a menu. */
.chrome-menu > summary::before {
  content: "\25be";
  display: inline-block;
  margin-right: 6px;
  transition: transform 0.15s;
}

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

.chrome-menu > summary:hover,
.chrome-menu[open] > summary {
  text-decoration: underline;
}

/* Under the label it opens from, not off at the far edge of the column. */
.chrome-menu > ul {
  position: absolute;
  left: 0;
  z-index: 11;
  margin: 6px 0 0;
  padding: 10px 16px;
  list-style: none;
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: 8px;
  box-shadow: 0 6px 16px rgb(0 0 0 / 35%);
  white-space: nowrap;
}

.chrome-menu > ul li {
  margin: 4px 0;
}

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

/* Where the chosen route begins, or carries on. Empty until the script fills
   it, so it takes no space when there is nothing to say. */
.route-start {
  margin: 8px 0 0;
  font-size: 1.06rem;
}

.route-start:empty,
.route-timing:empty {
  display: none;
}

.route-timing {
  margin: 4px 0 0;
  color: var(--muted);
}

/* One row of controls rather than a column of stacked buttons. */
.record-actions-row {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  max-width: none;
}

.record-restore {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 10px 16px;
  margin-top: 14px;
}

.unit-progress-meta {
  color: var(--muted);
  font-size: 0.92rem;
}

.colophon {
  margin-top: 6px;
  font-size: 0.86rem;
}

/* Text for screen readers only: in the layout, but not painted. Used where a
   glyph or a number carries the meaning visually and would be meaningless read
   aloud on its own. */
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* Every unit, as a row of numbers at the foot of the navigation menu. Previous
   and next walk the module a step at a time; this is for jumping. */
.unit-jump {
  margin-top: 10px;
  padding-top: 10px;
  border-top: 1px solid var(--line);
}

.unit-jump p {
  margin: 0 0 6px;
  color: var(--muted);
}

.unit-jump ul {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin: 0;
  padding: 0;
  list-style: none;
}

.unit-jump a {
  display: inline-block;
  min-width: 2rem;
  padding: 2px 0;
  border: 1px solid var(--line);
  border-radius: 4px;
  text-align: center;
  text-decoration: none;
}

.unit-jump a:hover {
  text-decoration: underline;
}

.unit-jump a[aria-current="page"] {
  border-color: var(--accent);
  background: var(--accent-soft);
  font-weight: 600;
}

/* The same step links again at the end of the page, so finishing a reading and
   leaving it are the same gesture. */
.pager {
  display: flex;
  flex-wrap: wrap;
  gap: 10px 20px;
  margin: 32px 0 0;
  padding: 14px 0 0;
  border-top: 1px solid var(--line);
}

.pager-hub {
  margin-left: auto;
}

/* The default route's next step, for readers without script: on its own line
   under the step links (site-nav.js hides it once it has retargeted them). */
.pager-route {
  flex-basis: 100%;
  margin: 0;
}

/* Narrow screens keep the bar to "where am I" plus the menu. The target names
   stay in the DOM -- hidden, never removed -- so a screen reader still hears
   "Next: Unit 5, Verification lab" rather than a bare "Next".
   This block sits after the rules it overrides on purpose: a media query does
   not raise specificity, so an earlier one would simply lose. */
@media (max-width: 600px) {
  .chrome {
    grid-template-columns: 60% 20% 20%;
  }

  .chrome-target,
  .chrome-title {
    display: none;
  }

  .chrome-menu > ul {
    position: absolute;
    right: auto;
    left: 0;
    max-width: 90vw;
    white-space: normal;
  }
}


.summary {
  margin: 0;
  color: var(--muted);
  font-size: 1.06rem;
}



.notice {
  margin: 24px 0;
  padding: 14px 16px;
  border: 1px solid var(--line);
  background: var(--panel);
  border-left: 5px solid var(--accent);
  border-radius: 0 6px 6px 0;
}

/* Unit marks and module parts.
 *
 * The marks take their colour from the element they sit in -- they are drawn
 * with `currentColor` -- so a part sets one hue and everything inside it
 * follows, in both themes, with no second palette to keep in step.
 *
 * Colour here is redundant by design. Every part prints its name above its
 * units and every unit carries its own mark, so a reader who cannot separate
 * the hues loses nothing: see check_accessibility.py, and the note at the top
 * of course/parts.yml. The lightness and saturation are fixed per theme so one
 * part cannot shout over another, and the hue never touches text -- it is used
 * on rules, marks and edges, never as a foreground colour on a background it
 * has not been contrast-checked against.
 */
.unit-icon {
  width: 1.5em;
  height: 1.5em;
  flex: none;
  vertical-align: -0.3em;
}

/* The site is dark unless the reader picks light (:root above), whatever the
 * system preference, so the part colours default to the dark values too; a
 * light-preference media query here used to put light-scheme colours on the
 * dark ground (1.6:1). */
[data-part] {
  --part: hsl(var(--part-hue) 62% 68%);
  --part-edge: hsl(var(--part-hue) 35% 30%);
}

[data-theme="light"] [data-part] {
  --part: hsl(var(--part-hue) 58% 34%);
  --part-edge: hsl(var(--part-hue) 45% 88%);
}

.part {
  margin: 34px 0 0;
}

.part > h3 {
  display: flex;
  align-items: baseline;
  gap: 10px;
  margin: 0 0 4px;
  font-size: 1.05rem;
  letter-spacing: 0.01em;
}

/* The rule carries the part's colour so the grouping is visible while
 * scanning, without tinting any text. */
.part > h3::after {
  content: "";
  flex: 1;
  height: 2px;
  border-radius: 2px;
  background: var(--part);
  opacity: 0.55;
}

.part > p {
  margin: 0 0 6px;
  color: var(--muted);
  max-width: 62ch;
}

.part .unit-grid {
  margin-top: 14px;
}

/* A card belongs to its part by its left edge. One border rather than a fill,
 * so the card background stays the panel colour that everything else uses and
 * the text keeps its checked contrast. */
.part article {
  border-left: 3px solid var(--part-edge);
}

.part article h3 {
  display: flex;
  align-items: flex-start;
  gap: 9px;
}

.part article .unit-icon {
  color: var(--part);
  margin-top: 0.1em;
}

/* On the unit page itself the mark sits beside the title. */
.unit-title {
  display: flex;
  align-items: flex-start;
  gap: 12px;
}

.unit-title .unit-icon {
  width: 1.15em;
  height: 1.15em;
  color: var(--part);
  margin-top: 0.12em;
}

/* The part name above the unit title: small, and the only place the part's
 * colour touches text -- so it is set at the checked --muted weight and the
 * hue is carried by the mark beside it instead. */
.unit-part {
  margin: 2px 0 0;
  font-size: 0.82rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--muted);
}

.unit-jump .unit-icon {
  width: 1.1em;
  height: 1.1em;
}

.unit-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: 14px;
  margin: 24px 0 40px;
  padding: 0;
  list-style: none;
}

article {
  min-height: 210px;
  padding: 18px;
  border: 1px solid var(--line);
  border-radius: 8px;
  background: var(--panel);
  display: flex;
  flex-direction: column;
  gap: 12px;
}

article h2,
article h3 {
  margin: 0;
  font-size: 1.12rem;
  line-height: 1.25;
}

article p {
  margin: 0;
  color: var(--muted);
}

.meta {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-top: auto;
  font-size: 0.84rem;
}

.tag,
.status {
  padding: 3px 9px;
  border-radius: 999px;
  max-width: 100%;
}

/* Status pills are single words and look better unbroken. Course-item tags
 * carry the full item name -- "Item 6: Re-Prompting,
 * Specification, And Overreliance" -- and a pill holding a sentence has to be
 * allowed to wrap, or it alone pushes the page past a 320px viewport. */
.status {
  white-space: nowrap;
}

.tag {
  white-space: normal;
}

.tag {
  background: var(--accent-soft);
  color: var(--accent-ink);
}

.status {
  background: var(--warn-soft);
  color: var(--warn);
}

.status[data-status="ready"] {
  background: var(--ok-soft);
  color: var(--ok);
}

.links {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  margin-top: 4px;
  font-size: 0.92rem;
}

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

a:focus-visible,
button:focus-visible,
input:focus-visible,
select:focus-visible,
textarea:focus-visible {
  outline: 3px solid var(--accent);
  outline-offset: 2px;
}

/* The one link to a unit's activity, placed in the reading at the point where
   the activity should be done. Deliberately loud: it is an instruction to stop
   reading and go and do something. */
.do-now {
  margin: 28px 0;
  padding: 14px 18px;
  border: 1px solid var(--accent);
  border-left: 5px solid var(--accent);
  border-radius: 0 6px 6px 0;
  background: var(--accent-soft);
}

.do-now p {
  margin: 0;
  font-size: 1.06rem;
}

.timings {
  color: var(--muted);
}

/* Checkpoint cards. Rendered by site/assets/selfcheck-widget.js on unit pages
   and on the self-check page, which inlines this stylesheet, so the rules have
   to live here rather than in either page. */
.cp { border: 1px solid var(--line); border-radius: 8px; background: var(--panel);
      padding: 18px; margin: 0 0 16px; overflow-wrap: anywhere; }
.cp *, .progress * { min-width: 0; max-width: 100%; }
.cp[data-state="pass"] { border-left: 5px solid var(--ok); }
.cp[data-state="fail"] { border-left: 5px solid var(--warn); }
.cp h3 { margin: 0 0 4px; font-size: 1.05rem; }
.cp .unit-tag { font-size: 0.82rem; color: var(--muted); }
.cp .question { margin: 12px 0; }
.cp ol.options { margin: 8px 0; padding-left: 22px; }
.cp ol.options li { margin: 4px 0; }
.cp .match-row { display: flex; flex-wrap: wrap; gap: 8px; align-items: center;
                 margin: 6px 0; }
.cp .match-row span { flex: 1 1 260px; min-width: 0; }
/* A select will not shrink below its longest option unless told to, and the
 * options here are whole sentences. Without this the match rows push the page
 * past a 320px viewport (WCAG 1.4.10 Reflow). */
.cp .match-row select { max-width: 100%; }
.cp select { max-width: 100%; }
.cp .code-note { white-space: pre-wrap; overflow-wrap: anywhere; }
.cp label { display: block; margin: 6px 0; }
.cp input[type="text"] { padding: 6px 8px; border: 1px solid var(--line);
      border-radius: 4px; font: inherit; background: var(--bg); color: var(--ink);
      min-width: 12ch; }
.cp select { padding: 5px 6px; border: 1px solid var(--line); border-radius: 4px;
      font: inherit; background: var(--bg); color: var(--ink); }
.cp button { padding: 7px 14px; border: 1px solid var(--accent); border-radius: 4px;
      background: var(--accent); color: var(--on-accent); font: inherit; cursor: pointer; }
.cp button.secondary { background: transparent; color: var(--accent); }
.cp .controls { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 10px; }
.feedback { margin-top: 12px; padding: 12px 14px; border-radius: 6px;
      border-left: 4px solid var(--line); background: var(--bg); }
.feedback.pass { border-left-color: var(--ok); }
.feedback.fail { border-left-color: var(--warn); }
.feedback.info { border-left-color: var(--accent); }
.feedback:empty { display: none; }
/* Sits below the sticky navigation bar rather than under it. */
.progress { position: sticky; top: 2.6rem; z-index: 5; background: var(--panel);
      border: 1px solid var(--line); border-radius: 8px; padding: 12px 16px;
      margin: 0 0 24px; }
.progress-bar { height: 8px; border-radius: 4px; background: var(--line);
      overflow: hidden; margin-top: 8px; }
.progress-bar div { height: 100%; background: var(--ok); width: 0; transition: width .25s; }
.code-note { font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
      font-size: 0.9rem; background: var(--bg); padding: 10px 12px;
      border-radius: 4px; white-space: pre-wrap; overflow-wrap: anywhere; }

/* Long, unbreakable strings (bare URLs, DOIs, hashes) are the one thing that
   makes a text page scroll sideways on a phone. Break them rather than let the
   page overflow: WCAG 1.4.10 reflow is checked at 320px. */
main {
  overflow-wrap: break-word;
}

code,
a {
  overflow-wrap: anywhere;
}

code {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 0.92em;
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: 4px;
  padding: 0.1em 0.35em;
  /* Inline code here is mostly file paths, and a path offers no break
   * opportunity by default -- `course/lab/exercises/ex04_bernoulli.py`
   * alone is wider than a 320px viewport (WCAG 1.4.10 Reflow). */
  overflow-wrap: anywhere;
}

pre {
  overflow-x: auto;
  padding: 12px 14px;
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: 6px;
}

pre code {
  border: 0;
  padding: 0;
  background: none;
}

table {
  border-collapse: collapse;
  width: 100%;
  margin: 16px 0;
  display: block;
  overflow-x: auto;
}

th,
td {
  border: 1px solid var(--line);
  padding: 7px 10px;
  text-align: left;
}

th {
  background: var(--panel);
}

.reviewer-path {
  margin: 32px 0;
  padding: 16px 18px;
  border: 1px dashed var(--line);
  border-radius: 8px;
  background: var(--panel);
}

.reviewer-path h2 {
  margin-top: 0;
}

footer {
  padding: 24px 0 48px;
  margin-top: 32px;
  border-top: 1px solid var(--line);
  color: var(--muted);
  font-size: 0.92rem;
}

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

@media (prefers-reduced-motion: reduce) {
  * {
    transition: none !important;
    animation: none !important;
  }
}


/* ---------------------------------------------------------------------------
 * Gothic register
 *
 * AUTHORING.md section 12 records this as a design decision with a reason: Ada
 * Lovelace was Byron's daughter, and the anxiety about a made thing that
 * reasons begins in the same century and the same household. Units 0-17 use the
 * restrained end of the register; Unit 18 adds `class="gothic"` for the full
 * one, matching the talk it derives from.
 *
 * The limits are not negotiable. Contrast wins over atmosphere, and colour
 * never carries meaning alone.
 * ------------------------------------------------------------------------ */

.signal {
  color: var(--signal-ink);
  font-weight: 600;
}

.gothic {
  /* Red marks divergence and nothing else (AUTHORING.md section 12), so links
   * and controls keep the site's teal accent; only --signal is red. */
  --on-accent: #06231f;
  --bg: #0b0b0d;
  --panel: #141417;
  --line: #2a2a30;
  --ink: #ece9e6;
  --muted: #8e8a86;
  --accent: #2dd4bf;
  --accent-ink: #5eead4;
  --accent-soft: #10352f;
  color-scheme: dark;

  /* This class forces a near-black ground regardless of the reader's colour
   * scheme, so every token that assumes a light ground has to be re-stated
   * here. It originally was not: in a light-preference browser --signal-ink
   * stayed at its light value and rendered dark red on near-black at 2.22:1 --
   * and .signal is precisely the class that marks the thing that is wrong, so
   * the one element a reader must not miss was the one that disappeared. The
   * dark-scheme values are repeated rather than inherited because a class
   * cannot reach into a media query. */
  --warn: #e3b341;
  --warn-soft: #3a2d0f;
  --ok: #3fb950;
  --ok-soft: #12291a;
  --signal: #e5484d;
  --signal-soft: #2a1215;
  --signal-ink: #ff6b6b;
}

.gothic h1,
.gothic h2 {
  letter-spacing: -0.01em;
}

.gothic .rule {
  height: 2px;
  border: 0;
  margin: 6px 0 22px;
  background: linear-gradient(90deg, var(--accent), transparent);
}

/* Song and source quotations in Unit 9. Attribution is mandatory: the lyrics
 * quoted are in copyright and are used briefly, for commentary. */
.gothic blockquote,
blockquote.quote {
  margin: 24px 0;
  padding: 4px 0 4px 20px;
  border-left: 3px solid var(--signal);
  font-style: italic;
  color: var(--ink);
}

blockquote.quote cite {
  display: block;
  margin-top: 8px;
  font-style: normal;
  font-size: 0.86rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--signal-ink);
}

/* Animations are embedded live and must never depend on motion to be readable;
 * each one also ships a still and a prose alternative. */
/* Browsers give <figure> a 40px horizontal margin by default, which is enough
 * on its own to break reflow at a 320px viewport. */
figure {
  margin: 24px 0;
}

/* Each embed has its own animation's shape (--animation-aspect, from the SVG's
 * viewBox), so none is letterboxed or cropped. Its frame is a size container:
 * an animation switches to its compact layout when its *own* width is 600px or
 * less, which is not the same moment as the page's, so the embed follows the
 * frame's width too (the 2px are the iframe's border). */
.animation-frame {
  container-type: inline-size;
}

.animation {
  display: block;
  width: 100%;
  max-width: 960px;
  height: auto;
  aspect-ratio: var(--animation-aspect, 960 / 420);
  border: 1px solid var(--line);
  border-radius: 8px;
  background: #0d1117;
}

.animation-alt {
  margin: 8px 0 24px;
  font-size: 0.94rem;
  color: var(--muted);
}

.animation-open {
  display: block;
  width: fit-content;
  margin-top: 6px;
}

.animation-toggle {
  margin-top: 8px;
  padding: 5px 12px;
  border: 1px solid var(--accent);
  border-radius: 4px;
  background: transparent;
  color: var(--accent-ink);
  font: inherit;
  font-size: 0.94rem;
  cursor: pointer;
}

/* Narrow: an animation with a compact layout takes that layout's height (it is
 * flowing text, not a scaled drawing); one without keeps its own shape. */
@media (max-width: 600px) {
  .animation {
    height: var(--animation-mobile-height, auto);
    aspect-ratio: var(--animation-mobile-aspect, var(--animation-aspect, 960 / 420));
  }
}

@container (max-width: 602px) {
  .animation {
    height: var(--animation-mobile-height, auto);
    aspect-ratio: var(--animation-mobile-aspect, var(--animation-aspect, 960 / 420));
  }
}

/* ---------------------------------------------------------------------------
 * Site chrome shared by every page: skip link, review banner, media
 * placeholders, and the download note on rendered reference documents.
 * ------------------------------------------------------------------------ */

/* First focusable element on each page; visually hidden until focused so
 * keyboard and screen-reader users can bypass the header on every page. */
.skip-link {
  position: absolute;
  left: -999px;
  top: 0;
  padding: 8px 14px;
  background: var(--panel);
  border: 2px solid var(--accent);
  border-radius: 0 0 6px 0;
  z-index: 20;  /* above the sticky .chrome bar (10), which used to hide it */
}

.skip-link:focus {
  left: 0;
}

.review-notice {
  padding: 1rem;
  margin-bottom: 1rem;
  border: 1px solid var(--warn);
  border-radius: 4px;
  background: var(--warn-soft);
  color: var(--warn);
  text-align: center;
  font-weight: bold;
}

.media-placeholder {
  border: 2px dashed var(--line);
  border-radius: 8px;
  padding: 1em;
  margin: 1em 0;
  background: var(--panel);
}

.media-placeholder h3 {
  margin-top: 0;
}

.doc-download {
  font-size: 0.92rem;
  color: var(--muted);
}

@media print {
  .skip-link,
  .review-notice,
  .storage-notice,
  .record-actions,
  .learner-work,
  .unit-record,
  .unit-checkpoints,
  .chrome,
  .pager,
  .unit-jump,
  .crumb {
    display: none !important;
  }
}

.featured-unit {
  grid-column: 1 / -1;
}
