:root {
  /* Aliases onto the canonical tokens in runi-tokens.css. The literal
     fallbacks are deliberate: the chrome must still render if the tokens
     stylesheet is missing on some surface. */
  --rs-shell-paper: var(--rs-paper, #f6f3ea);
  --rs-shell-ink: var(--rs-ink, #151311);
  --rs-shell-navy: var(--rs-navy, #0b1320);
  --rs-shell-red: var(--rs-red, #7a1c17);
  --rs-shell-line: var(--rs-line, rgba(21, 19, 17, 0.22));
  --rs-shell-sans: var(--rs-sans, 'Inter', system-ui, -apple-system, BlinkMacSystemFont, sans-serif);
  /* Height of the sticky global header. .rs-rail sticks directly below it. */
  /* 82px of inner height plus the header's own 1px bottom border. At 82
     the sticky rail parked 1px under its own top edge. */
  --rs-shell-header-h: 83px;

  /* The measure the chrome sits on. The header, rail and footer were pinned
     to a literal 1360px that matched no page on the site: --rs-max is 1280,
     the homepage shell 1280, platform 1320, about 1240, fabric 1120, and the
     article/document/legal/blog families 980. At 1440px the wordmark started
     at x=40 while an essay's title started at x=254. Each page family sets
     these, so the chrome follows the page instead of the page following the
     chrome. */
  --rs-shell-measure: var(--rs-max, 1280px);
  --rs-shell-gutter: 64px;
}

.rs-global-header,
.rs-global-footer,
.rs-global-header *,
.rs-global-footer * { box-sizing: border-box; }

.rs-global-header {
  position: sticky;
  top: 0;
  z-index: 1000;
  border-bottom: 1px solid var(--rs-shell-line);
  background: var(--rs-shell-paper);
  color: var(--rs-shell-ink);
  font-family: var(--rs-shell-sans);
}

.rs-global-header__inner {
  width: min(calc(100% - var(--rs-shell-gutter)), var(--rs-shell-measure));
  min-height: 82px;
  margin-inline: auto;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
}

.rs-global-header__home {
  display: inline-flex;
  align-items: center;
  flex: 0 0 auto;
  gap: 10px;
  color: var(--rs-shell-ink);
  font: 700 0.78rem/1 var(--rs-shell-sans);
  letter-spacing: 0.06em;
  text-decoration: none;
}

.rs-global-header__logo {
  display: block;
  width: 24px;
  height: 24px;
  filter: brightness(0);
}

.rs-global-header__nav {
  display: flex;
  align-items: center;
  /* Full bar height, so a submenu's top:100% lands on the bar's bottom edge
     rather than 27px above it, inside the chrome. */
  align-self: stretch;
  gap: 24px;
}

.rs-global-header__link {
  color: rgba(11, 19, 32, 0.74);
  font: 500 0.79rem/1 var(--rs-shell-sans);
  text-decoration: none;
  white-space: nowrap;
}

.rs-global-header__link:hover,
.rs-global-header__link:is([aria-current='page'], [aria-current='true']) {
  color: var(--rs-shell-ink);
}

/* "You are here" needs to be visible, not a shift from 0.74 to 1.0 alpha on
   12.6px text. */
.rs-global-header__link:is([aria-current='page'], [aria-current='true']) {
  box-shadow: inset 0 -1.5px 0 var(--rs-shell-ink);
}

.rs-global-header__login {
  padding: 8px 18px;
  border: 1px solid rgba(122, 28, 23, 0.96);
  border-radius: 999px;
  background: rgba(122, 28, 23, 0.92);
  color: #fff;
  font: 650 0.72rem/1 var(--rs-shell-sans);
  letter-spacing: 0.04em;
  text-decoration: none;
}

.rs-global-header__toggle {
  display: none;
  width: 44px;
  height: 44px;
  align-items: center;
  justify-content: center;
  border: 1px solid var(--rs-shell-navy);
  border-radius: 50%;
  background: transparent;
  color: var(--rs-shell-navy);
  cursor: pointer;
}

.rs-global-header__toggle-bar,
.rs-global-header__toggle-bar::before,
.rs-global-header__toggle-bar::after {
  display: block;
  width: 18px;
  height: 1.5px;
  background: currentColor;
}

.rs-global-header__toggle-bar { position: relative; }
.rs-global-header__toggle-bar::before,
.rs-global-header__toggle-bar::after { content: ''; position: absolute; left: 0; }
.rs-global-header__toggle-bar::before { top: -6px; }
.rs-global-header__toggle-bar::after { top: 6px; }

.rs-global-footer {
  padding: 54px 0;
  background: var(--rs-shell-navy);
  color: var(--rs-shell-paper);
  font-family: var(--rs-shell-sans);
}

.rs-global-footer__inner {
  width: min(calc(100% - var(--rs-shell-gutter)), var(--rs-shell-measure));
  margin-inline: auto;
  display: flex;
  justify-content: space-between;
  gap: 40px;
  align-items: flex-start;
}

.rs-global-footer__brand {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  margin-bottom: 10px;
  color: inherit;
  font: 700 0.9rem/1 var(--rs-shell-sans);
  text-decoration: none;
}

.rs-global-footer__brand img { width: 22px; height: 22px; }

.rs-global-footer__legal {
  color: rgba(246, 243, 234, 0.66);
  font: 400 0.76rem/1.65 var(--rs-shell-sans);
}

.rs-global-footer__legal a,
.rs-global-footer__links a { color: inherit; text-decoration: none; }

.rs-global-footer__nav {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 12px;
}

.rs-global-footer__links {
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-end;
  gap: 18px;
  color: rgba(246, 243, 234, 0.66);
  font: 500 0.74rem/1 var(--rs-shell-sans);
}

.rs-global-header :focus-visible,
.rs-global-footer :focus-visible {
  outline: 2px solid var(--rs-shell-paper);
  outline-offset: 2px;
  box-shadow: 0 0 0 4px var(--rs-shell-red);
}

@media (max-width: 1040px) {
  .rs-global-header__nav { gap: 14px; }
}

@media (max-width: 860px) {
  :root { --rs-shell-header-h: 75px; --rs-shell-gutter: 40px; }
  .rs-global-header__inner,
  .rs-global-footer__inner { width: min(calc(100% - var(--rs-shell-gutter)), var(--rs-shell-measure)); }
  .rs-global-header__inner { min-height: 74px; }
  .rs-global-header__toggle { display: inline-flex; }
  .rs-global-header__nav {
    display: none;
    position: absolute;
    top: 100%;
    left: 0;
    right: 0;
    padding: 8px 0 18px;
    flex-direction: column;
    align-items: stretch;
    gap: 0;
    border-bottom: 1px solid var(--rs-shell-line);
    background: var(--rs-shell-paper);
  }
  /* 18 rows tall with every essay listed. Without a cap, Contact and Login
     sit below the fold on any viewport under ~790px — and the panel is
     absolutely positioned inside a sticky header, so page scrolling cannot
     reach them. Bound it to the space under the bar and let it scroll.
     The article list is NOT trimmed: every essay stays in the menu. */
  .rs-global-header__nav.is-open {
    display: flex;
    max-height: calc(100dvh - var(--rs-shell-header-h));
    overflow-y: auto;
    overscroll-behavior: contain;
    -webkit-overflow-scrolling: touch;
  }
  .rs-global-header__link { padding: 14px 24px; font-size: 0.93rem; }
  .rs-global-header__login { margin: 8px 24px 0; text-align: center; }
  .rs-global-footer__inner { flex-direction: column; }
  .rs-global-footer__nav { align-items: flex-start; }
  .rs-global-footer__links { justify-content: flex-start; }
}

@media (prefers-reduced-motion: reduce) {
  }

/* ════════════════════════════════════════════════════════════════════
   CANONICAL MENU — generic dropdown
   One mechanism for every header submenu (Products, Writing). Replaces
   the Writing-only __writing/__articles pair, which hardcoded five
   article links into every page. The markup is emitted by
   scripts/generate-discovery.mjs; do not hand-edit headers.
   ════════════════════════════════════════════════════════════════════ */

/* Fills the bar's height rather than the ~25.6px line box it would otherwise
   inherit from body's line-height. That line box is why the panels used to
   open through the header's own bottom rule, and why these two triggers sat
   ~1.5px below their plain-link siblings. */
.rs-global-header__menu {
  position: relative;
  display: flex;
  align-self: stretch;
  align-items: center;
}

.rs-global-header__menu-trigger {
  display: inline-flex;
  align-items: center;
  gap: 5px;
}

/* The Products trigger is a <button>; strip the UA chrome so it is
   indistinguishable from the sibling nav links.

   :where() keeps this at element-only specificity (0,0,1). Written as
   `button.rs-global-header__menu-trigger` it scored (0,1,1) and beat
   `.rs-global-header__link`, so the button ignored the nav font and rendered
   at the inherited 16px/400 — one nav item visibly larger than the rest. */
button:where(.rs-global-header__menu-trigger) {
  padding: 0;
  border: 0;
  background: none;
  color: inherit;
  font: inherit;
  cursor: pointer;
}

/* SPLIT CONTROL — label navigates, caret discloses.
   Writing has an index page (/blog/) so its label is a link; Products has
   none, so it stays a single disclosure button. The caret is 7px of border,
   which is nowhere near a tap target, so the button carries padding and
   pulls it back with a negative margin: ~26px of hit area, no visual shift. */
.rs-global-header__menu--split { gap: 0; }

.rs-global-header__menu-trigger--caret {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  /* 26x27 of hit area around a 7px glyph — WCAG 2.2 target size is 24x24.
     The negative margins pull the box back so the caret still sits ~4.5px
     off the label, the same as when the two were one control. */
  min-width: 26px;
  padding: 10px 0;
  margin: -10px -7px -10px -5px;
  color: rgba(11, 19, 32, 0.74);
}

.rs-global-header__menu--split:hover .rs-global-header__menu-trigger--caret,
.rs-global-header__menu--split:focus-within .rs-global-header__menu-trigger--caret,
.rs-global-header__menu--split.is-open .rs-global-header__menu-trigger--caret {
  color: var(--rs-shell-ink);
}

/* Caret rotates when the submenu is showing, so the control reads as a
   disclosure rather than a link that happens to hover. */
.rs-global-header__caret {
  width: 7px;
  height: 7px;
  border-right: 1.5px solid currentColor;
  border-bottom: 1.5px solid currentColor;
  transform: translateY(-2px) rotate(45deg);
  transition: transform 150ms ease;
}

.rs-global-header__menu:hover .rs-global-header__caret,
.rs-global-header__menu:focus-within .rs-global-header__caret,
.rs-global-header__menu.is-open .rs-global-header__caret {
  transform: translateY(1px) rotate(-135deg);
}

.rs-global-header__dropdown {
  position: absolute;
  /* Hangs off the bar itself. Anchored left so the panel starts under the
     word that opened it — anchored right, Products opened under "About" and
     the two panels overlapped each other by ~230px. */
  top: 100%;
  left: 0;
  right: auto;
  margin-top: 1px;                 /* clear the bar's own hairline */
  width: min(300px, calc(100vw - 48px));
  padding: 10px;
  border: 1px solid var(--rs-shell-line);
  background: var(--rs-shell-paper);
  box-shadow: 0 20px 50px rgba(11, 19, 32, 0.12);
  opacity: 0;
  pointer-events: none;
  transform: translateY(-4px);
  transition: opacity 150ms ease, transform 150ms ease;
}

.rs-global-header__menu:hover .rs-global-header__dropdown,
.rs-global-header__menu:focus-within .rs-global-header__dropdown,
.rs-global-header__menu.is-open .rs-global-header__dropdown {
  opacity: 1;
  pointer-events: auto;
  transform: none;
}

/* The Writing panel grows by one entry per essay published. Cap it so the
   menu can never outgrow the viewport, and let it scroll instead. */
.rs-global-header__dropdown--scroll {
  max-height: min(60vh, 460px);
  overflow-y: auto;
  overscroll-behavior: contain;
}

/* The last submenu in the bar sits close enough to the edge that a
   left-anchored 300px panel leaves the viewport. Anchor it to its right. */
.rs-global-header__menu--end .rs-global-header__dropdown {
  left: auto;
  right: 0;
}

.rs-global-header__dropdown-link {
  display: block;
  padding: 10px 12px;
  color: rgba(11, 19, 32, 0.72);
  font: 500 0.77rem/1.35 var(--rs-shell-sans);
  text-decoration: none;
}

.rs-global-header__dropdown-link:hover,
.rs-global-header__dropdown-link[aria-current='page'] {
  /* design.md: "Never use red for text". Weight and a rule carry the state. */
  color: var(--rs-shell-ink);
}

.rs-global-header__dropdown-link[aria-current='page'] {
  font-weight: 600;
  box-shadow: inset 2px 0 0 var(--rs-shell-ink);
}

.rs-global-header__dropdown-meta {
  display: block;
  margin-top: 2px;
  /* Was rgba(...,0.42) at 0.68rem — 2.7:1 on the panel, under the 4.5:1 AA
     floor, on the only text that distinguishes one menu entry from another. */
  color: rgba(11, 19, 32, 0.62);
  font: 400 0.72rem/1.3 var(--rs-shell-sans);
}

/* Collapsed-menu overrides. These must follow the base rules above: a media
   query adds no specificity, so at equal specificity source order decides. */
@media (max-width: 860px) {
  /* Inside the collapsed menu the submenus are listed inline and always
     open, so there is nothing to disclose — the caret would be a lie, and
     the split control's caret button would be an empty full-width row. */
  .rs-global-header__menu { position: static; display: block; }
  .rs-global-header__caret { display: none; }
  .rs-global-header__menu-trigger--caret { display: none; }
  /* The button reset above zeroes UA padding at element+class specificity,
     which would otherwise beat .rs-global-header__link and leave Products
     flush to the edge while its siblings are indented. */
  button:where(.rs-global-header__menu-trigger) {
    width: 100%;
    padding: 14px 24px;
    text-align: left;
  }
  .rs-global-header__dropdown {
    position: static;
    width: auto;
    padding: 0 0 10px 18px;
    border: 0;
    box-shadow: none;
    opacity: 1;
    pointer-events: auto;
    transform: none;
  }
  .rs-global-header__dropdown::before { content: none; }
  .rs-global-header__dropdown-link { padding: 8px 24px; }
  .rs-global-header__dropdown-meta { display: none; }
  /* The desktop cap is lifted here because the nav itself now scrolls. */
  .rs-global-header__dropdown--scroll { max-height: none; overflow-y: visible; }
}

/* ════════════════════════════════════════════════════════════════════
   CANONICAL SECTION RAIL
   The second-level bar on long pages. Replaces three bespoke copies:
   .parent-rail (index), .measure-subnav, .hugin-subnav — each of which
   re-solved sticking, spacing and overflow separately.

   Sticks directly under the global header via CSS alone; the old copies
   used a scroll listener toggling .is-stuck, which this makes redundant.
   ════════════════════════════════════════════════════════════════════ */

.rs-rail {
  position: sticky;
  top: var(--rs-shell-header-h, 82px);
  z-index: 900;
  border-bottom: 1px solid var(--rs-shell-line);
  background: var(--rs-shell-paper);
  font-family: var(--rs-shell-sans);
}

.rs-rail *,
.rs-rail *::before,
.rs-rail *::after { box-sizing: border-box; }

.rs-rail__inner {
  width: min(calc(100% - var(--rs-shell-gutter)), var(--rs-shell-measure));
  min-height: 54px;
  margin-inline: auto;
  display: flex;
  align-items: center;
  gap: 32px;
}

.rs-rail__wordmark {
  display: inline-flex;
  align-items: center;
  flex: 0 0 auto;
  gap: 9px;
  color: var(--rs-shell-ink);
  font: 700 0.74rem/1 var(--rs-shell-sans);
  letter-spacing: 0.09em;
  text-transform: uppercase;
  text-decoration: none;
  white-space: nowrap;
}

.rs-rail__wordmark img { display: block; width: 26px; height: 26px; }

/* The links scroll rather than wrap or push the CTA off the edge. The mask
   fades the last visible link so the strip reads as scrollable; when they
   fit, the fade falls on empty space after them and is invisible — so this
   is inert on the three-link rails and load-bearing on the ten-link one
   (OrgAI has ten headings; RS Measure carries six links plus a CTA). It
   used to apply below 860px only, which left the widest rails to overflow
   the bar at desktop widths. */
.rs-rail__nav {
  display: flex;
  align-items: center;
  gap: 28px;
  min-width: 0;
  flex: 1 1 auto;
  overflow-x: auto;
  scrollbar-width: none;
  -webkit-overflow-scrolling: touch;
  -webkit-mask-image: linear-gradient(to right, #000 calc(100% - 28px), transparent);
  mask-image: linear-gradient(to right, #000 calc(100% - 28px), transparent);
}

.rs-rail__nav::-webkit-scrollbar { display: none; }

.rs-rail__link {
  color: rgba(11, 19, 32, 0.66);
  font: 600 0.72rem/1 var(--rs-shell-sans);
  letter-spacing: 0.09em;
  text-transform: uppercase;
  text-decoration: none;
  white-space: nowrap;
  padding: 6px 0;
  border-bottom: 1.5px solid transparent;
  transition: color 150ms ease, border-color 150ms ease;
}

.rs-rail__link:hover,
.rs-rail__link[aria-current='true'] {
  color: var(--rs-shell-ink);
  border-bottom-color: var(--rs-shell-red);
}

/* Pinned right and never allowed to shrink — this is the CTA, and the
   Hugin copy of this bar clipped it off-screen at 375px.

   Ink text on a red underline, not a red fill. design.md: "One red element
   per composition" — the Login pill directly above is that element — and
   "Never use red for text, borders, or backgrounds." A filled red button
   here put two red blocks in the chrome at once. This is the treatment the
   bespoke .hugin-nav-action and .measure-nav-action both used. */
.rs-rail__action {
  flex: 0 0 auto;
  margin-left: auto;
  padding: 8px 0 6px;
  border-bottom: 2px solid var(--rs-shell-red);
  color: var(--rs-shell-ink);
  font: 600 0.7rem/1 var(--rs-shell-sans);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  text-decoration: none;
  white-space: nowrap;
  transition: color 150ms ease, border-color 150ms ease;
}

.rs-rail__action:hover {
  color: var(--rs-shell-red);
}

.rs-rail :focus-visible {
  outline: 2px solid var(--rs-shell-navy);
  outline-offset: 2px;
}

@media (max-width: 1040px) {
  .rs-rail__inner { gap: 22px; }
  .rs-rail__nav { gap: 20px; }
}

@media (max-width: 860px) {
  .rs-rail__inner {
    width: min(calc(100% - var(--rs-shell-gutter)), var(--rs-shell-measure));
    gap: 14px;
    min-height: 48px;
  }
  /* The wordmark drops to its icon, or disappears when it has none: at this
     width the rail is section links plus CTA, and the global header directly
     above already carries site identity. */
  .rs-rail__wordmark span { display: none; }
  .rs-rail__wordmark:empty,
  .rs-rail__wordmark:not(:has(img)) { display: none; }

  .rs-rail__nav { gap: 18px; }

  .rs-rail__action { padding: 7px 0 5px; font-size: 0.66rem; }
}

@media (prefers-reduced-motion: reduce) {
  .rs-rail__link,
  .rs-rail__action { transition: none; }
}

/* ── Skip link ──────────────────────────────────────────────────────
   The first focusable thing on every page. Nothing on the site had one,
   so a keyboard user traversed the whole menu — up to 18 rows with every
   essay listed — before reaching the content, on every page. */
.rs-skip-link {
  /* Fixed, so it is always at the top of the viewport rather than wherever
     the containing block happens to start; and moved with transform rather
     than a percentage offset, which resolved against that block's height. */
  position: fixed;
  left: 16px;
  top: 0;
  transform: translateY(-140%);
  transition: transform 120ms ease;
  z-index: 1100;
  padding: 10px 18px;
  background: var(--rs-shell-navy);
  color: var(--rs-shell-paper);
  font: 600 0.8rem/1 var(--rs-shell-sans);
  text-decoration: none;
  border-radius: 0 0 4px 4px;
}

/* :focus, not :focus-visible — the link exists only for keyboard users and
   must show whenever it takes focus. */
.rs-skip-link:focus {
  transform: translateY(0);
  outline: 2px solid var(--rs-shell-paper);
  outline-offset: -4px;
}

@media (prefers-reduced-motion: reduce) {
  .rs-skip-link { transition: none; }
}

/* ── Anchor offset ──────────────────────────────────────────────────
   Targets used to land under the sticky bar. Six per-page values existed
   (24px in four places, 78px in hugin.css, 84px in measure.css) against
   83px of header, or 138px on a page carrying a rail. */
html { scroll-padding-top: calc(var(--rs-shell-header-h) + 24px); }
html:has(.rs-rail) { scroll-padding-top: calc(var(--rs-shell-header-h) + 55px + 24px); }
