/* Declare layer order first. vendor is lowest, so BRAIID's base layer overrides
   third-party CSS (e.g. tooltipster) loaded into the vendor layer, regardless of
   the vendor selector's specificity. Unlayered styles still win over both. */
@layer vendor, base;

@import url('https://fonts.googleapis.com/css2?family=Montserrat:ital,wght@0,100..900;1,100..900&family=Source+Sans+3:ital,wght@0,200..900;1,200..900&family=Source+Serif+4:ital,opsz,wght@0,8..60,200..900;1,8..60,200..900&family=Atkinson+Hyperlegible:ital,wght@0,400;0,700;1,400;1,700&display=swap');
@import url('https://fonts.googleapis.com/css2?family=Source+Code+Pro:ital,wght@0,200..900;1,200..900&display=swap');
@import url('pygments.css') layer(base);

@layer base {

/* Page-level background for dark mode */
body {
  background: var(--surface-page);
  margin: 0;
  padding: 0;
  transition: background 0.3s ease, color 0.3s ease;
}

:root {
  /* Follow the OS by default; .dark-theme / .light-theme force a choice. */
  color-scheme: light dark;
  /* Dark-only palette value (no light counterpart); used by --text-subtle. */
  --gray-550: #788188;
  /* Base font size - mobile-first responsive scaling */
  font-size: 12px;  /* Mobile default (< 640px) */

  /* General */
  --side-padding: 1rem;    /* Mobile-first, scaled up at breakpoints */

  /* Spacing - chunk system (1 chunk = 3rem: 36px mobile, 42px tablet, 48px desktop) */
  --chunk: 3rem;              /* base unit */
  --chunk-twelfth: 0.25rem;   /* chunk ÷ 12 */
  --chunk-sixth: 0.5rem;      /* chunk ÷ 6 */
  --chunk-quarter: 0.75rem;   /* chunk ÷ 4 */
  --chunk-third: 1rem;        /* chunk ÷ 3 */
  --chunk-half: 1.5rem;       /* chunk ÷ 2 */
  --chunk-2: 6rem;            /* chunk × 2 */
  --chunk-3: 9rem;            /* chunk × 3 */
  --chunk-20: 60rem;          /* chunk × 20 - max manuscript width */

  /* Handrail spacing (scales with viewport: mobile/tablet/desktop) */
  --hr-zone-width: var(--chunk-third);      /* 12px/14px/16px - width of each zone */
  --hr-left-total: var(--chunk);            /* 36px/42px/48px - total left region */
  --hr-right-total: var(--chunk);           /* 36px/42px/48px - info zone width */
  --hr-total-offset: var(--chunk-2);        /* 72px/84px/96px - left + right combined */

  /* Radius - border radius scale (in rem for responsive scaling) */
  --radius-none: 0;
  --radius-sm: 0.125rem;  /* 2px at 16px base - Halmos symbol */
  --radius-md: 0.25rem;   /* 4px at 16px base - default small elements */
  --radius-lg: 0.5rem;    /* 8px at 16px base - buttons, handrail controls */
  --radius-xl: 1rem;      /* 16px at 16px base - cards, modals, tooltips */
  --radius-full: 50%;     /* circles */

  /* Halmos */
  --halmos-side-length: 0.5rem;
  --halmos--ellipsis-width: 1em;

  /* Colors -- Primitive */
  --gray-1000: light-dark(#000000, #E8EDF0);
  --gray-950: light-dark(#13181B, #CED6DC);
  --gray-900: light-dark(#3C4952, #B5BFC8);
  --gray-800: light-dark(#51636F, #A0ACB7);
  --gray-700: light-dark(#5F7186, #8C9BA8);
  --gray-750: #6E787F;
  --gray-600: light-dark(#8B9FAD, #7A8A99);
  --gray-500: light-dark(#A9B7C1, #67798A);
  --gray-400: light-dark(#B3BFC7, #566877);
  --gray-300: light-dark(#C2CCD3, #475662);
  --gray-200: light-dark(#DAE1E5, #38454F);
  --gray-100: light-dark(#EDF0F2, #2A3740);
  --gray-75: light-dark(#F5F7F8, #232D35);
  --gray-50: light-dark(#FCFEFE, #1C242A);
  --gray-0: light-dark(#FFFFFF, #13181B);

  --blue-950: light-dark(#082C49, #C2E3FD);
  --blue-900: light-dark(#0C456E, #84CAF7);
  --blue-800: light-dark(#075487, #55B5F1);
  --blue-700: light-dark(#0361A1, #2BA1EA);
  --blue-600: light-dark(#027AC7, #1A8CD4);
  --blue-500: light-dark(#0E9AE9, #1577BA);
  --blue-400: light-dark(#38B4F8, #1266A0);
  --blue-300: light-dark(#7DCDFC, #0F5586);
  --blue-200: light-dark(#BAE3FD, #0D446D);
  --blue-100: light-dark(#E0F1FE, #0A375A);
  --blue-50: light-dark(#F0F8FF, #082C49);

  --purple-950: light-dark(#370B60, #E2D2F9);
  --purple-900: light-dark(#522182, #C5A5EF);
  --purple-800: light-dark(#6427A2, #A77CE4);
  --purple-700: light-dark(#7629C7, #A068E0);
  --purple-600: light-dark(#8B3BE2, #7A3BC7);
  --purple-500: light-dark(#AD71F2, #6831AD);
  --purple-400: light-dark(#BB89F7, #5A2997);
  --purple-300: light-dark(#D5B7FB, #4D2181);
  --purple-200: light-dark(#E7D7FD, #411A6C);
  --purple-100: light-dark(#F2E9FE, #341358);
  --purple-50: light-dark(#FAF5FF, #2A0D46);

  --green-950: light-dark(#072C2B, #A0E8DD);
  --green-900: light-dark(#174A46, #5DCFC5);
  --green-800: light-dark(#165954, #37B6AC);
  --green-700: light-dark(#157067, #279D96);
  --green-600: light-dark(#158C7F, #20847F);
  --green-500: light-dark(#1FB5A2, #1B706E);
  --green-400: light-dark(#37CAB6, #176260);
  --green-300: light-dark(#66E2CC, #135452);
  --green-200: light-dark(#9FF0E0, #0F4643);
  --green-100: light-dark(#CFF8EE, #0B3937);
  --green-50: light-dark(#F1FCFA, #072C2B);

  --red-950: light-dark(#450A0A, #F7C5C1);
  --red-900: light-dark(#7F1D1E, #EB9791);
  --red-800: light-dark(#991B1C, #DB6C64);
  --red-700: light-dark(#B81D1E, #D85851);
  --red-600: light-dark(#DB2728, #B73932);
  --red-500: light-dark(#EF4B4C, #A32E29);
  --red-400: light-dark(#F87172, #8E2522);
  --red-300: light-dark(#FCA5A6, #7A1E1B);
  --red-200: light-dark(#FECACA, #671715);
  --red-100: light-dark(#FEE2E2, #55110F);
  --red-50: light-dark(#FEF2F2, #450A0A);

  --orange-950: light-dark(#421708, #FAD7B9);
  --orange-900: light-dark(#7A3214, #F2B285);
  --orange-800: light-dark(#973A15, #E58753);
  --orange-700: light-dark(#BE4A10, #D06A37);
  --orange-600: light-dark(#E56211, #B5562B);
  --orange-500: light-dark(#F5862B, #9A4724);
  --orange-400: light-dark(#F79940, #873C1E);
  --orange-300: light-dark(#FABF77, #753218);
  --orange-200: light-dark(#FCDAAC, #632913);
  --orange-100: light-dark(#FEEFD6, #51200D);
  --orange-50: light-dark(#FFF8ED, #421708);

  --yellow-950: light-dark(#481E00, #FFD98A);
  --yellow-900: light-dark(#7C3C0B, #FFC04F);
  --yellow-800: light-dark(#984908, #FFA01A);
  --yellow-700: light-dark(#B85C01, #ED8100);
  --yellow-600: light-dark(#E28800, #D06800);
  --yellow-500: light-dark(#F5AB00, #B45700);
  --yellow-400: light-dark(#FFD31B, #9C4B00);
  --yellow-300: light-dark(#FFE446, #853F00);
  --yellow-200: light-dark(#FFF285, #6F3300);
  --yellow-100: light-dark(#FFF8C5, #5A2800);
  --yellow-50: light-dark(#FFFDEA, #481E00);

  --pink-950: light-dark(#500724, #FCC9E3);
  --pink-900: light-dark(#831843, #FA9FCD);
  --pink-800: light-dark(#9d174d, #F26DAE);
  --pink-700: light-dark(#be185d, #E5488D);
  --pink-600: light-dark(#db2777, #D13273);
  --pink-500: light-dark(#ec4899, #B72861);
  --pink-400: light-dark(#f472b6, #A12154);
  --pink-300: light-dark(#f9a8d4, #8C1B47);
  --pink-200: light-dark(#fbcfe8, #77143B);
  --pink-100: light-dark(#fce7f3, #630E2F);
  --pink-50: light-dark(#fdf2f8, #500724);

  /* Primary color defaults — mirrors [data-accent="blue"] so :root is always resolved */
  --primary-950: var(--blue-950);
  --primary-900: var(--blue-900);
  --primary-800: var(--blue-800);
  --primary-700: var(--blue-700);
  --primary-600: var(--blue-600);
  --primary-500: var(--blue-500);
  --primary-400: var(--blue-400);
  --primary-300: var(--blue-300);
  --primary-200: var(--blue-200);
  --primary-100: var(--blue-100);
  --primary-50: var(--blue-50);

  /* Colors -- Alias */
  --black: var(--gray-1000);
  --almost-black: var(--gray-950);
  --extra-dark: var(--gray-900);
  --dark: var(--gray-700);
  --medium: var(--gray-500);
  --light: var(--gray-300);
  --extra-light: var(--gray-100);
  --almost-white: var(--gray-50);
  --white: var(--gray-0);

  --information-950: var(--blue-950);
  --information-900: var(--blue-900);
  --information-800: var(--blue-800);
  --information-700: var(--blue-700);
  --information-600: var(--blue-600);
  --information-500: var(--blue-500);
  --information-400: var(--blue-400);
  --information-300: var(--blue-300);
  --information-200: var(--blue-200);
  --information-100: var(--blue-100);
  --information-50: var(--blue-50);

  --success-950: var(--green-950);
  --success-900: var(--green-900);
  --success-800: var(--green-800);
  --success-700: var(--green-700);
  --success-600: var(--green-600);
  --success-500: var(--green-500);
  --success-400: var(--green-400);
  --success-300: var(--green-300);
  --success-200: var(--green-200);
  --success-100: var(--green-100);
  --success-50: var(--green-50);

  --warning-950: var(--orange-950);
  --warning-900: var(--orange-900);
  --warning-800: var(--orange-800);
  --warning-700: var(--orange-700);
  --warning-600: var(--orange-600);
  --warning-500: var(--orange-500);
  --warning-400: var(--orange-400);
  --warning-300: var(--orange-300);
  --warning-200: var(--orange-200);
  --warning-100: var(--orange-100);
  --warning-50: var(--orange-50);

  --error-950: var(--red-950);
  --error-900: var(--red-900);
  --error-800: var(--red-800);
  --error-700: var(--red-700);
  --error-600: var(--red-600);
  --error-500: var(--red-500);
  --error-400: var(--red-400);
  --error-300: var(--red-300);
  --error-200: var(--red-200);
  --error-100: var(--red-100);
  --error-50: var(--red-50);

  /* Colors -- Semantic */
  --surface-page: light-dark(var(--white), var(--gray-0));
  --surface-primary: light-dark(var(--almost-white), var(--gray-50));
  --surface-hover: light-dark(var(--gray-75), var(--gray-100));
  --surface-information: light-dark(var(--information-50), var(--information-100));
  --surface-success: light-dark(var(--success-50), var(--success-100));
  --surface-warning: var(--warning-50);
  --surface-error: light-dark(var(--error-50), var(--error-100));
  --surface-disabled: light-dark(var(--gray-200), var(--gray-300));
  /* Figure caption box. Dark uses gray-75 (not gray-100) so a caption link
     (--link-default) clears WCAG AA (4.93:1 vs 4.30:1); light is unchanged. */
  --surface-caption: light-dark(var(--gray-100), var(--gray-75));

  --border-primary: light-dark(var(--gray-200), var(--gray-300));
  --border-information: light-dark(var(--information-100), var(--information-300));
  --border-success: light-dark(var(--success-100), var(--success-300));
  --border-warning: light-dark(var(--warning-100), var(--warning-300));
  --border-error: light-dark(var(--error-100), var(--error-300));
  --handrail-default: var(--border-primary);

  --text-body: light-dark(var(--extra-dark), var(--gray-950));
  --text-subtle: light-dark(var(--gray-750), var(--gray-550));
  --text-disabled: light-dark(var(--gray-400), var(--gray-600));
  --text-error: var(--error-500);

  --icon-success: var(--success-500);
  --icon-information: var(--information-500);
  --icon-warning: var(--warning-500);
  --icon-error: var(--error-500);

  /* Type */
  --body-line-height: 1.5;
  --header-line-height: 1.25;
  --h1-size: 2.000rem;
  --h2-size: 1.750rem;
  --h3-size: 1.500rem;
  --h4-size: 1.375rem;
  --h5-size: 1.250rem;
  --h6-size: 1.000rem;
  --font-size: 100%;
  --weight-bold: 700;
  --weight-semi: 600;
  --weight-medium: 500;
  --weight-regular: 400;
  --weight-light: 300;
  --font-heading: 'Montserrat', sans-serif;
  --font-body: 'Source Sans 3', sans-serif;
  --font-mono: 'Source Code Pro', monospace;

  /* Borders */
  --border-thick: 6px;
  --border-med: 4px;
  --border-thin: 2px;
  --border-extrathin: 1px;

  /* Shadows */
  --shadow-strong: 0px 1px 2px rgba(0, 0, 0, 30%);
  --shadow-soft: 0px 2px 6px rgba(0, 0, 0, 15%);

  /* Transitions */
  --transition-duration: 0.3s;
  --transition-bg-color: background-color 0.1s ease;
  --transition-bd-color: border-color 0.1s ease;

}

[data-accent="blue"] {
  --primary-950: var(--blue-950);
  --primary-900: var(--blue-900);
  --primary-800: var(--blue-800);
  --primary-700: var(--blue-700);
  --primary-600: var(--blue-600);
  --primary-500: var(--blue-500);
  --primary-400: var(--blue-400);
  --primary-300: var(--blue-300);
  --primary-200: var(--blue-200);
  --primary-100: var(--blue-100);
  --primary-50: var(--blue-50);
}

[data-accent="purple"] {
  --primary-950: var(--purple-950);
  --primary-900: var(--purple-900);
  --primary-800: var(--purple-800);
  --primary-700: var(--purple-700);
  --primary-600: var(--purple-600);
  --primary-500: var(--purple-500);
  --primary-400: var(--purple-400);
  --primary-300: var(--purple-300);
  --primary-200: var(--purple-200);
  --primary-100: var(--purple-100);
  --primary-50: var(--purple-50);
}

[data-accent="red"] {
  --primary-950: var(--red-950);
  --primary-900: var(--red-900);
  --primary-800: var(--red-800);
  --primary-700: var(--red-700);
  --primary-600: var(--red-600);
  --primary-500: var(--red-500);
  --primary-400: var(--red-400);
  --primary-300: var(--red-300);
  --primary-200: var(--red-200);
  --primary-100: var(--red-100);
  --primary-50: var(--red-50);
}

[data-accent="green"] {
  --primary-950: var(--green-950);
  --primary-900: var(--green-900);
  --primary-800: var(--green-800);
  --primary-700: var(--green-700);
  --primary-600: var(--green-600);
  --primary-500: var(--green-500);
  --primary-400: var(--green-400);
  --primary-300: var(--green-300);
  --primary-200: var(--green-200);
  --primary-100: var(--green-100);
  --primary-50: var(--green-50);
}

[data-accent="yellow"] {
  --primary-950: var(--yellow-950);
  --primary-900: var(--yellow-900);
  --primary-800: var(--yellow-800);
  --primary-700: var(--yellow-700);
  --primary-600: var(--yellow-600);
  --primary-500: var(--yellow-500);
  --primary-400: var(--yellow-400);
  --primary-300: var(--yellow-300);
  --primary-200: var(--yellow-200);
  --primary-100: var(--yellow-100);
  --primary-50: var(--yellow-50);
}

[data-accent="orange"] {
  --primary-950: var(--orange-950);
  --primary-900: var(--orange-900);
  --primary-800: var(--orange-800);
  --primary-700: var(--orange-700);
  --primary-600: var(--orange-600);
  --primary-500: var(--orange-500);
  --primary-400: var(--orange-400);
  --primary-300: var(--orange-300);
  --primary-200: var(--orange-200);
  --primary-100: var(--orange-100);
  --primary-50: var(--orange-50);
}

[data-accent="pink"] {
  --primary-950: var(--pink-950);
  --primary-900: var(--pink-900);
  --primary-800: var(--pink-800);
  --primary-700: var(--pink-700);
  --primary-600: var(--pink-600);
  --primary-500: var(--pink-500);
  --primary-400: var(--pink-400);
  --primary-300: var(--pink-300);
  --primary-200: var(--pink-200);
  --primary-100: var(--pink-100);
  --primary-50: var(--pink-50);
}

[data-accent="gray"] {
  --primary-950: var(--gray-950);
  --primary-900: var(--gray-900);
  --primary-800: var(--gray-800);
  --primary-700: var(--gray-700);
  --primary-600: var(--gray-600);
  --primary-500: var(--gray-500);
  --primary-400: var(--gray-400);
  --primary-300: var(--gray-300);
  --primary-200: var(--gray-200);
  --primary-100: var(--gray-100);
  --primary-50: var(--gray-50);
}

/* Semantic tokens built from the accent palette. They live on the data-accent
   carrier, not on :root, because a custom property's var() resolves where the
   property is declared and :root's --primary-* is always the blue default.
   Declared here, they recompute against the carrier's own accent so links,
   keywords and action colors follow :accent: in both themes (rsm-mwf). */
[data-accent] {
  --surface-action: light-dark(var(--primary-500), var(--primary-600));
  --surface-action-hover: light-dark(var(--primary-600), var(--primary-500));
  --surface-hint: light-dark(var(--primary-200), var(--primary-100));
  --border-action: var(--primary-500);
  --border-action-hover: light-dark(var(--primary-600), var(--primary-400));
  --handrail-active: var(--border-action);
  --link-default: var(--primary-700);
  --link-hover: var(--primary-800);
  --text-keyword: light-dark(var(--primary-700), var(--primary-600));
  --text-hilite-bg: light-dark(var(--primary-200), var(--primary-300));
  --text-action: light-dark(var(--primary-600), var(--primary-500));
  --text-action-hover: light-dark(var(--primary-600), var(--primary-400));
  --icon-action: light-dark(var(--information-600), var(--primary-500));
}

/* Typography */
[data-typography="serif"] {
  --font-heading: 'Source Serif 4', serif;
  --font-body: 'Source Serif 4', serif;
}

/* Reader type controls: the rail's Reading scope sets these data-* attributes
   on :root, overriding the variables the content already consumes. Default
   values (size 2, normal leading, normal width) carry no attribute. Widths are
   capped so the widest never collides with the rail in the gutter. */
:root[data-reading-typeface="serif"] {
  --font-heading: 'Source Serif 4', serif;
  --font-body: 'Source Serif 4', serif;
}
:root[data-reading-typeface="legible"] {
  --font-heading: 'Atkinson Hyperlegible', 'Source Sans 3', sans-serif;
  --font-body: 'Atkinson Hyperlegible', 'Source Sans 3', sans-serif;
}
:root[data-reading-size="1"] { --font-size: 90%; }
:root[data-reading-size="3"] { --font-size: 112%; }
:root[data-reading-size="4"] { --font-size: 125%; }
:root[data-reading-leading="tight"] { --body-line-height: 1.3; }
:root[data-reading-leading="loose"] { --body-line-height: 1.85; }
:root { --content-max-width: 54rem; }  /* default body measure; presets below override */
:root[data-reading-measure="narrow"] { --content-max-width: 52rem; }
:root[data-reading-measure="wide"] { --content-max-width: 68rem; }


/* Theme switch. Colors are single light-dark() definitions on :root; the
   browser picks a half from color-scheme. The OS sets it by default; these
   two classes (added by the reading controls) force an explicit choice. */
.dark-theme { color-scheme: dark; }
.light-theme { color-scheme: light; }

/* Respect system dark mode preference (unless .light-theme class overrides) */


/* CSS Reset */
*,
*::before,
*::after {
  box-sizing: border-box;
  margin: 0;
}


/* General utilities */
.manuscriptwrapper {
  .error {background-color: var(--error-100);}
  .node-with-no-class {background-color: var(--error-100);}
  .reference.unknown {background-color: var(--error-100);}
  .hide {display: none !important;}
}


/* Outermost wrapper */
.manuscriptwrapper {

  /* Default */
  border-radius: var(--radius-lg);
  margin: 0 auto;
  overflow: hidden;
  padding: 0 var(--side-padding) 0 var(--side-padding);
  font-family: var(--font-body);
  font-size: var(--font-size);
  font-weight: var(--weight-regular);
  line-height: var(--body-line-height);
  background: var(--surface-page);
  color: var(--extra-dark);
  transition: background 0.3s ease, color 0.3s ease;

  /* Outermost wrapper when the viewport is narrow */
  &.wrapper-narrow {
    margin: 2.5em 15em 2.5em auto;
  }

  /* Outermost wrapper when the the manuscript is embedded in another page */
  &.embedded {
    margin: 0;
    padding-inline: 1.5rem;
    box-shadow: unset;
    border-radius: var(--radius-none);
  }

}


/* Widths */
.manuscriptwrapper {
  width: 100%;
  max-width: var(--content-max-width, var(--chunk-20));

  & > .manuscript {
    width: 100%;
  }

  & > .manuscript section {
    width: 100%;
  }

  & > .manuscript section .hr {
    width: 100%;
  }

  /*
   * Nesting Depth Limits
   *
   * BRAIID supports up to 3 levels of nesting (depths 0-3). Deeper nesting
   * becomes unreadable and indicates the document needs restructuring.
   *
   * - Depth 0: Direct child of section (96px offset = 1× hr-total-offset)
   * - Depth 1: Nested once (192px = 2× hr-total-offset)
   * - Depth 2: Nested twice (288px = 3× hr-total-offset)
   * - Depth 3: Nested thrice (384px = 4× hr-total-offset) - MAXIMUM
   *
   * Content nested beyond depth 3 will trigger a linter warning suggesting
   * refactoring (e.g., extract nested content to a new section/theorem).
   */

  /* Depth 0: direct child of section */
  & > .manuscript section .hr .hr-content-zone {
    width: calc(100% - var(--hr-total-offset));
  }

  /* Depth 1: nested one level */
  & > .manuscript section .hr .hr .hr-content-zone {
    width: calc(100% - calc(var(--hr-total-offset) * 2));
  }

  /* Depth 2: nested two levels */
  & > .manuscript section .hr .hr .hr .hr-content-zone {
    width: calc(100% - calc(var(--hr-total-offset) * 3));
  }

  /* Depth 3: nested three levels (MAXIMUM) */
  & > .manuscript section .hr .hr .hr .hr .hr-content-zone {
    width: calc(100% - calc(var(--hr-total-offset) * 4));
  }

}



/* Tooltipster styles.
   BRAIID renders the tooltip as a plain card with no pointer. The vendored
   shadow theme reserves a margin for an arrow and draws one; hide the arrow and
   zero that reserved margin so the card sits at tooltipster's normal distance
   from the trigger. The vendored arrow rules load in @layer vendor, so these
   base-layer rules win regardless of specificity (rsm-3y7). */
.tooltipster-sidetip.tooltipster-shadow.tooltipster-shadow-rsm {

  .tooltipster-box {
    background: var(--surface-page);
    border-radius: var(--radius-xl);
    border: var(--border-thin) solid var(--border-primary);
    margin: 0;
  }

  .tooltipster-arrow {
    display: none;
  }

  .tooltipster-content {
    background: var(--surface-page);
    color: var(--extra-dark);
    border-radius: var(--radius-xl);

    .manuscriptwrapper {
      /* Neutralise the document-layout rules: inside a tooltip the wrapper must
         fit the (maxWidth-capped) box, not the document column. Without this it
         keeps the sidebar-clearance margin (margin-inline-start, applied to the
         document column to clear the fixed rail) and max-width:960px, which shove
         the cloned block sideways and overflow the box. !important on both the
         physical and logical margin: that push wins the cascade otherwise. */
      margin: 0 !important;
      margin-inline: 0 !important;
      width: auto;
      max-width: 100%;
      padding: 0 1rem 0 1rem;
    }

    /* Short plain-text control labels (rail tabs/buttons): body font, tooltip
       size, wrapped by the instance maxWidth. Not document content, so no
       .manuscriptwrapper (which would size to the document column width). */
    .rail-tip {
      font-family: var(--font-body);
      font-size: 0.82rem;
      line-height: 1.35;
      padding: var(--chunk-twelfth) var(--chunk-sixth);
    }

  }

}


/* Back pill: a transient affordance over the native browser Back button
   (onload.js shows it after an in-document jump). Reading-first, so it stays out
   of the way: a small floating pill, bottom-centre, that fades in on a jump and
   out again on Back or when the reader scrolls away. */
.rsm-back-pill {
  position: fixed;
  left: 50%;
  bottom: calc(1.25rem + env(safe-area-inset-bottom, 0px));
  z-index: 9998;
  display: inline-flex;
  align-items: center;
  gap: var(--chunk-sixth);
  padding: var(--chunk-sixth) var(--chunk-third);
  font-family: var(--font-body);
  font-size: 0.85rem;
  line-height: 1;
  color: var(--surface-page);
  background: var(--extra-dark);
  border: none;
  border-radius: 999px;
  box-shadow: 0 2px 12px rgba(0, 0, 0, 0.18);
  cursor: pointer;
  opacity: 0;
  visibility: hidden;
  transform: translateX(-50%) translateY(0.5rem);
  transition: opacity 0.18s ease, transform 0.18s ease, visibility 0s linear 0.18s;
}

.rsm-back-pill.is-visible {
  opacity: 1;
  visibility: visible;
  transform: translateX(-50%) translateY(0);
  transition: opacity 0.18s ease, transform 0.18s ease, visibility 0s;
}

.rsm-back-pill:hover { background: var(--primary-600); }
.rsm-back-pill:focus-visible { outline: 2px solid var(--primary-600); outline-offset: 2px; }
.rsm-back-pill-arrow { font-size: 1.05em; }

@media (prefers-reduced-motion: reduce) {
  .rsm-back-pill,
  .rsm-back-pill.is-visible {
    transform: translateX(-50%);
    transition: opacity 0.001s, visibility 0s;
  }
}


/* Shared-view pill: shown only when a ?view= link was opened (potf-44f). Same
   calm bottom-centre language as the back pill; it names the provenance and
   offers a one-click return to the pristine paper. Appended to <body>, so
   top-level (outside .manuscriptwrapper). */
.rsm-shared-pill {
  position: fixed;
  left: 50%;
  bottom: calc(1.25rem + env(safe-area-inset-bottom, 0px));
  z-index: 9998;
  display: inline-flex;
  align-items: center;
  gap: var(--chunk-sixth);
  padding: var(--chunk-sixth) var(--chunk-sixth) var(--chunk-sixth) var(--chunk-third);
  font-family: var(--font-body);
  font-size: 0.82rem;
  line-height: 1;
  color: var(--surface-page);
  background: var(--extra-dark);
  border: none;
  border-radius: 999px;
  box-shadow: 0 2px 12px rgba(0, 0, 0, 0.18);
  transform: translateX(-50%);
}
.rsm-shared-pill-label { font-weight: var(--weight-semi); }
.rsm-shared-pill-reset {
  font: inherit;
  font-weight: var(--weight-semi);
  color: var(--extra-dark);
  background: var(--surface-page);
  border: none;
  border-radius: 999px;
  padding: var(--chunk-twelfth) var(--chunk-sixth);
  cursor: pointer;
}
.rsm-shared-pill-reset:hover,
.rsm-shared-pill-reset:focus-visible { background: var(--primary-100); }
.rsm-shared-pill-x {
  font: inherit;
  font-size: 1.1em;
  line-height: 1;
  color: var(--surface-page);
  background: none;
  border: none;
  cursor: pointer;
  padding: 0 var(--chunk-twelfth);
}

/* Active dependency-lens affordance: a fixed pill, bottom-centre, that stays
   visible wherever the reader has scrolled (the rail-side cone can be far
   off-screen). It names the active lens, shows the counts, offers the other
   direction so the reader can compose, and makes the exit and the Esc shortcut
   explicit. The leading dot reads as a live "on" indicator. Top-level (not
   nested under .manuscriptwrapper) because deplens.js portals the pill to
   <body>, outside the wrapper, so fixed positioning is viewport-relative. */
.deplens-bar {
  position: fixed;
  bottom: var(--chunk-half);
  left: 50%;
  transform: translateX(-50%);
  z-index: 1000;
  display: flex;
  align-items: center;
  gap: var(--chunk-sixth);
  max-width: min(92vw, 660px);
  padding: var(--chunk-sixth) var(--chunk-quarter) var(--chunk-sixth) var(--chunk-quarter);
  /* Portaled to <body>, outside .manuscriptwrapper, so the brand font is not
     inherited (same fix as the singleton menu on body): set it explicitly. */
  font-family: var(--font-body);
  font-size: 0.82rem;
  line-height: 1.2;
  color: var(--text-body);
  background: var(--surface-primary);
  border: var(--border-thin) solid var(--primary-600);
  border-radius: 999px;
  box-shadow: 0 6px 24px rgba(0, 0, 0, 0.16);
}
.deplens-bar-badge {
  display: inline-flex;
  align-items: center;
  gap: 0.4em;
  font-weight: var(--weight-semi);
  color: var(--primary-700);
  white-space: nowrap;
}
.deplens-bar-badge::before {
  content: "";
  width: 0.55em;
  height: 0.55em;
  border-radius: var(--radius-full);
  background: var(--primary-600);
  box-shadow: 0 0 0 3px var(--primary-100);
}
.deplens-bar-label {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.deplens-bar .deplens-add,
.deplens-bar .deplens-exit {
  cursor: pointer;
  user-select: none;
  white-space: nowrap;
  padding: var(--chunk-twelfth) var(--chunk-sixth);
  border-radius: 999px;
  font-weight: var(--weight-semi);
}
.deplens-bar .deplens-add { color: var(--text-action); }
.deplens-bar .deplens-add:hover,
.deplens-bar .deplens-add:focus-visible { background: var(--primary-100); }
.deplens-bar .deplens-exit {
  display: inline-flex;
  align-items: center;
  gap: 0.4em;
  color: var(--almost-white);
  background: var(--primary-600);
}
.deplens-bar .deplens-exit:hover,
.deplens-bar .deplens-exit:focus-visible { background: var(--primary-700); }
.deplens-bar .deplens-exit-x { font-size: 1.1em; line-height: 1; }
.deplens-kbd {
  font: inherit;
  font-size: 0.72em;
  font-weight: var(--weight-semi);
  padding: 0.05em 0.4em;
  border-radius: var(--radius-sm);
  background: rgba(255, 255, 255, 0.22);
  border: var(--border-extrathin) solid rgba(255, 255, 255, 0.5);
}


/* Calculational proof (:calc:): the chain reads as one vertical sequence. Its
   relation steps keep the full .hr structure -- so their content lays out and
   aligns -- but render NONE of their own handrail chrome except the disclosure
   control: the native collapse, rendered in the info-zone (the right-margin
   per-row slot) and reglyphed as the math "because" sign. Collapsed shows the
   relation, open reveals its :p: justification. No left rule, dots, menu,
   spacer, or number. Presentation only; the handrail system is untouched. */
.manuscriptwrapper .calc .step {
  margin-block: 0;
}
.manuscriptwrapper .calc .statement,
.manuscriptwrapper .calc .statement p {
  margin-block: 0;
}
.manuscriptwrapper .calc .step .hr-menu-zone,
.manuscriptwrapper .calc .step .hr-border-zone,
.manuscriptwrapper .calc .step .hr-spacer-zone {
  display: none;
}
/* The disclosure control lives in the info-zone (below); the left-gutter slot and
   the nested relation/justification collapse zones carry nothing. */
.manuscriptwrapper .calc .step > .hr-collapse-zone,
.manuscriptwrapper .calc .step .statement .hr-collapse-zone,
.manuscriptwrapper .calc .step .subproof .hr-collapse-zone {
  display: none;
}
/* The justification toggle sits in the info-zone -- the right-margin, per-row
   slot where an equation number would go (translator: _hr_info_zone_disclosure).
   Reglyph the control as the because-sign (U+2235; no Tabler icon exists): quiet
   gray at rest -- it reads "a reason exists", not a toggle -- lifting on row
   hover/focus, and solid primary when its proof is open. */
.manuscriptwrapper .calc .step > .hr-info-zone .icon {
  display: none;
}
/* A PARENT step's info-zone (.hr-info, z-index:1) paints over this row's control
   -- the calc sits inside the parent's content-zone -- swallowing clicks/hover.
   Lift this info-zone above it, and make the control a solid, top-most hit target
   aligned to the relation (cancelling the base top: chunk-sixth nudge). */
.manuscriptwrapper .calc .step > .hr-info-zone {
  z-index: 5;
}
.manuscriptwrapper .calc .step > .hr-info-zone .hr-collapse {
  position: relative;
  z-index: 1;
  top: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  min-width: var(--chunk-half);
  min-height: var(--chunk-half);
  opacity: 1;
  cursor: pointer;
}
.manuscriptwrapper .calc .step > .hr-info-zone .hr-collapse::before {
  content: "";
  display: block;
  width: calc(var(--chunk-third) * 1.1);
  height: calc(var(--chunk-third) * 1.1);
  /* --dark, not --medium: the rest-state sign was ~2.05; --dark clears WCAG
     non-text (3:1) and 1.4.3 (4.5:1) while staying a quiet gray before hover. */
  background-color: var(--dark);
  transition: background-color 0.12s ease;
  /* the because-sign (two dots over one) as an SVG mask, so background-color
     still themes it (gray at rest, primary on hover/open). */
  --calc-because: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Ccircle cx='8' cy='9' r='2.3'/%3E%3Ccircle cx='16' cy='9' r='2.3'/%3E%3Ccircle cx='12' cy='16' r='2.3'/%3E%3C/svg%3E");
  -webkit-mask: var(--calc-because) center / contain no-repeat;
  mask: var(--calc-because) center / contain no-repeat;
}
.manuscriptwrapper .calc .step:hover > .hr-info-zone .hr-collapse::before,
.manuscriptwrapper .calc .step:focus-within > .hr-info-zone .hr-collapse::before {
  background-color: var(--primary-700);
}
.manuscriptwrapper .calc .step:not(.hr-collapsed) > .hr-info-zone .hr-collapse::before {
  background-color: var(--primary-600);
}
/* The chain has its own handrail, like a display equation. A labelled chain
   shows its number in the first row's info cell, above that row's mark. */
.manuscriptwrapper .calc.hr:hover .hr-info-stack .eqn-number p,
.manuscriptwrapper .calc.hr:focus .hr-info-stack .eqn-number p {
  color: var(--extra-dark);
  font-weight: var(--weight-semi);
}
/* Touch: give the because-mark a >=44px tap target. */
@media (hover: none) and (pointer: coarse) {
  .manuscriptwrapper .calc .step > .hr-info-zone .hr-collapse::after {
    content: "";
    position: absolute;
    top: 50%;
    left: 50%;
    width: 44px;
    height: 44px;
    transform: translate(-50%, -50%);
  }
}
@media (prefers-reduced-motion: reduce) {
  .manuscriptwrapper .calc .step > .hr-info-zone .hr-collapse::before {
    transition: none;
  }
}


/* Headings */
.manuscriptwrapper {

  h1, h2, h3, h4, h5 {
    font-family: var(--font-heading);
    line-height: var(--header-line-height);
  }

  h1 {
    font-size: var(--h1-size);
    font-weight: var(--weight-bold);
  }

  h2 {
    font-size: var(--h2-size);
    font-weight: var(--weight-semi);
  }

  h3 {
    font-size: var(--h3-size);
    font-weight: var(--weight-medium);
  }

  h4 {
    font-size: var(--h4-size);
    font-weight: var(--weight-medium);
  }

  h5 {
    font-size: var(--h5-size);
    font-weight: var(--weight-regular);
  }

  /* Margins around the heading but still inside the handrail */
  h1 {margin: calc(var(--h1-size) * 0.75) 0;}
  h2 {margin: calc(var(--h2-size) * 0.75) 0;}
  h3 {margin: calc(var(--h3-size) * 0.75) 0;}
  h4 {margin: calc(var(--h4-size) * 0.75) 0;}
  h5 {margin: calc(var(--h5-size) * 0.75) 0;}

  /* Margins around sections (before headings and after the last paragraph) */
  section {
    &.level-1 {margin-block: calc(var(--h1-size) * 1.5);}
    &.level-2 {margin-block: calc(var(--h2-size) * 1.5);}
    &.level-3 {margin-block: calc(var(--h3-size) * 1.5);}
    &.level-4 {margin-block: calc(var(--h4-size) * 1.5);}
    &.level-5 {margin-block: calc(var(--h5-size) * 1.5);}
  }

  /* Margins around sections in embedded mode */
  &.embedded section {
    &.level-1 {margin-block: calc(var(--h1-size) * 0.75);}
    &.level-2 {margin-block: calc(var(--h2-size) * 0.75);}
    &.level-3 {margin-block: calc(var(--h3-size) * 0.75);}
    &.level-4 {margin-block: calc(var(--h4-size) * 0.75);}
    &.level-5 {margin-block: calc(var(--h5-size) * 0.75);}
  }

  /* Margins between the heading and the first paragraph */
  .hr.heading {margin-bottom: var(--chunk-third);}

  /* Front-matter headings (abstract, table of contents) are emitted at semantic
   level 2 so the document outline never skips from the h1 title to an h3
   (WCAG 1.3.1 heading-order). But they are front matter, not numbered content
   sections, so they read as a quiet uppercase eyebrow label rather than as a
   section head: that distinguishes them from the h2 section heads and avoids
   colliding with the h3 subsection heads. Sized at h5 (not the body-sized h6) so
   the short label still has presence in the full-width handrail row. Semantic
   level and visual size are deliberately decoupled. (letter-spacing has no token
   in braiid; the em value follows the existing label convention.) */
  /* The heading row is a flex container, so its content-zone (the highlight box)
   stretches to the height of the taller control gutter. Center the short eyebrow
   label in that box so the highlight has equal space above and below it. */
  .abstract > .heading.hr > .hr-content-zone,
  .toc > .heading.hr > .hr-content-zone {
    display: flex;
    align-items: center;
  }
  .abstract > .heading.hr > .hr-content-zone > h2,
  .toc > .heading.hr > .hr-content-zone > h2 {
    font-size: calc(var(--h6-size) * 0.8);
    font-weight: var(--weight-semi);
    color: var(--text-subtle);
    text-transform: uppercase;
    letter-spacing: 0.08em;
    margin: 0;
  }

  /* Margins between most elements */
  .hr {margin-block-end: 1em;}
  .hr.mathblock, .hr.codeblock {margin-block-end: 0em;}
  .hr > .hr-content-zone > ol.enumerate,
  .hr > .hr-content-zone > ul.itemize {
    margin-block-end: 0;
    padding-inline-start: 1.5em;
  }

  /* Nested enumerate uses the LaTeX default marker per level: 1. then (a) then
     i. Each sub-list is its own <ol>, so the numbering restarts automatically. */
  ol.enumerate {list-style-type: decimal;}
  ol.enumerate ol.enumerate {list-style-type: lower-alpha;}
  ol.enumerate ol.enumerate ol.enumerate {list-style-type: lower-roman;}

  .proof .hr.statement, .proof .hr.paragraph {margin-block-end: 0.5em;}

  /* MathJax's own CSS gives a display equation `margin: 1em 0`. The last
   equation in a block drops its bottom margin so it does not double up with
   the following element's spacing. MathJax adds its CSS at runtime outside any
   layer, so only !important lets this layered rule win. */
  .mathblock:is(:last-child) mjx-container[jax="CHTML"][display="true"] {
    margin-block-end: 0 !important;
  }

}


/* Handrails: main flexbox */
.manuscriptwrapper {
  .hr {
    display: flex;

    .hr-collapse-zone {flex-shrink: 0;}
    .hr-menu-zone {position: absolute; display: none;}
    .hr-border-zone {flex-shrink: 0;}
    .hr-spacer-zone {flex-shrink: 0;}
    .hr-content-zone {flex-shrink: 1; flex-grow: 1; flex-basis: 0;}
    .hr-info-zone {padding: var(--chunk-sixth); flex-shrink: 0;}

    &.hr-offset {position: relative;}
    &.hr-offset > .hr-collapse-zone {position: absolute; left: calc(-3 * var(--hr-zone-width));}
    &.hr-offset > .hr-menu-zone {position: absolute; left: calc(-2 * var(--hr-zone-width));}
    &.hr-offset > .hr-border-zone {position: absolute; left: calc(-2 * var(--hr-zone-width));}
    &.hr-offset > .hr-spacer-zone {position: absolute; left: calc(-1 * var(--hr-zone-width));}
    &.hr-offset > .hr-info-zone {position: absolute; left: 100%;}

  }
}

/* With .subproof boxes pinned flush to the content column (see .hr-shift-1),
   every step box shares the same right edge, so the base rule
   `.hr-offset > .hr-info-zone { left: 100% }` lands every step number on one
   column at any nesting depth. The number sits in the margin at the box's right
   edge, so content never runs under it: no per-depth number offset and no
   content-zone reservation are needed. */

/* Handrails: individual elements -- collapse-zone */
.manuscriptwrapper .hr .hr-collapse-zone {
  .hr-collapse {
    /* A <button> for the keyboard and assistive tech; stripped back to the
       plain chevron the div used to be. */
    display: block;
    width: 100%;
    margin: 0;
    padding: 0;
    border: 0;
    background: none;
    color: inherit;
    font: inherit;
    text-align: inherit;
    cursor: pointer;

    position: relative;
    top: var(--chunk-sixth);
    border-radius: var(--radius-lg);
    opacity: 0%;

    .icon {padding-top: var(--chunk-sixth); padding-bottom: var(--chunk-sixth);}

  }
  /* Tabbing to the fold button reveals it (the hover/active reveals key off the
     block, not this descendant) and marks it like the other keyboard targets. */
  .hr-collapse:focus-visible {
    opacity: 100%;
    outline: var(--border-thin) solid var(--border-action);
    outline-offset: 1px;
  }
}
/* Handrails: individual elements -- border-zone */
.manuscriptwrapper .hr .hr-border-zone {
  .hr-border-dots {
    position: relative;
    top: var(--chunk-sixth);
    border-radius: var(--radius-lg);
    background: var(--surface-primary);
    color: var(--text-subtle);
    z-index: 1;

    .icon {padding-top: var(--chunk-sixth); padding-bottom: var(--chunk-sixth);}

  }
  .hr-border-rect {
    position: relative;
    top: calc(-2 * var(--hr-zone-width));
    left: calc(var(--chunk-sixth) - var(--border-thin) / 2);
    height: 100%;
    min-height: var(--chunk-sixth);
    border-left: var(--border-thin) solid var(--handrail-default);
  }
}
.manuscriptwrapper .hr.heading {
  .hr-border-rect {
    left: calc(var(--chunk-sixth) - var(--border-med) / 2);
    border-left: var(--border-med) solid var(--handrail-default);
  }
}
.manuscriptwrapper .hr.step .hr.paragraph .hr-border-zone {
  /* Put paragraphs' border zone last so if they are inside another element with a
     handrail, the other element's border zone appears first.  This happens e.g. in
     proof steps.
   */
  z-index: 0;
}
/* Handrails: individual elements -- menu-zone */
.manuscriptwrapper .hr .hr-menu-zone {

  .hr-menu {
    display: flex;
    flex-direction: column;
    gap: var(--chunk-twelfth);
    padding-block: var(--chunk-sixth);
    background: var(--surface-page);
    border: var(--border-extrathin) solid var(--border-primary);
    border-radius: var(--radius-lg);
    width: calc(2.5 * var(--chunk));
    box-shadow: var(--shadow-soft), var(--shadow-border);
  }

  .hr-menu-item, .hr-menu-label {
    display: flex;
    padding-inline: var(--chunk-quarter);
    gap: var(--chunk-sixth);
    align-items: center;

    .hr-menu-item-text {
      font-size: 0.875rem;
    }
  }

  .hr-menu-label .hr-menu-item-text {
    font-weight: var(--weight-semi);
  }

  .hr-menu-separator {
    border-top: var(--border-extrathin) solid var(--light);
    margin-block: var(--chunk-twelfth);
    width: calc(100% - var(--chunk-half));
    left: var(--chunk-quarter);
    position: relative;
  }

}
/* Handrails: individual elements -- spacer-zone */
.manuscriptwrapper .hr .hr-spacer-zone, .manuscriptwrapper .hr .hr-collapse-zone {
  .hr-spacer {
    height: 0;
    width: var(--chunk-third);
  }
}
/* Handrails: individual elements -- content-zone */
.manuscriptwrapper .hr .hr-content-zone {
  & > p:first-child {
    padding-top: var(--chunk-sixth);
  }
}
/* Handrails: individual elements -- info-zone */
.manuscriptwrapper .hr .hr-info-zone {
  .hr-info {
    min-width: 32px;
    height: 32px;
    display: flex;
    justify-content: center;
    align-items: center;
    position: relative;
    z-index: 1;

    .eqn-number p, .step-number p {
      font-size: 0.875rem;
      color: var(--text-subtle);
    }

    &:has(.eqn-number), &:has(.step-number) {justify-content: flex-start;}

    &:has(.eqn-number) {}

  }

  & .eqn-number, & .step-number {padding-bottom: var(--chunk-quarter);}

  /* Two items on one line of the column share one cell, stacked and centred
     (translator: _hr_info_zone). For example a step number over the number of
     the equation the step starts with, or a calc's number over its first
     row's because-mark. */
  & .hr-info.hr-info-stack {
    flex-direction: column;
    justify-content: center;
    height: auto;
    min-height: 32px;

    .eqn-number, .step-number {padding-bottom: 0;}
    .eqn-number p, .step-number p {margin: 0; line-height: 1.2;}
  }

}


/* Handrail hover */
.manuscriptwrapper .hr {
  & > .hr-border-zone .hr-border-dots, & > .hr-collapse-zone .hr-collapse {opacity: 0%;}
}
.manuscriptwrapper .hr:hover {
  & > .hr-border-zone .hr-border-dots, & > .hr-collapse-zone .hr-collapse {opacity: 100%;}
}

/* Touch has no hover, so the gutter controls would be invisible and unreachable.
   Under a no-hover, coarse-pointer environment, give every block a single
   control: drop the per-block collapse chevron (collapse/expand moves into the
   dots menu) and reveal the menu-dots at rest. The dots carry a page-coloured
   background so they BREAK the vertical rule instead of sitting on top of it,
   and a >=44px invisible ::before is the tap target (the glyph stays small).
   Desktop keeps the chevron and the hover reveal above. */
@media (hover: none) and (pointer: coarse) {
  .manuscriptwrapper .hr > .hr-collapse-zone .hr-collapse { display: none; }
  /* Remove the fold column itself, on every block. Hiding only the chevron
     left the column 0 wide in foldable blocks but 12px wide (its spacer) in
     the others, so their rails sat 12px apart. */
  .manuscriptwrapper .hr > .hr-collapse-zone { display: none; }
  /* And give the same 12px back on the right: the equation-number column
     drops its side padding (numbers still fit its 32px). */
  .manuscriptwrapper .hr > .hr-info-zone { padding-inline: 0; }
  /* Reveal the dots. At rest their background matches the page, so they break
     the vertical rule (occlude it) without reading as a pill. The visible
     rounded pill appears only while that block's menu is open. */
  .manuscriptwrapper .hr > .hr-border-zone .hr-border-dots {
    opacity: 1;
    background: var(--surface-page);
    color: var(--gray-900);
  }
  .manuscriptwrapper .hr.hr-menu-open > .hr-border-zone .hr-border-dots {
    background: var(--surface-hover);
  }
  .manuscriptwrapper .hr .hr-border-dots::before {
    content: "";
    position: absolute;
    top: 50%;
    left: 50%;
    width: 44px;
    height: 44px;
    transform: translate(-50%, -50%);
  }
  /* The dots sit near the left edge, so the menu opens to the RIGHT, flush to
     the handrail, or it clips off-screen. Mirror the global --menu-right
     override (which uses !important to beat the later base positioning rules). */
  .manuscriptwrapper .hr .hr-menu-zone .hr-menu { left: 18px !important; }
  .manuscriptwrapper .hr.hr-offset .hr-menu-zone .hr-menu { left: 18px !important; }
}
.manuscriptwrapper .hr.hr-hidden {
  & > .hr-border-zone {opacity: 0%;}
}
.manuscriptwrapper .hr.hr-hidden:hover {
  & > .hr-border-zone {opacity: 100%;}
}
/* Layout-only handrail for assets: reserves the content-zone gutter so the asset
   aligns with the reading column, but shows no chrome at all, even on hover. The
   empty zones carry no icons, so pin them to the standard zone width or the
   content-zone would start a notch to the left of body prose. */
.manuscriptwrapper .hr.hr-bare > .hr-border-zone { opacity: 0; }
.manuscriptwrapper .hr.hr-bare > .hr-collapse-zone,
.manuscriptwrapper .hr.hr-bare > .hr-border-zone,
.manuscriptwrapper .hr.hr-bare > .hr-spacer-zone {
  flex: 0 0 var(--hr-zone-width);
}
.manuscriptwrapper .step.hr:hover > .hr-info-zone p {
  color: var(--extra-dark);
  font-weight: var(--weight-semi);
}
.manuscriptwrapper .mathblock.hr:hover > .hr-info-zone p,
.manuscriptwrapper .mathblock.hr:focus > .hr-info-zone p,
.manuscriptwrapper .paragraph.hr:focus > .hr-content-zone > .mathblock > .hr-info-zone p {
  color: var(--extra-dark);
  font-weight: var(--weight-semi);
}
.manuscriptwrapper .mathblock > .hr-info-zone {
  display: flex;
  align-items: center;
}
/* Every block's content zone may shrink below the width of what it holds. As a
   flex item it would otherwise keep min-width: auto and grow to fit the widest
   thing inside it (an equation in a paragraph, a long formula), pushing the
   block past the column on narrow screens and when the sidebar panel narrows
   the column. Wide content then scrolls in its own box instead. */
.manuscriptwrapper .hr > .hr-content-zone {
  min-width: 0;
}

/* Wide display math scrolls inside its own box instead of running past the
   right edge and overlapping the number; the number lives in the sibling
   .hr-info-zone, outside this scroll container. */
.manuscriptwrapper .mathblock > .hr-content-zone {
  min-width: 0;
  overflow-x: auto;
  overflow-y: hidden;
}
.manuscriptwrapper .mathblock.hr-offset > .hr-info-zone {
  top: 0;
  bottom: 0;
  display: flex;
  align-items: center;
}


/* Handrail active */
/* Deliberately :focus, NOT :focus-visible. Clicking a block to highlight it is
   an intended interaction here, so the highlight must show for mouse clicks as
   well as keyboard focus. Keyboard focus is still always visible (WCAG 2.4.7 is
   satisfied either way); do not "fix" this to :focus-visible. */
.manuscriptwrapper .hr.active,
.manuscriptwrapper:not([data-selecting]) .hr:focus {
  /* reset the default behavior */
  outline: none;

  /* highlight EVERY nested handrail border */
  .hr-border-zone {
    /* The pill takes the rail's own colour. Light dots on it fail 3:1, so the
       dots are dark in both themes (the page colour is the dark one in dark). */
    .hr-border-dots {
      background: var(--handrail-active);
      color: light-dark(var(--text-body), var(--surface-page));
    }
    .hr-border-rect {border-left-color: var(--handrail-active);}
  }
  &.hr-collapsed > .hr-spacer-zone, &.hr-collapsed > .hr-border-zone > .hr-border-rect {
    border-bottom-color: var(--handrail-active) !important;
  }

  /* highlight ONLY the current active handrail's collapse and dots */
  & > .hr-border-zone > .hr-border-dots, & > .hr-collapse-zone > .hr-collapse {opacity: 100%}
  &.hr-hidden > .hr-border-zone, &.hr-hidden > .hr-collapse-zone {opacity: 100%}

  /* The active background is highlighted in zones but EXCEPT for collapse zone.
   Additionally, in the border zone we only paint the inner rectangle (hr-border-rect),
   otherwise the painted background would overflow the handrail border. */
  /* & > .hr-collapse-zone, */
  & > .hr-border-zone > .hr-border-rect,
  & > .hr-spacer-zone,
  & > .hr-content-zone,
  & > .hr-info-zone {
    /* Follow the accent (--primary-50) instead of a fixed blue. The 50 shade is
       the lightest in light mode and the darkest in dark mode, so a whole proof
       stays a quiet wash and text keeps its contrast in both themes. */
    background: var(--primary-50);
  }

  /* Most active areas include the info zone, except if the active area is a proof step statement */
  .hr-info-zone {
    border-top-right-radius: var(--radius-xl);
    border-bottom-right-radius: var(--radius-xl);
  }

  /* Colors when hovered */
  .hr-border-zone .hr-border-dots:hover {
    background: color-mix(in srgb, var(--handrail-active) 70%, white);
  }

}


/* Menu positioning and interaction. The in-zone z-index applies only for the
   instant the menu sits in its handrail zone, before handrails.js measures it
   and portals it to <body> (where the .hr-menu-portaled rules below take over). */
.manuscriptwrapper .hr .hr-menu-zone .hr-menu {
  position: relative;
  left: calc(-2.5 * var(--chunk) + var(--hr-zone-width));
  z-index: 1;
}
.manuscriptwrapper .hr.hr-offset .hr-menu-zone .hr-menu {
  position: relative;
  left: calc(-2.5 * var(--chunk));
}

/* The open menu is portaled to <body> (handrails.js) to escape ancestor stacking
   contexts that otherwise paint it behind the fixed proof-rail (z-index 40). Once
   on <body> it leaves .hr-menu-zone, so the zone-scoped appearance rules above no
   longer reach it: re-state the box, position, and z-index here. The popup is
   re-anchored with inline position:absolute + page-coordinate left/top from its
   measured in-zone rect, so it scrolls with the page yet sits above the rail. */
#hr-menu-singleton.hr-menu-portaled {
  position: static;
  /* On <body> the menu no longer inherits the manuscript's font-family, so it
     falls back to the browser serif default. Restate the document typeface. */
  font-family: var(--font-body);
}
#hr-menu-singleton.hr-menu-portaled .hr-menu {
  /* above the fixed proof-rail (40); below the source modal (2000)/toast (2100) */
  z-index: 1100;
  display: flex;
  flex-direction: column;
  gap: var(--chunk-twelfth);
  padding-block: var(--chunk-sixth);
  background: var(--surface-page);
  border: var(--border-extrathin) solid var(--border-primary);
  border-radius: var(--radius-lg);
  width: calc(2.5 * var(--chunk));
  box-shadow: var(--shadow-soft), var(--shadow-border);
}
/* The global icon-size rule (.manuscriptwrapper .icon svg) does not reach the
   body-level portal, so re-state it here or the menu icons render at their
   intrinsic (huge) size. */
#hr-menu-singleton.hr-menu-portaled .icon svg {
  width: var(--chunk-third);
  height: var(--chunk-third);
  display: block;
}
/* Item/label/separator layout and colors, mirroring the zone-scoped rules the
   portal escapes (see the .hr-menu-zone blocks above and below). */
#hr-menu-singleton.hr-menu-portaled .hr-menu-item,
#hr-menu-singleton.hr-menu-portaled .hr-menu-label {
  display: flex;
  padding-inline: var(--chunk-quarter);
  gap: var(--chunk-sixth);
  align-items: center;
}
#hr-menu-singleton.hr-menu-portaled .hr-menu-item .hr-menu-item-text,
#hr-menu-singleton.hr-menu-portaled .hr-menu-label .hr-menu-item-text {
  font-size: 0.875rem;
}
#hr-menu-singleton.hr-menu-portaled .hr-menu-label .hr-menu-item-text {
  font-weight: var(--weight-semi);
}
#hr-menu-singleton.hr-menu-portaled .hr-menu-separator {
  border-top: var(--border-extrathin) solid var(--light);
  margin-block: var(--chunk-twelfth);
  width: calc(100% - var(--chunk-half));
  left: var(--chunk-quarter);
  position: relative;
}
/* The .manuscriptwrapper .icon svg sizing also stops reaching the portaled menu,
   so its icons render at intrinsic (huge) size and balloon each row; re-state it.
   Mirrors the "Icons" rule. */
#hr-menu-singleton.hr-menu-portaled .icon svg {
  width: var(--chunk-third);
  height: var(--chunk-third);
  display: block;
}
#hr-menu-singleton.hr-menu-portaled .hr-menu-item {
  svg {stroke: var(--dark);}
  .hr-menu-item-text {color: var(--gray-800);}

  &.disabled {
    svg {stroke: var(--medium);}
    .hr-menu-item-text {color: var(--medium);}
  }

  &:hover:not(.disabled), &.active:not(.disabled) {
    background: var(--surface-hint);
    svg {stroke: var(--extra-dark);}
    .hr-menu-item-text {color: var(--gray-900);}
  }
}
.manuscriptwrapper .hr {
  .hr-border-dots:hover, .hr-collapse:hover {background: var(--surface-hint);}
  .hr-collapse:hover svg {stroke: var(--extra-dark);}
  .hr-menu-item:hover, .hr-menu-item.active {background: var(--surface-hint);}
}
.manuscriptwrapper .hr-menu-zone .hr-menu-item {
  svg {stroke: var(--dark);}
  .hr-menu-item-text {color: var(--gray-800);}

  &.disabled {
    svg {stroke: var(--medium);}
    .hr-menu-item-text{color: var(--medium);}
  }

  &:hover:not(.disabled), &.active:not(.disabled) {
    svg {stroke: var(--extra-dark);}
    .hr-menu-item-text{color: var(--gray-900);}
  }

}


/* Labeled handrails */
.manuscriptwrapper .hr.hr-labeled .hr-content-zone {
  .hr-label {
    padding-block: var(--chunk-sixth);
    border-bottom: var(--border-extrathin) solid var(--border-primary);
    margin-bottom: var(--chunk-sixth);

    span.label {font-weight: var(--weight-semi);}

  }
}

/* Labeled paragraphs */
.manuscriptwrapper .pg-label {
  font-weight: var(--weight-semi);
}


/* Collapsible handrails */
.manuscriptwrapper .hr.hr-collapsed  {

  /* Collapsed labeled blocks (theorem/proof/...) read as a short curved gutter
     tick, not a full-width rule. Drop the label's own underline so there is no
     long horizontal line for the curved bracket to fail to meet. */
  &.hr-labeled > .hr-content-zone > .hr-label {
    border-bottom: none;
    margin-bottom: 0;
  }

  & > .hr-border-zone > .hr-border-rect {
    border-bottom-left-radius: var(--chunk-half);
  }

  & > .hr-spacer-zone {
    border-bottom-left-radius: var(--chunk-twelfth);
  }

  & > .hr-spacer-zone, & > .hr-border-zone > .hr-border-rect {
    border-bottom: var(--border-med) solid var(--handrail-default);
  }

  &.hr-labeled > .hr-spacer-zone,
  &.hr-labeled > .hr-border-zone > .hr-border-rect,
  &.step > .hr-spacer-zone,
  &.step > .hr-border-zone > .hr-border-rect {
    border-bottom: var(--border-thin) solid var(--handrail-default);
  }

  & > .hr-collapse-zone > .hr-collapse {
    opacity: 100%;

    .icon.expand svg { stroke: var(--primary-700); }

  }

}

/* Proofs */
.manuscriptwrapper .hr {

  .hr-shift-1 {
    left: var(--chunk-half);
    /* As a flex item the shifted box grows back to full width and overflows
       its container's right edge by chunk-half at every nesting level,
       accumulating. Pin the width and stop the flex grow/shrink so each
       shifted box stays flush at the content column -- this is what keeps step
       numbers, highlights, and tombstones on one right-hand column at any
       depth, with no per-level offset hacks. !important is required to beat the
       flex defaults that otherwise restore the full width. */
    width: calc(100% - var(--chunk-half)) !important;
    flex-grow: 0 !important;
    flex-shrink: 0 !important;
    box-sizing: border-box !important;
  }

  &.subproof .hr-offset.hr.hr-hidden .hr-border-zone {opacity: 100%;}

  &.subproof > .hr-border-zone > .hr-border-rect {top: 0; width: var(--hr-zone-width)}

  &.subproof {margin-block-end: 0;}

  &.proof {position: relative;} /*to be able to position the Halmos*/

  /* The proof's own end-of-proof tombstone keys off its content-zone, not the
     proof box. The proof is hr-labeled (not hr-offset), so its info-zone stays
     in normal flex flow on the right; the proof box therefore runs past the
     content column that the offset steps' tombstones align to. Anchoring to the
     content-zone drops the proof tombstone into the same column as the steps. */
  &.proof > .hr-content-zone {position: relative;}
  &.proof:hover > .hr-content-zone::after,
  &.proof:focus > .hr-content-zone::after,
  &.proof .step:hover::after,
  &.proof .step:focus::after {
    content: "";
    position: absolute;
    background: var(--text-keyword);
    width: var(--chunk-sixth);
    height: var(--chunk-sixth);
    top: calc(100% - var(--chunk-sixth));
    border-radius: var(--radius-sm);
  }
  &.proof:hover > .hr-content-zone::after, &.proof:focus > .hr-content-zone::after,
  &.proof .step:hover::after, &.proof .step:focus::after {left: calc(100% + var(--chunk-sixth));}
  /* Tombstones ride the same flush right edge as the numbers (see .hr-shift-1),
     so the base rule above positions them at every depth; no per-level offset. */

  /* The fold label names what a folded block hides ("8 steps hidden"). The
     translator writes the words into data-fold-label. A step carries it on its
     content zone. A labelled block (proof, sketch, theorem) carries it on its
     .hr-label instead, because a proof's content-zone ::after is its tombstone. */
  &.step.hr-collapsed > .hr-content-zone[data-fold-label]::after,
  &.hr-labeled.hr-collapsed > .hr-content-zone > .hr-label[data-fold-label]::after {
    content: attr(data-fold-label);
    position: relative;
    left: var(--chunk-twelfth);
    top: var(--chunk-sixth);
    /* >= 4.5:1 on the page (--medium was ~2.05): the label stays a quiet
       gray but meets WCAG 1.4.3 AA. */
    color: var(--dark);
    font-style: italic;
    font-size: 0.875rem;
  }
  /* Inside .hr-label the label comes before the label's bottom padding, so move
     it down by that padding to land where a step's label does. The layout
     height is the same either way. */
  &.hr-labeled.hr-collapsed > .hr-content-zone > .hr-label[data-fold-label]::after {
    top: calc(var(--chunk-sixth) * 2);
  }

}


/* Prevent inline elements (math, references) followed by punctuation from being separated. */
.manuscriptwrapper .inline-wrapper {
  white-space: nowrap;
}


/* Icons */
.manuscriptwrapper .icon svg {
  width: var(--chunk-third);
  height: var(--chunk-third);
  display: block;
}

.manuscriptwrapper .tabler-icon {
  &.bookmark {fill: var(--information-500); }
  &.check {fill: var(--green-500);}
  &.success {fill: var(--success-500);}
  &.alert {fill: var(--warning-500); }
  &.exclamation {fill: var(--orange-500); }
  &.question {fill: var(--purple-500);}
  &.heart {fill: var(--red-500); }
  &.star {fill: var(--yellow-500); }
  &.quote {fill: var(--pink-500); }
  &.collapse {stroke: var(--dark); }
  &.error {fill: var(--error-500); }
}


/* TOC */
.manuscriptwrapper {

  /* TOC tree view: build-time dependency graph shown in place of the list */
  .toc svg.toc-tree {
    display: none;
  }

  .toc.tree svg.toc-tree {
    display: block;
    max-width: 100%;
    height: auto;
    overflow: visible;
  }

  .toc.tree ul.contents {
    display: none;
  }

  /* :orientation: horizontal emits both layouts. The left-to-right graph is too
     wide for a phone, so phones keep the vertical one. */
  .toc.tree svg.toc-tree-h {
    display: none;
  }

  @media (min-width: 768px) {
    .toc.tree svg.toc-tree-h {
      display: block;
    }

    .toc.tree svg.toc-tree-v {
      display: none;
    }
  }

  .toc-tree .toc-node rect {
    fill: var(--white, #fff);
    stroke: var(--gray-300, #d4dae3);
    stroke-width: 1.2;
  }

  .toc-tree .toc-node.level-1 rect {
    stroke-width: 1.6;
  }

  .toc-tree .toc-node .toc-secnum {
    /* user-unit size is set per-SVG by tocarcs.js so the on-screen size stays
       constant regardless of how the graph is scaled to fit. */
    font-size: var(--toc-label-px, 13.5px);
    font-weight: 600;
    fill: var(--gray-700);
  }

  .toc-tree .toc-node.level-1 .toc-secnum {
    fill: var(--text-body);
  }

  /* External named results a step depends on (lemmas, theorems). Tinted and
     dash-outlined in the accent color so they read as "proved elsewhere",
     distinct from the proof's own steps. */
  .toc-tree .toc-node-result rect {
    fill: var(--primary-50);
    stroke: var(--primary-500);
    stroke-width: 1.4;
    stroke-dasharray: 3 2;
  }

  .toc-tree .toc-node-result .toc-secnum {
    fill: var(--primary-700);
    font-weight: 600;
  }

  .toc-tree .toc-node a {
    text-decoration: none;
  }

  .toc-tree .toc-hover-label rect {
    fill: var(--white, #fff);
    stroke: var(--gray-300, #d4dae3);
    stroke-width: 1.2;
  }

  .toc-tree .toc-hover-label text {
    font-size: var(--toc-hover-px, 13px);
    font-weight: 600;
    fill: var(--text-body);
  }

  .toc-tree .toc-edge {
    fill: none;
    stroke: var(--primary-700);
    opacity: 0.75;
  }

  /* Forward pointers ("expanded later in...") are NOT prerequisites; keep them
     in a very gentle visual register so they never compete with the
     read-before structure. */
  .toc-tree .toc-edge.fwd {
    stroke: var(--gold, #b08a3e);
    stroke-dasharray: 3 5;
    stroke-width: 1 !important;
    opacity: 0.28;
  }

  /* Containment edges (read the enclosing section first) are real
     prerequisites, drawn as a quiet skeleton. */
  .toc-tree .toc-edge.struct {
    stroke: var(--gray-300, #d4dae3);
    opacity: 0.85;
  }

  /* A labelled root (the proof rail's "Goal") keeps the filled accent box. The
     TOC root carries no label and is a dot instead (see below), so these rules
     only reach the proof rail now. */
  .toc-tree .toc-node.level-0 rect {
    fill: var(--primary-700);
    stroke: var(--primary-700);
  }

  .toc-tree .toc-node.level-0 .toc-secnum {
    fill: var(--white, #fff);
    font-weight: 700;
  }

  /* The TOC graph root is a small muted dot, no box and no label. It is a UI
     control, so --text-subtle keeps it >= 3:1 against the page in both themes;
     it takes the accent colour on hover and keyboard focus. */
  .toc-tree .toc-node.level-0 circle.toc-root-dot {
    fill: var(--text-subtle);
    transition: fill 0.12s;
  }

  .toc-tree .toc-node.level-0:hover circle.toc-root-dot,
  .toc-tree .toc-node.level-0:focus-visible circle.toc-root-dot {
    fill: var(--primary-700);
  }

  .toc-tree .toc-arrowhead.dep { fill: var(--primary-700); }
  .toc-tree .toc-arrowhead.fwd { fill: var(--gold, #b08a3e); }

  /* Off the current path, edges fade to a faint web but node labels must stay
     readable. Fading the whole node group dropped the labels far below the 3:1
     UI-contrast floor (WCAG 1.4.11), so keep the node at full opacity and mute
     only its box, which still reads as off the path. */
  .toc-tree .toc-edge.toc-faded {
    opacity: 0.15;
    transition: opacity 0.15s;
  }
  .toc-tree .toc-node.toc-faded {
    opacity: 1;
  }
  .toc-tree .toc-node.toc-faded rect {
    opacity: 0.2;
    transition: opacity 0.15s;
  }

  .toc-tree .toc-node, .toc-tree .toc-edge {
    transition: opacity 0.15s;
  }

  /* ---- Reader sidebar (rsm build --sidebar) ----
     An icon strip in the left margin opens one view at a time in a panel beside
     it. The panel never covers the text: the column moves right (see the page
     layout rules at the end of this file). On phones the strip is a bottom bar
     and a view opens as a sheet above it. sidebar.js drives html.rail-open,
     html.rail-phone and the column geometry; prooftree.js the panel content. */
  .rail-strip {
    position: fixed;
    left: var(--rail-strip-left);
    top: var(--rail-top);
    z-index: 41;
    display: flex;
    flex-direction: column;
    gap: var(--chunk-twelfth);
    padding: var(--chunk-sixth) var(--chunk-twelfth);
    background: var(--surface-page);
    border: var(--border-extrathin) solid var(--border-primary);
    border-radius: var(--radius-xl);
    box-shadow: var(--shadow-soft);
  }
  .rail-strip-btn {
    position: relative;
    display: grid;
    place-items: center;
    width: calc(var(--chunk) - 2 * var(--chunk-twelfth) - 2 * var(--border-extrathin));
    height: calc(var(--chunk) - var(--chunk-quarter));
    padding: 0;
    border: 0;
    border-radius: var(--radius-lg);
    background: none;
    color: var(--text-subtle);
    font-family: var(--font-body);
    cursor: pointer;
  }
  .rail-strip-btn svg { width: 1.25rem; height: 1.25rem; }
  .rail-strip-btn:hover { color: var(--primary-700); background: var(--surface-hover); }
  /* A view this document cannot show (no structured proofs). Its tooltip says why. */
  .rail-strip-btn[aria-disabled="true"],
  .rail-strip-btn[aria-disabled="true"]:hover {
    color: var(--text-disabled);
    background: none;
    cursor: default;
  }
  .rail-strip-btn[aria-pressed="true"] { color: var(--primary-800); background: var(--primary-100); }
  .rail-strip-btn:focus-visible {
    outline: var(--border-thin) solid var(--border-action);
    outline-offset: 1px;
  }
  .rail-strip-action { height: calc(var(--chunk) - var(--chunk-half)); }
  .rail-strip-action svg { width: 1rem; height: 1rem; }
  .rail-strip-sep {
    display: block;
    margin: var(--chunk-twelfth) var(--chunk-sixth);
    border-top: var(--border-extrathin) solid var(--border-primary);
  }
  /* Desktop: one icon per view, icon only (the name is the tooltip). */
  .rail-strip-label { display: none; }
  .rail-strip-btn[data-mode="grouped"] { display: none; }

  /* The panel. Hidden until a view is open. */
  .proof-rail { display: none; }
  .proof-rail.active.open {
    display: flex;
    flex-direction: column;
    align-items: stretch;
    gap: var(--chunk-sixth);
    position: fixed;
    left: calc(var(--rail-strip-left) + var(--chunk) + var(--chunk-sixth));
    top: var(--rail-top);
    width: var(--rail-panel-width);
    max-height: calc(100vh - var(--rail-top) - var(--chunk-third));
    overflow: hidden;
    z-index: 40;
    padding: var(--chunk-quarter);
    background: var(--surface-page);
    border: var(--border-extrathin) solid var(--border-primary);
    border-radius: var(--radius-xl);
    box-shadow: var(--shadow-soft);
  }

  /* Visible keyboard focus for the panel's controls and the graph nodes. */
  .proof-rail :focus-visible,
  .toc-tree a:focus-visible {
    outline: var(--border-thin) solid var(--border-action);
    outline-offset: 2px;
  }

  /* Head: the view's name and what it shows. */
  .rail-head {
    display: flex;
    flex-direction: column;
    gap: var(--chunk-twelfth);
    flex-shrink: 0;
    padding: var(--chunk-twelfth) 0 var(--chunk-quarter);
    margin-bottom: var(--chunk-sixth);
    border-bottom: var(--border-extrathin) solid var(--border-primary);
  }
  .rail-title {
    margin: 0;
    font-family: var(--font-body);
    font-size: 1rem;
    font-weight: var(--weight-semi);
    line-height: 1.25;
    color: var(--text-body);
  }
  .rail-hint {
    margin: 0;
    font-size: 0.8125rem;
    line-height: 1.5;
    color: var(--text-subtle);
  }
  .rail-hint[hidden] { display: none; }

  /* A scope's two views switch with text tabs, used on phones only (the
     desktop strip has one icon per view). */
  .rail-subtabs { display: none; gap: var(--chunk-half); align-items: flex-end; }
  .rail-tab {
    min-height: 32px;
    padding: 0 0 var(--chunk-twelfth);
    border: 0;
    border-bottom: var(--border-thin) solid transparent;
    background: none;
    font-family: var(--font-body);
    font-size: 0.9375rem;
    font-weight: var(--weight-semi);
    color: var(--text-subtle);
    cursor: pointer;
  }
  .rail-tab[aria-selected="true"] {
    color: var(--primary-800);
    border-bottom-color: var(--primary-700);
  }
  .rail-tab:focus-visible { border-radius: var(--radius-md); }

  /* Reading: every option is a small preview tile. In the narrow desktop
     panel each label sits above its tiles; the phone sheet puts them side by
     side. */
  .rail-reading-panel {
    display: flex;
    flex-direction: column;
    gap: var(--chunk-quarter);
    padding: var(--chunk-sixth) var(--chunk-twelfth);
    overflow-y: auto;
  }
  .reading-row {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: var(--chunk-twelfth);
  }
  .reading-label {
    font-size: 0.75rem;
    font-weight: var(--weight-semi);
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--text-subtle);
  }
  .reading-opts { display: flex; flex-wrap: nowrap; gap: var(--chunk-twelfth); }
  .reading-opt {
    display: grid;
    place-items: center;
    width: 2.25rem;
    height: 2.25rem;
    padding: 0;
    border: var(--border-extrathin) solid var(--border-primary);
    border-radius: var(--radius-lg);
    background: none;
    color: var(--text-subtle);
    font-family: var(--font-body);
    line-height: 1;
    cursor: pointer;
  }
  .reading-opt svg { width: 1.25rem; height: 1.25rem; }
  .reading-opt:hover { color: var(--text-body); background: var(--surface-hover); }
  .reading-opt.active {
    color: var(--primary-800);
    background: var(--primary-50);
    border-color: var(--primary-700);
    box-shadow: inset 0 0 0 var(--border-extrathin) var(--primary-700);
  }
  .reading-opt:focus-visible { outline-offset: 1px; }
  .reading-ag { font-size: 1rem; }
  .reading-ag[data-face="sans"] { font-family: 'Source Sans 3', sans-serif; }
  .reading-ag[data-face="serif"] { font-family: 'Source Serif 4', serif; }
  .reading-ag[data-face="legible"] { font-family: 'Atkinson Hyperlegible', 'Source Sans 3', sans-serif; }
  .reading-a[data-size="1"] { font-size: 0.75rem; }
  .reading-a[data-size="2"] { font-size: 0.9375rem; }
  .reading-a[data-size="3"] { font-size: 1.125rem; }
  .reading-a[data-size="4"] { font-size: 1.375rem; }

  /* Scope routing: show only the active scope's sub-tabs and section. */
  .proof-rail.scope-document .rail-subtabs-proof,
  .proof-rail.scope-document .rail-proof,
  .proof-rail.scope-document .rail-pinned,
  .proof-rail.scope-document .rail-reading { display: none; }
  .proof-rail.scope-proof .rail-subtabs-document,
  .proof-rail.scope-proof .rail-document,
  .proof-rail.scope-proof .rail-pinned,
  .proof-rail.scope-proof .rail-reading { display: none; }
  .proof-rail.scope-reading .rail-subtabs-document,
  .proof-rail.scope-reading .rail-subtabs-proof,
  .proof-rail.scope-reading .rail-document,
  .proof-rail.scope-reading .rail-pinned,
  .proof-rail.scope-reading .rail-proof { display: none; }
  /* Pinned scope: only the pinned excerpt, no sub-tabs. */
  .proof-rail.scope-pinned .rail-subtabs-document,
  .proof-rail.scope-pinned .rail-subtabs-proof,
  .proof-rail.scope-pinned .rail-document,
  .proof-rail.scope-pinned .rail-proof,
  .proof-rail.scope-pinned .rail-reading { display: none; }
  /* The Pinned tab is always present (like Proof); its section shows an empty
     state until something is pinned, then swaps to the head + body. */
  .rail-pinned-empty {
    display: none;
    color: var(--gray-700);
    font-style: italic;
    font-size: 0.8rem;
  }
  .proof-rail:not(.has-pin) .rail-pinned-empty { display: block; }
  .proof-rail:not(.has-pin) .rail-pinned-head,
  .proof-rail:not(.has-pin) .rail-pinned-body { display: none; }

  .rail-pinned-head {
    display: flex;
    align-items: center;
    gap: var(--chunk-sixth);
    padding-bottom: var(--chunk-sixth);
    border-bottom: var(--border-thin) solid var(--border-primary);
    margin-bottom: var(--chunk-sixth);
  }
  /* With a block pinned, the panel's own title and hint go and the pinned
     block's row is the only header ("Pinned · Paragraph"), so the content
     starts higher. With nothing pinned the usual head stays. */
  .proof-rail.scope-pinned.has-pin .rail-head { display: none; }
  .proof-rail.scope-pinned.has-pin .rail-pinned { padding-top: var(--chunk-quarter); }
  .rail-pinned-title::before {
    content: "Pinned · " / "Pinned: ";
    font-weight: 400;
    color: var(--text-subtle);
  }
  .rail-pinned-title {
    flex: 1;
    min-width: 0;
    font-size: 0.8rem;
    font-weight: 600;
    color: var(--dark);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
  }
  .rail-pin-close {
    display: inline-flex;
    flex-shrink: 0;
    padding: 0;
    width: 1.4rem;
    height: 1.4rem;
    color: var(--dark);
    background: none;
    border: none;
    cursor: pointer;
  }
  .rail-pin-close:hover { color: var(--primary-700); }
  .rail-pin-close svg { width: 1rem; height: 1rem; }
  /* The pinned excerpt renders at rail width, no widening; long content scrolls. */
  .rail-pinned-body {
    min-height: 0;
    overflow-y: auto;
    font-size: 0.8rem;
  }

  .rail-section { display: flex; flex-direction: column; min-height: 0; overflow: hidden; }

  /* Document sub-tab routing. */
  .proof-rail.doc-view-map .rail-notation { display: none; }
  .proof-rail.doc-view-notation .rail-doc-map { display: none; }
  .rail-doc-map { display: flex; align-items: center; justify-content: center; min-height: 0; }

  .proof-rail-item { display: none; min-height: 0; }
  /* A little more breathing room below the sub-tabs than the row gap alone. */
  .proof-rail-item.shown {
    display: flex; flex-direction: column; min-height: 0;
    margin-top: var(--chunk-sixth);
  }

  /* Proof sub-tab routing: show only the active panel of the shown item. */
  .proof-rail.proof-view-map .rail-state { display: none; }
  .proof-rail.proof-view-state .rail-map { display: none; }
  .rail-map { display: flex; align-items: center; justify-content: center; min-height: 0; }

  /* Outside any proof the Proof scope has nothing live to show. */
  /* Outside any proof the panel lists the document's proofs to jump to. */
  .rail-proof-empty { display: none; font-size: 0.8rem; }
  .proof-rail.no-proof .rail-proof-empty { display: block; }
  .rail-proof-empty-hint { margin: 0; color: var(--gray-700); font-style: italic; }
  .rail-proof-list-head {
    margin: var(--chunk-third) 0 var(--chunk-twelfth);
    color: var(--text-subtle);
    font-size: 0.7rem;
    font-weight: 600;
    letter-spacing: 0.06em;
    text-transform: uppercase;
  }
  .rail-proof-list { list-style: none; margin: 0; padding: 0; }
  .rail-proof-link {
    display: flex;
    align-items: baseline;
    gap: var(--chunk-sixth);
    padding: var(--chunk-twelfth) var(--chunk-sixth);
    margin-inline: calc(-1 * var(--chunk-sixth));
    border-radius: var(--radius-md);
    color: light-dark(var(--primary-700), var(--primary-800));
    text-decoration: none;
    font-size: 0.85rem;
  }
  .rail-proof-link:hover { background: var(--surface-hover); text-decoration: none; }
  .rail-proof-link:focus-visible {
    outline: var(--border-thin) solid var(--border-action);
    outline-offset: 1px;
  }
  .rail-proof-meta {
    margin-left: auto;
    color: var(--text-subtle);
    font-size: 0.75rem;
    white-space: nowrap;
  }

  /* Collapsed proof: the rail shows a single node standing for the proof, not
     the step graph the page is hiding. The card itself expands the proof. */
  .proof-rail-item .rail-collapsed-card { display: none; }
  .proof-rail.proof-collapsed .proof-rail-item.shown .rail-map,
  .proof-rail.proof-collapsed .proof-rail-item.shown .rail-state { display: none; }
  .proof-rail.proof-collapsed .proof-rail-item.shown .rail-collapsed-card {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: var(--chunk-twelfth);
    width: 100%;
    padding: var(--chunk-quarter);
    text-align: left;
    font: inherit;
    background: var(--surface-primary);
    border: var(--border-extrathin) solid var(--border-primary);
    border-radius: var(--radius-lg);
    cursor: pointer;
  }
  .proof-rail.proof-collapsed .proof-rail-item.shown .rail-collapsed-card:hover {
    background: var(--primary-50);
    border-color: var(--primary-700);
  }
  .rail-collapsed-title { font-weight: 600; font-size: 0.875rem; color: var(--text-body); }
  .rail-collapsed-meta { font-size: 0.8rem; color: var(--dark); }  /* --text-subtle fails 4.5:1 on the rail surface (esp. dark); --dark passes */
  .rail-collapsed-cue { font-size: 0.8rem; color: var(--primary-700); }

  /* Notation pane: one rebind row per declared symbol. */
  .rail-notation {
    display: flex;
    flex-direction: column;
    gap: var(--chunk-quarter);
    min-height: 0;
    overflow-y: auto;
    padding: var(--chunk-twelfth) 0;
  }
  .rail-notation-row { display: flex; flex-direction: column; gap: var(--chunk-twelfth); }
  .rail-notation-label {
    font-size: 0.7rem;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--gray-700);
    font-weight: 700;
  }
  .rail-notation-edit { display: flex; align-items: center; gap: var(--chunk-sixth); }
  .rail-notation-actions { display: flex; align-items: center; gap: var(--chunk-sixth); }
  .rail-notation-apply {
    padding: var(--chunk-twelfth) var(--chunk-quarter);
    border: var(--border-extrathin) solid var(--primary-800);
    border-radius: var(--radius-lg);
    /* primary-50 on primary-800 so the label keeps WCAG AA in both themes: in
       dark mode primary-800 is a light accent, so a hard-coded #fff failed. */
    background: var(--primary-800);
    color: var(--primary-50);
    font-size: 0.82rem;
    font-weight: 600;
    cursor: pointer;
  }
  .rail-notation-apply:hover {
    background: var(--primary-900);
    border-color: var(--primary-900);
  }
  .rail-notation-input {
    flex: 1;
    min-width: 0;
    font-family: var(--font-mono);
    font-size: 0.82rem;
    padding: var(--chunk-twelfth) var(--chunk-sixth);
    border: var(--border-extrathin) solid var(--border-primary);
    border-radius: var(--radius-lg);
    background: var(--surface-page);
    color: var(--text-body);
  }
  .rail-notation-input:focus { outline: none; border-color: var(--primary-700); }
  /* Keyboard focus gets the same visible ring as the other rail controls. A
     bare border-color change is not a sufficient focus indicator (WCAG 2.4.7).
     Comes after the :focus rule so it wins for keyboard focus. */
  .rail-notation-input:focus-visible {
    outline: var(--border-thin) solid var(--border-action);
    outline-offset: 2px;
  }
  .rail-notation-input.invalid { border-color: var(--error-500); }
  .rail-notation-preview {
    min-width: 1.8em;
    text-align: center;
    color: var(--text-subtle);
    font-size: 0.9rem;
  }
  .rail-notation-reset,
  .rail-notation-locate {
    border: none;
    background: none;
    cursor: pointer;
    padding: var(--chunk-twelfth);
    /* These icons are the control's only label, so they need >= 3:1 against the
       rail (WCAG 1.4.11). gray-400 measured 1.88:1 in light mode. */
    color: var(--text-subtle);
    font-size: 1rem;
    line-height: 1;
  }
  .rail-notation-reset:hover,
  .rail-notation-locate:hover { color: var(--primary-700); }
  .rail-notation-locate { display: inline-flex; }
  .rail-notation-locate svg { width: 16px; height: 16px; }

  /* Gentle, temporary highlight on the symbol the reader located: a soft halo
     that fades in, holds, then fades out, so it's easy to spot without jarring.
     The box-shadow spread haloes even a small inline symbol without shifting
     layout. */
  /* Highlight the located symbol. Static (not animated) so it survives
     prefers-reduced-motion, and !important so it beats the math element's own
     background. The box-shadow spread haloes even a small inline symbol. */
  .notation-located {
    background-color: var(--yellow-300) !important;
    box-shadow: 0 0 0 4px var(--yellow-300);
    border-radius: 3px;
  }
  .rail-notation-empty {
    color: var(--gray-700);
    font-style: italic;
    font-size: 0.82rem;
  }

  /* Capped to the gutter box: a tree larger than this scales down (aspect
     preserved) to fit entirely; a smaller one stays at its natural size. No
     overflow, so the rail never shows a scrollbar. */
  .proof-rail svg.toc-tree {
    display: block;
    width: auto;
    height: auto;
    max-width: 100%;
    max-height: calc(90vh - 2.5rem);
    overflow: visible;
  }

  /* Label type is stabilized to a constant on-screen size by tocarcs.js (via
     --toc-label-px), so it stays readable whether the DAG is shrunk to fit the
     gutter or rendered large; it no longer tracks the graph's scale. */
  .proof-rail svg.toc-tree .toc-secnum { font-size: var(--toc-label-px, 13.5px); }

  /* Make only the node boxes 10% larger so the bumped type sits in them more
     comfortably. Scaled in place from each box's own centre (transform-box:
     fill-box), so node positions, edges, spacing, and the text are unchanged. */
  .proof-rail svg.toc-tree .toc-node rect {
    transform: scale(1.1);
    transform-origin: center;
    transform-box: fill-box;
  }

  /* Hover tooltip type bumped to match the nodes; the JS sizes its background
     box from getBBox, so the box re-fits the larger text automatically. */
  .proof-rail svg.toc-tree .toc-hover-label text { font-size: var(--toc-hover-px, 13px); }

  /* State view: live hypotheses and current goal, cloned from the body. */
  .rail-state {
    font-size: 0.82rem;
    color: var(--text-body);
    overflow: hidden; /* hard rule: no scrollbars */
    overflow-wrap: break-word;
  }
  .rail-state .hr-content-zone { width: auto; }
  .rail-state-block { margin-bottom: var(--chunk-half); }
  /* No trailing gap below the last band; the card padding alone sets the inset,
     matching the left/right padding instead of stacking a band gap on top. */
  .rail-state-block:last-child { margin-bottom: 0; }

  /* Band header is a full-width toggle button (keyboard- and AT-operable). */
  .rail-state-head {
    display: flex; align-items: center; justify-content: space-between;
    width: 100%; background: none; border: none; padding: 0;
    margin-bottom: var(--chunk-sixth); cursor: pointer;
  }
  .rail-state-label {
    font-size: 0.7rem;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--gray-700);
    font-weight: 700;
  }
  .rail-goal .rail-state-label { color: var(--primary-700); }
  /* Tabler chevron-down; rotates to point right when its band is collapsed. */
  .rail-state-caret {
    display: inline-flex;
    color: var(--gray-400);
    transition: transform 0.15s ease;
  }
  .rail-state-caret svg { width: 15px; height: 15px; display: block; }
  .rail-state-block.collapsed .rail-state-caret { transform: rotate(-90deg); }
  .rail-state-block.collapsed .rail-state-body { display: none; }

  .rail-goal .rail-state-body { color: var(--gray-900); }
  /* The accent rule is ONE continuous bar per band, parked at the gutter/content
     boundary, so it can never break between rows of differing height. */
  .rail-state-body { position: relative; }
  .rail-state-body::before {
    content: ""; position: absolute; top: 0; bottom: 0;
    left: var(--rail-num-width, 2.8ch);
    width: 2px; background: var(--gray-300);
  }
  .rail-hyps .rail-state-body::before { background: var(--primary-400); }
  .rail-goal .rail-state-body::before { width: 3px; background: var(--primary-600); }
  .rail-state-body ul {
    list-style: none; margin: 0; padding: 0;
    display: flex; flex-direction: column;
  }
  .rail-hyp-empty {
    color: var(--gray-400); font-style: italic;
    padding-left: calc(var(--rail-num-width, 2.8ch) + var(--chunk-sixth));
  }

  /* Every row jumps to where its item was introduced: the whole row is the
     mouse target, the leading keyword the keyboard control. A two-column grid
     reads "number │ content": the provenance number sits in a left gutter, the
     band's accent rule (drawn once on the body, above) runs at the column
     boundary, then the cloned construct. Markers align to the first text
     baseline. A faint wash on hover (or keyboard focus) is the only affordance. */
  .rail-jump-row {
    cursor: pointer;
    display: grid;
    grid-template-columns: var(--rail-num-width, 2.8ch) 1fr;
    align-items: baseline;
  }
  .rail-jump-control { cursor: pointer; }
  /* The hover/focus wash is the semantic hover surface, so it follows a
     document that recolors its surfaces instead of a fixed gray. */
  .rail-jump-row:hover,
  .rail-jump-row:focus-within { background: var(--surface-hover); }
  /* justify-self:end keeps the marker content-sized (not stretched to the
     column), so it stays right-aligned against the rule AND its measured
     scrollWidth is the true text width, letting the gutter shrink again after a
     deeply-nested proof. */
  .rail-state-num {
    justify-self: end;
    white-space: nowrap;
    padding: var(--chunk-twelfth) var(--chunk-sixth) var(--chunk-twelfth) 0;
    color: var(--gray-700);
    font-size: 0.78em;
    font-variant-numeric: tabular-nums;
    user-select: none;
  }
  /* A §n section marker is secondary provenance, not a step count: set it
     smaller and muted so it never reads as a stray ⟨n⟩ step number. The marker
     is aria-hidden, so the quieter --text-subtle color is fine here even though
     it would miss AA as body text. */
  .rail-state-num-section {
    font-size: 0.62em;
    color: var(--text-subtle);
  }
  .rail-jump-row > :not(.rail-state-num) {
    padding: var(--chunk-twelfth) 0 var(--chunk-twelfth) var(--chunk-sixth);
  }

  /* Demote the construct keyword to a quiet tag so the math reads loudest.
     --gray-700 (not --gray-600) keeps it quiet while clearing WCAG AA on the
     panel surface. */
  .rail-state .keyword {
    color: var(--gray-700);
    font-weight: 500;
    font-size: 0.85em;
    letter-spacing: 0.02em;
  }

  /* The goal preview: a clamped statement so the collapsed state still shows
     the opening words, with a quiet "show more" link below it. */
  .rail-goal-text.clamped {
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 2;
    overflow: hidden;
  }
  .rail-goal-toggle {
    display: block;
    margin-top: var(--chunk-twelfth);
    background: none; border: none; padding: 0; cursor: pointer; text-align: left;
    color: var(--primary-600); font-size: 0.78rem; text-decoration: underline;
  }

  .rail-state-empty { color: var(--gray-700); font-style: italic; font-size: 0.8rem; }

  /* Rail nodes are clickable (focus mode), so signal it. */
  .proof-rail-item .toc-node { cursor: pointer; }

  /* When a step is focused, everything outside its cone recedes hard and the
     cone path lights up bold blue so it reads as a single unambiguous route.
     Selectors match the specificity of the struct/fwd edge opacity rules above
     (and come later) so they actually win over them. */
  .toc-tree .toc-node.focus-faded,
  .toc-tree .toc-edge.focus-faded {
    opacity: 0.13;
  }

  .toc-tree .toc-edge.focus-lit {
    opacity: 1;
    stroke: var(--primary-700);
    stroke-dasharray: none;
    stroke-width: 2.4;
  }

  .toc-tree .toc-node.focus-lit rect {
    stroke: var(--primary-700);
    stroke-width: 1.8;
  }

  /* Active-step tracking: the step currently being read is filled to read as
     "you are here", distinct from the white default and the solid-blue root. */
  .toc-tree .toc-node.current-step rect {
    fill: var(--primary-100);
    stroke: var(--primary-700);
    stroke-width: 2;
  }
  .toc-tree .toc-node.current-step .toc-secnum {
    fill: var(--primary-700);
    font-weight: 700;
  }

  /* Proof DAG: the rail pins the current step's prerequisite cone, so the tree
     rests showing your path. Containment and reference edges share one blue (no
     blue-vs-gray puzzle); their stroke-width already sets them apart. Everything
     off the path fades to a faint web. The faded selectors are kept specific
     enough to beat the per-kind edge colour. */
  .rail-map .toc-tree .toc-edge,
  .rail-map .toc-tree .toc-edge.struct {
    stroke: var(--primary-600);
    opacity: 0.9;
  }
  /* Same split in the rail maps, where the acceptance is measured against the
     panel: faint edges, but off-path node labels stay at full opacity so they
     keep >= 3:1, with only the box muted. */
  .rail-map .toc-tree .toc-edge.toc-faded {
    opacity: 0.15;
  }
  .rail-map .toc-tree .toc-node.toc-faded {
    opacity: 1;
  }
  .rail-map .toc-tree .toc-node.toc-faded rect {
    opacity: 0.2;
  }

  /* The one way out of focus mode: a clickable bar in the rail (which is fixed
     on screen), stacked above the tree while focused. */
  .proof-rail.active.focusing {
    flex-direction: column;
    gap: var(--chunk-sixth);
  }

  /* The one way out of focus mode: a filled accent button so it reads clearly as
     the primary action of this transient mode. Tokens flip with the theme. */
  .proof-focus-exit {
    display: flex;
    align-items: center;
    gap: 0.4em;
    max-width: 100%;
    padding: var(--chunk-sixth) var(--chunk-quarter);
    font-size: 0.8rem;
    font-weight: var(--weight-semi);
    line-height: 1.2;
    color: var(--almost-white);
    background: var(--primary-600);
    border: var(--border-thin) solid var(--primary-600);
    border-radius: var(--radius-md);
    cursor: pointer;
    user-select: none;
  }

  .proof-focus-exit:hover,
  .proof-focus-exit:focus-visible {
    color: var(--almost-white);
    background: var(--primary-700);
    border-color: var(--primary-700);
  }

  .proof-focus-exit .proof-focus-back { font-size: 1.05em; line-height: 1; }
  .proof-focus-exit .proof-focus-show-all { font-weight: var(--weight-semi); }

  /* Dependency lens: a sticky, prose-anchored view of one step's cone, the dual
     of the rail's hover. Upstream ("what does this rest on") lights the route to
     the foundations and recedes the rest; downstream ("what rests on this")
     marks the dependents with an amber load fault-line WITHOUT dimming the rest,
     so the reader keeps reading them in place. Both directions compose. The
     amber is a fixed load colour (not the theme accent) so a dependent never
     reads as just another lit step. Marks use an inset shadow rather than a
     border so they never reflow the step or fight the offset handrail. */
  :is(.step, .theorem, .lemma, .corollary, .proposition).deplens-faded {
    opacity: 0.32;
    transition: opacity 160ms ease;
  }
  :is(.step, .theorem, .lemma, .corollary, .proposition).deplens-up {
    box-shadow: inset 3px 0 0 0 var(--primary-600);
    background: var(--primary-100);
    border-radius: var(--radius-md);
  }
  :is(.step, .theorem, .lemma, .corollary, .proposition).deplens-down {
    box-shadow: inset 3px 0 0 0 var(--yellow-600, #e28800);
    background: rgba(226, 136, 0, 0.07);
    border-radius: var(--radius-md);
  }
  /* The anchor reads strongest in either direction (wins over up/down by order). */
  :is(.step, .theorem, .lemma, .corollary, .proposition).deplens-anchor {
    box-shadow: inset 3px 0 0 0 var(--primary-700);
    background: var(--primary-100);
    border-radius: var(--radius-md);
    opacity: 1;
  }
  /* A downstream mark animates in once, a quiet "drain" from the anchor outward;
     skipped under reduced motion. */
  @media (prefers-reduced-motion: no-preference) {
    :is(.step, .theorem, .lemma, .corollary, .proposition).deplens-down {
      animation: deplens-drain 320ms ease both;
    }
  }
  @keyframes deplens-drain {
    from {
      background: rgba(226, 136, 0, 0);
      box-shadow: inset 0 0 0 0 transparent;
    }
  }

  /* Rail echo: the proof map mirrors the body lens. Same blue for upstream as
     focus uses, the amber load colour for downstream. */
  .toc-tree .toc-node.deplens-faded,
  .toc-tree .toc-edge.deplens-faded {
    opacity: 0.13;
  }
  .toc-tree .toc-edge.deplens-up {
    opacity: 1;
    stroke: var(--primary-700);
    stroke-dasharray: none;
    stroke-width: 2.4;
  }
  .toc-tree .toc-node.deplens-up rect {
    stroke: var(--primary-700);
    stroke-width: 1.8;
  }
  .toc-tree .toc-edge.deplens-down {
    opacity: 1;
    stroke: var(--yellow-600, #e28800);
    stroke-dasharray: none;
    stroke-width: 2.4;
  }
  .toc-tree .toc-node.deplens-down rect {
    stroke: var(--yellow-600, #e28800);
    stroke-width: 1.8;
  }
  .toc-tree .toc-node.deplens-anchor rect {
    fill: var(--primary-100);
    stroke: var(--primary-700);
    stroke-width: 2;
  }


  .toc-wrapper, .float-minimap-wrapper {
    display: flex;
    flex-direction: row;
    padding-left: var(--chunk);

    ul.contents {
      flex-grow: 1;
    }

    .minimap {
      width: var(--chunk);
      display: flex;
      justify-content: center;

      svg {
	    width: calc(2 * var(--chunk-third));
	    height: 100%;
	    display: block;
	    fill: var(--gray-200);
	    stroke: var(--gray-200);
      }
    }

  }

  .float-minimap-wrapper {

    .minimap {
      opacity: 50%;
      position: fixed;
      left: var(--chunk);
      top: 50%;
      transform: translateY(-50%);

      &:hover {opacity: 100%};

    }

  }

  ul.contents {
    list-style: none;
    padding-inline-start: 0;

    li {margin-block: 0.5rem;}

  }

  ul.contents ul.itemize {
    list-style: none;
    margin-block-end: 0;
    padding-inline-start: 1rem;
  }

}


/* Bibliography */
.manuscriptwrapper ol.bibliography {
  list-style: none;
  counter-reset: item 1;
  padding-inline-start: var(--chunk);
  font-size: 0.85em;

  li {margin-block: 0.75rem;}

}

.manuscriptwrapper :is(ol, ul).bibliography {list-style: none; padding-inline-start: 0;}

.manuscriptwrapper div.bibliography {
  font-size: 0.92em;
}

/* Each entry is a card: authors and year with the DOI in the top right
   corner, then the title, then the venue with the links back to the citations
   flush right. The
   card background, the DOI and the back links show on hover or focus, so the
   list reads as plain text at rest; without hover (touch) they always show.
   Numeric styles put the number in a pill like the footnote numbers. */
.manuscriptwrapper .bibitem > .hr {margin-bottom: .25em;}
.manuscriptwrapper .bib-card {
  display: grid;
  grid-template-columns: 1fr;
  column-gap: .3em;
  border-radius: 8px;
  padding: .45em .7em;
  transition: background-color .15s;
}
.manuscriptwrapper .bib-card:has(> .bib-num) {grid-template-columns: 2.4em 1fr;}
.manuscriptwrapper .bib-num {
  justify-self: start;
  align-self: start;
  margin-top: .3em;
  font-size: .8em;
  line-height: 1;
  padding: .18em .4em;
  border-radius: 4px;
  font-weight: 600;
  color: var(--primary-700);
  background: color-mix(in srgb, var(--primary-700) 12%, var(--surface-page));
}
.bib-body {min-width: 0;}
.bib-who {font-weight: 600;}
.bib-line {display: flex; align-items: baseline; gap: 1.25em;}
.bib-line > :first-child {flex: 1; min-width: 0;}
.bib-title {font-style: italic;}
.bib-venue {color: var(--text-subtle); font-size: .92em;}
.bib-line > .bibitem-doi, .bib-line > .bibitem-url, .bib-cited {
  flex: none;
  font-size: .85em;
  line-height: 1;
  white-space: nowrap;
}
.bib-cited sup {line-height: 0;}
.bib-cited a.backlink + a.backlink {margin-left: .15em;}
/* On a narrow screen the right-hand spots crowd long author lists, so the DOI
   and the back links share one row at the end of the entry. */
@media (max-width: 600px) {
  .bib-body {display: flex; flex-wrap: wrap; align-items: baseline; column-gap: 1.1em;}
  .bib-body > .bib-line {display: contents;}
  .bib-body > .bib-line > :first-child,
  .bib-body > .bib-title {flex: 1 1 100%;}
  .bib-who {order: 1;}
  .bib-title {order: 2;}
  .bib-venue {order: 3;}
  .bib-body .bibitem-doi, .bib-body .bibitem-url {order: 4;}
  .bib-cited {order: 5;}
}
@media (hover: hover) {
  .manuscriptwrapper :is(.bib-line > .bibitem-doi, .bib-line > .bibitem-url, .bib-cited) {opacity: 0; transition: opacity .15s;}
  /* Hover, or keyboard focus inside the entry. A click focuses the handrail,
     which has its own look, so plain focus does not light the card. */
  .manuscriptwrapper .bibitem:is(:hover, :has(:focus-visible)) .bib-card {background: var(--surface-primary);}
  .manuscriptwrapper .bibitem:is(:hover, :has(:focus-visible)) .bib-who {color: var(--primary-700);}
  .manuscriptwrapper .bibitem:is(:hover, :focus-within) :is(.bib-line > .bibitem-doi, .bib-line > .bibitem-url, .bib-cited) {opacity: 1;}
}


/* Highlighting */
.manuscriptwrapper .hilite {
  background-color: var(--text-hilite-bg);
}


/* Figures */
.manuscriptwrapper figure {
  margin-block: var(--h2-size);
  text-align: center;
  max-width: 100%;
  overflow-x: auto;
  /* Clip the vertical axis so overflow-x:auto does not force overflow-y to auto
     and grow a spurious vertical scrollbar on a 1px overflow (a tall inline
     fraction in the caption, a child margin, a focus ring). overflow-x stays so
     the figure keeps its block formatting context (dropping it collapses the
     child margins and shifts the layout) and wide content can still scroll. */
  overflow-y: hidden; }
.manuscriptwrapper figure > *:not(figcaption) {
  max-width: 100%;
  overflow-x: auto; }
/* Captions of every captioned block (figure, widget, video, table): small,
   unboxed, subtle text, with the label ("Figure 2.3.") bold in the body colour
   on its own line. The same look under the block and in the margin column. */
.manuscriptwrapper :is(figure, .table-wrapper) > figcaption {
  text-align: left;
  margin-block: var(--chunk-third); }
.manuscriptwrapper :is(figure, .table-wrapper) > figcaption > .hr-content-zone {
  font-size: 0.84em;
  line-height: 1.5;
  color: var(--text-subtle);
  text-align: left; }
/* The focus wash is too dark behind the subtle colour for AA contrast. */
.manuscriptwrapper :is(figure, .table-wrapper) > figcaption:is(.active, :focus) > .hr-content-zone {
  color: var(--text-body); }
.manuscriptwrapper :is(figure, .table-wrapper) > figcaption .label {
  display: block;
  font-weight: var(--weight-bold);
  color: var(--text-body); }

/* The caption's handrail lives in the left gutter beside its block, spanning
   the block's height (captions.js sets the --cap-rail-* offsets, measured from
   the caption's own box). Its empty collapse and spacer zones give way to
   padding, so the text keeps its column. */
@media screen {
  .manuscriptwrapper .manuscript .hr.rsm-cap-linked {
    position: relative;
    padding-left: calc(3 * var(--hr-zone-width));
  }
  .manuscriptwrapper .manuscript .hr.rsm-cap-linked > .hr-collapse-zone,
  .manuscriptwrapper .manuscript .hr.rsm-cap-linked > .hr-spacer-zone { display: none; }
  .manuscriptwrapper .manuscript .hr.rsm-cap-linked > .hr-border-zone,
  .manuscriptwrapper .manuscript .hr.rsm-cap-linked > .hr-menu-zone {
    position: absolute;
    top: var(--cap-rail-y, 0px);
  }
  .manuscriptwrapper .manuscript .hr.rsm-cap-linked > .hr-border-zone {
    left: calc(var(--cap-rail-x, 0px) + var(--hr-zone-width));
    width: var(--hr-zone-width);
    height: var(--cap-rail-h, 100%);
  }
  .manuscriptwrapper .manuscript .hr.rsm-cap-linked > .hr-menu-zone { left: var(--cap-rail-x, 0px); }
  .manuscriptwrapper .manuscript .hr.rsm-cap-linked.hr-offset .hr-menu-zone .hr-menu {
    left: calc(-2.5 * var(--chunk) + var(--hr-zone-width));
  }
  /* Hovering or focusing the block shows the caption's rail too, so the rail
     reads as the block's. */
  .manuscriptwrapper .manuscript :is(figure, .table-wrapper):is(:hover, :focus-within) > .hr.rsm-cap-linked {
    & > .hr-border-zone { opacity: 100%; }
    & > .hr-border-zone .hr-border-dots { opacity: 100%; }
  }
}
/* On touch there is no fold column (see the touch block above), so a caption's
   rail is one column in, like every other block's. */
@media screen and (hover: none) and (pointer: coarse) {
  .manuscriptwrapper .manuscript .hr.rsm-cap-linked { padding-left: calc(2 * var(--hr-zone-width)); }
  .manuscriptwrapper .manuscript .hr.rsm-cap-linked > .hr-border-zone { left: var(--cap-rail-x, 0px); }
}

/* The margin column (margin.js decides where each item goes; sidebar.js sets
   html.rsm-margin when the column fits). The page widens by the column, the
   text keeps its width, and items are placed in the column at their anchors.
   Screen only: print keeps captions under their blocks. */
:root {
  --rsm-margin-width: 15rem;
  --rsm-margin-gap: 2rem;
}
@media screen {
  html.rsm-margin main.manuscriptwrapper {
    padding-right: calc(var(--side-padding) + var(--rsm-margin-gap) + var(--rsm-margin-width));
  }
  html.rsm-margin .manuscriptwrapper .manuscript .rsm-margin-item {
    position: absolute;
    width: var(--rsm-margin-width);
    margin: 0;
    padding-left: 0;
  }
  /* Beside the text the caption's empty number zone would only narrow it. */
  html.rsm-margin .manuscriptwrapper .manuscript .hr.rsm-margin-item > .hr-info-zone { display: none; }
}
/* A <video> keeps its intrinsic pixel width otherwise and overflows the figure
   on a narrow screen (rsm-29p). height:auto keeps the aspect ratio. */
.manuscriptwrapper figure video {
  max-width: 100%;
  height: auto; }


/* Other */
.manuscriptwrapper .keyword {
  color: var(--text-keyword);
  font-weight: var(--weight-semi); }
/* Author block */
.manuscriptwrapper .author-block {
  margin-top: 1.5rem;
  margin-bottom: 3rem; }

.manuscriptwrapper .author-names {
  font-size: 1.0625rem;
  font-weight: var(--weight-medium);
  color: var(--text-body);
  line-height: var(--body-line-height);
  margin: 0; }

.manuscriptwrapper .author-names sup {
  font-weight: var(--weight-regular);
  color: var(--text-subtle);
  cursor: pointer; }

.manuscriptwrapper .author-details {
  margin-top: 0.75rem; }

.manuscriptwrapper .author-details summary {
  font-size: 0.875rem;
  font-weight: var(--weight-regular);
  color: var(--text-subtle);
  cursor: pointer;
  list-style: none;
  display: inline-flex;
  align-items: center;
  gap: 0.25em; }

.manuscriptwrapper .author-details summary::marker,
.manuscriptwrapper .author-details summary::-webkit-details-marker {
  display: none; }

.manuscriptwrapper .author-details summary .icon {
  display: inline-flex;
  transition: transform 0.15s ease; }

.manuscriptwrapper .author-details[open] summary .icon {
  transform: rotate(180deg); }

.manuscriptwrapper .author-details summary:hover {
  color: var(--text-body); }

.manuscriptwrapper .author-affiliations {
  list-style: none;
  padding-left: 0;
  margin: 0.75rem 0 0 0; }

.manuscriptwrapper .author-affiliations li {
  font-size: 0.875rem;
  font-weight: var(--weight-regular);
  color: var(--text-subtle);
  line-height: 1.4;
  margin-bottom: 0.25rem; }

.manuscriptwrapper .author-affiliations li sup {
  margin-right: 0.25em; }

.manuscriptwrapper .author-correspondence {
  font-size: 0.875rem;
  color: var(--text-subtle);
  margin-top: 0.75rem; }

.manuscriptwrapper .author-correspondence a {
  color: var(--link-default); }

.manuscriptwrapper .author-correspondence a:hover {
  color: var(--link-hover); }

.manuscriptwrapper .author-note {
  font-size: 0.875rem;
  color: var(--text-subtle);
  margin: 0.25rem 0; }

.manuscriptwrapper .author-orcid {
  font-size: 0.875rem;
  color: var(--text-subtle);
  margin: 0.25rem 0; }

/* Manuscript metadata */
.manuscriptwrapper .manuscript-date {
  font-size: 0.9375rem;
  color: var(--gray-700);
  margin-block: 0.5rem 1.5rem;
  margin-inline-start: var(--chunk); }

/* Abstract keywords and MSC */
.manuscriptwrapper .abstract .keywords,
.manuscriptwrapper .abstract .msc {
  font-size: 0.875rem;
  font-style: italic;
  color: var(--gray-700);
  margin-block: 0.75rem;
  margin-inline-start: var(--chunk); }


/* Tables */
.manuscriptwrapper .table-wrapper {
  margin-block: 1em;
  overflow-x: auto;
  /* Clip the vertical axis so overflow-x:auto does not force overflow-y to auto
     and grow a spurious vertical scrollbar; wide tables still scroll sideways.
     (As on the mathblock zone: alongside overflow-x:auto, this is the hidden the
     spec downgrades clip to.) */
  overflow-y: hidden;
  text-align: center; }
.manuscriptwrapper .table-wrapper > table {
  display: inline-table; }
.manuscriptwrapper table.table {
  border-collapse: collapse; }
.manuscriptwrapper tbody td.tabledatum {
  padding-inline: 1rem;
  padding-block: 0.5rem;
  border-bottom: var(--border-extrathin) solid var(--gray-300); }
.manuscriptwrapper thead.tablehead {
  background: var(--gray-200); }
.manuscriptwrapper thead.tablehead td.tabledatum {
  padding-inline: 1rem;
  padding-block: 0.75rem;
  border-top: var(--border-thin) solid var(--gray-600);
  border-bottom: var(--border-thin) solid var(--gray-600); }
.manuscriptwrapper table.table:not(:has(thead)) tbody tr:first-child td.tabledatum {
  border-top: var(--border-thin) solid var(--gray-600); }
.manuscriptwrapper tbody tr:last-child td.tabledatum {
  border-bottom: var(--border-thin) solid var(--gray-600); }

/* Hyperlinks */
.manuscriptwrapper {

  a:link, a:visited, a:active {
    text-decoration: none;
    color: var(--link-default);
    cursor: pointer;
    /* In-text links carry no underline, so without a second cue they rely on color
    alone to stand out from body text, which fails WCAG 1.4.1 (the link-vs-text
    contrast is under 3:1). Semi-bold is that non-color cue (rsm-1ue). */
    font-weight: 600;
    /* Links stay inline so a long link wraps between words along with the surrounding
    text instead of moving to its own line as one box (rsm-1ue). The padding grows the
    hit area to a 24px touch target and the matching negative margin keeps that padding
    from pushing the neighbouring lines apart. overflow-wrap lets a bare URL break
    mid-string instead of overflowing the line. */
    padding-block: 0.15em;
    margin-block: -0.15em;
    overflow-wrap: anywhere;

    &.reference.backlink, &.reference.cite {padding-inline: 0.05em;}
    &.reference.backlink {font-size: 0.875rem;}
    & > .icon.ext > svg {stroke: var(--link-default);}

  }

  .nowrap {
    white-space: nowrap;
  }

  a:hover {
    text-decoration: underline;
    color: var(--link-hover);

    & > .icon.ext > svg {stroke: var(--link-hover);}

  }

  a:focus {
    outline: none;              /* reset the default */
    box-shadow: 0 var(--border-thin) 0 var(--border-action);
    background: var(--surface-information);
    color: var(--information-700);
  }

  a:focus:has(.icon.ext) {
    outline: var(--border-thin) solid var(--information-500);
    box-shadow: unset;
    border-radius: var(--radius-md);
    outline-offset: var(--chunk-twelfth);
  }

}


/* Toast notifications */
/* Modified from https://codepen.io/jrsmiffy/pen/eYYwrap */
.toast {
  display: flex;
  align-items: center;
  gap: var(--chunk-sixth);

  /* The status icon is <svg class="icon">, which the global ".icon svg" rule
     does not match (that rule targets an svg inside a .icon, not an svg that is
     itself .icon). Size it here so every toast style, now and later, renders a
     normal icon instead of the svg's intrinsic (huge) size. */
  & svg.icon {
    width: var(--chunk-third);
    height: var(--chunk-third);
  }

  color: var(--extra-dark);
  border-width: var(--border-thin);
  border-style: solid;
  margin: auto;
  width: max-content;
  padding-block: var(--chunk-sixth);
  padding-inline: var(--chunk-third);
  border-radius: var(--radius-xl);
  position: fixed;
  z-index: 1;
  left: 0;
  right: 0;
  bottom: calc(2 * var(--chunk-third));
  white-space: nowrap;
  overflow: hidden;
  animation: fadein 0.5s, stay 3s 1s, fadeout 0.5s 4.5s;

  & .close {
    padding: var(--chunk-sixth);
    border-radius: var(--radius-lg);
    svg {stroke: var(--almost-black)};
  }

  & .close:hover {
    cursor: pointer;
    background: var(--light);
  }

  & .spacer {
    display: inline-block;
    width: var(--chunk-third);
  }

  & .bg {
    position: absolute;
    width: 100%;
    height: 100%;
    top: 0;
    left: 0;
    border-radius: calc(var(--radius-xl) - var(--border-thin));
    z-index: -1;
    animation: shrinkleft 5s;
  }

  &.information {
    background-color: var(--surface-information);
    border-color: var(--border-information);

    & .bg {background-color: var(--information-100);}

  }

  &.success {
    background-color: var(--surface-success);
    border-color: var(--border-success);

    & .bg {background-color: var(--success-100);}

  }

  &.error {
    background-color: var(--surface-error);
    border-color: var(--border-error);

    & .bg {background-color: var(--error-100);}

  }

}
@keyframes fadein {
  from {bottom: 0; opacity: 0;}
  to {bottom: calc(2 * var(--chunk-third)); opacity: 1;}
}
@keyframes stay {}
@keyframes fadeout {
  from {bottom: calc(2 * var(--chunk-third)); opacity: 1;}
  to {bottom: calc(4 * var(--chunk-third)); opacity: 0;}
}
@keyframes shrinkleft {
  from {width: 100%;}
  to {width: 0%;}
}


/* RSM predefined types */
.manuscriptwrapper {
  .tiny {font-size: 0.5rem;}
  .smallest {font-size: 0.7rem;}
  .smaller {font-size: 0.8rem;}
  .small {font-size: 0.9rem;}
  .normal {font-size: 1rem;}
  .large {font-size: 1.2rem;}
  .larger {font-size: 1.44rem;}
  .largest {font-size: 1.7281rem;}
  .huge {font-size: 2.0741rem;}
  .huger {font-size: 2.488rem;}
  .draft {color: var(--primary-600);}
}

/* Footnote markers: a small tinted chip with the number. The selector repeats the
link states so it wins over the general link rules above, whose padding, negative
margin and focus colors would otherwise apply. The number at the start of an entry
of the notes list uses the same chip at rest so the reader can match the two.
Asides have no marker: their call is the phrase they are about (.aside-phrase). */
.manuscriptwrapper {

  a.note-marker:link, a.note-marker:visited, a.note-marker:active,
  .note__number {
    display: inline-block;
    font-size: .68em;
    line-height: 1;
    vertical-align: .45em;
    padding: .08em .32em;
    margin: 0 0 0 .15em;
    border-radius: 4px;
    color: var(--primary-700);
    background: color-mix(in srgb, var(--primary-700) 12%, var(--surface-page));
    text-decoration: none;
  }

  .note__number {
    margin-left: 0;
    font-weight: 600;
  }

  a.note-marker:hover, a.note-marker:focus, a.note-marker:focus-visible {
    color: var(--surface-page);
    background: var(--primary-700);
    text-decoration: none;
    box-shadow: none;
    outline: none;
  }

  /* The notes list. Each entry starts with its number chip, so the list draws
  no numbers of its own. The <li> value attributes keep the numbers right when
  the list is read without this CSS. */
  ol.noteslist {
    list-style: none;
    padding-inline-start: 0;
    margin-block: 0;
    font-size: 0.92em;

    li.note {margin-block: 0.75rem;}
    li.note p {margin-block: 0;}
  }

  .note-preview p {margin-block: 0;}

}

/* Asides: a tinted card with no border, no rail of its own (a vertical line is
kept for handrails) and no number. The HTML puts each aside at the end of the
block that calls it, so the block's handrail covers it. Without scripts it stays
there, open. asides.js sets html.has-asides (the builder's pre-paint script sets
it before first paint), and then an aside is either in the margin column
(html.rsm-margin, placed by margin.js) or closed until its phrase opens it.
Screen only: print shows every aside below its block. */
/* --text-subtle alone is below AA contrast on the tint, so the text is moved
   halfway to the body colour. In dark mode even that reads dim, and a lit aside
   dropped near 4.5:1, so asides there take the body colour. */
:root { --aside-text: light-dark(color-mix(in srgb, var(--text-subtle) 50%, var(--text-body)), var(--text-body)); }
.manuscriptwrapper .aside {
  background: color-mix(in srgb, var(--primary-700) 9%, var(--surface-page));
  border-radius: 8px;
  padding: .65em .85em;
  margin: var(--chunk-third) 0;
  /* em, as captions use: the root font is smaller than body text. */
  font-size: .84em;
  line-height: 1.5;
  color: var(--aside-text);
  text-align: left;
  transition: background-color .15s;

}
/* The same spacing in the hover preview of a closed aside. */
.manuscriptwrapper :is(.aside, .aside-preview) {
  & > p { margin: 0; }
}

/* The phrase an aside is about: body text with a faint dotted accent underline,
no chip and no number. The selector repeats the link states so it wins over the
general link rules above (link colour, semi-bold, focus colours). The tint is
clipped to the text so the padding that grows the tap target does not show. */
.manuscriptwrapper a.aside-phrase:is(:link, :visited, :active, :hover, :focus) {
  color: inherit;
  font-weight: inherit;
  /* The dotted line is a background, not a text underline: math inside the
     phrase is drawn as inline-blocks, which a text underline does not reach. */
  text-decoration: none;
  background-color: transparent;
  /* Full strength in dark mode: at 70% the dots fall to about 3:1 on a dark page. */
  background-image: linear-gradient(to right, light-dark(color-mix(in srgb, var(--primary-700) 70%, transparent), var(--primary-700)) 50%, transparent 50%);
  background-size: 4px 1.5px;
  background-repeat: repeat-x;
  background-origin: content-box;
  background-position: 0 100%;
  background-clip: content-box;
  box-decoration-break: clone;
  -webkit-box-decoration-break: clone;
  border-radius: 3px;
  box-shadow: none;
  outline: none;
  transition: background-color .15s;
}
.manuscriptwrapper a.aside-phrase:focus-visible {
  outline: 2px solid var(--border-action);
  outline-offset: 2px;
}

/* Linked hover and focus: the phrase and its aside light up together, with the
same tint. */
.manuscriptwrapper .aside.rsm-aside-lit,
.manuscriptwrapper a.aside-phrase.rsm-aside-lit:is(:link, :visited, :active, :hover, :focus) {
  background-color: color-mix(in srgb, var(--primary-700) 18%, var(--surface-page));
}
/* In dark mode 18% barely shows behind the phrase, so the phrase takes a
   stronger tint. The aside keeps 18%: its subtle text would fall below 4.5:1. */
.manuscriptwrapper a.aside-phrase.rsm-aside-lit:is(:link, :visited, :active, :hover, :focus) {
  background-color: light-dark(color-mix(in srgb, var(--primary-700) 18%, var(--surface-page)), color-mix(in srgb, var(--primary-700) 32%, var(--surface-page)));
}

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

@media screen {
  html.has-asides:not(.rsm-margin) .manuscriptwrapper .aside:not(.rsm-aside-open) {
    display: none;
  }
  html.has-asides:not(.rsm-margin) .manuscriptwrapper a.aside-phrase {
    cursor: pointer;
  }
  /* Touch, with the aside below the paragraph: tapping the phrase opens it, so
  the phrase gets a tap target at least 44px tall. The glyph box is always taller
  than 1em, so 1em plus this padding is enough. Inline padding does not move the
  lines around it. */
  @media (hover: none) and (pointer: coarse) {
    html.has-asides:not(.rsm-margin) .manuscriptwrapper a.aside-phrase {
      padding-block: max(.15em, calc(22px - .5em));
    }
    /* That padding reaches into the lines above and below. Any other control
    there (a footnote marker, a reference) must stay on top of it, or tapping
    it opens the aside instead. Positioning does not move inline content. */
    html.has-asides:not(.rsm-margin) .manuscriptwrapper :is(a, button, [role="button"]):not(.aside-phrase) {
      position: relative;
      z-index: 1;
    }
  }
  /* The margin item rule clears padding (for captions): the card keeps its own. */
  html.rsm-margin .manuscriptwrapper .manuscript .aside.rsm-margin-item {
    padding: .65em .85em;
  }
}

/* External cross-file references */
a.reference.external::after {
  content: " ↗";
  font-size: 0.8em;
  vertical-align: super;
  opacity: 0.6;
}

a.reference.external {
  color: var(--link-default);
}

a.reference.external:hover {
  color: var(--link-hover);
}

a.reference.external:hover::after {
  opacity: 1;
}

/* Dark mode toggle button */
.rsm-theme-toggle {
  position: fixed;
  top: 1rem;
  right: 1rem;
  z-index: 1000;
  width: 2.5rem;
  height: 2.5rem;
  border-radius: var(--radius-full);
  border: var(--border-thin) solid var(--border-primary);
  background-color: var(--surface-primary);
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: all 0.3s ease;
  box-shadow: var(--shadow-soft);
  padding: 0;
}

.rsm-theme-toggle:hover {
  box-shadow: var(--shadow-soft), var(--shadow-border);
  background-color: var(--surface-hover);
  transform: scale(1.1);
}

.rsm-theme-toggle:active {
  transform: scale(0.95);
}

.rsm-theme-toggle:focus-visible {
  outline: var(--border-thin) solid var(--border-action);
  outline-offset: var(--border-thin);
}

.rsm-theme-toggle svg {
  width: 1.25rem;
  height: 1.25rem;
  stroke: var(--extra-dark);
}

/* Show/hide icons based on theme */
.rsm-theme-toggle .dark-icon {
  display: none;
}

.dark-theme .rsm-theme-toggle .dark-icon {
  display: block;
}

.dark-theme .rsm-theme-toggle .light-icon {
  display: none;
}

/* Dark mode asset variants. display cannot follow color-scheme, so the OS dark
   setting needs its own twin of the .dark-theme rules (guarded by
   :not(.light-theme) so an explicit light choice still wins), the same pattern
   the static fallbacks below use. */
.dark-only { display: none; }
.dark-theme .dark-only { display: revert; }
.dark-theme .light-only { display: none; }
@media (prefers-color-scheme: dark) {
  :root:not(.light-theme) .dark-only { display: revert; }
  :root:not(.light-theme) .light-only { display: none; }
}

/* Static fallback for interactive figures (:static: / :dark:).
   The fallback (shown by the JS static-toggle and inside <noscript> when JS is
   off) must scale to the container, not render at the image's natural pixel
   size, which would overflow the figure. */
.static-fallback,
.static-fallback img,
.static-fallback-light,
.static-fallback-dark {
  max-width: 100%;
  height: auto;
}

/* Light is the default; the dark variant only shows in dark mode. We gate on
   both the explicit .dark-theme class and prefers-color-scheme: dark (the same
   two mechanisms braiid uses elsewhere), with .light-theme forcing light. */
.static-fallback-dark { display: none; }
.dark-theme .static-fallback-light { display: none; }
.dark-theme .static-fallback-dark { display: revert; }

@media (prefers-color-scheme: dark) {
  :root:not(.light-theme) .static-fallback-light { display: none; }
  :root:not(.light-theme) .static-fallback-dark { display: revert; }
}

/* Accessibility: Disable transitions and animations when user prefers reduced motion */
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }

  /* Disable transforms */
  .rsm-theme-toggle:hover,
  .rsm-theme-toggle:active {
    transform: none !important;
  }
}

/* Responsive: mobile-first scaling (Tailwind-inspired breakpoints) */
/* Mobile: < 640px uses default 12px from :root */

@media (min-width: 640px) {
  /* sm: Small tablets and up */
  :root {
    font-size: 14px;  /* 116.7% of mobile */
    --side-padding: 3rem;
  }
}

@media (min-width: 1024px) {
  /* lg: Desktop and up */
  :root {
    font-size: 16px;  /* 133% of mobile, 114% of tablet */
    --side-padding: 6.5rem;
  }
}

/* RSM Source Modal */
.rsm-source-modal {
  display: none;
  position: fixed;
  z-index: 2000;
  left: 0;
  top: 0;
  width: 100%;
  height: 100%;
  background-color: rgba(0, 0, 0, 0.4);
}

.rsm-source-modal-content {
  background-color: var(--surface-primary);
  margin: 10% auto;
  padding: var(--chunk-third);
  border: var(--border-extrathin) solid var(--border-primary);
  border-radius: var(--radius-xl);
  width: 80%;
  max-width: calc(16 * var(--chunk));
  box-shadow: var(--shadow-soft);
  position: relative;
}

.rsm-source-modal-actions {
  position: absolute;
  top: var(--chunk-half);
  right: var(--chunk-half);
  display: flex;
  gap: var(--chunk-sixth);
}

.rsm-source-modal-icon-button {
  background: none;
  border: none;
  cursor: pointer;
  padding: 6px;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--text-primary);
  border-radius: var(--radius-md);
  transition: background-color 0.2s, color 0.2s;
}

.rsm-source-modal-icon-button:hover {
  color: var(--text-primary);
  background-color: var(--gray-200);
}

.dark-theme .rsm-source-modal-icon-button:hover {
  background-color: var(--gray-800);
}

.rsm-source-modal-icon-button svg {
  width: var(--chunk-half);
  height: var(--chunk-half);
  stroke: currentColor;
}

.rsm-source-modal-body {
  max-height: 60vh;
  overflow-y: auto;
  background: var(--gray-100);
  border-radius: var(--radius-md);
}

.dark-theme .rsm-source-modal-body {
  background: var(--gray-900);
}

.rsm-source-modal-body pre {
  background-color: transparent;
  padding: var(--chunk-third);
  border: none;
  overflow-x: auto;
  margin: 0;
  color: var(--text-primary);
}

.toast {
  z-index: 2100;
}


/* Reorder mode: opt-in drag reordering of a proof's steps (reorder.js). The
   handles and the insertion marker show only while the proof is .reorder-active;
   normal reading carries no extra chrome. */
.reorder-handle {
  display: none;
}
.proof.reorder-active .step {
  position: relative;
}
/* The handle takes the step's gutter, where its dots menu sits, so hide the
   dots while reordering (they would overlap). The proof's own dots stay, so the
   reader can reopen the menu for "Done reordering" (Esc also cancels). */
.proof.reorder-active .step .hr-border-dots {
  display: none;
}
.proof.reorder-active .reorder-handle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  position: absolute;
  left: calc(-1 * var(--chunk-half));
  top: var(--chunk-sixth);
  width: 1.4rem;
  height: 1.4rem;
  padding: 0;
  /* --dark (gray-700), not --medium: the grip is the primary affordance and
     must clear WCAG 1.4.11 (3:1) for non-text UI, as the collapse sign does. */
  color: var(--dark);
  background: var(--surface-page);
  border: var(--border-thin) solid var(--border-primary);
  border-radius: var(--radius-md);
  cursor: grab;
  touch-action: none;
  z-index: 3;
}
.proof.reorder-active .reorder-handle:hover,
.proof.reorder-active .reorder-handle:focus-visible {
  color: var(--primary-700);
}
.proof.reorder-active .reorder-handle:active {
  cursor: grabbing;
}
.proof.reorder-active .step.reorder-dragged {
  opacity: 0.55;
}
.reorder-sr-status,
.focus-sr-status,
.deplens-sr-status {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
}
.proof.reorder-dragging {
  user-select: none;
}
.reorder-indicator {
  position: relative;
  height: var(--border-med);
  margin: calc(-1 * var(--border-med)) 0;
  background: var(--primary-700);
  border-radius: var(--border-med);
  pointer-events: none;
}
.reorder-indicator.illegal {
  background: var(--error-600);
}
/* The rejection reason: a pill in the handrail gutter to the left of the (red)
   indicator, naming the dependency the move would break. Rides the indicator so
   it tracks the target slot; hidden whenever it carries no text (a legal slot).*/
.reorder-reason {
  position: absolute;
  /* Anchor at the proof's handrail rule and read rightward: the left gutter has
     no room for the text (the floating rail occupies the far-left margin, which
     clips leftward overflow). Sit just above the red line to clear the steps. */
  left: calc(-2 * var(--hr-zone-width));
  bottom: calc(100% + 0.25rem);
  white-space: nowrap;
  padding: 0.12rem 0.55rem;
  font-family: var(--font-body);
  font-size: 0.72rem;
  font-weight: var(--weight-semi);
  line-height: 1.3;
  color: var(--error-600);
  background: var(--surface-page);
  border: var(--border-thin) solid var(--error-600);
  border-radius: 999px;
  box-shadow: var(--shadow-soft);
  pointer-events: none;
  z-index: 4;
}
.reorder-reason:empty {
  display: none;
}

/* "Pin" button inside a reference's hover preview: promotes the transient
   excerpt into the rail's Pinned tab. The tooltip mounts on <body>, so this
   lives outside the rail's media block. */
.ref-pin-bar {
  display: flex;
  justify-content: flex-end;
  margin-bottom: var(--chunk-sixth);
}
.ref-pin {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  padding: 0.15rem 0.5rem;
  font-size: 0.72rem;
  color: var(--dark);
  background: var(--surface-page);
  border: var(--border-thin) solid var(--border-primary);
  border-radius: var(--radius-md);
  cursor: pointer;
}
.ref-pin:hover,
.ref-pin:focus-visible {
  color: var(--primary-700);
  border-color: var(--primary-700);
}
.ref-pin svg {
  width: 0.85rem;
  height: 0.85rem;
}

/* Print / PDF.

   Browsers export PDF with JavaScript ON, so the JS-off <noscript> path never
   fires and the screen-only chrome (floating rail, gutter controls, collapse
   folds) would otherwise leak onto paper. This block is the real print fallback:
   it swaps live widgets for their static images, strips interactive chrome, and
   unfolds everything JS hid so the PDF is a complete, readable paper.

   Kept inside @layer base, as the last rules in the layer, on purpose. The
   on-screen folds use .hide { display:none !important }, and for important
   declarations the cascade-layer order is REVERSED: an unlayered !important
   would LOSE to that layered one. Declaring these unfold rules later in the
   same layer lets source order (and specificity) decide, so they win. The few
   rules that fight the unlayered desktop layout below use !important, which
   beats any normal (non-important) unlayered declaration regardless of layer. */
@media print {

  /* Interactive widget figures: show the static image, hide the live mount.
     The .static-fallback span carries an inline display:none (set by the JS
     toggle, see translator._static_fallback_img), so revealing it needs
     !important to beat that inline style. The live mount is every non-fallback
     child of a content-zone inside the figure: the <picture>/<img> for an image
     figure, or the widget element (and its <script>s) for a :html: figure,
     which RSM emits as its own .paragraph block inside the same <figure>. */
  figure[data-static] .static-fallback {
    display: revert !important;
  }
  figure[data-static] .static-fallback .static-fallback-light {
    display: revert;
  }
  figure[data-static] .static-fallback .static-fallback-dark {
    display: none;
  }
  /* Hide the live mount but keep the caption. A :html: figure renders the static
     image, the live widget, and the :caption: as three separate handrails (the
     widget is its own .paragraph block), each with its own .hr-content-zone.
     Hiding non-fallback content-zone children swaps the live image/widget for
     the static one, but the figcaption's zone must be excluded or the printed
     caption text would vanish and leave an empty caption box. */
  figure[data-static] .hr-content-zone:not(figcaption *) > :not(.static-fallback):not(noscript) {
    display: none !important;
  }

  /* Screen-only chrome: none of it belongs in a printed document. The rail
     hides every form it takes (desktop side panel and mobile bottom-sheet
     drawer share the .proof-rail class). The handrail gutter holds collapse
     chevrons and the dots menu; the back-pill, focus-mode exit bar, toasts,
     tooltips, theme toggle, and the source modal are all transient overlays. */
  .proof-rail,
  .rail-strip,
  .ui-tip,
  .hr-collapse-zone,
  .hr-menu-zone,
  .rsm-back-pill,
  .proof-focus-exit,
  .toast,
  .tooltipster-base,
  .rsm-theme-toggle,
  .rsm-source-modal,
  .rsm-source {
    display: none !important;
  }

  /* The border-zone is the gutter holding the dots and the handrail's vertical
     rule. Hide it on every block except proofs. On a proof we keep that vertical
     rule as a printed "proof bracket" marking the proof's extent: reveal the zone
     (it sits at opacity:0 off-screen until hover), paint only the rule (the rect)
     and not the dots, and clear the collapsed fold cap so it reads as one clean
     vertical line. Only the proof's own direct rule shows; nested step rules stay
     hidden so the bracket is not doubled. */
  *:not(.proof) > .hr-border-zone {
    display: none !important;
  }
  .proof.hr > .hr-border-zone {
    opacity: 1 !important;
  }
  .proof.hr > .hr-border-zone > .hr-border-dots {
    display: none !important;
  }
  .proof.hr > .hr-border-zone > .hr-border-rect {
    /* The on-screen rect is shifted up by two gutter-zone heights to clear the
       stacked collapse/menu zones; those are gone in print, so reset it to sit
       flush with the proof. Also drop the collapsed fold cap (border-bottom) so
       the bracket is a single clean vertical line. */
    top: 0 !important;
    border-bottom: none !important;
  }

  /* Unfold everything JS collapsed. closeHandrail adds .hide (display:none
     !important) to a collapsed block's body and the page shows a fold label
     ("8 steps hidden"). :collapsed: proofs and focus mode do the same. A PDF reader must
     see every step and proof, so re-show the hidden bodies and drop the
     fold labels. The unfolded body is either a handrail block (.hr, laid out as
     flex) or plain block content, so restore each its own display rather than
     `revert` (which would roll back to .hide's display:none); the higher-
     specificity .hr.hide rule then wins the flex case over the block default.

     Exclude .rsm-source: it is the view-source payload (the raw RSM markup the
     build ships hidden for the copy-source feature) and it also carries .hide,
     so without this guard the unfold would dump the literal source onto the
     printed page. It is the one .hide element baked into the static HTML;
     collapsed handrail bodies get .hide from JS at runtime. */
  .manuscriptwrapper .hide:not(.rsm-source) {
    display: block !important;
  }
  .manuscriptwrapper .hr.hide {
    display: flex !important;
  }
  .hr.hr-collapsed > .hr-content-zone::after,
  .hr.hr-collapsed > .hr-content-zone > .hr-label::after {
    content: none !important;
  }
  /* A collapsed block caps its gutter with a border-bottom on the spacer-zone
     (the rounded foot of the fold). Print unfolds the body above but leaves the
     .hr-collapsed class, so that cap would print as a stray ~16px dash at the
     block's bottom-left. Drop only the border, not the zone: the 16px spacer is
     what aligns every handrail's content onto one left column, so removing the
     zone would shift collapsed blocks left and break that shared edge. The cap
     is set by two branches, and the .hr-labeled/.step one (matching proofs and
     steps) outranks a plain selector here, so use !important to beat both. */
  .manuscriptwrapper .hr.hr-collapsed > .hr-spacer-zone {
    border-bottom: none !important;
  }

  /* Layout: with the sidebar gone, reclaim the margin the screen layout keeps
     for its strip and panel and re-centre the column on the page. !important
     is needed because those rules are unlayered and would otherwise outrank
     this layered one. */
  .manuscriptwrapper {
    margin: 0 auto !important;
    max-width: 100% !important;
    width: auto !important;
  }

  @page {
    margin: 2cm;
  }

  /* Keep mathematical units intact across page breaks, and keep a heading with
     the content it introduces. break-inside is the modern property; the legacy
     page-break-inside is kept for older print engines. */
  figure,
  .theorem,
  .proof,
  .mathblock,
  .step {
    break-inside: avoid;
    page-break-inside: avoid;
  }
  h1, h2, h3, h4, h5, h6 {
    break-after: avoid;
    page-break-after: avoid;
  }
}

}


/* pseudocode.js overrides -- outside @layer to beat inline styles.
   pseudocode.js injects KaTeX_Main via inline style attributes;
   !important is required to override them. */

.ps-root {
  font-family: var(--font-body) !important;
  font-weight: 450 !important;
  color: var(--text-body) !important;
}

.ps-keyword {
  font-family: var(--font-body) !important;
  font-weight: var(--weight-bold) !important;
}

.ps-funcname {
  font-family: var(--font-body) !important;
  font-variant: small-caps;
}

.ps-comment {
  font-family: var(--font-body) !important;
  color: var(--text-subtle) !important;
}

.ps-line {
  font-family: var(--font-body) !important;
}

.ps-linenum {
  font-family: var(--font-mono) !important;
  color: var(--text-subtle) !important;
  font-size: 0.85em;
}

.ps-algorithm {
  border-color: var(--gray-300) !important;
}

.ps-algorithm > .ps-line:first-child {
  border-top-color: var(--gray-300) !important;
}

/* Preamble math nodes contain macro definitions and should be invisible */
.math-preamble { display: none; }



/* ---- Reader sidebar: page layout (rsm build --sidebar) ----
   Geometry of the strip and panel, shared with sidebar.js (which reads the
   same numbers to place the column). */
:root {
  --rail-strip-left: var(--chunk-third);   /* strip offset from the window edge */
  --rail-top: calc(var(--chunk) + 2 * var(--chunk-third));
  --rail-panel-width: 17rem;
}

/* The strip and the phone bottom bar are rendered by the HTML but only work
   once sidebar.js has wired them, so hide them until it marks the page with
   html.has-rail. With scripting off they never show, so there is no dead strip
   and no bottom bar eating the screen. The specificity clears the base and
   phone .rail-strip rules. */
html:not(.has-rail) .manuscriptwrapper .rail-strip { display: none; }

/* Skip link: the first focusable element on a --sidebar page, so a keyboard
   reader can jump past the strip to the text. Hidden until focused, then shown
   as a small chip at the top left, in either theme. */
.rsm-skip-link {
  position: fixed;
  top: var(--chunk-sixth);
  left: var(--chunk-sixth);
  z-index: 100;
  padding: var(--chunk-sixth) var(--chunk-third);
  background: var(--surface-page);
  color: var(--text-body);
  border: var(--border-thin) solid var(--border-action);
  border-radius: var(--radius-md);
  font: var(--weight-semi) 0.95rem/1.2 var(--font-body);
  text-decoration: none;
  box-shadow: var(--shadow-soft);
  transform: translateY(-150%);
  opacity: 0;
  pointer-events: none;
}
.rsm-skip-link:focus,
.rsm-skip-link:focus-visible {
  transform: translateY(0);
  opacity: 1;
  pointer-events: auto;
  outline: var(--border-thin) solid var(--border-action);
  outline-offset: 2px;
}
/* The skip target is focusable only so focus can move to it; it is not a
   control, so it shows no focus ring of its own. */
#rsm-content:focus { outline: none; }

/* Wide windows: the column sits right of the strip, centred in the space left
   when that space allows; with a view open it moves next to the panel instead
   of under it. sidebar.js sets --rail-ml / --rail-mw (the column's left edge
   and width); the fallbacks hold until it runs. */
@media (min-width: 768px) {
  /* Keyed only on has-rail, which the inline head script sets before first
     paint when scripting is on, so the column is placed from the start and
     never jumps. With scripting off has-rail is absent, so no margin is
     reserved and the text uses the full column. */
  html.has-rail main.manuscriptwrapper {
    margin-left: var(--rail-ml, max(calc(var(--chunk) + 2 * var(--chunk-third) + var(--chunk-sixth)),
      calc((100vw - var(--content-max-width, var(--chunk-20))) / 2)));
    margin-right: 0;
    width: var(--rail-mw, auto);
    max-width: var(--rail-mw, var(--content-max-width, var(--chunk-20)));
    --side-padding: 2rem;
  }
  /* Animate only the reader's own open/close, not the first placement. */
  html.rail-ready main.manuscriptwrapper {
    transition: margin-left 0.22s ease, width 0.22s ease, max-width 0.22s ease;
  }
  html.rail-open main.manuscriptwrapper { --side-padding: var(--chunk-third); }
}
@media (prefers-reduced-motion: reduce) {
  html.rail-ready main.manuscriptwrapper { transition: none; }
}

/* Phones: the strip is a bottom bar of labelled buttons, one per scope, and a
   view opens as a sheet above it. */
@media (max-width: 767px) {
  .manuscriptwrapper .rail-strip {
    top: auto;
    left: 0;
    right: 0;
    bottom: 0;
    flex-direction: row;
    justify-content: stretch;
    gap: 0;
    padding: var(--chunk-sixth) max(var(--chunk-sixth), env(safe-area-inset-left, 0px))
      calc(var(--chunk-sixth) + env(safe-area-inset-bottom, 0px))
      max(var(--chunk-sixth), env(safe-area-inset-right, 0px));
    border-width: var(--border-extrathin) 0 0;
    border-radius: 0;
    box-shadow: none;
  }
  .manuscriptwrapper .rail-strip-sep,
  .manuscriptwrapper .rail-strip-btn[data-mode="split"] { display: none; }
  .manuscriptwrapper .rail-strip-btn[data-mode="grouped"],
  .manuscriptwrapper .rail-strip-btn {
    display: flex;
    flex: 1 1 0;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 2px;
    width: auto;
    min-width: 48px;
    min-height: 48px;
    height: auto;
  }
  .manuscriptwrapper .rail-strip-btn[data-mode="split"] { display: none; }
  .manuscriptwrapper .rail-strip-btn svg { width: 24px; height: 24px; }
  .manuscriptwrapper .rail-strip-label {
    display: block;
    font: var(--weight-semi) 11px/1.1 var(--font-body);
  }

  .manuscriptwrapper .proof-rail.active.open {
    left: var(--chunk-sixth);
    right: var(--chunk-sixth);
    top: auto;
    bottom: calc(48px + 3 * var(--chunk-sixth) + var(--border-extrathin) + env(safe-area-inset-bottom, 0px));
    width: auto;
    height: auto;
    max-height: 62vh;
    padding: 0;
    overflow: auto;
    z-index: 42;
  }
  /* One inset for the head and the content. */
  .manuscriptwrapper .proof-rail.active.open > .rail-section {
    padding: 0 var(--chunk-half) var(--chunk-half);
  }
  .manuscriptwrapper .proof-rail .rail-head {
    margin: 0 var(--chunk-half) var(--chunk-quarter);
    padding: var(--chunk-quarter) 0;
  }
  .manuscriptwrapper .proof-rail .rail-reading-panel { padding-left: 0; padding-right: 0; }
  /* The two graph views keep a usable height; the others fit their content. */
  .manuscriptwrapper .proof-rail.active.open.scope-document.doc-view-map,
  .manuscriptwrapper .proof-rail.active.open.scope-proof.proof-view-map:not(.no-proof) {
    height: 62vh;
  }
  /* Text tabs replace the title for a scope with two views. */
  .manuscriptwrapper .proof-rail.scope-document .rail-subtabs-document,
  .manuscriptwrapper .proof-rail.scope-proof .rail-subtabs-proof { display: flex; }
  .manuscriptwrapper .proof-rail.has-tabs .rail-title { display: none; }
  /* With tabs in place of the title, the hint shares their row, at the right. */
  .manuscriptwrapper .proof-rail.has-tabs .rail-head {
    flex-direction: row;
    align-items: center;
    gap: var(--chunk-third);
  }
  .manuscriptwrapper .proof-rail.has-tabs .rail-hint {
    flex: 1 1 auto;
    min-width: 0;
    text-align: right;
    order: 2;
  }

  /* Reading in the sheet: label beside its tiles; no Width (the column already
     fills the screen). */
  .manuscriptwrapper .proof-rail .reading-row {
    display: grid;
    grid-template-columns: 4.75rem minmax(0, 1fr);
    align-items: center;
    column-gap: var(--chunk-sixth);
  }
  .manuscriptwrapper .proof-rail .reading-opt { width: 44px; height: 44px; }
  .manuscriptwrapper .proof-rail .reading-row[data-control="measure"] { display: none; }

  /* Keep the end of the manuscript, and the transient pills, clear of the bar.
     Only when the bar is live (has-rail): with scripting off there is no bar,
     so no space is reserved at the bottom. */
  html.has-rail body:has(.rail-strip) {
    padding-bottom: calc(48px + 2 * var(--chunk-sixth) + var(--chunk-half) + env(safe-area-inset-bottom, 0px));
  }
  html.has-rail:has(.rail-strip) { scroll-padding-bottom: calc(48px + 2 * var(--chunk-sixth) + var(--chunk-half)); }
  html.has-rail body:has(.rail-strip) :is(.rsm-back-pill, .rsm-shared-pill) {
    bottom: calc(48px + 2 * var(--chunk-sixth) + var(--chunk-third) + env(safe-area-inset-bottom, 0px));
  }
}

/* Interface tooltip (uitip.js): a small label for the reader interface's
   controls, deliberately unlike the previews of manuscript content. */
.ui-tip {
  position: fixed;
  z-index: 10000001;
  max-width: 16rem;
  padding: var(--chunk-twelfth) var(--chunk-sixth);
  border-radius: var(--radius-md);
  background: var(--text-body);
  color: var(--surface-page);
  font: var(--weight-semi) 0.75rem/1.35 var(--font-body);
  box-shadow: var(--shadow-soft);
  pointer-events: none;
  opacity: 0;
  transition: opacity 0.12s;
}
.ui-tip.on { opacity: 1; }
@media (prefers-reduced-motion: reduce) { .ui-tip { transition: none; } }

/* The excerpt of a widget (an :html: block) in a reference preview or the
   Pinned card: its number, its still image, and a muted line saying the
   interactive version is in the manuscript (tooltips.js widgetPreview). The
   still image switches with the theme through the .static-fallback-light and
   .static-fallback-dark rules above. */
.widget-preview {
  font-family: var(--font-body);
}
.widget-preview > p {
  margin: var(--chunk-sixth) 0;
}
.widget-preview-label {
  font-weight: var(--weight-semi);
}
.widget-preview-still img {
  display: block;
  max-width: 100%;
  height: auto;
}
.widget-preview-still img.static-fallback-dark { display: none; }
.dark-theme .widget-preview-still img.static-fallback-light { display: none; }
.dark-theme .widget-preview-still img.static-fallback-dark { display: block; }
@media (prefers-color-scheme: dark) {
  :root:not(.light-theme) .widget-preview-still img.static-fallback-light { display: none; }
  :root:not(.light-theme) .widget-preview-still img.static-fallback-dark { display: block; }
}
.widget-preview-note {
  color: var(--text-subtle);
  font-size: 0.9em;
}

/* The excerpt of a :calc: (tooltips.js calcPreview). In the page each row's
   number hangs in the right margin, which a preview or the Pinned card does
   not have, so put the number column back in the row. */
.calc-preview .step.hr-offset > .hr-info-zone {
  position: static;
}

/* The pin control in a reference preview: an icon in the corner. Quiet at
   rest, a surface on hover, the accent while pressed, filled once pinned. */
.tooltipster-content > .manuscriptwrapper:has(> .ref-pin-bar) {
  position: relative;
  padding-right: calc(var(--chunk-half) + var(--chunk-sixth));
}
.tooltipster-content .ref-pin-bar {
  position: absolute;
  top: 0;
  right: 0;
  margin: 0;
}
.tooltipster-content .ref-pin {
  display: grid;
  place-items: center;
  width: 1.75rem;
  height: 1.75rem;
  padding: 0;
  border: 0;
  border-radius: var(--radius-lg);
  background: none;
  color: var(--text-subtle);
  cursor: pointer;
}
.tooltipster-content .ref-pin svg { width: 1rem; height: 1rem; }
.tooltipster-content .ref-pin:hover { background: var(--surface-primary); color: var(--text-body); }
.tooltipster-content .ref-pin:active { background: var(--primary-50); color: var(--primary-800); }
.tooltipster-content .ref-pin:focus-visible {
  outline: var(--border-thin) solid var(--border-action);
  outline-offset: -2px;
}
.tooltipster-content .ref-pin[data-pinned] { color: var(--primary-700); }
.tooltipster-content .ref-pin[data-pinned]:hover { background: var(--primary-50); color: var(--primary-800); }

/* Notation: every use of a declared symbol is marked in the math (only with
   the sidebar); clicking one opens Notation with that symbol selected. */
.manuscript :is([class^="nt-"], [class*=" nt-"]) {
  cursor: pointer;
  border-radius: var(--radius-md);
  transition: background-color 0.15s;
}
.manuscript :is([class^="nt-"], [class*=" nt-"]):hover { background: var(--primary-100); }
.rail-notation-row.rail-notation-row-hi { animation: rail-notation-row 1.8s ease-out; }
@keyframes rail-notation-row {
  0%, 50% { background: var(--primary-50); box-shadow: 0 0 0 var(--chunk-twelfth) var(--primary-50); }
  100% { background: transparent; box-shadow: none; }
}
@media (prefers-reduced-motion: reduce) {
  .rail-notation-row.rail-notation-row-hi { animation: none; }
}

/* Document minimap (minimap.js): a strip at the right edge of the viewport
   that replaces the native scrollbar. A mark per section (line) and figure
   (accent dash), and a box over the part on screen. The native scrollbar is
   hidden only while the strip is mounted (html.rsm-minimap-on), so with
   scripting off the page keeps it. Below the sidebar (z-index 40 to 42), menus
   and modals. */
html.rsm-minimap-on { scrollbar-width: none; }
html.rsm-minimap-on::-webkit-scrollbar { display: none; }

.rsm-minimap {
  --mm-width: 24px;
  --mm-draw: 24px;
  --mm-box-inset: 8px;
  position: fixed;
  top: 0;
  right: 0;
  bottom: 0;
  z-index: 30;
  width: var(--mm-width);
  margin: 0;
  padding: 0;
  touch-action: none;
  cursor: pointer;
  user-select: none;
  -webkit-user-select: none;
}
.rsm-minimap.is-dragging { cursor: grabbing; }
.rsm-minimap-track {
  position: absolute;
  top: 0;
  bottom: 0;
  right: 0;
  width: var(--mm-draw);
}
.rsm-minimap-marks { position: absolute; inset: 0; pointer-events: none; }
.rsm-minimap-box {
  position: absolute;
  left: var(--mm-box-inset);
  right: var(--mm-box-inset);
  border-radius: 4px;
  background: var(--gray-600);
  opacity: 0.25;
  pointer-events: none;
}

.rsm-minimap-mark {
  position: absolute;
  left: 0;
  right: 0;
  height: 16px;
  display: flex;
  align-items: center;
  transform: translateY(-50%);
  pointer-events: none;
}
.rsm-minimap-mark::after {
  content: "";
  display: block;
  border-radius: 2px;
  transform: scaleX(0.75);
  opacity: 0.3;
  transition: transform 0.25s ease, opacity 0.25s ease, background-color 0.15s ease;
}
.rsm-minimap-mark.mm-section::after {
  flex: 1 1 auto;
  height: 3px;
  margin-inline: 4px;
  background: var(--text-subtle);
}
.rsm-minimap-mark.mm-section.mm-top::after { height: 4px; margin-inline: 2px; }
.rsm-minimap-mark.mm-figure::after {
  width: 60%;
  height: 3px;
  margin-inline: auto;
  background: var(--primary-500);
}

.rsm-minimap:hover .rsm-minimap-mark::after,
.rsm-minimap.is-touching .rsm-minimap-mark::after,
.rsm-minimap:focus-within .rsm-minimap-mark::after { transform: scaleX(1); opacity: 1; }
.rsm-minimap.has-hovered .rsm-minimap-mark::after { opacity: 0.35; }
.rsm-minimap.has-hovered .rsm-minimap-mark.is-near::after { opacity: 0.1; }
.rsm-minimap.has-hovered .rsm-minimap-mark.is-hovered::after { opacity: 1; transform: scaleX(1); }
.rsm-minimap-mark.is-hovered { z-index: 1; }
.rsm-minimap-mark.mm-section.is-hovered::after { background: var(--text-body); }
.rsm-minimap-mark.mm-figure.is-hovered::after { background: var(--primary-600); }
.rsm-minimap-mark.is-focused {
  outline: 2px solid var(--border-action);
  outline-offset: -2px;
  border-radius: var(--radius-md);
}

.rsm-minimap-tip {
  position: absolute;
  right: calc(100% + 6px);
  max-width: 18rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  transform: translateY(-50%);
  padding: var(--chunk-twelfth) var(--chunk-sixth);
  border-radius: var(--radius-md);
  background: var(--text-body);
  color: var(--surface-page);
  font: var(--weight-semi) 0.75rem/1.35 var(--font-body);
  box-shadow: var(--shadow-soft);
  pointer-events: none;
  visibility: hidden;
}
.rsm-minimap-tip.is-visible { visibility: visible; }

/* The marks as a list of links for screen readers and the keyboard. The links
   are visually hidden: the focused one shows as its mark's focus ring. */
.rsm-minimap-links {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: 0;
  padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
  list-style: none;
}

/* Phones: drawn 16px wide with a 24px hit area (8px over the text). The strip
   shows only while the page scrolls, is touched, or holds focus, and takes
   pointer events only while it shows, so a tap on the text is never caught. */
@media (max-width: 767px) {
  .rsm-minimap {
    --mm-draw: 16px;
    --mm-box-inset: 4px;
    opacity: 0;
    pointer-events: none;
    transition: opacity 0.3s ease;
  }
  .rsm-minimap.is-awake,
  .rsm-minimap.is-touching,
  .rsm-minimap:focus-within {
    opacity: 1;
    pointer-events: auto;
    transition: opacity 0.1s ease;
  }
  /* Stop above the sidebar's bottom bar. */
  .rail-strip ~ .rsm-minimap {
    bottom: calc(48px + 2 * var(--chunk-sixth) + var(--border-extrathin) + env(safe-area-inset-bottom, 0px));
  }
}

@media (prefers-reduced-motion: reduce) {
  .rsm-minimap,
  .rsm-minimap.is-awake,
  .rsm-minimap.is-touching,
  .rsm-minimap:focus-within,
  .rsm-minimap-mark::after { transition: none; }
}

@media print {
  .rsm-minimap { display: none !important; }
}
