/* Styles for spec content rendered via lib/spec/<chapter>.jsx (Sec component
   wraps everything in <div class="ecma-spec">). */

/* IBM Plex Mono — same WOFF2 files tc39.es ships at
   /ecma262/assets/fonts/, vendored under lume-poc/fonts/. font-display:
   swap lets system mono render first and the Plex glyphs swap in as
   soon as they load (no FOIT flash). Four weights cover regular / bold
   / italic / bold-italic; ecmarkup uses bold-mono for inline <code>
   and regular-mono inside <pre><code>.

   NOTE: keep the src url() RELATIVE ("fonts/…", no leading slash). url()
   resolves against this stylesheet's own location, so a relative path
   works whether the site is served from the origin root (local PoC) or a
   sub-path (GitHub Pages /ecma262/…). A leading-slash "/fonts/…" would
   resolve to the origin root and 404 under any base path. */
@font-face {
  font-family: "IBM Plex Mono";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("fonts/IBMPlexMono-Regular-SlashedZero.woff2") format("woff2");
}
@font-face {
  font-family: "IBM Plex Mono";
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url("fonts/IBMPlexMono-Bold-SlashedZero.woff2") format("woff2");
}
@font-face {
  font-family: "IBM Plex Mono";
  font-style: italic;
  font-weight: 400;
  font-display: swap;
  src: url("fonts/IBMPlexMono-Italic-SlashedZero.woff2") format("woff2");
}
@font-face {
  font-family: "IBM Plex Mono";
  font-style: italic;
  font-weight: 700;
  font-display: swap;
  src: url("fonts/IBMPlexMono-BoldItalic-SlashedZero.woff2") format("woff2");
}

/* IBM Plex Serif — the body typeface tc39.es/ecma262 ships (ecmarkup.css
   `body { font-family: 'IBM Plex Serif', serif }`). Same SlashedZero subset
   tc39 serves from /assets/fonts, so digits read with a slashed zero. Four
   faces (regular/bold/italic/bold-italic) cover the spec's prose, <emu-val>
   bold, and italic emphasis. */
@font-face {
  font-family: "IBM Plex Serif";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("fonts/IBMPlexSerif-Regular-SlashedZero.woff2") format("woff2");
}
@font-face {
  font-family: "IBM Plex Serif";
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url("fonts/IBMPlexSerif-Bold-SlashedZero.woff2") format("woff2");
}
@font-face {
  font-family: "IBM Plex Serif";
  font-style: italic;
  font-weight: 400;
  font-display: swap;
  src: url("fonts/IBMPlexSerif-Italic-SlashedZero.woff2") format("woff2");
}
@font-face {
  font-family: "IBM Plex Serif";
  font-style: italic;
  font-weight: 700;
  font-display: swap;
  src: url("fonts/IBMPlexSerif-BoldItalic-SlashedZero.woff2") format("woff2");
}

/* Universal border-box reset — matches Nextra's Tailwind preflight
   (node_modules/nextra-theme-docs/dist/style.css:133-139), which sets
   `*, ::after, ::before, ::backdrop, ::file-selector-button {
   box-sizing: border-box }`. Without this the page inherits the CSS
   default content-box, so `width: 100%` + horizontal padding silently
   overflows the parent (the mobile sidebar search input was leaking
   ~8px past the viewport for exactly that reason). */
*,
*::before,
*::after {
  box-sizing: border-box;
}

/* ── Page chrome (Lume PoC) ──────────────────────────────────────────────
   Body is a vertical flex stack of header / .layout-wrapper / footer.
   The wrapper itself is a 3-column grid for sidebar / main / TOC; the
   footer sits OUTSIDE it so sticky sidebar/TOC slide up out of view as
   the footer scrolls in. TOC drops off below ~1100px; sidebar drops off
   below ~800px. Matches the affordance Nextra gives us today. */
:root {
  --bg: #ffffff;
  --bg-rgb: 255 255 255;
  --fg: #111827;
  --muted: #6b7280;
  --rule: #e5e7eb;
  --chrome-bg: #fafafa;
  --accent: #2563eb;
  --accent-rgb: 37 99 235;
  --sidebar-w: 256px; /* matches Nextra's x:w-64 */
  --toc-w: 256px; /* matches Nextra's x:w-64 */
  --content-max-w: 90rem; /* matches Nextra's --nextra-content-width (1440px) */
  --header-h: 4rem; /* matches Nextra's --nextra-navbar-height: 64px */

  /* Body / spec typography — the IBM Plex families tc39.es ships:
       - Plex Serif (body) — loaded via @font-face above, so the reading
         zone matches tc39 exactly; Charter / Cambria / Georgia remain as
         fallbacks for the brief swap window or a failed font load.
       - Plex Sans (emu-const / emu-gprose / figcaption) ≈ Segoe UI
         (Windows) / -apple-system (= SF Pro on macOS), both humanist
         sans in the same family Plex Sans belongs to.
       - Plex Mono (emu-t / emu-opt / emu-params / inline code) — loaded
         via @font-face above (leads the --font-mono stack below).
     Used only inside the spec body so the page chrome (header,
     sidebar, footer) keeps its Nextra-matching sans stack. */
  --font-serif:
    "IBM Plex Serif",
    Charter,
    "Bitstream Charter",
    Cambria,
    "Iowan Old Style",
    Georgia,
    ui-serif,
    serif;
  --font-sans:
    -apple-system,
    BlinkMacSystemFont,
    "Segoe UI",
    "Segoe UI Variable",
    Cantarell,
    "Helvetica Neue",
    Arial,
    sans-serif;
  /* Plex Mono first — declared via @font-face above; falls back to the
     OS mono stack while it loads and on machines that block webfonts. */
  --font-mono:
    "IBM Plex Mono",
    ui-monospace,
    "SF Mono",
    Menlo,
    "Cascadia Mono",
    "Cascadia Code",
    Consolas,
    "DejaVu Sans Mono",
    "Liberation Mono",
    monospace;

  /* Spec <var> styling + the 7 cycling highlight backgrounds ecmarkup applies
     when a variable is clicked (tc39 parity, ecmarkup.css). */
  --var-foreground-color: #218379;
  --referenced0-background-color: #ffff6c;
  --referenced1-background-color: #ffa4a8;
  --referenced2-background-color: #96e885;
  --referenced3-background-color: #3eeed2;
  --referenced4-background-color: #eacfb6;
  --referenced5-background-color: #82ddff;
  --referenced6-background-color: #ffbcf2;

  /* Grammar annotation colours (tc39 parity, ecmarkup.css): parameter lists
     (`[Yield]`) and constraints (`[+In]`) in teal, optional markers (`?`) in
     gold, and the RHS hover highlight (dashed → solid border + tint). */
  --params-foreground-color: #2aa198;
  --opt-foreground-color: #b58900;
  --production-rhs-background-color: #f0f0f0;
  --production-rhs-border-color: #888;

  color-scheme: light;
}
html.dark {
  --bg: #0f1117;
  --bg-rgb: 15 17 23;
  --fg: #e5e7eb;
  --muted: #9ca3af;
  --rule: #2a2e37;
  --chrome-bg: #161922;
  --accent: #60a5fa;
  --accent-rgb: 96 165 250;
  --var-foreground-color: #4fb6ab;
  --referenced0-background-color: #6d6d1e;
  --referenced1-background-color: #7e171c;
  --referenced2-background-color: #1f6211;
  --referenced3-background-color: #1a7a6b;
  --referenced4-background-color: #7b4719;
  --referenced5-background-color: #185870;
  --referenced6-background-color: #731761;
  --params-foreground-color: #57d0c7;
  --opt-foreground-color: #d8b750;
  --production-rhs-background-color: #4d4d4d;
  --production-rhs-border-color: #888;
  color-scheme: dark;
}

/* Skip-to-content: visually hidden until a keyboard user tabs into it,
   at which point it snaps to the top-left as a proper button. */
.skip-nav {
  position: fixed;
  top: 0.5rem;
  inset-inline-start: 0.5rem;
  z-index: 100;
  padding: 0.5em 0.9em;
  background: var(--accent);
  color: #fff;
  text-decoration: none;
  border-radius: 4px;
  transform: translateY(-200%);
  transition: transform 0.15s;
}
.skip-nav:focus {
  transform: translateY(0);
  outline: 2px solid #fff;
  outline-offset: -3px;
}
/* In dark mode --accent lightens to blue-400, where white text drops to
   2.5:1 — use dark text (and a matching outline) on the light button. */
html.dark .skip-nav {
  color: #111827;
}
html.dark .skip-nav:focus {
  outline-color: #111827;
}

body {
  margin: 0;
  font-family:
    -apple-system, BlinkMacSystemFont, "Segoe UI", "Helvetica Neue", Helvetica,
    Arial, sans-serif;
  background: var(--bg);
  color: var(--fg);
  display: flex;
  flex-direction: column;
  /* Track the dynamic viewport so the footer sits at the current bottom
     edge on iOS even as the URL bar grows/shrinks; the vh line is the
     fallback for browsers without dvh. Same pattern Nextra uses
     (style.css:3031-3032). */
  min-height: 100vh;
  min-height: 100dvh;
}
/* Sidebar + main + TOC live in this flex wrapper; footer sits as a
   sibling of the wrapper. Sticky elements inside cannot exceed the
   wrapper's bottom edge — when the footer (outside) scrolls into view,
   sidebar/TOC slide up rather than overlapping it. Mirrors Nextra's
   <div class="x:mx-auto x:flex x:max-w-(--nextra-content-width)">. */
.layout-wrapper {
  display: grid;
  /* `minmax(0, 1fr)` instead of bare `1fr` for the main column: the
     implicit `auto` min would otherwise force the track to expand to
     fit the largest item's min-content (long emu-nt names, pre block
     lines), pushing the grid container wider than the viewport. */
  grid-template-columns: var(--sidebar-w) minmax(0, 1fr) var(--toc-w);
  /* Cap the sidebar/main/TOC trio at --content-max-w (1440px) and centre
     it, matching Nextra's <div class="x:mx-auto x:flex x:max-w-(--nextra-content-width)">.
     On viewports wider than 1440px the trio sits in the middle with even
     gutters; below 1440px it fills the viewport (no visual change). */
  max-width: var(--content-max-w);
  margin-inline: auto;
  width: 100%;
  flex: 1 1 auto;
  min-height: 0;
  transition: grid-template-columns 0.2s ease-in-out;
}

/* Offset in-page anchor jumps (#sec-…, dfn / step / prodref targets, etc.) by
   the sticky header's height so the target lands below the bar instead of
   hidden under it. Adapted from Nextra's
   `html:not(:has(*:focus)) { scroll-padding-top: var(--nextra-navbar-height) }`,
   but the guard is narrowed to input-like elements: Chrome makes scrollable
   containers keyboard-focusable, so a fragment jump to a wide emu-table
   focuses the table itself — with Nextra's `*:focus` guard that very jump
   lost the offset and the table landed under the header. Only a focused
   field's scroll-into-view should skip the offset. */
html:not(
  :has(input:focus, textarea:focus, select:focus, [contenteditable]:focus)
) {
  scroll-padding-top: var(--header-h);
}

.site-header {
  /* Outer bar: sticky positioning only — bg + border live on the absolute
     .site-header-blur child so the underlying layer can blur content that
     scrolls below. Matches Nextra's
     <header class="nextra-navbar x:sticky x:top-0 x:z-30 x:w-full x:bg-transparent">.
     z-index above the mobile nav panel (z-20) so the navbar stays visible
     while the menu is open, and above the breadcrumb (z-35) so the header's
     version-switcher dropdown opens over it rather than slipping behind it
     (the dropdown's own z-index can't escape this header stacking context). */
  position: sticky;
  top: 0;
  z-index: 40;
}
.site-header-blur {
  /* Translucent frosted-glass background, matches Nextra's
     <div class="nextra-navbar-blur x:absolute x:-z-1 x:size-full
                 nextra-border x:border-b
                 x:backdrop-blur-md x:bg-nextra-bg/70">. */
  position: absolute;
  inset: 0;
  z-index: -1;
  background: rgb(var(--bg-rgb) / 0.7);
  border-bottom: 1px solid var(--rule);
  -webkit-backdrop-filter: blur(12px); /* x:backdrop-blur-md */
  backdrop-filter: blur(12px);
}
.site-header-inner {
  /* Inner row: matches Nextra's navbar inner container
     <nav class="x:mx-auto x:flex x:max-w-(--nextra-content-width)
                 x:items-center x:gap-4 x:justify-end
                 x:pl-[max(env(safe-area-inset-left),1.5rem)] x:pr-[...]">.
     The row is justify-end + gap-4; .site-title-group below has
     margin-inline-end:auto (Nextra's me-auto on the logo) which absorbs
     the available space and pushes search/theme to the far right.
     min-height pins the bar to --header-h (4rem / 64px =
     --nextra-navbar-height); without it the row collapses to its
     tallest child (~33px search input). */
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 1rem;
  min-height: var(--header-h);
  max-width: var(--content-max-w);
  margin-inline: auto;
  padding-inline: max(env(safe-area-inset-left), 1.5rem)
    max(env(safe-area-inset-right), 1.5rem);
}
/* Title cluster: bold main title + dotted-underline status badge link +
   VersionSwitcher chevron, all on one baseline. Mirrors the navbar logo of
   the former Nextra spec-layout so the visual hierarchy is
   identical to the Nextra deploys. */
.site-header .site-title-group {
  /* margin-inline-end: auto mirrors Nextra's logo wrapper class
     (x:flex x:items-center x:me-auto); combined with the parent's
     justify-end it pulls the title cluster to the left and pushes the
     remaining items (search, theme) to the right edge.
     No white-space: nowrap — Nextra leaves the title wrappable
     (navbar/index.js:34, spec-layout.jsx logo span) so the flex item's
     min-content is just the longest unbreakable token (~"ES2027"), not
     the full 290px-wide unwrapped title. On narrow phones the title
     wraps to two lines instead of pushing the page into horizontal
     scroll. */
  display: inline-flex;
  align-items: center;
  gap: 0.4em;
  margin-inline-end: auto;
  font-size: 0.95rem;
}
.site-header .site-title {
  text-decoration: none;
  color: inherit;
}
.site-header .qual-link {
  color: inherit;
  text-decoration: underline;
  text-decoration-style: dotted;
  text-underline-offset: 3px;
}
.site-header .qual-link:hover {
  text-decoration-style: solid;
}
.site-header .site-search {
  position: relative; /* anchor the absolutely-positioned drawer */
  flex: 0 0 auto;
  width: 16rem; /* matches Nextra's x:md:w-64 (256px) */
}
/* Keyboard-shortcut hint, pinned inside the right edge of the search pill.
   Pagefind's input lives in the same .site-search box; padding on the input
   (added below in the pagefind overrides) keeps typed text from overlapping
   this badge. Sized to match Nextra's <kbd> in the navbar search:
   x:h-5 (20px) x:text-[11px] x:px-1.5 (6px) x:rounded x:border. */
.site-header .search-kbd {
  position: absolute;
  top: 50%;
  right: 0.375rem; /* matches Nextra's x:end-1.5 (6px) */
  transform: translateY(-50%);
  display: inline-flex;
  align-items: center;
  gap: 0.25rem;
  height: 1.25rem; /* 20px = x:h-5 */
  padding: 0 0.375rem; /* 6px = x:px-1.5 */
  border: 1px solid var(--rule);
  border-radius: 4px;
  background: var(--bg);
  color: var(--muted);
  font-weight: 500;
  font-size: 0.6875rem; /* 11px = x:text-[11px] */
  font-family:
    ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono",
    "Courier New", monospace;
  pointer-events: none;
  z-index: 5;
}
.site-header .search-kbd .search-kbd-mac {
  display: none;
}
body.is-mac .site-header .search-kbd .search-kbd-mac {
  display: inline;
}
body.is-mac .site-header .search-kbd .search-kbd-other {
  display: none;
}
/* Theme toggle (sidebar footer): borderless icon + label button. Matches
   Nextra's ThemeSwitch (Select.Trigger) chrome — transparent background,
   hover fills with a translucent gray, no visible border. Click flips
   html.dark and persists the choice (see page.tsx scripts). */
.theme-toggle {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  background: transparent;
  border: none;
  border-radius: 6px;
  height: 2rem;
  padding: 0 0.6rem;
  color: var(--fg);
  font: inherit;
  font-size: 0.8rem;
  cursor: pointer;
  transition: background 0.15s;
}
.theme-toggle:hover {
  background: rgba(127, 127, 127, 0.15);
}
/* The trigger reflects the *selected* mode (Light / Dark / System), not the
   resolved colour — Nextra's ThemeSwitch shows the chosen option. Which
   icon+label is visible is driven by html[data-theme], set by the early-paint
   + menu scripts. */
.theme-toggle .icon-sun,
.theme-toggle .icon-moon,
.theme-toggle .icon-system,
.theme-toggle .label-light,
.theme-toggle .label-dark,
.theme-toggle .label-system {
  display: none;
}
html[data-theme="light"] .theme-toggle .icon-sun,
html[data-theme="light"] .theme-toggle .label-light,
html[data-theme="dark"] .theme-toggle .icon-moon,
html[data-theme="dark"] .theme-toggle .label-dark,
html[data-theme="system"] .theme-toggle .icon-system,
html[data-theme="system"] .theme-toggle .label-system,
html:not([data-theme]) .theme-toggle .icon-system,
html:not([data-theme]) .theme-toggle .label-system {
  display: inline-flex;
}
/* Theme menu (popover) — replaces the former direct toggle. The trigger keeps
   the .theme-toggle chrome; the menu opens upward from the sidebar-footer with
   Light / Dark / System options, mirroring Nextra's theme <Select>. */
.theme-switch {
  position: relative;
  display: inline-flex;
}
.theme-switch .theme-toggle {
  width: 100%;
  justify-content: flex-start;
}
.theme-switch-caret {
  margin-left: auto;
  opacity: 0.55;
}
.theme-menu {
  position: absolute;
  bottom: calc(100% + 0.4rem);
  left: 0;
  margin: 0;
  padding: 0.3rem;
  z-index: 50;
  min-width: max-content;
  list-style: none;
  background: var(--bg);
  border: 1px solid var(--rule);
  border-radius: 8px;
  box-shadow: 0 8px 28px rgba(0, 0, 0, 0.14);
}
.theme-menu-hidden {
  display: none;
}
.theme-option {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  width: 100%;
  padding: 0.35rem 0.6rem;
  background: transparent;
  border: none;
  border-radius: 6px;
  color: var(--fg);
  font: inherit;
  font-size: 0.8rem;
  white-space: nowrap;
  cursor: pointer;
}
.theme-option svg {
  flex: none;
  opacity: 0.8;
}
.theme-option:hover {
  background: var(--chrome-bg);
  color: var(--accent);
}
.theme-option[aria-checked="true"] {
  color: var(--accent);
  font-weight: 600;
}
/* Custom search input (search.js owns the <input>). Nextra navbar input:
   x:rounded-lg (8px) x:px-3 x:py-2 (12px / 8px) x:text-sm (14px). Right
   padding reserves room for the kbd hint pinned to the right edge. */
.site-search .search-input {
  width: 100%;
  padding: 0.5rem 4rem 0.5rem 0.75rem;
  border: none;
  border-radius: 8px;
  background: rgba(0, 0, 0, 0.05); /* matches Nextra's x:bg-black/[.05] */
  color: var(--fg);
  font: inherit;
  font-size: 0.875rem; /* 14px = x:text-sm */
  line-height: 1.25;
  transition: background 0.15s;
  -webkit-appearance: none;
  appearance: none;
}
html.dark .site-search .search-input {
  background: rgba(249, 250, 251, 0.1); /* matches x:dark:bg-gray-50/10 */
}
.site-search .search-input::placeholder {
  color: var(--muted);
}
.site-search .search-input:focus {
  outline: none;
  background: var(--bg);
  box-shadow: 0 0 0 1px var(--accent);
}
/* Browser's built-in search-cancel button is ugly; we don't render our
   own either — the panel closes on Escape / outside-click. */
.site-search .search-input::-webkit-search-cancel-button {
  -webkit-appearance: none;
  appearance: none;
}
/* Hide the kbd hint while the input has focus (Nextra parity: their
   <kbd> gets x:invisible x:opacity-0 when focused). */
.site-search:focus-within .search-kbd {
  display: none;
}

/* Dropdown panel — Nextra's <ComboboxOptions> chrome:
   x:bg-nextra-bg/70 x:backdrop-blur-md, x:rounded-xl x:shadow-xl,
   x:max-md:h-full but capped at min(100vh-5rem, 400px) on desktop,
   x:w-full x:md:w-[576px]. Hidden until search.js adds .open. */
.search-panel {
  display: none;
  position: absolute;
  top: calc(100% + 0.625rem);
  right: 0;
  width: min(36rem, calc(100vw - 2rem));
  max-height: min(calc(100vh - 5rem), 400px);
  max-height: min(calc(100dvh - 5rem), 400px);
  overflow-y: auto;
  background: rgb(var(--bg-rgb) / 0.7);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
  border: 1px solid var(--rule);
  border-radius: 12px;
  box-shadow: 0 10px 30px rgba(0, 0, 0, 0.15);
  padding: 0.625rem 0;
  z-index: 30;
}
.search-panel.open {
  display: block;
}
/* One page's results: section header (page title) + nested links. */
.search-page + .search-page {
  margin-top: 1rem;
}
.search-page-title {
  font-size: 0.75rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  color: var(--muted);
  border-bottom: 1px solid var(--rule);
  margin: 0 0.625rem 0.375rem;
  padding: 0 0.625rem 0.375rem;
}
.search-result {
  display: block;
  padding: 0.5rem 0.625rem;
  margin: 0 0.625rem;
  border-radius: 6px;
  text-decoration: none;
  color: var(--fg);
  scroll-margin: 0.5rem;
}
.search-result:hover,
.search-result.active {
  background: rgb(var(--accent-rgb) / 0.1);
  color: var(--accent);
}
.search-result-title {
  font-weight: 600;
  font-size: 0.95rem;
  line-height: 1.25;
}
.search-result-excerpt {
  font-size: 0.85rem;
  color: var(--muted);
  margin-top: 0.25rem;
  line-height: 1.35;
}
/* Pagefind wraps matched terms in <mark>; tint to the accent like
   Nextra's x:[&_mark]:bg-primary-600/80 x:[&_mark]:text-white. */
.search-result-excerpt mark {
  background: rgb(var(--accent-rgb) / 0.8);
  color: #fff;
  padding: 0 0.15em;
  border-radius: 2px;
}
.search-status {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  padding: 1.5rem 1.25rem;
  text-align: center;
  font-size: 0.875rem;
  color: var(--muted);
}
.search-error {
  color: #ef4444;
}
/* Tabler loader-2 (3/4 arc) spinner used in the search status row.
   currentColor inherits the surrounding .search-status muted tone. */
.search-spinner {
  flex-shrink: 0;
  animation: spin 1s linear infinite;
}
@keyframes spin {
  to {
    transform: rotate(360deg);
  }
}

aside.sidebar {
  position: sticky;
  top: var(--header-h);
  /* Matches Nextra's x:sticky x:top-(--nextra-navbar-height)
     x:h-[calc(100dvh-var(--nextra-navbar-height))]. Containing block is
     this element's grid cell inside .layout-wrapper — the wrapper ends
     above the footer, so the sticky element can't bleed past it.
     No background, no right border: Nextra's nextra-sidebar is just a
     transparent column on the page bg, separated from the article by
     whitespace alone. */
  align-self: start;
  height: calc(100vh - var(--header-h));
  height: calc(100dvh - var(--header-h));
  /* Vertical stack: search (only mobile), scrolling list, footer pinned
     to the bottom. Nextra renders the same three pieces with
     `x:flex x:flex-col` + `x:mt-auto` on the footer (sidebar.js:392, 417);
     `auto 1fr auto` grid was structurally equivalent but flex matches
     Nextra's behaviour byte-for-byte and avoids edge cases where the
     1fr track sized differently from the flex remainder. */
  display: flex;
  flex-direction: column;
  min-height: 0;
  /* Width changes smoothly when toggling between full and narrow. The grid
     column track on body animates the same way (see body transition below). */
  transition: width 0.2s ease-in-out;
}
aside.sidebar > .sidebar-search {
  /* In-sidebar search box — hidden on desktop (the navbar copy is
     visible there) and shown only ≤767px to stand in for the navbar
     search that's hidden at mobile widths. Mirrors Nextra's MobileNav,
     which wraps themeConfig.search in <div class="x:px-4 x:pt-4"> as
     the first child of <aside class="nextra-mobile-nav">. */
  display: none;
  padding: 1rem 1rem 0;
}
@media (max-width: 767px) {
  aside.sidebar > .sidebar-search {
    display: block;
  }
}
/* The base .site-search has no explicit width / position — only the
   navbar copy gets width: 16rem (see .site-header .site-search). Pin
   the search input to fill the sidebar column and drop the kbd-hint
   padding (no Cmd/K badge here on mobile). */
.sidebar-search .search-input {
  padding-right: 0.75rem;
}
/* In-sidebar dropdown sits inline below the input (taking layout space
   inside the search row, pushing the chapter list down) instead of
   absolute-positioning over the chapter list — Nextra's MobileNav uses
   the same vertical stack. */
.sidebar-search .search-panel {
  position: static;
  width: 100%;
  margin-top: 0.625rem;
  max-height: 50vh;
}
aside.sidebar > .sidebar-list {
  /* Chapter list grows to fill the space between the search row and the
     footer. With overflow-y:auto + min-height:0 it shrinks rather than
     forcing the sidebar to overflow when the chapter list is taller
     than the viewport. */
  flex: 1 1 0;
}
aside.sidebar > .sidebar-footer {
  /* margin-top: auto absorbs any leftover slack so the footer always sits
     at the bottom of the sidebar even when the chapter list is short
     (Nextra parity: sidebar.js:417 footer carries x:mt-auto). */
  margin-top: auto;
}
/* Chapter list — Nextra's <ul class="x:grid x:gap-1 x:p-4">: uniform 16px
   padding, items separated by a 4px grid gap rather than per-item margin.
   The mask-image pair is Nextra's .nextra-mask: two linear gradients
   composited (default add) so the visible region is opaque in the
   middle and fades to transparent in the top + bottom 20px. Effect:
   list rows visually fade into the header above and the footer below
   instead of getting a hard scroll-edge cutoff. */
aside.sidebar .sidebar-list {
  list-style: none;
  margin: 0;
  padding: 1rem;
  overflow-y: auto;
  min-height: 0;
  display: grid;
  /* Pack rows at the top — without this the grid stretches its rows to fill the
     tall rail when there are only a few items (e.g. a proposal's short chapter
     list), leaving huge gaps between them. */
  align-content: start;
  gap: 0.25rem;
  mask-image:
    linear-gradient(to bottom, transparent, #000 20px, transparent 100%),
    linear-gradient(to top, transparent, #000 20px, transparent 100%);
  -webkit-mask-image:
    linear-gradient(to bottom, transparent, #000 20px, transparent 100%),
    linear-gradient(to top, transparent, #000 20px, transparent 100%);
}
aside.sidebar li {
  margin: 0;
}
/* Each link — Nextra's per-item <a>: x:flex x:rounded x:px-2 x:py-1.5
   x:text-sm x:text-gray-600 (default), hover lifts the text to gray-900
   with a gray-100 bg fill. break-word so long Annex titles wrap. */
aside.sidebar .sidebar-list a {
  display: block;
  padding: 0.375rem 0.5rem;
  border-radius: 4px;
  color: var(--muted);
  text-decoration: none;
  font-size: 0.875rem;
  /* `calc(1.25 / 0.875)` mirrors Tailwind v4's text-sm line-height
     (style.css `--x-text-sm--line-height`) Nextra applies on its
     sidebar links. With unitless 1.35 the lines were ~6% tighter
     than Nextra and long wrapping chapter titles (e.g. "Annex E
     (informative) Corrections and Clarifications…") felt cramped. */
  line-height: calc(1.25 / 0.875);
  word-break: break-word;
  transition: background 0.15s, color 0.15s;
}
aside.sidebar .sidebar-list a:hover {
  background: rgba(127, 127, 127, 0.12);
  color: var(--fg);
}
/* Reading-time labels (idea C, injected in _config.ts): a muted "~12 min" on
   each chapter link, plus a spec-wide total line above the list. */
aside.sidebar .sidebar-list a .ch-time {
  float: right;
  margin-left: 0.5em;
  font-weight: 400;
  font-size: 0.78em;
  font-variant-numeric: tabular-nums;
  color: var(--muted);
  opacity: 0.6;
}
aside.sidebar .sidebar-readtime {
  padding: 0.85rem 1rem 0.4rem;
  color: var(--muted);
  font-size: 0.72rem;
  letter-spacing: 0.02em;
  opacity: 0.75;
}
/* Active item — Nextra's x:bg-primary-100 x:font-semibold x:text-primary-800
   (and the dark-mode primary-400/10 + primary-600). The HSL values fall out
   of Nextra's --nextra-primary-* tokens; lume-poc doesn't share that token
   system so the literal HSLs are inlined here. */
aside.sidebar li.current > a {
  background: hsl(212 100% 94%);
  color: hsl(212 100% 35%);
  font-weight: 600;
}
html.dark aside.sidebar li.current > a {
  background: hsl(204 100% 76% / 0.1);
  color: hsl(204 100% 65%);
}
/* Inline TOC under the active chapter — mirrors Nextra's <File> rendering
   h2 anchors as a child list of the active item (sidebar.js:249-258).
   Populated post-render by _config.ts. Visible at ≤1100px where
   aside.toc itself is display:none; hidden above so we don't duplicate
   the right-rail TOC. */
aside.sidebar .sidebar-toc {
  list-style: none;
  margin: 0.25rem 0 0;
  padding: 0;
  display: grid;
  gap: 0.125rem;
}
aside.sidebar .sidebar-toc li {
  margin: 0;
}
aside.sidebar .sidebar-toc a {
  display: block;
  padding: 0.25rem 0.5rem 0.25rem 1rem;
  border-radius: 4px;
  color: var(--muted);
  text-decoration: none;
  font-size: 0.8125rem;
  font-weight: 400;
  /* Match Nextra's text-sm line-height ratio (~1.428) for parity with
     the chapter list above; unitless so it scales with the slightly
     smaller font-size used here. */
  line-height: calc(1.25 / 0.875);
  word-break: break-word;
  background: transparent;
  transition: background 0.15s, color 0.15s;
}
aside.sidebar .sidebar-toc a:hover {
  background: rgba(127, 127, 127, 0.12);
  color: var(--fg);
}
@media (min-width: 1101px) {
  aside.sidebar .sidebar-toc {
    display: none;
  }
}
/* Section break: thin top border + extra space above the first item of each
   `group` (annex, back). Matches the implicit Annex / Bibliography break that
   the Nextra sidebar conveys by virtue of the title prefixes alone. */
aside.sidebar li.group-start {
  margin-top: 0.75rem;
  padding-top: 0.75rem;
  border-top: 1px solid var(--rule);
}
/* Non-scrolling sidebar footer (theme toggle lives here). Matches the
   .nextra-sidebar-footer affordance Nextra uses — same top border,
   no bg fill (the sidebar is transparent over the page bg). The
   horizontal margin (Nextra's x:mx-4) is what makes the border-top
   render shorter than the full sidebar width: the element itself is
   inset, so the border drawn on it inherits that narrower box. */
aside.sidebar .sidebar-footer {
  margin: 0 1rem;
  padding: 1rem 0;
  border-top: 1px solid var(--rule);
}
/* Sidebar footer row: theme toggle (grows) + collapse button (fixed). */
aside.sidebar .sidebar-footer {
  display: flex;
  align-items: center;
  gap: 0.4rem;
}
aside.sidebar .sidebar-footer .theme-switch {
  flex: 1 1 auto;
}
/* Collapse button: borderless icon-only square with hover bg-fill,
   matching Nextra's footer toggle Button (x:rounded-md x:p-2
   x:text-gray-600 + hover bg-gray-200). */
.sidebar-collapse-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2rem;
  height: 2rem;
  padding: 0;
  background: transparent;
  border: none;
  border-radius: 6px;
  color: var(--muted);
  cursor: pointer;
  flex: 0 0 auto;
  transition: background 0.15s, color 0.15s;
}
.sidebar-collapse-btn:hover {
  color: var(--fg);
  background: rgba(127, 127, 127, 0.15);
}
/* Collapsed sidebar: narrow rail (80px) instead of disappearing — matches
   nextra-theme-docs, which toggles between x:w-64 (256px) and x:w-20 (80px).
   Body grid track + sidebar both transition for a smooth slide. */
:root {
  --sidebar-w-narrow: 80px;
}
body.sidebar-collapsed .layout-wrapper {
  grid-template-columns: var(--sidebar-w-narrow) minmax(0, 1fr) var(--toc-w);
}
/* Hide the scrollable chapter list — only the footer's icon-only buttons
   remain visible while collapsed. The "Full read · ~Nh" total goes with it. */
body.sidebar-collapsed aside.sidebar .sidebar-list,
body.sidebar-collapsed aside.sidebar .sidebar-readtime {
  display: none;
}
/* Footer reflow: stack the two buttons vertically so they read as a column
   in the narrow rail, anchored to the bottom of the sidebar (Nextra's
   x:sticky x:bottom-0 affordance). margin-top: auto is the belt-and-braces
   anchor in case the sidebar's `1fr auto` grid track collapses when the
   list is display:none. */
body.sidebar-collapsed aside.sidebar .sidebar-footer {
  flex-direction: column;
  align-items: center;
  gap: 0.5rem;
  padding: 0.75rem 0;
  margin-top: auto;
}
/* Both footer buttons share the same lite-mode chrome while collapsed:
   borderless, square, hover bg-fill — matches Nextra's collapse Button
   (x:rounded-md x:p-2 x:text-gray-600 + hover bg-gray-200) and
   ThemeSwitch lite. Keeps the two buttons visually consistent in the
   narrow rail. */
body.sidebar-collapsed aside.sidebar .theme-toggle,
body.sidebar-collapsed .sidebar-collapse-btn {
  flex: 0 0 auto;
  width: 2rem;
  height: 2rem;
  padding: 0;
  justify-content: center;
  border: none;
  background: transparent;
}
body.sidebar-collapsed aside.sidebar .theme-toggle:hover,
body.sidebar-collapsed .sidebar-collapse-btn:hover {
  background: rgba(127, 127, 127, 0.15);
}
body.sidebar-collapsed aside.sidebar .theme-switch {
  flex: 0 0 auto;
}
body.sidebar-collapsed aside.sidebar .theme-toggle .theme-toggle-label,
body.sidebar-collapsed aside.sidebar .theme-switch-caret {
  display: none;
}
/* Flip just the arrow path of the collapse-button SVG (not the whole icon —
   the surrounding "panel" path stays put, matching Nextra). */
body.sidebar-collapsed .sidebar-collapse-btn .collapse-arrow {
  transform-origin: 35% 50%;
  transform: rotate(180deg);
}
@media (max-width: 1100px) {
  body.sidebar-collapsed .layout-wrapper {
    grid-template-columns: var(--sidebar-w-narrow) minmax(0, 1fr);
  }
}

main {
  /* Match Nextra's article: x:pt-4 x:pb-8 x:px-4 x:md:px-12 — 16px top,
     32px bottom, 16px sides on mobile, 48px sides on md+. The
     overflow-wrap rule mirrors Nextra's x:break-words on the
     <article> wrapper (catch-all-route.jsx); without it long
     unbreakable tokens (e.g. `InputElementRegExpOrTemplateTail`
     inside <emu-nt>, or unbroken URLs) push past the column edge
     and the page picks up a horizontal scrollbar on narrow phones. */
  padding: 1rem 1rem 2rem;
  min-width: 0;
  overflow-wrap: break-word;
}
@media (min-width: 768px) {
  main {
    padding-inline: 3rem;
  }
}

/* Prev / next chapter pagination: plain text + chevron icon, with a thin
   top-border separator above. Matches what nextra-theme-docs renders — no
   card chrome, no "Previous"/"Next" label. The next link sits right-aligned
   via margin-inline-start: auto so it works whether prev exists or not. */
.prev-next {
  display: flex;
  align-items: center;
  margin-top: 4rem;
  margin-bottom: 2rem;
  padding-top: 2rem;
  border-top: 1px solid var(--rule);
}
.prev-next-link {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  max-width: 50%;
  padding: 0.5rem 0;
  color: var(--muted);
  text-decoration: none;
  font-size: 1rem;
  font-weight: 500;
  line-height: 1.4;
  transition: color 0.15s;
  word-break: break-word;
}
.prev-next-link:hover {
  color: var(--fg);
}
.prev-next-link.prev-link {
  padding-inline-end: 1rem;
}
.prev-next-link.next-link {
  margin-inline-start: auto;
  text-align: end;
  padding-inline-start: 1rem;
}
.prev-next-arrow {
  flex: 0 0 auto;
}
.prev-next-arrow.flip {
  transform: rotate(180deg);
}
@media (min-width: 768px) {
  .prev-next-link {
    font-size: 1.125rem;
  }
}

/* Right-rail TOC — port of nextra-theme-docs' <nav class="nextra-toc">:
   sticky 3-row grid (heading / list / footer), borderless, 14px text.
   Same trick as the sidebar: let the row stretch this to its cell
   (default align-self) and cap with max-height. That way the sticky
   element rides the cell's bottom edge and slides up as the footer
   scrolls into view, instead of overflowing onto the footer. */
aside.toc {
  position: sticky;
  top: var(--header-h);
  align-self: start;
  height: calc(100vh - var(--header-h));
  height: calc(100dvh - var(--header-h));
  display: grid;
  /* position strip (top) / heading / scrolling list / feedback (bottom) */
  grid-template-rows: min-content min-content 1fr min-content;
  font-size: 0.875rem; /* x:text-sm */
}
/* Proposal pages have no position strip, so their TOC is heading / list / repo
   link — three rows. Without this the scrolling list would land in a
   min-content row and overflow instead of scrolling. */
aside.toc.toc-proposal {
  grid-template-rows: min-content 1fr min-content;
}
/* "On This Page" heading — plain semibold label, no uppercase / muted /
   spacing, matching Nextra's <p class="x:pt-6 x:px-4 x:font-semibold">. */
aside.toc h2 {
  margin: 0;
  /* The position strip above now carries the top clearance, so the heading
     sits a smaller gap below it. */
  padding: 0.9rem 1rem 0;
  font-size: inherit;
  font-weight: 600;
  letter-spacing: -0.01em;
  color: var(--fg);
}
aside.toc ol {
  list-style: none;
  padding: 1rem;
  margin: 0;
  overflow-y: auto;
  overscroll-behavior-y: contain;
  /* Same .nextra-mask two-gradient fade the chapter list uses: rows
     dissolve into the heading above and the feedback link below
     instead of getting a hard scroll-edge cutoff. */
  mask-image:
    linear-gradient(to bottom, transparent, #000 20px, transparent 100%),
    linear-gradient(to top, transparent, #000 20px, transparent 100%);
  -webkit-mask-image:
    linear-gradient(to bottom, transparent, #000 20px, transparent 100%),
    linear-gradient(to top, transparent, #000 20px, transparent 100%);
}
aside.toc li {
  margin: 0.5rem 0; /* x:my-2 */
}
/* nextra renders the TOC section number (.secnum) in monospace, whose taller
   glyph box lifts each TOC line box by ~0.5px on 2x displays; lume keeps the
   number in the sans body font (by design), so each line sits 0.5px shorter.
   Add that 0.5px back to the gap between rows so the overall TOC rhythm tracks
   nextra. li margins collapse, so nudging only li+li's top margin (max(8, 8.5)
   = 8.5px gap) keeps the first/last row flush at 8px like nextra. */
aside.toc li + li {
  margin-top: calc(0.5rem + 0.5px);
}
/* Depth-based indent matches Nextra's x:ms-3 (12px) / x:ms-6 (24px) /
   x:ms-9 (36px) on nested headings. Depth comes from _config.ts based
   on how many <emu-clause> ancestors a section has. */
aside.toc li[data-level="2"] a {
  margin-inline-start: 0.75rem;
}
aside.toc li[data-level="3"] a {
  margin-inline-start: 1.5rem;
}
aside.toc li[data-level="4"] a {
  margin-inline-start: 2.25rem;
}
aside.toc li[data-level="5"] a {
  margin-inline-start: 3rem;
}
aside.toc a {
  display: block;
  color: var(--muted);
  text-decoration: none;
  /* Match Nextra's text-sm line-height ratio (~1.428) — the toc itself
     inherits font-size 0.875rem from aside.toc so this gives the same
     ~1.25rem line box Nextra uses on TOC anchors. */
  line-height: calc(1.25 / 0.875);
  word-break: break-word;
  transition: color 0.15s;
}
/* Top-level entries are semibold; nested ones are normal weight —
   matches Nextra's per-depth class application. */
aside.toc li[data-level="1"] > a {
  font-weight: 600;
}
aside.toc a:hover {
  color: var(--fg);
}
/* Scrollspy: the IntersectionObserver in page.tsx tags whichever
   <emu-clause id=…> currently sits near the top of the viewport, and
   flips `.active` on the matching <a> in both the right-rail TOC and
   the inline TOC under the current chapter. Matches Nextra's
   `classes.active` (sidebar.js:18) — same primary token, semibold. */
aside.toc a.active,
aside.sidebar .sidebar-toc a.active {
  color: hsl(212 100% 35%);
  font-weight: 600;
}
html.dark aside.toc a.active,
html.dark aside.sidebar .sidebar-toc a.active {
  color: hsl(204 100% 65%);
}
/* Footer row under the TOC list: thin top border, x:py-4 x:mx-4 inset,
   x:gap-2. Holds the "Question? Give us feedback" external link (and a
   stub scroll-to-top button later if we want it). */
aside.toc .toc-feedback {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  margin: 0 1rem;
  padding: 1rem 0;
  border-top: 1px solid var(--rule);
  color: var(--muted);
  font-size: 0.75rem; /* x:text-xs */
  font-weight: 500;
  text-decoration: none;
  transition: color 0.15s;
}
aside.toc .toc-feedback:hover {
  color: var(--fg);
}
/* On a page with no sections the TOC heading + list are removed (see the
   .toc-empty flag in _config.ts), so the feedback link's top rule is dropped. */
aside.toc.toc-empty .toc-feedback {
  border-top: none;
}
/* Whole-spec position strip (idea F): a thin V3 tick timeline + dot showing how
   far through the entire spec the current position is. Pinned at the top of the
   rail (above "On This Page"); the bottom rule separates it from the heading.
   reading-progress.js advances the dot/fill/label; per-page data (data-before/
   -span) + ticks + chapter segments come from _config.ts. */
aside.toc .spec-pos {
  margin: 0 1rem;
  padding: 1.5rem 0 0.9rem;
  border-bottom: 1px solid var(--rule);
}
/* Taller than the visible line so each chapter segment is comfortable to hover;
   the 2px line itself is drawn by ::before, the dot/fill/ticks ride its centre. */
aside.toc .spec-pos .sp-track {
  display: block;
  position: relative;
  height: 14px;
}
aside.toc .spec-pos .sp-track::before {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  top: 50%;
  height: 2px;
  border-radius: 1px;
  background: color-mix(in srgb, var(--muted) 25%, transparent);
  transform: translateY(-50%);
}
aside.toc .spec-pos .sp-done {
  position: absolute;
  left: 0;
  top: 50%;
  height: 2px;
  border-radius: 1px;
  background: var(--accent);
  opacity: 0.5;
  transform: translateY(-50%);
}
aside.toc .spec-pos .sp-tick {
  position: absolute;
  top: 50%;
  width: 1px;
  height: 6px;
  background: color-mix(in srgb, var(--muted) 35%, transparent);
  transform: translate(-50%, -50%);
}
/* Transparent per-chapter hit areas over the line; on hover, a highlight band
   spans the chapter and reading-progress.js shows its name in .sp-pop. */
aside.toc .spec-pos .sp-seg {
  position: absolute;
  top: 0;
  height: 100%;
  z-index: 2;
  cursor: pointer;
}
aside.toc .spec-pos .sp-seg:hover::before {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  top: 50%;
  height: 7px;
  border-radius: 2px;
  background: color-mix(in srgb, var(--accent) 30%, transparent);
  transform: translateY(-50%);
}
aside.toc .spec-pos .sp-dot {
  position: absolute;
  top: 50%;
  z-index: 3;
  pointer-events: none;
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: var(--accent);
  border: 2px solid rgb(var(--bg-rgb));
  box-shadow: 0 0 0 1px var(--accent);
  transform: translate(-50%, -50%);
}
aside.toc .spec-pos .sp-pop {
  position: absolute;
  /* Fixed box the full width of the timeline, always in the same spot — in the
     cramped rail a segment-anchored, variable-width popover jumped around. Below
     the timeline (not above), since the strip sits right under the header and a
     popover above would slip behind the higher-stacked, blurred header. */
  top: calc(100% + 6px);
  left: 0;
  right: 0;
  z-index: 36;
  padding: 0.35rem 0.5rem;
  border: 1px solid var(--rule);
  border-radius: 6px;
  background: var(--chrome-bg);
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.12);
  color: var(--fg);
  font-size: 0.7rem;
  line-height: 1.3;
  text-align: left;
  pointer-events: none;
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.1s;
}
aside.toc .spec-pos .sp-pop.show {
  opacity: 1;
  visibility: visible;
}
aside.toc .spec-pos .sp-label {
  display: block;
  margin-top: 0.5rem;
  font-size: 0.72rem;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
}
aside.toc .toc-feedback-arrow {
  width: 1em;
  height: 1em;
  flex-shrink: 0;
}

/* Footer matches the former Nextra spec-layout: two centred
   columns of grey text. Outer flex centres the columns; each column is a
   top-aligned stack with 0.4rem between rows (Nextra's gap). py-12 = 3rem
   top/bottom; safe-area insets pad the sides so it doesn't hug the screen
   edge on notched phones. Tinted chrome (--chrome-bg + border-top) lifts
   it off the article so the page bottom has a clear visual boundary. */
/* Footer chrome wrapper — mirrors Nextra's
   <div class="x:bg-gray-100 x:pb-[env(safe-area-inset-bottom)]
                x:dark:bg-neutral-900 x:print:bg-transparent"> that
   holds the Switchers row + hr + actual <footer>. Background and the
   thin separator above live here so the Switchers strip and the
   editions/about columns share one visual surface. The
   safe-area-inset-bottom padding pads under the iOS home indicator
   without inflating the editions row's own bottom padding. */
.site-footer-wrap {
  background: var(--chrome-bg);
  border-top: 1px solid var(--rule);
  padding-bottom: env(safe-area-inset-bottom);
}
/* Hairline above the editions columns — matches Nextra's
   <hr className="nextra-border"> separator, which it emits above the footer
   even when the (sidebar-only) <Switchers> row is absent. */
.site-footer-divider {
  margin: 0;
  border: 0;
  border-top: 1px solid var(--rule);
}
footer.site-footer {
  padding: 3rem max(env(safe-area-inset-right), 1.5rem) 3rem
    max(env(safe-area-inset-left), 1.5rem);
  color: #4b5563;
  font-size: 0.875rem;
  line-height: 1.6;
}
/* gray-600 sits at 2.3:1 on the dark page — lift to gray-400 (Nextra's own
   dark footer tone). */
html.dark footer.site-footer {
  color: #9ca3af;
}
footer.site-footer .footer-cols {
  display: flex;
  justify-content: center;
  gap: 4rem;
  flex-wrap: wrap;
  width: 100%;
}
footer.site-footer .footer-col {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 0.4rem;
}
footer.site-footer a {
  color: inherit;
  text-decoration: none;
}
footer.site-footer a:hover {
  text-decoration: underline;
}
footer.site-footer .copyright {
  margin-top: 0.5rem;
  color: #6b7280;
}
footer.site-footer .copyright a {
  text-decoration: underline;
}

/* Hamburger button: hidden on desktop, visible on mobile (≤767px).
   Borderless icon-only button, matching Nextra's
   <Button class="nextra-hamburger x:md:hidden"> — default variant is
   just "x:transition x:cursor-pointer" (button.js:34), with the icon
   itself supplying the visual. Background fades in only while the
   menu is open (Nextra's `active && "x:bg-gray-400/20"` at
   navbar/index.client.js:229). Inter-item spacing comes from
   .site-header-inner's gap: 1rem. */
.menu-toggle {
  display: none;
  align-items: center;
  justify-content: center;
  background: transparent;
  border: none;
  border-radius: 4px;
  padding: 0.25rem;
  color: var(--fg);
  cursor: pointer;
  transition: background 0.15s;
}
body.menu-open .menu-toggle {
  background: rgba(127, 127, 127, 0.2); /* x:bg-gray-400/20 */
}
/* Hamburger ⇄ X morph — mirrors Nextra's .nextra-hamburger svg block at
   nextra-theme-docs/dist/style.css:3070-3108. The SVG is three pieces:
   wrapping <g>s for the top and bottom lines (so each line has its
   own rotation anchor), and a bare <path> for the middle line that
   simply fades out. When .menu-open is on body, the top <g> rotates
   45° + its path slides down 6 units, the bottom rotates -45° + slides
   up 6 units, so both meet at the centre to draw the X.
   transform-box: fill-box pins transform-origin to each <g>'s own
   bounding box (otherwise it would default to the SVG view-box on
   modern browsers and the rotation centre would be wrong). */
.menu-toggle svg {
  --hamb-anim: transform 0.15s cubic-bezier(0.25, 1, 0.5, 1);
}
.menu-toggle svg > g {
  transform-box: fill-box;
  transform-origin: center;
  transition: var(--hamb-anim);
}
.menu-toggle svg > path {
  opacity: 1;
  transition:
    var(--hamb-anim) 0.15s,
    opacity 0.15s ease 0.15s;
}
body.menu-open .menu-toggle svg > path {
  opacity: 0;
  transition:
    var(--hamb-anim),
    opacity 0s ease 0.15s;
}
body.menu-open .menu-toggle svg > g {
  transition: var(--hamb-anim) 0.15s;
}
body.menu-open .menu-toggle svg > g:nth-of-type(1) {
  transform: rotate(45deg);
}
body.menu-open .menu-toggle svg > g:nth-of-type(1) > path {
  transform: translate3d(0, 6px, 0);
}
body.menu-open .menu-toggle svg > g:nth-of-type(2) {
  transform: rotate(-45deg);
}
body.menu-open .menu-toggle svg > g:nth-of-type(2) > path {
  transform: translate3d(0, -6px, 0);
}
@media (prefers-reduced-motion: reduce) {
  .menu-toggle svg > g,
  .menu-toggle svg > path {
    transition-property: none !important;
  }
}

@media (max-width: 1100px) {
  .layout-wrapper {
    grid-template-columns: var(--sidebar-w) minmax(0, 1fr);
  }
  aside.toc {
    display: none;
  }
}
@media (max-width: 767px) {
  /* Mobile breakpoint matches Nextra's md (768px). Below 768px the
     navbar hides the inline search (Nextra's x:max-md:hidden on the
     search element) and shows the hamburger (Nextra's x:md:hidden on
     the Button); the sidebar becomes a fullscreen overlay. */
  .layout-wrapper {
    grid-template-columns: minmax(0, 1fr);
  }
  .menu-toggle {
    display: inline-flex;
  }
  .site-header .site-search {
    display: none;
  }
  /* Fullscreen mobile nav — matches Nextra's
     <aside class="nextra-mobile-nav x:flex x:flex-col x:fixed x:inset-0
                   x:pt-(--nextra-navbar-height) x:z-20 x:bg-nextra-bg
                   x:[transform:translate3d(0,-100%,0)] (or 0,0,0)">.
     The base aside.sidebar rule is already display:flex flex-col, so we
     just pin the fixed positioning and the navbar offset here.
     Use an explicit `height: 100dvh` instead of `inset: 0`'s implicit
     layout-viewport height — on iOS Safari with the URL bar visible,
     `inset:0` sizes to the larger layout viewport so the footer ends
     up below the visible bottom edge. dvh tracks the current visible
     viewport. */
  aside.sidebar {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    height: 100vh; /* fallback */
    height: 100dvh;
    padding-top: var(--header-h);
    width: auto;
    z-index: 20;
    transform: translate3d(0, -100%, 0);
    background: var(--bg);
    /* x:overscroll-contain — stops scrolls inside the mobile nav from
       chaining out to the (now scroll-locked) body, and disables iOS
       pull-to-refresh on the panel. */
    overscroll-behavior: contain;
    /* x:[contain:layout_style] — CSS containment isolates the panel's
       layout/paint costs so opening/closing doesn't reflow the page
       behind it. */
    contain: layout style;
  }
  body.menu-open aside.sidebar {
    transform: translate3d(0, 0, 0);
  }
  /* Lock background scrolling on <html> while the menu is open —
     matches Nextra's `documentElement.classList.toggle("x:max-md:overflow-hidden", hasMenu)`
     pattern (stores/menu.js:10). Without this, the page underneath
     keeps responding to swipes even though it's invisible behind the
     fullscreen panel. */
  html.menu-open {
    overflow: hidden;
  }
  /* Desktop-only sidebar collapse rail doesn't apply at fullscreen widths.
     Nextra's mobile-nav footer just renders theme + locale switchers
     (sidebar.js:417), no collapse button. */
  .sidebar-collapse-btn {
    display: none;
  }
  /* The footer is already at the viewport bottom via the aside.sidebar
     grid (auto 1fr auto), but on iOS the home indicator overlays the
     bottom edge and steals tap area from the theme toggle. Reserve
     env(safe-area-inset-bottom) the same way Nextra's site footer
     does (x:pb-[env(safe-area-inset-bottom)] in footer/index.js:53). */
  aside.sidebar > .sidebar-footer {
    padding-bottom: calc(1rem + env(safe-area-inset-bottom));
  }
}

/* Print: drop the chrome so the spec body owns the whole page. Mirrors
   Nextra's x:print:hidden on the navbar, sidebar, TOC, and pagination
   (navbar/index.js:36, sidebar.js:519, wrapper.client.js:25,
   pagination.js:34); footer stays visible but its tinted background
   goes transparent (footer/index.js:53 x:print:bg-transparent). The
   layout-wrapper grid collapses to a single column so the article
   spans the full printable width. */
@media print {
  .site-header,
  aside.sidebar,
  aside.toc,
  .prev-next,
  .skip-nav {
    display: none;
  }
  .layout-wrapper {
    grid-template-columns: minmax(0, 1fr);
  }
  .site-footer-wrap,
  footer.site-footer {
    background: transparent;
  }
  .site-footer-divider {
    display: none;
  }
}

/* ── End of page chrome ──────────────────────────────────────────────── */

/* Nextra's per-page breadcrumb only contains a single item — the current
   page's display label from _meta.js (e.g. "1. Scope") — which duplicates
   the in-page H1. Hide it. */
.nextra-breadcrumb {
  display: none;
}

/* Each section is wrapped in <emu-clause id="sec-…"> (and the chapter root
   in <emu-clause|emu-intro|emu-annex>) so the DOM mirrors tc39.es/ecma262.
   These custom elements have no default styling, so make them block-level
   containers; the inner <h*> and <Sec> divs do all the visual work. */
emu-clause,
emu-intro,
emu-annex {
  display: block;
}

/* The section number prefix in headings (`<span class="secnum">5.1.2</span>`).
   A tabular monospace gives the digits a steady column even across
   variable-width headings, with a touch of opacity so the title text leads. */
/* tc39.es ecmarkup.css does just two things with .secnum:
     h1 .secnum         { text-decoration: none; margin-right: 5px; }
     h1 .secnum:empty   { margin: 0; padding: 0; }
   No font / opacity / tabular-numeral tweaks — the section number
   inherits the same serif and weight as the rest of the heading and
   sits 5px to the left of the title text. Match that. */
h1 .secnum {
  text-decoration: none;
  margin-right: 5px;
}
h1 .secnum:empty {
  margin: 0;
  padding: 0;
}

/* One panel surface — semi-transparent neutral bg + 1px border + radius —
   shared by emu-note callouts, grammar <pre>, and inline <code> chips, so
   they feel like one design system rather than three accidents. Borrowed
   from Linear's docs (where pre/note/inline-code all share the same chrome). */
.ecma-spec {
  --ecma-panel-bg: rgba(115, 115, 115, 0.08);
  --ecma-panel-border: rgba(115, 115, 115, 0.25);
  --ecma-panel-radius: 6px;
}

/* Measure / breakout grid: prose lives in a fixed-width `main` track (the
   reading measure); wide blocks (tables, figures) span into the extra `1fr`
   to the right via `grid-column`. The grid is applied to each <emu-clause>
   (and the outer container) because Sec wrappers are display:contents — the
   real block ancestors of the prose are the clauses themselves. */
.ecma-spec,
.ecma-spec emu-clause,
.ecma-spec emu-intro,
.ecma-spec emu-annex {
  display: grid;
  grid-template-columns:
    [main-start] minmax(0, var(--ecma-measure, 48rem)) [main-end]
    minmax(0, 1fr) [wide-end];
}

/* Vertical rhythm: em-based so spacing tracks font size, with one knob
   (--ecma-rhythm) to scale density. Grid items don't margin-collapse, so
   spacing is driven from a single margin-top between adjacent siblings. */
.ecma-spec :is(emu-clause, emu-intro, emu-annex) > *,
.ecma-spec > * {
  grid-column: main;
  min-width: 0;
}
.ecma-spec :is(emu-clause, emu-intro, emu-annex) > * + *,
.ecma-spec > * + * {
  margin-top: calc(1.5em * var(--ecma-rhythm, 1));
}
/* Tables and figures break out of the measure into the wider column. */
.ecma-spec :is(emu-table, emu-figure) {
  grid-column: main-start / wide-end;
}
.ecma-spec emu-table {
  display: block;
  overflow-x: auto;
}
/* Long monospace lines — code examples, and the ES1/ES2 chapter-15 date
   formulas (93-char lines) — scroll inside the measure instead of widening
   the page, same treatment as emu-table. Without an overflow value the
   automatic grid minimum keeps a <pre> at min-content width, so on narrow
   viewports it blows past the viewport and the whole page pans. */
.ecma-spec pre {
  overflow-x: auto;
}
/* Code blocks get the shared panel surface — this is the framing the
   hljs-zeroing rule below assumes the surrounding <pre> provides (tc39.es
   likewise frames pre and strips the theme's inner chrome). The .es2-math
   formula pres are typeset math, not code — no box in print, none here. */
.ecma-spec pre:not(.es2-math) {
  background: var(--ecma-panel-bg);
  border: 1px solid var(--ecma-panel-border);
  border-radius: var(--ecma-panel-radius);
  padding: 0.75em 1em;
}
.ecma-spec emu-table,
.ecma-spec emu-table + * {
  margin-top: calc(2em * var(--ecma-rhythm, 1));
}
/* Paragraphs inside the spec: the * + * grid rule above only fires for
   direct grid children of <emu-clause>, but in practice spec <p>s sit
   inside a Sec-wrapper <div> so they never match — they'd otherwise
   collapse to the browser default 1em (cramped). Match Nextra's
   mdx-components/index.js, which applies `not-first:mt-[1.25em]
   leading-7` to every <p> regardless of container. Reset margin to 0
   so the rhythm comes only from the explicit `+ 1.25em` between
   adjacent paragraphs (no double-margin from browser defaults). */
.ecma-spec p {
  margin-block: 0;
  line-height: 1.75rem;
}
.ecma-spec p:not(:first-child) {
  margin-top: 1.25em;
}
/* Paragraphs nested inside notes / lists / quotes get the same 1.25em
   rhythm but also need a bottom margin so they don't run into the
   container's own padding/border (Nextra applies it via margin-block
   on the <p> itself). Specificity wins over the .ecma-spec p rule
   above. */
.ecma-spec :is(emu-note, li, blockquote) > p {
  margin-block: 1.25em;
  line-height: 1.75rem;
}

/* List + blockquote dimensions — port of Nextra's mdx-components/index.js:
   ul gets list-disc + ms-[1.5em]; ol gets list-decimal + ms-6 (= 1.5rem);
   li gets my-[.5em]; nested ul/ol get my-[.75em]; blockquote gets
   ps-[1.5em] + border-s-2. lume-poc previously inherited browser
   defaults (~40px indent, 0 li margin) so list rhythm drifted from
   Nextra's. */
.ecma-spec ul {
  padding-inline-start: 1.5em;
  list-style: disc;
}
.ecma-spec ol {
  padding-inline-start: 1.5rem; /* ms-6 = 24px */
  list-style: decimal;
}
.ecma-spec li {
  margin: 0.5em 0;
}
.ecma-spec :is(ul, ol) :is(ul, ol) {
  margin: 0.75em 0;
}
.ecma-spec blockquote {
  padding-inline-start: 1.5em;
  border-inline-start: 2px solid var(--rule);
}

.ecma-spec emu-alg {
  display: block;
}

/* Reading zone (proposals 3–5): a ~17px reading size with a relaxed
   line-height, the measure exposed as a variable, and an em-based heading
   scale so headings track the body size. */
main[data-pagefind-body] {
  --ecma-measure: 48rem;
  /* 18px, matching tc39.es's `body { font-size: 18px }` (ecmarkup.css).
     line-height ratio stays 1.65 (tc39 uses 1.5 — we keep the slightly
     airier measure deliberately). */
  font-size: 1.125rem;
  line-height: 1.65;
  /* Serif body text mirrors tc39.es's `body { font-family: 'IBM Plex
     Serif', serif }` (ecmarkup.css). Scoped to the main reading zone
     so the chrome (header / sidebar / footer) keeps its sans stack. */
  font-family: var(--font-serif);
  /* tc39.es sets `font-variant-numeric: slashed-zero` on <body>. The Plex
     SlashedZero subsets we ship already default to a slashed zero, but the
     property keeps the distinction for any fallback face (and matches tc39
     explicitly). Scoped here so it rides with the serif reading zone. */
  font-variant-numeric: slashed-zero;
}
/* Headings stay aligned to the prose measure; only tables/figures break out. */
main[data-pagefind-body] > :is(h1, h2, h3, h4, h5, h6) {
  max-width: var(--ecma-measure);
}
/* Slight negative tracking tightens large headings (proposal 4). */
main[data-pagefind-body] :is(h1, h2, h3, h4) {
  letter-spacing: -0.011em;
}

/* Heading sizes — em-relative so they scale with the body reading size.
   Margins are NOT set here on purpose: tc39.es puts the section break
   margin on the <emu-clause>/<emu-intro>/<emu-annex> container itself
   (rules below), not on the heading, so the headings only need their
   font scale + a margin-bottom of 0 to mirror tc39.es's general
   `h1 { margin-bottom: 0 }` (ecmarkup.css). The * + * rule further
   down already supplies the gap from heading to the first paragraph. */
.ecma-spec :is(emu-clause, emu-intro, emu-annex) > h1 {
  margin-top: 0;
  margin-bottom: 0;
  /* tc39.es applies `line-height: 1em` to every h1 via its generic
     `h1` rule (ecmarkup.css). Without this our headings inherit
     main[data-pagefind-body]'s 1.65 ratio so each heading gets a
     line box ~65% taller than the glyph — tc39's compact 1em ratio
     keeps the heading flush to its baseline. */
  line-height: 1em;
  /* Section-heading scale ported straight from tc39.es ecmarkup.css
     (emu-clause/intro/annex h1 by depth: 2 / 1.56 / 1.25 / 1.11 / 1 / .9em).
     Now that the reading base is tc39's 18px (above), these em values land
     on tc39's exact px with no rescaling. Depth = emu-clause/intro/annex
     ancestor count; the deeper rule wins by specificity (more :is()
     compounds), the same mechanism as the section-margin scale below.
     This rule is depth-1; depths 2–6 override it below. */
  font-size: 2em;
}
.ecma-spec
  :is(emu-clause, emu-intro, emu-annex)
  :is(emu-clause, emu-intro, emu-annex)
  > h1 {
  font-size: 1.56em;
}
.ecma-spec
  :is(emu-clause, emu-intro, emu-annex)
  :is(emu-clause, emu-intro, emu-annex)
  :is(emu-clause, emu-intro, emu-annex)
  > h1 {
  font-size: 1.25em;
}
.ecma-spec
  :is(emu-clause, emu-intro, emu-annex)
  :is(emu-clause, emu-intro, emu-annex)
  :is(emu-clause, emu-intro, emu-annex)
  :is(emu-clause, emu-intro, emu-annex)
  > h1 {
  font-size: 1.11em;
}
.ecma-spec
  :is(emu-clause, emu-intro, emu-annex)
  :is(emu-clause, emu-intro, emu-annex)
  :is(emu-clause, emu-intro, emu-annex)
  :is(emu-clause, emu-intro, emu-annex)
  :is(emu-clause, emu-intro, emu-annex)
  > h1 {
  font-size: 1em;
}
.ecma-spec
  :is(emu-clause, emu-intro, emu-annex)
  :is(emu-clause, emu-intro, emu-annex)
  :is(emu-clause, emu-intro, emu-annex)
  :is(emu-clause, emu-intro, emu-annex)
  :is(emu-clause, emu-intro, emu-annex)
  :is(emu-clause, emu-intro, emu-annex)
  > h1 {
  font-size: 0.9em;
}

/* Section break spacing lives on the clause container, not the heading
   — mirrors tc39.es's ecmarkup.css emu-clause/intro/annex margin-top
   scale that shrinks by depth: 4em → 3.12em → 2.5em → 2.22em → 2em
   → 1.8em. The first clause at the top of the page suppresses its
   own margin so the article doesn't start with a 4em gap. With our
   grid layout these clause margins don't collapse with the heading
   inside, which is why the heading margins above were zeroed out. */
.ecma-spec :is(emu-clause, emu-intro, emu-annex) {
  margin-top: 4em;
}
.ecma-spec
  :is(emu-clause, emu-intro, emu-annex)
  :is(emu-clause, emu-intro, emu-annex) {
  margin-top: 3.12em;
}
.ecma-spec
  :is(emu-clause, emu-intro, emu-annex)
  :is(emu-clause, emu-intro, emu-annex)
  :is(emu-clause, emu-intro, emu-annex) {
  margin-top: 2.5em;
}
.ecma-spec
  :is(emu-clause, emu-intro, emu-annex)
  :is(emu-clause, emu-intro, emu-annex)
  :is(emu-clause, emu-intro, emu-annex)
  :is(emu-clause, emu-intro, emu-annex) {
  margin-top: 2.22em;
}
.ecma-spec
  :is(emu-clause, emu-intro, emu-annex)
  :is(emu-clause, emu-intro, emu-annex)
  :is(emu-clause, emu-intro, emu-annex)
  :is(emu-clause, emu-intro, emu-annex)
  :is(emu-clause, emu-intro, emu-annex) {
  margin-top: 2em;
}
.ecma-spec
  :is(emu-clause, emu-intro, emu-annex)
  :is(emu-clause, emu-intro, emu-annex)
  :is(emu-clause, emu-intro, emu-annex)
  :is(emu-clause, emu-intro, emu-annex)
  :is(emu-clause, emu-intro, emu-annex)
  :is(emu-clause, emu-intro, emu-annex) {
  margin-top: 1.8em;
}
.ecma-spec > :is(emu-clause, emu-intro, emu-annex):first-of-type {
  margin-top: 0;
}

/* Literal headings inside the clause body (e.g. the spec source's
   <h2>Syntax</h2>, <h2>Supplemental Syntax</h2>) are in-body labels, not
   numbered section headings (those come from MDX as direct h1 children of
   <emu-clause>). Render them as small sub-labels. Scoped to headings inside
   the Sec-wrapper <div> so it doesn't swallow the section heading itself. */
main[data-pagefind-body]
  .ecma-spec
  :is(emu-clause, emu-intro, emu-annex)
  > div :is(h1, h2, h3, h4, h5, h6) {
  font-size: 1.1em;
  font-weight: 600;
  margin-top: 1.5em;
  margin-bottom: 0.4em;
}

/* Spec body links — tc39.es's anchor treatment for every prose / xref /
   external link: a muted blue link with no underline at rest, gaining an
   underline + brighter blue on hover. tc39 ecmarkup.css:
   `a{text-decoration:none;color:#206ca7}` /
   `a:hover{text-decoration:underline;color:#239dee}` (dark: #439de2 / #80cafb).
   `:visited` keeps the same colour (tc39 doesn't distinguish visited, and it
   avoids the UA purple). Scoped to .ecma-spec so the nav / sidebar / footer
   keep their own treatment; emu-nt grammar-symbol links override the colour
   below. */
.ecma-spec a,
.ecma-spec a:visited {
  color: #206ca7;
  text-decoration: none;
}
.ecma-spec a:hover {
  color: #239dee;
  text-decoration: underline;
  text-decoration-color: currentColor;
}
html.dark .ecma-spec a,
html.dark .ecma-spec a:visited {
  color: #439de2;
}
html.dark .ecma-spec a:hover {
  color: #80cafb;
}

/* Zero out the GitHub theme's chrome on <pre><code class="hljs">: the
   theme sets `padding: 1em` on the outer code (so the syntax colours
   sit inside a white-or-tinted block) but the surrounding <pre> already
   provides the framing in our layout. tc39.es uses the same trick
   (ecmarkup.css: `pre code.hljs { background: 0 0; margin: 0; padding: 0 }`)
   so the highlighter just contributes colours, not a separate surface. */
.ecma-spec pre code.hljs {
  background: transparent;
  padding: 0;
  margin: 0;
}
/* Dark mode token colours — the GitHub theme is a light-only design,
   so its keyword red / string blue / built-in orange wash out (and the
   default text colour goes near-invisible) against a dark page. Pull
   them onto a One Dark-ish palette while leaving the rest of the
   theme's token colours intact for tokens we haven't overridden. */
html.dark .ecma-spec pre code.hljs {
  color: inherit;
}
html.dark .ecma-spec .hljs-keyword,
html.dark .ecma-spec .hljs-built_in {
  color: #c678dd;
}
html.dark .ecma-spec .hljs-string {
  color: #98c379;
}
/* The rest of the GitHub-light token palette that sinks into the dark panel
   (found by the dark-mode contrast audit): regexp #032f62 was outright
   invisible (1.2:1); title #6f42c1 and number/literal/attr #005cc5 sat
   below 3:1. Same One Dark-ish hues as above. */
html.dark .ecma-spec .hljs-regexp {
  color: #56b6c2;
}
html.dark .ecma-spec .hljs-title {
  color: #61afef;
}
html.dark .ecma-spec .hljs-number,
html.dark .ecma-spec .hljs-literal,
html.dark .ecma-spec .hljs-attr {
  color: #d19a66;
}

/* Inline code matches tc39.es's `code { font-weight: 700;
   font-family:'IBM Plex Mono', monospace; white-space: pre }`
   (ecmarkup.css). No chip background / border / padding /
   shrunken size — tc39.es renders inline <code> as plain bold-mono
   tokens (single-character operators like <code>+</code> would look
   strange wrapped in a chip wider than the glyph). `white-space: pre`
   preserves the spaces tc39 emits inside identifiers like
   `<code>StringToNumber( )</code>` and prevents wrap mid-token. */
.ecma-spec :not(pre) > code {
  font-family: var(--font-mono);
  font-weight: 700;
  /* pre-wrap, not pre: a long inline code run (`(Math.exp(x) -
     Math.exp(-x)) / …`) has no scroll container, so with `pre` it pans the
     whole page at any width where the content column is narrower than the
     run (first seen on phones, then again at 768–1023px tablet widths).
     pre-wrap keeps the meaningful interior spaces and only breaks when the
     line genuinely doesn't fit. */
  white-space: pre-wrap;
}
/* tc39 itself walks back the `white-space: pre` inside regular tables
   so long identifiers don't blow the cell wide (ecmarkup.css
   `emu-table:not(.code) td code { white-space: normal }`). */
.ecma-spec emu-table:not(.code) td code {
  white-space: normal;
}

/* In-body tables — Nextra's docs treatment rather than tc39's heavier grid:
   a full 1px cell grid (the divider colour), padded cells (px-4 py-2), a
   bold-but-unshaded header row, and zebra striping on even body rows. (tc39
   instead shades the header and centres the table; we keep the lighter docs
   look here.) Bare <tr>s under <table> get an implicit <tbody>, so the stripe
   targets `tbody tr`. */
.ecma-spec emu-table table {
  border-collapse: collapse;
  /* Centre the table within the (full-width) emu-table block, like tc39's
     `emu-table table { margin: 0 auto }`. */
  margin-inline: auto;
}
.ecma-spec emu-table th,
.ecma-spec emu-table td {
  border: 1px solid var(--rule);
  padding: 0.5rem 1rem; /* Nextra py-2 px-4 */
  vertical-align: baseline;
}
.ecma-spec emu-table th {
  font-weight: 600;
}
/* Multi-column header/data cells centre their span (ecmarkup parity). */
.ecma-spec emu-table td[colspan]:not([colspan="1"]),
.ecma-spec emu-table th[colspan]:not([colspan="1"]) {
  text-align: center;
}
.ecma-spec emu-table tbody tr:nth-child(even) {
  background: rgba(0, 0, 0, 0.025);
}
html.dark .ecma-spec emu-table tbody tr:nth-child(even) {
  background: rgba(255, 255, 255, 0.035);
}
/* tc39.es captions are bold (ecmarkup.css `figure figcaption {
   font-weight: 700 }`). */
.ecma-spec figcaption {
  font-weight: 700;
}

/* Block-form grammar (a numbered/named production definition). Like tc39.es it
   has NO panel chrome — it sits directly on the page; the spacing and indent
   come from the emu-production margins below. It inherits the body (prop) font
   so non-terminal names, `::`, "one of", "but not" etc. read as prose, with
   only the bits tc39 renders monospace (terminals, `?`/`*`/`+` modifiers,
   `[Yield]`-style parameter lists) switched back to mono via emu-t/opt/params.
   The structural elements (emu-production, emu-rhs) supply the line-breaks via
   display:block. */
.ecma-spec emu-grammar[type="definition"] {
  display: block;
}
/* Inline grammar (mid-paragraph) — flat, no panel chrome. */
.ecma-spec emu-grammar:not([type="definition"]) {
  font-family: inherit;
}
/* Production layout: LHS, geq, and each RHS on their own line. Productions in
   a definition/example block are indented and vertically spaced like tc39
   (ecmarkup.css `emu-grammar[type=definition] emu-production { margin: 1em 0;
   margin-left: 5ex }`). */
.ecma-spec emu-production {
  display: block;
}
.ecma-spec emu-grammar[type="definition"] emu-production,
.ecma-spec emu-grammar[type="example"] emu-production {
  margin-top: 1em;
  margin-bottom: 1em;
  margin-left: 5ex;
}
/* Each RHS alternative on its own line with tc39's hanging indent (wrapped
   lines clear the production head) and the dashed-border-on-hover highlight
   that lets you isolate one alternative (ecmarkup.css `emu-rhs` +
   `emu-production:not([collapsed]) emu-rhs(:hover)`). */
.ecma-spec emu-production > emu-rhs {
  display: block;
  padding-left: 75px;
  text-indent: -25px;
}
.ecma-spec emu-production:not([collapsed]) emu-rhs {
  border: 0.2ex dashed transparent;
}
.ecma-spec emu-production:not([collapsed]) emu-rhs:hover {
  border-color: var(--production-rhs-border-color);
  background-color: var(--production-rhs-background-color);
}
/* Collapsed (single-line) productions keep the RHS inline right after the `::`
   rather than dropping to its own indented line — no hanging indent, no hover
   border. tc39 ecmarkup.css: `emu-production[collapsed] emu-rhs { display:inline;
   padding-left:.5ex; margin-left:0 }` and `emu-production[collapsed] { margin:0 }`.
   The `:not([collapsed])` hover rule above already exempts these.

   The production itself KEEPS the base 5ex indent + 1em vertical margins (it's
   not flush-left/tight). That mirrors tc39's cascade: tc39's
   `emu-production[collapsed] { margin:0 }` (specificity 0,1,1) is LESS specific
   than `emu-grammar[type=definition] emu-production` (0,1,2), so for collapsed
   productions inside a definition grammar the base margins win and the `margin:0`
   only takes effect outside definition grammars (e.g. inline grammars). We match
   that by leaving this selector unscoped — `.ecma-spec emu-production[collapsed]`
   is (0,2,1), below our base rule's (0,2,2), so base wins the same way. (An
   earlier `[type="definition"]`-scoped version was 0,3,2 and wrongly beat base,
   zeroing the indent — see git history.) */
.ecma-spec emu-production[collapsed] {
  margin: 0;
}
.ecma-spec emu-production[collapsed] > emu-rhs {
  display: inline;
  padding-left: 0.5ex;
  text-indent: 0;
  margin-left: 0;
}
/* tc39.es: `emu-oneof { font-weight: 700; margin-left: .5ex }` — bold "one of",
   no italic or opacity. */
.ecma-spec emu-oneof {
  font-weight: 700;
  margin-left: 0.5ex;
}

/* Notes get a "Note" label and the shared panel surface. The label and
   contents are real DOM (<span class="note"> + <div class="note-contents">)
   emitted by build-chapters.mjs, matching tc39's structure. */
.ecma-spec emu-note {
  display: block;
  background: var(--ecma-panel-bg);
  border: 1px solid var(--ecma-panel-border);
  border-radius: var(--ecma-panel-radius);
  padding: 0.75em 1em;
  margin-block: 1.5em;
}
.ecma-spec emu-note > span.note {
  display: block;
  font-weight: 600;
  font-size: 0.8em;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  margin-bottom: 0.4em;
  opacity: 0.7;
}
.ecma-spec emu-note > div.note-contents > p:first-of-type {
  margin-top: 0;
}
.ecma-spec emu-note > div.note-contents > p:last-of-type {
  margin-bottom: 0;
}

/* Normative Optional / Legacy / Deprecated annotations. tc39 tints the
   annotated region with a saturated orange wash (ecmarkup
   `[normative-optional] { border-left: 5px solid; background: #ffeedd }`);
   recast that on the site's panel grammar — a soft amber wash, 3px accent
   edge, the shared panel radius — with the uppercase tag label in the same
   voice as the NOTE label. Carriers: annotated clauses (the whole region,
   heading included), algorithm steps (<li>), lists, and inline <span>s. */
.ecma-spec {
  --ecma-kind-accent: #b45309;
  --ecma-kind-bg: rgba(217, 119, 6, 0.07);
}
html.dark .ecma-spec {
  --ecma-kind-accent: #fbbf24;
  --ecma-kind-bg: rgba(251, 191, 36, 0.08);
}
.ecma-spec :is([normative-optional], [legacy], [deprecated]) {
  background: var(--ecma-kind-bg);
  border-left: 3px solid var(--ecma-kind-accent);
  border-radius: 0 var(--ecma-panel-radius) var(--ecma-panel-radius) 0;
}
.ecma-spec emu-clause:is([normative-optional], [legacy], [deprecated]),
.ecma-spec li:is([normative-optional], [legacy], [deprecated]) {
  padding: 0.75em 1em;
}
.ecma-spec li:is([normative-optional], [legacy], [deprecated]) {
  margin-block: 0.5em;
}
.ecma-spec ul:is([normative-optional], [legacy], [deprecated]) {
  padding-block: 0.5em;
}
.ecma-spec span:is([normative-optional], [legacy], [deprecated]) {
  border-left: none;
  border-radius: 3px;
  padding: 0.05em 0.3em;
}
.ecma-spec .attributes-tag {
  font-weight: 600;
  font-size: 0.74em;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--ecma-kind-accent);
}

/* Inline ecmarkup markup, expanded by build-chapters.mjs from |Foo|, ~enum~,
   %intrinsic%, etc. Browsers have no default style for these custom elements,
   so add the typographic conventions ecmarkup uses. */
.ecma-spec emu-nt {
  font-style: italic;
}
/* Nonterminal links (<emu-nt> grammar-symbol references) use a muted nt-link
   colour at rest, then turn the standard link-blue on hover. tc39.es does the
   hover via a specificity quirk: `emu-nt a{color:var(--nt-link-foreground-color)}`
   (0,0,2) sets the rest colour, but the generic `a:hover{color:var(--link-hover-foreground-color)}`
   (0,1,1) wins on hover — so the muted symbol becomes a prominent blue link
   under the cursor. We reproduce both states explicitly, no underline at rest,
   underline on hover.

   The REST colour is NOT tc39's #333/#d0d0d0. We keep a different body text
   colour from tc39 (--fg #111827/#e5e7eb vs tc39 #111/#fcfcfc) AND we want the
   grammar symbols to lift off the prose a touch more than tc39 does — its
   body↔emu-nt contrast (~1.5:1) reads a bit too flat against our body. So the
   greys are tuned to ~1.8:1 against OUR body: #434343 → 1.79:1 on #111827,
   #aeaeae → 1.79:1 on #e5e7eb (tc39 is ~1.5:1 for reference). See
   docs/tc39-deviations.md §2. Hover blue (#239dee/#80cafb) is
   unchanged and matches tc39. */
.ecma-spec emu-nt a,
.ecma-spec emu-nt a:visited {
  color: #434343;
  text-decoration: none;
}
.ecma-spec emu-nt a:hover {
  color: #239dee;
  text-decoration: underline;
  text-decoration-color: currentColor;
}
html.dark .ecma-spec emu-nt a,
html.dark .ecma-spec emu-nt a:visited {
  color: #aeaeae;
}
html.dark .ecma-spec emu-nt a:hover {
  color: #80cafb;
}
.ecma-spec emu-const {
  font-variant: small-caps;
  /* tc39.es renders emu-const in IBM Plex Sans + uppercase + small-caps
     (ecmarkup.css `emu-const { font-family:'IBM Plex Sans',sans-serif;
     font-variant:small-caps; text-transform:uppercase }`). We keep our
     system-sans token (--font-sans) rather than loading IBM Plex Sans,
     but match tc39's uppercase + small-caps so well-known-constant names
     stand out from the surrounding serif body. */
  text-transform: uppercase;
  font-family: var(--font-sans);
}
/* Spec values (true/false/null/undefined/error names from `*foo*`) — bold,
   matches the typographic weight ecmarkup gives <emu-val>. tc39.es uses
   font-weight: 700. */
.ecma-spec emu-val {
  font-weight: 700;
  color: var(--ecma-text-strong);
}
/* Spec variables (`_x_` → <var>x</var>): tc39's teal var colour + a chip-ish
   hit area (padding/negative-margin so the box doesn't shift layout) and a
   pointer cursor, because clicking a var highlights every same-named var in
   the clause (see the var-highlight script in page.tsx). mix-blend-mode lets
   the click-highlight background tint the glyphs rather than sit behind them;
   tc39 turns it off in dark mode. Mirrors ecmarkup.css `var { … }`. */
.ecma-spec var {
  color: var(--var-foreground-color);
  cursor: pointer;
  border-radius: 5px;
  padding: 0 4px;
  margin: 0 -4px;
  transition: 0.25s ease-out;
  mix-blend-mode: multiply;
}
html.dark .ecma-spec var {
  mix-blend-mode: normal;
}
/* Click-highlight: ecmarkup adds `referenced` + `referenced0…6` to every var
   sharing the clicked var's name within the clause, cycling 7 background tints
   so distinct variables get distinct colours. `referenced` restores the text
   colour so it stays legible on the tint. */
.ecma-spec var.referenced {
  color: inherit;
}
.ecma-spec var.referenced0 {
  background-color: var(--referenced0-background-color);
}
.ecma-spec var.referenced1 {
  background-color: var(--referenced1-background-color);
}
.ecma-spec var.referenced2 {
  background-color: var(--referenced2-background-color);
}
.ecma-spec var.referenced3 {
  background-color: var(--referenced3-background-color);
}
.ecma-spec var.referenced4 {
  background-color: var(--referenced4-background-color);
}
.ecma-spec var.referenced5 {
  background-color: var(--referenced5-background-color);
}
.ecma-spec var.referenced6 {
  background-color: var(--referenced6-background-color);
}
/* Record / Property Descriptor field accesses (`[[Value]]`, `[[Foo]]`).
   tc39 nullifies <var>'s default italic+colour so the brackets read as
   part of the surrounding identifier text rather than as a variable. */
.ecma-spec var.field {
  font: inherit;
  color: inherit;
}
.ecma-spec emu-intrinsic {
  font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
  font-size: 0.92em;
}
/* Math equations like 𝔽(_x_), abs(_x_), floor(_x_) - _x_ - lean on a math/
   serif font so identifiers read like LaTeX-style math italics, not ordinary
   italic prose. Block emu-eqn (its own line) is indented; emu-eqn.inline
   sits in text flow with no extra margin (matches tc39's display split). */
.ecma-spec emu-eqn {
  display: block;
  margin-inline-start: 4em;
  font-style: italic;
  font-family:
    "Cambria Math", "Latin Modern Math", "STIX Two Math",
    "TeX Gyre Termes Math", Cambria, "Times New Roman", serif;
}
.ecma-spec emu-eqn.inline {
  display: inline;
  margin: 0;
}
/* Variables inside an equation are already italic via <var>; switch them
   off so the parent's math font is what carries the slant, matching the
   typographic feel of the source markup. */
.ecma-spec emu-eqn var {
  font-style: normal;
}

/* Grammar tokens, emitted by build-chapters.mjs's tokenizeGrammarLine as
   <emu-nt|emu-t|emu-geq|emu-mods|emu-params|emu-opt|emu-gprose|emu-oneof>
   inside <emu-grammar>. emu-nt is already italicised by the inline rule
   higher in the file; we no longer add bold to LHS nonterminals — tc39.es
   leaves them at normal weight and relies on the layout (LHS at start
   of line, RHS indented) for the hierarchy. */
.ecma-spec emu-grammar emu-t {
  font-family: var(--font-mono);
  /* tc39.es: `emu-t { font-family:'IBM Plex Mono',monospace; font-weight:700;
     display:inline-block; white-space:nowrap; text-indent:0 }`. inline-block +
     nowrap keep a multi-glyph terminal from wrapping or inheriting the RHS
     hanging text-indent. */
  font-weight: 700;
  display: inline-block;
  white-space: nowrap;
  text-indent: 0;
}
/* Inter-token spacing inside productions (tc39 `emu-gann, emu-production emu-t,
   emu-rhs emu-nt { margin-right:.5ex }`), zeroed on the last child of a grammar
   annotation so trailing tokens don't push a gap. Without this the RHS symbols
   render flush against each other. */
.ecma-spec emu-production emu-t,
.ecma-spec emu-rhs emu-nt,
.ecma-spec emu-grammar emu-gann {
  margin-right: 0.5ex;
}
.ecma-spec emu-gann emu-gprose:last-child,
.ecma-spec emu-gann emu-nt:last-child,
.ecma-spec emu-gann emu-t:last-child {
  margin-right: 0;
}
/* tc39.es: `emu-geq { margin-left: .5ex; font-weight: 700 }` — the `:`/`::`
   production separator is bold, not dimmed. */
.ecma-spec emu-grammar emu-geq {
  font-weight: 700;
  margin-left: 0.5ex;
}
/* tc39.es gives <emu-gann> no font treatment (it's a bare container for
   annotations like `[no LineTerminator here]`); only emu-gmod keeps the dimmed
   mono look, which tc39 leaves unstyled but reads fine here. */
.ecma-spec emu-grammar emu-gmod {
  font-family: var(--font-mono);
  font-size: 0.85em;
  opacity: 0.75;
}
/* Parameter lists (`[Yield, Await]`) — tc39 colours these teal
   (`emu-params { color: var(--params-foreground-color); font-family: mono;
   margin-right: 1ex }`) rather than dimming them. */
.ecma-spec emu-grammar emu-params {
  font-family: var(--font-mono);
  color: var(--params-foreground-color);
  margin-right: 1ex;
}
/* Constraints (`[+In]`, `[lookahead …]`) — tc39 `emu-constraints { font-size:
   .75em; color: var(--params-foreground-color); margin-right: .5ex }` with NO
   font-family override, so it stays in the serif body font. */
.ecma-spec emu-grammar emu-constraints {
  font-size: 0.75em;
  color: var(--params-foreground-color);
  margin-right: 0.5ex;
}
/* Optional markers (`?`/`*`/`+` as <emu-opt>) — tc39 colours these gold
   (`emu-opt { color: var(--opt-foreground-color); font-family: mono;
   margin-right: 1ex }`), rendered inline at full size. */
.ecma-spec emu-grammar emu-opt {
  font-family: var(--font-mono);
  color: var(--opt-foreground-color);
  margin-right: 1ex;
}
/* tc39.es: `emu-mods { font-size:.85em; vertical-align:sub;
   font-style:normal; font-weight:400 }` (the `?`/`*`/`+` style modifiers). */
.ecma-spec emu-grammar emu-mods {
  font-size: 0.85em;
  vertical-align: sub;
  font-style: normal;
  font-weight: 400;
}
/* tc39.es: `emu-gprose { font-size:.9em; font-family:'IBM Plex Sans',
   sans-serif }` — grammar prose ("but not", "no LineTerminator here") in plain
   sans, no italic or opacity. We keep our system-sans token (--font-sans)
   rather than loading IBM Plex Sans, but match the .9em size. */
.ecma-spec emu-grammar emu-gprose {
  font-size: 0.9em;
  font-family: var(--font-sans);
}
/* tc39.es: `emu-production emu-gprose { margin-right: 1ex }` — a gap after
   grammar prose ("but not", "but only if …") when it sits inside a production,
   so the following symbol doesn't run flush against the prose text. */
.ecma-spec emu-production emu-gprose {
  margin-right: 1ex;
}
.ecma-spec emu-grammar .cm {
  opacity: 0.5;
  font-style: italic;
}

/* Algorithm steps that begin with "Return" or "Throw" are marked
   `li.exit` by build-chapters.mjs (mirroring tc39.es/ecma262). Surface them
   visually only when the page opts in via `html.show-early-exits`, matching
   tc39's behaviour so the default reading mode stays unadorned. */
html.show-early-exits
  .ecma-spec
  emu-alg li.exit:not(.ecma-spec emu-alg > ol > li:last-child)::before {
  content: "↩";
  position: absolute;
  margin-inline-start: -1.4em;
  opacity: 0.5;
  font-size: 0.85em;
}

/* ECMA-262 algorithm step numbering cycles decimal → lower-alpha → lower-roman
   with depth, instead of the browser default of decimal at every level. */
.ecma-spec emu-alg ol {
  list-style-type: decimal;
  /* Tailwind's preflight zeroes list padding; restore it so nested algorithm
     steps indent per level (matches ecmarkup's 40px). */
  padding-inline-start: 40px;
}
/* Mobile: 40px per nesting level starves deep steps of measure — a six-deep
   algorithm leaves ~100px for text and the longest identifier widens the
   page. Tighten the per-level indent below the mobile breakpoint. */
@media (max-width: 767px) {
  .ecma-spec emu-alg ol {
    padding-inline-start: 1.4em;
  }
}
.ecma-spec emu-alg ol ol {
  list-style-type: lower-alpha;
}
.ecma-spec emu-alg ol ol ol {
  list-style-type: lower-roman;
}
.ecma-spec emu-alg ol ol ol ol {
  list-style-type: decimal;
}
.ecma-spec emu-alg ol ol ol ol ol {
  list-style-type: lower-alpha;
}
/* ecmarkup caps the cycle at six levels — deeper lists keep lower-roman
   (the 6-deep selector also matches them), matching the step-reference
   labels ("step 12.b.ii.2.a.ii.iii"). */
.ecma-spec emu-alg ol ol ol ol ol ol {
  list-style-type: lower-roman;
}

/* emu-figure: centre the image and render the caption= attribute below it.
   Ecmarkup stores caption text as an HTML attribute; without this rule the
   caption is invisible because no JS custom-element is registered. */
.ecma-spec emu-figure {
  display: block;
  text-align: center;
  margin-block: 2em;
  /* <object data="figure-1.svg" width="719"> embeds keep their attribute
     width (unlike img with max-width), so let the figure scroll on narrow
     viewports instead of widening the page. */
  overflow-x: auto;
}
.ecma-spec emu-figure img {
  max-width: 100%;
  height: auto;
  display: inline-block;
}
.ecma-spec emu-figure::after {
  content: attr(caption);
  display: block;
  margin-top: 0.6em;
  font-size: 0.875em;
  color: var(--ecma-text-body);
  opacity: 0.75;
  font-style: italic;
}
/* Prefix the number when build-chapters assigned one (data-num). A rare
   id-less float has no number, so it falls back to the caption alone above. */
.ecma-spec emu-figure[data-num]::after {
  content: "Figure " attr(data-num) ": " attr(caption);
}

/* emu-table: like emu-figure, the caption= attribute is invisible by default.
   Render it above the table as "Table N: <caption>" (N from data-num, set by
   build-chapters' float numbering). */
.ecma-spec emu-table {
  display: block;
}
.ecma-spec emu-table::before {
  content: attr(caption);
  display: block;
  margin-bottom: 0.5em;
  /* Match tc39's `figure figcaption`: bold, upright, full body size, in the
     dedicated caption grey (ecmarkup --caption-foreground-color #555 / dark
     #b0b0b0) — not the small italic sub-text we used before. */
  font-weight: 700;
  color: #555555;
  text-align: center;
}
html.dark .ecma-spec emu-table::before {
  color: #b0b0b0;
}
.ecma-spec emu-table[data-num]::before {
  content: "Table " attr(data-num) ": " attr(caption);
}

/* Prose lists (outside emu-alg): Tailwind's preflight strips markers + indent;
   restore disc/decimal + indentation. Algorithm lists keep their own cycling
   scheme via the more-specific .ecma-spec emu-alg ol rules above. */
.ecma-spec ul {
  list-style-type: disc;
  padding-inline-start: 40px;
}
.ecma-spec ol {
  list-style-type: decimal;
  padding-inline-start: 40px;
}

/* Inter-item spacing. Tailwind's preflight zeroes li margins, so list items
   were separated only by line-height, reading cramped next to the now-airier
   paragraphs. Give prose lists a small rhythm (~1/3 of the paragraph gap);
   keep algorithm steps dense per ecmarkup convention with just a hairline. */
.ecma-spec :is(ul, ol) > li + li {
  margin-top: 0.4em;
}
.ecma-spec emu-alg ol > li + li {
  margin-top: 0.15em;
}

/* ── Unify the gray palette onto an achromatic neutral ramp ───────────────
   Nextra mixes cool `gray`, blue `slate`, and achromatic `neutral`. An
   earlier slate unification read as too blue, so remap all three onto neutral
   grays whose luminance equals the slate ramp's — identical contrast, no hue.
   The blue accent (--nextra-primary-*) is intentionally left untouched.
   Unlayered :root overrides Tailwind's @layer theme token definitions. */
:root {
  --x-color-gray-50: #fafafa;
  --x-color-gray-100: #f4f4f4;
  --x-color-gray-200: #e7e7e7;
  --x-color-gray-300: #d4d4d4;
  --x-color-gray-400: #a0a0a0;
  --x-color-gray-500: #737373;
  --x-color-gray-600: #545454;
  --x-color-gray-700: #404040;
  --x-color-gray-800: #292929;
  --x-color-gray-900: #181818;
  --x-color-gray-950: #070707;

  --x-color-slate-50: #fafafa;
  --x-color-slate-100: #f4f4f4;
  --x-color-slate-200: #e7e7e7;
  --x-color-slate-300: #d4d4d4;
  --x-color-slate-400: #a0a0a0;
  --x-color-slate-500: #737373;
  --x-color-slate-600: #545454;
  --x-color-slate-700: #404040;
  --x-color-slate-800: #292929;
  --x-color-slate-900: #181818;
  --x-color-slate-950: #070707;

  --x-color-neutral-50: #fafafa;
  --x-color-neutral-100: #f4f4f4;
  --x-color-neutral-200: #e7e7e7;
  --x-color-neutral-300: #d4d4d4;
  --x-color-neutral-400: #a0a0a0;
  --x-color-neutral-500: #737373;
  --x-color-neutral-600: #545454;
  --x-color-neutral-700: #404040;
  --x-color-neutral-800: #292929;
  --x-color-neutral-900: #181818;
  --x-color-neutral-950: #070707;
}

/* Spec content text as equal-luminance neutral grays (matching what were the
   slate-700/300 body and slate-800/50 heading values): */
:root {
  --ecma-text-body: #404040;
  --ecma-text-strong: #292929;
}
.dark {
  --ecma-text-body: #d4d4d4;
  --ecma-text-strong: #fafafa;
}
main[data-pagefind-body] {
  color: var(--ecma-text-body);
}
main[data-pagefind-body] :is(h1, h2, h3, h4, h5, h6),
main[data-pagefind-body] :is(b, strong) {
  color: var(--ecma-text-strong);
}
/* ── Version switcher dropdown (navbar) — port of the former Nextra
   version-switcher. Uses the PoC's own theme
   tokens (--rule / --bg / --chrome-bg / --accent / --muted / --fg) so it
   tracks light/dark via html.dark like everything else in this file. */
.ecma-vs {
  position: relative;
  display: inline-flex;
  align-items: center;
}
.ecma-vs-trigger {
  cursor: pointer;
  background: transparent;
  border: 1px solid var(--rule);
  border-radius: 6px;
  color: var(--muted);
  padding: 0.2em;
  line-height: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  transition: color 0.15s, background 0.15s, border-color 0.15s;
}
.ecma-vs-trigger:hover {
  color: var(--fg);
  background: var(--chrome-bg);
  border-color: var(--accent);
}
.ecma-vs-menu {
  position: absolute;
  top: 100%;
  left: 0;
  margin: 0.5rem 0 0;
  padding: 0.3rem;
  z-index: 50;
  min-width: max-content;
  list-style: none;
  background: var(--bg);
  border: 1px solid var(--rule);
  border-radius: 8px;
  box-shadow: 0 8px 28px rgba(0, 0, 0, 0.14);
}
.ecma-vs-hidden {
  display: none;
}
/* On mobile the trigger sits mid-header, so an absolute max-content menu runs
   off the right edge. Pin it as a fixed sheet just under the header, spanning
   the viewport width, with its own scroll for the full edition list. */
@media (max-width: 767px) {
  .ecma-vs-menu {
    position: fixed;
    top: calc(var(--header-h) + 0.25rem);
    left: 0.75rem;
    right: 0.75rem;
    margin: 0;
    min-width: 0;
    max-height: calc(100dvh - var(--header-h) - 1rem);
    overflow-y: auto;
  }
}
.ecma-vs-item {
  display: block;
  padding: 0.35rem 0.6rem;
  border-radius: 6px;
  white-space: nowrap;
  text-decoration: none;
  font-size: 0.85rem;
  color: var(--fg);
}
.ecma-vs-item:hover {
  background: var(--chrome-bg);
  color: var(--accent);
}
.ecma-vs-current {
  font-weight: 600;
  color: var(--accent);
}

/* --- ES5.1 re-skin add-on (Approach A, see docs/es5.1-plan.md) ---------------
   The ES5.1 edition is the official already-rendered HTML, which uses its own
   class vocabulary (.proc, .prod, .note, .nt …) rather than the emu-* elements
   the rest of styles.css targets. Map those classes to the same look so ES5.1
   matches the modern editions without rewriting the source markup. */

/* Algorithms: ol.proc with the same decimal → lower-alpha → lower-roman cycle
   ecmarkup uses for emu-alg. */
.ecma-spec ol.proc {
  list-style-type: decimal;
  padding-inline-start: 40px;
}
.ecma-spec ol.proc ol.proc {
  list-style-type: lower-alpha;
}
.ecma-spec ol.proc ol.proc ol.proc {
  list-style-type: lower-roman;
}
.ecma-spec ol.proc ol.proc ol.proc ol.proc {
  list-style-type: decimal;
}
.ecma-spec ol.proc ol.proc ol.proc ol.proc ol.proc {
  list-style-type: lower-alpha;
}
.ecma-spec ol.proc > li {
  margin: 0.25em 0;
}

/* Grammar tokens: nonterminals italic, terminals monospace. Colours come from
   the same theme variables the rest of the spec uses, so they adapt in dark
   mode (no hardcoded greys). ES5.1 has two production shapes — inline
   <span class="prod"> in prose (left inline) and block <div class="gp"> display
   grammar (laid out below, like the modern emu-production). */
.ecma-spec .nt {
  font-style: italic;
}
.ecma-spec .t,
.ecma-spec .gp .t {
  font-family: var(--font-mono, ui-monospace, monospace);
  font-style: normal;
}

/* Block productions: indented like the modern emu-production, each RHS on its
   own hanging-indented line with the same hover highlight to isolate one
   alternative. */
.ecma-spec .gp {
  margin: 1em 0 1em 5ex;
}
.ecma-spec .gp .lhs {
  display: block;
}
.ecma-spec .gp .rhs {
  display: block;
  padding-left: 75px;
  text-indent: -25px;
  border: 0.2ex dashed transparent;
}
.ecma-spec .gp .rhs:hover {
  border-color: var(--production-rhs-border-color);
  background-color: var(--production-rhs-background-color);
}
.ecma-spec .geq {
  margin: 0 0.5ex;
}
/* "one of" / "but not" modifiers and [lookahead …] annotations read as muted
   prose, matching the modern emu-gann treatment. */
.ecma-spec .grhsmod,
.ecma-spec .grhsannot {
  font-style: italic;
  color: var(--muted);
}
/* The "See clause N" reference ecmarkup floats to the right of a summary
   production. */
.ecma-spec .gsumxref {
  float: right;
  font-size: 0.85em;
}
/* Optional-symbol subscript (the ingester tags <sub>opt</sub> as .g-opt) gets
   the same gold the modern emu-opt uses. */
.ecma-spec .g-opt {
  color: var(--opt-foreground-color);
}

/* Tables: the official HTML's per-cell inline borders are stripped by the
   ingester so .real-table (the captioned "Table N" data tables) can take the
   same grid + padding + striping as emu-table — otherwise they'd look unlike
   every other edition. .lightweight-table is a borderless layout grid (e.g. the
   reserved-word list), so it keeps no borders, just spacing. */
.ecma-spec table.real-table,
.ecma-spec table.lightweight-table {
  border-collapse: collapse;
  margin: 1em 0;
}
/* es5.1's URI character tables are wider than even a desktop viewport —
   scroll them in place like emu-table (display:block keeps the row layout). */
.ecma-spec table.real-table {
  display: block;
  overflow-x: auto;
}
/* es5.1 wraps its figures in plain <figure> (not emu-figure); the <object>
   embed keeps its attribute width, so give the figure the same scroll
   treatment as emu-figure. */
.ecma-spec figure {
  overflow-x: auto;
}
.ecma-spec table.real-table th,
.ecma-spec table.real-table td {
  border: 1px solid var(--rule);
  padding: 0.5rem 1rem;
  vertical-align: baseline;
  text-align: left;
}
.ecma-spec table.real-table th {
  font-weight: 600;
}
.ecma-spec table.real-table tbody tr:nth-child(even) {
  background: rgba(0, 0, 0, 0.025);
}
html.dark .ecma-spec table.real-table tbody tr:nth-child(even) {
  background: rgba(255, 255, 255, 0.035);
}
.ecma-spec table.lightweight-table td {
  padding: 0.15em 1em 0.15em 0;
}

/* Notes: .nh is the "NOTE" label, .note its body — match the theme-aware
   emu-note callout (panel variables are already defined light + dark). */
.ecma-spec p.note {
  margin-block: 1.5em;
  padding: 0.75em 1em;
  border: 1px solid var(--ecma-panel-border);
  border-radius: var(--ecma-panel-radius);
  background: var(--ecma-panel-bg);
}
.ecma-spec .nh {
  font-weight: 600;
  font-size: 0.8em;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  opacity: 0.7;
  margin-inline-end: 0.5em;
}

/* --- ES3 (bclary HTML) re-skin add-on (see docs/es3-plan.md) -----------------
   The 3rd-edition source is Bob Clary's HTML. It has no hardcoded colours (so
   dark mode needs no neutralisation) but its own sparse markup: grammar is a
   <dl class="grammar"> with <i> nonterminals and <b><tt> terminals, "See clause
   N" refs are .gsee, and content tables are bare <table> (no class). These
   classes are ES3-only, so they're styled globally; the bare tables are scoped
   to .ecma-es3 so they can't touch other editions' emu-table / .real-table. */

/* The bclary "not normative" note at the top of the index page — a faint,
   bordered callout so it reads as a sidebar caveat, not body text. */
.ecma-spec .es3-source-note {
  margin: 0 0 1.5em;
  padding: 0.6em 1em;
  border: 1px solid var(--rule);
  border-radius: var(--ecma-panel-radius);
  color: var(--muted);
  font-size: 0.9em;
}
.ecma-spec .es3-source-note a {
  color: inherit;
  text-decoration: underline;
}

/* Grammar productions: <dl class="grammar"> — LHS in <dt>, each RHS in a <dd>.
   The source already carries italic nonterminals / monospace terminals via
   <i>/<b><tt>, so this only supplies the production layout. div.grammar is
   the LHS-less variant (es1/es2 §11.2.1 displays) — a dt-less dl is invalid
   markup, so those emit divs with the same layout. */
.ecma-spec :is(dl, div).grammar {
  margin: 1em 0 1em 5ex;
}
.ecma-spec dl.grammar > dt {
  font-style: italic;
}
.ecma-spec dl.grammar > dd,
.ecma-spec div.grammar > div {
  margin: 0.15em 0 0.15em 5ex;
}
.ecma-spec .nonterminal {
  font-style: italic;
}
/* The "::" / ":" production separator sits flush against the LHS in the
   source; give it breathing room. */
.ecma-spec dl.grammar > dt > b {
  margin: 0 0.4em;
}
/* "See clause N" grammar-summary reference — float it to the right edge of the
   production line (like the modern grammar xref) so the LHS stays clean. */
.ecma-spec .gsee {
  float: right;
  font-style: normal;
  font-size: 0.85em;
  color: var(--muted);
}

/* ES3 content tables are bare <table> (no class); give them the same bordered,
   theme-aware look as emu-table. Scoped to .ecma-es3. */
.ecma-es3 table {
  border-collapse: collapse;
  margin: 1em 0;
}
.ecma-es3 table th,
.ecma-es3 table td {
  border: 1px solid var(--rule);
  padding: 0.5rem 1rem;
  vertical-align: baseline;
  text-align: left;
}
.ecma-es3 table th {
  font-weight: 600;
}
.ecma-es3 table tbody tr:nth-child(even) {
  background: rgba(0, 0, 0, 0.025);
}
html.dark .ecma-es3 table tbody tr:nth-child(even) {
  background: rgba(255, 255, 255, 0.035);
}
/* The lone figure (figure-1.gif, §4.3) is a bare <img> in a <div>; centre it. */
.ecma-es3 img {
  display: block;
  max-width: 100%;
  height: auto;
  margin: 1em auto;
}

/* === ES1 / ES2 (Marker re-skin) ==========================================
   ES1 and ES2 have no HTML source — reconstructed from the PDFs with Marker,
   with grammar productions borrowed from ES3 (so they reuse the global
   dl.grammar / .gsee styling above). Own additions: the provenance note, bare
   content tables (scoped like ES3), algorithm <ol>s, kept Marker "one of"
   labels, and the flag for grammar productions that couldn't be sourced from
   ES3. The es2-* class names are shared by both editions; container scoping
   uses :is(.ecma-es1, .ecma-es2). */
.ecma-spec .es2-source-note {
  margin: 0 0 1.5em;
  padding: 0.6em 1em;
  border: 1px solid var(--rule);
  border-radius: var(--ecma-panel-radius);
  color: var(--muted);
  font-size: 0.9em;
}
.ecma-spec .es2-source-note a {
  color: inherit;
  text-decoration: underline;
}
/* Bare <table> content tables — same bordered, theme-aware look as ES3.
   Centred within the measure: the printed ECMA-262 1st/2nd editions centre
   their tables (ES1 PDF §7.1: body x≈99–500, table x≈190–420), as do the
   modern editions (tc39 `emu-table table { margin: 0 auto }`).
   .lightweight-table (the keyword-family one-of grids) is grammar, not
   data — excluded here and laid out at the grammar indent below. */
:is(.ecma-es1, .ecma-es2) table:not(.lightweight-table) {
  border-collapse: collapse;
  margin: 1em auto;
}
:is(.ecma-es1, .ecma-es2) table:not(.lightweight-table) th,
:is(.ecma-es1, .ecma-es2) table:not(.lightweight-table) td {
  border: 1px solid var(--rule);
  padding: 0.5rem 1rem;
  vertical-align: baseline;
  text-align: left;
}
:is(.ecma-es1, .ecma-es2) table:not(.lightweight-table) th {
  font-weight: 600;
}
:is(.ecma-es1, .ecma-es2)
  table:not(.lightweight-table)
  tbody tr:nth-child(even) {
  background: rgba(0, 0, 0, 0.025);
}
html.dark
  :is(.ecma-es1, .ecma-es2)
  table:not(.lightweight-table)
  tbody tr:nth-child(even) {
  background: rgba(255, 255, 255, 0.035);
}
/* Keyword-family "one of" terminal grids (§7.4.2/7.4.3/7.6): the official
   es5.1 HTML presents the same productions as a borderless lightweight-table;
   align it to the grammar dd position (dl 5ex + dd 5ex) under its one-of
   label. */
:is(.ecma-es1, .ecma-es2) table.lightweight-table {
  margin: 0.25em 0 1em 10ex;
  /* the generic scroll rule's max-width:100% doesn't account for this
     indent — without the subtraction the grid's right edge pans the page
     at tablet widths */
  max-width: calc(100% - 10ex);
}
/* In-body tables (the ES1/ES2 keyword grids, the §7.7.4 escape-sequence
   table, es5.1's .real-table code-unit tables, es3's bare tables) scroll in
   place at EVERY width — this was mobile-only, but the 768–1023px band has
   an even narrower content column than a phone and overflowed the page.
   display:block keeps the row layout via the anonymous table box;
   fit-content + max-width keeps `margin: auto` centering working and is a
   no-op for tables narrower than the column. emu-table's inner table is
   excluded — emu-table itself is the scroll container there. */
.ecma-spec table:not(emu-table table) {
  display: block;
  overflow-x: auto;
  width: fit-content;
  max-width: 100%;
}
@media (max-width: 767px) {
  :is(.ecma-es1, .ecma-es2) table.lightweight-table {
    margin-left: 0;
  }
}
:is(.ecma-es1, .ecma-es2) img {
  display: block;
  max-width: 100%;
  height: auto;
  margin: 1em auto;
}
/* Algorithm steps: Marker emits them as numbered list items; rendered as <ol>. */
:is(.ecma-es1, .ecma-es2) ol.ecma-alg {
  margin: 1em 0 1em 3ex;
  padding-left: 2ex;
}
:is(.ecma-es1, .ecma-es2) ol.ecma-alg > li {
  margin: 0.15em 0;
}
/* Math formulas (demathify's .es2-math spans, and the §15.9.1.x Date
   formulas Marker emitted as <pre>/<p>) — the PDFs typeset these as math
   (roman + italic variables), never monospace; give all forms the same
   math-serif voice as the modern emu-eqn. The pre keeps its line breaks
   (DaysInYear's aligned cases) and the overflow-x from the generic rule. */
:is(.ecma-es1, .ecma-es2) .es2-math,
:is(.ecma-es1, .ecma-es2) pre.es2-math {
  font-style: italic;
  font-family:
    "Cambria Math", "Latin Modern Math", "STIX Two Math",
    "TeX Gyre Termes Math", Cambria, "Times New Roman", serif;
}
:is(.ecma-es1, .ecma-es2) :is(p, pre).es2-math {
  margin-inline-start: 4em;
}
/* Bulleted value lists (the Math special cases, the §7.7.4 SV/CV lists) —
   printed as • items in the PDFs, not numbered steps. Same spacing as the
   algorithm lists; list-style restored against the preflight reset. */
:is(.ecma-es1, .ecma-es2) ul.ecma-list {
  margin: 1em 0 1em 3ex;
  padding-left: 2ex;
  list-style: disc;
}
:is(.ecma-es1, .ecma-es2) ul.ecma-list > li {
  margin: 0.15em 0;
}
/* NOTE / Discussion callouts in hand-authored sections (e.g. §9.5–9.7). */
:is(.ecma-es1, .ecma-es2) .es2-note {
  margin: 1em 0;
  padding: 0.5em 1em;
  border-left: 3px solid var(--rule);
  color: var(--muted);
  font-size: 0.95em;
}
/* The "Nonterminal :: one of" label kept from Marker, above its terminal table. */
:is(.ecma-es1, .ecma-es2) .grammar-oneof {
  margin: 1em 0 0.25em 5ex;
  font-style: italic;
}
/* Bracketed grammar annotations, e.g. [no LineTerminator here], [empty]. */
:is(.ecma-es1, .ecma-es2) .grammar-note {
  font-style: italic;
  color: var(--muted);
}
/* Grammar productions that genuinely diverged from ES3 (ch.7 lexical) and were
   kept from Marker's flattened output — flag faintly so they're easy to find
   and hand-correct. */
.ecma-spec dl.grammar.es2-grammar-unmapped > dt {
  border-left: 2px solid var(--rule);
  padding-left: 0.6ex;
}

/* xref hover-card (xref-hover.js) — a definition popover shown on hovering an
   emu-xref / dfn link. It is appended to <body>, OUTSIDE .ecma-spec, so the
   panel/kind tokens scoped there don't reach it; the surface tokens (--fg,
   --muted, --chrome-bg) live at :root/html.dark and do. Border/radius and the
   term-badge accent are inlined here with an explicit dark override. */
.xref-card {
  position: absolute;
  /* Above the prose but below the breadcrumb (z-35) and header (z-40) so the
     card tucks under them when a hovered link sits near the top of the
     viewport, rather than covering the chrome. */
  z-index: 25;
  max-width: 30rem;
  padding: 0.7rem 0.85rem 0.8rem;
  background: var(--chrome-bg, #fff);
  border: 1px solid rgba(115, 115, 115, 0.25);
  border-radius: 6px;
  box-shadow: 0 8px 28px rgba(0, 0, 0, 0.16), 0 1px 3px rgba(0, 0, 0, 0.1);
  font-size: 0.82rem;
  line-height: 1.5;
  color: var(--fg);
  opacity: 0;
  transform: translateY(3px);
  transition: opacity 0.12s ease, transform 0.12s ease;
  pointer-events: none;
}
html.dark .xref-card {
  box-shadow: 0 8px 28px rgba(0, 0, 0, 0.5), 0 1px 3px rgba(0, 0, 0, 0.4);
}
.xref-card.show {
  opacity: 1;
  transform: translateY(0);
  pointer-events: auto;
}
/* Bridge the gap between the link and the card: a transparent hit-area that
   extends the card's interactive box ~10px above and below (the card sits ~8px
   off the link). Without it, moving the pointer toward the card crosses dead
   space that triggers the hide timer ("the card closes on the slightest
   move"). Vertical only — no horizontal halo to swallow clicks. Behind the
   content (z-index:-1) so it never covers the card's own text. */
.xref-card.show::before {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  top: -10px;
  bottom: -10px;
  z-index: -1;
}
.xref-card .xc-head {
  display: flex;
  align-items: baseline;
  gap: 0.5em;
  margin-bottom: 0.3em;
}
.xref-card .xc-num {
  font-family: var(--font-mono);
  font-size: 0.86em;
  color: var(--muted);
}
.xref-card .xc-title {
  font-weight: 600;
}
.xref-card .xc-kind {
  margin-left: auto;
  align-self: center;
  font-family: var(--font-mono);
  font-size: 0.62em;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: #b45309;
  border: 1px solid rgba(180, 83, 9, 0.4);
  background: rgba(180, 83, 9, 0.07);
  border-radius: 5px;
  padding: 0.1em 0.5em;
}
html.dark .xref-card .xc-kind {
  color: #fbbf24;
  border-color: rgba(251, 191, 36, 0.4);
  background: rgba(251, 191, 36, 0.08);
}
.xref-card .xc-summary {
  color: var(--muted);
}
/* Grammar productions / equations: render the title and the rendered-text
   summary in the mono face (and let the fg colour through) so a production
   like "StrWhiteSpace ::: StrWhiteSpaceChar StrWhiteSpace opt" reads as code. */
.xref-card .xc-mono {
  font-family: var(--font-mono);
}
.xref-card .xc-summary.xc-mono {
  color: var(--fg);
  font-size: 0.92em;
}

/* Reading progress (reading-progress.js). Mobile/narrow: a thin bar pinned
   under the sticky header, filled by the chapter scroll fraction. The desktop
   indicator lives in the right-rail TOC, which is hidden ≤1100px, so the bar
   is the fallback exactly where the TOC isn't — hide it once the TOC shows. */
#reading-bar {
  position: fixed;
  left: 0;
  top: var(--header-h, 64px);
  height: 3px;
  width: 100%;
  transform-origin: 0 50%;
  transform: scaleX(var(--rp, 0));
  background: linear-gradient(90deg, var(--accent), #fbbf24);
  /* Below the header (z-40) so the header's version-switcher dropdown opens
     over the bar, not under it. Still above the prose. */
  z-index: 25;
  pointer-events: none;
  transition: transform 0.08s linear;
}
@media (min-width: 1101px) {
  #reading-bar {
    display: none;
  }
}
/* Desktop: "section N / M" counter in the TOC heading + a continuous fill on
   the aside's left edge. The fill sits on the non-scrolling <aside> (sticky,
   so it's the containing block) rather than inside the internally-scrolling
   <ol>, so it stays put and visible as the TOC list scrolls. */
aside.toc h2 .rp-count {
  font-family: var(--font-mono);
  font-weight: 400;
  font-size: 0.8em;
  color: var(--muted);
  margin-left: 0.6em;
}
#reading-rail {
  position: absolute;
  left: 0;
  top: 0;
  bottom: 0;
  width: 2px;
  background: var(--rule);
}
#reading-rail-fill {
  position: absolute;
  left: 0;
  top: 0;
  width: 2px;
  height: calc(var(--rp, 0) * 100%);
  background: var(--accent);
  transition: height 0.08s linear;
}

/* Context breadcrumb (breadcrumb.js): a sticky bar under the header showing
   the ancestor clause chain of the section being read. JS sets left/width/top
   to align it to the content column; shown (display:flex) only when ≥3 levels
   deep. Content scrolls under it (fixed), so the background is semi-opaque +
   blurred. */
#crumb-bar {
  position: fixed;
  z-index: 35;
  display: none;
  align-items: center;
  gap: 0.1rem;
  padding: 0.4rem 1rem;
  font-size: 0.78rem;
  white-space: nowrap;
  overflow: hidden;
  background: color-mix(in srgb, var(--chrome-bg) 86%, transparent);
  -webkit-backdrop-filter: blur(8px);
  backdrop-filter: blur(8px);
  /* Sits within the content column: left + bottom borders, with only the
     bottom-left corner rounded, so it reads as a panel hanging off the header
     inside the reading column. */
  border-left: 1px solid var(--rule);
  border-bottom: 1px solid var(--rule);
  border-bottom-left-radius: 8px;
}
#crumb-bar.show {
  display: flex;
}
#crumb-bar .cb {
  color: var(--muted);
  text-decoration: none;
  /* Keep each crumb's full text — breadcrumb.js collapses the oldest
     ancestors into a leading "…" when the row overflows, rather than letting
     flexbox shrink every level into a meaningless "22 Te…". */
  flex: 0 0 auto;
  max-width: 20rem;
  overflow: hidden;
  text-overflow: ellipsis;
}
#crumb-bar .cb:hover {
  color: var(--fg);
}
#crumb-bar .cb:last-child {
  color: var(--fg);
  font-weight: 600;
  /* The current section may be a long title on its own — let just this one
     shrink and ellipsize so it never clips the right edge. */
  flex: 0 1 auto;
}
#crumb-bar .cb-ellip {
  flex: 0 0 auto;
  color: var(--muted);
  opacity: 0.6;
  padding: 0 0.1rem;
}
@media (max-width: 767px) {
  #crumb-bar {
    display: none !important;
  }
}
#crumb-bar .sep {
  flex: none;
  margin: 0 0.25rem;
  color: var(--muted);
  opacity: 0.5;
}
#crumb-bar .cb-num {
  font-family: var(--font-mono);
  opacity: 0.7;
  margin-right: 0.35em;
}

/* Heading anchor links (N1): a "#" permalink injected into each clause heading
   (_config.ts), revealed on hover/focus. heading-anchors.js also copies the
   URL on click and flashes a "Copied" confirmation via .copied. */
.heading-anchor {
  position: relative;
  margin-left: 0.4em;
  color: var(--muted);
  font-weight: 400;
  text-decoration: none;
  opacity: 0;
  transition: opacity 0.12s, color 0.12s;
}
/* The glyph is a pseudo-element so the <a> carries no text — see _config.ts. */
.heading-anchor::before {
  content: "#";
}

/* "Back to top" pill (N3, back-to-top.js): a subtle floating control at the
   content column's bottom-right (the `right` offset is set by JS to clear the
   rail), fading in once scrolled down a screenful. */
#to-top {
  position: fixed;
  bottom: 1.25rem;
  z-index: 30;
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  height: 40px;
  padding: 0 0.95rem 0 0.75rem;
  border: 1px solid var(--rule);
  border-radius: 999px;
  background: color-mix(in srgb, var(--chrome-bg) 92%, transparent);
  -webkit-backdrop-filter: blur(8px);
  backdrop-filter: blur(8px);
  color: var(--muted);
  font-size: 0.82rem;
  font-weight: 500;
  cursor: pointer;
  box-shadow: 0 4px 14px rgba(0, 0, 0, 0.12);
  opacity: 0;
  visibility: hidden;
  transform: translateY(6px);
  transition: opacity 0.15s, transform 0.15s, color 0.15s, border-color 0.15s;
}
#to-top svg {
  flex: none;
}
#to-top.show {
  opacity: 1;
  visibility: visible;
  transform: none;
}
#to-top:hover {
  color: var(--accent);
  border-color: var(--accent);
}
@media (prefers-reduced-motion: reduce) {
  #to-top {
    transition: opacity 0.15s, color 0.15s, border-color 0.15s;
    transform: none;
  }
}

/* TC39 proposal pages (scripts/build-proposals.mjs). */
.prop-stage {
  /* No left margin — the title-group flex gap already spaces it from the
     title; an extra margin made the title↔badge gap too wide. */
  padding: 0.1rem 0.5rem;
  border-radius: 999px;
  background: color-mix(in srgb, var(--accent) 16%, transparent);
  color: var(--accent);
  font-size: 0.7rem;
  font-weight: 600;
  vertical-align: 0.12em;
}
/* In the proposal selector dropdown, each item shows its stage badge pushed to
   the right edge. */
#proposal-switcher-menu .ecma-vs-item {
  display: flex;
  align-items: center;
  gap: 0.75rem;
}
#proposal-switcher-menu .prop-stage {
  margin-left: auto;
}
.ecma-spec .prop-title {
  font-size: 1.9rem;
  line-height: 1.15;
  margin: 0.2rem 0 1.1rem;
}
.prop-index {
  list-style: none;
  margin: 0 0 1.6rem;
  padding: 0;
}
.prop-index li {
  margin: 0.5rem 0;
}
.prop-index > li > a:first-child {
  font-weight: 600;
}
.prop-index-src {
  margin-left: 0.5rem;
  font-size: 0.8rem;
  color: var(--muted);
  text-decoration: none;
}
.prop-index-src:hover {
  color: var(--accent);
}
/* Proposal pages: the left sidebar lists the proposal's own sections, indented
   by depth (the editions' chapter list is flat, so this only affects the
   data-level items the proposal sidebar emits). */
aside.sidebar .sidebar-list li[data-level="2"] > a {
  padding-inline-start: 1.4rem;
}
aside.sidebar .sidebar-list li[data-level="3"] > a {
  padding-inline-start: 2.2rem;
}
aside.sidebar .sidebar-list li[data-level="4"] > a {
  padding-inline-start: 3rem;
}
h1:hover > .heading-anchor,
h2:hover > .heading-anchor,
h3:hover > .heading-anchor,
h4:hover > .heading-anchor,
h5:hover > .heading-anchor,
h6:hover > .heading-anchor,
.heading-anchor:focus {
  opacity: 0.5;
}
.heading-anchor:hover {
  opacity: 1;
  color: var(--accent);
}
/* "Copied" confirmation bubble */
.heading-anchor.copied {
  opacity: 1;
  color: var(--accent);
}
.heading-anchor.copied::after {
  content: "Copied";
  position: absolute;
  left: 50%;
  bottom: calc(100% + 4px);
  transform: translateX(-50%);
  padding: 0.15em 0.45em;
  border-radius: 5px;
  background: var(--chrome-bg);
  border: 1px solid var(--rule);
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.12);
  color: var(--fg);
  font-size: 0.7rem;
  font-weight: 400;
  letter-spacing: normal;
  white-space: nowrap;
  pointer-events: none;
}

/* Glossary page (scripts/glossary.mjs writes _site/glossary/index.html, which
   reuses the spec page shell and tags <main> with .glossary). A–Z jump bar +
   one definition list per initial. */
.glossary .gl-h1 {
  margin: 0.2rem 0 0.3rem;
}
.glossary .gl-lede {
  /* Note panel, in the spirit of the ES1/ES2 Introduction's source note:
     bordered + lightly tinted, muted and slightly smaller, to read as an
     editorial aside rather than spec prose. */
  margin: 0 0 1.4rem;
  padding: 0.7em 1em;
  border: 1px solid var(--rule);
  border-radius: var(--ecma-panel-radius, 8px);
  background: color-mix(in srgb, var(--muted) 7%, transparent);
  color: var(--muted);
  font-size: 0.9em;
}
.glossary .gl-az {
  position: sticky;
  top: var(--header-h, 64px);
  display: flex;
  flex-wrap: wrap;
  gap: 0.15rem;
  padding: 0.4rem 0.5rem;
  margin-bottom: 1.2rem;
  background: color-mix(in srgb, var(--chrome-bg) 92%, transparent);
  -webkit-backdrop-filter: blur(8px);
  backdrop-filter: blur(8px);
  /* A self-contained toolbar box (border on all four sides + radius) reads as
     deliberate chrome rather than a strip floating in the column. */
  border: 1px solid var(--rule);
  border-radius: 10px;
  z-index: 2;
  transition: border-radius 0.12s, border-top-color 0.12s;
}
/* Pinned to the header (toggled by glossary.js): merge with it by dropping the
   top border + top corners so the box hangs off the header instead of floating
   under it. */
.glossary .gl-az.stuck {
  border-top-color: transparent;
  border-top-left-radius: 0;
  border-top-right-radius: 0;
}
.glossary .gl-az a {
  display: inline-flex;
  justify-content: center;
  min-width: 1.6em;
  padding: 0.15em 0.3em;
  border-radius: 6px;
  font-family: var(--font-mono);
  font-size: 0.85rem;
  color: var(--accent);
  text-decoration: none;
}
.glossary .gl-az a:hover {
  background: color-mix(in srgb, var(--accent) 15%, transparent);
}
.glossary .gl-letter {
  font-family: var(--font-mono);
  font-size: 1.1rem;
  color: var(--muted);
  border-bottom: 1px solid var(--rule);
  padding-bottom: 0.2rem;
  margin: 2rem 0 0.6rem;
}
.glossary .gl-list {
  margin: 0;
}
.glossary .gl-list dt {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 0.6rem;
  margin-top: 1rem;
}
.glossary .gl-term {
  font-weight: 600;
  font-size: 1.05rem;
  color: var(--fg);
  text-decoration: none;
  /* Long intrinsic names (%AsyncGeneratorFunction.prototype.prototype%) are
     unbreakable tokens; as a flex item they keep min-width:auto and push the
     row past a phone's width. Let them shrink and wrap. */
  min-width: 0;
  overflow-wrap: anywhere;
}
.glossary .gl-term:hover {
  color: var(--accent);
}
.glossary .gl-var {
  color: var(--muted);
  font-size: 0.82rem;
  font-style: italic;
}
.glossary .gl-list dd {
  margin: 0.15rem 0 0;
  color: var(--muted);
  line-height: 1.5;
}
.glossary .gl-loc {
  display: inline-block;
  margin-top: 0.15rem;
  font-size: 0.82rem;
  color: var(--accent);
  text-decoration: none;
  /* The §reference is a multi-word section title; let it wrap on narrow
     screens rather than forcing a horizontal scrollbar. */
  overflow-wrap: anywhere;
}
.glossary .gl-loc:hover {
  text-decoration: underline;
}
