/* ============================================================
   Sway site chrome — banner, sidebar, content shell.

   The .gs-* rules are carried over verbatim from the Google Sites
   replica embedded in the legacy pages (extracted from the live
   Google Sites via getComputedStyle — see docs/ARCHITECTURE.md).
   Three deliberate departures from the legacy replica:
     1. The chrome is always visible (this IS the site now), so the
        body.standalone gating is gone.
     2. Below the 1280px breakpoint the sidebar becomes a slide-in
        drawer opened from a hamburger button in the banner, instead
        of disappearing entirely (the old replica left no navigation
        at all on narrow screens).
     3. The active-page underline (a.gs-active) has no fixed height.
        The replica's height:26px assumed Google Sites' tight line
        box; under this site's 1.6 line-height it left a 19px content
        box (border-box minus the 7px border) and the inherited
        overflow:hidden clipped descenders under the bar.
   ============================================================ */

/* ---- Tokens ----
   Chrome tokens (--gs-*) are theme-independent: the banner and sidebar are
   always dark, exactly like the legacy pages. Content tokens flip with the
   html.light-mode / html.dark-mode classes (same localStorage 'theme' key
   the legacy pages use, so the choice carries across the whole site).
   Light is the default — :root carries the light values for no-JS visitors. */
:root {
  --gs-banner-bg: rgb(35, 0, 77);
  --gs-sidebar-bg: rgb(25, 24, 26);
  --gs-text: rgb(249, 249, 249);
  --gs-active: rgb(80, 80, 208);
  --banner-h: 64px;
  --sidebar-w: 250px;

  color-scheme: light;

  /* ---- Elevation ladder ----
     The base is a warm off-white rather than pure white — 2.1 L* down, chroma
     1.55, which reads as paper without reading as cream. Raised and overlay
     surfaces share that value, so their lift is carried entirely by
     --border-subtle plus a shadow, and the tonal rungs run downward.

       sunken-2 L* 91.5   pressed/active states, nested wells
       sunken   L* 94.2   wells, table headers, inputs — cut into the page
       page     L* 97.9   the base
       raised   L* 97.9   cards — + border + --shadow-sm
       overlay  L* 97.9   + --shadow-lg

     Every rung sits the same distance below the page as it did when the page
     was white, so wells and hairlines separate exactly as before. Chroma stays
     between 1.5 and 3.4: enough that nothing reads blue, little enough that
     the grays never look tinted against the page.

     --surface-raised is kept as its own token even though it equals --page-bg
     today; setting it back to #ffffff is the one-line change that gives cards
     2.1 L* of lift again. */
  --surface-sunken-2: #e8e7e2;
  --surface-sunken: #f0eeea;
  --page-bg: #faf9f6;
  --surface-raised: #ffffff;
  --surface-overlay: #ffffff;

  --text: #212529;
  --text-strong: #111118;
  --text-soft: #3f3f46;
  --link-color: #5a5ad1;
  --link-hover: #3a3ab1;

  /* Hairlines. Opaque at the two ends of the ramp so they hold their hue on
     any surface; --rule stays alpha because it is drawn over both. With the
     page and the cards on one value the border is load-bearing — it is what
     makes a card a card. */
  --border-subtle: #dedcd7;
  --border-strong: #c6c3bd;
  --rule: rgba(60, 50, 40, 0.16);

  /* Shadows carry the elevation the page color no longer can, so they are a
     touch deeper than they would be on a tinted ground. Warm-tinted rather
     than neutral black, and two layers each — a tight contact shadow plus a
     wide diffuse one. */
  --shadow-sm: 0 1px 2px rgba(60, 50, 40, 0.07);
  --shadow-md: 0 1px 2px rgba(60, 50, 40, 0.07), 0 3px 8px -4px rgba(60, 50, 40, 0.12);
  --shadow-lg: 0 2px 4px rgba(60, 50, 40, 0.08), 0 12px 28px -14px rgba(60, 50, 40, 0.24);

  --footer-text: #6f6d68;
  --fab-color: #333333;
}
html.dark-mode {
  color-scheme: dark;

  /* ---- Dark elevation ladder ----
     The base stays true black (OLED pixels-off), so the ladder runs upward
     and "sunken" lifts like everything else — the standard true-black
     inversion. Every rung sits on one hue: OKLCh h≈285, the violet-ink the
     evidence deck and the purple chrome already live on, with chroma held
     between 1.2 and 2.1 so the tint reads as ink, never as a navy theme.
     Rungs are evenly spaced in OKLCh lightness:

       page      okL  0     true black
       sunken-2  okL 13     pressed/active states, nested wells
       sunken    okL 16.5   wells, table headers, inputs
       raised    okL 20     cards — + border + --shadow-sm
       overlay   okL 24.5   popovers, drawer, the FAB

     Cards carry real tonal lift over the floor (the dark analog of the
     light pass that put cards back on #ffffff); the border and the shadow's
     top hairline do the rest. */
  --surface-sunken-2: #07070c;
  --surface-sunken: #0e0e15;
  --page-bg: #000000;
  --surface-raised: #15151e;
  --surface-overlay: #1f1f2a;

  /* Text runs a real four-step ramp (strong 96 / body 89.5 / soft 77 /
     footer 64 in okL). Body sits at ~13-15:1 on the surfaces it reads on —
     comfortable at night — rather than the old near-white #f3f3f3, which
     glared at 19:1 and left headings nowhere to go. */
  --text: #dcdce1;
  --text-strong: #f1f1f5;
  --text-soft: #b3b3bc;
  --link-color: #8a8aff;
  --link-hover: #b3b3ff;

  /* Opaque, like the light borders, so they hold their hue on any rung;
     both sit ~8-9 okL above the card face, mirroring the light deltas.
     --rule stays alpha because it draws over page and card alike — 14%
     violet-white, calmed from the old 25% pure white. */
  --border-subtle: #2b2b36;
  --border-strong: #3e3e4b;
  --rule: rgba(226, 226, 248, 0.14);

  /* Drop shadows can't darken a true-black page, so raised surfaces also
     get a 1px top inner hairline — light falling on the card's upper edge —
     which is what actually reads as elevation on ink. */
  --shadow-sm: inset 0 1px 0 rgba(226, 226, 255, 0.05), 0 1px 2px rgba(0, 0, 0, 0.55);
  --shadow-md: inset 0 1px 0 rgba(226, 226, 255, 0.05), 0 1px 2px rgba(0, 0, 0, 0.55), 0 3px 8px -4px rgba(0, 0, 0, 0.7);
  --shadow-lg: inset 0 1px 0 rgba(226, 226, 255, 0.06), 0 2px 4px rgba(0, 0, 0, 0.55), 0 12px 28px -14px rgba(0, 0, 0, 0.9);

  --footer-text: #8b8b92;
  --fab-color: #dcdce1;
}

/* ---- Base ---- */
*, *::before, *::after { box-sizing: border-box; }
html {
  background: var(--page-bg);
  /* Fluid root size: content type scales with the window so large displays
     read comfortably by default (≈20.5px at 1728px wide, a floor at the
     reader's own default on small screens, and a cap at 134% of it).
     Everything sized in rem scales with it; the banner/sidebar chrome is
     deliberately fixed in px.

     Percentages rather than px, which is the same choice the legacy half
     made in lib/legacy-chrome/fragments/fluid-*.css and for the same reason:
     a percentage here resolves against the browser's default font size, so a
     reader who has set theirs to 20px gets a page that starts there and
     scales from there. In px the curve overrode that setting outright — the
     one accessibility preference this site was quietly ignoring, on the half
     of the site that carries most of the reading.

     The numbers are the previous ones expressed against a 16px default:
     100% = 16, 68.75% = 11, 134.375% = 21.5. At that default the computed
     size is identical at every width. */
  font-size: clamp(100%, 68.75% + 0.55vw, 134.375%);
}
/* Smooth scrolling only where it has not been asked to stop. This sheet loads
   on every templated page, so an unconditional `scroll-behavior: smooth` here
   overrode deck-wireframe.css's own `no-preference` guard: measured under
   `prefers-reduced-motion: reduce`, the landing deck and the walkthrough rigs
   still scrolled smoothly — the two page types the preference most exists
   for. The guard belongs wherever the property is set, not only where the
   animation is. */
@media (prefers-reduced-motion: no-preference) {
  html { scroll-behavior: smooth; }
}
body {
  margin: 0;
  padding: var(--banner-h) 0 0 0;
  background: var(--page-bg);
  color: var(--text);
  font-family: "IBM Plex Sans", sans-serif;
  font-size: 1rem;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}
/* Line-break quality (progressive enhancements; no-ops where text-wrap is
   unsupported): headings balance their lines instead of stranding a word,
   prose avoids a single short word as its last line. The legacy pages get
   the same rules from lib/legacy-chrome/fragments/fluid-*.css. */
h1, h2, h3, h4, h5 { text-wrap: balance; }
p, li, blockquote, figcaption { text-wrap: pretty; }
@media (min-width: 1280px) {
  body { margin-left: var(--sidebar-w); }
}

/* ---- Banner (Google Sites announcement bar) ---- */
.gs-banner{display:flex;position:fixed;top:0;left:0;right:0;z-index:1300;height:64px;background-color:rgb(35,0,77);padding:0 12px;justify-content:center;align-items:center}
.gs-banner p{font-family:Roboto,sans-serif;font-weight:300;font-size:16px;color:rgb(255,255,255);margin:16px 0}
.gs-banner .gs-btn-outer{display:flex;padding-left:23px}
.gs-banner .gs-btn-box{border:1px solid rgb(255,255,255);border-radius:4px;overflow:hidden;cursor:pointer;position:relative}
.gs-banner .gs-btn-box a{display:block;font-family:Roboto,sans-serif;font-weight:300;font-size:16px;color:rgb(255,255,255);line-height:34px;letter-spacing:0.25px;text-align:center;text-decoration:none;padding:0 23px}
.gs-banner .gs-btn-box:hover{background:rgba(255,255,255,0.08)}

/* Hamburger — only below the sidebar breakpoint */
.gs-menu-btn{display:none;position:absolute;left:8px;top:50%;transform:translateY(-50%);background:none;border:0;color:#fff;padding:8px;cursor:pointer;border-radius:4px}
.gs-menu-btn:hover{background:rgba(255,255,255,0.08)}
@media (max-width: 1279px) {
  .gs-menu-btn{display:flex;align-items:center;justify-content:center}
}

/* ---- Sidebar (Google Sites navigation replica) ---- */
.gs-sidebar{display:block;position:fixed;top:64px;left:0;bottom:0;z-index:1250;width:250px;background-color:rgb(25,24,26);overflow-y:auto;padding-bottom:64px;font-family:Roboto,sans-serif;font-size:17.6px;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}
.gs-sidebar::-webkit-scrollbar{width:4px}
.gs-sidebar::-webkit-scrollbar-thumb{background:rgba(255,255,255,0.1);border-radius:2px}
.gs-sidebar .gs-logo{display:block;margin:48px 32px 0 48px;text-decoration:none}
.gs-sidebar .gs-logo img{width:36px;height:36px;max-width:100%}
.gs-sidebar .gs-brand{display:block;position:relative;z-index:20;margin:48px 32px 62px 48px;font-family:Roboto,sans-serif;font-size:26.6667px;font-weight:400;color:rgb(249,249,249);text-decoration:none;overflow:hidden;text-overflow:ellipsis;cursor:pointer}
.gs-sidebar ul{list-style:none;padding:0;margin:0}
.gs-sidebar li{list-style:none;padding:0;margin:0}
.gs-l1{position:relative;margin-top:12px;padding:0 12px 0 36px;font-family:Roboto,sans-serif;color:rgb(249,249,249);text-overflow:ellipsis}
.gs-l1-in{position:relative}
.gs-l1-in>a{display:block;position:relative;padding-left:4px;font-family:Roboto,sans-serif;font-size:17.6px;font-weight:400;color:rgb(249,249,249);text-decoration:none;overflow:hidden;text-overflow:ellipsis;vertical-align:middle}
.gs-l1-in>a:hover{background:rgba(255,255,255,0.06)}
.gs-l2{position:relative;margin-top:12px;padding:0 12px 0 56px;font-family:Roboto,sans-serif;color:rgb(249,249,249);text-overflow:ellipsis}
.gs-l2-in{position:relative}
.gs-l2-in>a{display:block;position:relative;padding-left:4px;font-family:Roboto,sans-serif;font-size:17.6px;font-weight:400;color:rgb(249,249,249);text-decoration:none;overflow:hidden;text-overflow:ellipsis;vertical-align:middle}
.gs-l2-in>a:hover{background:rgba(255,255,255,0.06)}
.gs-l2-in>a.gs-active{display:inline-block;margin-right:12px;margin-left:4px;padding-left:0;border-bottom:7px solid rgb(80,80,208)}
.gs-l1-in>a.gs-section{cursor:default}
/* Keyboard-accessible sidebar group toggles: the <button>s (group labels on
   hrefless groups, chevrons on linked groups) restyled to match the anchors
   they replaced. line-height is set explicitly because form controls do not
   inherit it (the UA sets line-height:normal), which made the group rows
   shorter than the plain anchor rows and bunched them up vertically. */
.gs-l1-in>button.gs-section{display:block;width:100%;text-align:left;background:none;border:0;margin:0;position:relative;padding:0 0 0 4px;font-family:Roboto,sans-serif;font-size:17.6px;line-height:1.6;font-weight:400;color:rgb(249,249,249);cursor:pointer;overflow:hidden;text-overflow:ellipsis}
.gs-l1-in>button.gs-section:hover{background:rgba(255,255,255,0.06)}
button.gs-chev{background:none;border:0;margin:0;padding:0 0 0 12px;cursor:pointer;color:rgb(249,249,249)}
.gs-chev{position:absolute;top:-14.5px;left:-32px;width:36px;height:24px;padding-left:12px;cursor:pointer;transform:translateY(12px);vertical-align:middle;color:rgb(249,249,249)}
.gs-chev svg{width:24px;height:24px;transition:transform 0.2s}
.gs-open .gs-chev svg{transform:rotate(180deg)}
.gs-subnav{display:none;position:relative}
.gs-open>.gs-subnav{display:block}
.gs-l1-in>a.gs-active{display:inline-block;margin-right:12px;margin-left:4px;padding-left:0;border-bottom:7px solid rgb(80,80,208)}

/* Drawer mode below the breakpoint */
@media (max-width: 1279px) {
  /* Shadow only while open — on the closed drawer it bled past the parked
     sidebar's edge and drew a gray fade down the left side of the page. */
  .gs-sidebar{transform:translateX(-100%);transition:transform 0.25s ease}
  body.nav-open .gs-sidebar{transform:translateX(0);box-shadow:2px 0 18px rgba(0,0,0,0.6)}
  .gs-scrim{position:fixed;top:var(--banner-h);left:0;right:0;bottom:0;z-index:1240;background:rgba(0,0,0,0.5);opacity:0;pointer-events:none;transition:opacity 0.25s ease}
  body.nav-open .gs-scrim{opacity:1;pointer-events:auto}
}
@media (min-width: 1280px) {
  .gs-scrim{display:none}
}

/* What the narrow-screen banner still owes the page, rather than the bar.
   The bar itself — its height, its padding, the type in it and the shape it
   takes — is set in css/banner-tools.css, which is the sheet both halves of
   the site load and so the one place it can be said once. This block used to
   restate all of it, including a --banner-h of 48px that that sheet then
   overrode with 54; what is left is the two rules that are about the page
   under the bar, and that only the templated pages need (the legacy ones get
   their equivalents from legacy-chrome's banner-metrics.css). */
@media (max-width: 600px) {
  body{padding-top:var(--banner-h)}
  .gs-sidebar{top:var(--banner-h)}
}

/* ---- Content shell ---- */
.site-main { min-height: calc(100vh - var(--banner-h) - 80px); }
.page-container {
  max-width: 78rem;   /* scales with the fluid root, so text fills the window */
  margin: 0 auto;
  padding: 3rem 2.5rem 5rem;
}
@media (max-width: 600px) {
  .page-container { padding: 32px 20px 60px; }
}

/* Creators page: small headshot left, bio right (as on Google Sites) */
.creator-row {
  display: flex;
  gap: 40px;
  align-items: flex-start;
  margin: 3rem 0;
}
.creator-row img {
  flex: 0 0 178px;
  width: 178px;
  height: auto;
  margin: 0;
}
.creator-row p { margin: 0; }
@media (max-width: 700px) {
  .creator-row { flex-direction: column; }
}

/* Social icon row (about page) */
.social-links {
  display: flex;
  justify-content: flex-end;
  gap: 14px;
  margin: 8px 0 -24px;
}
.social-links a { display: inline-flex; border-radius: 50%; }
.social-links svg { display: block; }

/* Disclosures in the run of a page — the walkthroughs' notes and the
   examples' asides. /faq had these too until it moved into the
   wireframe language, which draws its questions as rows in a box
   instead (css/wire-page.css) and turns these off for itself. */
.page-content details {
  border-bottom: 1px solid var(--rule);
  padding: 26px 0 26px;
}
.page-content details > summary {
  list-style: none;
  cursor: pointer;
  font-family: "Roboto Mono", monospace;
  font-weight: 500;
  font-size: clamp(1.15rem, 2vw, 1.667rem);
  line-height: 1.5;
  color: var(--text);
}
.page-content details > summary::-webkit-details-marker { display: none; }
.page-content details > summary:hover { color: var(--text-strong); }
.page-content details[open] { padding-bottom: 8px; }

/* The page index ("on this page") lives in css/page-index.css, loaded by
   layouts/base.njk on the pages that set `toc: true`. It is not here because
   it is not site chrome: it is a component in the wireframe language, with
   its own tokens, and the four implementation examples carry it without
   carrying any of the language's other sheets. */

/* ---- Theme toggle FAB (same placement/behavior as the legacy pages) ---- */
.theme-toggle {
  position: fixed;
  bottom: 2rem;
  right: 2rem;
  z-index: 1200;
  width: 50px;
  height: 50px;
  border-radius: 50%;
  border: 1px solid var(--border-subtle);
  background: var(--surface-overlay);
  color: var(--fab-color);
  cursor: pointer;
  box-shadow: var(--shadow-lg);
  display: flex;
  align-items: center;
  justify-content: center;
  transition: transform 0.15s ease;
}
.theme-toggle:hover { transform: scale(1.08); }
/* The button still grows under the pointer; it stops traveling there. */
@media (prefers-reduced-motion: reduce) {
  .theme-toggle { transition: none; }
}
/* Bottom right is where a thumb rests, which is the trouble: on a phone the
   button sat over the last line of whatever the reader had scrolled to. Below
   the banner in the top right instead, and smaller, so it overlaps a heading's
   margin rather than a paragraph's text. */
@media (max-width: 1279px) {
  .theme-toggle {
    top: calc(var(--banner-h, 48px) + 0.55rem);
    right: 0.8rem;
    bottom: auto;
    width: 42px;
    height: 42px;
  }
  .theme-toggle svg { width: 19px; height: 19px; }
}
.theme-toggle .icon-sun { display: none; }
.theme-toggle .icon-moon { display: block; }
html.dark-mode .theme-toggle .icon-sun { display: block; }
html.dark-mode .theme-toggle .icon-moon { display: none; }

/* ---- Content typography ----
   Sizes measured from the live Google Sites pages via getComputedStyle:
   h1 Roboto Mono 700 40px; h2 Roboto Mono 500 26.67px;
   body Roboto 300 16px, line-height ~2. Colors come from the theme tokens. */
.page-content h1 {
  font-family: "Roboto Mono", monospace;
  font-weight: 700;
  font-size: clamp(1.7rem, 3.2vw, 2.5rem);
  line-height: 1.38;
  color: var(--text-strong);
  margin: 0.9em 0 0.6em;
}
.page-content h2 {
  font-family: "Roboto Mono", monospace;
  font-weight: 500;
  font-size: clamp(1.25rem, 2.2vw, 1.667rem);
  line-height: 1.5;
  color: var(--text);
  margin: 2.6em 0 0.9em;
}
.page-content h3 {
  font-family: "Roboto Mono", monospace;
  font-weight: 500;
  font-size: 1.2rem;
  color: var(--text);
  margin: 1.8em 0 0.6em;
}
.page-content h4 {
  font-family: "Roboto Mono", monospace;
  font-weight: 500;
  font-size: 1.02rem;
  color: var(--text-soft);
  margin: 1.7em 0 0.5em;
}
.page-content h2, .page-content h3 { scroll-margin-top: calc(var(--banner-h) + 20px); }
.page-content p, .page-content li {
  font-weight: 400;
  font-size: 1rem;
  line-height: 1.8;
  color: var(--text);
}
.page-content a { color: var(--link-color); text-decoration: none; }
.page-content a:hover { color: var(--link-hover); text-decoration: underline; }
/* A bare URL has no break opportunity in it, so on a phone it sets the page's
   width instead of taking the column's. /students/ prints
   https://chat.swaybeta.ai/student/reset_password in full and was 397px wide
   in a 390px window — the only page of the site that scrolled sideways. */
.page-content a, .page-content code { overflow-wrap: anywhere; }
/* Content images: constrained and centered by default (source files are
   large exports). Use class="img-wide" for anything that should span the
   full column. */
.page-content img {
  max-width: min(100%, 39rem);
  height: auto;
  display: block;
  margin: 2em auto;
  border-radius: 6px;
}
.page-content img.img-wide { max-width: 100%; }
.page-content figure {
  margin: 2.5em auto;
  max-width: 40rem;
}
.page-content figure img { margin: 0 auto 0.6em; }
.page-content figcaption {
  font-size: 0.88rem;
  color: var(--text-soft);
  text-align: center;
}
.page-content blockquote {
  margin: 1.5em 0;
  padding: 0.2em 0 0.2em 1.2em;
  border-left: 4px solid var(--gs-active);
  font-style: italic;
}
.page-content hr { border: 0; border-top: 1px solid var(--rule); margin: 2.5em 0; }
/* Tables ride the elevation ladder: the table itself is a raised surface, the
   header row is cut into it, and the hairlines are the subtle border. */
.page-content table {
  border-collapse: collapse;
  width: 100%;
  margin: 1.5em 0;
  background: var(--surface-raised);
  border: 1px solid var(--border-subtle);
  border-radius: 8px;
  overflow: hidden;
  box-shadow: var(--shadow-sm);
}
.page-content th, .page-content td { padding: 9px 14px; text-align: left; }
.page-content th { background: var(--surface-sunken); font-weight: 600; }
.page-content tbody tr + tr td, .page-content thead + tbody td { border-top: 1px solid var(--border-subtle); }

/* ---- Rotating student pull-quote (landing + design studies) ----
   Typographic treatment — serif italic, no quotation mark — rather than a
   card, and fully theme-aware. Rendered by js/landing.js.

   There is no mark on purpose. A Charter open-quote used to hang in the
   margin here at 4.4rem in the accent, and it was the only thing on the
   page speaking neither of the site's two languages: not the drawn ink,
   not the speech bubble. On the most-read block on the site it read as
   ornament rather than as part of the page. It also indented the text
   2.3rem, so the quote began at a place the heading and the paragraph
   above it did not. The italic and the attribution under it already say
   this is someone speaking, which is the whole job the mark was doing —
   so removing it costs nothing and returns the quote to the column edge.

   Nothing here is positioned any more, so .pull-quote no longer needs
   `position: relative`; figcaption's rule is a flex item, not an
   absolutely placed one. */
.quotes-container {
  display: flex;
  align-items: center;
  width: 100%;
  min-height: 170px;
}
.pull-quote {
  margin: 0;
  opacity: 1;
  transition: opacity 1s ease-in-out;
}
.pull-quote.fade-out { opacity: 0; }
/* js/landing.js stops the rotation outright under this preference, so the
   crossfade has nothing left to fade — the transition goes with it so a
   theme switch cannot animate the quote either. */
@media (prefers-reduced-motion: reduce) {
  .pull-quote { transition: none; }
}
/* In the run of a reading page, this figure is not one of those figures.
   `.page-content figure` above centers illustrations and caps them at 40rem,
   which is right for a large export with a caption under it and wrong for a
   quotation: text on this site runs to the column's edge rather than to a
   measure of its own, and a quote indented 166px from the paragraph above it
   reads as a fault. That rule is (0,1,1) and outranks `.pull-quote`'s own
   `margin: 0`, so this says it at the same weight. Only the centering and the
   cap go; the vertical rhythm is that rule's, because in prose the quote does
   need the air. Until /orientation/film/ the pull-quote had only ever been
   used outside .page-content — on the landing deck, inside .quotes-container
   — so the two had never met. */
.page-content .pull-quote {
  margin: 2.5em 0;
  max-width: none;
}
.pull-quote blockquote {
  margin: 0;
  padding: 0;
  border: 0;
  font-family: Charter, "Bitstream Charter", Georgia, serif;
  font-style: italic;
  font-weight: 400;
  font-size: clamp(1.15rem, 1.8vw, 1.45rem);
  line-height: 1.65;
  color: var(--text);
}
.pull-quote figcaption {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  margin-top: 0.9rem;
  font-family: "Roboto Mono", monospace;
  font-size: 0.72rem;
  font-weight: 500;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--text-soft);
}
.pull-quote figcaption::before {
  content: "";
  width: 26px;
  height: 1px;
  background: var(--gs-active);
}

/* Responsive video embeds */
.video-embed {
  position: relative;
  width: 100%;
  aspect-ratio: 16 / 9;
  margin: 1.5em 0;
}
.video-embed iframe {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  border: 0;
}

/* Buttons in content (outlined, Google Sites style) */
.gs-button {
  display: inline-block;
  border: 1px solid var(--text-strong);
  border-radius: 4px;
  padding: 8px 24px;
  color: var(--text-strong);
  font-family: Roboto, sans-serif;
  font-weight: 300;
  text-decoration: none;
}
.gs-button:hover { background: var(--surface-sunken); }

/* ---- Footer (black strip, small gray centered text — as on Google Sites) ---- */
.site-footer {
  padding: 28px 16px 36px;
  text-align: center;
}
.site-footer p {
  margin: 0;
  font-family: Roboto, Arial, sans-serif;
  font-weight: 300;
  font-size: 12px;
  line-height: 1.5;
  color: var(--footer-text);
}
/* The org credits link out but read as plain text — no underline, no color
   shift, in any state. The :hover selectors are spelled out because the
   credit line on /about/ sits inside .page-content, whose a:hover rule would
   otherwise underline it. The focus ring stays so the links are still
   reachable by keyboard. */
.site-footer a,
.site-footer a:hover,
.site-footer a:focus,
.credit-line a,
.credit-line a:hover,
.credit-line a:focus {
  color: inherit;
  text-decoration: none;
}
.site-footer a:focus-visible,
.credit-line a:focus-visible {
  outline: 2px solid currentColor;
  outline-offset: 2px;
}
