/* Aim wiki theme.
 *
 * The look is a shooting range HUD: dark chrome across the top and bottom, a technical display
 * face for anything that labels rather than reads, reticle brackets on the cards, and a faint
 * range grid behind the page.
 *
 * Three parts, in order: the colour and type layer, which overrides the theme's own custom
 * properties; the chrome, which restyles the header, tabs, footer and typeset defaults; and the
 * page components. Each component is documented above its rules with the Markdown that produces
 * it: .aim-hero, .aim-cards, .aim-steps, .aim-category, .aim-myth, .aim-key,
 * .aim-theme-picker, .aim-links, .aim-figure.
 *
 * Two components have no markup in the built HTML at all, because JavaScript builds them after the
 * page loads: .aim-theme-picker and its children come from aim-theme.js, and .aim-hotspot,
 * .aim-hotspot-layer, .aim-hotspot-frame, .aim-hotspot-says and .aim-hotspot-kind come from
 * aim-hotspots.js. Any tool that looks for unused CSS by reading the built pages will call all of
 * them dead. They are not. Check the scripts before deleting a rule that nothing seems to use.
 *
 * .aim-links carries a set of per-type modifiers, --article, --video, --playlist, --doc, --sheet,
 * --post and --image, for the case where a link's address says the wrong thing about it. Only
 * --image is used today, because no address pattern means "image" while a YouTube address really
 * does mean a video. The rest are the other half of a documented escape hatch, not leftovers.
 */

@import url("https://fonts.googleapis.com/css2?family=Chakra+Petch:wght@500;600;700&display=swap");

/* ---------------------------------------------------------------------------
 * Colour and type
 * ------------------------------------------------------------------------ */

:root {
  /* Chakra Petch labels a control rather than sets prose. It is used for headings, the wordmark,
   * table headers, buttons and badges, and never for running text. */
  --aim-display: "Chakra Petch", "Inter", -apple-system, system-ui, sans-serif;

  /* Night is the dark scheme's chrome colour and the ink on solid-colour tags. It never changes
   * with the scheme, so anything that must stay dark reads from it. */
  --aim-night: #0b0d12;
  --aim-night-fg: #e8eaf0;
  --aim-night-accent: #e38268;

  /* The chrome is the header, tabs and footer. In the dark scheme it is night; the light scheme gives
   * it a warm paper tone instead, set with the light scheme's tokens below, so a light page is not
   * framed by two black bars. */
  --aim-chrome: var(--aim-night);
  --aim-chrome-fg: var(--aim-night-fg);
  --aim-chrome-fg-light: #ffffffb3;
  --aim-chrome-fg-lighter: #ffffff73;
  --aim-chrome-accent: var(--aim-night-accent);
  --aim-chrome-line: #ffffff1f;
  /* The landing hero is a panel of chrome, so it needs button colours that hold up on the chrome in
   * either scheme. */
  --aim-chrome-on-accent: var(--aim-night);
  --aim-chrome-hover: #fff;
  --aim-chrome-on-hover: var(--aim-night);

  --aim-ink: #14171c;
  --aim-accent: #9c3522;
  /* Section headings take the accent pulled back off the link colour, so a heading reads as a
   * heading rather than as something to click. */
  --aim-heading: #7a291b;
  /* The wash behind a myth block. Light enough to read body text on, strong enough that the block
   * is visibly not the sourcing note. */
  /* Myth and NOTE blocks keep fixed colours whatever colour a reader picks: red means wrong and
   * yellow means remember on every page, so aim-theme.js never writes these. */
  --aim-myth: #85291a;
  --aim-myth-wash: #9c35220f;
  /* Notes are yellow so red keeps meaning "wrong". Yellow bright enough to read as yellow is too
   * light to set text in on white, so the light scheme splits it: the bright yellow fills the bar and
   * tag, and a dark amber sets the title. The tag always carries dark text, since light text on
   * either yellow is unreadable. */
  --aim-key: #e0a800;
  --aim-key-ink: #7d5e00;
  --aim-key-wash: #e0a8001f;
  --aim-key-tag-ink: var(--aim-night);
  --aim-tag-ink: var(--aim-night-fg);
  /* Tension in the figures reads as a fixed three-colour scale, like the myth and NOTE blocks
   * above: red is too tense, blue is too loose, green is balanced. aim-theme.js never writes these,
   * so the meaning holds whatever colour a reader picks. */
  --aim-tense: #9c3522;
  /* The same hues as text rather than as fills. A bar is a graphic and WCAG holds it to 3:1; a label
   * is text and is held to 7:1, which the fills above do not reach on a panel. */
  --aim-tense-ink: #8f2f1d;
  --aim-loose-ink: #145067;
  --aim-balanced-ink: #225a31;
  --aim-accent-ink: #8f2f1d;
  --aim-loose: #1d6a8c;
  --aim-balanced: #2f7a43;
  /* The hand in the blend figure. It lies over the mouse, so it is a solid tone rather than a tint
   * of the ink: anything see-through there leaves the two shapes reading as one. */
  --aim-hand: #cdc8c1;
  --aim-hand-edge: #6d675f;
  --aim-grid: #14171c08;
  --aim-clicking: #9c3522;
  --aim-tracking: #145067;
  --aim-switching: #6a4396;
}

/* The theme sets its link colour per scheme and primary colour together, so these blocks have to
 * carry both attributes to override it rather than lose on specificity. */
[data-md-color-scheme="default"][data-md-color-primary] {
  /* The theme's muted ink is black at 54%, which is 4.6:1 on white. Everything a reader reads is
   * held to AAA here, so it is set as a solid grey instead; scripts/check_contrast.py checks it. */
  --md-default-fg-color--light: #4a4f57;
  --aim-chrome: #e8e4df;
  --aim-chrome-fg: var(--aim-ink);
  --aim-chrome-fg-light: #14171cd0;
  /* 64% rather than the dark scheme's 45%: dark ink on paper loses contrast faster than light ink
   * on black, and the lighter tone sets small footer labels that still have to clear 4.5:1. */
  --aim-chrome-fg-lighter: #14171ca3;
  --aim-chrome-accent: var(--aim-accent);
  --aim-chrome-line: #14171c17;
  /* The page you are on sits on the header's stone in dark text, tying the sidebar to the chrome. */
  --aim-nav-active-bg: var(--aim-chrome);
  --aim-nav-active-fg: var(--aim-ink);
  --aim-chrome-on-accent: #fff;
  --aim-chrome-hover: var(--aim-ink);
  --aim-chrome-on-hover: #fff;

  --md-primary-fg-color: var(--aim-ink);
  --md-primary-fg-color--light: #2a2f39;
  --md-primary-fg-color--dark: #0b0d11;
  --md-accent-fg-color: var(--aim-accent);
  /* The theme tints the active navigation item and code-in-link hovers with this, and it keeps its
   * own indigo unless it is set alongside the accent it belongs to. */
  --md-accent-fg-color--transparent: #9c352214;
  --md-typeset-a-color: var(--aim-accent);
}

[data-md-color-scheme="slate"][data-md-color-primary] {
  --aim-accent: #e89179;
  --aim-heading: #f0a590;
  --aim-myth: #e89179;
  --aim-myth-wash: #e3826814;
  --aim-key: #f2c14e;
  --aim-key-ink: #f2c14e;
  --aim-key-wash: #f2c14e14;
  --aim-tag-ink: var(--aim-night);
  --aim-tense: #e89179;
  /* Dark fills already carry text at this size, so the inks are the fills. */
  --aim-tense-ink: #e89179;
  --aim-loose-ink: #56b6d8;
  --aim-balanced-ink: #6cc48a;
  --aim-accent-ink: #e89179;
  --aim-loose: #56b6d8;
  --aim-balanced: #6cc48a;
  --aim-hand: #444b58;
  --aim-hand-edge: #aab3c1;
  --aim-grid: #ffffff08;
  --aim-clicking: #e89179;
  --aim-tracking: #56b6d8;
  --aim-switching: #b18cf0;

  /* The theme's dark ink is 82% opaque, which lands at #bcbec3 on this background: 10:1, against
   * 16:1 for the light scheme's near-black on white, so dark pages read dimmer for no reason. These
   * are brighter, but deliberately short of the light scheme's contrast: light text at the top of
   * the range blooms into a dark background, which is worse to read than it is to measure. */
  --md-default-fg-color: #d5d9e2;
  --md-default-fg-color--light: #a2aaba;
  --md-default-fg-color--lighter: #79808f;
  --md-default-fg-color--lightest: #333a47;

  --md-default-bg-color: #0f1115;
  --md-primary-fg-color: #0b0d11;
  --md-primary-fg-color--light: #2a2f39;
  --md-primary-fg-color--dark: #07080b;
  --md-accent-fg-color: var(--aim-accent);
  --md-accent-fg-color--transparent: #e382681f;
  --aim-nav-active-bg: var(--md-accent-fg-color--transparent);
  --aim-nav-active-fg: var(--aim-accent);
  --md-typeset-a-color: var(--aim-accent);
  --md-code-bg-color: #171a21;
}

::selection {
  background-color: var(--aim-accent);
  color: #fff;
}

/* The range grid. It has to stay at the edge of visible: a hint that the page sits on a range,
 * not a texture anyone should notice while reading. */
body {
  background-image:
    linear-gradient(var(--aim-grid) 1px, transparent 1px),
    linear-gradient(90deg, var(--aim-grid) 1px, transparent 1px);
  background-size: 2rem 2rem;
  background-position: center;
}

/* ---------------------------------------------------------------------------
 * Chrome: header, tabs, footer
 * ------------------------------------------------------------------------ */

.md-header {
  color: var(--aim-chrome-fg);
  background-color: var(--aim-chrome);
  border-bottom: 1px solid var(--aim-chrome-line);
}

.md-header__button.md-logo :is(img, svg) {
  color: var(--aim-chrome-accent);
}

/* The first topic in the header title is the site name; the second is the current page. */
.md-header__topic:first-child .md-ellipsis {
  font-family: var(--aim-display);
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
}

.md-header .md-search__button,
.md-header .md-source {
  color: var(--aim-chrome-fg);
  border-color: var(--aim-chrome-line);
}

.md-tabs {
  color: var(--aim-chrome-fg);
  background-color: var(--aim-chrome);
  box-shadow: none;
  font-family: var(--aim-display);
  font-weight: 500;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

.md-tabs__link {
  font-size: 0.66rem;
  opacity: 0.72;
}

.md-tabs__link:is(:hover, :focus),
.md-tabs__item--active .md-tabs__link {
  opacity: 1;
}

/* The active tab is marked by one accent line under its label. The theme also draws its own border
 * under the whole tab item; where the item is taller than the label, as on narrow screens, that
 * showed as a second line below the first, so it is removed. */
.md-tabs__item--active {
  border-bottom: 0;
}

.md-tabs__item--active .md-tabs__link {
  box-shadow: 0 -0.12rem 0 inset var(--aim-chrome-accent);
}

/* The footer is chrome, so its foreground follows the chrome tokens in both schemes. The meta strip
 * takes its colours from the default tokens rather than the footer ones, so both sets are redefined
 * here. */
.md-footer,
.md-footer-meta {
  --md-footer-bg-color: var(--aim-chrome);
  --md-footer-bg-color--dark: var(--aim-chrome);
  --md-footer-fg-color: var(--aim-chrome-fg);
  --md-footer-fg-color--light: var(--aim-chrome-fg-light);
  --md-footer-fg-color--lighter: var(--aim-chrome-fg-lighter);

  --md-default-fg-color: var(--aim-chrome-fg);
  --md-default-fg-color--light: var(--aim-chrome-fg-light);
  --md-default-fg-color--lighter: var(--aim-chrome-fg-lighter);
  --md-default-fg-color--lightest: var(--aim-chrome);

  color: var(--aim-chrome-fg);
  background-color: var(--aim-chrome);
}

/* Tags name a topic, so they are labels like the skill badges, not prose. */
.md-typeset .md-tag {
  border: 1px solid var(--md-default-fg-color--lighter);
  border-radius: 0;
  background-color: transparent;
  font-family: var(--aim-display);
  font-size: 0.6rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

/* Both sidebars are lists of labels, so they take the display face: the section headings in the
 * navigation, and "On this page" over the table of contents. */
.md-nav__title {
  font-family: var(--aim-display);
  font-size: 0.62rem;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}

.md-nav__link--active,
.md-nav__item--active > .md-nav__link {
  font-weight: 600;
}

/* Weight in the sidebar says how deep an entry sits, not whether it has children: every top-level
 * entry of a tab is bold, whether it is a single page like Myths or a section like Fundamentals, and
 * everything inside a section is regular. Sections open and close with their arrow instead of
 * being held open, so having children is shown by the arrow, not by weight. The page you are on
 * stays semibold, on top of its tint and tick. */
.md-nav--primary > .md-nav__list > .md-nav__item > .md-nav > .md-nav__list > .md-nav__item > .md-nav__link,
.md-nav--primary > .md-nav__list > .md-nav__item > .md-nav > .md-nav__list > .md-nav__item > .md-nav__container > .md-nav__link {
  font-weight: 700;
}

.md-nav--primary .md-nav__item .md-nav__item .md-nav__item .md-nav__link:not(.md-nav__link--active) {
  font-weight: 400;
}

/* Entries inside a section sit visibly inside it. The theme's 0.6rem step is smaller than a link's
 * own padding, so a page and its section read as the same level. Only the desktop sidebar changes:
 * the mobile drawer shows each level as its own panel, where an indent has nothing to line up
 * against. The top-level list keeps no indent, since three lists deep is the first list inside a
 * section. */
@media screen and (min-width: 76.25em) {
  [dir="ltr"] .md-sidebar--primary .md-nav--primary .md-nav__list .md-nav__list .md-nav__list {
    margin-left: 1.2rem;
  }

  [dir="rtl"] .md-sidebar--primary .md-nav--primary .md-nav__list .md-nav__list .md-nav__list {
    margin-right: 1.2rem;
  }
}

/* The page you are on gets one plain highlight, set per scheme: stone with dark text in the light
 * scheme, an accent tint in the dark one. */
.md-sidebar--primary .md-nav--primary .md-nav__item .md-nav__link--active {
  color: var(--aim-nav-active-fg);
  background-color: var(--aim-nav-active-bg);
}

/* A section's row is one control: its page link and its open/close arrow share a container. The
 * theme styles the two halves separately, so an active or hovered section showed two rounded pills
 * side by side. The container carries the pill instead, and its halves stay transparent. Keyboard focus lights the whole row too, so focus is still visible. The
 * selectors are long to outrank the theme's hover rule, which is five classes deep. */
.md-sidebar--primary .md-nav--primary .md-nav__container {
  gap: 0;
}

.md-sidebar--primary .md-nav--primary .md-nav__item > .md-nav__container > .md-nav__link.md-nav__link {
  margin-inline: 0;
  background-color: transparent;
  box-shadow: none;
}

.md-sidebar--primary .md-nav--primary .md-nav__container:is(:hover, :has(> :focus-visible)) {
  background-color: var(--md-default-fg-color--lightest);
}

.md-sidebar--primary .md-nav--primary .md-nav__container:has(> .md-nav__link--active) {
  background-color: var(--aim-nav-active-bg);
}

/* ---------------------------------------------------------------------------
 * Typeset defaults
 * ------------------------------------------------------------------------ */

.md-typeset :is(h1, h2, h3, h4) {
  font-family: var(--aim-display);
  font-weight: 700;
}

/* The page title reads as the name of a range, so it is set as a label: uppercase, tracked, and
 * closed off with a rule that runs the measure. */
.md-typeset h1 {
  margin-bottom: 1.6rem;
  padding-bottom: 0.6rem;
  border-bottom: 1px solid var(--md-default-fg-color--lightest);
  color: var(--md-default-fg-color);
  font-size: 1.6rem;
  letter-spacing: 0.02em;
  text-transform: uppercase;
}

.md-typeset h1::after {
  content: "";
  display: block;
  width: 2.4rem;
  height: 2px;
  margin-top: 0.6rem;
  margin-bottom: -0.7rem;
  background-color: var(--aim-accent);
}

.md-typeset h2 {
  color: var(--aim-heading);
  letter-spacing: 0.01em;
}

/* Numbers are read against each other all over this wiki: sensitivities, cm/360, refresh rates,
 * benchmark scores. Tabular figures keep them aligned down a column. */
/* The theme makes a table an inline-block, so it shrinks to its content and a narrow table leaves
 * the rest of the line empty. A table here is a layout of the whole measure, like a figure.
 *
 * Three rules, because the theme's script wraps every table at runtime: the page ships a bare
 * <table>, and the script puts it inside .md-typeset__scrollwrap > .md-typeset__table, which is
 * itself an inline-block. Styling only the table leaves it sized by that wrapper, so the wrapper
 * has to be widened too. Both states are covered, so the width does not depend on the script. */
.md-typeset table:not([class]) {
  display: table;
  width: 100%;
}

/* The theme pulls the scroll wrapper out past the text column and pads the table back in, so a
 * table that overflows bleeds to the screen edge. That inset leaves every table misaligned with
 * the prose above it, so it is cancelled: a table still scrolls when it has to, it just starts and
 * ends on the same line as the paragraphs around it. */
.md-typeset__scrollwrap {
  width: 100%;
  margin-inline: 0;
}

.md-typeset__table {
  display: block;
  width: 100%;
  padding: 0;
}

.md-typeset table:not([class]) td {
  font-variant-numeric: tabular-nums;
}

.md-typeset table:not([class]) th {
  border-bottom: 2px solid var(--aim-accent);
  background-color: transparent;
  font-family: var(--aim-display);
  font-size: 0.68rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

.md-typeset .md-button {
  font-family: var(--aim-display);
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

/* The sourcing note sits at the top of nearly every page, so it has to be readable without
 * taking the page over. A bar and a label do that; a filled block does not. */
.md-typeset .admonition[class],
.md-typeset details[class] {
  border-width: 0 0 0 3px;
  border-radius: 0;
  box-shadow: none;
  background-color: var(--md-code-bg-color);
}

.md-typeset :is(.admonition-title, summary)[class] {
  font-family: var(--aim-display);
  font-size: 0.68rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  background-color: transparent;
}

/* ---------------------------------------------------------------------------
 * .aim-hero — landing page opener.
 *
 *   <div class="aim-hero" markdown>
 *   ## Heading
 *
 *   Lead paragraph.
 *
 *   [Open the wiki](wiki/index.md){ .md-button .md-button--primary }
 *
 *   <!-- aim:stats -->
 *
 *   </div>
 *
 * The hero is a HUD rail: corners cut like Chakra Petch's, an accent rail with a slanted tab along
 * the top, and measuring ticks at the right. The stats marker is optional;
 * see .aim-stats below. A page with a hero has the theme's generated page title hidden: the hero
 * is the title.
 * ------------------------------------------------------------------------ */

/* The landing page drops the theme's edit button too: it floats beside the hero, and the hero's
 * overflow clipping makes the hero shrink to clear it instead of running full width. */
.md-content__inner:has(> .aim-hero) > :is(h1, .md-content__button) {
  display: none;
}

.md-typeset .aim-hero {
  position: relative;
  overflow: hidden;
  margin: 0 0 2.5rem;
  padding: 2.4rem 2.2rem 2.6rem;
  color: var(--aim-chrome-fg);
  background-color: var(--aim-chrome);
  border-top: 3px solid var(--aim-chrome-accent);
  /* Cuts the top right and bottom left corners at 45°, the way Chakra Petch cuts its letters. */
  clip-path: polygon(
    0 0,
    calc(100% - 1.8rem) 0,
    100% 1.8rem,
    100% 100%,
    1.8rem 100%,
    0 calc(100% - 1.8rem)
  );
  /* Lets .aim-stats switch layout by the hero's own width. */
  container-type: inline-size;
}

/* The slanted tab on the top rail, leaning the same way as the display face's italics. */
.md-typeset .aim-hero::before {
  content: "";
  position: absolute;
  top: 0;
  left: 2.2rem;
  width: 9rem;
  height: 0.55rem;
  pointer-events: none;
  background-color: var(--aim-chrome-accent);
  transform: skewX(-35deg);
  transform-origin: top left;
}

/* Measuring ticks along the top and bottom right, every third tick long, and two accent bars down the
 * right edge. All of it sits in the hero's padding, fading out towards the text, so it never runs
 * behind the words. */
.md-typeset .aim-hero::after {
  --aim-tick: color-mix(in srgb, var(--aim-chrome-fg) 28%, transparent);
  --aim-tick-long: color-mix(in srgb, var(--aim-chrome-fg) 45%, transparent);
  --aim-bar: color-mix(in srgb, var(--aim-chrome-accent) 70%, transparent);
  content: "";
  position: absolute;
  top: 1.2rem;
  right: 1.2rem;
  bottom: 1.2rem;
  width: 40%;
  pointer-events: none;
  background:
    repeating-linear-gradient(90deg, var(--aim-tick) 0 1px, transparent 1px 0.6rem) top / 100% 0.45rem no-repeat,
    repeating-linear-gradient(90deg, var(--aim-tick-long) 0 2px, transparent 2px 1.8rem) top / 100% 0.9rem no-repeat,
    repeating-linear-gradient(90deg, var(--aim-tick) 0 1px, transparent 1px 0.6rem) bottom / 100% 0.45rem no-repeat,
    linear-gradient(var(--aim-bar), var(--aim-bar)) right 0 top 1.6rem / 2px 30% no-repeat,
    linear-gradient(var(--aim-bar), var(--aim-bar)) right 0.5rem top 1.6rem / 2px 12% no-repeat;
  mask: linear-gradient(90deg, transparent, #000 40%);
  -webkit-mask: linear-gradient(90deg, transparent, #000 40%);
}

.md-typeset .aim-hero > * {
  position: relative;
  max-width: 32rem;
}

.md-typeset .aim-hero > :first-child {
  margin-top: 0;
}

.md-typeset .aim-hero > :last-child {
  margin-bottom: 0;
}

.md-typeset .aim-hero h2 {
  margin-bottom: 0.8rem;
  color: inherit;
  font-size: 1.9rem;
  line-height: 1.15;
  letter-spacing: 0.01em;
  text-transform: uppercase;
}

.md-typeset .aim-hero .md-button--primary {
  color: var(--aim-chrome-on-accent);
  background-color: var(--aim-chrome-accent);
  border-color: var(--aim-chrome-accent);
}

.md-typeset .aim-hero .md-button--primary:is(:hover, :focus) {
  color: var(--aim-chrome-on-hover);
  background-color: var(--aim-chrome-hover);
  border-color: var(--aim-chrome-hover);
}

/* ---------------------------------------------------------------------------
 * .aim-stats — a row of site counts under a rule, each linking to the page it counts, written into
 * the hero by the aim_stats extension in place of the marker line:
 *
 *   <!-- aim:stats -->
 *
 * The extension emits a list, one item per count:
 *
 *   <li><a href="wiki/resources/guides/"><span class="aim-stats__count">46</span>
 *   <span class="aim-stats__label">Guides</span></a></li>
 *
 * so a screen reader reads each link as "46 Guides". docs/assets/javascripts/aim-stats.js counts
 * the numbers up from 0 when the row scrolls into view; digits are equal width so the count does
 * not shift the row.
 * ------------------------------------------------------------------------ */

.md-typeset .aim-hero .aim-stats {
  display: flex;
  flex-wrap: wrap;
  gap: 1rem 2.2rem;
  max-width: none;
  margin: 1.6rem 0 0;
  padding: 1.2rem 0 0;
  border-top: 1px solid var(--aim-chrome-line);
  list-style: none;
}

/* The theme indents list items under a direction attribute selector, so this needs the extra class. */
.md-typeset .aim-hero .aim-stats > li {
  margin: 0;
}

.md-typeset .aim-stats a {
  display: flex;
  flex-direction: column;
  line-height: 1.15;
  color: inherit;
  text-decoration: none;
}

.md-typeset .aim-stats a:focus-visible {
  outline: 2px solid var(--aim-chrome-accent);
  outline-offset: 0.3rem;
}

.md-typeset .aim-stats__count {
  color: var(--aim-chrome-accent);
  font-family: var(--aim-display);
  font-size: 1.7rem;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}

.md-typeset .aim-stats__label {
  color: var(--aim-chrome-fg-light);
  font-family: var(--aim-display);
  font-size: 0.6rem;
  font-weight: 500;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  /* The theme lets typeset text break inside a word; a label breaks only between its words. */
  word-break: normal;
  overflow-wrap: normal;
}

.md-typeset .aim-stats a:is(:hover, :focus-visible) .aim-stats__label {
  color: var(--aim-chrome-fg);
  text-decoration: underline;
  text-decoration-color: var(--aim-chrome-accent);
  text-decoration-thickness: 2px;
  text-underline-offset: 0.3em;
}

/* In a hero too narrow for one row, such as on a phone, the counts stay side by side in three
 * columns rather than wrapping one per line. A two-word label may break between its words, never
 * inside one, since each column is at least as wide as its longest word. */
@container (max-width: 24rem) {
  .md-typeset .aim-hero .aim-stats {
    display: grid;
    grid-template-columns: repeat(3, auto);
    justify-content: start;
    gap: 1rem 1.4rem;
  }

  .md-typeset .aim-stats__label {
    letter-spacing: 0.08em;
  }
}

/* ---------------------------------------------------------------------------
 * .aim-cards — a list of links rendered as a card grid, each card bracketed like a reticle.
 *
 *   <div class="aim-cards" markdown>
 *
 *   - **[Categories](wiki/categories/index.md)**: what the section covers.
 *   - **[Training](wiki/training/index.md)**: what the section covers.
 *
 *   </div>
 * ------------------------------------------------------------------------ */

.md-typeset .aim-cards > ul {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
  gap: 0.8rem;
  margin: 1em 0;
  padding: 0;
  list-style: none;
}

.md-typeset .aim-cards > ul > li {
  position: relative;
  margin: 0;
  padding: 1.1rem;
  border: 1px solid var(--md-default-fg-color--lighter);
  transition: border-color 125ms, background-color 125ms;
}

.md-typeset .aim-cards > ul > li:hover {
  border-color: var(--md-default-fg-color--light);
  background-color: var(--md-code-bg-color);
}

/* Two opposite corner brackets. With the box border they read as a reticle framing the card, and
 * they come up to full strength on hover. */
.md-typeset .aim-cards > ul > li::before,
.md-typeset .aim-cards > ul > li::after {
  content: "";
  position: absolute;
  width: 0.7rem;
  height: 0.7rem;
  border: 0 solid var(--aim-accent);
  opacity: 0.55;
  transition: opacity 125ms;
}

.md-typeset .aim-cards > ul > li::before {
  top: -1px;
  left: -1px;
  border-width: 2px 0 0 2px;
}

.md-typeset .aim-cards > ul > li::after {
  right: -1px;
  bottom: -1px;
  border-width: 0 2px 2px 0;
}

.md-typeset .aim-cards > ul > li:hover::before,
.md-typeset .aim-cards > ul > li:hover::after {
  opacity: 1;
}

/* The whole card is the link's hit area: the card's own title link is stretched over it, so a
 * click anywhere follows it. The anchor is still the only focusable thing, so tab order and the
 * status bar target are unchanged; the ring is drawn on the card it now covers. */
.md-typeset .aim-cards > ul > li {
  cursor: pointer;
}

.md-typeset .aim-cards > ul > li > strong:first-child a::after {
  content: "";
  position: absolute;
  z-index: 1;
  inset: 0;
}

.md-typeset .aim-cards > ul > li:has(a:focus-visible) {
  outline: 2px solid var(--aim-accent);
  outline-offset: 2px;
}

.md-typeset .aim-cards > ul > li > :first-child {
  margin-top: 0;
}

.md-typeset .aim-cards > ul > li > :last-child {
  margin-bottom: 0;
}

/* ---------------------------------------------------------------------------
 * .aim-steps — an ordered list read as a route through the wiki.
 *
 *   <div class="aim-steps" markdown>
 *
 *   1. **Set up your gear.** Why it comes first.
 *
 *   </div>
 * ------------------------------------------------------------------------ */

.md-typeset .aim-steps > ol {
  margin-left: 0;
  padding-left: 0;
  list-style: none;
  counter-reset: aim-step;
}

.md-typeset .aim-steps > ol > li {
  position: relative;
  margin-bottom: 1.1em;
  padding-left: 2.6rem;
  counter-increment: aim-step;
}

.md-typeset .aim-steps > ol > li::before {
  content: counter(aim-step, decimal-leading-zero);
  position: absolute;
  top: 0.05em;
  left: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 1.8rem;
  height: 1.8rem;
  border: 2px solid var(--aim-accent);
  color: var(--aim-accent);
  font-family: var(--aim-display);
  font-size: 0.62rem;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.04em;
}

/* A route whose steps are each one line wants its numbers centred on that line instead. A badge is
 * taller than a line box, so the top alignment above, which is right when a step runs to a
 * paragraph, leaves a one-line step looking hung from its number:
 *
 *   <div class="aim-steps aim-steps--tight" markdown>
 */
.md-typeset .aim-steps--tight > ol > li::before {
  top: calc(0.8em - 0.9rem);
}

.md-typeset .aim-steps--tight > ol > li:not(:last-child)::after {
  top: calc(0.8em + 1.1rem);
}

/* The line joining one step to the next, so the list reads as a route rather than as six
 * unrelated items. */
.md-typeset .aim-steps > ol > li:not(:last-child)::after {
  content: "";
  position: absolute;
  top: 2.1rem;
  left: calc(0.9rem - 1px);
  bottom: -1.1em;
  width: 2px;
  background-color: var(--md-default-fg-color--lightest);
}

/* ---------------------------------------------------------------------------
 * .aim-category — an inline badge naming one of the three skills.
 *
 *   [Clicking](clicking.md){ .aim-category .aim-category--clicking }
 * ------------------------------------------------------------------------ */

.md-typeset .aim-category {
  display: inline-block;
  padding: 0.15em 0.55em;
  border: 1px solid currentcolor;
  font-family: var(--aim-display);
  font-size: 0.68em;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  text-decoration: none;
  white-space: nowrap;
  transition: background-color 125ms;
}

.md-typeset a.aim-category:hover {
  background-color: color-mix(in srgb, currentcolor 14%, transparent);
}

.md-typeset .aim-category--clicking { color: var(--aim-clicking); }
.md-typeset .aim-category--tracking { color: var(--aim-tracking); }
.md-typeset .aim-category--switching { color: var(--aim-switching); }

/* ---------------------------------------------------------------------------
 * Narrow tables — below 38em a table stops being a grid. Each row becomes its
 * own block, and each cell a label and value pair, so a three-column table
 * does not squeeze down to one word per line.
 *
 * The label is the text of that column's heading, copied onto every cell under
 * it as data-label by assets/javascripts/aim-tables.js, because CSS cannot
 * read another element's text. Without that script a reader still gets every
 * value in row order, only unlabelled.
 * ------------------------------------------------------------------------ */

@media screen and (max-width: 38em) {
  .md-typeset table:not([class]) {
    display: block;
    overflow: visible;
    border: 0;
    background-color: transparent;
  }

  /* The heading row is dropped: a heading sitting once at the top no longer lines up with cells
   * repeated under every row, and each cell now carries its own label. */
  .md-typeset table:not([class]) thead {
    display: none;
  }

  .md-typeset table:not([class]) tr {
    display: block;
    margin-bottom: 0.5rem;
    padding: 0.1rem 0.6rem;
    border: 1px solid var(--md-typeset-table-color);
    border-radius: 0.1rem;
    background-color: var(--md-default-bg-color);
  }

  /* Label over value rather than beside it. A label column wide enough for a real heading leaves
   * the value squeezed into what is left, which is the squeeze this whole block exists to avoid. */
  .md-typeset table:not([class]) td {
    display: block;
    padding: 0.4rem 0;
    border: 0;
  }

  .md-typeset table:not([class]) td:not(:last-child) {
    border-bottom: 1px solid var(--md-typeset-table-color);
  }

  /* The heading, printed per cell. It reads as a label rather than as content, so it takes the
   * same display face and letter-spacing the real table header has. */
  .md-typeset table:not([class]) td[data-label]::before {
    content: attr(data-label);
    display: block;
    margin-bottom: 0.1rem;
    color: var(--md-default-fg-color--light);
    font-family: var(--aim-display);
    font-size: 0.6rem;
    letter-spacing: 0.08em;
    line-height: 1.4;
    text-transform: uppercase;
  }

}

/* .aim-myth and .aim-key are admonition types, not classes you write. They share one shape, a
 * coloured rule, a wash, and a tag naming the block, and differ only in colour and tag word:
 * red for something wrong, yellow for something to remember. Red must keep meaning "wrong", so a
 * note never borrows it. Both colours are fixed: the colour picker re-colours the accent, but a
 * myth stays red and a note stays yellow under every picked colour.
 *
 * .aim-myth marks a claim that is repeated as fact and is wrong, on the page whose subject it gets
 * wrong, and sends the reader to the full answer on wiki/myths.md.
 *
 *   !!! myth "Changing your sensitivity will ruin your aim"
 *       A change costs short-term readjustment, not a lasting setback.
 *       [Evidence](../myths.md#changing-your-sensitivity-will-ruin-your-aim){ .aim-myth-more }
 *
 * Two rules. The title is the myth stated as the claim, word for word as its heading on the hub
 * page, because that heading is the anchor. The body is at most two sentences plus the hub link,
 * and carries no footnote: the citation lives on the hub, so a myth is only ever sourced in one
 * place and cannot go stale in two. The link takes .aim-myth-more and reads "Evidence", with no
 * full stop. scripts/check_pages.py enforces the title, the link text and the anchor. The hub
 * itself uses the block differently; see .aim-myth-title below.
 *
 * .aim-key marks the one point on a page a reader should leave with if they read nothing else.
 * Its tag reads NOTE, but the Markdown type stays key: note is the theme's built-in admonition, so
 * styling .note would turn every ordinary note on the site into this block.
 *
 *   !!! key "Keep the trainer to a slice of your practice"
 *       A daily playlist of 30 to 40 minutes is enough; past that, the game is better practice.[^x]
 *
 * Three rules. At most one per page, which scripts/check_pages.py enforces: a page where several
 * points are "key" has none. The title is the point stated as an instruction. The body is at most
 * two sentences and cites its source with a footnote, because unlike a myth there is no hub page
 * to carry the citation for it.
 *
 * Both are allowed to raise their voice where other admonitions are not. The chrome section above
 * keeps admonitions quiet, because the sourcing note sits on nearly every page and must not take it over.
 * Each sets its own border-style because the chrome block sets a width without one.
 *
 * The title bar is a flex row of two parts: the tag, then the title. The tag is the title's ::after
 * pulled in front with a negative order, because a pseudo-element is the only place to put a word
 * the Markdown does not carry — and for a myth the Markdown must not carry it, since the title has
 * to match the hub heading character for character. The theme's admonition icon is dropped: the
 * tag already says what the block is. */
.md-typeset .myth {
  --aim-callout: var(--aim-myth);
  --aim-callout-ink: var(--aim-myth);
  --aim-callout-wash: var(--aim-myth-wash);
  --aim-callout-tag: "Myth";
  --aim-callout-tag-ink: var(--aim-tag-ink);
}

.md-typeset .key {
  --aim-callout: var(--aim-key);
  --aim-callout-ink: var(--aim-key-ink);
  --aim-callout-wash: var(--aim-key-wash);
  --aim-callout-tag: "Note";
  --aim-callout-tag-ink: var(--aim-key-tag-ink);
}

.md-typeset :is(.admonition, details):is(.myth, .key) {
  border-left: 3px solid var(--aim-callout);
  background-color: var(--aim-callout-wash);
}

.md-typeset :is(.myth, .key) > :is(.admonition-title, summary) {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  /* The theme indents a title past its icon. There is no icon here, and the tag that replaces it
   * is a filled box, so the indent is dropped and the tag's left edge starts on the same column
   * the body text below it starts on. */
  padding-left: 0;
  color: var(--aim-callout-ink);
}

.md-typeset :is(.myth, .key) > :is(.admonition-title, summary)::before {
  display: none;
}

/* The tag naming what the reader is looking at, so a myth's claim is never mistaken for the wiki
 * asserting it, and a key point is never mistaken for one more paragraph. */
.md-typeset :is(.myth, .key) > :is(.admonition-title, summary)::after {
  content: var(--aim-callout-tag);
  order: -1;
  padding: 0.05rem 0.3rem;
  color: var(--aim-callout-tag-ink);
  background-color: var(--aim-callout);
  font-size: 0.58rem;
  letter-spacing: 0.12em;
}

/* The hub link reads as an ordinary link at the end of the correction, named for what it opens,
 * the sourced answer, rather than dressed as a second tag. The arrow points on, not out: it is a page
 * on this site. It stays the myth's red under any picked colour, like the rest of the block, and
 * never wraps away from its last word. */
.md-typeset .myth .aim-myth-more {
  margin-left: 0.2rem;
  color: var(--aim-myth);
  white-space: nowrap;
}

.md-typeset .myth .aim-myth-more::after {
  content: " \2192";
}

/* On wiki/myths.md each entry's heading is the myth block's title bar, so the box reads the same as
 * a myth block anywhere else, the myth and then why it is wrong, while the heading stays a real
 * heading: the anchor inline blocks link to, and an entry in the page contents.
 *
 *   ## Lower sensitivity means better aim { .aim-myth-title }
 *
 *   !!! myth ""
 *       Low and high sensitivity trade desk space against room to correct. Neither end is the
 *       correct one.
 *
 * The body is the entry's verdict, which a myth block on another page repeats word for word. The
 * heading carries the rule, wash and tag; the untitled block after it continues the same box. The
 * verdict is set at body size, since on the hub it opens the entry rather than sitting aside. */
.md-typeset h2.aim-myth-title {
  display: flex;
  align-items: baseline;
  gap: 0.5rem;
  margin-bottom: 0;
  padding: 0.6rem 0.8rem 0.1rem;
  border-left: 3px solid var(--aim-myth);
  background-color: var(--aim-myth-wash);
}

.md-typeset h2.aim-myth-title::before {
  content: "Myth";
  flex: none;
  position: relative;
  top: -0.15em;
  padding: 0.1rem 0.35rem;
  color: var(--aim-tag-ink);
  background-color: var(--aim-myth);
  font-family: var(--aim-display);
  font-size: 0.64rem;
  font-weight: 700;
  letter-spacing: 0.12em;
  line-height: 1.4;
  text-transform: uppercase;
}

.md-typeset h2.aim-myth-title + .admonition.myth {
  margin-top: 0;
  font-size: inherit;
}

/* A key block can open a section the same way, so the point is a real heading rather than a line
 * inside a box: the anchor the contents list links to, at heading size.
 *
 *   ## The beat is a reference, not necessarily a trigger { .aim-key-title }
 *
 *   !!! key ""
 *       The tick measures how fast you are going, not when to click.
 *
 * The rules are the key block's own: at most one per page, the heading states the point, and the
 * untitled block under it carries at most two sentences. */
.md-typeset h2.aim-key-title {
  display: flex;
  align-items: baseline;
  gap: 0.5rem;
  margin-bottom: 0;
  padding: 0.6rem 0.8rem 0.1rem;
  border-left: 3px solid var(--aim-key);
  background-color: var(--aim-key-wash);
}

.md-typeset h2.aim-key-title::before {
  content: "Note";
  flex: none;
  position: relative;
  top: -0.15em;
  padding: 0.1rem 0.35rem;
  color: var(--aim-key-tag-ink);
  background-color: var(--aim-key);
  font-family: var(--aim-display);
  font-size: 0.64rem;
  font-weight: 700;
  letter-spacing: 0.12em;
  line-height: 1.4;
  text-transform: uppercase;
}

.md-typeset h2.aim-key-title + .admonition.key {
  margin-top: 0;
  font-size: inherit;
}

/* .aim-theme-picker — the colour picker in the header. docs/assets/javascripts/aim-theme.js inserts
 * it next to the light/dark toggle, so no Markdown produces it:
 *
 *   <div class="md-header__option aim-theme-picker">
 *     <button class="md-header__button md-icon aim-theme-picker__button">…dot…</button>
 *     <div class="aim-theme-picker__panel" role="dialog" hidden>
 *       swatches, a Custom… colour input, a status line, and Reset
 *     </div>
 *   </div>
 *
 * Every colour here reads from the site's tokens, so the picker re-colours with the page it
 * changes. The button's dot is the live accent, so it always shows the colour in use. */
.aim-theme-picker {
  position: relative;
}

.aim-theme-picker__button {
  border: 0;
  background: none;
  cursor: pointer;
}

.aim-theme-picker__dot {
  display: block;
  width: 1rem;
  height: 1rem;
  border-radius: 50%;
  background-color: var(--aim-accent);
  box-shadow: 0 0 0 2px var(--aim-chrome), 0 0 0 3px var(--aim-chrome-fg-lighter);
}

.aim-theme-picker__panel {
  position: absolute;
  top: calc(100% + 0.3rem);
  right: 0;
  z-index: 10;
  width: 12rem;
  padding: 0.8rem;
  border: 1px solid var(--md-default-fg-color--lightest);
  color: var(--md-default-fg-color);
  background-color: var(--md-default-bg-color);
  box-shadow: 0 0.3rem 1rem #0000002e;
  font-size: 0.68rem;
}

.aim-theme-picker__panel:not([hidden]) {
  display: grid;
  gap: 0.7rem;
}

.aim-theme-picker__swatches {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 0.5rem;
  justify-items: center;
}

.aim-theme-picker__swatch {
  width: 1.8rem;
  height: 1.8rem;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background-color: var(--aim-swatch-light);
  cursor: pointer;
}

/* A swatch shows the accent its colour produces in the scheme on screen, so it matches the dot,
 * links and headings it is about to set. aim-theme.js computes both values per swatch. */
[data-md-color-scheme="slate"] .aim-theme-picker__swatch {
  background-color: var(--aim-swatch-dark);
}

/* The chosen swatch gets a ring in the page's own ink, clear of the swatch by a gap of page
 * background, so it shows on every swatch colour in both schemes. */
.aim-theme-picker__swatch[aria-checked="true"] {
  box-shadow: 0 0 0 2px var(--md-default-bg-color), 0 0 0 4px var(--md-default-fg-color);
}

.aim-theme-picker__swatch:focus-visible,
.aim-theme-picker__reset:focus-visible,
.aim-theme-picker__input:focus-visible {
  outline: 2px solid var(--aim-accent);
  outline-offset: 3px;
}

.aim-theme-picker__custom {
  user-select: none;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem;
  cursor: pointer;
}

.aim-theme-picker__custom[data-active] {
  font-weight: 700;
}

.aim-theme-picker__input {
  width: 2.2rem;
  height: 1.4rem;
  padding: 0;
  border: 1px solid var(--md-default-fg-color--lighter);
  background: none;
  cursor: pointer;
}

.aim-theme-picker__message {
  margin: 0;
  color: var(--aim-accent);
}

.aim-theme-picker__reset {
  justify-self: start;
  padding: 0;
  border: 0;
  color: var(--aim-accent);
  background: none;
  font: inherit;
  font-family: var(--aim-display);
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  cursor: pointer;
}

/* .aim-links — an icon before each entry on the Guides page naming what the link opens: an article,
 * a video, a playlist, a document, a spreadsheet, a post, or an image. Wrap the page's lists once:
 *
 *   <div class="aim-links" markdown>
 *
 *   ## A section
 *
 *   - [Title](https://www.youtube.com/watch?v=...) — Publisher, 2025 — what it teaches.
 *
 *   </div>
 *
 * The type is read from the link's address, so a new entry gets its icon with no markup: YouTube
 * watch and youtu.be links are videos, YouTube playlist links are playlists, Google Docs and Sheets
 * are documents and spreadsheets, x.com and twitter.com links are posts, and anything else is an
 * article. When the address says the wrong thing, as with a post that is really an image, name the
 * type on the link: [Title](https://x.com/...){ .aim-link--image }. The classes are
 * .aim-link--article, --video, --playlist, --doc, --sheet, --post and --image.
 *
 * Only the entry's first link gets an icon, so a second link in its description does not. The icon
 * is announced as its type, "Video" and so on, through the content property's alternative text. */
:root {
  --aim-icon-article: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="4" width="18" height="16" rx="2"/><path d="M7 8h10M7 12h10M7 16h6"/></svg>');
  --aim-icon-video: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="2" y="5" width="20" height="14" rx="3"/><path d="m10 9 5 3-5 3z"/></svg>');
  --aim-icon-playlist: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 6h12M3 12h12M3 18h7"/><path d="m15 14 6 3.5-6 3.5z"/></svg>');
  --aim-icon-doc: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M14 3H7a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V8z"/><path d="M14 3v5h5M9 13h6M9 17h6"/></svg>');
  --aim-icon-sheet: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="18" height="18" rx="2"/><path d="M3 9h18M3 15h18M9 3v18"/></svg>');
  --aim-icon-post: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15a2 2 0 0 1-2 2H8l-5 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/></svg>');
  --aim-icon-image: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="18" height="18" rx="2"/><circle cx="9" cy="9" r="2"/><path d="m21 15-5-5L5 21"/></svg>');
}

.md-typeset .aim-links li > a:first-child::before,
.md-typeset .aim-links li > p:first-child > a:first-child::before {
  content: "" / "Article";
  display: inline-block;
  width: 0.95em;
  height: 0.95em;
  margin-right: 0.35em;
  vertical-align: -0.12em;
  background-color: var(--md-default-fg-color--light);
  -webkit-mask: var(--aim-icon-article) center/contain no-repeat;
  mask: var(--aim-icon-article) center/contain no-repeat;
}

.md-typeset .aim-links li > a:first-child:is(:hover, :focus)::before,
.md-typeset .aim-links li > p:first-child > a:first-child:is(:hover, :focus)::before {
  background-color: currentcolor;
}

/* Types read from the address. */
.md-typeset .aim-links li > a:first-child[href*="youtube.com/watch"]::before,
.md-typeset .aim-links li > p:first-child > a:first-child[href*="youtube.com/watch"]::before,
.md-typeset .aim-links li > a:first-child[href*="youtu.be/"]::before,
.md-typeset .aim-links li > p:first-child > a:first-child[href*="youtu.be/"]::before {
  content: "" / "Video";
  -webkit-mask-image: var(--aim-icon-video);
  mask-image: var(--aim-icon-video);
}

.md-typeset .aim-links li > a:first-child[href*="youtube.com/playlist"]::before,
.md-typeset .aim-links li > p:first-child > a:first-child[href*="youtube.com/playlist"]::before {
  content: "" / "Playlist";
  -webkit-mask-image: var(--aim-icon-playlist);
  mask-image: var(--aim-icon-playlist);
}

.md-typeset .aim-links li > a:first-child[href*="docs.google.com/document"]::before,
.md-typeset .aim-links li > p:first-child > a:first-child[href*="docs.google.com/document"]::before {
  content: "" / "Document";
  -webkit-mask-image: var(--aim-icon-doc);
  mask-image: var(--aim-icon-doc);
}

.md-typeset .aim-links li > a:first-child[href*="docs.google.com/spreadsheets"]::before,
.md-typeset .aim-links li > p:first-child > a:first-child[href*="docs.google.com/spreadsheets"]::before {
  content: "" / "Spreadsheet";
  -webkit-mask-image: var(--aim-icon-sheet);
  mask-image: var(--aim-icon-sheet);
}

.md-typeset .aim-links li > a:first-child[href^="https://x.com/"]::before,
.md-typeset .aim-links li > p:first-child > a:first-child[href^="https://x.com/"]::before,
.md-typeset .aim-links li > a:first-child[href^="https://twitter.com/"]::before,
.md-typeset .aim-links li > p:first-child > a:first-child[href^="https://twitter.com/"]::before {
  content: "" / "Post";
  -webkit-mask-image: var(--aim-icon-post);
  mask-image: var(--aim-icon-post);
}

/* Types named on the link, for addresses that say the wrong thing. These come last so they win. */
.md-typeset .aim-links li > a:first-child.aim-link--article::before,
.md-typeset .aim-links li > p:first-child > a:first-child.aim-link--article::before {
  content: "" / "Article";
  -webkit-mask-image: var(--aim-icon-article);
  mask-image: var(--aim-icon-article);
}

.md-typeset .aim-links li > a:first-child.aim-link--video::before,
.md-typeset .aim-links li > p:first-child > a:first-child.aim-link--video::before {
  content: "" / "Video";
  -webkit-mask-image: var(--aim-icon-video);
  mask-image: var(--aim-icon-video);
}

.md-typeset .aim-links li > a:first-child.aim-link--playlist::before,
.md-typeset .aim-links li > p:first-child > a:first-child.aim-link--playlist::before {
  content: "" / "Playlist";
  -webkit-mask-image: var(--aim-icon-playlist);
  mask-image: var(--aim-icon-playlist);
}

.md-typeset .aim-links li > a:first-child.aim-link--doc::before,
.md-typeset .aim-links li > p:first-child > a:first-child.aim-link--doc::before {
  content: "" / "Document";
  -webkit-mask-image: var(--aim-icon-doc);
  mask-image: var(--aim-icon-doc);
}

.md-typeset .aim-links li > a:first-child.aim-link--sheet::before,
.md-typeset .aim-links li > p:first-child > a:first-child.aim-link--sheet::before {
  content: "" / "Spreadsheet";
  -webkit-mask-image: var(--aim-icon-sheet);
  mask-image: var(--aim-icon-sheet);
}

.md-typeset .aim-links li > a:first-child.aim-link--post::before,
.md-typeset .aim-links li > p:first-child > a:first-child.aim-link--post::before {
  content: "" / "Post";
  -webkit-mask-image: var(--aim-icon-post);
  mask-image: var(--aim-icon-post);
}

.md-typeset .aim-links li > a:first-child.aim-link--image::before,
.md-typeset .aim-links li > p:first-child > a:first-child.aim-link--image::before {
  content: "" / "Image";
  -webkit-mask-image: var(--aim-icon-image);
  mask-image: var(--aim-icon-image);
}

/* Sources for one claim are cited back to back, [^REF-77][^REF-74], so their markers render as bare
 * superscript numbers with nothing between them and two of them read as one number. A middot
 * between neighbours separates them, the same divider the References page puts between an entry's
 * details. It is drawn on the second marker of a pair, which aim_references.py marks with
 * .aim-citation-run, so a lone marker and a marker that only follows an earlier sentence's carry
 * none. */
.md-typeset sup.aim-citation-run::before {
  content: "·";
  margin: 0 0.15em;
  color: var(--md-default-fg-color--light);
}

/* A wiki page's footnotes render below a "References" heading, which already separates them from
 * the page, so the rule the theme draws above its footnote list is dropped there. */
.md-typeset h2#references + .footnote > hr {
  display: none;
}

/* The References page, which the aim_references extension builds. Each entry starts with an empty
 * anchor, id="ref-12", that a footnote's REF link points at. The entry followed to is tinted, so a
 * reader lands on it instead of hunting for it, and the anchor stops short of the sticky header.
 * The tint reaches past the text with a spread shadow, not margin or padding, so the entry and its
 * bullet stay exactly where they sit when not targeted. */
.md-typeset span[id^="ref-"] {
  scroll-margin-top: 6rem;
}

.md-typeset li:has(> span[id^="ref-"]:target) {
  border-radius: 0.1rem;
  background-color: var(--md-accent-fg-color--transparent);
  box-shadow: 0 0 0 0.3rem var(--md-accent-fg-color--transparent);
}


/* ---------------------------------------------------------------------------
 * .aim-figure — a diagram or render with a caption.
 *
 *   <figure class="aim-figure">
 *   <svg viewBox="0 0 760 250" role="img" aria-labelledby="id">...</svg>
 *   <figcaption>What the reader should take from it.</figcaption>
 *   </figure>
 *
 * An inline SVG takes its colors from the classes below, so it follows the reader's scheme and picked
 * colour: ink and muted for text and lines, grid for axes, panel for backgrounds, accent (too much)
 * and cool (too little) for the two ends of a comparison, and target for the thing aimed at. Each
 * has a -fill and -stroke form; plain fig-ink, fig-muted and fig-accent-text set text colour.
 *
 * Where a figure shows how tense a hand is, it uses tense, loose and balanced instead. Those three
 * carry a meaning rather than a mood, so unlike accent they hold their colour whatever the reader
 * picks: red is too tense, blue is too loose, green is balanced.
 *
 * A diagram may animate with CSS keyframes in its own <style>. It has to pause on a telling frame
 * under prefers-reduced-motion. An <img> render works too, and gets rounded corners. Keep the SVG
 * free of blank lines, or Markdown splits it apart.
 * ------------------------------------------------------------------------ */

.md-typeset .aim-figure {
  width: 100%;
  max-width: none;
  margin: 1.6em 0;
}

.md-typeset .aim-figure :is(svg, img) {
  display: block;
  width: 100%;
  height: auto;
  font-family: var(--aim-display);
}

/* A diagram's labels shrink with it, so on a phone it keeps a readable width and scrolls sideways
 * instead. Renders have no small text and simply scale down. */
.md-typeset .aim-figure:has(> svg) {
  overflow-x: auto;
}

.md-typeset .aim-figure > svg {
  min-width: 34rem;
}

/* An animation is hard to follow while scrolled halfway, so an SVG with fig-fit always fits the width.
 * On phones its labels grow to make up for the scaling: fig-name for lane names, fig-note for their
 * notes, fig-phase for step labels and fig-caption for small captions. */
.md-typeset .aim-figure > svg.fig-fit {
  min-width: 0;
}

@media screen and (max-width: 30em) {
  .aim-figure .fig-fit .fig-name { font-size: 25px; }
  .aim-figure .fig-fit .fig-note { font-size: 20px; }
  .aim-figure .fig-fit .fig-phase { font-size: 21px; }
  .aim-figure .fig-fit .fig-caption { font-size: 12px; }
}

.md-typeset .aim-figure img {
  border-radius: 0.4rem;
}

/* A render built from someone else's model credits it on its own line under the caption:
 *   <figcaption>What it shows. <span class="aim-figure-credit">Model: ... by ..., licence.</span></figcaption> */
.md-typeset .aim-figure-credit {
  display: block;
  margin-top: 0.3em;
  font-size: 0.85em;
}

.md-typeset .aim-figure figcaption {
  max-width: none;
  margin-top: 0.6em;
  color: var(--md-default-fg-color--light);
  font-size: 0.72rem;
  text-align: left;
}

.aim-figure .fig-ink,
.aim-figure .fig-ink-fill { fill: var(--md-default-fg-color); }
.aim-figure .fig-muted { fill: var(--md-default-fg-color--light); }
.aim-figure .fig-ink-stroke { stroke: var(--md-default-fg-color); }
.aim-figure .fig-muted-stroke { stroke: var(--md-default-fg-color--light); }
.aim-figure .fig-grid-stroke { stroke: var(--md-default-fg-color--lighter); }
.aim-figure .fig-grid-fill { fill: var(--md-default-fg-color--lighter); }
.aim-figure .fig-panel { fill: var(--md-code-bg-color); }
/* The page behind the panels, for an object that has to read as a thing sitting on one rather than
 * as part of the drawing, such as the mouse under the hand in the blend figure. */
.aim-figure .fig-surface { fill: var(--md-default-bg-color); }
.aim-figure .fig-hand-fill { fill: var(--aim-hand); }
.aim-figure .fig-hand-line { stroke: var(--aim-hand); }
.aim-figure .fig-hand-stroke { stroke: var(--aim-hand-edge); }
.aim-figure .fig-accent-fill { fill: var(--aim-accent); }
.aim-figure .fig-accent-stroke { stroke: var(--aim-accent); }
.aim-figure .fig-accent-text { fill: var(--aim-accent); }
.aim-figure .fig-cool-fill { fill: var(--aim-tracking); }
.aim-figure .fig-cool-stroke { stroke: var(--aim-tracking); }
.aim-figure .fig-target { fill: var(--aim-key); }
/* The key colour as text rather than as a wash: it darkens in the light scheme, where the wash
 * itself is too pale to carry a label. */
.aim-figure .fig-key-text { fill: var(--aim-key-ink); }

/* A figure's colours do double duty: a bar or a band is a graphic, which WCAG holds to 3:1, while a
 * label is text, held to 7:1. Text elements take the ink of their hue, and the shapes beside them
 * keep the fill they had. */
.aim-figure text.fig-tense-fill { fill: var(--aim-tense-ink); }
.aim-figure text.fig-loose-fill { fill: var(--aim-loose-ink); }
.aim-figure text.fig-balanced-fill { fill: var(--aim-balanced-ink); }
.aim-figure text.fig-accent-text { fill: var(--aim-accent-ink); }
.aim-figure .fig-tense-fill { fill: var(--aim-tense); }
.aim-figure .fig-tense-stroke { stroke: var(--aim-tense); }
.aim-figure .fig-loose-fill { fill: var(--aim-loose); }
.aim-figure .fig-loose-stroke { stroke: var(--aim-loose); }
.aim-figure .fig-balanced-fill { fill: var(--aim-balanced); }
.aim-figure .fig-balanced-stroke { stroke: var(--aim-balanced); }

/* An image whose nodes can be hovered, with the matching animation shown beside it.
 *
 * The image is a third party's work reproduced under credit, so nothing here touches it. The
 * hotspots are a layer of transparent buttons over the top, positioned in percentages so they hold
 * their nodes at any width. aim-hotspots.js builds the layer and fetches the animations.
 *
 * Everything degrades to the plain figure: without JavaScript no layer is built, and the rules
 * below apply to nothing. */
.aim-hotspot-map { position: relative; }
.aim-hotspot-map > figure { margin-bottom: 0.4rem; }
/* Wraps the image alone, so the layer over it is the image's box and not the figure's. A figure
 * carries a caption, and measuring percentages against that would drop every lower node below the
 * picture. aim-hotspots.js inserts this. */
.aim-hotspot-frame { position: relative; display: block; line-height: 0; }
.aim-hotspot-layer { position: absolute; inset: 0; line-height: normal; }

.aim-hotspot {
  position: absolute;
  padding: 0;
  border: 1px solid transparent;
  border-radius: 0.2rem;
  background: transparent;
  cursor: pointer;
}

/* Hover and keyboard focus are the same state on purpose: a reader tabbing through the chart sees
 * exactly what a reader pointing at it sees. */
.aim-hotspot:hover,
.aim-hotspot:focus-visible {
  border-color: var(--aim-accent);
  background: color-mix(in srgb, var(--aim-accent) 18%, transparent);
  outline: none;
}

/* A pinned node stays marked once the pointer leaves, which is the whole reason for pinning. */
.aim-hotspot[data-pinned] {
  border-color: var(--aim-accent);
  border-width: 2px;
  background: color-mix(in srgb, var(--aim-accent) 26%, transparent);
}

/* While one node is pinned, hovering another must not look like it changed the panel, because it
 * has not. */
.aim-hotspot-map[data-pinned] .aim-hotspot:hover:not([data-pinned]) { background: transparent; }

/* The panel also carries .aim-figure, and has to: every figure colour rule is written
 * .aim-figure .fig-target and friends, so an SVG injected outside one renders black.
 *
 * It sticks to the bottom of the viewport while the chart is in view. The chart is over two
 * thousand pixels tall, so a panel that simply followed it would sit a screen and a half below the
 * node being pointed at, and a reader would never see what they triggered. */
.aim-hotspot-panel {
  position: sticky;
  bottom: 0.4rem;
  z-index: 2;
  box-shadow: 0 0 0.6rem rgba(0, 0, 0, 0.18);
  min-height: 6rem;
  padding: 0.6rem;
  border: 1px solid var(--md-default-fg-color--lightest);
  border-radius: 0.3rem;
  background: var(--md-code-bg-color);
  font-size: 0.72rem;
  color: var(--md-default-fg-color--light);
}

.aim-hotspot-panel svg { display: block; width: 100%; height: auto; }

/* Said once, to a reader who has not hovered anything yet. */
.aim-hotspot-map:not([data-ready]) .aim-hotspot-panel { display: none; }

/* What a hotspot panel says before it draws anything.
 *
 * The chart's own wording leads, because that is what the reader pointed at, and it is quoted
 * rather than paraphrased. This site's reading of the node follows it, smaller, so the two are
 * never mistaken for each other. */
.aim-hotspot-says {
  margin: 0 0 0.2rem;
  font-size: 0.78rem;
  font-weight: 600;
  line-height: 1.35;
  color: var(--md-default-fg-color);
}

/* Which kind of node it is, above the chart's own sentence. Small, because it is a tag rather
 * than a sentence, and the chart's words are what the reader came for. */
.aim-hotspot-kind {
  margin: 0 0 0.15rem;
  font-size: 0.62rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--md-default-fg-color--light);
}
