/* =========================================================================
   richian — site.css
   Everything shared by every page: webfonts, design tokens, the reset, the
   button, the floating nav (+ its mobile menu), and the reveal system.

   Page-specific styling lives in its own sheet alongside this one
   (home.css, and so on). Load order is always site.css first — the page
   sheet overrides it, never the other way round.
   ========================================================================= */

/* ---- Self-hosted webfonts ------------------------------------------------ */
@font-face {
  font-family: 'Instrument Serif';
  font-style: normal; font-weight: 400; font-display: swap;
  src: url('../fonts/instrument-serif-400-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Instrument Serif';
  font-style: normal; font-weight: 400; font-display: swap;
  src: url('../fonts/instrument-serif-400-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'Inter';
  font-style: normal; font-weight: 100 900; font-display: swap;   /* variable */
  src: url('../fonts/inter-variable-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Inter';
  font-style: normal; font-weight: 100 900; font-display: swap;
  src: url('../fonts/inter-variable-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* ---- Design tokens ------------------------------------------------------- */
:root {
  --scroll-factor: 5;        /* set from JS to match CONFIG (home page only) */
  --seq-bg: #181710;         /* sampled from the frames, so no seams   */
  --brand: #c8441e;          /* matches the dot in the logo SVG        */
  --seq-scrim-h: 62%;        /* mobile: height of the copy scrim       */
  --seq-cream: #f3e8d0;
  --seq-body: #cfcdc6;
  /* Light palette */
  --cream-bg: #f4f2ec;
  --cream-panel: #efebdd;    /* sand-tinted panel                       */
  --ink: #1b1813;            /* headlines                              */
  --ink-body: #55504a;       /* body copy                              */
  --ink-mute: #8b8880;       /* eyebrow, on cream                      */
  --mute-dark: #8a877c;      /* the same job on the dark sections      */
  --sand: #d0c48c;           /* dark sections only; currently unused    */
  --rule: #d6d2c7;           /* hairlines                              */
  --nav-inset: clamp(14px, 2.2vh, 26px);   /* float gap, and its stuck top */
  --nav-h: clamp(68px, 6vw, 82px);

  /* ---- The content column ------------------------------------------------
     Backgrounds bleed to the browser edges; the content inside them caps at
     --content-max and centres. --page-pad is the side padding that does it:
     the old edge inset while the viewport is narrow, then the empty margin
     outside the column once the viewport outgrows it. --page-gutter is that
     margin on its own (0px below the cap), for things positioned against the
     viewport rather than padded — the hero copy and the sequence copy.

     The cap engages at ~1377px: below that 8.6vw is the wider of the two.

     ALWAYS write these WITH their fallback at the use site:
         var(--page-pad, clamp(22px, 8.6vw, 124px))
         var(--page-gutter, 0px)
     A site-wide cap was built once before and reverted, and this is why. The
     token lived here and was used by home.css; python3 -m http.server sends no
     cache headers, so a browser served a stale site.css against a new home.css
     and the token was simply undefined. An undefined var() invalidates the
     WHOLE declaration at computed-value time — `padding` doesn't fall back to
     something sensible, it drops to its initial value of 0 — so every section
     on the page went edge to edge. The fallbacks above degrade to the old
     layout instead. See CLAUDE.md.

     NB the percentage resolves against the element's OWN containing block, so
     these only mean "the viewport" on a full-width element. Don't reach for
     them inside a box that is already padded — that's why .audience moved its
     padding onto its children rather than negative-margining them back out. */
  --content-max: 1140px;
  --page-edge: clamp(22px, 8.6vw, 124px);
  --page-gutter: max(0px, (100% - var(--content-max)) / 2);
  --page-pad: max(var(--page-edge), var(--page-gutter));
  --serif: 'Instrument Serif', Georgia, 'Times New Roman', serif;
  --sans: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
}

/* ---- Reset --------------------------------------------------------------- */
* { box-sizing: border-box; }
html { scroll-behavior: auto; }          /* let ScrollTrigger own the scroll */
body {
  margin: 0;
  background: var(--seq-bg);
  color: #f4f4f6;
  font-family: var(--sans);
  -webkit-font-smoothing: antialiased;
}

/* ---- Button -------------------------------------------------------------- */
/* Pill, on the base class rather than on .btn--primary, so every future
   variant is the right shape by default. It matches .nav__cta exactly, which
   is the shape the page already establishes at the top. .btn--ghost opts back
   out below — see the note there. */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-family: var(--sans);
  font-size: clamp(14px, 1.1vw, 16px);
  line-height: 1;
  text-decoration: none;
  white-space: nowrap;
  cursor: pointer;
  border-radius: 999px;
  transition: background-color .25s ease, color .25s ease, transform .25s ease;
}
.btn--primary {
  background: var(--brand);
  color: #fff;
  padding: clamp(15px, 1.5vw, 19px) clamp(22px, 2.4vw, 34px);
}
.btn--primary:hover { background: #b03a18; }
/* NOT a pill, and this is the one exception on the page. .btn--ghost has no
   fill — it's an underlined text CTA, and its `border-bottom` is the underline.
   A border-radius curves that border up at both ends into a shallow U, which
   reads as a broken box rather than an underline. Radius is reset to 0 rather
   than the rule being narrowed to .btn--primary, so any future filled variant
   still gets the pill for free. If this ever should become a pill it needs a
   real outline (`border: 2px solid`) instead of a bottom edge. */
.btn--ghost {
  color: #fff;
  padding: clamp(15px, 1.5vw, 19px) 4px;
  border-bottom: 2px solid var(--brand);
  border-radius: 0;
}
.btn--ghost:hover { color: var(--brand); }
.btn:active { transform: translateY(1px); }

/* ---- Section heading primitives ------------------------------------------
   Every section opens the same way: a small uppercase eyebrow, then a large
   serif headline whose lead-in phrase is held back in terracotta. Four
   sections carried near-identical copies of both; this is the one definition.
   What genuinely varies stays with the section — margin-bottom, max-width,
   colour, and whether the headline wants text-wrap: balance.

   .close__title is deliberately NOT on .section-title: it's the page's
   crescendo and the largest type on the site, at its own scale and tracking.
   It shares only the em treatment.
   -------------------------------------------------------------------------- */
.eyebrow {
  font-family: var(--sans);
  font-size: clamp(11.5px, .95vw, 13px);
  font-weight: 400;
  letter-spacing: .13em;
  text-transform: uppercase;
  color: var(--ink-mute);
  margin: 0 0 clamp(18px, 2.4vh, 30px);
}

.section-title {
  font-family: var(--serif);
  font-weight: 400;
  font-size: clamp(34px, 4.7vw, 66px);
  line-height: 1.08;
  letter-spacing: -.005em;
  margin: 0;
}
/* Terracotta, never sand — sand goes muddy on cream at headline size. Matches
   the nav's Contact button exactly.
   .close__title and .contact-hero__title are not on .section-title — each is
   its page's crescendo and carries its own scale — but they share this one
   treatment, which is why the selector is grouped rather than duplicated.
   .contact-hero__title is the only one of the three on a DARK background;
   --brand holds up there (it is what .person__name's given name uses on the
   same dark), so it needs no variant. */
.section-title em,
.close__title em,
.contact-hero__title em,
.articles-hero__title em,
.article-hero__title em { font-style: normal; color: var(--brand); }

/* =========================================================================
   SHARED REVEAL
   A section marked .rv gets its [data-reveal] children risen/unblurred and
   its [data-rule] hairlines drawn, each at its own --d. wireReveal() reads
   the SAME --d out of the markup for the GSAP path, so the two can't drift.

   NOTE both attributes are global hidden states — anything carrying one
   starts invisible and MUST sit inside a section wireReveal() runs on.
   ========================================================================= */
/* NB the hidden state is 0.01, not 0, and that hundredth is load-bearing.
   Chrome excludes an element at `opacity: 0` from being a Largest Contentful
   Paint candidate entirely, so the hero's <h1> — which IS this page's LCP
   element, measured — only became eligible once its fade had run, putting LCP
   at 1.9s against a 0.4s first paint. 0.01 is the documented workaround: the
   element is a candidate from its first paint and is still invisible to the
   eye. Anything that re-asserts this state in JS has to match it, or the
   inline style puts 0 back: see wireReveal() below and both fromTo() calls in
   home.js. Read the LCP note in CLAUDE.md before changing it back to 0. */
[data-reveal] {
  opacity: .01;
  filter: blur(14px);
  transform: translateY(26px);
}
[data-rule] {
  display: block;
  height: 1px;
  background: var(--rule);
  transform: scaleX(0);
  transform-origin: left center;
}

/* No-GSAP path: the same choreography as CSS transitions, off the same --d. */
.rv--css [data-reveal],
.rv--css [data-rule] {
  transition: opacity .9s ease,
              filter .9s ease,
              transform .9s cubic-bezier(.22,.7,.3,1);
  transition-delay: var(--d, 0s);
}
.rv--css.is-in [data-reveal] { opacity: 1; filter: blur(0); transform: none; }
.rv--css.is-in [data-rule]   { transform: scaleX(1); }
/* Reduced motion / no IntersectionObserver: no choreography, just show it. */
.rv--static [data-reveal],
.rv--static [data-rule] { opacity: 1 !important; filter: none !important; transform: none !important; }

/* =========================================================================
   FLOATING NAV
   The markup is built by site.js from one definition, so every page carries
   an identical bar. LiquidGlass paints into a canvas it injects as the nav's
   first child at z-index:-1, so the nav must NOT carry an opaque background
   of its own — the values below are the fallback that shows when the library
   or WebGL is unavailable, and they sit behind the glass canvas when it is.
   ========================================================================= */
#glass-root { position: relative; }   /* required by LiquidGlass */

.nav {
  /* Sticky, not fixed: it rides up with the page from wherever the page put
     it in the flow, and locks itself at --nav-inset. The negative bottom
     margin means it occupies no layout space and doesn't push what follows
     down. --nav-h is shared with .nav-menu's offset. */
  position: sticky;
  top: var(--nav-inset);
  z-index: 60;
  /* Content-driven width, centred by auto margins (works with sticky, and
     leaves transform free). max-width keeps it inside narrow viewports. */
  width: max-content;
  max-width: calc(100vw - 2 * var(--nav-inset));
  /* margin-top floats it below the top edge of whatever it sits above while
     it's still riding up — the same gap it settles into once stuck, so
     nothing appears to shift at the handoff. The bottom margin cancels the
     height AND that top gap. */
  margin: var(--nav-inset) auto calc(-1 * (var(--nav-h) + var(--nav-inset)));
  height: var(--nav-h);
  display: flex;
  align-items: center;
  gap: clamp(12px, 1.6vw, 26px);
  padding: 0 clamp(14px, 1.6vw, 22px) 0 clamp(20px, 2.2vw, 32px);
  border-radius: 999px;
  /* Fallback surface — replaced visually by the WebGL glass when it loads. */
  background: rgba(244,242,236,.62);
  backdrop-filter: blur(18px) saturate(1.4);
  -webkit-backdrop-filter: blur(18px) saturate(1.4);
  box-shadow: 0 18px 40px rgba(12,11,8,.28), inset 0 1px 0 rgba(255,255,255,.5);
}
/* The gap between the wordmark and the first link. It's a flex item rather
   than margin-left:auto, because with a content-width bar there's no free
   space for auto to distribute. */
.nav__spacer {
  display: block;
  flex: 0 0 auto;
  width: clamp(28px, 6vw, 96px);
}
/* Once the glass canvas exists the CSS surface would double up, so drop it.
   .is-glass is also removed again while the glass is paused over a
   [data-glass-off] section, which brings the cheap CSS surface back. */
.nav.is-glass {
  background: none;
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
  box-shadow: none;
}

/* ---- The THIRD surface state, and why it exists -------------------------
   Over a [data-glass-off] section the WebGL glass is paused and the CSS
   surface takes over — but it must take over WITHOUT the blur.
   `backdrop-filter` re-snapshots and re-blurs whatever sits behind it every
   time that content changes, and the sections marked [data-glass-off] are
   precisely the three that repaint every frame (the hero video and the two
   scrubbed canvases). Leaving the blur on meant an 18px blur of a
   ~1326×164 device-px strip recomputed on every frame of the entire pinned
   scrub — the expensive work wasn't removed when WebGL was switched off, it
   was just moved to the compositor. Crossing the boundary also created and
   destroyed a backdrop root, which is a hitch of its own.
   So: keep the tint and the shadow, drop the blur. Behind the nav those
   sections are dark and near-uniform at this scale, and the tint is 62%
   opaque, so the blurred backdrop was contributing very little to begin with.
   This is driven straight off [data-glass-off] in syncContrast() rather than
   off the glass controller, so it still applies when LiquidGlass fails to
   load and the CSS surface is all there is. */
.nav--flat,
.nav-menu--flat {
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
}

/* ---- Cross-fade between the two surfaces --------------------------------
   The swap used to be a hard cut: the canvas went display:none in the same
   frame the tint and shadow appeared. The canvas is position:absolute with
   z-index:-1, so fading it costs no layout — but `display` can't transition,
   hence opacity. It stays in the layer tree while hidden, which is cheap:
   _running is false there, so it's a static texture that never repaints. */
.nav { transition: background-color .28s ease, box-shadow .28s ease; }
.nav > canvas,
.nav-menu > canvas { transition: opacity .28s ease; }
.nav:not(.is-glass) > canvas,
.nav-menu:not(.is-glass) > canvas { opacity: 0; }

.nav__logo { display: block; flex: 0 0 auto; line-height: 0; position: relative; }
.nav__logo img {
  display: block;
  width: clamp(112px, 11vw, 152px);
  height: auto;
  transition: opacity .3s ease;
}
/* The light variant stacks exactly on the ink one and cross-fades, so the
   swap has no reflow and no flash of a missing file. */
.nav__logo-light { position: absolute; inset: 0; opacity: 0; }
.nav--dark .nav__logo-ink { opacity: 0; }
.nav--dark .nav__logo-light { opacity: 1; }

.nav__links {
  display: flex;
  align-items: center;
  gap: clamp(14px, 2vw, 34px);
}
.nav__link {
  font-family: var(--sans);
  font-size: clamp(13.5px, 1vw, 15px);
  color: var(--ink);
  text-decoration: none;
  white-space: nowrap;
  padding: 6px 2px;
  border-bottom: 1px solid transparent;
  transition: color .25s ease, border-color .25s ease;
}
.nav__link:hover { color: var(--brand); border-bottom-color: var(--brand); }

.nav__cta {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  font-family: var(--sans);
  font-size: clamp(13.5px, 1vw, 15px);
  line-height: 1;
  color: #fff;
  background: var(--brand);
  text-decoration: none;
  white-space: nowrap;
  padding: clamp(12px, 1.5vh, 16px) clamp(18px, 1.8vw, 28px);
  border-radius: 999px;
  transition: background-color .25s ease, transform .25s ease;
}
.nav__cta:hover { background: #b03a18; }
.nav__cta:active { transform: translateY(1px); }

/* Adaptive contrast — .nav--dark is toggled as the nav crosses a section
   marked [data-nav-dark], so the contents stay legible over both palettes. */
.nav--dark .nav__link { color: var(--seq-cream); }
.nav--dark .nav__link:hover { color: #fff; border-bottom-color: #fff; }
.nav--dark .nav__bar { background: var(--seq-cream); }
.nav:not(.is-glass).nav--dark {
  background: rgba(24,23,16,.52);
  box-shadow: 0 18px 40px rgba(0,0,0,.4), inset 0 1px 0 rgba(255,255,255,.16);
}

/* ---- Mobile menu --------------------------------------------------------- */
.nav__toggle {
  display: none;
  flex: 0 0 auto;
  width: 44px;
  height: 44px;
  margin-left: auto;
  padding: 0;
  border: 0;
  background: none;
  cursor: pointer;
  align-items: center;
  justify-content: center;
  flex-direction: column;
  gap: 6px;
}
.nav__bar {
  display: block;
  width: 22px;
  height: 2px;
  background: var(--ink);
  transition: transform .3s cubic-bezier(.22,.7,.3,1), opacity .2s ease;
}
.nav__toggle[aria-expanded="true"] .nav__bar:first-child { transform: translateY(4px) rotate(45deg); }
.nav__toggle[aria-expanded="true"] .nav__bar:last-child  { transform: translateY(-4px) rotate(-45deg); }

.nav-menu {
  /* Fixed, with its top set from the nav's live position when it opens —
     the nav is sticky, so it can be mid-ride rather than locked at the top. */
  position: fixed;
  top: calc(var(--nav-inset) + var(--nav-h) + 10px);
  left: 50%;
  width: min(340px, calc(100vw - 2 * var(--nav-inset)));
  z-index: 59;
  display: flex;
  flex-direction: column;
  padding: 14px;
  gap: 4px;
  border-radius: 28px;
  background: rgba(244,242,236,.72);
  backdrop-filter: blur(18px) saturate(1.4);
  -webkit-backdrop-filter: blur(18px) saturate(1.4);
  box-shadow: 0 20px 44px rgba(12,11,8,.3);
  opacity: 0;
  transform: translate(-50%, -10px);
  transition: opacity .3s ease, transform .3s cubic-bezier(.22,.7,.3,1);
}
.nav-menu[hidden] { display: none; }
.nav-menu.is-open { opacity: 1; transform: translate(-50%, 0); }
.nav-menu.is-glass { background: none; backdrop-filter: none; -webkit-backdrop-filter: none; box-shadow: none; }
.nav-menu__link {
  font-family: var(--sans);
  font-size: 16px;
  color: var(--ink);
  text-decoration: none;
  padding: 15px 16px;
  border-radius: 18px;
  transition: background-color .2s ease;
}
.nav-menu__link:hover { background: rgba(27,24,19,.06); }
/* The one filled button in the menu, so it's a pill like every other button.
   Its plain siblings keep the 18px radius above — they're navigation rows with
   a hover highlight, not buttons, and at rest they have no shape at all. */
.nav-menu__link--cta {
  margin-top: 4px;
  color: #fff;
  background: var(--brand);
  text-align: center;
  border-radius: 999px;
}
.nav-menu__link--cta:hover { background: #b03a18; }

@media (max-width: 700px) {
  .nav__links, .nav__cta { display: none; }
  .nav__toggle { display: flex; }
}
@media (min-width: 701px) {
  .nav-menu { display: none !important; }
}
/* The nav is decorative chrome over a moving background; if the visitor has
   asked for less motion, show it immediately with no slide. */
@media (prefers-reduced-motion: reduce) {
  .nav, .nav-menu { transition: none; }
  /* The surface cross-fade is motion too — cut it with everything else. */
  .nav > canvas, .nav-menu > canvas { transition: none; }
}

/* =========================================================================
   FOOTER
   Shared styling, per-page markup: every page writes its own <footer
   class="footer"> so a future page can vary its links, but the look lives
   here once. It carries data-nav-dark like any other dark section.

   Deliberately has NO [data-reveal] children — the footer is static by
   design, the only section on the page that doesn't perform. Note that means
   it must never be given the attribute casually: [data-reveal] is a global
   hidden state and nothing here would ever reveal it.
   ========================================================================= */
.footer {
  position: relative;
  z-index: 1;
  background: var(--seq-bg);
  color: var(--cream-bg);
  padding-top: clamp(64px, 10vh, 120px);
  overflow: hidden;              /* this is what clips the wordmark */
}

.footer__cols {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: clamp(30px, 3.4vw, 48px);
  padding-inline: var(--page-pad, clamp(22px, 8.6vw, 124px));
}

.footer__head {
  font-family: var(--sans);
  font-size: clamp(16px, 1.3vw, 18px);
  font-weight: 600;
  line-height: 1.2;
  color: var(--brand);
  margin: 0 0 clamp(12px, 1.6vh, 18px);
}

.footer__list { list-style: none; margin: 0; padding: 0; }
.footer__list li + li { margin-top: clamp(6px, .9vh, 10px); }

.footer__link {
  font-family: var(--sans);
  font-size: clamp(14.5px, 1.1vw, 16px);
  line-height: 1.5;
  color: var(--cream-bg);
  text-decoration: none;
  transition: color .2s ease;
}
.footer__link:hover { color: var(--brand); }

/* ---- The base: giant wordmark, and the copyright beside it -------------- */
/* Two columns on desktop: the wordmark takes the remaining width and the
   copyright sits in a reserved right gutter, level with the dot, as drawn.
   max-width caps the whole row on very wide screens and centres it, so a 4K
   display doesn't get a 2000px-tall wordmark.

   This row is deliberately NOT on --page-pad, unlike .footer__cols above it.
   The wordmark is decoration and bleeds like a section background; the
   copyright can't be pulled in to the content column on its own because it
   shares the grid with the wordmark, so moving it inward shrinks the wordmark
   with it (~21% smaller at 1920). The author's call: leave the row alone and
   cap only the links. The copyright is the one piece of the page not on the
   1140 edge. */
.footer__base {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: end;
  column-gap: clamp(14px, 2vw, 36px);
  max-width: 1800px;
  margin: clamp(56px, 9vh, 116px) auto 0;
}

/* Placed explicitly rather than by source order: the copyright is FIRST in the
   DOM so it leads on mobile and reads before a decorative image, but on
   desktop it belongs in the right-hand gutter. */
.footer__copy {
  grid-column: 2;
  grid-row: 1;
  font-family: var(--sans);
  font-size: clamp(12.5px, 1vw, 14px);
  line-height: 1.4;
  color: var(--cream-bg);
  white-space: nowrap;
  margin: 0;
  padding: 0 clamp(16px, 2.2vw, 32px) clamp(10px, 1.6vh, 22px) 0;
}

/* The wordmark is an <img> of the nav's cropped light SVG (wordmark + dot, no
   tagline) rather than the master logo, and it's vector so it stays sharp at
   any size. The clip is done with aspect-ratio, not a height calculation: the
   artwork is 341.6 × 75.1, so a box at 341.6 / (75.1 × .92) shows 92% of the
   image's own height and overflow: hidden takes the rest off the bottom.
   NB the depth of that clip is load-bearing: the logo's dot sits ON the
   baseline, so it's the first thing to go. At .8 the dot was reduced to a
   sliver and read as a stray mark; .92 keeps it square, as the mockup has it,
   while still cutting the glyphs. Check the dot before changing this. */
.footer__mark {
  grid-column: 1;
  grid-row: 1;
  overflow: hidden;
  aspect-ratio: 4.944;
  align-self: end;
}
.footer__mark img {
  display: block;
  width: 100%;
  height: auto;
}

@media (max-width: 860px) {
  /* One centred column, and the copyright moves above the wordmark, which
     then goes properly full width. */
  .footer__cols {
    grid-template-columns: 1fr;
    gap: clamp(34px, 5.4vh, 56px);
    text-align: center;
  }
  .footer__base { grid-template-columns: 1fr; }
  .footer__copy {
    grid-column: 1;
    grid-row: 1;
    text-align: center;
    padding: 0 0 clamp(20px, 3.4vh, 34px);
  }
  .footer__mark { grid-column: 1; grid-row: 2; }
}
