/* ==========================================================================
   section-rail.css — the section rail

   NOT "the rail". On this page that word already means the FRAME rail — the
   shared left edge, width, radius and chrome that makes nine mockups read as
   one app being zoomed (layout.css, `.zframe-rail`). This is a different
   object: a reader's position indicator in the left margin, `.srail`. The file
   is named in full for exactly that reason.

   Twelve hairlines down the left margin, one lit. It is the page's only
   position indicator, and it exists because this page is one long descent
   through a single product: the frames deliberately look alike, the sections
   carry no numbers, and the nav names four of twelve beats. The rail answers
   "how far in am I, and how much is left" without adding a word of copy.

   Three rules it lives under.

   1. IT IS NOT ACCENT. Page chrome spends the accent exactly twice — the two
      wordmarks — plus links and the filled CTAs; tokens.css calls that budget
      the whole discipline. So the rail speaks in neutrals, on the same ladder
      the nav links already use: border-2 at rest, muted on hover, ink when
      current. A teal tick here would be the page's fifth accent.
   2. IT PAINTS COMPLETE. All twelve anchors, and the first one's `is-on`, are
      in the markup — a visitor with no JS gets a working rail, and rail.js only
      ever moves the lit state. Nothing here is a reveal, a stagger or a fade
      in; the width change is a hover-class response, on the same 0.2s the nav
      and the buttons use.
   3. IT RECEDES WHEN NOBODY IS READING IT. A permanent ladder of twelve marks
      is a permanent claim on the left margin, which a page this quiet cannot
      afford. So the resting state is MINIMIZED — short, dim bars — and the
      rail comes back to full size for the two moments it is being used: while
      the page is scrolling, and while the cursor or keyboard focus is on it.
      `is-idle` is in the markup, so the minimized state is what a no-JS
      visitor gets, and CSS `:hover` alone is enough to open it; the script only
      adds the scroll half. Reduced motion drops the whole behaviour and parks
      the rail open — see section-rail.js.
   4. IT NEVER TOUCHES THE CONTENT COLUMN. Everything about the horizontal
      geometry here is measured off the container edge rather than the window,
      because what the rail must clear is the copy, not the glass. The bar
      alone needs the viewport to be 1280px; the label needs 120px more than
      that, so it is gated separately at 1400px and the rail is simply bars
      below it. Neither ever crosses a word of copy at any width — the rail is
      an affordance, not a thing to reflow the page for.
   ========================================================================== */

.srail {
  /* Off the container edge, not the window: the rail should read as the outer
     margin of the layout rather than something stuck to the glass. Bottoms out
     at 18px right as the gate below takes over, settles at 60px when there is
     room.

     The middle term is `- 94px`, not `- 46px`, and that number is the LABEL's,
     not the bar's. Measured, not guessed: the widest label is "Governance" at
     68.7px, sitting 36px out from the rail's left edge, so the label occupies
     up to +104.7px and 94px is what still leaves ~12px of air between it and
     the first character of the copy — which is 24px inside the container box,
     the gutter being empty. Rename a section to something longer and this has
     to be re-measured. */
  position: fixed;
  left: clamp(18px, calc((100vw - var(--maxw)) / 2 - 94px), 60px);
  top: 50%;
  transform: translateY(-50%);
  z-index: 90;                 /* under the sticky nav (100), over everything else */
  display: flex;
  flex-direction: column;

  /* Widening the hover target without moving anything: padding grows the box
     rightward into empty margin, the bars stay put at the box's left edge.
     24px is what still clears the copy at 1280, the narrowest width the rail
     appears at. The label overshoots this box but is `pointer-events: none`. */
  padding-right: 24px;

  --rail-off: var(--border-2);
  --rail-mid: var(--muted);
  --rail-on:  var(--ink);

  /* The three numbers the idle state moves. Kept as properties so there is one
     set of width rules below rather than a second, idle copy of each. */
  --bar:      13px;
  --bar-on:   26px;
  --rail-fade: 1;

  opacity: var(--rail-fade);
  transition: opacity 0.3s var(--ease);
}

/* Resting state: a ghost of the ladder, and the lit bar still legible in it.
   Shrink first, dim second — "minimized" should read as smaller, not just
   fainter, or the rail turns into a smudge instead of a smaller rail. */
.srail.is-idle {
  --bar:      6px;
  --bar-on:   14px;
  --rail-fade: 0.6;
}

/* Attention beats idle, whatever the scroll is doing. `:focus-within` is not
   decoration here — it is the only way a keyboard user ever opens the rail. */
.srail.is-idle:hover,
.srail.is-idle:focus-within {
  --bar:      13px;
  --bar-on:   26px;
  --rail-fade: 1;
}

/* §9's band is dark in BOTH themes and its background spans the full viewport
   width, so it runs behind a fixed rail. `--ink` on `--paper` there is ink on
   ink — invisible. rail.js flips this class when the current section carries
   `.sec--dark`, which re-points the three locals exactly the way tokens.css
   re-points the page tokens for the band itself. */
.srail.is-dark {
  --rail-off: rgba(180, 200, 196, 0.24);
  --rail-mid: #7F8F8C;
  --rail-on:  #F2F5F4;
}

/* The link is the hit target (26×20); the bar is the pseudo-element, so the
   clickable row is comfortable while the mark stays a hairline. */
.srail a {
  display: block;
  position: relative;
  width: 26px;
  height: 20px;
}

.srail a::before {
  content: '';
  position: absolute;
  left: 0;
  top: 9px;
  width: var(--bar);
  height: 2px;
  border-radius: 1px;
  background: var(--rail-off);
  transition: width 0.22s var(--ease), background-color 0.18s var(--ease);
}

/* Order matters: `a:hover::before` and `a.is-on::before` have identical
   specificity, so is-on must come second or hovering the current section
   would shorten its bar. */
.srail a:hover::before        { width: 19px; background: var(--rail-mid); }
.srail a.is-on::before        { width: var(--bar-on); background: var(--rail-on); }
.srail a.is-on:hover::before  { background: var(--rail-on); }

/* The name, on hover. Twelve identical hairlines cannot say where they go, so
   each one says it — quietly, in the nav's own type (--font-display 500, the
   face `.nav__links a` already uses) rather than an eyebrow's mono caps, since
   this IS a nav label and not a data caption.

   Absolutely positioned, so it never widens the flex column, and
   `pointer-events: none` so the 26x20 bar row stays the whole hit target and
   the cursor can never sit on the label with the bar un-hovered. Revealed by
   opacity ONLY — never `visibility` or `display` — because the span is also
   the link's visible name and must stay in the accessibility tree at rest. */
.srail__l {
  position: absolute;
  left: 36px;                  /* 26px bar + a 10px gap */
  top: 50%;
  transform: translateY(-50%);
  white-space: nowrap;
  font-family: var(--font-display);
  font-weight: 500;
  font-size: 12.5px;
  letter-spacing: -0.01em;
  line-height: 1;
  color: var(--rail-mid);
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.18s var(--ease), color 0.18s var(--ease);
}
.srail a:hover .srail__l,
.srail a:focus-visible .srail__l { opacity: 1; }
/* On the lit bar the name matches the bar, so hovering the current section
   reads as one mark rather than a bar and a caption. */
.srail a.is-on .srail__l { color: var(--rail-on); }

/* No outline hugging a 2px line — the row is the target, so ring the row. */
.srail a:focus-visible { outline-offset: 3px; }

/* Park every transition. The idle state itself is dropped in JS rather than
   here, because a scroll-driven size change that snaps with no transition is
   worse than one that never happens. */
@media (prefers-reduced-motion: reduce) {
  .srail,
  .srail a::before,
  .srail__l { transition: none; }
}

/* Between 1280 and 1399 there is room for the bar but not for a name beside
   it, so the rail degrades to bars rather than printing over the copy. Here
   `display: none` is correct and the `aria-label` on each link is what carries
   the accessible name — which is why that attribute duplicates the span text
   instead of describing it differently. */
@media (max-width: 1399px) { .srail__l { display: none; } }

/* Below 1280px the bar plus a real gap stops clearing the container's gutter,
   and by 900px the whole layout is one column with nowhere to put it. */
@media (max-width: 1279px) { .srail { display: none; } }
/* A short window would push twelve rows into the nav. */
@media (max-height: 620px) { .srail { display: none; } }
