@import url('https://fonts.googleapis.com/css2?family=Fraunces:opsz,wght@9..144,400;9..144,500;9..144,600&family=Inter:wght@400;500;600&display=swap');

/* Visually hides content while keeping it in the accessibility tree and
   keyboard tab order -- e.g. the per-node <h2> heading in nodeCardHtml, or
   the #searchStatus live region. Never use display:none/visibility:hidden
   for this -- both remove the element from the tree entirely, defeating
   the point. */
.sr-only{
  position:absolute;width:1px;height:1px;padding:0;margin:-1px;
  overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0;
}

/* Skip link: the first focusable thing on the page (see index.html).
   Hidden the same sr-only way until it actually receives keyboard focus,
   at which point it needs to be plainly visible -- a skip link nobody can
   see defeats its own purpose. */
.skip-link{
  position:absolute;width:1px;height:1px;padding:0;margin:-1px;
  overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0;
}
.skip-link:focus{
  position:fixed;top:12px;left:12px;width:auto;height:auto;
  clip:auto;overflow:visible;white-space:normal;z-index:1000;
  background:var(--accent);color:var(--paper);
  font-family:var(--sans);font-size:14px;font-weight:600;
  padding:10px 18px;border-radius:8px;box-shadow:0 8px 24px rgba(27,26,23,.25);
}

:root{
  /* ===== Color palette: light (default) =====
     An earthy, "ink on paper" palette -- warm off-white paper, near-black
     ink, a forest green for the brand accent. Every accent pairing below
     is verified against WCAG 2.2 contrast minimums (computed via the
     actual relative-luminance formula, not eyeballed):
       - accent on paper / paper-raised (links, icons):      7.3 / 7.8 : 1
       - paper text on accent background (filled buttons):   7.3 : 1
       - accent-ink text on accent-soft background (bubbles): 9.8 : 1
     All comfortably clear AA (4.5:1) and land at AAA (7:1) too. */
  --ink:#1B1A17;
  --mute:#726c5e;
  --faint:#a39c8a;
  --paper:#FAF7F2;
  --paper-raised:#FFFFFF;
  --line:#E2E6D8;
  --accent:#1F5D3A;
  --accent-soft:#DCEAD6;
  --accent-ink:#123D26;
  --topbar-bg:rgba(250,247,242,.92);
  --radius:10px;
  --serif:'Fraunces', Georgia, serif;
  --sans:'Inter', system-ui, sans-serif;
  --nav-logo-height:44px;

  /* ===== Layout width system =====
     --max-text-width caps anything meant to stay readable (prose, plain
     images, the chat bubbles, the search bar itself) at roughly an 80
     character line length. --edge-pad is the "appropriate padding" that
     breakout content (rows, galleries, card grids) is still allowed to
     expand into, right up to the edge of the browser window. Both
     #content and .search-bar-fixed read --edge-pad, so the chat column
     and the search bar always resolve to the exact same centered box --
     which is what keeps the user message bubbles' right edge lined up
     with the search bar's right edge at any viewport size. */
  --max-text-width:80ch;
  --edge-pad:40px;
}
@media (max-width:640px){
  :root{ --edge-pad:16px; }
}

/* ===== Color palette: dark =====
   Deliberately higher-contrast than the light palette, not just an
   inverted copy of it -- every pairing below is AAA (7:1, the strictest
   WCAG 2.2 text tier), not just AA:
     - ink on paper (body text):                        16.1 : 1
     - mute on paper (secondary text):                    9.4 : 1
     - accent on paper / paper-raised (links, icons):   8.8 / 7.9 : 1
     - paper text on accent background (filled buttons):  8.8 : 1
     - accent-ink text on accent-soft background:         8.2 : 1
   Applied two ways: automatically when the OS/browser is set to dark
   (the @media block) and whenever manually forced via the [data-theme]
   attribute the toggle button sets (which also overrides the OS setting
   either direction -- see the two [data-theme] rules below). Both end up
   setting the exact same variables, on purpose, so there's only one dark
   palette to maintain. */
@media (prefers-color-scheme:dark){
  :root{
    --ink:#F4F1E7;
    --mute:#BFBAA4;
    --faint:#8B8672;
    --paper:#14160F;
    --paper-raised:#1E2117;
    --line:#2E3125;
    --accent:#57C98D;
    --accent-soft:#1B3324;
    --accent-ink:#8FD9A8;
    --topbar-bg:rgba(20,22,15,.85);
  }
}
/* Manual override via the theme-toggle button (see app.js) -- higher
   specificity than the plain :root above and the @media block above it,
   so these two always win regardless of source order or OS setting. */
:root[data-theme="dark"]{
  --ink:#F4F1E7;
  --mute:#BFBAA4;
  --faint:#8B8672;
  --paper:#14160F;
  --paper-raised:#1E2117;
  --line:#2E3125;
  --accent:#57C98D;
  --accent-soft:#1B3324;
  --accent-ink:#8FD9A8;
  --topbar-bg:rgba(20,22,15,.85);
}
:root[data-theme="light"]{
  --ink:#1B1A17;
  --mute:#726c5e;
  --faint:#a39c8a;
  --paper:#FAF7F2;
  --paper-raised:#FFFFFF;
  --line:#E2E6D8;
  --accent:#1F5D3A;
  --accent-soft:#DCEAD6;
  --accent-ink:#123D26;
  --topbar-bg:rgba(250,247,242,.92);
}

*{box-sizing:border-box;}
html,body{margin:0;padding:0;}
body{
  font-family:var(--sans);
  background:var(--paper);
  color:var(--ink);
  line-height:1.6;
  min-height:100vh;
  display:flex;
  flex-direction:column;
  transition:background-color .2s ease, color .2s ease;
}
a{color:var(--accent);}

/* ===== Top bar (brand + quick links + theme toggle) ===== */
.topbar{
  position:sticky;top:0;z-index:60;
  display:flex;align-items:center;justify-content:space-between;gap:20px;
  padding:16px 28px;
  background:var(--topbar-bg);
  backdrop-filter:blur(10px);-webkit-backdrop-filter:blur(10px);
  border-bottom:1px solid var(--line);
  flex-wrap:wrap;
  transition:background-color .2s ease, border-color .2s ease;
}
/* .brand is now a row: an optional logo (inserted by app.js only when
   header.logo is configured in content.json) next to the name/role text
   stack, which keeps its own column layout via .brand-text. */
.brand{display:flex;align-items:center;gap:10px;text-decoration:none;color:var(--ink);flex-shrink:0;min-height:44px;}
.brand-logo{height:var(--nav-logo-height);width:auto;display:block;border-radius:6px;flex-shrink:0;}
.brand-text{display:flex;flex-direction:column;line-height:1.15;}
.brand-name{font-family:var(--serif);font-size:19px;font-weight:600;}
.brand-role{font-size:11.5px;color:var(--mute);letter-spacing:.02em;}

/* Groups the quick-link buttons and the theme toggle together on the
   right, so the toggle always sits at the far right of the bar without
   disturbing the brand/quick-links space-between balance. */
.nav-right{display:flex;align-items:center;gap:2px;flex-shrink:0;}

/* ===== Quick links (inline nav) =====
   Shown whenever there's room for the brand + every button + the theme
   toggle on one line (see the min-width media query further down, which
   flips this off and the hamburger toggle on once that stops being true).
   Each link's visible pill is the nested .nav-label span; the <a> itself
   is the 44px-tall touch target, wider than the pill it contains so the
   touch-target itself supplies the horizontal breathing room between
   links instead of the .pill-track-style gap doing it. */
.quick-links{display:flex;gap:2px;flex-shrink:0;}
.quick-links a{
  display:inline-flex;align-items:center;justify-content:center;
  min-height:44px;padding:0 6px;text-decoration:none;color:inherit;box-sizing:border-box;
}
.quick-links a .nav-label{
  font-size:13.5px;font-weight:500;color:var(--ink);
  padding:8px 14px;border-radius:20px;border:1px solid var(--line);transition:all .15s ease;
}
.quick-links a:hover .nav-label{border-color:var(--accent);color:var(--accent);}
.quick-links a.quick-contact .nav-label{background:var(--ink);color:var(--paper);border-color:var(--ink);}
.quick-links a.quick-contact:hover .nav-label{background:var(--accent);border-color:var(--accent);color:var(--paper);}

/* ===== Dark-mode toggle button =====
   Shows a moon (meaning "tap to go dark") in light mode, a sun (meaning
   "tap to go light") in dark mode -- whichever icon is showing describes
   where a click takes you, not the current state. Three ways this can
   resolve, all handled in pure CSS: OS dark with no manual override (the
   @media rule), and either manual override (the two [data-theme] rules,
   which win over the @media rule via higher specificity). app.js only
   ever toggles the [data-theme] attribute; it doesn't touch these icons
   directly.

   The button itself is a 44x44 invisible hit target (no background/
   border of its own); .icon-circle is the nested span carrying the
   actual visible circle at its original, smaller size -- the gap between
   the two is the required touch-target margin, not extra visual bulk. */
.theme-toggle, .nav-toggle{
  width:44px;height:44px;border-radius:50%;flex-shrink:0;
  border:none;background:none;color:var(--ink);padding:0;
  display:flex;align-items:center;justify-content:center;cursor:pointer;
}
.theme-toggle .icon-circle, .nav-toggle .icon-circle{
  width:36px;height:36px;border-radius:50%;
  border:1px solid var(--line);background:var(--paper-raised);
  display:flex;align-items:center;justify-content:center;
  transition:border-color .15s ease, color .15s ease, background-color .2s ease;
}
.theme-toggle:hover .icon-circle, .nav-toggle:hover .icon-circle{border-color:var(--accent);color:var(--accent);}
.theme-toggle .icon-sun{display:none;}
.theme-toggle .icon-moon{display:block;}
@media (prefers-color-scheme:dark){
  :root:not([data-theme="light"]) .theme-toggle .icon-sun{display:block;}
  :root:not([data-theme="light"]) .theme-toggle .icon-moon{display:none;}
}
:root[data-theme="dark"] .theme-toggle .icon-sun{display:block;}
:root[data-theme="dark"] .theme-toggle .icon-moon{display:none;}
:root[data-theme="light"] .theme-toggle .icon-sun{display:none;}
:root[data-theme="light"] .theme-toggle .icon-moon{display:block;}

/* ===== Hamburger / drawer nav toggle =====
   Same dual-SVG show/hide trick as .theme-toggle above: one button, two
   icons, only one visible at a time via the .nav-open class on <body> --
   so the tap target's position never changes between "open" and "close"
   states, it's the exact same element both times. Hidden by default;
   only shown (via the min-width media query below) once .quick-links no
   longer fits on one line. */
.nav-toggle{display:none;position:relative;z-index:61;}
.nav-toggle .icon-close{display:none;}
.nav-toggle .icon-menu{display:block;}
body.nav-open .nav-toggle .icon-menu{display:none;}
body.nav-open .nav-toggle .icon-close{display:block;}

/* ===== Nav mode switch =====
   Below this width, the brand plus all quick-links plus the theme toggle
   no longer fit on a single line at their natural size -- swap to the
   hamburger + slide-out drawer instead of letting them wrap onto a second
   line under the sticky, blurred top bar. Tuned against this site's
   actual content (name + 4 buttons + toggle); see CONTENT-GUIDE.md if the
   number of header buttons changes significantly, as this may need
   revisiting. */
@media (max-width:900px){
  .quick-links{display:none;}
  .nav-toggle{display:flex;}
}

/* Locks background scrolling while the drawer is open. */
body.nav-open{overflow:hidden;}

.nav-drawer-backdrop{
  position:fixed;inset:0;z-index:49;
  background:rgba(21,19,16,.4);
  opacity:0;pointer-events:none;
  transition:opacity .25s ease;
}
body.nav-open .nav-drawer-backdrop{opacity:1;pointer-events:auto;}

.nav-drawer{
  position:fixed;top:0;right:0;bottom:0;z-index:50;
  width:min(300px, 84vw);
  background:var(--paper-raised);border-left:1px solid var(--line);
  box-shadow:-16px 0 40px rgba(27,26,23,.18);
  transform:translateX(100%);
  transition:transform .28s cubic-bezier(.2,.8,.2,1), background-color .2s ease, border-color .2s ease;
  display:flex;flex-direction:column;
  padding:88px 20px 32px;
  overflow-y:auto;
}
body.nav-open .nav-drawer{transform:translateX(0);}
/* Same "invisible 44px hit target around a visible pill" pattern as
   .quick-links -- the <a> is the touch target (min-height:44px, no
   background of its own), .nav-label is the nested span with the actual
   visible pill styling. Font-size and border-radius match .quick-links
   exactly (same button, just in a drawer), but here the label is
   stretched to the drawer's full width (width:100%, box-sizing so its
   own horizontal padding doesn't add to that) with its text kept
   left-aligned, so every button's left edge lines up in the vertical
   list -- unlike the inline top-bar version, which stays content-sized. */
.nav-drawer-links{display:flex;flex-direction:column;gap:4px;}
.nav-drawer-links a{
  display:flex;align-items:center;
  min-height:44px;text-decoration:none;color:var(--ink);
}
.nav-drawer-links a .nav-label{
  width:100%;box-sizing:border-box;text-align:left;
  font-size:13.5px;font-weight:500;
  padding:8px 14px;border-radius:20px;border:1px solid var(--line);transition:all .15s ease;
}
.nav-drawer-links a:hover .nav-label{border-color:var(--accent);color:var(--accent);}
.nav-drawer-links a.quick-contact .nav-label{background:var(--ink);color:var(--paper);border-color:var(--ink);}
.nav-drawer-links a.quick-contact:hover .nav-label{background:var(--accent);border-color:var(--accent);color:var(--paper);}

/* ===== Fixed bottom search bar (chat-style), suggestions float ABOVE it ===== */
.search-bar-fixed{
  position:fixed;left:0;right:0;bottom:0;z-index:40;
  display:flex;flex-direction:column;align-items:center;
  padding:0 var(--edge-pad) 20px;
}
.search-results{
  position:relative;width:100%;max-width:min(var(--max-text-width), 100%);margin-bottom:8px;
  background:var(--paper-raised);border:1px solid var(--line);border-radius:12px;
  box-shadow:0 -10px 28px rgba(27,26,23,.12);
  max-height:44vh;overflow-y:auto;display:none;
  transition:background-color .2s ease, border-color .2s ease;
}
.search-results.show{display:block;}
.result-item{
  display:flex;flex-direction:column;justify-content:center;width:100%;min-height:44px;
  text-align:left;background:none;border:none;box-sizing:border-box;
  padding:11px 16px;cursor:pointer;font-family:var(--sans);font-size:14px;color:var(--ink);
  border-bottom:1px solid var(--line);
}
.result-item:last-child{border-bottom:none;}
.result-item:hover, .result-item.active{background:var(--accent-soft);}
.result-kind{font-size:10.5px;text-transform:uppercase;letter-spacing:.06em;color:var(--mute);display:block;margin-bottom:2px;}
.result-empty{padding:14px 16px;font-size:13.5px;color:var(--mute);}

/* Suggested-next-question pills: float above the input, always reflect only
   the most recently opened result, swap with a cross-fade. A single
   horizontal line, not a wrapping block \u2014 keeps height predictable so it
   never grows tall enough to cover content behind the fixed search bar. */
/* No --edge-pad here, unlike everything else on this bar -- negative
   margins cancel .search-bar-fixed's own side padding so the pills track
   has room to grow wider than the search bar without being clipped by
   it. In practice the container's own rendered edge still lands exactly
   at the same inset as the search bar below it (verified against
   .search-input-row's left edge), which is what keeps a swipeable
   row's resting first pill lined up with the search bar with no extra
   alignment padding needed (see .scrollable further down). Whether the
   space is filled by centered, static pills (.fits), an auto-scrolling
   overflow (.marquee), or a swipeable overflow (.scrollable) is decided
   in JS by actually measuring the content against the available width --
   only content that doesn't fit should ever move on its own. */
.suggestion-pills{
  /* width:100% (i.e. exactly the parent's available content width) would
     look right, but it isn't -- .search-bar-fixed centers its children
     (align-items:center), and centering a width:100% box together with
     equal negative side-margins is self-cancelling: the margins pull it
     left by --edge-pad, but centering an item that's now --edge-pad*2
     "narrower" (in margin-box terms) pushes it right by that same
     --edge-pad to keep it centered. Net effect: zero bleed, landing
     right back at the padded inset -- which read as a gap between the
     pills and the screen edge. Sizing the box to calc(100% + 2 *
     --edge-pad) up front (i.e. the container's own full OUTER width, pad
     and all) cancels that out algebraically: the same centering math that
     used to erase the bleed now lands the box exactly at the true left
     and right viewport edges instead. */
  width:calc(100% + 2 * var(--edge-pad));margin:0 calc(-1 * var(--edge-pad)) 10px;
  overflow:hidden;
  opacity:0;transform:translateY(6px);
  transition:opacity .28s ease, transform .28s ease;
  pointer-events:none;
}
.suggestion-pills:empty{display:none;}
.suggestion-pills.show{opacity:1;transform:translateY(0);pointer-events:auto;}
/* Suppressed (while the user is typing a search) collapses height too, not
   just opacity -- otherwise the pills track still occupies its normal
   space even though invisible, leaving a much bigger gap than intended
   between the search-results dropdown above it and the search bar below. */
.suggestion-pills.suppressed{
  opacity:0;transform:translateY(6px);pointer-events:none;
  height:0;margin-top:0;margin-bottom:0;
}

.pill-track{display:flex;gap:8px;}

/* Fits: content is narrower than the available width. No scrolling of any
   kind -- just a single, centered, static row. */
.suggestion-pills.fits .pill-track{width:100%;justify-content:center;}

/* Overflow + non-touch, hover-capable: slow seamless auto-scroll, pauses
   on hover. The track holds the pill set duplicated once, so
   translateX(-50%) loops back to an identical starting position with no
   visible seam. Only reachable when content actually overflows. No edge
   fade -- pills scroll flush to both screen edges by design. */
.suggestion-pills.marquee .pill-track{width:max-content;}
@media (hover:hover) and (pointer:fine){
  .suggestion-pills.marquee .pill-track{ animation-name:pill-marquee; animation-timing-function:linear; animation-iteration-count:infinite; }
  .suggestion-pills.marquee:hover .pill-track{ animation-play-state:paused; }
  /* Keyboard users can't "hover" -- without this, tabbing onto a pill mid-
     scroll leaves it sliding out from under the focus ring, and a screen
     reader can lose track of the focused control entirely. */
  .suggestion-pills.marquee:focus-within .pill-track{ animation-play-state:paused; }
}
@keyframes pill-marquee{ from{ transform:translateX(0); } to{ transform:translateX(-50%); } }
@media (prefers-reduced-motion:reduce){
  .suggestion-pills.marquee .pill-track{ animation:none; }
}

/* Overflow + touch (or any device without real hover): a single row you
   can swipe through manually, no duplication, no looping illusion. Also
   reachable only when content overflows. No edge fade here either. The
   container itself now bleeds all the way to both true screen edges (see
   the width calc on .suggestion-pills above), so swiping all the way to
   the end lets the last pill run flush to the true right edge with no
   gap. Left padding equal to --edge-pad on the track (right side stays
   at 0, on purpose) keeps only the RESTING position's first pill lined
   up with the search bar below it -- once swiped, everything after that
   first pill is free to reach the true edge. */
.suggestion-pills.scrollable{
  overflow-x:auto;-webkit-overflow-scrolling:touch;
  scrollbar-width:none;
}
.suggestion-pills.scrollable .pill-track{width:max-content;padding-left:var(--edge-pad);}
.suggestion-pills.scrollable::-webkit-scrollbar{display:none;}

/* The button itself is the 44px-tall touch target (no visible styling of
   its own); .pill-label is the nested span carrying the actual pill look
   at its original, smaller size -- the gap between the two is the
   touch-target margin, not extra visual size. */
.pill{
  background:none;border:none;padding:0;
  min-height:44px;display:inline-flex;align-items:center;box-sizing:border-box;
  cursor:pointer;flex-shrink:0;
}
.pill-label{
  font-family:var(--sans);font-size:13px;font-weight:500;color:var(--ink);
  background:var(--paper-raised);border:1px solid var(--line);border-radius:18px;
  padding:8px 14px;box-shadow:0 4px 14px rgba(27,26,23,.06);
  transition:all .15s ease;
}
.pill:hover .pill-label{border-color:var(--accent);color:var(--accent);}

.search-input-row{
  width:100%;max-width:min(var(--max-text-width), 100%);display:flex;align-items:center;gap:8px;
  background:var(--paper-raised);border:1px solid var(--line);border-radius:26px;
  padding:6px 6px 6px 20px;box-shadow:0 8px 24px rgba(27,26,23,.08);
  transition:background-color .2s ease, border-color .2s ease, box-shadow .15s ease;
}
/* #searchInput has outline:none below (the default focus ring doesn't fit
   this pill shape) -- this is the replacement focus indicator, applied to
   the whole row rather than just the input so it's not clipped by the
   row's own border-radius, and satisfies WCAG 2.4.11/2.4.13 (a visible,
   non-removed focus indicator). */
.search-input-row:focus-within{
  border-color:var(--accent);
  box-shadow:0 8px 24px rgba(27,26,23,.08), 0 0 0 3px color-mix(in srgb, var(--accent) 35%, transparent);
}
#searchInput{
  flex:1;font-family:var(--sans);font-size:15px;padding:10px 0;
  border:none;background:none;color:var(--ink);outline:none;
}
/* This button's 44x44 touch target and its visible circle are one and
   the same -- a search "go" button is prominent enough that there's no
   reason to shrink the visible circle inside a larger invisible hit area
   (unlike the smaller icon buttons elsewhere); the row's own padding
   already gives it comfortable breathing room. */
#searchGoBtn{
  width:44px;height:44px;flex-shrink:0;border:none;background:none;padding:0;
  display:flex;align-items:center;justify-content:center;cursor:pointer;
}
#searchGoBtn .btn-visual{
  width:44px;height:44px;border-radius:50%;background:var(--accent);color:var(--paper);
  display:flex;align-items:center;justify-content:center;
  transition:background .15s ease;
}
#searchGoBtn:hover .btn-visual{background:var(--accent-ink);}

/* ===== Main content: a stack of cards, like a chat thread =====
   #content itself now runs the full width of the browser window (minus
   --edge-pad) so breakout content (rows, galleries, card grids) has
   somewhere to expand into. Ordinary content stays readable via the
   max-width rule on .answer-block / .topic-eyebrow / .content-block
   further down, which caps things back down to --max-text-width and
   self-centers -- see the "breakout" comment there for how the two
   interact. */
#content{flex:1;width:100%;padding:40px var(--edge-pad) 200px;display:flex;flex-direction:column;gap:28px;}
.stack-card{opacity:0;transform:translateY(14px);transition:opacity .4s cubic-bezier(.2,.8,.2,1), transform .4s cubic-bezier(.2,.8,.2,1);}

/* .msg.user is now just an alignment wrapper: full width, capped at
   --max-text-width and centered exactly like the search bar (same
   max-width expression, same "100%" it's centered against), with the
   actual bubble as an inner flex child pinned to the right via
   justify-content. Because the wrapper's box is geometrically identical
   to .search-input-row's box, the bubble's right edge and the search
   bar's right edge always land in the same place. */
.msg.user{
  width:100%;max-width:min(var(--max-text-width), 100%);margin:0 auto;
  display:flex;justify-content:flex-end;
  opacity:1;transform:translateY(0);
  transition:opacity .4s cubic-bezier(.2,.8,.2,1), transform .4s cubic-bezier(.2,.8,.2,1);
}
.msg.user.entering{opacity:0;transform:translateY(14px);}
.msg.user .bubble{
  max-width:100%;font-size:15px;font-weight:500;line-height:1.5;
  background:var(--accent-soft);color:var(--accent-ink);padding:10px 18px;
  border-radius:16px 16px 4px 16px;
  word-break:break-word;
}
.stack-card.in{opacity:1;transform:translateY(0);}
.stack-card + .stack-card{padding-top:28px;border-top:1px solid var(--line);}

/* Home / welcome */
.welcome-eyebrow{font-size:12.5px;letter-spacing:.1em;text-transform:uppercase;color:var(--accent);margin-bottom:14px;font-weight:600;}
.welcome h1{font-family:var(--serif);font-size:clamp(30px,5vw,44px);font-weight:600;line-height:1.15;margin:0 0 18px;}
.welcome p.lede{font-size:17px;color:var(--mute);max-width:56ch;margin:0 0 36px;}
.category-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(220px,1fr));gap:14px;}
.category-card{
  display:block;text-decoration:none;color:var(--ink);
  background:var(--paper-raised);border:1px solid var(--line);border-radius:var(--radius);
  padding:18px 20px;transition:border-color .15s ease, transform .15s ease;
}
.category-card:hover{border-color:var(--accent);transform:translateY(-2px);}
.category-card h3{font-family:var(--serif);font-size:17px;margin:0 0 5px;font-weight:600;}
.category-card p{font-size:13px;color:var(--mute);margin:0;}

/* Breadcrumb */
.breadcrumb{font-size:13px;color:var(--mute);margin-bottom:22px;}
.breadcrumb a{color:var(--mute);text-decoration:none;border-bottom:1px solid var(--line);}
.breadcrumb a:hover{color:var(--accent);border-color:var(--accent);}

/* Topic view */
/* Font-size lives on the link, not on .topic-eyebrow itself. .topic-eyebrow
   is one of the elements capped at --max-text-width using "ch" units (see
   the shared rule below), and "ch" resolves against whatever font-size
   the element carrying it has -- if .topic-eyebrow itself were 12px, its
   80ch would measure smaller than everything else's 80ch (which resolves
   against the page's default, larger font-size), throwing off the left
   alignment everything else shares. Keeping .topic-eyebrow at the
   inherited base size and styling the link instead avoids that. */
.topic-eyebrow{margin-bottom:10px;}
.topic-eyebrow a{font-size:12px;letter-spacing:.08em;text-transform:uppercase;color:var(--accent);font-weight:600;}
.topic-title{font-family:var(--serif);font-size:clamp(26px,4vw,36px);font-weight:600;margin:0 0 16px;line-height:1.18;}
.topic-blurb{font-size:16px;color:var(--ink);max-width:62ch;margin:0 0 32px;}
/* (question-list / related-nav styles removed — those are now the floating suggestion pills, see below) */

/* Question view */
.question-title{font-family:var(--serif);font-size:clamp(24px,4vw,32px);font-weight:600;margin:0 0 22px;line-height:1.22;}

/* ===== Width rule shared by every "normal" block =====
   .answer-block, .topic-eyebrow, and .content-block are the three
   wrappers that can sit directly inside a .stack-card (or inside a
   column's own stacked content). By default all three are capped at
   --max-text-width and self-center via margin auto -- this is what keeps
   plain paragraphs and standalone images lined up at a consistent,
   readable line length no matter how wide #content itself has become.
   .answer-block.breakout opts back out of the cap for the handful of
   content types (rows, galleries, card grids) that are explicitly
   allowed to use the full width #content provides. Because this rule is
   applied uniformly, the same "never wider than --max-text-width" cap
   also falls out automatically for anything nested one level deeper
   inside a column (a column's own width already tops out at
   --max-text-width, so the inner cap is a no-op there, not a conflict). */
.answer-block, .topic-eyebrow, .content-block{
  width:100%;max-width:min(var(--max-text-width), 100%);margin-left:auto;margin-right:auto;
}
.answer-block{margin-bottom:22px;}
.answer-block.breakout{max-width:none;}
.answer-block p{font-size:16px;margin:0 0 4px;}
.answer-block img{width:100%;border-radius:var(--radius);display:block;}
.answer-caption{font-size:13px;color:var(--mute);margin-top:8px;}

/* ===== Image "size": contain / cover =====
   An image with "size":"contain" or "size":"cover" is meant to be sized
   to match whatever height the rest of its row's columns end up needing
   (e.g. a photo next to a paragraph, sized down to the paragraph's
   height instead of the paragraph stretching to the photo's height).
   CSS has no direct "shrink to match my shortest sibling" behavior --
   align-items:stretch only ever grows things to match the tallest -- so
   this works by taking the <img> out of normal flow (position:absolute)
   so its own natural height no longer counts when the row/column decides
   how tall it needs to be. .image-fit-block is the piece that DOES stay
   in flow and stretches like any other column content; .image-fit fills
   whatever height that ends up being.

   Deliberately no fallback height here (e.g. a fixed aspect-ratio) for
   when there's no sibling to match -- a fallback would itself count as
   this column's "natural" height even when a sibling IS present,
   overriding the sibling's shorter height and defeating the whole
   mechanism. Instead, app.js only ever renders "contain"/"cover" markup
   at all when it already knows (from the row it's building) that there's
   a sibling column -- otherwise it renders a plain, normal image
   instead, so this CSS never has to handle the no-sibling case. */
.image-fit-block{
  width:100%;max-width:min(var(--max-text-width), 100%);margin:0 auto 22px;
  height:100%;display:flex;flex-direction:column;
}
.image-fit-block > .answer-caption{flex:0 0 auto;}
.image-fit{
  position:relative;flex:1 1 auto;min-height:0;
  border-radius:var(--radius);overflow:hidden;
}
.image-fit img{position:absolute;inset:0;width:100%;height:100%;display:block;}
.image-fit-contain img{object-fit:contain;}
.image-fit-cover img{object-fit:cover;}
.content-link{margin:0;}
.content-link a{
  font-weight:500;text-decoration:none;border-bottom:1px solid var(--accent-soft);
  padding-bottom:1px;transition:border-color .15s ease;
}
.content-link a:hover{border-color:var(--accent);}

/* Galleries live inside a .breakout .answer-block, so this can use the
   full available width. Packed toward the center (not stretched to
   fill) -- images keep a natural thumbnail size and wrap onto additional
   rows, still centered, once they no longer fit on one line. */
.gallery-grid{display:flex;flex-wrap:wrap;justify-content:center;gap:10px;}
/* min-width is wrapped in min(...,100%) so a single image can never be
   forced wider than its container -- without that, a fixed 140px floor
   would force horizontal overflow on any viewport narrower than ~140px
   plus edge padding (nowhere close on a real phone, but the pattern
   matters more once .content-card-wide's much larger floor is involved
   below). */
.gallery-grid img{flex:0 1 220px;min-width:min(140px, 100%);height:160px;object-fit:cover;border-radius:8px;cursor:pointer;transition:opacity .15s ease;}
.gallery-grid img:hover{opacity:.85;}
/* Now keyboard-focusable (tabindex="0" + role="button" in app.js) since
   activating one opens the lightbox the same as a click -- needs its own
   visible focus indicator to match. */
.gallery-grid img:focus-visible{outline:3px solid var(--accent);outline-offset:2px;}
/* On narrow screens, switch from "wrap at a fixed thumbnail size" to a
   strict two-per-row grid that shrinks with the viewport -- a fixed
   140px floor can still force a single column (or overflow) on the
   narrowest phones, but a 2-column grid always fits exactly two side by
   side at whatever width is available, with the same edge padding as
   everything else. Desktop/tablet thumbnail sizing above is unchanged. */
@media (max-width:640px){
  .gallery-grid{display:grid;grid-template-columns:repeat(2, 1fr);gap:8px;}
  .gallery-grid img{width:100%;height:auto;aspect-ratio:4/3;min-width:0;flex:none;}
}

/* ===== Lightbox: click any gallery image to open, arrow-navigate through
   the rest of that same gallery, Escape/click-backdrop/close-button to exit ===== */
.lightbox{
  position:fixed;inset:0;z-index:200;
  background:rgba(21,19,16,.94);
  display:flex;align-items:center;justify-content:center;
  opacity:0;pointer-events:none;
  transition:opacity .25s ease;
}
.lightbox.show{opacity:1;pointer-events:auto;}
.lightbox-img{max-width:88vw;max-height:82vh;border-radius:8px;box-shadow:0 24px 60px rgba(0,0,0,.5);}
/* Each button is a (>=44x44) invisible hit target; .lightbox-icon is the
   nested span carrying the actual visible circle, which can be smaller
   than the hit target -- e.g. the close button's hit box is 44x44 but
   its visible circle is the original, smaller 40x40. */
.lightbox-close,.lightbox-prev,.lightbox-next{
  position:absolute;background:none;color:#fff;border:none;
  cursor:pointer;display:flex;align-items:center;justify-content:center;
}
.lightbox-icon{
  background:rgba(255,255,255,.1);border-radius:50%;
  display:flex;align-items:center;justify-content:center;
  transition:background .15s ease;padding-bottom:0.2em;
}
.lightbox-close:hover .lightbox-icon,.lightbox-prev:hover .lightbox-icon,.lightbox-next:hover .lightbox-icon{background:rgba(255,255,255,.22);}
.lightbox-close{top:20px;right:20px;width:44px;height:44px;}
.lightbox-close .lightbox-icon{width:40px;height:40px;font-size:22px;line-height:1;}
.lightbox-prev,.lightbox-next{top:50%;transform:translateY(-50%);width:52px;height:52px;}
.lightbox-prev .lightbox-icon,.lightbox-next .lightbox-icon{width:52px;height:52px;font-size:30px;}
.lightbox-prev{left:16px;}
.lightbox-next{right:16px;}
@media (max-width:640px){
  .lightbox-prev,.lightbox-next{width:44px;height:44px;}
  .lightbox-prev .lightbox-icon,.lightbox-next .lightbox-icon{width:42px;height:42px;font-size:24px;}
  .lightbox-close{width:44px;height:44px;}
  .lightbox-close .lightbox-icon{width:36px;height:36px;font-size:20px;}
}

/* ===== Cards: title required, image/text/link all optional. A row made
   up entirely of cards renders straight into this centered, wrapping
   grid (see app.js's renderRow) inside a .breakout .answer-block, so it
   can use the full available width -- e.g. a row of 6 cards packs
   toward the center at their natural size, with equal space left over
   on both sides, and wraps to additional centered rows once they no
   longer fit on one line. Only linking cards get the hover lift. ===== */
.card-grid{display:flex;flex-wrap:wrap;justify-content:center;gap:14px;}
/* min-width is wrapped in min(...,100%) -- a bare 220px floor is harmless
   on desktop but on a real phone viewport (as narrow as ~320-375px, and
   still narrower once edge padding is subtracted) it becomes a hard
   overflow: the card refuses to shrink below that floor even though
   there's nowhere near 220px of room, forcing the whole page to scroll
   horizontally and clip content at the edges. min() caps the floor at
   the container's own width so the card can still shrink to fit. */
.content-card{
  flex:0 1 280px;min-width:min(220px, 100%);
  display:block;text-decoration:none;color:var(--ink);
  background:var(--paper-raised);border:1px solid var(--line);border-radius:var(--radius);
  overflow:hidden;transition:border-color .15s ease, transform .15s ease, background-color .2s ease;
  padding:16px 18px;
}
/* Same reasoning, doubled: a bare 454px floor is FAR wider than any phone
   screen, so this was the most severe offender -- every "wide" card (work
   history, the six process steps) was forcing horizontal overflow on
   mobile no matter what. */
.content-card-wide{flex:0 1 calc(280px * 2 + 14px);min-width:min(calc(220px * 2 + 14px), 100%);}
.content-card-link:hover{border-color:var(--accent);transform:translateY(-2px);}
.content-card img{width:100%;aspect-ratio:16/10;object-fit:cover;display:block;margin-bottom:0.5rem;border-radius:6px;}
.content-card-body{}
.content-card-title{font-family:var(--serif);font-size:16px;font-weight:600;margin:0 0 6px;line-height:1.25;}
.content-card-text{font-size:13.5px;color:var(--mute);margin:0;line-height:1.5;}

/* ===== Rows and columns: any array found inside a "contents" list becomes
   a row, one column per array item; any array found inside THAT (a
   column's own content) stacks vertically again -- alternating at any
   depth. (A row made up entirely of cards skips this and renders as a
   .card-grid instead -- see app.js's renderRow.)

   Lives inside a .breakout .answer-block, so the row itself can use the
   full width #content provides. Each column is capped at
   --max-text-width -- the same width a plain paragraph or image gets --
   so a two-column row (say, an image next to some text) tops out at
   roughly twice the width of the search bar. Columns don't stretch to
   fill leftover space (flex-grow:0): they sit at their natural/target
   width and the row packs them toward the horizontal center, with any
   leftover width distributed evenly on both sides. As available width
   shrinks, columns shrink smoothly with them (flex-shrink) down to a
   floor of ~260px, at which point they wrap onto their own line --
   i.e. stack, exactly like on mobile -- with no fixed breakpoint needed;
   it falls out of the available width continuously. ===== */
.content-row{
  display:flex;flex-wrap:wrap;justify-content:center;gap:24px;
}
/* flex-basis is deliberately a small ~260px, not the --max-text-width cap:
   the flex-wrap line-breaking algorithm decides what fits on a line using
   each item's *basis* size, before any growing/shrinking is applied -- a
   large basis would make columns wrap onto separate lines the instant
   their combined basis exceeds the row's width, even if shrinking them
   slightly would have let them comfortably share a line. A small basis
   lets several columns land on the same line first; flex-grow then
   expands them (up to the max-width cap) to use the row's available
   width, and flex-shrink pulls them back down smoothly as that width
   shrinks -- only wrapping once they'd drop below min-width. */
.content-column{
  flex:1 1 min(260px, 100%);
  max-width:min(var(--max-text-width), 100%);
  min-width:min(260px, 100%);
}
.content-column > .answer-block:last-child, .content-column > .content-block:last-child{margin-bottom:0;}

/* ===== Block: an explicit wrapper for when you want a column's content
   vertically distributed instead of packed at the top. Stretches to fill
   its column's full height (a grid item stretches to the row's height by
   default), then uses justify-content to position its own children within
   that height -- which is naturally a no-op if this column is already the
   tallest one in its row, since there's no extra height to distribute
   into. No JS "am I the tallest" check needed; it falls out of the CSS box
   model on its own. Deliberately NOT wrapped in .answer-block (unlike every
   other block type) -- height:100% needs a direct, definite-height parent
   to resolve against; an auto-height wrapper would silently break it. Has
   its own margin-bottom instead, for when it's used outside a column. ===== */
.content-block{height:100%;display:flex;flex-direction:column;margin-bottom:22px;}
.content-block.align-top{justify-content:flex-start;}
.content-block.align-center{justify-content:center;}
.content-block.align-bottom{justify-content:flex-end;}
.content-block > .answer-block:last-child{margin-bottom:0;}

.video-placeholder{
  background:var(--ink);color:var(--paper);border-radius:var(--radius);
  aspect-ratio:16/9;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:10px;
  font-size:13.5px;color:var(--faint);
}
.video-placeholder .play{width:52px;height:52px;border-radius:50%;background:rgba(255,255,255,.12);display:flex;align-items:center;justify-content:center;}
.doc-block{display:flex;flex-direction:column;gap:12px;}
.doc-embed{width:100%;height:520px;border:1px solid var(--line);border-radius:var(--radius);background:#fff;}
/* The <a> is the (invisible) 44px-tall touch target; .btn-visual is the
   nested span with the actual pill look at its original, smaller size. */
.doc-download{
  align-self:flex-start;display:inline-flex;align-items:center;box-sizing:border-box;
  min-height:44px;text-decoration:none;
}
.doc-download .btn-visual{
  font-size:13.5px;font-weight:500;
  padding:9px 16px;border-radius:20px;background:var(--ink);color:var(--paper);
  transition:background .15s ease;
}
.doc-download:hover .btn-visual{background:var(--accent);}

.contact-form{display:flex;flex-direction:column;gap:12px;max-width:440px;}
.contact-form label{font-size:13px;color:var(--mute);}
.contact-form input, .contact-form textarea{
  font-family:var(--sans);font-size:14.5px;padding:10px 14px;border:1px solid var(--line);
  border-radius:8px;background:var(--paper-raised);color:var(--ink);outline:none;
}
.contact-form input:focus, .contact-form textarea:focus{
  border-color:var(--accent);
  box-shadow:0 0 0 3px color-mix(in srgb, var(--accent) 35%, transparent);
}
/* The <button> is the (invisible) 44px-tall touch target; .btn-visual is
   the nested span with the actual pill look at its original, smaller
   size. */
.contact-form button{
  align-self:flex-start;min-height:44px;box-sizing:border-box;
  display:inline-flex;align-items:center;
  background:none;border:none;padding:0;cursor:pointer;
}
.contact-form button .btn-visual{
  display:inline-flex;align-items:center;
  font-family:var(--sans);font-size:14.5px;font-weight:500;
  padding:10px 20px;border-radius:20px;background:var(--accent);color:var(--paper);
  transition:background .15s ease;
}
.contact-form button:hover .btn-visual{background:var(--accent-ink);}

@media (max-width:640px){
  .topbar{padding:12px 16px;}
  .brand-role{display:none;}
  #content{padding:32px var(--edge-pad) 180px;}
  .search-bar-fixed{padding:0 var(--edge-pad) 14px;}
  /* At this width the 760px nav-mode breakpoint has already switched the
     top bar over to the hamburger toggle (see the "Nav mode switch" rule
     above), so the top bar only ever holds the brand plus two round icon
     buttons (theme toggle + nav toggle) here -- no button-wrapping
     problem left to solve, just a slightly smaller logo/name so the
     brand doesn't crowd the buttons on the narrowest phones. */
  :root{ --nav-logo-height:32px; }
  .brand-name{font-size:16px;}
  .nav-drawer{width:min(300px, 88vw);padding:80px 16px 28px;}
}