/* =========================================================================
   Section rail — the fixed contents nav shared by the homepage and the case
   study pages. A column of dots in the left margin; the active section shows
   a label pill and the rest reveal on hover. Plain anchors underneath, so it
   degrades to a working contents list without JS. On narrow screens it becomes
   a dropdown pinned bottom-right.

   Class names keep the `cs-` prefix from where the pattern started (the case
   studies) so that page's markup didn't have to change when this moved out.

   Behaviour lives in the page's own script: case.js / alt.js.
   ========================================================================= */

/* ----------------------------- section rail -----------------------------
   Desktop: a fixed column of dots in the left margin. The active section's
   label shows as an accent pill; the others reveal on hover (the Onki
   pattern). Plain anchors underneath, so with JS off it's still a working
   contents list. Mobile: collapses to a dropdown — see the media query. */
.cs-rail {
  /* left offset is deliberately tight: the Back circle is wider than the dots,
     and in the narrowest sliver of the dots-only tier (~1130px) the extra width
     would clip a wide figure's edge. Pulling the rail toward the edge keeps
     that worst case to a couple of imperceptible px. */
  position: fixed; z-index: 30; left: clamp(.85rem, 2vw, 2.4rem); top: 50%; transform: translateY(-50%);
  /* Fixed to the Back circle's width and centre-aligned, so the dots sit on a
     shared vertical axis with the circle's icon rather than flush to its left
     edge. The circle expands rightward on hover (align-self below), so it can
     overflow this width without shifting that axis. */
  width: 40px;
  display: flex; flex-direction: column; align-items: center; gap: var(--space-s);
  /* dot -> label pill. Wide enough that an active pill reads as a separate
     object floating in the margin rather than a tail on the dot. */
  --rail-lbl-gap: 2.6rem;
}
.cs-rail__home { align-self: flex-start; }
.cs-rail__toggle { display: none; }               /* desktop has no toggle */
.cs-rail__back { display: none; }                 /* mobile-only entry */

/* Circular Back button, first in the rail. Icon-only at rest, expands to a
   pill saying "Back" on hover — same width-morph as a FAB. Black on white
   with a soft shadow, matching the section pills below it. */
.cs-rail__home {
  display: inline-flex; align-items: center; overflow: hidden;
  height: 40px; max-width: 40px; border-radius: var(--radius-pill); white-space: nowrap;
  background: var(--card); color: var(--ink); border: 1px solid var(--line);
  box-shadow: var(--shadow-raised);
  text-decoration: none;
  transition: max-width .3s cubic-bezier(.16,1,.3,1), box-shadow .2s ease;
}
.cs-rail__home-ico { flex: none; width: 40px; height: 40px; display: grid; place-items: center; font-size: 18px; }
.cs-rail__home-txt {
  font-size: 12px; font-weight: 700; letter-spacing: .09em; text-transform: uppercase;
  padding-right: 1.15em; /* audit-ok: label reveal width, tuned to the 40px pill */
  opacity: 0; transition: opacity .2s ease;
}
.cs-rail__home:hover, .cs-rail__home:focus-visible {
  max-width: 160px; box-shadow: var(--shadow-raised-hi);
}
.cs-rail__home:hover .cs-rail__home-txt, .cs-rail__home:focus-visible .cs-rail__home-txt { opacity: 1; }
.cs-rail__list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--space-2xs); }
.cs-rail__list a {
  position: relative; display: flex; align-items: center;
  padding: var(--space-2xs); text-decoration: none; color: var(--ink-faint);
}
.cs-rail__dot {
  width: 7px; height: 7px; border-radius: var(--radius-round);
  background: currentColor; opacity: .4; flex: none;
  transition: opacity .2s ease, transform .2s ease, background .2s ease;
}
.cs-rail__lbl {
  position: absolute; left: var(--rail-lbl-gap); top: 50%;
  transform: translateY(-50%) translateX(-4px);
  font-size: var(--type-pill-size); font-weight: var(--type-pill-weight);
  letter-spacing: var(--type-pill-track); text-transform: uppercase;
  line-height: 1.4;
  /* A long section name wraps instead of running on, so the pill stays inside
     the page margin instead of lying across column one. The cap is measured
     against the real distance to the content band by rail.js (fitLabels); the
     fallback covers first paint and the no-JS case. Because the pill is
     centred on the dot (top 50% + translateY -50%), a two-line pill stays
     centred on it too. */
  white-space: normal; text-wrap: balance;
  /* Centred once it wraps, so a short second line sits evenly in the pill
     rather than hugging one edge. On a single line this is a no-op — the pill
     shrink-wraps its text. */
  text-align: center;
  width: max-content; max-width: var(--rail-lbl-max, 11rem);
  color: var(--paper);
  padding: var(--pad-pill); border-radius: var(--radius-pill);
  background: var(--ink);
  box-shadow: var(--shadow-raised);
  opacity: 0; pointer-events: none;
  transition: opacity .2s ease, transform .2s ease;
}
/* a two-line pill is tall enough to reach its neighbours, so whichever one is
   being pointed at has to win */
.cs-rail__list a:hover, .cs-rail__list a.is-active { z-index: 1; }
/* A label may carry a qualifier (a year) to tell two entries from the same
   company apart — set back rather than sized down.

   No margin on it: a left margin becomes a first-character indent the moment
   the qualifier wraps onto its own line, which is exactly the off-centre look
   the centring above is there to avoid. The word space plus the drop in
   opacity already separate it. */
.cs-rail__lbl i { font-style: normal; opacity: .55; }

.cs-rail__list a:hover .cs-rail__dot,
.cs-rail__list a:focus-visible .cs-rail__dot { opacity: .85; }

/* Gated to the tier it belongs to. Ungated, this rule's (0,3,0) beat the
   dropdown's own (0,1,0) `.cs-rail__lbl { transform: none }` — a media query
   doesn't add specificity — so hovering a row in the collapsed menu yanked its
   label up by half its height. In the dropdown a hover is a background, and
   nothing moves. */
@media (min-width: 1130px) {
  .cs-rail__list a:hover .cs-rail__lbl,
  .cs-rail__list a:focus-visible .cs-rail__lbl { opacity: 1; transform: translateY(-50%); }
}
/* Active section: the dot is the whole marker, at every width. It fills to ink
   and grows, which is legible on its own and needs no words beside it.

   There used to be a persistent black label pill here above 1400px. It was a
   third floating capsule on a page that already had the masthead and the Back
   button, and naming the section you are already reading is the one label a
   reader does not need — the heading is on screen. Labels are still one hover
   away, which is when a name is actually wanted: deciding where to go next. */
.cs-rail__list a.is-active .cs-rail__dot { opacity: 1; transform: scale(1.3); background: var(--ink); }

@media (prefers-reduced-motion: reduce) {
  .cs-rail__dot, .cs-rail__lbl, .cs-rail__toggle,
  .cs-rail__list, .cs-rail__list li { transition: none; }
}

/* Between 1130 and 1399 the dots still clear the wide figures but the active
   label pill wouldn't — so that tier runs dots-only (handled above: the pill
   rule is gated to >=1400, hover labels still work). Below 1130 even the dots
   would collide, so the rail becomes a floating dropdown pinned bottom-right —
   thumb-reachable, clear of the hero, carrying a Back entry since the hero's
   own Back pill has scrolled away by then. */
@media (max-width: 1129px) {
  .cs-rail { left: auto; right: 1rem; bottom: 1rem; top: auto; transform: none; width: auto; align-items: flex-end; }
  .cs-rail__home { display: none; }               /* Back lives in the dropdown list here */
  .cs-rail__toggle {
    display: inline-flex; align-items: center;
    font: inherit; font-size: 13px; font-weight: 600; color: var(--ink);
    background: var(--card); border: 1px solid var(--line); border-radius: var(--radius-pill);
    padding: var(--pad-pill-lg); min-height: 44px; cursor: pointer;
    box-shadow: var(--shadow-raised-hi);
    /* A <button> is centred by the UA stylesheet, which centres each wrapped
       line on its own — so a two-line label sat with the shorter line indented
       against the longer one. Section names are a left-reading list, not a
       centred title. */
    text-align: left;
    /* Bounds the wrap instead of letting a long name stretch the pill across
       the viewport. */
    max-width: min(14rem, 62vw);
    line-height: 1.3;
    transition: background .18s ease, color .18s ease,
                border-color .18s ease, box-shadow .18s ease;
  }
  /* Open state: the pill inverts, the same way an active section's label pill
     does in the expanded rail. Without it the only signal that the menu is
     open was the panel itself. */
  .cs-rail.is-open .cs-rail__toggle {
    background: var(--ink); color: var(--paper); border-color: var(--ink);
    box-shadow: var(--shadow-raised-hi);
  }

  /* Opens and closes from the toggle it hangs off, growing upward because the
     rail sits at the bottom of the screen.

     Nothing here animates height. A height animation would relayout the flex
     rows on every frame and the items would visibly compress — exactly the
     jump we don't want. Opacity and transform are composited instead, so the
     content never reflows; the panel is only ever scaled and faded. */
  .cs-rail__list {
    position: absolute; right: 0; bottom: calc(100% + .5rem);
    min-width: 13rem; gap: 0;
    background: var(--card); border: 1px solid var(--line); border-radius: 14px;
    padding: var(--space-2xs); box-shadow: var(--shadow-overlay);
    display: none;
    opacity: 0; transform: translateY(16px) scale(.93);
    transform-origin: bottom right;
    transition: opacity .2s ease,
                transform .3s cubic-bezier(.16, 1, .3, 1),
                display .3s allow-discrete;
  }
  .cs-rail.is-open .cs-rail__list { display: flex; opacity: 1; transform: none; }
  /* Without this the panel would appear at its open state on the very first
     frame and only animate on the way out. */
  @starting-style {
    .cs-rail.is-open .cs-rail__list { opacity: 0; transform: translateY(16px) scale(.93); }
  }

  /* The rows fade in slightly behind the panel and leave immediately ahead of
     it, so a collapse reads as the card closing rather than as seven rows
     sliding around inside it. */
  .cs-rail__list li { opacity: 0; transition: opacity .1s ease; }
  .cs-rail.is-open .cs-rail__list li { opacity: 1; transition: opacity .2s ease .09s; }
  .cs-rail__back { display: block; margin-bottom: var(--space-3xs); padding-bottom: var(--space-3xs); border-bottom: 1px solid var(--line); }
  .cs-rail__back a { color: var(--ink-soft); font-weight: 600; }
  .cs-rail__list a {
    position: static; padding: var(--space-2xs) var(--space-xs); gap: var(--space-xs); border-radius: var(--radius-s); color: var(--ink);
  }
  .cs-rail__list a:hover { background: var(--paper); }
  .cs-rail__dot { opacity: .5; }
  .cs-rail__lbl {
    position: static; opacity: 1; transform: none; pointer-events: auto;
    background: none; box-shadow: none; padding: 0; color: inherit; letter-spacing: .04em;
    max-width: none; width: auto;
  }
  .cs-rail__list a.is-active { color: var(--ink); }
  .cs-rail__list a.is-active .cs-rail__dot { background: var(--ink); opacity: 1; transform: none; }
  .cs-rail__list a.is-active .cs-rail__lbl { color: var(--ink); background: none; }
}
