/* learn.sigreturn.com: what the platform adds on top of the site's style.css.
   Same tokens, same rules: Archivo, square corners, hairlines, navy only for emphasis. */

/* Archivo's own italic (the same Google Fonts latin subset as the upright, OFL): without it the browser slants the
   upright face, and the last letter of an emphasised word runs into the space after it ("the video isthe"). It is
   only downloaded by a page that sets italic text (an exercise sheet, for one). */
@font-face {
  font-family: "Archivo";
  src: url("/static/archivo-italic-latin.woff2?v=3d109f97a7") format("woff2");
  font-weight: 100 900;
  font-style: italic;
  font-display: swap;
}
/* The letters the site's Latin file lacks (Latin Extended, Vietnamese: Jiří, Łukasz, Nguyễn, Ağaoğlu), from the face the
   certificates print with (learn/fonts/archivo-cert.woff2, OFL): without it a student's name on the public certificate
   page, a question's author line or the account's Name field took those letters from a system font. The range is what
   that face has beyond the Latin file, so a page downloads it only once such a character is on it. */
@font-face {
  font-family: "Archivo";
  src: url("/static/archivo-ext.woff2?v=497ab70f10") format("woff2");
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0100-0101, U+0103-0130, U+0132-0151, U+0154-017F, U+018F, U+0192, U+019D, U+01A0-01A1,
                 U+01AF-01B0, U+01C4-01DC, U+01E6-01E7, U+01EA-01EB, U+01FA-021B, U+022A-022D, U+0230-0233, U+0237,
                 U+0259, U+0272, U+02B9-02BA, U+02C7, U+02C9, U+02D8-02D9, U+02DB, U+02DD, U+0302, U+0306-0307,
                 U+030A-030C, U+030F, U+0311-0313, U+031B, U+0324, U+0326-0328, U+032E, U+0331, U+0335-0338, U+0394,
                 U+03A9, U+03BC, U+03C0, U+1E24-1E25, U+1E62-1E63, U+1E6C-1E6D, U+1E80-1E85, U+1E9E, U+1EA0-1EF9,
                 U+2010-2011, U+2015, U+2017, U+2020-2021, U+2030, U+203E, U+2052, U+2070, U+2074-2079, U+207F-2089,
                 U+2099, U+20A1, U+20A3-20A4, U+20A6-20A7, U+20A9, U+20AB, U+20AD, U+20B1-20B2, U+20B5, U+20B9-20BA,
                 U+20BC-20BD, U+2105, U+2113, U+2116-2117, U+2126, U+212E, U+2190, U+2192, U+2194-2195, U+21A8,
                 U+2202, U+2205-2206, U+220F, U+2211, U+2219-221A, U+221E-221F, U+2229, U+222B, U+2248, U+2260-2261,
                 U+2264-2265, U+2302, U+2310, U+2320-2321, U+25CA, U+27E8-27E9, U+FB01-FB02;
}
/* a tap is shown by the control's own pressed state (.lbtn:active), not by the browser's cyan flash on Android */
html { -webkit-tap-highlight-color: transparent; }

/* One column for every page, the header's: the title starts under the logo whatever the page. A page of
   reading (account, sign-in, report) keeps the 42rem measure, left-aligned in that column. */
.learn-main {
  max-width: var(--max-w);
  margin: 0 auto;
  padding: clamp(2rem, 5vw, 3.5rem) var(--pad-x) clamp(3rem, 6vw, 5rem);
  min-height: 60vh;
  /* a word with no space in it (a 49-letter address, an 80-letter certificate name) breaks where it would run past the
     column instead of widening a phone's page; ordinary words never break */
  overflow-wrap: break-word;
}
/* :where, so a component's own narrower width (a form's 26rem) still wins */
:where(.learn-main:not(.learn-wide)) > * { max-width: var(--measure); }
/* viewport-fit=cover lets the player's full screen reach the iPhone's edges; everything else keeps clear
   of the notch and the home bar (env() is 0 everywhere else) */
body > .nav, .learn-main, .footer-inner { padding-left: max(var(--pad-x), env(safe-area-inset-left));
                                          padding-right: max(var(--pad-x), env(safe-area-inset-right)); }
.footer-inner { padding-bottom: max(clamp(1.5rem, 3vw, 2rem), env(safe-area-inset-bottom)); }

/* Vertical rhythm. The site's reset zeroes every paragraph's margin; inside learn pages a paragraph keeps a
   space under it, at the lowest specificity (:where) so every component below still decides its own. */
:where(.learn-main) :is(p, ul, ol, dl, pre, table, details) { margin-top: 0; margin-bottom: 1rem; }
:where(.learn-main) :is(p, ul, ol, dl, pre, table, details):last-child { margin-bottom: 0; }
/* the brand's weights stop at 600: the browser's bold (700) for strong text and headings is off the system */
:where(.learn-main) :is(strong, b, h1, h2, h3, h4, th, summary) { font-weight: 600; }

/* A link in the page looks like one: the site's reset strips every link of its underline, and a download or an
   invoice set in body colour read as plain text. Lists that are plainly links (the lesson rows, the admin
   menu, admin tables) and links drawn as buttons keep their own look. One underline for every link that has one
   (a component only says that it has one, text-decoration-line): 1px, a soft line that turns solid on hover. */
:where(.learn-main) a { text-underline-offset: 2px; text-decoration-thickness: 1px;
                        text-decoration-color: color-mix(in srgb, currentColor 45%, transparent); }
:where(.learn-main) a:hover { text-decoration-color: currentColor; }
:where(.learn-main) a:not([class]) { text-decoration-line: underline; }
:where(.ltitle, .admin-nav) a:not([class]) { text-decoration-line: none; }
:where(.ltitle) a:not([class]):hover { text-decoration-line: underline; }

.learn-head { display: flex; flex-direction: column; gap: 0.6rem; margin-bottom: 2.25rem; }
.learn-head h1 { font-size: var(--fs-title); font-weight: 600; letter-spacing: -0.03em; line-height: 1.1; }
/* the course page's title beside the course's mark (views.COURSE_MARK, styled by style.css) */
.learn-head-mark { display: flex; align-items: center; gap: 1rem; }
/* 'lesson 11 of 12' in one piece, under the video (.lplace): a narrow line breaks after the comma, or inside the
   section's name */
.lpos { white-space: nowrap; }
/* a 77-character address or a long name on a certificate breaks inside the column instead of widening a phone */
.learn-head h1, .learn-head .learn-lede { overflow-wrap: anywhere; }
.learn-lede, .learn-text { color: var(--muted); font-size: var(--fs-lede); line-height: 1.55; max-width: var(--measure); }
/* the text of a message page is what to do next: read as text, not as a muted aside, at the Text size (DESIGN.md):
   the lede's size made it a second lede, under the real one on the report page */
.learn-text { color: var(--fg-soft); font-size: var(--fs-md); }
.learn-actions { display: flex; gap: 0.75rem; flex-wrap: wrap; align-items: center; margin-top: 1.5rem; }
.learn-actions .lsmall { flex-basis: 100%; }
/* A row of buttons (a page's, or a form's two: Validate and Back) fits on one line or stacks whole, every button at one
   width, as the certificate's do: two side by side over a third, or a long one over a short one, read ragged. learn.js
   measures which, at any width (is-line, is-stacked); without it, a phone stacks every row of two. On a phone a row that
   fits shares its line. A form's row of small buttons (a post's Save and Delete) keeps their own widths */
@media (max-width: 37.5rem) {
  .learn-actions:where(:not(.codes-actions)) > :is(.lbtn, form),   /* the backup codes' pair has its own (below) */
  .lform-actions:where(:has(> .lbtn ~ .lbtn):not(:has(> .lbtn-sm))) > .lbtn { flex: 1 1 100%; }
  .learn-actions > form { display: flex; }
  .learn-actions > form > .lbtn { flex: 1 1 auto; }
  /* with scripts (theme.js marks the page in the head, html.js-rows), a row of two that learn.js measures is painted
     as it will stay: sharing the line, or each on a line of its own once they wrap, which for two is both is-line and
     is-stacked. Stacked until the deferred script came, a pair that fits jumped side by side on a first visit (CLS
     0.1 on /account/confirm). A row of three still waits for the measure */
  :where(html.js-rows) :is(.learn-actions:where(:not(.codes-actions)), .lform-actions):where(:not(:has(> .lbtn-sm)):has(
      > :is(.lbtn, form) ~ :is(.lbtn, form)):not(:has(> :is(.lbtn, form) ~ :is(.lbtn, form) ~ :is(.lbtn, form))))
    > :is(.lbtn, form) { flex: 1 1 auto; }
  :is(.learn-actions, .lform-actions).is-line > :is(.lbtn, form) { flex: 1 1 auto; }
}
:is(.learn-actions, .lform-actions).is-stacked > :is(.lbtn, form) { flex: 1 1 100%; }
.learn-actions.is-stacked > form { display: flex; }
.learn-actions.is-stacked > form > .lbtn { flex: 1 1 auto; }
/* how the buttons' own widths lay the row out, the moment learn.js measures it */
:is(.learn-actions, .lform-actions).is-natural > :is(.lbtn, form) { flex: 0 1 auto; }
/* the next step at the top of the course page: what it is, why, and its button */
.lnextstep { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 1rem 2rem;
             border: 1px solid var(--rule); background: var(--bg-card); padding: 1.25rem 1.4rem; margin: 0 0 1.75rem; }
.lnextstep-main { display: flex; flex-direction: column; gap: 0.35rem; flex: 1 1 22rem; min-width: 0; }
.lnextstep-main > p { margin: 0; }
.lnextstep-title { font-size: var(--fs-xl); font-weight: 600; letter-spacing: -0.018em; line-height: 1.3; }
.lnextstep-acts { display: flex; flex-wrap: wrap; gap: 0.75rem; align-items: center; }
.lnextstep-acts form { margin: 0; }
.llegend { margin: 0 0 1.25rem; max-width: 40rem; }
.learn-text a { color: var(--fg); text-decoration-line: underline; }
.learn-text strong { color: var(--fg); font-weight: 600; overflow-wrap: anywhere; }   /* an address to check for typos */
/* points of a page (what the purchase gives, why a guarantee ended): hairline rows, as the account lists */
/* the price on the buy page: the one figure the page is about, on its own line */
.lprice { font-size: var(--fs-2xl); font-weight: 600; letter-spacing: -0.018em; margin: 0 0 1.25rem; }
.lprice .lsmall { font-weight: 400; letter-spacing: 0; }
/* the buy page keeps one column, the points' own: the price, the button, then what the purchase gives and asks,
   under a hairline, so the button is on a laptop's first screen with the word of the step just taken (a notice
   under the title) above it */
.lform[action="/buy"] { max-width: 40rem; }
.notice:has(+ .lprice) { max-width: 40rem; }
.lbuy-points { border-top: 1px solid var(--rule); }
/* /buy signed out: creating an account and signing in, two equal ways, side by side where both fit (the new account
   first, on the left or on top: most buyers have none yet), each under a hairline, with its own heading and button */
.lways { display: grid; gap: 2.25rem 3rem; }
.lway { border-top: 1px solid var(--rule); padding-top: 1.25rem; min-width: 0; }
.lway h2 { font-size: var(--fs-xl); font-weight: 600; letter-spacing: -0.015em; margin: 0 0 1rem; }
.lway > p { color: var(--fg-soft); }
/* two ways of equal weight: a 26rem sign-in column left the new account ~140px between 641 and 720px */
@media (min-width: 641px) { .lways { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); max-width: 56rem; } }
.lpoints { list-style: none; padding: 0; margin: 0 0 1.75rem; max-width: 40rem; }
/* a long unbroken name or address (a certificate's, in the audit log) breaks inside the column on a phone */
.lpoints li { padding: 0.75rem 0; line-height: 1.55; overflow-wrap: anywhere; }
.lpoints li + li { border-top: 1px solid var(--rule); }
.lpoints a, .lsteps a { color: var(--fg); text-decoration-line: underline; }
/* reasons under a small lead (the account's guarantee): at the lead's size and colour, never louder than it */
.lpoints-small { margin: -0.5rem 0 1rem; }
.lpoints-small li { padding: 0.2rem 0; font-size: var(--fs-xs); color: var(--muted); line-height: 1.5; }
.lpoints-small li + li { border-top: 0; }
/* numbered next steps (after the purchase) */
.lsteps { counter-reset: step; list-style: none; padding: 0; margin: 0 0 1.75rem; max-width: 40rem; }
.lsteps li { counter-increment: step; position: relative; padding: 0.9rem 0 0.9rem 2rem; line-height: 1.55; }
.lsteps li::before { content: counter(step); position: absolute; left: 0; font-weight: 600; font-variant-numeric: tabular-nums; }
.lsteps li + li { border-top: 1px solid var(--rule); }

.lform { display: flex; flex-direction: column; gap: 1.1rem; max-width: 26rem; margin: 0 0 1.75rem; }
.lform:last-child { margin-bottom: 0; }
.lform-actions { display: flex; gap: 0.75rem; flex-wrap: wrap; align-items: center; margin-top: 0.35rem; }
.lform .lsmall, .lform .notice { margin: 0; }
/* the second step paused after too many wrong codes: the form stays in view, greyed, until the wait ends */
.lform-paused { border: 0; padding: 0; margin: 0; min-width: 0; }
.lform-paused input:disabled, .lform-paused .lswitch:disabled { opacity: 0.55; cursor: default; }
.lform-paused .lbtn:disabled { cursor: default; }
.notice:has(+ [data-wait] + .lform-paused) { max-width: 26rem; }   /* as wide as the form it pauses */
/* "What you typed is back" (learn.js): a line of its own, above a one-line form's field and button too */
.ldraft-note { width: 100%; margin: 0; }
.lfield { display: flex; flex-direction: column; gap: 0.4rem; }
.llabel { font-size: var(--fs-sm); font-weight: 600; }
.lhint, .lsmall { font-size: var(--fs-xs); color: var(--muted); line-height: 1.5; }
.lerror { color: var(--status-brick); font-weight: 600; }
/* a field refused in place (learn.js): in a one-row form, under the whole row; under a tick box, aligned with its words */
.lerror-row { flex-basis: 100%; }
.lcheck + .lcheck-hint { padding-left: 1.6rem; }
@media (pointer: coarse) { .lcheck + .lcheck-hint { padding-left: 1.85rem; } }
/* A field's edge is a control, not a divider: it keeps 3:1 against the page and its own fill (WCAG 1.4.11),
   where the hairline (1.3:1) left an empty field nearly invisible. Ticks and radios take the ink, not the
   browser's blue. */
:root { --field-edge: #868b94; }
html.dark { --field-edge: #6b727d; }
@media (prefers-color-scheme: dark) { html:not(.light) { --field-edge: #6b727d; } }
.learn-main input[type="checkbox"], .learn-main input[type="radio"] { accent-color: var(--fg); }
/* Tick boxes are drawn, one look in every engine and both themes (the native ones are 13 px, faint in Safari, and
   grey squares in Chrome's dark scheme): a square on the field's edge, filled with ink and ticked once checked. Low
   specificity (:where), so a component's size and place (.lcheck, a sheet's step, a quiz choice) still win. */
:where(.learn-main) input[type="checkbox"] {
  -webkit-appearance: none; appearance: none; display: inline-grid; place-content: center; flex: none;
  width: 1.1rem; height: 1.1rem; margin: 0; vertical-align: -0.2rem; border: 1px solid var(--field-edge);
  border-radius: var(--r-md); background: var(--bg-card); cursor: pointer;
  -webkit-print-color-adjust: exact; print-color-adjust: exact; }   /* a ticked step prints ticked */
:where(.learn-main) input[type="checkbox"]::before {
  content: ""; width: 0.65rem; height: 0.65rem; background: var(--bg-card); transform: scale(0);
  clip-path: polygon(14% 44%, 0 65%, 50% 100%, 100% 16%, 80% 0, 43% 62%); }
:where(.learn-main) input[type="checkbox"]:checked { background: var(--fg); border-color: var(--fg); }
:where(.learn-main) input[type="checkbox"]:checked::before { transform: scale(1); }
:where(.learn-main) input[type="checkbox"]:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 2px; }
:where(.learn-main) input[type="checkbox"]:disabled { opacity: 0.55; cursor: default; }
@media (hover: hover) { :where(.learn-main) input[type="checkbox"]:hover { border-color: var(--fg); } }
/* Windows high contrast drops backgrounds: the system's own box there */
@media (forced-colors: active) { :where(.learn-main) input[type="checkbox"] { -webkit-appearance: auto; appearance: auto; } }
.lfield input {
  font: inherit;
  padding: 0.65rem 0.75rem;
  border: 1px solid var(--field-edge);
  border-radius: var(--r-md);
  background: var(--bg-card);
  color: var(--fg);
}
.lfield input:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 1px; }
/* a field at fault: its whole edge in brick (The Hairline Rule: no coloured side stripe) */
.lfield [aria-invalid="true"], .tn-form input[aria-invalid="true"], .srename-form input[aria-invalid="true"] {
  border-color: var(--status-brick); }
/* the browsers' own placeholder grey falls under 4.5:1 on the dark card */
.lfield input::placeholder { color: var(--muted); opacity: 1; }
.lfield input[autocomplete="one-time-code"] { max-width: 16rem; font-variant-numeric: tabular-nums; letter-spacing: 0.06em; }
/* the second step's switch between the digits keypad and letters (learn.js, phones only) */
.lswitch { align-self: flex-start; font: inherit; font-size: var(--fs-sm); color: var(--fg); background: none; border: 0;
           padding: 0.35rem 0; cursor: pointer; text-decoration: underline; text-underline-offset: 2px; }
.lswitch:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 2px; }
@media (pointer: coarse) { .lswitch { min-height: 2.75rem; } }   /* a finger's 44px, still a link to the eye */
/* a new password's Show button (learn.js), joined to its field */
.lreveal { display: flex; }
.lreveal input { flex: 1; min-width: 0; border-radius: var(--r-md) 0 0 var(--r-md); }
.lreveal input:focus-visible { position: relative; z-index: 1; }   /* its focus ring over the button's edge */
.lreveal-btn { font: inherit; font-size: var(--fs-sm); font-weight: 600; padding: 0 0.9rem; border: 1px solid var(--rule);
               border-left: 0; border-radius: 0 var(--r-md) var(--r-md) 0; background: var(--bg-card); color: var(--fg);
               cursor: pointer; transition: background-color 160ms ease, border-color 160ms ease, color 160ms ease; }
/* an outline button as tall as the field it is joined to: inverted on hover and focus, as .lbtn-ghost is */
.lreveal-btn:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 1px;
                             background: var(--fg); color: var(--bg); border-color: var(--fg); }
@media (hover: hover) { .lreveal-btn:hover { background: var(--fg); color: var(--bg); border-color: var(--fg); } }
/* the box and its sentence: the sentence is one item (its links and period stay in the line), the box on its first line */
.lcheck { font-size: var(--fs-sm); color: var(--muted); display: flex; gap: 0.6rem; align-items: flex-start; cursor: pointer;
          line-height: 1.5; }
.lcheck input { width: 1rem; height: 1rem; margin: 0.15rem 0 0; flex: none; }
.lcheck a, .lsmall a, .notice a { color: var(--fg); text-decoration-line: underline; }
/* a box left unticked that the form needs (the sign-up's terms): its edge in brick, as a field at fault */
.lcheck input[aria-invalid="true"]:not(:focus-visible) { outline: 2px solid var(--status-brick); outline-offset: 1px; }
/* what the box's sentence leaves out (the sign-up's terms): just under it, aligned with its words */
.lform .lcheck-hint { margin: -0.8rem 0 0; padding-left: 1.6rem; }
/* under the box's refusal (said between the box and its hint), a line's breath below it, not against it */
.lform .lerror.lcheck-hint + .lcheck-hint { margin-top: -0.8rem; }
/* fingers: the whole sentence is the target, a 44px row at least, and the box a size a thumb can see */
@media (pointer: coarse) {
  .lcheck { padding-block: 0.75rem; }
  .lcheck input { width: 1.25rem; height: 1.25rem; margin-top: 0.05rem; }
  .lform .lcheck-hint { margin-top: -1.1rem; padding-left: 1.85rem; }   /* the sentence's row stays whole to tap */
}

.lbtn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  align-self: flex-start;
  font: inherit;
  font-weight: 600;
  line-height: 1.2;
  white-space: nowrap;
  max-width: 100%;
  text-decoration: none;
  padding: 0.7rem 1.25rem;
  border: 1px solid var(--fg);
  border-radius: var(--r-md);
  background: var(--fg);
  color: var(--bg);
  cursor: pointer;
  transition: background-color 160ms ease, border-color 160ms ease, color 160ms ease;   /* DESIGN.md: 160ms */
}
/* hover only where there is one: a phone keeps :hover on the button last tapped, and an outline button left
   inverted after "Copy" reads as the page's main action */
.lbtn:focus-visible { background: var(--fg-soft); }
@media (hover: hover) { .lbtn:hover { background: var(--fg-soft); } }
/* the site's outline button (DESIGN.md, Buttons): a card on a hairline, inverted to ink on hover */
.lbtn-ghost { background: var(--bg-card); color: var(--fg); border-color: var(--rule); }
.lbtn-ghost:focus-visible { background: var(--fg); color: var(--bg); border-color: var(--fg); }
@media (hover: hover) { .lbtn-ghost:hover { background: var(--fg); color: var(--bg); border-color: var(--fg); } }
.lbtn:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 2px; }
/* pressed: what a finger sees, where there is no hover */
.lbtn:active { background: var(--fg-soft); border-color: var(--fg-soft); color: var(--bg); }
.lbtn:disabled { opacity: 0.55; cursor: progress; }
.lbtn[aria-disabled="true"] { opacity: 0.55; cursor: progress; }   /* busy and still focused (report.js) */
.lbtn[hidden] { display: none; }   /* its display would beat the attribute (a button that shows once a wait ends) */
/* a long label (the Resume button carries a lesson title) wraps instead of running off a phone */
@media (max-width: 640px) { .lbtn { white-space: normal; text-align: center; } }
.lbtn-sm { padding: 0.45rem 0.85rem; font-size: var(--fs-sm); }
/* fingers: a small button is still 44px tall (WCAG 2.5.5, Apple's 44pt) */
@media (pointer: coarse) { .lbtn-sm { min-height: 2.75rem; } }
.lbtn-danger { background: transparent; color: var(--status-brick); border-color: var(--status-brick); }
.lbtn-danger:focus-visible { background: color-mix(in srgb, var(--status-brick) 8%, transparent); color: var(--status-brick);
                              border-color: var(--status-brick); }   /* a ghost's ink edge (above) would win otherwise */
@media (hover: hover) {
  .lbtn-danger:hover { background: color-mix(in srgb, var(--status-brick) 8%, transparent); color: var(--status-brick);
                       border-color: var(--status-brick); } }
.lbtn-danger:active { background: color-mix(in srgb, var(--status-brick) 16%, transparent); border-color: var(--status-brick);
                      color: var(--status-brick); }

/* Sign out is a button in a form: as a flex item (not a line box) it sits on the same line as the links */
.nav-form { margin: 0; display: flex; }
/* half signed in (the second step is due), Sign out is the bar's only link: at the right, where links always are */
.brand + .nav-form { margin-left: auto; }
.linklike { font: inherit; font-size: var(--fs-sm); color: var(--muted); background: none; border: 0; border-bottom: 1px solid transparent; cursor: pointer; padding: 0 0 2px; }
.linklike:hover { color: var(--fg); border-bottom-color: var(--fg); }
.nav .linklike, .nav-links a { line-height: inherit; white-space: nowrap; }
.nav-contact[aria-current] { color: var(--fg); border-bottom-color: var(--fg); }
.nav-links a[aria-current="true"] { color: var(--fg); }
@media (hover: none) { .nav .linklike { padding-block: 0.7rem; } }
/* Below 400px the site drops its links to a second tier when it has no menu script (html:not(.js-nav)), which
   is always the case here. Learn has three links and they fit: they stay in the bar, beside Sign out. */
@media (max-width: 400px) {
  html:not(.js-nav) .nav { flex-wrap: nowrap; gap: 0.6rem; }
  html:not(.js-nav) .nav-links { order: 0; flex-basis: auto; margin: 0 0 0 auto; padding: 0; border: 0;
                                 justify-content: flex-end; gap: 0.75rem; }
  html:not(.js-nav) .nav-contact { margin-left: 0; }
}
/* The administrator's fourth link does not fit beside Sign out under 360px (measured: 305px of items for 280px of
   room): the links take the site's own second tier, under a hairline, rather than break "Sign out" in two. */
@media (max-width: 359px) {
  html:not(.js-nav) .nav:has(.nav-links > a:nth-child(3)) { flex-wrap: wrap; row-gap: 0; }
  html:not(.js-nav) .nav:has(.nav-links > a:nth-child(3)) .nav-links {
    order: 3; flex-basis: 100%; margin: 0.35rem 0 0; padding-top: 0.55rem; border-top: 1px solid var(--rule);
    justify-content: flex-start; gap: 1.25rem; }
  html:not(.js-nav) .nav:has(.nav-links > a:nth-child(3)) .nav-form { margin-left: auto; }
}

.notice {
  border: 1px solid var(--rule);
  background: var(--bg-card);
  padding: 0.85rem 1.1rem;
  margin: 0 0 1.75rem;
  font-size: var(--fs-md);
  line-height: 1.5;
}
.notice-error { border-color: var(--status-brick); }
.notice strong { font-weight: 600; overflow-wrap: anywhere; }   /* an address, which has no space to break at */
/* a notice keeps to the column it speaks about: the reading column of a wide page (a quiz's questions, the notes
   under a lesson), the form under it on a page of forms. A quiz's result (or its closed attempt) is about the
   attempt, not about the Start button under it: it keeps the reading column, during the wait and after it alike. */
.learn-wide .notice { max-width: 46rem; }
/* a notice stacked with cards (the course page): the card's width and inner space, one edge */
.learn-wide .notice-card { max-width: none; padding: 1.25rem 1.4rem; }
/* ...but in a section of the account page, one width for every notice, the section's: "Name saved" over its form was
   416px, "Device renamed" over the list 672 */
.notice:has(+ .lform:not(.quiz-form, [action^="/quiz/"])):not(.lsection > .notice) { max-width: 26rem; }

.lsection { padding: 2rem 0; border-top: 1px solid var(--rule); }
.lsection h2 { font-size: var(--fs-xl); font-weight: 600; letter-spacing: -0.015em; margin: 0 0 1.1rem; }
.lsection h3 { font-size: var(--fs-lg); font-weight: 600; margin: 0 0 0.6rem; }

/* settings: a status line, sub-blocks, rows with their action on the right */
.sstatus { padding: 0.85rem 1.1rem; border: 1px solid var(--status-brick); background: var(--bg-card);
           font-size: var(--fs-md); margin: 0 0 1.75rem; }
.sstatus-on { border-color: var(--rule); color: var(--accent); font-weight: 600; }
/* off, but nothing needs it yet (no purchase): said plainly, not as a warning */
.sstatus-plain { border-color: var(--rule); }
.ssub + .ssub { margin-top: 2rem; }
#backup-codes { scroll-margin-top: 1.5rem; }   /* "Make new codes" after a backup-code sign-in lands here */
.ssub > .lsmall { margin: 0 0 1rem; }
.slist { list-style: none; padding: 0; margin: 0 0 1.25rem; }
.slist:empty { display: none; }
.srow { display: flex; flex-wrap: wrap; gap: 0.75rem 1rem; align-items: center; justify-content: space-between; padding: 0.85rem 0; }
.srow:first-child { padding-top: 0.25rem; }
.srow + .srow { border-top: 1px solid var(--rule); }
.lsection > .lsmall + .slist { margin-top: 0.25rem; }
.srow-main { display: flex; flex-direction: column; gap: 0.2rem; min-width: 0; flex: 1 1 0; }
/* on a phone every row puts its actions under its text, rather than squeezing the text into what they leave */
@media (max-width: 480px) { .srow-main { flex-basis: 100%; } }
/* its tags (this device, temporary) wrap under the name flush with its edge, never indented by their own margin */
.srow-name { font-weight: 600; overflow-wrap: anywhere; display: flex; flex-wrap: wrap; align-items: baseline;
             gap: 0.2rem 0.4rem; }
.srow-name .tag { margin-left: 0; }
.srow-meta { font-size: var(--fs-xs); color: var(--muted); }
.srow-form { margin: 0; flex: none; }
.srow-acts { display: flex; gap: 0.5rem; flex: none; align-items: center; }
@media (pointer: coarse) { .srow-acts { gap: 0.75rem; } }   /* a slip from Replace does not land on Remove */
/* renaming a key or a device: a quiet link under the row, its form opens in place */
.srename { margin-top: 0.15rem; font-size: var(--fs-xs); color: var(--muted); }
/* a disclosure's label: muted, the chevron after it says it opens (no underline: it is not a link) */
.srename > summary { cursor: pointer; width: fit-content; list-style: none; }
.srename > summary::-webkit-details-marker { display: none; }
.srename > summary:hover { color: var(--fg); }
.srename > summary:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 2px; }
@media (pointer: coarse) { .srename > summary { padding-block: 0.7rem; } .srename[open] > summary { margin-bottom: 0; } }
.srename[open] > summary { margin-bottom: 0.5rem; }
.srename-form { display: flex; gap: 0.5rem; flex-wrap: wrap; align-items: center; margin: 0; }
/* a field's edge is a control's (--field-edge, 3:1), and its text 16px: under it, iOS zooms the page in on focus */
.srename-form input { font: inherit; font-size: var(--fs-md); color: var(--fg); background: var(--bg-card); min-width: 0;
                      flex: 1 1 12rem; max-width: 18rem; padding: 0.4rem 0.6rem; border: 1px solid var(--field-edge);
                      border-radius: var(--r-md); }
.srename-form input:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 1px; }
/* a field and its button, one height: the button takes the field's (40px), and on a touch screen both are 44px */
.srename-form .lbtn { align-self: stretch; }
@media (pointer: coarse) { .srename-form input { min-height: 2.75rem; } }
/* a name refused (blank): the field marked, its reason on a line of its own under the field and Save */
.srename-form input[aria-invalid="true"] { border-color: var(--status-brick); }
.srename-form .lerror { flex-basis: 100%; }
.sactions { display: flex; gap: 0.5rem 1rem; flex-wrap: wrap; align-items: center; margin-top: 0.25rem; }
.sactions .lsmall { margin: 0; flex: 1 1 16rem; }
/* the name field sits under its own heading: its label stays for screen readers only */
#name .llabel { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; }
.tag { display: inline-block; font-size: var(--fs-2xs); font-weight: 600; color: var(--muted); border: 1px solid var(--rule);
       padding: 0.05rem 0.4rem; margin-left: 0.4rem; vertical-align: 0.1em; }
.ssub .lform { margin-top: 1.25rem; }
.ssub > .slist + .lsmall { margin-top: -0.5rem; }

/* second factor: the key first, then a code */
.wa-block { display: flex; flex-direction: column; gap: 0.6rem; align-items: flex-start; }
.wa-msg { min-height: 1.2em; margin: 0; }
.wa-msg:empty { display: none; }
.lor { display: flex; align-items: center; gap: 0.75rem; color: var(--muted); font-size: var(--fs-xs); margin: 0.25rem 0; }
.lor::before, .lor::after { content: ""; flex: 1; border-top: 1px solid var(--rule); }

.totp-setup { display: flex; gap: 2rem; flex-wrap: wrap; align-items: flex-start; margin: 0 0 1.5rem; }
.totp-side { flex: 1 1 16rem; min-width: 0; }
.totp-side .lform { margin-top: 1.25rem; }
/* the passkey way, under both columns, as far below the buttons as a form's own margin (the form is the column's
   last child now, so .lform:last-child drops that margin) */
.totp-alt { margin-top: 1.75rem; }
.totp-get { margin: -1rem 0 1.75rem; }   /* under the lede it completes */
/* on a touch screen the app is usually on this very phone: the link first, as a button, and the QR code folded
   behind its summary (learn.js closes it there), for a scan from another device; a computer simply shows it */
.totp-open { display: none; margin: 0 0 1.5rem; }
.totp-scan { flex: none; margin: 0; }
.totp-scan > summary { display: none; }
.totp-touch { display: none; }
@media (pointer: coarse) {
  .totp-open { display: block; }
  .totp-scan { flex-basis: 100%; }
  /* the platform's one chevron after the label (below), not the browser's triangle before it */
  .totp-scan > summary { display: block; width: fit-content; cursor: pointer; font-size: var(--fs-sm); font-weight: 400;
                         color: var(--fg-soft); padding-block: 0.75rem; line-height: 1.5; }   /* a finger's 44px row */
  .totp-scan > summary:hover { color: var(--fg); }
  .totp-scan > summary:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 2px; }
  .totp-scan[open] > summary { margin-bottom: 0.25rem; }   /* its own padding makes the rest of the space */
  .totp-desk { display: none; }
  .totp-touch { display: inline; }
}
.lsecnum { color: var(--muted); font-variant-numeric: tabular-nums; margin-right: 0.35rem; }
/* the public page's badge (style.css, .curriculum-free): the visitor who signs up finds the same tag on section 00.
   As tall as that one, an inline box there whose border wraps the letters: here a flex item, its line height counted
   and the box 6px taller, so the letters' own height (normal) */
.lfree { padding: 0.05rem 0.45rem; border: 1px solid currentColor; border-radius: var(--r-md); font-size: var(--fs-xs);
         font-weight: 600; line-height: normal; color: var(--accent); }

/* a course section: one line when folded (number, title, progress, length, why it is closed), its lessons open */
.lsec { border-top: 1px solid var(--rule); margin: 0; scroll-margin-top: 1.5rem; }   /* /course#s05 keeps air above */
/* its number over the lesson numbers of the rows under it, the rows' hover fill (a phone keeps no hover after a tap) */
.lsec-head { display: flex; flex-wrap: wrap; align-items: baseline; gap: 0.3rem 1rem; padding: 1.1rem 2rem 1.1rem 0.25rem;
             position: relative; transition: background-color 160ms ease; }
@media (hover: hover) { html.js-fold .lsec-head:hover { background: var(--bg-soft); } }
.lsec-head h2 { font-size: var(--fs-xl); font-weight: 600; letter-spacing: -0.015em; margin: 0; }
/* the heading holds the fold's button (course.js builds it: without scripts every section is open and the heading is
   text), which reaches over the whole line as a lesson row's link does */
.lsec-toggle { font: inherit; letter-spacing: inherit; color: inherit; background: none; border: 0; border-radius: 0;
               padding: 0; margin: 0; text-align: inherit; cursor: pointer; }
.lsec-toggle::after { content: ""; position: absolute; inset: 0; }
.lsec-toggle:focus-visible { outline: none; }
.lsec-toggle:focus-visible::after { outline: 2px solid var(--focus-ring); outline-offset: -2px; }
.lsec-head::after { content: ""; position: absolute; right: 0.4rem; top: 50%; width: 0.5rem; height: 0.5rem;
                    border: solid var(--muted); border-width: 0 1px 1px 0; transform: translateY(-75%) rotate(45deg);
                    transition: transform 160ms ease; pointer-events: none; }
html:not(.js-fold) .lsec-head::after { display: none; }
.lsec.is-open > .lsec-head::after { transform: translateY(-25%) rotate(-135deg); }
@media (hover: hover) { .lsec-head:hover::after { border-color: var(--fg); } }
/* folded from the first paint (course.js marks the page in the head), then hidden by course.js, findable where the
   browser can (hidden="until-found" keeps its box, so its margin goes too) */
html.js-fold .lsec:not(.is-open) > .llist:not([hidden]) { display: none; }
/* This chevron is the one disclosure mark of learn: every other <details> (a reply or edit box, a sheet's hint, the
   account's delete and rename, the admin's folded rows) shows it after its label, down when folded and up when open,
   in the label's colour, where the browser put its triangle before the label. The admin's section rows (.asec) carry
   it at the row's right end, as the course page's sections do (below). */
:is(.qa-reply, .qa-edit, .srename, .ldanger, .aq, .sheet-body details, .totp-scan, .qa-ban) > summary { list-style: none; }
:is(.qa-reply, .qa-edit, .srename, .ldanger, .aq, .sheet-body details, .totp-scan, .qa-ban) > summary::-webkit-details-marker { display: none; }
:is(.qa-reply, .qa-edit, .srename, .ldanger, .aq, .sheet-body details, .totp-scan, .qa-ban) > summary::after {
  content: ""; display: inline-block; width: 0.42em; height: 0.42em; margin-left: 0.6em; vertical-align: 0.22em;
  border: solid currentColor; border-width: 0 1px 1px 0; transform: rotate(45deg); transition: transform 160ms ease; }
:is(.qa-reply, .qa-edit, .srename, .ldanger, .aq, .sheet-body details, .totp-scan, .qa-ban)[open] > summary::after {
  transform: translateY(0.2em) rotate(-135deg); }
@media (prefers-reduced-motion: reduce) {
  :is(.qa-reply, .qa-edit, .srename, .ldanger, .aq, .sheet-body details, .totp-scan, .qa-ban) > summary::after {
    transition: none; } }
@media (hover: hover) {   /* a tap would leave it on a phone */
  html.js-fold .lsec-head:hover .lsec-toggle { text-decoration: underline; text-underline-offset: 3px;
                                               text-decoration-thickness: 1px; } }   /* a button's own: none inherited */
.lsec-meta { font-size: var(--fs-sm); color: var(--muted); font-variant-numeric: tabular-nums; }
.lsec-tag { font-size: var(--fs-sm); font-weight: 600; color: var(--muted); }
.lsec-tag-done { color: var(--fg); }
.lsec > .llist { margin: 0 0 1.5rem; }
.lsec > .llist[hidden] { margin: 0; }
@media (prefers-reduced-motion: reduce) { .lsec-head::after { transition: none; } }
/* the title on its line, the facts under it, and the state (Done, why it is closed) on a line of its own: left to wrap
   by length, it went beside the facts of a short section and under those of a long one, row after row. Up to a small
   tablet, as a lesson row there (.lrow, 48rem), the title alone and the facts with the state under it, on every row: a
   phone held sideways put 'Needs two-factor authentication' under the facts of 3 sections and beside the other 15 */
@media (max-width: 48rem) { .lsec-head h2 { flex-basis: 100%; } }
@media (max-width: 640px) { .lsec-tag { flex-basis: 100%; } }

.llist { list-style: none; margin: 0; padding: 0; }
.lrow {
  display: grid;
  /* fixed side columns: the marks and lengths line up. The marks' fits 'MINI-05A  Files' (100px in all three engines),
     an exercise with no video of its own named on the lesson it goes with; two sheets go under the title (below) */
  grid-template-columns: 3.2rem 1fr 6.5rem 3.5rem 6rem;
  gap: 1rem;
  padding: 0.6rem 0.25rem;
  border-top: 1px solid var(--rule);
  font-size: var(--fs-md);
  align-items: baseline;
  position: relative;
}
.lrow:first-child { border-top: 0; }
.lnum, .ldur { color: var(--muted); font-variant-numeric: tabular-nums; font-size: var(--fs-sm); }
.ldur { text-align: right; }
.lstate { font-size: var(--fs-xs); color: var(--muted); text-align: right; white-space: nowrap; font-variant-numeric: tabular-nums; }
.lrow-done .lstate, .lrow-progress .lstate, .lrow-current .lstate { color: var(--fg); font-weight: 600; }
.lrow-current .ltitle { font-weight: 600; }
.lrow-locked .ltitle { color: var(--muted); }
/* the whole row opens the lesson, with the hover fill of the site's row lists */
.lrow-link { transition: background-color 160ms ease; }
.lrow-link:hover { background: var(--bg-soft); }
.lrow-link .ltitle a::after { content: ""; position: absolute; inset: 0; }
.lrow-link:hover .ltitle a { text-decoration-line: underline; }
/* what the lesson brings (a sheet, files): a quiet column of words, not a chip on every row */
.lmarks { display: flex; flex-wrap: wrap; gap: 0 0.5rem; font-size: var(--fs-sm); }
.lmarks .tag { font-weight: 500; border: 0; padding: 0; margin: 0; vertical-align: baseline; color: var(--muted);
               white-space: nowrap; }
/* a lesson with two sheets (its own and an exercise, 6.12 and 11.4) is wider than the column: its marks take a line
   under the title, on one line, and the title takes the column back. Widening the column for them cost every other
   row 3rem of title, and many titles a lone word on a second line at tablet widths. (A phone already puts every row's
   marks under the title: its own rules below win.) */
.lrow-marks-under { row-gap: 0.15rem; }
.lrow-marks-under .ltitle { grid-column: 2 / 4; grid-row: 1; }
.lrow-marks-under .lmarks { grid-column: 2 / 4; grid-row: 2; }

.dlist { list-style: none; padding: 0; margin: 0 0 1rem; }

.qr { width: 220px; height: 220px; flex: none; background: #ffffff; padding: 12px; border: 1px solid var(--rule); margin: 0; }
.qr canvas { width: 100%; height: 100%; image-rendering: pixelated; }
.secret, .codes code { font-family: var(--font-mono); font-size: var(--fs-sm); }
.secret { display: inline-block; padding: 0.35rem 0.6rem; background: var(--bg-card); border: 1px solid var(--rule);
          letter-spacing: 0.04em; user-select: all; overflow-wrap: anywhere; }
/* the key on one line where its column holds it, else broken between its groups of four, evenly: beside the QR code
   on a small laptop window the column is narrower than the key, which a nowrap from 420px wide ran off the page */
.secret-row { display: flex; flex-wrap: wrap; gap: 0.6rem; align-items: center; }
.secret { text-wrap: balance; }
/* two columns where a number and its code fit side by side twice, one column on a narrow phone */
.codes { display: grid; grid-template-columns: repeat(auto-fill, minmax(8rem, 1fr)); gap: 0.6rem 1.5rem;
         list-style: decimal-leading-zero inside;
         padding: 1.1rem 1.4rem; border: 1px solid var(--rule); background: var(--bg-card); max-width: 26rem; margin: 0 0 1.5rem; }
.codes li { color: var(--muted); font-size: var(--fs-xs); font-variant-numeric: tabular-nums; white-space: nowrap; }  /* never under its number */
.codes code { color: var(--fg); margin-left: 0.35rem; user-select: all; white-space: nowrap; }  /* never at the hyphen */
.codes-msg:empty { display: none; }
/* on a phone the buttons under the codes keep one shape whatever Done says ("Done", or "Done, continue" on the way to a
   lesson, which wrapped alone): Copy and Download share a row, Done takes the next one whole */
@media (max-width: 640px) {
  .codes-actions > button { flex: 1 1 0; }
  .codes-actions > form { flex: 1 1 100%; margin: 0; }
  .codes-actions > form .lbtn { width: 100%; }
}

/* player: its chrome is dark in both themes (the palette's dark surfaces), so its focus ring is the
   light one (The Inversion Rule) */
.player { position: relative; background: #101216; border: 1px solid var(--rule-strong); outline: none; }
.player:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 2px; }
.player-stage, .player-ui { --focus-ring: #f7f7f5; }
.player [hidden] { display: none !important; }
.player-stage { position: relative; container: player-stage / inline-size; }  /* the help's columns follow the picture's width */
/* the visible mark over the FairPlay video (the canvas player draws its own into the picture) */
.player-mark { position: absolute; z-index: 1; pointer-events: none; user-select: none; -webkit-user-select: none;
  white-space: nowrap; line-height: 1; font-weight: 600; color: #f7f7f5; opacity: 0.15;
  -webkit-text-stroke: 1px #101216; paint-order: stroke fill; }
/* the picture never pushes the bar off the screen: past the window's height it is letterboxed (--ui-h: the bar and the line
   under it, measured by player.js; 2px: the borders), so a phone held sideways shows picture and bar together */
.player-screen { display: block; width: 100%; height: auto; aspect-ratio: 16 / 9; background: #101216; cursor: pointer;
                 max-height: calc(100vh - var(--ui-h, 6rem) - 2px); max-height: calc(100svh - var(--ui-h, 6rem) - 2px); object-fit: contain;
                 touch-action: manipulation; }  /* a double tap skips 10 s (player.js), it never zooms the page */
/* a laptop screen: title, picture, bar and a line under it fit together when the page opens */
@media (min-width: 56em) and (min-height: 37.5em) {
  .player-screen { max-height: calc(100vh - 18.8rem); max-height: calc(100svh - 18.8rem); }  /* the cap, plus the 1.7rem below */
  /* and the player no wider than that picture, so no dark bands beside it and a bar as wide as it (2px: borders),
     from the column's left edge, as the title above it and the text under it. Within 3rem of the column's width it
     takes the whole column, its picture up to 1.7rem taller than the cap: a 1440x900 window gets a player whose right
     edge lines up with the text too, not a 20 px step. Previous and Next under it (.lafter) end where it ends, wherever
     it is: across the whole column, Next sat alone in the empty right of the page, 256 px past the picture */
  .player:not(.is-fsn, .is-full, .is-mini, .is-pip), .lafter {
    max-width: calc((100vh - 20.5rem) * 16 / 9 + 2px
                    + clamp(0px, (3rem - 100% + (100vh - 20.5rem) * 16 / 9 + 2px) * 1000, 100% - (100vh - 20.5rem) * 16 / 9 - 2px));
    max-width: calc((100svh - 20.5rem) * 16 / 9 + 2px
                    + clamp(0px, (3rem - 100% + (100svh - 20.5rem) * 16 / 9 + 2px) * 1000, 100% - (100svh - 20.5rem) * 16 / 9 - 2px)); }
}
/* waiting for the picture, passing notices, the end of the lesson, the shortcuts: over the picture. The notices sit
   at its top, clear of the subtitles burned into the bottom of every lesson */
.player-wait { position: absolute; inset: 0; display: flex; flex-direction: column; align-items: center; justify-content: center;
               gap: 0.6rem; pointer-events: none; color: #f7f7f5; font-size: var(--fs-sm); }
.player-wait > span:last-child { background: #181b20; padding: 0.2rem 0.6rem; }
/* on a dark disc, the pill's colour: on a white slide its ring alone read as a grey mark on the slide */
.player-spin { width: 2.25rem; height: 2.25rem; border: 2px solid #5c6470; border-top-color: #f7f7f5; border-radius: 50%;
               background: #181b20; box-shadow: 0 0 0 0.4rem #181b20; animation: player-spin 0.9s linear infinite; }
@keyframes player-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) { .player-spin { animation: none; } }
.player-toast { position: absolute; left: 50%; top: 1rem; transform: translateX(-50%); z-index: 4; margin: 0; max-width: calc(100% - 2rem);
                width: max-content; padding: 0.5rem 0.85rem; background: #181b20; color: #f7f7f5; border: 1px solid #2d323a;
                font-size: var(--fs-sm); line-height: 1.4; text-align: center; pointer-events: none; }
.player-toast:empty { display: none; }
/* under the shortcuts list while it is open, which it covered from its title ('Ke[Speed 1.75×]uts'): the player's live
   region says it all the same, and what is left of it shows once the list is closed */
.player-stage:has(> .player-help:not([hidden])) > .player-toast { z-index: 2; }
/* a double tap's skip, on the side tapped (player.js, tapSkip) */
.player-tapcue { position: absolute; top: 50%; left: 25%; z-index: 4; margin: 0; transform: translate(-50%, -50%); padding: 0.5rem 0.85rem;
                 background: #181b20; color: #f7f7f5; border: 1px solid #2d323a; font-size: var(--fs-md); font-weight: 600;
                 font-variant-numeric: tabular-nums; white-space: nowrap; pointer-events: none; }
.player-tapcue.is-fwd { left: 75%; }
.player-end, .player-help { position: absolute; inset: 0; z-index: 3; display: flex; flex-direction: column; align-items: center;
                            justify-content: center; gap: 1.1rem; padding: 1rem 1.25rem; overflow: auto; background: #101216;
                            color: #f7f7f5; text-align: center; }
/* over the bar's top by a pixel, in the page: a picture of a fractional height (412.625px on a 390px iPhone) left the
   card and the bar meeting inside one device pixel, and the last frame showed through it as a light line */
.player:not(.is-fsn, .is-full, .is-bar) :is(.player-end, .player-help) { bottom: -1px; }
.player-end-title { margin: 0; font-size: var(--fs-2xl); font-weight: 600; letter-spacing: -0.02em; line-height: 1.2; }
.player-end-acts { display: flex; gap: 0.75rem; flex-wrap: wrap; justify-content: center; }
.player-end-btn { font: inherit; font-size: var(--fs-sm); font-weight: 600; line-height: 1.3; padding: 0.6rem 1.1rem; max-width: 100%;
                  color: #f7f7f5; background: transparent; border: 1px solid #5c6470; border-radius: var(--r-md); cursor: pointer;
                  text-decoration: none; }
@media (hover: hover) { .player-end-btn:hover { border-color: #f7f7f5; } }
.player-end-main { color: #101216; background: #f7f7f5; border-color: #f7f7f5; }
.player-end-main:hover { background: #d9dce1; border-color: #d9dce1; }
/* the list scrolls from its top on a short picture (centred content would be cut off above), and only itself: the page
   behind it stays put at its ends. Its own height is a container (below: tighter rows on a short picture) */
.player-help { font-size: var(--fs-sm); justify-content: flex-start; padding-bottom: 0; overscroll-behavior: contain;
               container: player-help / size; }
.player-help:focus { outline: none; }
.player-help:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: -2px; }  /* it scrolls with the keys */
.player-help > :first-child { margin-top: auto; }
/* Close sits under the list, the two centred together in the picture; on a short picture (a laptop, the small player, a
   phone) it stays in sight at the bottom, the list scrolling under it, and a hairline over it says there is more below
   (player.js, is-cut). It runs from edge to edge and, lying over the list's focus ring, draws that ring's lower part
   itself */
.player-help-foot { position: sticky; bottom: 0; align-self: stretch; margin: 0 -1.25rem auto; padding: 0.75rem 1.25rem 1rem;
                    background: #101216; box-shadow: var(--foot-ring, 0 0 transparent), var(--foot-cut, 0 0 transparent); }
.player-help.is-cut .player-help-foot { --foot-cut: inset 0 1px #5c6470; }
/* the ring's sides where Close covers them, and its bottom only where Close lies on the list's bottom edge (is-low:
   pinned over a list that scrolls), not under a Close centred with the list */
.player-help:focus-visible .player-help-foot { --foot-ring: inset 2px 0 var(--focus-ring), inset -2px 0 var(--focus-ring); }
.player-help.is-low:focus-visible .player-help-foot { --foot-ring: inset 2px 0 var(--focus-ring), inset -2px 0 var(--focus-ring),
                                                                   inset 0 -2px var(--focus-ring); }
.player-help-title { margin: 0; font-size: var(--fs-md); font-weight: 600; }
/* the descriptions wrap rather than push the grid past the picture's edges (a narrow window, the small player) */
.player-help-keys { display: grid; grid-template-columns: max-content minmax(0, 1fr); gap: 0.35rem 1.5rem; max-width: 100%;
                    text-align: left; }
.player-help dl { grid-column: 1 / -1; display: grid; grid-template-columns: inherit; grid-template-columns: subgrid;
                  row-gap: 0.35rem; margin: 0; }
/* across the grid without sizing its columns (contain): its width went to the key columns, which then left the
   descriptions too little room and wrapped them where the line had space */
.player-help-sub { grid-column: 1 / -1; margin: 0.6rem 0 0.1rem; color: #9aa1ac; text-align: center; contain: inline-size; }
/* a wide picture: the keys in two columns, so the list fits a laptop's picture without scrolling. A description wraps
   rather than push the right-hand pair past the picture's edge (the arrows' line on a 13-inch laptop's 840px picture) */
@container player-stage (min-width: 50rem) {
  .player-help-keys { grid-template-columns: repeat(2, max-content minmax(0, max-content)); } }
.player-help dt { font-weight: 600; text-align: right; }
.player-help dd { margin: 0; color: #9aa1ac; }
/* a phone-wide picture: each key over what it does, from the left */
@container player-stage (max-width: 26rem) {
  .player-help-keys { grid-template-columns: minmax(0, 1fr); row-gap: 0; }
  .player-help dl { row-gap: 0; }
  .player-help dt { margin-top: 0.5rem; text-align: left; }
  .player-help-sub { margin-top: 0.9rem; text-align: left; }
}
/* a short picture (a 1366x768 laptop, a window snapped beside the lab machine): tighter rows, so the whole list and
   its Close fit without scrolling where they can */
@container player-help (max-height: 32rem) {
  .player-help-keys { row-gap: 0.1rem; line-height: 1.4; }
  .player-help dl { row-gap: 0.1rem; }
  .player-help-sub { margin: 0.3rem 0 0; }
  .player-help-foot { padding-top: 0.5rem; padding-bottom: 0.75rem; }
}
/* clear of the small player's close button */
.player.is-mini .player-help { padding-top: 2.9rem; }
/* a list taller than the picture (a 400 % zoom, a window snapped narrow, the small player): the player grows under it,
   up to the window's height (the bar goes below it meanwhile; the small player keeps its bar and stays 1rem clear of the
   window's edges), the list over picture and room (player.js) */
.player.is-helproom:not(.is-fs, .is-bar, .is-pip) .player-stage { min-height: min(var(--help-h), 100vh - 2px);
                                                                   min-height: min(var(--help-h), 100svh - 2px); }
.player.is-helproom.is-mini:not(.is-bar) .player-stage {
  min-height: min(var(--help-h), 100vh - var(--ui-h, 6rem) - 2rem - 2px);
  min-height: min(var(--help-h), 100svh - var(--ui-h, 6rem) - 2rem - 2px); }
/* in full screen the bar lies over the bottom of the picture: the list, and its Close, end above it */
.player:is(.is-fsn, .is-full) .player-help { bottom: var(--ui-h, var(--bar-h)); }
@media (max-width: 640px) { .player-end-title { font-size: var(--fs-md); } .player-end, .player-help { gap: 0.6rem; } }
/* the bar: the position on its own row, then the buttons; icon buttons keep one width whatever the state */
.player-ui { background: #181b20; color: #f7f7f5; user-select: none; -webkit-user-select: none; }
.player-seek { position: relative; display: flex; align-items: center; height: 1.5rem; margin: 0 0.75rem; cursor: pointer; touch-action: none; }
/* the part not watched yet is an outline (3.2:1 against the bar), the part watched is filled (3.2:1 against the
   outline's inside), the part played is white (5:1 against the watched part) */
.player-seek-track { position: relative; flex: 1; height: 4px; box-shadow: inset 0 0 0 1px #646b77; transition: height 160ms ease; }
.player-seek:hover .player-seek-track, .player-seek:focus-visible .player-seek-track, .player-seek.is-drag .player-seek-track { height: 6px; }
.player-seek-seen { position: absolute; inset: 0 auto 0 0; width: 0; background: #646b77; }
.player-seek-pos { position: absolute; inset: 0 auto 0 0; width: 0; background: #f7f7f5; }
.player-seek-pos::after { content: ""; position: absolute; right: -6px; top: 50%; width: 12px; height: 12px; margin-top: -6px;
                          border-radius: 50%; background: #f7f7f5; transform: scale(0); transition: transform 160ms ease; }
.player-seek:hover .player-seek-pos::after, .player-seek:focus-visible .player-seek-pos::after,
.player-seek.is-drag .player-seek-pos::after { transform: scale(1); }
.player-seek:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 0; }
.player-seek[aria-disabled="true"] { cursor: default; opacity: 0.5; }
.player-seek-tip { position: absolute; bottom: calc(100% + 0.2rem); left: 0; z-index: 4; padding: 0.15rem 0.45rem; pointer-events: none;
                   background: #f7f7f5; color: #101216; font-size: var(--fs-sm); font-variant-numeric: tabular-nums; white-space: nowrap; }
.player-seek-tip.is-ahead { background: #5c6470; color: #f7f7f5; }
.player-bar { display: flex; align-items: center; gap: 0.15rem; padding: 0 0.4rem 0.35rem; font-size: var(--fs-sm); }
.player-bar button { flex: none; display: inline-flex; align-items: center; justify-content: center; width: 2.5rem; height: 2.5rem;
                     padding: 0; font: inherit; color: #f7f7f5; background: transparent; border: 1px solid transparent;
                     border-radius: var(--r-md); cursor: pointer; }
@media (hover: hover) { .player-bar button:hover { border-color: #5c6470; } }
.player-bar button:active { background: #2d323a; }   /* pressed, on a phone too (no hover there) */
.player-bar button:disabled { opacity: 0.45; cursor: default; border-color: transparent; }
.player-bar .pi { width: 1.25rem; height: 1.25rem; display: block; }
.player-play .pi-pause, .player-mute .pi-muted, .player-full .pi-unfull { display: none; }
/* at the end the button keeps the play mark (its name says Replay): a circular arrow beside "back 10 s" read as it */
.player[data-state="playing"] .player-play .pi-play { display: none; }
.player[data-state="playing"] .player-play .pi-pause { display: block; }
.player.is-muted .player-mute .pi-sound, .player.is-fs .player-full .pi-full { display: none; }
.player.is-muted .player-mute .pi-muted, .player.is-fs .player-full .pi-unfull { display: block; }
/* the time gives way last, and clips rather than push full screen out of the window */
.player-time { flex: 0 1 auto; min-width: 0; overflow: hidden; padding: 0 0.5rem; color: #9aa1ac; font-variant-numeric: tabular-nums;
               white-space: nowrap; }
.player-fill { flex: 1; }
.player-vol { position: relative; flex: none; display: flex; align-items: center; width: 4.5rem; height: 2.5rem; margin: 0 0.4rem 0 0.1rem;
              cursor: pointer; touch-action: none; }
.player-vol-track { position: relative; flex: 1; height: 4px; background: #646b77; }
.player-vol-level { position: absolute; inset: 0 auto 0 0; width: 100%; background: #f7f7f5; }
.player-vol:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 0; }
.player-rate-box { position: relative; flex: none; }
.player-bar .player-rate { width: auto; min-width: 3.6rem; padding: 0 0.5rem; font-weight: 600; font-variant-numeric: tabular-nums; }
.player-rates { position: absolute; right: 0; bottom: calc(100% + 0.35rem); z-index: 5; display: flex; flex-direction: column;
                min-width: 8.5rem; padding: 0.25rem 0; background: #181b20; border: 1px solid #2d323a; }
.player-bar .player-rates button { width: 100%; height: auto; justify-content: flex-start; gap: 0.5rem; padding: 0.45rem 0.85rem;
                                   border: 0; font-variant-numeric: tabular-nums; }
.player-rates button::before { content: ""; width: 0.85em; text-align: center; }
.player-rates button[aria-checked="true"] { font-weight: 600; }
.player-rates button[aria-checked="true"]::before { content: "✓"; }
.player-rates button:hover, .player-rates button:focus-visible { background: #2d323a; outline-offset: -2px; }
.player-bar .player-keys { font-weight: 600; }
/* More: shown once the bar has let something go (below), its menu built from what is gone (player.js) */
.player-more-box { position: relative; flex: none; display: none; }
.player-bar .player-more { width: auto; min-width: 3.6rem; padding: 0 0.6rem; font-weight: 600; }
.player-menu { position: absolute; right: 0; bottom: calc(100% + 0.35rem); z-index: 5; display: flex; flex-direction: column;
               min-width: 12rem; padding: 0.25rem 0; background: #181b20; border: 1px solid #2d323a; }
.player-bar .player-menu button { width: 100%; height: auto; justify-content: flex-start; padding: 0.6rem 0.85rem; border: 0; white-space: nowrap; }
.player-menu button:hover, .player-menu button:focus-visible { background: #2d323a; outline-offset: -2px; }
.player-msg { margin: 0; padding: 0 0.75rem 0.6rem; color: #9aa1ac; font-size: var(--fs-sm); line-height: 1.45; }
.player-msg:empty { padding: 0; }  /* no band under the bar */
/* a line that has said something keeps its height (player.js, say): the first Play takes "Start from the
   beginning" away without the page below moving; full screen and the small player give the room back */
.player-msg.is-held { min-height: var(--held, calc(1.45em + 0.6rem + 2px)); }  /* --held: the tallest it has been */
.player:is(.is-fsn, .is-full, .is-mini, .is-pip) .player-msg.is-held:empty { min-height: 0; }
.player-act:not(:last-child) { margin-right: 0.75rem; }  /* after, not before: a wrapped action starts flush */
.player-act { font: inherit; color: #f7f7f5; background: transparent; border: 0; border-bottom: 1px solid #f7f7f5; padding: 0 0 1px; cursor: pointer; }
.player-act:hover { border-bottom-width: 2px; padding-bottom: 0; }
/* fingers: a 44px target ("Start from the beginning" was 22px tall) that keeps the look of a link in the line and
   moves nothing around it, the line's height included */
@media (pointer: coarse) { .player-act { position: relative; } .player-act::before { content: ""; position: absolute; inset: -11px 0; } }
/* in the page, the line is page text under the player, not a band of its dark chrome: once held and empty it is plain
   space before the lesson links. It covers the player's border beside and under it (-1px); the bar draws the bottom edge,
   so a line wider than a centred player (above) does not draw it past the player's sides */
.player:not(.is-fsn, .is-full, .is-mini, .is-pip) .player-msg { --focus-ring: var(--fg); margin: 0 -1px -1px; padding: 0.6rem 0 0;
  color: var(--muted); background: var(--bg); }
.player:not(.is-fsn, .is-full, .is-mini, .is-pip) .player-bar { border-bottom: 1px solid var(--rule-strong); }
.player:not(.is-fsn, .is-full, .is-mini, .is-pip) .player-msg:empty { padding: 0; }
/* and its actions are drawn as the page's links (learn.css, top: 1px at 2px, a soft line that turns solid on hover), not
   with the dark chrome's border; their fingers' room (above) stays */
.player:not(.is-fsn, .is-full, .is-mini, .is-pip) .player-act { color: var(--fg); border-bottom: 0; padding-bottom: 0;
  text-decoration-line: underline; text-underline-offset: 2px; text-decoration-thickness: 1px;
  text-decoration-color: color-mix(in srgb, currentColor 45%, transparent); }
.player:not(.is-fsn, .is-full, .is-mini, .is-pip) .player-act:hover { text-decoration-color: currentColor; }
/* under a laptop's player narrower than the column (above) the line runs on to the column's right edge, as the lesson
   links below do, rather than wrapping at the player's (--col-w: the column, measured by player.js; 100%: the bar inside
   the player's borders). Not a container query on the column: a container there made Safari jump the page as the small
   player docked */
@media (min-width: 56em) and (min-height: 37.5em) {
  .player:not(.is-fsn, .is-full, .is-mini, .is-pip) .player-msg {
    margin-right: calc(100% + 2px - var(--col-w, 100% + 2px) - 1px); }
}
/* fingers: bigger targets; no volume slider (the phone's buttons) and no keyboard list; the narrowest
   phones keep Play, the time, the speed and full screen */
@media (pointer: coarse) {
  .player-seek { height: 2.25rem; }
  .player-seek-pos::after { transform: scale(1); }
  .player-bar button { width: 2.75rem; height: 2.75rem; }
  /* the items of the speed and More menus too: six at 36px edge to edge, a finger meaning 1.5x got 1.25x */
  .player-bar :is(.player-rates, .player-menu) button { min-height: 2.75rem; }
  .player-vol, .player-bar .player-keys { display: none; }
}
@media (pointer: coarse) and (max-width: 480px) { .player-mute { display: none !important; } }
/* a narrow window whatever the pointer (a desktop window snapped beside the lab machine, a 400 % zoom): the bar
   lets go of what has another way in (the volume keys, the ? key, and More, which holds what went) and full screen
   stays on it */
@media (max-width: 560px) { .player-vol { display: none !important; } }
@media (max-width: 480px) { .player-bar .player-keys { display: none !important; } }
/* not on a phone for its sound alone (it has its buttons), but once the skips go too */
@media (max-width: 480px) and (pointer: fine), (max-width: 330px) { .player-more-box { display: block; } }
@media (max-width: 440px) { .player-pip { display: none !important; } }
@media (max-width: 400px) {
  .player-mute { display: none !important; }
  .player-bar { gap: 0; padding-left: 0.25rem; padding-right: 0.25rem; }
  .player-bar button { width: 2.5rem; }
  .player-bar .player-rate { min-width: 3rem; }
  .player-time { padding: 0 0.3rem; }
}
@media (max-width: 330px) { .player-back, .player-fwd { display: none !important; } }
/* a phone: the picture from edge to edge, as phones show video */
@media (max-width: 640px) {
  .player:not(.is-full):not(.is-pip) { margin-left: calc(-1 * max(var(--pad-x), env(safe-area-inset-left)));
                                       margin-right: calc(-1 * max(var(--pad-x), env(safe-area-inset-right)));
                                       border-left: 0; border-right: 0; }
  /* its line under it starts where the page's text does */
  .player:not(.is-fsn, .is-full, .is-mini, .is-pip) .player-msg { margin: 0 0 -1px;
    padding-left: max(var(--pad-x), env(safe-area-inset-left)); padding-right: max(var(--pad-x), env(safe-area-inset-right)); }
}
/* full screen, the browser's (is-fsn) or the window covered where there is none (is-full, the iPhone): the bar lies
   over the picture and goes away after 2.5 s without a move, paused too, so a subtitle read on a paused picture is
   whole; a pause or a mouse moving over the picture leaves it gone, only the bottom of the picture brings it back
   (player.js, wake), and the pointer hides on its own (is-still). A screen taller than the picture (16:10, 4:3) keeps the picture above the bar: its box is the screen less
   the bar, never shorter than the picture (cqw: the stage's width), so 16:9 and wider screens are filled */
.player:is(.is-fsn, .is-full) { --bar-h: 4.35rem; }
@media (pointer: coarse) { .player:is(.is-fsn, .is-full) { --bar-h: 5.35rem; } }
.player.is-fsn { display: block; border: 0; width: 100%; height: 100%; }
.player.is-fsn .player-stage { position: absolute; inset: 0; }
.player:is(.is-fsn, .is-full) .player-screen { height: 100%; height: max(calc(100% - var(--bar-h)), min(100%, 56.25cqw));
                                               max-height: none; aspect-ratio: auto; }
.player:is(.is-fsn, .is-full) .player-ui { position: absolute; left: 0; right: 0; bottom: 0; z-index: 5; transition: opacity 200ms ease; }
.player:is(.is-fsn, .is-full).is-still, .player:is(.is-fsn, .is-full).is-still .player-screen { cursor: none; }
.player:is(.is-fsn, .is-full).is-idle .player-ui { opacity: 0; }
/* the window covered: picture and bar kept clear of the notch and the home bar */
.player.is-full { position: fixed; inset: 0; z-index: 1000; display: block; border: 0; }
.player.is-full .player-stage { position: absolute; top: env(safe-area-inset-top); right: env(safe-area-inset-right);
                                bottom: env(safe-area-inset-bottom); left: env(safe-area-inset-left); }
.player.is-full .player-ui { left: env(safe-area-inset-left); right: env(safe-area-inset-right); bottom: env(safe-area-inset-bottom); }
html.player-full-on, html.player-full-on body { overflow: hidden; }
/* the small player, in the corner while a sheet is read further down */
.player.is-mini { position: fixed; right: 1rem; bottom: 1rem; z-index: 900; width: clamp(20rem, 30vw, 30rem); box-shadow: 0 8px 30px rgba(0, 0, 0, 0.25); }
/* and 1rem clear of the reading column (46rem from the column's edge: the sheets, the questions), whose lines it never
   covers; player.js docks it only from 74em, where that leaves it 20rem at least, and lays a bar along the bottom below */
.player.is-mini { max-width: calc(100% - 2rem - max(0px, (100% - var(--max-w)) / 2) - var(--pad-x) - 46rem); }
.player.is-mini .player-screen { max-height: none; }
.player.is-mini :is(.player-back, .player-fwd, .player-mute, .player-vol, .player-rate-box, .player-pip, .player-keys, .player-more-box) { display: none; }
.player.is-mini .player-end-title { font-size: var(--fs-md); }
.player.is-mini .player-toast { max-width: calc(100% - 6rem); }  /* clear of the close button */
.player-undock { position: absolute; top: 0.4rem; right: 0.4rem; z-index: 4; display: inline-flex; align-items: center; justify-content: center;
                 width: 2rem; height: 2rem; padding: 0; color: #f7f7f5; background: #181b20; border: 1px solid #5c6470; cursor: pointer; }
.player-undock .pi { width: 1rem; height: 1rem; }
/* it sits on the picture, often a white slide: its ring is light between two dark bands, seen on any frame */
.player-undock:focus-visible { outline: 2px solid #f7f7f5; outline-offset: 2px; border-radius: 0; box-shadow: 0 0 0 6px #101216; }
/* no room for it beside the text (a phone, a tablet, a laptop zoomed in, a short window): a bar along the bottom of the
   screen instead, without the picture, clear of the iPhone's home bar (player.js, is-bar). Its notices, its end card and
   the shortcuts list open above it */
.player.is-mini.is-bar { left: 0; right: 0; bottom: 0; width: auto; max-width: none; margin: 0; border: 0;
                         border-top: 1px solid #2d323a; box-shadow: 0 -4px 20px rgba(0, 0, 0, 0.2); background: #181b20;
                         padding: 0 env(safe-area-inset-right) env(safe-area-inset-bottom) env(safe-area-inset-left); }
.player.is-bar .player-stage { position: static; height: 0; }
.player.is-bar :is(.player-screen, .player-wait, .player-tapcue, .player-mark, .player-seek, .player-fwd, .player-full) { display: none; }
.player.is-mini.is-bar .player-back { display: inline-flex; }
.player.is-bar .player-bar { padding: 0.35rem 0.4rem; }
/* the close button is the bar's last control (player.js moves it there), at its right end and last in the tab order */
.player.is-bar .player-undock { position: static; width: 2.5rem; height: 2.5rem; border-color: transparent; background: transparent; }
.player.is-bar .player-undock:focus-visible { box-shadow: none; }   /* on the bar, not a picture: the ring alone, as its neighbours' */
@media (hover: hover) { .player.is-bar .player-undock:hover { border-color: #5c6470; } }
@media (pointer: coarse) { .player.is-bar .player-undock { width: 2.75rem; height: 2.75rem; } }
.player.is-bar .player-toast { top: auto; bottom: calc(100% + 0.75rem); }
.player.is-bar .player-end { inset: auto 0 100% 0; flex-direction: row; flex-wrap: wrap; justify-content: flex-start; gap: 0.6rem 1rem;
                             padding: 0.75rem max(1rem, env(safe-area-inset-right)) 0.75rem max(1rem, env(safe-area-inset-left));
                             border-top: 1px solid #2d323a; text-align: left; }
.player.is-bar .player-end-title { font-size: var(--fs-md); }
.player.is-bar .player-end-acts { justify-content: flex-start; }
/* the shortcuts list over the page, as tall as the room above the bar up to 30rem (100%: the bar's own height):
   capped at 70% of a short window (a 400 % zoom, 320x256), it showed its title and a key without its meaning */
.player.is-bar .player-help { inset: auto 0 100% 0; height: min(30rem, 100vh - 100%); height: min(30rem, 100svh - 100%);
                              padding-top: 1rem; border-top: 1px solid #2d323a; }
.player-bar .player-tovideo { display: none; }
.player.is-bar .player-bar .player-tovideo { display: inline-flex; width: auto; padding: 0 0.75rem; border-color: #5c6470; font-weight: 600;
                                             white-space: nowrap; }
@media (hover: hover) { .player.is-bar .player-bar .player-tovideo:hover { border-color: #f7f7f5; } }
/* a 340 px phone keeps the whole time beside it (narrower still, the 10 s skip goes, as on the inline bar) */
@media (max-width: 22em) {
  .player.is-bar .player-bar { padding-right: 0.25rem; }
  .player.is-bar .player-bar .player-tovideo { padding: 0 0.4rem; }
}
/* while it is docked the page keeps room for it: a control that takes the focus scrolls clear of it, and the footer
   comes out from under it (--mini-room: its height and the gaps around it, set by player.js) */
html.player-docked { scroll-padding-bottom: var(--mini-room, 0px); }
html.player-docked body > .footer { padding-bottom: var(--mini-room, 0px); }
/* picture in picture: the player alone in its window, and a line where it was */
.player-pip-body { margin: 0; min-height: 100vh; background: #101216; }
.player.is-pip { flex: 1; display: flex; flex-direction: column; border: 0; }
.player.is-pip .player-stage { flex: 1; min-height: 0; }
.player.is-pip .player-screen { height: 100%; max-height: none; aspect-ratio: auto; }
.player.is-pip .player-full { display: none; }
.player-pip-hold { display: flex; gap: 0.75rem 1rem; flex-wrap: wrap; align-items: center; margin: 0; padding: 1.5rem;
                   border: 1px solid var(--rule); background: var(--bg-card); }
/* Windows high contrast: the bars are drawn with system colours, not backgrounds that get dropped */
@media (forced-colors: active) {
  .player-seek-track, .player-vol-track { border: 1px solid CanvasText; }
  .player-seek-seen { forced-color-adjust: none; background: GrayText; }
  .player-seek-pos, .player-seek-pos::after, .player-vol-level { forced-color-adjust: none; background: Highlight; }
  .player-bar button, .player-undock { border-color: ButtonText; }
}
/* a lesson page's title stays short: the player is what the page is for; one size whether it plays or not */
.lhead { gap: 0.35rem; margin-bottom: 1.25rem; }
.lhead h1 { font-size: var(--fs-heading); letter-spacing: -0.025em; line-height: 1.15; }
/* a lesson not open yet: under its own head, the notice of every closed page (views.gate), at the reading measure
   with the page's space above it, not stretched to the player's column */
.lhead:has(+ .notice) { margin-bottom: 2.25rem; }
.lhead + .notice { max-width: var(--measure); }
/* under the player: one line on where the lesson stands, then the previous and next lessons. Nothing there changes
   height while the video plays, so the questions and the files under it stay put: until 90% the line says what comes
   next and how it opens, and the completion takes its place in the same box (one grid cell for both, as tall as the
   taller); Next keeps its box, unseen, until it shows. Revealed from nothing at 90%, the two pushed the page down by a
   line and a half mid-video. The completion line stays in the page while empty, so that a screen reader has the live
   region before player.js fills it. The line comes between the two links in the page (views._lafter): set here above
   them, the whole width, where they share a row; under Previous up to a tablet (below) */
/* where the lesson sits, under Previous and Next: its section (a link to its place on the course page) and its rank */
.lplace { margin: 0.75rem 0 0; font-size: var(--fs-sm); color: var(--muted); }
.lplace a { color: var(--muted); text-decoration-line: underline; }
.lplace a:hover { color: var(--fg); }
@media (pointer: coarse) { .lplace a { position: relative; } .lplace a::before { content: ""; position: absolute; inset: -14px 0; } }
.notice + .lplace { margin: -0.5rem 0 1.25rem; }
.lafter { margin-top: 1.5rem; display: grid; grid-template-columns: auto auto; justify-content: space-between;
  gap: 0.75rem 2rem; align-items: baseline; }
/* across both columns without sizing them: its width is the row's, not its words', which would widen Previous */
.lsay { grid-area: 1 / 1 / 2 / -1; display: grid; contain: inline-size; }
.lsay > * { grid-area: 1 / 1; }
.ldone { margin: 0; font-weight: 600; }
/* empty, as tall as the words player.js puts in it at 90%, unseen: on a phone they are a line taller than the wait */
.ldone:empty::before { content: attr(data-say); visibility: hidden; }
.lsay:has(.ldone:not(:empty)) .lnext-wait { visibility: hidden; }
/* while the end card is up in the picture, it says the lesson is complete and offers Next: the line and the link
   under the player stand down, so neither is shown twice (player.js says it once, on the card), and their rows close
   up. Kept in place unseen, they left an empty band of 60 to 100 px between the picture and Previous. The card is in
   sight when it shows (a player docked away from its place is not followed by .lafter), so the questions and files
   that move up by those rows are below what the student is looking at; Replay puts the rows back */
.player:has(.player-end:not([hidden])) + .lafter :is(.ldone, .lnext),
.player:has(.player-end:not([hidden])) + .lafter .lsay:has(.ldone:not(:empty)) { display: none; }
.player:has(.player-end:not([hidden])) + .lafter:has(.ldone:not(:empty)) { row-gap: 0; }
.lprev, .lnext { font-weight: 600; text-decoration: underline; text-decoration-thickness: 1px; text-underline-offset: 4px; }
/* quieter than Next by its weight, but ink: muted grey with a pale line read as a disabled link */
.lprev { font-weight: 500; color: var(--fg-soft); text-decoration-color: var(--muted); grid-area: 2 / 1; }
.lprev:hover { color: var(--fg); text-decoration-color: var(--fg); }
/* side by side, each on its own column: a long pair wraps inside them, Next is never pushed under Previous. Next's
   lines end at the player's right edge, a wrapped one's too (left-aligned, its second line started mid-row), in two
   lines of a length (not one word alone under the rest) */
.lnext { grid-area: 2 / 2; justify-self: end; text-align: right; text-wrap: balance; }
/* hidden in place until 90%: out of sight, of the tab order and of a screen reader, its box kept (the head of .lafter) */
.lnext[hidden] { display: block; visibility: hidden; }
/* until the lesson is complete: what comes next and how it opens, on the line above Previous and Next, the whole width
   (beside Previous, it wrapped and was pushed right, a staircase) */
.lnext-wait { margin: 0; color: var(--muted); }

/* up to a small tablet, not only a phone: under 48rem the five columns left a title about 200px, on three lines */
@media (max-width: 48rem) {
  /* the title across the width, then under it the length and what the lesson brings ('12:04  Sheet  Files') */
  .lrow { grid-template-columns: 2.6rem auto 1fr auto; row-gap: 0.15rem; }
  .lrow .ltitle { grid-column: 2 / 4; }
  .lrow .lstate { grid-column: 4; }
  .lrow .ldur { grid-column: 2; grid-row: 2; text-align: left; min-width: 5ch; }   /* 9:28 and 11:18 alike */
  /* under the state too, empty on that line: on a 320px phone 'MINI-05A  Files' wrapped beside a 'Locked' */
  .lrow .lmarks { grid-column: 3 / 5; grid-row: 2; }
  .lrow .lmarks:empty { display: none; }
}
/* up to a tablet, not only a phone: side by side on a 768px tablet, Previous wrapped inside its half beside the half
   kept empty for the unseen Next */
@media (max-width: 56rem) {
  /* one column: Previous, then the line, whose wait spans both the completion's place and Next's under it. Next,
     unseen until 90%, waits inside the line's box instead of leaving a hole of one or two lines under Previous */
  .lafter { grid-template-columns: minmax(0, 1fr); row-gap: 0; align-items: start; }
  .lprev { grid-area: 1 / 1; margin-bottom: 0.75rem; }
  .lsay { display: contents; }
  .lsay > .ldone { grid-area: 2 / 1; }
  .lsay:has(+ .lnext) > .ldone { margin-bottom: 0.75rem; }
  .lsay > .lnext-wait { grid-area: 2 / 1 / 4; }
  /* a Next shown before the lesson is complete (an administrator's) is on its own row: the wait, spanning it, ran
     its second line under the link. Not a Next revealed at 90% (lafter-held): the wait, unseen then, still took its
     two lines on the completion's row alone and pushed Next and the page down by a line */
  .lafter:not(.lafter-held) .lsay:has(+ .lnext:not([hidden])) > .lnext-wait { grid-area: 2 / 1; margin-bottom: 0.75rem; }
  .lnext-wait > span { display: block; }   /* 'Next: …' then 'It opens…': the two lines that replace it, near enough */
  .lnext { grid-area: 3 / 1; justify-self: start; text-align: left; }
  /* the line and Next closed up under the end card (above): Previous ends the block, no room kept under it */
  .player:has(.player-end:not([hidden])) + .lafter:has(.ldone:not(:empty)) .lprev { margin-bottom: 0; }
}
/* a lesson whose Next is unseen until 90% (views._lafter, lafter-held): the same column at every width, Previous across
   the row, then the line in two lines ('Next: …', 'It opens…') in the place of the completion and of Next, which show
   there at 90% with nothing moving, and stay there until the page is opened again. Side by side, a long Previous
   wrapped in its half beside the half kept empty for Next (a 13-inch laptop, an iPad held sideways), and the line, set
   above them, was read after Previous */
.lafter-held { grid-template-columns: minmax(0, 1fr); row-gap: 0; align-items: start; }
.lafter-held .lprev { grid-area: 1 / 1; margin-bottom: 0.75rem; }
.lafter-held .lsay { display: contents; }
.lafter-held .lsay > .ldone { grid-area: 2 / 1; }
.lafter-held .lsay:has(+ .lnext) > .ldone { margin-bottom: 0.75rem; }
.lafter-held .lsay > .lnext-wait { grid-area: 2 / 1 / 4; }
.lafter-held .lnext-wait > span { display: block; }
.lafter-held .lnext { grid-area: 3 / 1; justify-self: start; text-align: left; }
.player:has(.player-end:not([hidden])) + .lafter-held:has(.ldone:not(:empty)) .lprev { margin-bottom: 0; }

/* The site swaps its logo through a class set by an inline pre-paint script,
   which this origin's CSP forbids: follow the system theme in CSS instead. */
@media (prefers-color-scheme: dark) {
  html:not(.light) .brand-logo-dark { display: inline-block; }
}

/* footer at the bottom of short pages */
body { min-height: 100vh; display: flex; flex-direction: column; }
.learn-main { flex: 1 0 auto; width: 100%; }
/* six footer links: one line where they fit, two rows of three below, never the last one alone on a second line */
@media (max-width: 460px) { .footer-nav { display: grid; grid-template-columns: repeat(3, auto); justify-content: center;
                                           justify-items: center; column-gap: 1.5rem; } }

/* as flex items with auto margins, the header and footer would shrink to their content */
body > .nav, body > .footer { width: 100%; box-sizing: border-box; }

/* admin */
.admin-nav { display: flex; gap: 0.5rem 1.4rem; flex-wrap: wrap; font-size: var(--fs-sm); color: var(--muted);
             padding-bottom: 1rem; margin-bottom: 2rem; border-bottom: 1px solid var(--rule); }
.admin-nav a { border-bottom: 1px solid transparent; padding-bottom: 2px; }
.admin-nav a:hover { color: var(--fg); }
/* the current page by colour and a rule, as the site's nav: bold would widen it and push the next items aside */
.admin-nav a[aria-current] { color: var(--fg); border-bottom-color: var(--fg); }
.anav-n { margin-left: 0.35rem; padding: 0 0.35rem; border: 1px solid var(--rule); color: var(--fg); font-weight: 600;
          font-variant-numeric: tabular-nums; }
.admin-nav + .lsection { border-top: 0; padding-top: 0; }
/* fingers: each link a 44px row (the site's header does the same on touch screens) */
@media (pointer: coarse) {
  .admin-nav { row-gap: 0; padding-bottom: 0.3rem; }
  .admin-nav a { padding: 0.7rem 0; }
}
/* a tablet: two rows of five in columns, rather than one full row and "Audit log" alone under it */
@media (min-width: 641px) and (max-width: 900px) {
  .admin-nav { display: grid; grid-template-columns: repeat(5, max-content); }
  .admin-nav a { width: fit-content; }
}
/* a student's page is titled by their address: data, at the heading size, each half held together so that a long
   one breaks at the @ (a half wider than the line still breaks inside, by the head's overflow-wrap); a certificate's
   page by its number, held on one line, which fits a phone's line whole at this size */
.learn-head h1.ahead-email { font-size: var(--fs-heading); letter-spacing: -0.025em; line-height: 1.15; }
/* its lines end where the room does: the site's text-wrap: pretty, which a paragraph's address takes from it (the post
   header), made Safari even the half out by cutting it at a hyphen ("karim.abdelrahman- / benyoussef.longaddress@")
   where the other browsers break before the dot, as the Ban form under it does */
.aemail { display: inline-block; max-width: 100%; text-wrap-style: auto; }
/* a run with no break point, cut into even pieces (backoffice._even): whole on a line that holds it, in pieces only
   on one that does not */
.apart { display: inline-block; max-width: 100%; }
/* a stretch between two dots with a hyphen in it (backoffice._half): whole on a line that holds it, so that the line
   breaks before a dot, at the hyphen only on a line narrower than the stretch */
.aseg { display: inline-block; max-width: 100%; }
a .aemail, a .apart, a .aseg { text-decoration: inherit; }   /* in a link, its underline: an inline-block does not take its parent's */
.anum { white-space: nowrap; }
.alink { text-decoration-line: underline; }
/* an explanation that runs to a second line keeps the measure the account's actions keep (.aact) */
.aprose { max-width: 46rem; }
/* the dashboard's figures: one hairline between cells, flat (the site's .tile lifts on hover, a figure does not
   invite a click); label, figure and caption share rows so a label on two lines does not lower its figure */
.atiles { display: grid; grid-template-columns: repeat(6, minmax(0, 1fr)); gap: 1px; background: var(--rule);
          border: 1px solid var(--rule); margin: 0 0 2rem; }
.atiles-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.atile { background: var(--bg-card); padding: 1rem; display: grid; grid-row: span 3; grid-template-rows: subgrid;
         row-gap: 0.25rem; align-content: start; }
.atile strong { font-size: var(--fs-2xl); font-weight: 600; letter-spacing: -0.02em; font-variant-numeric: tabular-nums; }
@media (max-width: 960px) { .atiles { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
/* two columns on a phone for every row of tiles; the Sales row's first figure (what was kept, the biggest sum) takes
   the whole width so that three tiles leave no empty cell */
@media (max-width: 520px) { .atiles { grid-template-columns: repeat(2, minmax(0, 1fr)); } .atiles-3 .atile:first-child { grid-column: 1 / -1; } }
.aneeds a { font-weight: 600; }
.atable { width: 100%; border-collapse: collapse; font-size: var(--fs-sm); display: block; overflow-x: auto; max-width: 100%; }
.atable th, .atable td { text-align: left; padding: 0.45rem 0.6rem; border-bottom: 1px solid var(--rule); vertical-align: top; }
/* rules between rows only: the section or the fold after a table draws the line under it, once */
.atable tbody tr:last-child > td { border-bottom: 0; }
.atable th { font-size: var(--fs-2xs); color: var(--muted); font-weight: 600; }
.atable a { text-decoration-line: underline; }
/* a wrapped table spans the column (the wrapper scrolls if it must), figures right-aligned */
.atable-wrap { overflow-x: auto; margin: 0 0 1.5rem; }
.atable-wrap:last-child { margin-bottom: 0; }
.atable-wrap > .atable { display: table; overflow: visible; }
.atable .num { text-align: right; font-variant-numeric: tabular-nums; }
.atable .nowrap { white-space: nowrap; }
.atable tr.is-muted td { color: var(--muted); }
.atable .azero { color: var(--muted); }   /* a count of none (Content's sheets and files): a fact, said quietly */
.asub { display: block; color: var(--muted); font-size: var(--fs-xs); font-weight: 400; overflow-wrap: anywhere; }
.amark { font-family: var(--font-mono); font-size: var(--fs-sm); letter-spacing: 0.04em; }
/* a command in the admin's prose (Security): set as code is in the sheets and the quizzes, at the dense size, in ink:
   the muted grey of the prose around it read at 3.68:1 on the chip */
.admin-nav ~ .lsection code { font-family: var(--font-mono); font-size: var(--fs-sm); background: var(--rule); color: var(--fg);
                              padding: 0.05rem 0.3rem; border-radius: var(--r-md); }
.tag-warn { border-color: var(--status-brick); color: var(--status-brick); }
.atable .tag:first-child { margin-left: 0; }
/* a device's name and its "temporary" tag: the space between them is a gap, which a wrapped tag does not carry to the
   start of its line, as a margin did (backoffice.user) */
.adev-name { display: inline-flex; flex-wrap: wrap; align-items: baseline; gap: 0.25rem 0.4rem; }
.adev-name > .tag { margin-left: 0; }
/* a state on a detail page, the chip the lists use: it starts its fact, or follows "Verdict:" */
.adl dd > .tag:first-child, .areport .tag { margin-left: 0; }
.asearch { display: flex; gap: 0.6rem; margin-bottom: 1rem; flex-wrap: wrap; }
.asearch input { font: inherit; padding: 0.65rem 0.75rem; border: 1px solid var(--field-edge); border-radius: var(--r-md);
                 background: var(--bg-card); color: var(--fg); min-width: 18rem; }
.asearch .lbtn { align-self: stretch; }   /* as tall as the field beside it (48px), not its own 44px */
.afilters { display: flex; flex-wrap: wrap; gap: 0.4rem 1.1rem; font-size: var(--fs-sm); color: var(--muted); margin: 0 0 1.25rem; }
/* as the admin menu above it: the current filter has one rule under it, no link underlines */
.afilters a { border-bottom: 1px solid transparent; padding-bottom: 2px; text-decoration: none; }
.afilters a:hover, .afilters a[aria-current] { color: var(--fg); border-bottom-color: var(--fg); }
@media (pointer: coarse) { .afilters { row-gap: 0; } .afilters a { padding: 0.7rem 0; min-width: 1.5rem; text-align: center; } }
.acount { margin: 0 0 0.75rem; }
.apager { display: flex; gap: 1.5rem; font-size: var(--fs-sm); }
.apager a { text-decoration-line: underline; }
@media (pointer: coarse) { .apager a { padding: 0.7rem 0; } }
.atags { margin: -1rem 0 1.5rem; }
.atags .tag:first-child { margin-left: 0; }
.adl { display: grid; grid-template-columns: 12rem 1fr; gap: 0.5rem 1.25rem; font-size: var(--fs-md); margin: 0 0 1.5rem; }
.adl dt { color: var(--muted); }
.adl dd { margin: 0; }
.adl a { text-decoration-line: underline; }
.aline { display: block; }   /* a second fact of the same row, in the row's own ink (.asub is the muted one) */
/* actions on an account: one row each, the box before its button, what it does underneath, a hairline between */
/* the rows' hairlines run as wide as the section's own rule; what is in them keeps the reading measure */
.aacts { margin-top: 1.25rem; border-top: 1px solid var(--rule); }
.aact { padding: 0.9rem 0; border-bottom: 1px solid var(--rule); display: flex; flex-direction: column; gap: 0.4rem; }
.aact > * { max-width: 46rem; }
.aact:last-child { border-bottom: 0; }
.aact[id] { scroll-margin-top: 1.5rem; }   /* Cancel on its confirmation lands on it: its field in view, with room above */
.aact > .lsmall { margin: 0; }
.aact-form { margin: 0; display: flex; flex-direction: column; gap: 0.6rem; }
.aact-ctl { display: flex; align-items: center; gap: 0.6rem 1rem; flex-wrap: wrap; }
.aact .lbtn, .adev-acts .lbtn { font-size: var(--fs-sm); padding: 0.45rem 0.85rem; }   /* .lbtn-sm's size */
@media (pointer: coarse) { .aact .lbtn, .adev-acts .lbtn { min-height: 2.75rem; } }
.aact-field { display: flex; flex-direction: column; gap: 0.35rem; max-width: 26rem; }
.aact-field input, .aact-field textarea, .acert-revoke input {
  font: inherit; font-size: var(--fs-md); padding: 0.45rem 0.7rem; border: 1px solid var(--field-edge); border-radius: var(--r-md);
  background: var(--bg-card); color: var(--fg); min-width: 0;
}
.aact-field textarea { resize: vertical; line-height: 1.5; }   /* a reason sent to the student, read back whole */
.aact-field [aria-invalid="true"] { border-color: var(--status-brick); }   /* a reason required and left out */
.aact-field input:focus-visible, .acert-revoke input:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 1px; }
.aact-field textarea:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 1px; }
.acert-revoke input::placeholder { color: var(--muted); opacity: 1; }
.adev-acts { display: flex; flex-direction: column; gap: 0.5rem; }
.acert-revoke { display: flex; gap: 0.5rem; flex-wrap: wrap; align-items: center; margin: 0; }
.acert-revoke input { flex: 1 1 10rem; }
/* a section of lessons or of a quiz bank, folded */
.asec { border-top: 1px solid var(--rule); margin: 0; }
.asec:last-child { border-bottom: 1px solid var(--rule); }
/* the chevron of the course's sections, the sign that the row opens (the browser's black triangle is not the
   site's); the name is bold, what follows it is a caption */
.asec > summary, .aquiz > summary { cursor: pointer; list-style: none; position: relative; font-weight: 400; }
.asec > summary { padding: 0.8rem 2rem 0.8rem 0; }
.asec > summary::-webkit-details-marker, .aquiz > summary::-webkit-details-marker { display: none; }
.asec > summary::after, .aquiz > summary::after {
  content: ""; position: absolute; right: 0.4rem; top: 1.25rem; width: 0.5rem; height: 0.5rem; border: solid var(--muted);
  border-width: 0 1px 1px 0; transform: translateY(-50%) rotate(45deg); transition: transform 160ms ease; }
.asec[open] > summary::after, .aquiz[open] > summary::after { transform: translateY(-10%) rotate(-135deg); }
.asec > summary:hover::after, .aquiz > summary:hover::after { border-color: var(--fg); }
.asec > summary:focus-visible, .aquiz > summary:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 2px; }
@media (prefers-reduced-motion: reduce) { .asec > summary::after, .aquiz > summary::after { transition: none; } }
.asec-name { font-weight: 600; margin-right: 0.75rem; }
/* on a phone, the name on its line and the facts under it */
@media (max-width: 640px) { .asec-name { display: block; margin: 0 0 0.15rem; } }
.asec[open] > summary { padding-bottom: 0.5rem; }
.asec > .atable-wrap { margin-bottom: 1rem; }
/* a student's quizzes, folded at the end of the Course section: the next section's rule closes it, once */
.lsection > .aquizzes:last-child { border-bottom: 0; }
/* a student's removed and expired devices, folded under the live ones: closed the same way */
.lsection > .adevs-ended:last-child { border-bottom: 0; }
/* a bank's question, opened in its table cell to show its choices */
.aquiz { margin: 0; }
.aquiz > summary { padding-right: 1.5rem; }
.aquiz > summary::after { top: 0.6rem; right: 0.2rem; }
.aq-choices { margin: 0.5rem 0 0.25rem; padding-left: 1.1rem; list-style: disc; }
.aq-choices li { margin: 0.2rem 0; }
@media (max-width: 640px) {
  .adl { grid-template-columns: minmax(6.5rem, 38%) 1fr; gap: 0.55rem 0.9rem; font-size: var(--fs-sm); }
  .adl dd { overflow-wrap: anywhere; }
  /* the field keeps room for an address being typed (and its whole placeholder): on a narrower row Search goes
     under it */
  .asearch input { min-width: 0; flex: 1 1 14.5rem; }
  /* tables become one card per row, each value under its column's name: nothing hides off the screen */
  .atable-stack thead { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; }
  .atable-wrap > .atable-stack, .atable-stack tbody, .atable-stack tr { display: block; width: 100%; }
  .atable-stack tr { padding: 0.65rem 0; border-bottom: 1px solid var(--rule); }
  .atable-stack tr:last-child { border-bottom: 0; }   /* the next section's rule closes the last card */
  .atable-stack td { display: grid; grid-template-columns: minmax(6.5rem, 38%) minmax(0, 1fr); gap: 0.9rem; padding: 0.15rem 0;
                     border: 0; overflow-wrap: anywhere; }
  .atable-stack td::before { content: attr(data-label); color: var(--muted); overflow-wrap: normal; }
  .atable-stack td.num, .atable-stack td.nowrap { text-align: left; white-space: normal; }
  .atable-stack td:first-child { display: block; font-weight: 600; padding-bottom: 0.3rem; }
  .atable-stack td:first-child::before, .atable-stack td.wide::before { content: none; }
  /* a long cell (a bank's question) takes the card's width under its title rather than 62% beside a label */
  .atable-stack td.wide { display: block; padding: 0.3rem 0 0.45rem; }
  .atable-stack td:empty { display: none; }
  .atable-stack td .lbtn { white-space: nowrap; }
  /* a long address (backoffice._acct) takes the card's width, under its label */
  .atable-stack td:not(:first-child):has(.along) { grid-template-columns: minmax(0, 1fr); gap: 0.1rem; }
  .atable-stack td .ashort { white-space: normal; }
  /* Stripe's checkout id in three pieces of 22 (backoffice._even), each whole on a line the card's width */
  .atable-stack td.aref { grid-template-columns: minmax(0, 1fr); gap: 0.1rem; }
  /* a few columns of figures stay side by side, in tighter cells so that a phone holds five */
  .atable-figs th, .atable-figs td { padding: 0.45rem 0.3rem; }
  .atable-figs th:first-child, .atable-figs td:first-child { padding-left: 0; }
  .atable-figs th:last-child, .atable-figs td:last-child { padding-right: 0; }
  /* with three figures or more, each row's name takes a line of its own (no section name on three lines) and its
     figures share the line under it in columns, the same in every row and in the head: a column still reads down
     the page, as a funnel must. One grid for the whole table, its rows subgrids of it: a column is as wide as its
     widest word, a head's or a figure's, and the room left is shared evenly. In equal columns a head broke inside a
     word ("Attemp / ts", "Studen / ts") and a date under a chip ran out of its cell */
  .atable-wrap > .atable-split { display: grid; grid-template-columns: repeat(var(--acols, 4), minmax(min-content, 1fr)); }
  .atable-split:has(> thead > tr > :nth-child(4):last-child) { --acols: 3; }
  .atable-split:has(> thead > tr > :nth-child(6):last-child) { --acols: 5; }
  .atable-split:has(> thead > tr > :nth-child(7):last-child) { --acols: 6; }
  .atable-split thead, .atable-split tbody, .atable-split tr { display: grid; grid-template-columns: subgrid;
                                                               grid-column: 1 / -1; }
  .atable-split thead tr { align-items: end; }   /* a one-line head sits on the line of a two-line one */
  .atable-split th { overflow-wrap: normal; }
  .atable-split th:first-child { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0, 0, 0, 0); }
  .atable-split td:first-child { grid-column: 1 / -1; border-bottom: 0; padding: 0.55rem 0 0.1rem; font-weight: 600; }
  .atable-split th:nth-child(2), .atable-split td:nth-child(2) { padding-left: 0; }
  .atable-split td:not(:first-child) { padding-top: 0.1rem; padding-bottom: 0.55rem; }
  .atable-split .num { text-align: left; }   /* as on a tablet (.atable-wide): columns, figures from the left */
}
/* the Students table's seven columns do not fit a tablet (an iPad in portrait is 820 or 834px): there its account
   takes a line of its own and the six others share the line under it in equal columns, as figures do on a phone,
   instead of scrolling sideways; the band ends where the admin menu's two rows do */
@media (min-width: 641px) and (max-width: 900px) {
  .atable-wrap > .atable-wide, .atable-wide thead, .atable-wide tbody { display: block; }
  .atable-wide tr { display: flex; flex-wrap: wrap; }
  .atable-wide th, .atable-wide td { flex: 1 1 0; min-width: 0; }
  /* six equal columns overshoot the row by a fraction of a pixel in Safari (it lays out in 64ths of a pixel), which
     scrolled the table sideways by 1px: a pixel of slack takes it */
  .atable-wide tr { padding-right: 1px; }
  .atable-wide th:first-child { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0, 0, 0, 0); }
  .atable-wide td:first-child { flex-basis: 100%; border-bottom: 0; padding: 0.55rem 0 0.1rem; font-weight: 600; }
  .atable-wide th:nth-child(2), .atable-wide td:nth-child(2) { padding-left: 0; }
  .atable-wide td:not(:first-child) { padding-top: 0.1rem; padding-bottom: 0.55rem; }
  .atable-wide .num { text-align: left; }   /* equal columns: a figure on the right would leave a hole before it */
  /* Stripe's checkout id (66 characters) wraps in a narrow column: left whole, it took the room of the student's
     address, which then broke inside a word */
  .atable td.aref .asub { max-width: 24ch; }
}
/* an account in any admin table (Students, Sales, Reports, Certificates, Security, Audit log): an address with no
   hyphen (42 characters, 77) breaks inside its cell rather than scrolling the table sideways: at the @, before a dot
   of the name, then between the even pieces of a long run (backoffice._acct), inside a piece only on a line narrower
   than it. break-word, not anywhere: the column keeps the width of its longest piece, where anywhere let the table
   crush it to a few letters and leave the @ alone on a line. The halves and pieces are inline blocks, which do not
   inherit the link's underline: they draw their own */
.atable a[href^="/admin/user?"] { overflow-wrap: break-word; }
.atable a .aemail, .atable a .apart, .atable a .aseg { text-decoration: inherit; }
/* an address of 24 characters or fewer holds whole: it fits any column, where beside a long detail the table crushed
   it to "mid@ / ux.test" (backoffice._acct); a phone's card crushes nothing and lets it wrap */
.atable .ashort { white-space: nowrap; }
/* one of 25 to 40 characters, most real ones, holds whole where a laptop's table has the room: the table gave a long
   detail beside it that room and broke the address at the @ on every row ("karim.benali@ / protonmail.com") */
@media (min-width: 1100px) { .atable .amid { white-space: nowrap; }
  /* its halves as one run of text, its break points out: as two inline blocks the table still counted a break between
     them, gave the column the first half's width and the address ran into the next column; and a <wbr> breaks a line
     whatever white-space says */
  .atable .amid :is(.aemail, .aseg) { display: inline; }
  .atable .amid wbr { display: none; } }
/* a column of accounts holds a typical address, 26 to 31 characters: at 1024px a long detail beside it took the room
   and broke "marie-charlotte.de-la- / fontaine@ / universite-paris.fr" at its hyphens. There each half fits, so an
   address breaks at the @ only, and the detail gives way first. On a tablet (641 to 900px, an iPad upright) the same,
   as wide as the other columns leave it: 16rem made five columns scroll sideways at 641px, where the room left after
   the date, the event, the administrator and the detail's longest word is about 10rem. Only where such an address is
   listed: a column of short ones keeps its own width */
@media (min-width: 641px) {
  .atable td[data-label="Account"]:has(a:is(.amid, .along)) { min-width: min(16rem, 100vw - 30rem); } }
/* Stripe's checkout id in its even pieces (backoffice._even): the column keeps a piece's width */
.atable td.aref .asub { overflow-wrap: break-word; }

/* Questions under a lesson */
.lfield textarea {
  font: inherit; font-size: var(--fs-md); color: var(--fg); background: var(--bg-card);
  border: 1px solid var(--field-edge); border-radius: var(--r-md); padding: 0.65rem 0.75rem; width: 100%; resize: vertical;
}
.lfield textarea:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 1px; }
/* the questions' boxes show code as it will be posted: the monospace face keeps a pasted dump's columns and makes the
   ``` fences plain before the Post button, a tab four columns as in the posted block; 1rem, or an iPhone zooms in.
   They grow with the text (qa.js fit) */
.qa .lfield textarea, .aq .lfield textarea, .aq-lost .lfield textarea { font-family: var(--font-mono); tab-size: 4;
  white-space: pre-wrap; }
.qa { margin-top: 2.5rem; padding-top: 1.75rem; border-top: 1px solid var(--rule); max-width: 46rem; }
.qa h2, .lfiles h2 { font-size: var(--fs-xl); font-weight: 600; letter-spacing: -0.015em; }  /* as .lsection h2 */
.qa h2 { margin: 0 0 0.35rem; }
.qa-mine { margin: 0 0 1.25rem; }
.qa .lform { max-width: none; }   /* the composer as wide as the thread it adds to */
.qa-ask { margin-bottom: 1.5rem; }
.qa-thread { padding: 1rem 0; border-top: 1px solid var(--rule); }
.qa-post { scroll-margin-top: 1.5rem; }
.qa-post + .qa-post { margin: 0.75rem 0 0 1.25rem; padding-left: 0.9rem; border-left: 1px solid var(--rule); }
/* the post a link or an action points at: a quiet fill, no layout shift. It reaches above and below the post, never
   past its sides: an outline went 8px into a phone's gutter, off the column every other block keeps to */
.qa-post:target, .qa-post:focus { background: var(--bg-soft); outline: 0;
  box-shadow: 0 -0.5rem 0 var(--bg-soft), 0 0.5rem 0 var(--bg-soft); }
.qa-meta { font-size: var(--fs-xs); color: var(--muted); margin: 0 0 0.3rem; display: flex; gap: 0.2rem 0.5rem; flex-wrap: wrap; align-items: baseline; }
.qa-meta strong { color: var(--fg); font-weight: 600; }
.qa-badge { font-weight: 600; color: var(--accent); }
.qa-email { color: var(--muted); text-decoration-line: underline; overflow-wrap: anywhere; }
.qa-flag { color: var(--muted); border: 1px solid var(--rule); padding: 0 0.35rem; }
.qa-flag-warn { border-color: var(--status-brick); }
.qa-meta .qa-flag-new { color: var(--fg); }   /* a reply this student has not had on screen yet */
.qa-body { font-size: var(--fs-md); line-height: 1.6; overflow-wrap: anywhere; }
.qa-body p { margin: 0 0 0.6rem; white-space: pre-wrap; }
.qa-body > :last-child { margin-bottom: 0; }
.qa-body a { color: var(--fg); text-decoration-line: underline; }
.qa-body code { font-family: var(--font-mono); font-size: var(--fs-sm); background: var(--rule); padding: 0.05rem 0.3rem; border-radius: var(--r-md); }
.qa-body pre { font-family: var(--font-mono); font-size: var(--fs-sm); line-height: 1.5; background: var(--bg-card); border: 1px solid var(--rule);
  padding: 0.75rem 0.9rem; margin: 0 0 0.6rem; overflow-x: auto; white-space: pre; overflow-wrap: normal; }
.qa-body pre code { background: none; padding: 0; font-size: inherit; }
/* a post's code with its Copy button (qa.js): the box is the wrapper's, the button in a bar above the lines, so a
   line wider than the box scrolls under nothing */
.qa-body .code-block { margin: 0 0 0.6rem; background: var(--bg-card); border: 1px solid var(--rule); }
.qa-body .code-block > pre { margin: 0; border: 0; padding-top: 0.15rem; }
.qa-body .code-block > pre:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: -2px; }  /* it scrolls */
.qa-body > .code-block:last-child { margin-bottom: 0; }
.qa-code-bar { display: flex; justify-content: flex-end; padding: 0.35rem 0.35rem 0; }
.qa-copy { font: inherit; font-size: var(--fs-sm); line-height: 1.2;
  color: var(--fg); background: var(--bg-card); border: 1px solid var(--rule); border-radius: var(--r-md);
  padding: 0.3rem 0.65rem; cursor: pointer; }
@media (hover: hover) { .qa-copy:hover { background: var(--bg-soft); } }   /* a tap leaves :hover on a phone */
.qa-copy:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 2px; }
.qa-said { margin: 0.5rem 0 0; font-size: var(--fs-sm); }
.qa-said-error { color: var(--status-brick); }
.qa > .qa-said { margin: 0 0 1rem; }
/* a reply deleted: said where it stood, in its thread's indent */
.qa-thread > .qa-said { margin: 0.75rem 0 0 1.25rem; padding-left: 0.9rem; border-left: 1px solid var(--rule); }
.qa-said:focus { outline: none; }   /* focused by qa.js so it is read out; a line of text, not a control */
/* a post's buttons, its author's Edit and Delete as the instructor's moderation: the queue's small outline buttons */
.qa-own, .qa-mod { display: flex; flex-wrap: wrap; gap: 0.5rem; align-items: center; margin-top: 0.75rem; }
.qa-own + .qa-mod { margin-top: 0.5rem; }   /* his own question: its Edit and its Pin, one group over two rows */
.qa-mod .qa-inline:has(.qa-person) { margin-left: auto; }   /* Ban acts on the person: set apart from the post's */
/* quiet text, in the weight of Ban, whose place it takes: the post's own buttons are the work */
.qa-person.linklike { text-align: right; font-weight: 600; }
/* Ban folds the reason the student's mail carries: its quiet label at the row's end on every post and every card, as
   Unban; open, it stays there, and the reason and the button (the form after it, views.qa_ban) take a line of their
   own under the row, on its left like the rest of the row */
.qa-ban { margin: 0 0 0 auto; }
.qa-ban-form { display: none; }
.qa-ban[open] + .qa-ban-form { display: flex; flex-basis: 100%; max-width: none; margin: 0.25rem 0; gap: 0.75rem; }
.qa-ban-form > * { max-width: 26rem; }
/* a long address over several lines starts at the left, as the address above the post does (views.qa_ban); the phone
   rule centres every button's text */
.qa-ban-form .lbtn { text-align: left; }
/* its reason is prose for a mail, not a post: the text face, where the questions' boxes take the monospace one */
.qa-ban-form .lfield textarea { font-family: inherit; }
/* the disclosure mark and the weight of every other summary (Reply, Rename), at the row's end */
.qa-ban > summary { cursor: pointer; font-size: var(--fs-sm); color: var(--muted); width: fit-content; margin-left: auto; }
@media (hover: hover) { .qa-ban > summary:hover { color: var(--fg); } }
.qa-ban > summary:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 2px; }
.qa-inline { margin: 0; }
.qa-edit { margin: 0; }   /* in a row of buttons: no paragraph's space under it (it opened a gap above Hide) */
.qa-edit[open] { flex-basis: 100%; }
/* while the post is edited, its Delete stands beside Save (views._post_html): the row's own gives way to it */
.qa-edit[open] ~ .qa-inline { display: none; }
.qa-reply > summary:not(.lbtn) { cursor: pointer; font-size: var(--fs-sm); color: var(--muted); width: fit-content; }
@media (hover: hover) { .qa-reply > summary:not(.lbtn):hover { color: var(--fg); } }   /* a tap left it dark */
/* a summary drawn as a button (Edit, the instructor's Reply): its chevron after the label at the label's own gap */
:is(.qa-edit, .qa-answer) > summary.lbtn { gap: 0; width: fit-content; }
.qa-edit form { margin: 0.6rem 0 0.25rem; }
.qa-reply { margin: 0.6rem 0 0; }   /* under the post it answers, aligned with it and its Edit, as in the queue */
.qa-reply form { margin-top: 0.6rem; }
/* the instructor's Reply, on a lesson as in the queue: its one filled button; open, it hands that weight to "Post the
   answer" */
.qa-answer { margin-top: 0.9rem; }
.qa-answer[open] > summary.lbtn { background: var(--bg-card); color: var(--fg); border-color: var(--rule); }
@media (hover: hover) {
  .qa-answer[open] > summary.lbtn:hover { background: var(--fg); color: var(--bg); border-color: var(--fg); } }
.qa-count { font-size: var(--fs-sm); color: var(--muted); }
/* a text handed over from a box that could no longer take it (views._qa_field, qa.js rehome): under the box, as help
   in the text's ink, never a box of its own between the label and the field, nor a refusal's brick */
.qa .qa-moved { color: var(--fg); margin: 0; }
.qa form .qa-fail { margin: 0; }
.qa-post > .qa-fail { margin: 0.6rem 0 0; }   /* under a row of small buttons (qa.js fail), its own line */
.qa-jump { margin: 1rem 0 0; font-size: var(--fs-sm); }
.qa-jump a, .qa-mine a, .qa .lhint a { color: var(--fg); text-decoration-line: underline; }
.qa .lhint code { font-family: var(--font-mono); }
.qa-list .qa-mine-text { line-height: 1.5; overflow-wrap: anywhere; }
.qa-list .qa-mine-code { color: var(--muted); }   /* a code block stands as a word in the sentence (views._excerpt) */
.qa-list .srow-name a { text-decoration-line: underline; }
.qa-list .qa-flag-new { margin-left: 0.6rem; font-size: var(--fs-sm); font-weight: 400; color: var(--fg); white-space: nowrap; }
@media (pointer: coarse) {  /* fingers: every small control of a thread is at least 44px tall, all on one line */
  .qa-reply > summary:not(.lbtn), .qa-person.linklike { padding: 0.75rem 0.15rem; line-height: 1.5; }
  /* Ban and Unban, one short word: as wide as a finger too */
  .qa-ban > summary, .qa-person.linklike { min-width: 44px; box-sizing: border-box; text-align: center; }
  .qa-ban > summary { padding: 0.75rem 0.15rem; line-height: 1.5; }
  .qa-reply > summary:not(.lbtn) { padding-left: 0; }   /* on the post's left edge */
  .qa-own .lbtn, .qa-mod .lbtn, .aq-acts .lbtn { min-height: 44px; }
  .qa-copy { min-height: 44px; padding: 0.5rem 0.9rem; }
}
/* the admin queue of questions: one card per thread, answered in place (its Waiting and Hidden tabs are .afilters) */
.aq-intro { max-width: 46rem; }
.aq { border-top: 1px solid var(--rule); padding: 1.25rem 0; max-width: 46rem; scroll-margin-top: 1.5rem; }
.aq-head { font-size: var(--fs-lg); font-weight: 600; letter-spacing: 0; margin: 0 0 0.2rem; }
.aq-head a { text-decoration-line: underline; }
.aq > .lsmall { margin: 0 0 0.25rem; }
.aq-post { margin-top: 0.75rem; }
.aq-post + .aq-post { margin-left: 1.25rem; padding-left: 0.9rem; border-left: 1px solid var(--rule); }
.aq .lform { max-width: none; }
.aq-acts { display: flex; gap: 0.6rem; flex-wrap: wrap; align-items: center; margin-top: 0.9rem; }
.aq-acts .qa-inline:has(.qa-person) { margin-left: auto; }   /* Ban acts on the person: set apart, as on a lesson */
.aq-acts .qa-inline { max-width: 100%; }
.aq-acts .qa-person { overflow-wrap: anywhere; }   /* it names an unnamed student by address, which may be long */
.aq-post-acts { margin-top: 0.6rem; }   /* a reply's own buttons, under it, inside its indent */
.aq-done { display: flex; flex-wrap: wrap; align-items: center; gap: 0.5rem 1rem; }   /* the notice and its Undo */
.aq-lost { max-width: 46rem; margin: 0 0 1.75rem; }
.aq-lost .notice { margin-bottom: 1rem; }

/* Section quiz. The number hangs in the left gutter on the prompt's first line (a fieldset's legend is not a
   line box, so a list marker would sit on the line under it); the picked choice's row is marked. */
.quiz-list { list-style: none; padding: 0; margin: 1.5rem 0; }
.quiz-q { position: relative; padding: 1.25rem 0 1.25rem 2.25rem; border-top: 1px solid var(--rule); scroll-margin-top: 1rem; }
.quiz-q fieldset { border: 0; padding: 0; margin: 0; min-width: 0; }
.quiz-prompt { font-weight: 600; margin: 0 0 0.5rem; padding: 0; line-height: 1.45; }
.quiz-num { position: absolute; left: 0; font-variant-numeric: tabular-nums; color: var(--muted); }
.quiz-hint { margin: -0.25rem 0 0.5rem; }
.quiz-choice { display: flex; gap: 0.75rem; align-items: flex-start; padding: 0.55rem 0.75rem; margin: 0 0 0 -0.75rem;
               font-size: var(--fs-md); line-height: 1.45; cursor: pointer; }
/* drawn controls: the native ones are 13 px, faint in Safari, and grey discs in Chrome's dark scheme */
.quiz-choice input { -webkit-appearance: none; appearance: none; flex: none; display: grid; place-content: center;
                     width: 1.1rem; height: 1.1rem; margin: 0.15rem 0 0; border: 1px solid var(--field-edge);
                     border-radius: var(--r-md); background: var(--bg-card); cursor: pointer; }
.quiz-choice input[type="radio"] { border-radius: 50%; }
.quiz-choice input::before { content: ""; width: 0.5rem; height: 0.5rem; border-radius: 50%; background: var(--fg); transform: scale(0); }
.quiz-choice input[type="checkbox"]::before { width: 0.65rem; height: 0.65rem; border-radius: 0; background: var(--bg-card);
                                              clip-path: polygon(14% 44%, 0 65%, 50% 100%, 100% 16%, 80% 0, 43% 62%); }
.quiz-choice input:checked { border-color: var(--fg); }
.quiz-choice input[type="checkbox"]:checked { background: var(--fg); }
.quiz-choice input:checked::before { transform: scale(1); }
@media (forced-colors: active) { .quiz-choice input { -webkit-appearance: auto; appearance: auto; } }
@media (hover: hover) {  /* a tap leaves :hover on touch screens */
  .quiz-choice:hover { background: var(--bg-soft); }
  .quiz-choice:hover input { border-color: var(--fg); }
}
/* the picked row is filled; its edge stays uncoloured (The Hairline Rule: no side stripe) */
.quiz-choice:has(input:checked) { background: var(--bg-soft); }
.quiz-choice:has(input:focus-visible) { outline: 2px solid var(--focus-ring); outline-offset: -2px; }
.quiz-choice input:focus-visible { outline: none; }
/* in ink, as the sheets' code: inside an explanation (muted) the grey read at 3.68:1 on the chip */
.quiz-q code { font-family: var(--font-mono); font-size: var(--fs-sm); font-weight: 400; background: var(--rule);
               color: var(--fg); padding: 0.05rem 0.3rem; border-radius: var(--r-md); white-space: nowrap; }
.quiz-q code.long { white-space: normal; overflow-wrap: anywhere; }
@media (max-width: 640px) { .quiz-q { padding-left: 1.75rem; } }
.quiz-missing { margin: -0.25rem 0 0.35rem; font-size: var(--fs-sm); font-weight: 600; color: var(--status-brick); }
.quiz-q-missing .quiz-num { color: var(--status-brick); font-weight: 600; }
.quiz-form .lform-actions { margin-top: 0.5rem; }
.quiz-form > .notice { margin: 1.5rem 0 0; }
/* why a pick was not saved: held at the foot of the screen while the questions are in view, the strip under it
   painted in the page's colour so no question shows through */
.quiz-form > .quiz-save { position: sticky; bottom: 0.75rem; z-index: 2; box-shadow: 0 0.75rem 0 var(--bg); }
.quiz-answer { margin: 0.5rem 0 0; font-size: var(--fs-md); line-height: 1.5; }
.quiz-answer .kicker { margin-right: 0.35rem; }
.quiz-picks { list-style: disc; padding-left: 1.2rem; margin: 0.25rem 0 0; }
.quiz-picks li + li { margin-top: 0.2rem; }
.quiz-mark { margin: 0 0 0.25rem; font-size: var(--fs-sm); font-weight: 600; color: var(--status-brick); }
.quiz-q-missed .quiz-num { color: var(--status-brick); font-weight: 600; }
.quiz-explain { margin: 0.5rem 0 0; color: var(--muted); font-size: var(--fs-md); line-height: 1.55; }
/* the result of an attempt: the score, then the lessons to watch again, as links, under it */
.quiz-result p, .quiz-pass p { margin: 0 0 0.5rem; }
.quiz-result > :last-child, .quiz-pass > :last-child { margin-bottom: 0; }
/* the questions a result or a refused hand-in names, each a jump to its question: a box a finger can hit (the digits
   in a sentence were 8px wide and 15px apart), 44px on a touch screen */
.quiz-jumps { display: flex; flex-wrap: wrap; gap: 0.5rem; list-style: none; padding: 0; margin: 0; }
.notice > p:has(+ .quiz-jumps) { margin-bottom: 0.75rem; }
.quiz-jumps a { display: flex; align-items: center; justify-content: center; min-width: 2.25rem; height: 2.25rem;
                padding: 0 0.25rem; border: 1px solid var(--rule); border-radius: var(--r-md); background: var(--bg-card);
                color: var(--fg); font-weight: 600; font-variant-numeric: tabular-nums; text-decoration-line: none; }
@media (hover: hover) { .quiz-jumps a:hover { border-color: var(--fg); } }
.quiz-jumps a:active { background: var(--bg-soft); }
@media (pointer: coarse) { .quiz-jumps a { min-width: 2.75rem; height: 2.75rem; } }
.quiz-missed { margin: 0 0 1.5rem; }
.quiz-missed p { margin: 0 0 0.25rem; font-size: var(--fs-md); line-height: 1.5; }
.quiz-lessons { list-style: none; padding: 0; margin: 0; }
.quiz-lessons li { padding: 0.3rem 0; }
.quiz-wait { margin: 0 0 1rem; }
.lrow-quiz .ltitle { font-weight: 600; }
/* the quiz row has no marks or length: its state ("Retry at 16:45") takes those columns, on the title's line */
.lrow-quiz .lstate { grid-column: 3 / -1; white-space: nowrap; }
@media (max-width: 48rem) { .lrow-quiz .lstate { grid-column: 4; } }

/* Final report and certificate */
/* a path, a URL or a SHA-256 in a comment wraps inside the column instead of widening the page */
.report-comment { margin: 0.75rem 0 0; white-space: pre-wrap; font-size: var(--fs-md); line-height: 1.6; overflow-wrap: anywhere; }
.report-comment .kicker { display: block; margin: 0 0 0.3rem; }
/* its code wraps, as the mail of the same comment shows it, rather than scrolling in a box the keyboard cannot reach */
.report-comment .qa-body pre { white-space: pre-wrap; overflow-wrap: anywhere; }
/* a revocation's reason as it was typed, its paragraphs kept like a comment's (views.revocation, the panel's pages) */
.revoke-reason { white-space: pre-line; overflow-wrap: anywhere; }
.notice:has(> .revoke-reason) > p { margin: 0 0 0.5rem; }
.notice:has(> .revoke-reason) > p:last-child { margin-bottom: 0; }
.report-status { color: var(--muted); font-weight: 400; margin-left: 0.3rem; }
.lcode-whole { white-space: nowrap; }   /* LAB-16 on one line: Safari broke it at its hyphen (views.whole_codes) */
.report-version > .lsmall { overflow-wrap: anywhere; }   /* the name on the certificate, as long as it was typed */
.report-files, .report-picked { list-style: none; padding: 0; margin: 0.75rem 0 0; font-size: var(--fs-sm); }
.report-files li, .report-picked li { display: flex; gap: 0.6rem; align-items: baseline; flex-wrap: wrap; padding: 0.4rem 0;
  border-top: 1px solid var(--rule); }
.report-file, .report-picked .report-file { overflow-wrap: anywhere; min-width: 0; }
.report-picked .report-bad { color: var(--status-brick); }
.report-picked .report-bad .lsmall { color: inherit; font-weight: 600; }
.report-errors { white-space: pre-line; }  /* one refusal per line, as report.js writes them */
.report-progress { margin: 0; font-size: var(--fs-sm); font-variant-numeric: tabular-nums; }
/* an upload whose answer was lost: the way to ask the server again, on its own line under what the notice says */
.report-failed .report-check { display: flex; width: fit-content; margin-top: 0.75rem; }
.report-actions { display: flex; flex-direction: column; gap: 1.5rem; margin-top: 1.5rem; }
.report-actions .lform { margin: 0; }
/* the name to print, on the hand-in, its correction and the panel's steps, shown whole while it is proofread: 80
   characters fit the measure, not a 26rem form; the forms stacked under it in one column take the same width (the
   take-back under the name of a report waiting, the revocation under the correction of a certificate) */
.lform:has(input.lcert-name), .report-actions .lform, .acert-revocation { max-width: var(--measure); }
.report-earlier { font-size: var(--fs-xl); font-weight: 600; letter-spacing: -0.015em; margin: 2.5rem 0 0; }
.report-statement { margin: 0 0 2rem; }
.lfield input[type="file"] { padding: 0.5rem; font-size: var(--fs-sm); color: var(--muted); max-width: 100%; }
/* the file picker's own button, drawn like a ghost button instead of the browser's grey rounded one */
.lfield input[type="file"]::file-selector-button {
  font: inherit; font-weight: 600; font-size: var(--fs-sm); color: var(--fg); background: var(--bg-card);
  border: 1px solid var(--rule); border-radius: var(--r-md); padding: 0.4rem 0.85rem; margin-right: 0.85rem; cursor: pointer;
  transition: background-color 160ms ease, border-color 160ms ease, color 160ms ease;
}
@media (hover: hover) {
  .lfield input[type="file"]::file-selector-button:hover { background: var(--fg); color: var(--bg); border-color: var(--fg); } }
.lfield input[type="file"]:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 1px; }
/* with report.js, the hand-in's file input is a ghost button of the page, in its words and at its height: the input
   lies over it, transparent, so a click, a dropped file and the browser's "choose a file" bubble still land on it */
.report-pick { position: relative; align-self: flex-start; max-width: 100%; }
.report-pick-on input[type="file"] { position: absolute; inset: 0; width: 100%; height: 100%; margin: 0; padding: 0;
  border: 0; opacity: 0; cursor: pointer; }
.report-pick-on .report-pick-btn { pointer-events: none; }
/* hover only where there is one: a tapped "Choose files" stayed inverted on a phone, a second primary button */
@media (hover: hover) { .report-pick-on:hover .report-pick-btn { background: var(--fg); color: var(--bg); border-color: var(--fg); } }
.report-pick-on:has(input:focus-visible) .report-pick-btn { outline: 2px solid var(--focus-ring); outline-offset: 2px; }
.report-pick-on:has(input[aria-invalid="true"]) .report-pick-btn { border-color: var(--status-brick); }
/* the certificate, once earned: what it is, and the three ways to use it */
.cert-block { border: 1px solid var(--rule); background: var(--bg-card); padding: 1.25rem 1.4rem; margin: 0 0 2rem; }
.cert-block h2 { font-size: var(--fs-xl); font-weight: 600; margin: 0 0 1.1rem; }   /* the sections' rhythm (.lsection h2) */
.cert-block-name { font-size: var(--fs-2xl); font-weight: 600; letter-spacing: -0.02em; line-height: 1.2; margin: 0 0 0.3rem;
  overflow-wrap: anywhere; }
.cert-block .learn-actions { margin: 1.1rem 0; }
@media (max-width: 37.5rem) {  /* where the three do not fit on one line they stack, at one width, not two and one */
  /* and the course page's card offers the same certificate with the same sizing */
  .cert-block .learn-actions .lbtn, .lnextstep-acts:has(> a[href="/certificate"]) .lbtn { flex: 1 1 100%;
    justify-content: center; }
}
.cert-block .lsmall { margin: 0 0 0.4rem; }
.cert-link { overflow-wrap: anywhere; }
.cert-link .cert-path { white-space: nowrap; }   /* the number whole: the line breaks before its path, if at all */
.cert-date { white-space: nowrap; }   /* "11 November 2026" whole: a phone left the day alone at a line's end */
/* under the LAB-16 sheet: where the report goes */
.sheet-handin { margin-top: 1.5rem; padding-top: 1.25rem; border-top: 1px solid var(--rule); max-width: 46rem; }
.sheet-handin h2 { font-size: var(--fs-xl); font-weight: 600; margin: 0 0 1.1rem; }   /* the sections' rhythm (.lsection h2) */
/* admin: a report and a certificate */
.areport { max-width: 46rem; }
.areport a:not(.lbtn), .adl a { text-decoration-line: underline; }
.areport .lform { max-width: 36rem; }
.areport .report-comment { margin: 0 0 1rem; }
.areport .lfield > .report-comment, .areport .lfield > .llabel { margin: 0; }  /* the validation step's comment, read back */
.areport-files { margin-bottom: 0.75rem; }
.areport-files li { overflow-wrap: anywhere; padding: 0.2rem 0; }
.areport-verdict { margin-top: 1.5rem; }
/* the Reports list: "validated" and "certificate revoked" side by side, the second one starting its own line on a
   phone flush with the first, as a gap does and a margin did not (backoffice.reports_page) */
.areport-state { display: inline-flex; flex-wrap: wrap; align-items: baseline; gap: 0.25rem 0.4rem; }
.areport-state > .tag { margin-left: 0; }
/* a report's certificates, one per line under its verdict (backoffice._story); a reason of several paragraphs set
   apart as a quote, so the next certificate is not read as its last line */
.areport p:has(+ .areport-certs) { margin-bottom: 0.4rem; }
.areport-certs li { padding: 0.15rem 0; }
.areport-round .areport-certs { margin: 0.4rem 0 0; }
.areport-reason { display: block; margin: 0.3rem 0 0.15rem; padding-left: 0.9rem; border-left: 1px solid var(--rule); }
.areport-name { font-size: var(--fs-2xl); font-weight: 600; letter-spacing: -0.02em; overflow-wrap: anywhere; }
.areport-h { font-size: var(--fs-xl); font-weight: 600; margin: 2.5rem 0 0.5rem; }
.areport-round { padding: 0.75rem 0; border-top: 1px solid var(--rule); }
.areport-round > p { margin: 0; }   /* its own lines: a comment's paragraphs keep their gap, as on the verdict above */
.areport-round .report-comment { margin: 0.5rem 0 0; }
/* a comment's label, spaced from the story above it, its comment right under it: the verdict's and every earlier
   version's alike */
.areport .areport-label { margin: 0.75rem 0 0; }
.areport .areport-label + .report-comment { margin-top: 0.3rem; }
.areport-foot { margin-top: 2rem; }
.cert-state { font-size: var(--fs-lede); font-weight: 600; margin: 0.5rem 0 1rem; }
/* a long name on the public page at the heading size, where it breaks at its own spaces and hyphens: no hyphen of
   the browser's, which an employer could not tell from the name's own (views.verify_page) */
.learn-head h1.cert-name-long { font-size: var(--fs-heading); letter-spacing: -0.025em; line-height: 1.15; }
.cert-valid { color: var(--accent); }
.cert-revoked { color: var(--status-brick); }
.cert-replaced { color: var(--fg); }  /* a corrected name: not an alarm, and not the navy of a valid one */
.cert-specimen { color: var(--fg); }  /* the course page's example: neither valid nor withdrawn */
.cert-facts { display: grid; grid-template-columns: max-content 1fr; gap: 0.4rem 1.5rem; margin: 1.5rem 0; font-size: var(--fs-sm); }
.cert-facts dt { color: var(--muted); }
.cert-facts dd { margin: 0; overflow-wrap: anywhere; }
.cert-covers { margin-top: 1.5rem; }
/* the sections in the course's order, down the first column then the second where there is room for two */
.cert-sections { list-style: none; margin: 0; padding: 0; columns: 2 16rem; column-gap: 2rem; font-size: var(--fs-sm); }
.cert-sections li { break-inside: avoid; padding: 0.2rem 0; }
.cert-section-num { color: var(--muted); font-variant-numeric: tabular-nums; margin-right: 0.4rem; }

/* Tournament */
.tn-score { font-size: var(--fs-lede); margin: 0 0 1rem; }
.tn-badge { display: inline-block; margin-left: 0.75rem; font-size: var(--fs-sm); font-weight: 600; color: var(--accent);
            border: 1px solid currentColor; padding: 0.05rem 0.5rem; vertical-align: 0.15em; }
/* a flag's hairline runs as wide as its round's rule; the question and its field keep the reading measure */
.tn-list { list-style: none; padding: 0; margin: 0; }
.tn-flag { padding: 0.9rem 0; border-top: 1px solid var(--rule); }
.tn-flag > * { max-width: 46rem; }
.tn-head { margin: 0; display: flex; gap: 0.6rem; align-items: baseline; flex-wrap: wrap; }
.tn-q { margin: 0.3rem 0 0.6rem; font-size: var(--fs-md); line-height: 1.55; }
.tn-form { display: flex; gap: 0.5rem; flex-wrap: wrap; }
.tn-form input { flex: 1 1 10rem; min-width: 0; font: inherit; font-size: var(--fs-md); padding: 0.65rem 0.75rem;
  border: 1px solid var(--field-edge); border-radius: var(--r-md); background: var(--bg-card); color: var(--fg); }
/* Check beside its field on a phone too (a 16rem basis sent it under the field at 390px), as tall as the field */
.tn-form .lbtn { align-self: stretch; }
.tn-count { font-size: var(--fs-sm); font-weight: 400; letter-spacing: 0; color: var(--muted); margin-left: 0.5rem; }
.tn-flag { scroll-margin-top: 1.5rem; }
.tn-flag > .lsmall:last-child { display: block; margin-top: 0.4rem; }
/* the answer's feedback, under its own flag */
/* the page's notice, set under its flag: a hairline all round, brick for a refusal (The Hairline Rule: no coloured
   side stripe), and the notice's own inner space */
.tn-msg { margin: 0 0 0.6rem; padding: 0.85rem 1.1rem; border: 1px solid var(--rule); background: var(--bg-card);
  font-size: var(--fs-md); line-height: 1.5; }
.tn-msg-error { border-color: var(--status-brick); }
.tn-found { color: var(--fg); }
.tn-found::before { content: "\2713"; margin-right: 0.4rem; font-weight: 600; }
.tn-found:focus { outline: none; }  /* focused by the script for a screen reader to read it, not to be typed in */

/* Exercise sheets */
.sheets { margin-top: 2rem; max-width: 46rem; }
.sheet { border-top: 1px solid var(--rule); padding: 1.25rem 0; }
/* the title is a heading, the marker a chevron drawn here at its end: it turns when the sheet opens, the same in
   every browser, and the title stays aligned with the sheet under it */
.sheet > summary { cursor: pointer; list-style: none; display: grid; grid-template-columns: minmax(0, 1fr) 1rem;
  column-gap: 1rem; row-gap: 0.3rem; }
.sheet > summary::-webkit-details-marker { display: none; }
.sheet > summary::after { content: ""; grid-area: 1 / 2; justify-self: end; align-self: start; margin-top: 0.4rem;
  width: 0.55rem; height: 0.55rem; border-right: 1.5px solid currentColor; border-bottom: 1.5px solid currentColor;
  transform: rotate(45deg); transition: transform 200ms ease; }
.sheet[open] > summary::after { transform: translateY(0.25rem) rotate(-135deg); }
/* the size of the page's other h2 (Files for this lesson, Questions: .qa h2), its sibling sections */
.sheet-title { grid-area: 1 / 1; font-size: var(--fs-xl); font-weight: 600; letter-spacing: -0.015em; line-height: 1.25; margin: 0; }
/* how far the student got, under the title (folded too): '3 of 9 steps ticked' (sheet.js keeps it up to date) */
.sheet-count { grid-area: 2 / 1; font-size: var(--fs-sm); font-weight: 400; color: var(--muted); }
@media (hover: hover) {   /* a tapped title stayed underlined on a phone */
  .sheet > summary:hover .sheet-title { text-decoration: underline; text-decoration-thickness: 1px; text-underline-offset: 4px; } }
.sheet > summary:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 4px; }
@media (prefers-reduced-motion: reduce) { .sheet > summary::after { transition: none; } }
.sheet-actions { display: flex; gap: 0.75rem; flex-wrap: wrap; align-items: center; margin: 1.1rem 0 0; }
.sheet-actions > .lsmall { flex-basis: 100%; margin: 0; }
.sheet-corr { margin: 0; display: flex; gap: 0.75rem; align-items: center; }
/* the correction ends the money-back guarantee while it runs: the tick sits above its button */
.sheet-corr-consent { flex-basis: 100%; flex-direction: column; align-items: flex-start; gap: 0.6rem; margin-top: 0.4rem; }
.sheet-corr-consent .lcheck { align-items: flex-start; max-width: 40rem; line-height: 1.5; color: var(--fg); }
.sheet-corr-consent .lcheck input { margin-top: 0.15rem; }
.sheet-meta { margin: 0.75rem 0 0; }
.is-preparing { cursor: progress; opacity: 0.6; }
.sheet-body { margin-top: 1.5rem; font-size: var(--fs-md); line-height: 1.65; overflow-wrap: anywhere; }
/* the sheet's own headings sit one level under its title (views._shift_headings), so smaller than it: at its size, an
   h3 read as one more section of the page. A hairline over it starts its part of the sheet, which tells it from a
   step's bold lead and from a part under it ('Part A, read it', h4) at the same size */
.sheet-body h3 { font-size: var(--fs-lg); font-weight: 600; line-height: 1.35; margin: 2rem 0 0.75rem; padding-top: 1.25rem;
                 border-top: 1px solid var(--rule); }
.sheet-body h4, .sheet-body h5 { font-size: var(--fs-md); font-weight: 600; margin: 1.25rem 0 0.5rem; }
.sheet-body strong { font-weight: 600; }
.sheet-body ul, .sheet-body ol { padding-left: 1.3rem; }
/* room for a two-digit number (a list that goes on from step 7 to 10): "10." hung past the text's left edge */
.sheet-body ol { padding-left: 1.85rem; }
.sheet-body li { margin: 0.45rem 0; }
/* the box hangs in the step's margin, so a wrapped line, a picture or a code block of the step starts under its
   text, not under the box; it stays in the first line, so a page break never leaves it behind */
.sheet-body li.task { list-style: none; margin-left: -1.3rem; padding-left: 1.75rem; text-indent: -1.75rem; }
.sheet-body li.task > * { text-indent: 0; }
.sheet-body li.task > p:first-child { text-indent: -1.75rem; }
.sheet-body .sheet-step { width: 1.15rem; height: 1.15rem; margin: 0 0.6rem 0 0; vertical-align: -0.2rem; cursor: pointer; }
.sheet-body li.task label { cursor: pointer; }
@media (pointer: coarse) {
  .sheet-body li.task { padding-left: 2.1rem; text-indent: -2.1rem; }
  .sheet-body li.task > p:first-child { text-indent: -2.1rem; }
  /* the finger's bigger box: its negative margins keep it from making the step's first line taller than the next
     ones, and it is lifted by as much, so it stays centred on that line */
  .sheet-body .sheet-step { width: 1.4rem; height: 1.4rem; margin-block: -0.3rem; margin-right: 0.7rem;
    vertical-align: -0.05rem; }
}
/* inline code keeps a short command whole (views._inline_code) and is picked up whole by one click */
.sheet-body code { font-family: var(--font-mono); font-size: var(--fs-sm); background: var(--rule); padding: 0.05rem 0.3rem;
  border-radius: var(--r-md); -webkit-user-select: all; user-select: all; }
.sheet-body .nobr { white-space: nowrap; }
/* a path wider than its line (sheet.js): after a separator, each name kept whole (views._inline_code); any other
   command after its punctuation, and one with none, still wider than its line, anywhere rather than past the edge */
.sheet-body .sheet-path.is-split, .sheet-body .nobr.is-split { white-space: normal; }
.sheet-body .is-long, .sheet-body .is-long .nobr { white-space: normal; overflow-wrap: anywhere; }
/* code blocks are inverted, as on the blog (DESIGN.md, The Inversion Rule), and may run wider than the text, up to
   the page's width: a 96-column template reads whole on a laptop instead of scrolling in a 46rem box */
.sheet-body pre { --focus-ring: var(--bg); background: var(--fg); color: var(--bg); border-radius: var(--r-md);
  padding: 0.9rem 5.5rem 0.9rem 1rem; overflow-x: auto; font-family: var(--font-mono); font-size: var(--fs-sm);
  line-height: 1.55; }
.sheet-body > pre, .sheet-body > .code-block { width: max-content; min-width: 100%;
  max-width: min(calc(var(--max-w) - 2 * var(--pad-x)), calc(100vw - 2 * var(--pad-x) - 1.5rem)); }
.sheet-body pre code { background: none; padding: 0; font-size: inherit; color: inherit; -webkit-user-select: text; user-select: text; }
.sheet-body pre ::selection { background: color-mix(in srgb, var(--accent-inv) 45%, transparent); color: var(--bg); }
/* a block that scrolls takes the focus: its ring is drawn inside, on the block's own dark, where --focus-ring shows
   (outside, the light theme's ring is the colour of the page) */
.sheet-body pre:focus-visible { outline-offset: -4px; }
.code-block { position: relative; margin: 0 0 1rem; }
.code-block > pre { margin: 0; }
/* always shown (there is no hover on a phone), on the dark of the block, and never over its text: beside the first
   line, in the right padding, as wide as its longest word ("Selected") so a change of word does not grow it over the
   line; above the first line, in a strip of its own, when the block scrolls sideways (sheet.js marks it is-wide),
   since the end of a long line is under the right padding until scrolled to */
.sheet-copy { position: absolute; top: 0.45rem; right: 0.45rem; font: inherit; font-size: var(--fs-sm); line-height: 1.2;
  color: #f7f7f5; background: #2d323a; border: 1px solid #5c6470; border-radius: var(--r-md); padding: 0.3rem 0.5rem;
  min-width: 4.6rem; text-align: center; cursor: pointer; }
@media (hover: hover) { .sheet-copy:hover { border-color: #9aa1ac; } }
.sheet-copy:focus-visible { outline: 2px solid #f7f7f5; outline-offset: 2px; }
@media (pointer: coarse) {  /* a finger's 44px, in the corner */
  .sheet-copy { top: 0.25rem; right: 0.25rem; min-height: 2.75rem; min-width: 4.8rem; padding: 0.5rem 0.6rem; }
  .sheet-body pre { padding-right: 5.6rem; }
}
@media screen {
  .sheet-body .code-block.is-wide > pre { padding-top: 2.6rem; padding-right: 1rem; }
}
@media screen and (pointer: coarse) { .sheet-body .code-block.is-wide > pre { padding-top: 3.25rem; } }
/* dark picked with the header button (html.dark) or followed from the system, as the site writes every dark rule;
   on screen only: paper is white whatever the theme */
@media screen {
  html.dark .sheet-body pre { --focus-ring: var(--fg); background: #0b0d10; color: #d9dce1; border: 1px solid var(--rule); }
  html.dark .sheet-body pre ::selection { background: color-mix(in srgb, var(--accent) 22%, transparent); color: var(--fg); }
}
@media screen and (prefers-color-scheme: dark) {
  html:not(.light) .sheet-body pre { --focus-ring: var(--fg); background: #0b0d10; color: #d9dce1; border: 1px solid var(--rule); }
  html:not(.light) .sheet-body pre ::selection { background: color-mix(in srgb, var(--accent) 22%, transparent); color: var(--fg); }
}
.sheet-body img { max-width: 100%; height: auto; border: 1px solid var(--rule); display: block; margin: 0.75rem 0; }
.sheet-body a:has(> img) { cursor: zoom-in; }
.sheet-body details { border: 1px solid var(--rule); padding: 0.6rem 0.9rem; margin: 0.6rem 0; }
.sheet-body summary { cursor: pointer; font-weight: 600; }
/* fingers: a hint's label is a 44px row, as every other summary of learn */
@media (pointer: coarse) {
  .sheet-body details > summary { padding-block: 0.55rem; margin-block: -0.6rem; }
  .sheet-body details[open] > summary { margin-bottom: 0; } }
.sheet-body table { display: block; max-width: 100%; overflow-x: auto; border-collapse: collapse; font-size: var(--fs-sm); }
.sheet-body th, .sheet-body td { border: 1px solid var(--rule); padding: 0.4rem 0.65rem; vertical-align: top; }
.sheet-body th { font-weight: 600; }
.sheet-body th:not([align]) { text-align: left; }
.sheet-body p { margin: 0 0 0.8rem; }
.sheet-body ol { list-style: decimal; }
.sheet-body ul { list-style: disc; }
.sheet-body li > p { margin: 0 0 0.4rem; }
/* printing a lesson prints its sheets (sheet.js opens the folded ones), not the player, the lesson's state and
   links, or the questions */
@media print {
  body:has(.sheets) :is(.nav, .footer, .skip-link, .player, .lafter, .qa-jump, .lab-wait, .lfiles, .qa, .sheet-handin,
    .sheet-actions, .sheet-meta, .sheet-copy) { display: none; }
  body:has(.sheets) { display: block; min-height: 0; }  /* a page-tall body printed a blank last page */
  .sheet > summary::after, .sheet-body details > summary::after { display: none; }   /* paper does not unfold */
  .sheet + .sheet { break-before: page; }
  /* a heading stays with what follows it, a list or a table with the line that introduces it ("Part 2: ..." in
     bold), a hint and a picture stay whole */
  .sheet-title, .sheet-body :is(h3, h4, h5) { break-after: avoid; }
  .sheet-body p + :is(ol, ul, table) { break-before: avoid; }
  .sheet-body :is(details, img, tr) { break-inside: avoid; }
  /* a code block keeps its columns: its font shrinks until its longest line (--cols, counted by sheet.js) fits the
     width its box really has on this paper (100cqi of its wrapper: A4 or Letter, any margins and list depth, less its
     padding and border), between 6.3 and 9 pt; only a line too long even at 6.3 pt wraps */
  .sheet-body .code-block { container-type: inline-size; }
  .sheet-body pre { background: none; color: #101216; border: 1px solid #d6d8d4; white-space: pre-wrap; overflow: visible;
    font-size: 8pt; font-size: clamp(6.3pt, calc((100cqi - 1.5rem - 2px) / (var(--cols, 80) * 0.61)), 9pt);
    padding: 0.5rem 0.75rem; }
  .sheet-body > pre, .sheet-body > .code-block { width: auto; max-width: 100%; }
}

/* Files of a lesson */
.lfiles { margin-top: 2rem; max-width: 46rem; }
/* what to do while the lab builder is not out (views.lab_wait), above the files and sheets it speaks about */
.lab-wait { margin: 2rem 0 0; }
.lab-wait + .lfiles, .lab-wait + .sheets { margin-top: 1.5rem; }
.lfiles h2 { font-size: var(--fs-xl); font-weight: 600; letter-spacing: -0.018em; margin: 0 0 0.6rem; }
.lfiles li { display: flex; gap: 0.35rem 0.75rem; align-items: baseline; flex-wrap: wrap; padding: 0.6rem 0; }
.lfiles li + li { border-top: 1px solid var(--rule); }
.lfiles li > a { min-width: 0; overflow-wrap: anywhere; text-decoration-line: underline; }
.lfiles li > p, .lfiles li > .prep-msg { flex-basis: 100%; margin: 0; }
.lfile-wait { color: var(--muted); }
/* a digest is one 64-letter word: it breaks anywhere rather than widen a phone, and one click selects it */
.lhash { font-family: var(--font-mono); font-size: var(--fs-sm); word-break: break-all; -webkit-user-select: all; user-select: all; }
.ldanger { margin-top: 1.5rem; }
.ldanger summary { cursor: pointer; color: var(--status-brick); font-size: var(--fs-sm); width: fit-content; }
@media (pointer: coarse) { .ldanger > summary { padding-block: 0.75rem; line-height: 1.5; } }
.ldanger[open] > summary { margin-bottom: 1rem; }
.ldanger > .lsmall { max-width: 40rem; }
.ldanger > .lsmall strong { color: var(--fg); font-weight: 600; }
.player-screen { cursor: pointer; }
