/* shared.css — Path of Sitting. Variables, reset, body, nav, animations.
   Linked by every page. */

/* ── Color tokens, typed via @property so data-theme changes
   interpolate — every var(--x) user animates with no per-element
   transition. ─────────────────────────────────────────────────── */
@property --bg      { syntax: '<color>'; inherits: true; initial-value: #0e0d0c; }
@property --bg2     { syntax: '<color>'; inherits: true; initial-value: #161513; }
@property --bg3     { syntax: '<color>'; inherits: true; initial-value: #1e1c1a; }
@property --border  { syntax: '<color>'; inherits: true; initial-value: rgba(255,255,255,0.07); }
@property --outline { syntax: '<color>'; inherits: true; initial-value: rgba(255,255,255,0.25); }
@property --text    { syntax: '<color>'; inherits: true; initial-value: #e8e4dc; }
@property --muted   { syntax: '<color>'; inherits: true; initial-value: #9a9690; }
@property --accent  { syntax: '<color>'; inherits: true; initial-value: #c9a96e; }
@property --accent2 { syntax: '<color>'; inherits: true; initial-value: #9a7d52; }
@property --accent-display { syntax: '<color>'; inherits: true; initial-value: #c9a96e; }
@property --accent-mark    { syntax: '<color>'; inherits: true; initial-value: #c9a96e; }
@property --accent-label   { syntax: '<color>'; inherits: true; initial-value: #c9a96e; }

/* ── Tokens ─────────────────────────────────────────────────── */
:root {
  --bg:       #0e0d0c;
  --bg2:      #161513;
  --bg3:      #1e1c1a;
  --border:   rgba(255,255,255,0.07);
  /* Edge of a quiet (uncoloured) pill. --border is a hairline that
     all but vanishes as a button outline; 0.25 matches the gold pill's
     --accent-tint-strong in weight, minus the colour. */
  --outline:  rgba(255,255,255,0.25);
  --text:     #e8e4dc;
  --muted:    #9a9690;
  --accent:   #c9a96e;
  /* Quieter gold. Set by contrast, not by eye: --accent2 carries real
     text (.module-context, the Record's <cite>, .path-questions-label),
     so it must clear 4.5:1 on --bg alone. The earlier #8b6f47 (4.13:1)
     and light #b08a3e (2.87:1) both failed. */
  --accent2:  #9a7d52;
  /* Golds for light mode's paper, where --accent reads brown; dark
     keeps --accent. Each is as bright as its use allows on --bg:
     display type 24px up (3:1), marks that aren't text (3:1), small
     labels (4.5:1). On --bg2/--bg3 none of them holds. */
  --accent-display: #c9a96e;
  --accent-mark:    #c9a96e;
  --accent-label:   #c9a96e;
  /* Translucent accent tints for hover borders, themed so light
     mode doesn't inherit the dark gold. */
  --accent-tint:        rgba(201,169,110,0.28);
  --accent-tint-strong: rgba(201,169,110,0.4);
  /* The thread of the home page's path and the tldr's timeline. */
  --thread: color-mix(in srgb, var(--accent) 32%, transparent);
  /* Type roles: --display (Newsreader) is the title face, at title
     sizes only (about 20px up; smaller, its strokes break up on a
     phone), --read (Spectral) is italic accents only (never body
     copy), --sans (DM Sans) is body + UI. */
  --display:  'Newsreader', Georgia, serif;
  --read:     'Spectral', Georgia, serif;
  --sans:     'DM Sans', system-ui, sans-serif;
  /* Bright gold for emphasized display italics + verse numbers, where
     base --accent reads slightly flat. Body <em> keeps --accent. */
  --accent-bright: #dcc290;
  --ease:     0.4s cubic-bezier(0.4,0,0.2,1);
  --pad:      clamp(24px, 5vw, 80px);
  /* Room the phone tab bar takes at the foot of the page (nav.js sets
     it on <html>); body pads by it so nothing hides behind the bar. */
  --bar-h: 0px;
  /* Paragraph text, on every page: one line length (about 70
     characters on a computer) and one size. CLAUDE.md has the rule. */
  --measure:   572px;
  --body-size: clamp(15px, 1.4vw, 17px);
  /* Resting dim for text. 0.84 is the floor where --muted still clears
     4.5:1 on --bg in both themes (dark 4.95, light 4.53) — lower fails
     AA. --accent text can carry no dim: light --accent is only 4.93:1
     to start, so it stays full strength and dimming uses --accent2
     instead. Opacity multiplies down the tree, so a dimmed row can't
     hold a dimmed child — see .thinker-card / .search-result. */
  --dim-text: 0.84;
}

/* ── Light theme — warm paper. Accent golds darken for contrast
   against the light ground; the rest is the same palette inverted
   in warmth, not flipped to clinical white. ──────────────────── */
[data-theme="light"] {
  --bg:       #f5f2ec;
  --bg2:      #ede9e0;
  --bg3:      #e5e0d4;
  --border:   rgba(0,0,0,0.09);
  --outline:  rgba(0,0,0,0.25);
  --text:     #2b2823;
  --muted:    #5a554c;
  --accent:   #8f5f13;
  /* The most chroma a gold can carry at 4.5:1 on the paper (4.60):
     lighter loses contrast, and less chroma reads as brown. */
  --accent2:  #926503;
  --accent-display: #b07a10;
  --accent-mark:    #b5801a;
  --accent-label:   #926503;
  --accent-tint:        rgba(143,95,19,0.28);
  --accent-tint-strong: rgba(143,95,19,0.4);
  --accent-bright:      #8f5f13; /* light: same as --accent */
}


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

html {
  scroll-behavior: smooth;
  /* No gray flash behind a tapped control on iOS/Android; each
     control answers a tap with its own state change. Inherited. */
  -webkit-tap-highlight-color: transparent;
  /* Reserve the scrollbar gutter even while breath.js locks scroll
     (overflow: hidden) for the entrance overlay — otherwise the
     scrollbar vanishing/reappearing shifts the whole page, overlay
     text included, sideways by its width. */
  scrollbar-gutter: stable;
  /* Backstop for Safari compositor edge cases and the source of truth
     for iOS overscroll/status-bar color. Must use var(--bg), not a
     hardcoded value — otherwise switching to light mode shows a black
     bar above the nav. */
  background: var(--bg);
  /* Animates the @property tokens above on a data-theme flip; every
     var(--x) user picks up the interpolation with no transition of
     its own. */
  transition: --bg 0.6s ease, --bg2 0.6s ease, --bg3 0.6s ease,
              --border 0.6s ease, --text 0.6s ease, --muted 0.6s ease,
              --accent 0.6s ease, --accent2 0.6s ease,
              --accent-display 0.6s ease, --accent-mark 0.6s ease,
              --accent-label 0.6s ease;
}

/* Only the @property tokens above should animate during a theme flip.
   Elements with their own `transition: color` (nav links, logo, footer)
   read var(--token) while it's mid-transition — Chrome treats that as a
   moving target, so colour stalls then lurches (the gold logo flashing
   dark orange, muted links dipping to flat gray). theme.js flags <html>
   for the 0.6s flip; this rule kills every other element's transition
   during that window so colour follows the single token interpolation.
   Hover transitions work normally outside the flip. */
html.theme-animating *:not(html) {
  transition: none !important;
}

/* ── Base ────────────────────────────────────────────────────── */
body {
  background: var(--bg);
  color: var(--text);
  font-family: var(--sans);
  line-height: 1.7;
  min-height: 100vh;
  overflow-x: hidden;
}

/* Grain overlay */
body::before {
  content: '';
  position: fixed;
  inset: 0;
  background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 256 256' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
  opacity: 0.022;
  pointer-events: none;
  z-index: 1000;
}

.band-content a {
  color: var(--accent-label);
}

.band-content a:visited {
  color: var(--accent-label);
}

/* ── Visually hidden — accessible but not visible ───────────── */
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* A hidden container with a FOCUSABLE child must reveal on focus, or a
   keyboard user tabs into invisible stops. Needed for the empirical
   canvas: it's aria-hidden and its module list (js/convergence.js) is a
   .visually-hidden <ul> of real links. Same reveal the skip link uses;
   costs sighted users nothing since the panel only shows while focus
   is inside it. */
.visually-hidden:focus-within {
  position: fixed;
  top: calc(16px + env(safe-area-inset-top));
  left: 50%;
  transform: translateX(-50%);
  width: min(90vw, 420px);
  height: auto;
  margin: 0;
  padding: 20px 24px;
  overflow: visible;
  clip: auto;
  white-space: normal;
  list-style: none;
  background: var(--bg);
  border: 1px solid var(--accent);
  z-index: 9999;
  font-family: var(--sans);
  font-size: 14px;
  line-height: 2;
  color: var(--muted);
}
.visually-hidden:focus-within a { color: var(--text); }

/* ── Vertical space — <div class="space"></div> wherever a paragraph
   break needs room; stack two for longer. Sized larger than the normal
   paragraph gap since inserting the div breaks the `p + p` margin rule,
   so it must carry the whole gap itself plus the pause. ──────────── */
.space {
  height: 3rem;
}

/* ── Skip to content ─────────────────────────────────────────── */
.skip-to-content {
  position: absolute;
  top: -100%;
  left: 50%;
  transform: translateX(-50%);
  background: var(--bg);
  color: var(--accent);
  border: 1px solid var(--accent);
  padding: 10px 20px;
  font-family: var(--sans);
  font-size: 12px;
  font-weight: 500;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  text-decoration: none;
  z-index: 9999;
  transition: top 0.2s;
}
.skip-to-content:focus {
  top: 16px;
}

/* ── Navigation ──────────────────────────────────────────────── */
nav {
  width: 100%;
  background: var(--bg);
}

.nav-inner {
  max-width: var(--max-w, 1280px);
  margin: 0 auto;
  padding: calc(24px + env(safe-area-inset-top)) var(--pad, clamp(24px, 5vw, 80px)) 24px;
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
}


/* The mark links home on a computer; a phone has no site name up top.
   The negative margin mirrors .nav-right's, keeping it on the edge. */
.nav-logo {
  display: flex;
  align-items: center;
  grid-column: 1;
  justify-self: start;
  width: fit-content;
  min-height: 44px;
  margin: -7px 0 -7px -0.5rem;
  padding: 0 0.5rem;
  color: var(--accent-mark);
  text-decoration: none;
  transition: color var(--ease);
}
.nav-logo svg { display: block; }
.nav-logo:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
@media (hover: hover) {
  .nav-logo:hover { color: var(--accent-label); }
}

/* Nav right group */
.nav-right {
  display: flex;
  align-items: center;
  gap: 25px;
  justify-self: end;
  grid-column: 3;
  margin-right: -0.5rem;
}
.nav-tab:focus-visible,
.nav-search:focus-visible,
.nav-theme:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }



/* ── Footer shared ───────────────────────────────────────────── */
.footer-logo {
  color: var(--accent-mark);
  opacity: 0.5;
  text-decoration: none;
  transition: opacity var(--ease), color var(--ease);
  display: block;
  width: 36px;
  height: 36px;
  line-height: 0;
}
.footer-logo:hover { opacity: 1; color: var(--text); }

.footer-mark {
  color: inherit;
}

/* ── Back link — top of every spoke page. Lives here, not module.css,
   so pages that skip module.css (e.g. the empirical canvas) get it
   too. ─────────────────────────────────────────────────────────── */
.nav-back {
  display: block;
  padding-top: 40px;
  font-family: var(--sans);
  font-size: 11px;
  font-weight: 400;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--muted);
  text-decoration: none;
  transition: color var(--ease);
  white-space: nowrap;
}
.nav-back:hover { color: var(--accent-label); }
.nav-back::before { content: '← '; }

/* When a module label follows a back link, the label's default 60px
   top margin is too tall — the back link already provides the
   breathing room. A bare title after a back link needs its own air. */
.nav-back + .module-label { margin-top: 32px; }
.nav-back + .book-title   { margin-top: 44px; }
.nav-back + .hero-title   { margin-top: 14px; }

/* ── Next navigation — bottom of every spoke page ────────────── */
.next-book {
  padding: 90px 0 0;
  display: flex;
  justify-content: flex-end;
}
/* Two links: the way back (left) and the way on (right). */
.next-book-split { justify-content: space-between; }

.next-link {
  text-align: right;
  text-decoration: none;
  transition: opacity 0.3s;
}
.next-link:hover { opacity: 0.7; }

.next-link-label {
  font-family: var(--sans);
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--muted);
  margin-bottom: 8px;
}

.next-link-title {
  font-family: var(--sans);
  font-size: clamp(20px, 2.2vw, 28px);
  font-weight: 400;
  color: var(--text);
}
.next-link-title::after { content: ' →'; color: var(--accent-label); }

/* Back variant — arrow leads, text sits left. */
.next-link-back { text-align: left; }
.next-link-back .next-link-title::after { content: none; }
.next-link-back .next-link-title::before { content: '← '; color: var(--accent-label); }

/* ── Book hero ───────────────────────────────────────────────── */
.book-hero {
  display: flex;
  flex-direction: column;
  gap: 0;
  padding: 0 var(--pad, clamp(24px, 5vw, 80px)) 100px;
  border-bottom: 1px solid var(--border);
}

.module-label {
  font-family: var(--sans);
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--accent-label);
  margin-bottom: 8px;
  margin-top: 60px;
}

.module-context {
  font-family: var(--sans);
  font-size: 11px;
  font-weight: 400;
  letter-spacing: 0.15em;
  text-transform: uppercase;
  color: var(--accent2);
  margin-bottom: 32px;
}

/* A hero that opens straight on its title (no label or back link above
   it) takes the label's 60px itself, so the title never sits on the nav. */
.book-hero > .book-title:first-child { margin-top: 60px; }

.book-title {
  font-family: var(--display);
  font-size: clamp(64px, 9vw, 125px);
  font-weight: 300;
  line-height: 1.0;
  letter-spacing: -0.02em;
  color: var(--text);
  margin-bottom: 30px;
}
/* An emphasized word inside a display title rides a touch heavier than
   the light roman around it — the redesign's roman+italic title mix. */
.book-title i,
.book-title em { font-style: italic; font-weight: 400; }

/* An intro is in the larger type, so it stops short of --measure's
   width at the same line length: a ragged edge, not a gap. */
.book-intro {
  max-width: 36em;
  font-family: var(--sans);
  font-size: clamp(16px, 1.5vw, 19px);
  font-weight: 400;
  line-height: 1.9;
  color: var(--text);
}
.book-intro + .book-intro { margin-top: 1.5em; }
.book-intro em { color: var(--accent-bright); font-style: italic; }
.book-intro a { color: var(--accent-label); text-decoration: underline; text-underline-offset: 3px; }
.book-intro a:hover { color: var(--text); }

/* ── Animations ──────────────────────────────────────────────── */
/* The map's centre spine breathes slowly; the prefers-reduced-motion
   rule below stills it. */
@keyframes spine-breath {
  0%, 100% { opacity: 0.22; }
  50%      { opacity: 0.58; }
}

@media (prefers-reduced-motion: reduce) {
  * { animation: none !important; }
}

/* ── Between pages — a short crossfade, where the browser offers one
   (Safari 18.2+, Chrome); elsewhere a plain cut, as before. Short on
   purpose: a reading site is walked page to page, and a slow fade
   reads as waiting. Off with reduced motion, which the * rule above
   can't reach — the transition's layers are pseudo-elements. ───── */
@view-transition { navigation: auto; }
::view-transition-old(root),
::view-transition-new(root) {
  animation-duration: 0.25s;
}
@media (prefers-reduced-motion: reduce) {
  @view-transition { navigation: none; }
}

/* ── Table of contents ───────────────────────────────────────── */
.toc {
  padding: 64px 0 72px;
  border-bottom: 1px solid var(--border);
}

.toc-header {
  /* A real <button> for keyboard access — reset UA button styles */
  background: none;
  border: none;
  width: 100%;
  text-align: left;
  display: flex;
  align-items: center;
  gap: 14px;
  font-family: var(--sans);
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--accent-label);
  margin-bottom: 32px;
  cursor: pointer;
  user-select: none;
}

.toc-chevron {
  font-size: 20px;
  color: var(--accent);
  display: inline-block;
  transition: transform 0.3s ease;
  opacity: 0.7;
  line-height: 1;
}
.toc.collapsed .toc-chevron { transform: rotate(-90deg); }

.toc-items {
  display: flex;
  flex-direction: column;
  overflow: hidden;
  max-height: 1200px;
  opacity: 1;
  transition: max-height 0.4s ease, opacity 0.3s ease;
}
.toc.collapsed .toc-items { max-height: 0; opacity: 0; }

.toc-link {
  display: flex;
  align-items: baseline;
  gap: 20px;
  padding: 15px 0;
  border-bottom: 1px solid var(--border);
  text-decoration: none;
  transition: border-color 0.3s ease;
}
.toc-link:last-child { border-bottom: none; }
/* Rests undimmed: .toc-num is gold text, which can't sit in a dimmed
   row (--dim-text). Hover leans on the border and the arrow. */
@media (hover: hover) {
  .toc-link:hover { border-color: var(--accent-tint); }
}
.toc-link.is-active .toc-name { color: var(--accent-label); }

.toc-num {
  font-family: var(--sans);
  font-size: 10px;
  font-weight: 400;
  letter-spacing: 0.12em;
  color: var(--accent2);
  min-width: 22px;
  flex-shrink: 0;
  transition: color 0.3s;
}
@media (hover: hover) {
  .toc-link:hover .toc-num { color: var(--accent-label); }
}
.toc-link.is-active .toc-num { color: var(--accent-label); }

.toc-name {
  font-family: var(--sans);
  font-size: clamp(14px, 1.35vw, 16px);
  font-weight: 400;
  color: var(--text);
  transition: color 0.3s ease;
}

.toc-arrow {
  margin-left: auto;
  font-size: 11px;
  color: var(--accent);
  opacity: 0;
  transform: translateX(-6px);
  transition: opacity 0.25s ease, transform 0.25s ease;
  flex-shrink: 0;
}
@media (hover: hover) {
  .toc-link:hover .toc-arrow { opacity: 0.6; transform: translateX(0); }
}
.toc-link.is-active .toc-arrow { opacity: 0.6; transform: translateX(0); }

/* ── Sliding rows (js/schedule.js): a touch swipes, but a mouse may
   not scroll sideways, so it gets an arrow either side, in room of
   its own, shown while there's more that way. Kept in place when
   hidden, so the row doesn't shift ── */
.slide-arrow { display: none; }
@media (hover: hover) {
  .slide {
    display: flex;
    align-items: center;
    gap: 6px;
  }
  .slide > :first-child { flex: 1; min-width: 0; }
  .slide-arrow {
    display: block;
    flex: 0 0 18px;
    padding: 8px 0;
    border: none;
    background: none;
    color: var(--accent);
    font-family: var(--sans);
    font-size: 16px;
    cursor: pointer;
    opacity: 0;
    visibility: hidden;
    transition: opacity 0.25s ease, visibility 0.25s;
  }
  .slide-back { order: -1; }
  .slide-arrow.is-on { opacity: 1; visibility: visible; }
  .slide-arrow span { display: inline-block; transition: transform 0.25s ease; }
  .slide-back:hover span { transform: translateX(-2px); }
  .slide-on:hover span { transform: translateX(2px); }
}


/* ── Breathing overlay — first-visit entrance ────────────────── */
#breath-overlay {
  position: fixed;
  inset: 0;
  z-index: 9999;
  /* var(--bg), not a hardcoded dark — theme.js sets data-theme from
     system preference before first paint, so the overlay matches the
     page behind it. A fixed dark left black safe-area bars on iOS
     against a light page. */
  background: var(--bg);
  display: flex;
  align-items: center;
  justify-content: center;
  flex-direction: column;
  gap: 0;
  opacity: 1;
  /* Quick: by the time it lifts it is only the page colour, and the
     page itself is fading in beneath (html.breath-arrive, below). */
  transition: opacity 0.6s cubic-bezier(0.4,0,0.2,1);
  cursor: pointer;
  /* No will-change, transform, or backface tricks: they cause the
     brownish flash they'd be meant to fix. will-change:opacity
     pre-promotes the overlay to a compositor layer; when the text
     labels' opacity transitions then fire inside it, Safari
     re-rasterizes and briefly shows the stale compositor buffer,
     revealing the warm page behind. Without it there's no pre-created
     layer to go stale. The fade-out transition still GPU-composes
     fine — it promotes only when it actually runs. */
  isolation: isolate;
}
#breath-overlay.leaving {
  opacity: 0;
  pointer-events: none;
}
/* Page hidden until the overlay is up — breath.js sets this in <head>
   before first paint so the page can't flash in first. Body's own
   background (--bg) still paints, matching the overlay. */
html.breath-pending body > *:not(#breath-overlay) { visibility: hidden; }
/* After the breath, the page comes to meet you: it fades in, and the
   content rises a little, as the overlay lifts (breath.js adds
   html.breath-arrive). Outside the overlay, so its Safari rules don't
   apply here. */
html.breath-arrive body > *:not(#breath-overlay) {
  animation: breath-arrive 1.4s cubic-bezier(0.2, 0.6, 0.3, 1) both;
}
html.breath-arrive body > main {
  animation-name: breath-arrive-rise;
}
@keyframes breath-arrive      { from { opacity: 0; } }
@keyframes breath-arrive-rise { from { opacity: 0; transform: translateY(14px); } }
/* Ring lives inside a fixed-size wrapper so scaling never overlaps the text below */
.breath-ring-wrap {
  width: 220px;
  height: 220px;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
}
.breath-ring {
  width: 72px;
  height: 72px;
  /* Masked for the same reason as .path-breath-ring (path.css). Its
     own SVG, not ring.svg: that one's stroke would scale up to 2.5px. */
  background-color: var(--accent-mark);
  -webkit-mask: url('/img/breath-ring.svg') no-repeat center / contain;
  mask: url('/img/breath-ring.svg') no-repeat center / contain;
  animation: breath-cycle 9s ease-in-out infinite;
  /* No will-change — Safari already GPU-promotes @keyframes on
     transform/opacity; explicit will-change nests a compositor
     sub-layer inside the overlay, compounding the flash. */
}
/* Loops until the visitor taps: 100% must match 0% or each cycle jumps.
   breath.js times the words to these stops; change them together. */
@keyframes breath-cycle {
  0%   { transform: scale(0.85); opacity: 0.25; }
  8%   { opacity: 0.6; }
  42%  { transform: scale(2.4);  opacity: 0.75; }
  52%  { transform: scale(2.4);  opacity: 0.70; }
  92%  { transform: scale(0.85); opacity: 0.30; }
  100% { transform: scale(0.85); opacity: 0.25; }
}
/* Two-label crossfade wrapper — avoids a textContent DOM mutation
   (which triggers a Safari repaint and can cause the flash). */
.breath-text-wrap {
  position: relative;
  height: 18px;
  margin-top: 10px;
  display: flex;
  align-items: center;
  justify-content: center;
}
.breath-text {
  position: absolute;
  font-family: 'DM Sans', system-ui, sans-serif;
  font-size: 13px;
  font-weight: 400;
  letter-spacing: 0.32em;
  text-transform: uppercase;
  color: var(--accent-label);
  /* No CSS transition — breath.js drives the words' opacity via
     requestAnimationFrame so no compositor promotion fires inside
     the overlay (the cause of Safari's color flashes). */
  white-space: nowrap;
}
.breath-skip {
  margin-top: 36px;
  font-family: 'DM Sans', system-ui, sans-serif;
  font-size: 11px;
  font-weight: 400;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--muted);
  opacity: 0;
  animation: breath-skip-in 1s 5s forwards;
}
@keyframes breath-skip-in {
  to { opacity: var(--dim-text); }
}
@media (prefers-reduced-motion: reduce) {
  #breath-overlay { display: none !important; }
}

/* ── Section words and search ───────────────────────────────── */
.nav-tab,
.nav-search {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 7px;
  color: var(--muted);
  text-decoration: none;
  /* Even sides, so the focus ring sits around the label, not off to
     one side; .nav-right's negative margin keeps the last on the edge. */
  padding: 0 0.5rem;
  /* Apple's minimum touch target. The negative margin keeps the row
     at the logo's 30px; width can't grow, the phone row is already
     sized to fit 320px. */
  min-height: 44px;
  margin-block: -7px;
  transition: color var(--ease);
  flex-shrink: 0;
}

/* Study, Sit and Together have no universal symbol, so they stay words;
   only search and theme, whose symbols are universal, stay icons.
   Safari's translator lowercases a bare word as if mid-sentence; the
   capital is restored after translation. */
.nav-tab {
  font-family: var(--sans);
  font-size: 15px;
  font-weight: 400;
  letter-spacing: 0.02em;
  white-space: nowrap;
}
.nav-tab::first-letter { text-transform: uppercase; }
@media (hover: hover) {
  .nav-tab:hover,
  .nav-search:hover { color: var(--accent-label); }
}

/* The search trigger is a <button>, not a link — reset UA styles. */
.nav-search {
  background: none;
  border: none;
  cursor: pointer;
  font: inherit;
}

/* ── Search overlay — injected by nav.js on every page ───────── */
.search-overlay {
  position: fixed;
  inset: 0;
  z-index: 9500;
  background: var(--bg);
  display: none;
  overflow-y: auto;
  padding: calc(72px + env(safe-area-inset-top)) var(--pad, clamp(24px, 5vw, 80px)) 80px;
}
.search-overlay.is-open { display: block; }

.search-inner {
  max-width: 640px;
  margin: 0 auto;
}

.search-label {
  font-family: var(--sans);
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--accent-label);
  margin-bottom: 24px;
}

.search-input {
  display: block;
  width: 100%;
  background: none;
  border: none;
  border-bottom: 1px solid var(--border);
  border-radius: 0;
  padding: 10px 0 14px;
  font-family: var(--display);
  font-size: clamp(26px, 4vw, 38px);
  font-weight: 300;
  color: var(--text);
  outline: none;
  transition: border-color 0.3s;
  -webkit-appearance: none;
  appearance: none;
}
.search-input:focus { border-bottom-color: var(--accent-tint-strong); }
.search-input::placeholder { color: var(--muted); opacity: 0.5; }

.search-hint {
  font-family: var(--sans);
  font-size: 12px;
  font-weight: 400;
  letter-spacing: 0.04em;
  color: var(--muted);
  opacity: var(--dim-text);
  margin-top: 18px;
  min-height: 1.5em;
}

.search-results {
  margin-top: 28px;
  display: flex;
  flex-direction: column;
}

/* Result rows speak the same language as the path spokes:
   title + section on top, the page's one-line hook underneath. */
.search-result {
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding: 16px 0;
  border-bottom: 1px solid var(--border);
  text-decoration: none;
  transition: border-color 0.25s ease;
}
/* No resting dim: .search-result-where is gold text, and a dimmed row
   is a ceiling its child can't climb back out of (opacity multiplies)
   — at 0.85 the label would land at 3.72:1 in light. Hover still
   speaks via border-color and the sliding arrow. Same reason
   .thinker-card rests undimmed. */

.search-result-top {
  display: flex;
  align-items: baseline;
  gap: 18px;
}
.search-result:last-child { border-bottom: none; }
@media (hover: hover) {
  .search-result:hover { opacity: 1; border-color: var(--accent-tint); }
  .search-result:hover .search-result-arrow { opacity: 0.9; transform: translateX(3px); }
}
.search-result.is-active { opacity: 1; border-color: var(--accent-tint); }
.search-result.is-active .search-result-arrow { opacity: 0.9; transform: translateX(3px); }

.search-result-name {
  font-family: var(--sans);
  font-size: clamp(15px, 1.4vw, 17px);
  font-weight: 400;
  color: var(--text);
}

.search-result-where {
  font-family: var(--sans);
  font-size: 10px;
  font-weight: 500;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--accent-label);
  flex-shrink: 0;
}

.search-result-arrow {
  margin-left: auto;
  font-size: 12px;
  color: var(--accent);
  opacity: 0.35;
  flex-shrink: 0;
  display: inline-block;
  transition: opacity 0.25s ease, transform 0.25s ease;
}

.search-result-desc {
  font-family: var(--sans);
  font-size: 13px;
  font-weight: 400;
  line-height: 1.7;
  color: var(--muted);
}

/* Topics line — shown only when it carried the match, so the reader
   sees why "the crack" found The breath. Set apart from the
   description by space, size and italic, not opacity (--muted is
   already at its contrast floor). */
.search-result-topics {
  margin-top: 6px;
  font-family: var(--sans);
  font-size: 12px;
  font-style: italic;
  font-weight: 400;
  letter-spacing: 0.02em;
  line-height: 1.6;
  color: var(--muted);
}

/* Matched query words — quiet accent, not the UA's yellow. */
.search-results mark {
  background: none;
  color: var(--accent-label);
}

.search-close {
  position: absolute;
  top: calc(28px + env(safe-area-inset-top));
  right: var(--pad, clamp(24px, 5vw, 80px));
  background: none;
  border: none;
  cursor: pointer;
  color: var(--muted);
  font-family: var(--sans);
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  padding: 8px;
  transition: color var(--ease);
}
@media (hover: hover) {
  .search-close:hover { color: var(--accent-label); }
}

/* ── Theme toggle ────────────────────────────────────────────── */
.nav-theme {
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--muted);
  background: none;
  border: none;
  cursor: pointer;
  padding: 0 0.5rem; /* even sides, as .nav-tab */
  min-height: 44px; /* touch target, as .nav-tab */
  margin-block: -7px;
  transition: color var(--ease);
  flex-shrink: 0;
  line-height: 0;
}
@media (hover: hover) {
  .nav-theme:hover { color: var(--accent-mark); }
}

/* ── Sections — the top row carries the mark and Study, Sit and
   Together as words (nav.js), the page you're in in gold. The
   installed app (html[data-standalone], theme.js) has the three as a
   tab bar along the bottom of a phone instead; its top row is the mark,
   search and the toggle, and on Together the map runs to the very top.
   The map has its own search, so the site's is hidden there. ── */
.nav-tab[aria-current="page"],
html[data-tab="study"] .nav-tab[href="/study"],
html[data-tab="sit"] .nav-tab[href="/sit"],
html[data-tab="together"] .nav-tab[href="/together"] { color: var(--accent-label); }
html[data-tab="together"] .nav-search { display: none; }
.tab-bar { display: none; }

@media (max-width: 760px) {
  /* The bar is in the HTML, so its room is reserved from first paint;
     a locked visitor's page keeps it hidden until nav.js finds two
     places open to them (has-tabbar). */
  :root[data-standalone]:not([data-locked]), :root[data-standalone].has-tabbar { --bar-h: calc(56px + env(safe-area-inset-bottom)); }
  :root[data-standalone] body { padding-bottom: var(--bar-h); }

  /* One flex row: mark, the three words, search and theme, spread
     evenly across the full width. .nav-right dissolves so its children
     join the row. */
  .nav-inner {
    display: flex;
    justify-content: space-between;
    padding: calc(8px + env(safe-area-inset-top)) var(--pad) 8px;
  }
  .nav-right { display: contents; }
  .nav-logo { margin-block: 0; }
  .nav-tab { margin-block: 0; padding: 0 8px; }
  .nav-search, .nav-theme { margin-block: 0; padding: 0; width: 44px; }
  .nav-theme { margin-right: -13px; }

  :root[data-standalone] .nav-tab { display: none; }
  :root[data-standalone][data-tab="together"] nav:not(.tab-bar) { display: none; }

  :root[data-standalone] .tab-bar {
    display: flex;
    position: fixed;
    left: 0; right: 0; bottom: 0;
    z-index: 950;
    height: var(--bar-h);
    padding-bottom: env(safe-area-inset-bottom);
    background: var(--bg);
    border-top: 1px solid var(--border);
    /* Pre-paints the strip under the bar, where iOS Safari's translucent
       toolbar would show page text. Offset equal to spread keeps the
       shadow's top edge at the bar's own and pushes the rest down. */
    box-shadow: 0 200px 0 200px var(--bg);
  }
  :root[data-standalone][data-locked]:not(.has-tabbar) .tab-bar { display: none; }
  .tab-bar a {
    flex: 1;
    display: flex;
    align-items: center;
    justify-content: center;
    min-height: 44px;
    font: 400 15px/20px var(--sans);
    letter-spacing: 0.02em;
    color: var(--muted);
    text-decoration: none;
    transition: color var(--ease);
  }
  /* data-tab (theme.js) lights the right one at first paint. */
  .tab-bar a[aria-current="page"],
  html[data-tab="study"] .tab-bar a[href="/study"],
  html[data-tab="sit"] .tab-bar a[href="/sit"],
  html[data-tab="together"] .tab-bar a[href="/together"] { color: var(--accent-label); }
  .tab-bar a:focus-visible { outline: 2px solid var(--accent); outline-offset: -4px; }
}
@media (max-width: 760px) and (hover: hover) {
  .tab-bar a:hover { color: var(--accent-label); }
}
@media (prefers-reduced-motion: reduce) {
  .tab-bar a { transition: none; }
}

.tldr-coda {
  position: relative;
  margin-top: 1.5rem;
  padding-top: 2.5rem;
  text-align: center;
  font-family: var(--sans);
}

/* ── Footer — shared across all pages ───────────────────────── */
footer {
  border-top: 1px solid var(--border);
  padding: 40px var(--pad);
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
}

/* The date and the links are one type, set in this one rule. On a
   phone it sits a step below the reading text; desktop takes the
   nav's 15px. */
.footer-updated,
.footer-links a {
  font-family: var(--sans);
  font-size: 13px;
  font-weight: 400;
  letter-spacing: 0.02em;
  color: var(--muted);
  opacity: var(--dim-text);
}

/* Ordered smallest ask to largest: pass it on (Share, js/nav.js, on
   published pages), write, leave an email, give.
   On a phone the links come first and the date closes the page,
   the wider gap above it keeping the two apart. */
.footer-links {
  display: flex;
  gap: 28px;
  margin-top: 18px;
  order: 1;
}
.footer-updated {
  margin-top: 30px;
  order: 2;
}
/* 44px touch target; the margins above give back the extra 10px a
   side, so the spacing reads as before. Block, not flex: ::first-letter
   only reaches a block box. */
.footer-links a {
  display: block;
  line-height: 44px;
  text-decoration: none;
  transition: opacity 0.3s;
}
/* Safari's translator reads the links as one phrase and lowercases
   all but the first; the capital is restored after translation. */
.footer-links a::first-letter { text-transform: uppercase; }
/* Hover goes to full opacity — resting is already --dim-text (0.84),
   so anything less wouldn't read as a change. Hover-gated: a phone
   holds :hover on the link last tapped, and Share stays on the page. */
@media (hover: hover) {
  .footer-links a:hover { opacity: 1; }
}

/* Desktop: one row on the nav's column edges — mark and date left,
   links right. */
@media (min-width: 769px) {
  footer {
    flex-direction: row;
    gap: 28px;
    padding-inline: max(var(--pad),
      calc((100% - var(--max-w, 1280px)) / 2 + var(--pad)));
  }
  .footer-updated,
  .footer-links a { font-size: 15px; }
  .footer-updated {
    margin-top: 0;
    order: 0;
  }
  .footer-links {
    gap: 34px;
    margin: 0 0 0 auto;
  }
}

/* ── Support — donation buttons: three monthly tiers plus a quiet
   one-time pill, same pill language as sit/timer buttons. ────── */
.support-give {
  margin-top: 2.2em;
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  align-items: center;
  gap: 16px 22px;
}
a.support-btn {
  display: inline-block;
  border: 1px solid var(--outline);
  border-radius: 50px;
  padding: 10px 26px;
  font-family: var(--sans);
  font-size: 12px;
  font-weight: 400;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--muted);
  text-decoration: none;
  transition: border-color 0.3s, color 0.3s;
}
a.support-btn:hover {
  color: var(--text);
  border-color: var(--accent-mark);
}
a.support-btn-primary {
  color: var(--accent-label);
  border-color: var(--accent-tint-strong);
}
a.support-btn-primary:hover {
  border-color: var(--accent-mark);
}
.support-group {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 12px;
}
.support-group-label {
  font-family: var(--sans);
  font-size: 11px;
  font-weight: 400;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--muted);
  opacity: var(--dim-text);
}
.support-group-btns {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  align-items: center;
  gap: 10px;
}
/* The four monthly pills hold one line down to a 320px phone. */
@media (max-width: 400px) {
  .support-group-btns { gap: 8px; }
  .support-group-btns a.support-btn-primary { padding: 10px 18px; }
}

/* ── Print — clean text on paper, no chrome ──────────────────── */
@media print {
  :root, [data-theme="light"], [data-theme="dark"] {
    --bg:      #ffffff;
    --bg2:     #ffffff;
    --bg3:     #ffffff;
    --border:  #cccccc;
    --text:    #000000;
    --muted:   #444444;
    --accent:  #000000;
    --accent2: #333333;
  }

  /* body's overflow-x: hidden makes it a scroller, and Safari prints
     only what fits in a scroller, cutting off the rest of the page. */
  html, body { background: #ffffff; overflow: visible; }

  /* Grain overlay and vertical border lines */
  body::before,
  body::after { display: none !important; }

  nav,
  footer,
  .skip-to-content,
  #breath-overlay,
  .toc,
  .next-book,
  .nav-back,
  .philosopher-back,
  .timer-wrap { display: none !important; }

  a { color: #000000; text-decoration: none; }

  /* "body" outranks the page stylesheets, which load after this one. */
  @page { margin: 0.7in 0.75in; }
  body .site-wrap { border: none; }
  .next-path { display: none !important; }

  /* Sections flow on, label above text: a section kept whole jumps to
     the next page when it doesn't fit, and leaves the rest blank. */
  body .band, body .band-grid {
    display: block;
    padding: 22pt 0 6pt;
    border: none;
  }
  body .band-label, body .band-grid .band-label {
    position: static;
    display: block;
    padding-top: 0;
    margin-bottom: 8pt;
    break-after: avoid;
  }
  body .band-grid .band-content p + p { margin-top: 0.7em; }
  p { orphans: 3; widows: 3; }
  .koan, .pull-quote, .postures-img, .tldr-steps { break-inside: avoid; }

  /* The postures and the tldr's thread are painted as backgrounds,
     which browsers leave off paper unless told otherwise. */
  .postures-img, .tldr-steps li {
    -webkit-print-color-adjust: exact;
    print-color-adjust: exact;
  }
}
