/* ==========================================================================
   Braid — shared stylesheet
   --------------------------------------------------------------------------
   Holds the design tokens, reset, base type, and the site-wide chrome that is
   IDENTICAL across every page: nav, left rail, section-header pattern, footer.

   Each page links this file and then declares its own page-specific sections
   in a co-located <style> block in its <head>. That split keeps page styles
   from colliding (.prose / .lead-row / .braid-emph differ per page) and maps
   cleanly onto a future Astro migration (this file → global.css, each page's
   <style> → an Astro scoped page style).

   Tokens, type scale and component rules are lifted from the design handoff
   (design_handoff_website/) — see BRAID-design-system.md for the source.
   Typography is the brand's Aeonik Pro + Aeonik Fono (declared below, files in
   /fonts/), with the original system stack kept as a fallback in every cascade.
   ========================================================================== */

*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }

:root {
  --bg:     #F3EFED;   /* warm off-white page */
  --ink:    #1C1C27;   /* navy-charcoal text */
  --muted:  #5C5C66;   /* secondary text */
  /* Was #9A9A9F, which measured 2.45:1 on --bg — below AA (4.5:1) everywhere it
     is used, and it carries the whole mono microtype layer: rail metadata, TOC
     numbers and page refs, figure captions, entry meta, contact labels, footers.
     #6B6B72 is 4.63:1 on --bg and stays clearly lighter than --muted (5.78:1),
     so the three-step ink → muted → subtle hierarchy survives. */
  --subtle: #6B6B72;   /* tertiary / mono captions */
  --rule:   #D9D3D0;   /* hairlines */
  --beige:  #E6E0DC;   /* placeholder image fills */
  --magic:  #FDF202;   /* electric yellow accent */
  --pop:    #FF5500;
  /* Height of the sticky masthead. Seven values across four stylesheets depend
     on it — the rail's sticky offset and height, the mobile panel's top, and
     three scroll-margins that keep jumped-to headings clear of the bar. Three
     of those fail silently when they drift (a heading lands *under* the bar on
     a TOC jump), so they all derive from here rather than repeating a literal. */
  --nav-h:  80px;     /* bright orange accent (used sparingly, e.g. in AURA) */
}

/* ── FONTS ────────────────────────────────────────────────────────────────
   Aeonik Pro (display + body) and Aeonik Fono (mono labels/eyebrows/meta) —
   the brand's licensed faces. Files live in /fonts/. Only the weights the
   design uses are declared here; the full family is staged under /fonts/ for
   later use. The cascade keeps the old system stack as a fallback. */
@font-face {
  font-family: 'Aeonik Pro';
  src: url('/fonts/pro/AeonikPro-Regular.woff2') format('woff2'),
       url('/fonts/pro/AeonikPro-Regular.woff') format('woff');
  font-weight: 400; font-style: normal; font-display: swap;
}
@font-face {
  font-family: 'Aeonik Pro';
  src: url('/fonts/pro/AeonikPro-RegularItalic.woff2') format('woff2'),
       url('/fonts/pro/AeonikPro-RegularItalic.woff') format('woff');
  font-weight: 400; font-style: italic; font-display: swap;
}
@font-face {
  font-family: 'Aeonik Pro';
  src: url('/fonts/pro/AeonikPro-Medium.woff2') format('woff2'),
       url('/fonts/pro/AeonikPro-Medium.woff') format('woff');
  font-weight: 500; font-style: normal; font-display: swap;
}
@font-face {
  font-family: 'Aeonik Pro';
  src: url('/fonts/pro/AeonikPro-Bold.woff2') format('woff2'),
       url('/fonts/pro/AeonikPro-Bold.woff') format('woff');
  font-weight: 700; font-style: normal; font-display: swap;
}
@font-face {
  font-family: 'Aeonik Fono';
  src: url('/fonts/fono/AeonikFono-Regular.woff2') format('woff2'),
       url('/fonts/fono/AeonikFono-Regular.woff') format('woff');
  font-weight: 400; font-style: normal; font-display: swap;
}
@font-face {
  font-family: 'Aeonik Fono';
  src: url('/fonts/fono/AeonikFono-Medium.woff2') format('woff2'),
       url('/fonts/fono/AeonikFono-Medium.woff') format('woff');
  font-weight: 500; font-style: normal; font-display: swap;
}
/* No Light (300) face is declared here on purpose. The Illustrator figure
   exports set font-weight:300 and then position every tspan at an absolute x
   computed from Light's advance widths, so they do need it — but they are
   referenced from disk, and an SVG loaded as an image cannot reach this
   stylesheet at all. Each such file embeds its own subset instead
   (see assets/images/aura_architecture.svg). Declaring it here would be a rule
   nothing on the site matches. AeonikFono-Light.woff2 remains in /fonts/fono/
   as the source for those subsets. */

html { scroll-behavior: smooth; }

/* Metric-matched fallback — the largest measured performance defect on the site.
   The cascade used to read 'Aeonik Pro', 'Helvetica Neue', Arial: Helvetica Neue
   is the prototype's license-free placeholder and was never removed from the
   fallback slot once Aeonik shipped. Measured against Aeonik Pro at 100px over a
   representative string, Helvetica Neue sets 9.7% narrower while Arial is 0.17%
   off — so on any machine with Helvetica Neue the page laid out ~10% narrow,
   broke headings onto different line counts, and reflowed hard when the webfont
   landed. On a throttled 1.6 Mbps connection that measured CLS 0.2408 on the
   longest article; blocking the fonts dropped it to 0.0006.

   size-adjust pins the fallback to Aeonik's advance width whatever resolves
   locally, so the reserved space matches before and after the swap. No
   ascent/descent overrides: line-heights are set explicitly nearly everywhere
   here, and the measured content-box difference was under 1%. */
@font-face {
  font-family: 'Aeonik Fallback';
  src: local('Arial'), local('Helvetica'), local('Liberation Sans'), local('Nimbus Sans');
  size-adjust: 100.17%;
  font-weight: 100 900;
  font-style: normal;
}

/* The mono counterpart, and the one that actually cost us. Measured against
   Aeonik Fono, generic `monospace` sets 5.9% WIDER — enough that the article
   header's tracked uppercase eyebrow wrapped to two lines in the fallback and
   snapped back to one when Fono arrived. That single line-count change moved the
   whole article down 16px and was worth CLS 0.2404 on the throttled profile;
   images and MathJax were both innocent.

   size-adjust is deliberately conservative at 94%: the failure mode is a change
   in line COUNT, so a fallback that is slightly narrow is harmless (the text
   simply grows on swap without reflowing) while one that is slightly wide wraps
   and shifts. Menlo/SF Mono measure 99.3%, generic monospace 94.4%; 94% is at or
   under both. */
@font-face {
  font-family: 'Aeonik Fono Fallback';
  src: local('SF Mono'), local('Menlo'), local('Consolas'), local('DejaVu Sans Mono'),
       local('Liberation Mono'), local('Courier New');
  size-adjust: 94%;
  font-weight: 100 900;
  font-style: normal;
}

body {
  font-family: 'Aeonik Pro', 'Aeonik Fallback', Arial, sans-serif;
  background: var(--bg);
  color: var(--ink);
  -webkit-font-smoothing: antialiased;
  line-height: 1.5;
}

a { color: inherit; text-decoration: none; }
::selection { background: var(--magic); color: var(--ink); }

/* The signature yellow inline highlight (gradient mask). Index overrides this
   with a 2px underline variant via a more specific selector in its own style. */
.braid-emph { background: linear-gradient(transparent 60%, var(--magic) 60%); padding: 0 1px; }

/* ── FOCUS ────────────────────────────────────────────────────────────────
   Authored rather than inherited from the browser: the UA default is
   engine-specific, and one stylesheet already switched it off (article.css's
   share row). Ink on the paper ground; the ink plates flip it to paper, since a
   single ink ring would be invisible against them. The ring is drawn outside
   the box, so a light button sitting on a dark plate still wants the paper
   colour. `.cta-sec` gets the same flip in aura.css — it is an ink plate that
   does not carry the .section.dark class. */
:focus-visible { outline: 2px solid var(--ink); outline-offset: 3px; }
.section.dark :focus-visible { outline-color: var(--bg); }
/* <main tabindex="-1"> exists only so the skip link can move focus, not just
   scroll. It is a landmark, not a control, so it takes no ring — outlining a
   whole page column would read as an error state. */
main[tabindex]:focus, main[tabindex]:focus-visible { outline: none; }

/* ── SKIP LINK ────────────────────────────────────────────────────────────
   First focusable element on every page. The rail's in-page nav sits before
   <main> in the DOM, so without this a keyboard or screen-reader visit crosses
   4–6 TOC links and the rail's contact address before reaching the first
   sentence — on every page, with no way around it (WCAG 2.4.1, Level A). */
.skip-link {
  position: absolute; left: 8px; top: 8px;
  z-index: 60;                     /* above the sticky nav (50) */
  transform: translateY(-200%);    /* parked off-screen, still focusable */
  background: var(--ink); color: var(--bg);
  font-family: 'Aeonik Fono', 'Aeonik Fono Fallback', 'SF Mono', Menlo, monospace;
  font-size: 11px; letter-spacing: 0.14em; text-transform: uppercase;
  padding: 12px 16px;
  transition: transform 0.15s ease;
}
/* Keeps the inherited ink ring: outline-offset draws it 3px clear of the plate,
   so it lands on the paper page ground, where paper-on-paper would vanish. */
.skip-link:focus, .skip-link:focus-visible { transform: translateY(0); }

/* ── NAV (sticky, 56px) ───────────────────────────────────────────────── */
.nav {
  position: sticky; top: 0; z-index: 50;
  /* 0.92, the value the design handoff specifies — the code had drifted to 0.82.
     At 0.82 the bar composited to rgb(204,201,201) over the ink plates, which
     dropped the nav links to 4.02:1, split the bar into a two-tone band with a
     visible seam where the light rail ends (x=280), and turned pale chartreuse
     over the Careers yellow plate — an accidental large yellow surface. */
  background: rgba(243,239,237,0.92);
  backdrop-filter: saturate(140%) blur(10px);
  border-bottom: 1px solid var(--rule);
}
.nav-inner {
  display: grid;
  /* 280px matches the left rail so the logo sits over the rail column. The
     second track holds the links; the mobile toggle is display:none here. */
  grid-template-columns: 280px 1fr;
  column-gap: 24px;
  align-items: center;
  height: var(--nav-h);
  padding: 0 32px;
  max-width: 1440px;
  margin: 0 auto;
}
.logo { display: inline-flex; align-items: center; }
/* Height, not width, because the mark is set by its own aspect ratio — and that
   ratio changed completely with braid-logo-black-slim.svg: the old lockup was
   1185x299 (3.96:1), the slim one is 1020.57x93.62 (10.90:1). At the old 46px
   the slim mark renders 501px wide, which overlapped the nav links on desktop
   and pushed 112px of overflow at 390px. 18px puts it at 196px — within 14px of
   the width the previous lockup occupied, so the masthead keeps its proportions
   while the mark itself is a quieter single line. */
.logo img { height: 18px; width: auto; display: block; }
.nav-links { display: flex; gap: 28px; font-size: 18px; justify-self: start; }
.nav-link { color: var(--muted); }
.nav-link.active { color: var(--ink); }
/* Active-page marker: a small orange dot (pop accent) trails the current
   link, like a full-stop. The dot space is reserved on EVERY link so the
   nav doesn't shift as you switch pages — only the visible color changes. */
.nav-link::after {
  content: '';
  display: inline-block;
  width: 0.26em; height: 0.26em;
  border-radius: 50%;
  background: transparent;
  margin-left: 0.15em;
  vertical-align: baseline;
}
.nav-link.active::after { background: var(--pop); }

/* Mobile menu toggle — hidden on desktop, shown below the breakpoint. The bars
   span (+ its two pseudo-elements) draws a hamburger that morphs to an X when
   the nav is open. */
.nav-toggle {
  display: none;
  align-items: center; justify-content: center;
  width: 40px; height: 40px; margin-right: -8px;
  background: none; border: none; cursor: pointer;
  color: var(--ink);
  -webkit-tap-highlight-color: transparent;
}
/* 40px clears WCAG 2.5.8 but sits under the 44px comfort standard, and under a
   thumb this is the one control the whole mobile nav depends on. It has empty
   space on both sides, so it can grow to 44 for nothing: the bars are 20px and
   stay centred, and the negative margin tracks the new padding so they land on
   the same pixel as before. Only touch pays the extra 4px. */
@media (pointer: coarse) {
  .nav-toggle { width: 44px; height: 44px; margin-right: -10px; }
}
.nav-toggle-bars, .nav-toggle-bars::before, .nav-toggle-bars::after {
  display: block; width: 20px; height: 1.5px;
  background: currentColor;
  transition: transform 0.25s ease, background 0.25s ease;
}
.nav-toggle-bars { position: relative; }
.nav-toggle-bars::before, .nav-toggle-bars::after { content: ''; position: absolute; left: 0; }
.nav-toggle-bars::before { top: -6px; }
.nav-toggle-bars::after  { top: 6px; }
.nav.open .nav-toggle-bars { background: transparent; }
.nav.open .nav-toggle-bars::before { transform: translateY(6px) rotate(45deg); }
.nav.open .nav-toggle-bars::after  { transform: translateY(-6px) rotate(-45deg); }

/* ── PAGE GRID + LEFT RAIL (Home, AURA, About, Careers) ───────────────── */
.page {
  display: grid;
  grid-template-columns: 280px minmax(0, 1fr);
  max-width: 1440px;
  margin: 0 auto;
  border-left: 1px solid var(--rule);
  border-right: 1px solid var(--rule);
}
.rail {
  border-right: 1px solid var(--rule);
  padding: 32px;
  position: sticky; top: var(--nav-h);
  align-self: start;
  height: calc(100vh - var(--nav-h));
  /* Scroll rather than clip. The fixed height stays because .rail-spacer needs
     it to push .rail-foot to the bottom, but at 100vh the foot — and with it the
     contact address — was cut off on any viewport under ~620px tall. Scrollbar
     hidden for a clean edge, the pattern the article pages already used. */
  overflow-y: auto; scrollbar-width: none;
  display: flex; flex-direction: column;
}
.rail::-webkit-scrollbar { display: none; }
.rail-meta {
  font-family: 'Aeonik Fono', 'Aeonik Fono Fallback', 'SF Mono', Menlo, monospace;
  font-size: 10px; letter-spacing: 0.14em;
  color: var(--subtle); text-transform: uppercase;
  display: grid; gap: 6px;
  margin-bottom: 32px;
}
.rail-meta .row { display: flex; justify-content: space-between; }
.rail-meta .row b { color: var(--ink); font-weight: 500; }
/* The rail's one stamped value, marked in the markup rather than by position so
   it is obvious which row carries it. It sits on the Document name — the row
   that says what you are reading — having previously been on the Edition value,
   where it spent the rail's only accent on the least consequential string in it.
   Scoped to .rail-meta; about.css has its own .stamp inside .tokyo-seal. */
.rail-meta .row b.stamp { background: linear-gradient(transparent 60%, var(--magic) 60%); }

.rail-toc {
  border-top: 1px solid var(--rule);
  border-bottom: 1px solid var(--rule);
  padding: 16px 0;
  margin-bottom: 32px;
}
.rail-toc a {
  display: grid; grid-template-columns: 32px 1fr auto;
  /* Without a column gap the longest labels touched their page reference —
     measured 4px between "How engineers work with it" and "p. 04" on AURA. */
  column-gap: 12px;
  align-items: baseline;
  padding: 8px 0;
  font-size: 13px;
  color: var(--muted);
}
/* Hover is an enhancement, not a state. On touch, tapping a link applies
   :hover and leaves it applied until the next tap elsewhere: measured on a
   390px touch context, .band-row stayed padded 16px, .entry 12px, .deeper's
   rule 2px and .social's underline ink — so a row you merely visited kept
   reading as selected. Guarding on (hover: hover) means pointers keep every
   effect and touch gets none of the residue. Each rule is wrapped where it
   stands rather than collected into one block, so cascade order is
   untouched. */
@media (hover: hover) {
  .rail-toc a:hover { color: var(--ink); }
}
.rail-toc a .num { font-family: 'Aeonik Fono', 'Aeonik Fono Fallback', 'SF Mono', Menlo, monospace; font-size: 10px; letter-spacing: 0.14em; color: var(--subtle); }
.rail-toc a .pages { font-family: 'Aeonik Fono', 'Aeonik Fono Fallback', 'SF Mono', Menlo, monospace; font-size: 10px; color: var(--subtle); }

/* Current-section marker, set by site.js as aria-current="location". Styled off
   the attribute rather than a class so the state is legible in the DOM and to
   assistive tech, not just visually.

   The mark is the site's own highlighter — the same
   linear-gradient(transparent 60%, --magic 60%) that .braid-emph and .key-line
   use in body copy, and that .rail-meta b.stamp uses a few rows above. A
   marked row reads as a line struck through with a marker pen, which is the
   gesture the whole rail is built on, and it stays inside the accent's
   documented uses rather than becoming a filled surface.

   One mark per row, deliberately: an earlier version paired this with a 4px
   accent bar in the rail's left padding and the two competed. justify-self
   shrink-wraps the label to its text so the highlight hugs the words instead of
   painting the empty half of the 1fr track. */
.rail-toc a[aria-current="location"] { color: var(--ink); }
.rail-toc a[aria-current="location"] .num { color: var(--muted); }
.rail-toc a[aria-current="location"] span:not(.num):not(.pages) {
  justify-self: start;
  background-image: linear-gradient(transparent 60%, var(--magic) 60%);
  /* Sized and pinned to a single line rather than left to fill the box. The
     label is a grid item, so it is blockified: on the rails whose labels wrap
     (AURA §02, §03; every article heading) an unsized gradient puts its yellow
     40% across the *box*, which highlights the last line, misses the first, and
     trails a slab of colour past a short final word. One line-height, no
     repeat, top-aligned: the swipe lands under the first line, and since the
     shrink-wrapped box is as wide as the longest line, it matches that line's
     width instead of overhanging. */
  line-height: 1.35;
  background-size: 100% 1.35em;
  background-repeat: no-repeat;
  background-position: 0 0;
  padding: 0 3px; margin-left: -3px;
}

.rail-spacer { flex: 1; }
.rail-foot {
  font-family: 'Aeonik Fono', 'Aeonik Fono Fallback', 'SF Mono', Menlo, monospace;
  font-size: 10px; letter-spacing: 0.12em; color: var(--subtle); text-transform: uppercase;
}
.rail-foot p + p { margin-top: 6px; }

/* ── SECTION + SECTION-HEADER PATTERN ─────────────────────────────────── */
.content { display: block; }
.section {
  border-bottom: 1px solid var(--rule);
  padding: 56px 56px 72px;
  position: relative;
}
.sec-head {
  display: grid; grid-template-columns: 1fr auto;
  align-items: baseline;
  border-bottom: 1px solid var(--rule);
  padding-bottom: 12px;
  margin-bottom: 40px;
  font-family: 'Aeonik Fono', 'Aeonik Fono Fallback', 'SF Mono', Menlo, monospace;
  font-size: 11px; letter-spacing: 0.12em; color: var(--muted);
  text-transform: uppercase;
}
.sec-head .ja { color: var(--subtle); }

/* ── FOOTER ───────────────────────────────────────────────────────────── */
.footer {
  border-top: 1px solid var(--rule);
  padding: 24px 32px;
  font-family: 'Aeonik Fono', 'Aeonik Fono Fallback', 'SF Mono', Menlo, monospace;
  font-size: 11px; letter-spacing: 0.12em; color: var(--subtle); text-transform: uppercase;
  max-width: 1440px;
  margin: 0 auto;
  display: flex; justify-content: space-between;
}

/* ── SHARED RESPONSIVE (rail layout collapses at 960px) ───────────────── */
@media (max-width: 960px) {
  /* 80px is 9.5% of a 390x844 viewport — too much to give a permanently sticky
     bar on the screen that can least afford it. Everything derived from --nav-h
     follows automatically. */
  :root { --nav-h: 64px; }
  /* 14px -> 153px wide, leaving ~155px to the hamburger. */
  .logo img { height: 14px; }
  /* The hamburger opens a full-screen overlay menu with large, bold links —
     the bar (logo + X) stays on top; background scroll is locked by JS. */
  .nav-toggle { display: inline-flex; }
  /* The nav's backdrop-filter would otherwise make it the containing block for
     the fixed overlay (collapsing it). Drop the blur to a solid bar on mobile. */
  .nav { background: var(--bg); backdrop-filter: none; }
  /* .nav-links goes position:fixed below, so it leaves the grid flow: the two
     tracks here are the logo and the hamburger. */
  .nav-inner { grid-template-columns: 1fr auto; padding: 0 24px; }
  .nav-links {
    position: fixed;
    top: var(--nav-h); left: 0; bottom: 0;
    width: 100vw;                        /* explicit — an auto-width fixed flex
                                            item shrink-to-fits instead of filling */
    z-index: 40;                         /* under the bar (50), over the page */
    flex-direction: column;
    justify-content: flex-start;
    gap: 0;
    margin: 0;
    padding: 24px 24px 48px;
    background: var(--bg);
    overflow-y: auto;
    /* fade + slight rise */
    opacity: 0; visibility: hidden;
    transform: translateY(-6px);
    transition: opacity 0.28s ease, transform 0.28s ease, visibility 0s linear 0.28s;
    pointer-events: none;
  }
  .nav.open .nav-links {
    opacity: 1; visibility: visible;
    transform: none;
    pointer-events: auto;
    transition: opacity 0.28s ease, transform 0.28s ease, visibility 0s;
  }
  .nav-links a {
    font-size: clamp(34px, 9vw, 52px);
    font-weight: 700;
    letter-spacing: -0.030em;
    line-height: 1.0;
    color: var(--ink);
    padding: 22px 0;
    border-bottom: 1px solid var(--rule);
  }
  .nav-links a:first-child { border-top: 1px solid var(--rule); }
  .nav-links a.active { color: var(--pop); }

  /* On mobile the active text itself is orange — no dot, no reserved space. */
  .nav-link::after { display: none; }
  /* minmax(0, 1fr), not 1fr — the same guard the desktop rule above already
     carries. A bare 1fr keeps grid's default min-width:auto, so the column
     refuses to shrink below its widest child and pushes the whole page wider
     than the viewport instead. Any content with an intrinsic minimum (a wide
     figure, a long unbroken string, a min-width) blows out the mobile layout
     under 1fr. Found via /aura's Fig. 02 scroller: 601px of page overflow. */
  .page { grid-template-columns: minmax(0, 1fr); }
  /* The left rail (document metadata + in-page TOC) is hidden on mobile —
     it's redundant with the content and confusing at this size. */
  .rail { display: none; }
  .section { padding: 40px 24px 56px; }
}

/* ── REDUCED MOTION ───────────────────────────────────────────────────────
   Every state change is kept; only the travel is removed. That matters here
   because the site's motion is all hover feedback and one panel entrance —
   nothing conveys state through the animation itself, so collapsing durations
   costs no information. The real trigger is the smooth scroll: a rail TOC jump
   animates up to 4,400px, and the cross-page anchors (/#sec-2 from Careers) do
   the same. The overlay menu keeps a short cross-fade, because that is the one
   place motion tells the visitor a new surface arrived, but loses its rise. */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    transition-duration: 0.01ms !important;
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
  }
  .nav-links, .nav.open .nav-links {
    transform: none !important;
    transition-duration: 120ms !important;
  }
}
