/* ============================================================================
   TisuMazwi — PR, Publishing & Communications in Africa
   Visual direction: dark mode, brand-led. Near-black ground, bone-ivory text,
   brand orange (#F79521) as the primary accent, brand indigo (#2C3890) as a
   surface. Familjen Grotesk (display) + Inter (body) + JetBrains Mono
   (captions). No editorial italics.

   WHY ORANGE ON DARK, AND INDIGO ONLY AS A SURFACE
   The two brand colours want opposite grounds, and the contrast maths is not
   close:

     orange #F79521 on near-black  7.97:1  AAA   <- primary accent lives here
     orange #F79521 on white       2.26:1  FAIL  <- never orange type on light
     indigo #2C3890 on near-black  1.78:1  FAIL  <- never indigo type on dark
     indigo #2C3890 on white      10.12:1  AAA

   So on this dark ground orange carries every accent, and indigo can never be
   text — it earns its place as a section ground instead (see --indigo-deep).
   Text sitting ON an orange fill must be near-black (7.97:1 AAA); white on
   orange is 2.26:1 and always fails.
   ============================================================================ */

/* ---------- 1. Tokens ---------------------------------------------------- */
:root {
  /* Neutral ground */
  --ink-deep:      #0F0F10;        /* primary surface */
  --ink:           #16161A;        /* default body bg */
  --ink-card:      #1B1B1F;        /* elevated cards */
  --ink-elev:      #222226;        /* hover / further elevation */
  --ink-line:      rgba(232, 228, 214, 0.10);
  --ink-line-soft: rgba(232, 228, 214, 0.06);
  --ink-rule:      rgba(232, 228, 214, 0.18);

  --ivory:         #E8E4D6;        /* primary text   — 14.18:1 on --ink (AAA) */
  --ivory-soft:    #BFB9A5;        /* secondary text —  9.19:1 on --ink (AAA) */
  --ivory-mute:    #9A937C;        /* tertiary / captions — 5.88:1 on --ink,
                                      5.16:1 on --ink-elev (worst ground). Was
                                      #807A66, which failed WCAG AA at 4.20:1. */
  --ivory-deep:    #5A5546;        /* hairlines / quiet — decorative only,
                                      never used for text */

  /* Brand orange — PRIMARY accent. Brand value, used as drawn. */
  --orange:        #F79521;        /* 7.97:1 on --ink, 7.58:1 on --ink-card (AAA) */
  --orange-soft:   #FFB258;        /* hover — 10.11:1 on --ink (AAA) */
  --orange-deep:   #B4610C;        /* deep / pressed. Not for text on dark. */
  --orange-glow:   rgba(247, 149, 33, 0.18);

  /* Brand indigo — SURFACE ONLY. Never type on a dark ground (1.78:1). */
  --indigo:        #2C3890;        /* brand value. Fills and logo contexts. */
  --indigo-deep:   #141833;        /* section ground. On it: orange 7.68:1 AAA,
                                      ivory 13.66:1 AAA, ivory-mute 5.66:1 AA.
                                      Raw #2C3890 as a ground fails — orange is
                                      only 4.47:1 on it, and mute 3.29:1. */
  --indigo-card:   #1B2154;        /* cards sitting on --indigo-deep */
  --indigo-line:   rgba(232, 228, 214, 0.12);

  --wine:          #5B1A1F;        /* tiny secondary — oxblood for variety only */

  /* Semantic surfaces */
  --bg:            var(--ink);
  --bg-alt:        var(--ink-deep);
  --bg-card:       var(--ink-card);
  --bg-elev:       var(--ink-elev);
  --bg-warm:       #1F1815;         /* slightly warm dark, for rhythm sections */

  --fg:            var(--ivory);
  --fg-soft:       var(--ivory-soft);
  --fg-mute:       var(--ivory-mute);
  --rule:          var(--ink-line);
  --rule-strong:   var(--ink-rule);
  --rule-accent:   rgba(247, 149, 33, 0.36);

  --primary:       var(--orange);
  --primary-deep:  var(--orange-deep);
  --accent:        var(--orange);

  /* Typography — Boldonse (display) + Inter (body) + JetBrains Mono.
     No serif in this template by design. */
  /* H1 and H2 only. Anton ships ONE weight (400) — never ask for 700 here or
     the browser synthesises a faux bold and smears the letterforms. It is
     already heavy at 400. Fallbacks are condensed faces so a failed webfont
     load degrades in roughly the right shape rather than to a wide grotesque. */
  --font-heading:  'Anton', 'Arial Narrow', Impact, system-ui, sans-serif;
  /* H3–H5, ledes, pull-quotes, numerals, prices. */
  --font-display:  'Familjen Grotesk', 'Inter Tight', system-ui, sans-serif;
  --font-body:     'Inter', system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;
  --font-mono:     'JetBrains Mono', ui-monospace, 'SF Mono', Menlo, Consolas, monospace;

  --text-xs:    0.75rem;
  --text-sm:    0.875rem;
  --text-base:  clamp(1rem, 0.94rem + 0.28vw, 1.0625rem);
  --text-lg:    clamp(1.125rem, 1rem + 0.45vw, 1.25rem);
  --text-xl:    clamp(1.25rem, 1.1rem + 0.6vw, 1.5rem);
  --text-2xl:   clamp(1.625rem, 1.35rem + 1vw, 2.125rem);
  --text-3xl:   clamp(2.25rem, 1.7rem + 2.4vw, 3.5rem);
  --text-4xl:   clamp(3rem, 2.1rem + 3.8vw, 4.5rem);
  --text-5xl:   clamp(3.5rem, 2.4rem + 4.6vw, 5.5rem);
  --text-mega:  clamp(2.75rem, 1.8rem + 3.2vw, 5.25rem);

  /* Spacing */
  --space-1:  0.25rem;
  --space-2:  0.5rem;
  --space-3:  0.75rem;
  --space-4:  1rem;
  --space-5:  1.5rem;
  --space-6:  2rem;
  --space-7:  3rem;
  --space-8:  4.5rem;
  --space-9:  6.5rem;
  --space-10: 9rem;
  --space-11: 12rem;

  /* Square by design. Every container, card, panel, image frame and form field
     has hard corners; only buttons stay rounded (see .btn). The scale is kept
     as named tokens rather than deleted so the decision lives in one place —
     set these back to 4/8/12/18/24/36px to restore rounded corners site-wide. */
  --radius-xs: 0;
  --radius-sm: 0;
  --radius:    0;
  --radius-lg: 0;
  --radius-xl: 0;
  --radius-2xl: 0;
  --radius-full: 0;

  --container: 1340px;
  --container-wide: 1480px;
  --container-narrow: 1080px;
  /* Side gutter for ALL content, header and footer included — every region
     routes through .container, so this token is the single source of truth.
     Ramps to a hard 100px and holds there from ~1370px up.
     A flat 100px at every width is not viable: on a 360px phone it would
     leave 160px of content. So it tapers below desktop —
       360px  -> ~29px      1024px -> ~76px
       768px  -> ~58px      1370px+ -> 100px (capped) */
  --gutter:    clamp(1.25rem, 0.25rem + 7vw, 100px);

  /* Height of the sticky header. Drives scroll-padding-top so an anchor link
     (#enquiry, #packages, the category chips) lands below the bar instead of
     underneath it — without this the heading you jumped to is the one thing
     you cannot see.

     A measured CEILING, not the exact height: the row is 18px of padding
     around the tallest of the logo, the menu toggle and the "Get in touch"
     button, and that button's padding is fluid, so the header is 79px at
     375px and 81.4px from 640px up. Erring high costs a couple of pixels of
     gap above an anchored heading; erring low hides it. Re-measure if the
     header's contents change. */
  --header-h: 82px;

  --shadow-xs: 0 1px 2px rgba(0, 0, 0, 0.4);
  --shadow-sm: 0 2px 8px rgba(0, 0, 0, 0.35);
  --shadow:    0 12px 32px -10px rgba(0, 0, 0, 0.55);
  --shadow-md: 0 24px 48px -16px rgba(0, 0, 0, 0.55);
  --shadow-lg: 0 42px 88px -28px rgba(0, 0, 0, 0.6);
  --shadow-accent: 0 22px 60px -22px rgba(247, 149, 33, 0.35);

  --ease-out:    cubic-bezier(0.16, 1, 0.3, 1);
  --ease-in-out: cubic-bezier(0.65, 0, 0.35, 1);
  --dur-fast:   180ms;
  --dur-base:   320ms;
  --dur-slow:   620ms;
}

/* ---------- 2. Reset / base --------------------------------------------- */
*, *::before, *::after { box-sizing: border-box; }
/* No scroll-behavior: smooth — anchor links jump straight there. Animated
   scrolling is a scroll effect too, and it is the one that makes a long page
   feel like it is moving on its own.
   scroll-padding-top offsets those jumps by the sticky header's height, so the
   target sits below the bar rather than behind it. */
html {
  -webkit-text-size-adjust: 100%;
  background: var(--bg);
  scroll-padding-top: calc(var(--header-h) + var(--space-4));
}
body {
  margin: 0;
  background: var(--bg);
  color: var(--fg);
  font-family: var(--font-body);
  font-size: var(--text-base);
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-rendering: optimizeLegibility;
}
img, svg, video { display: block; max-width: 100%; height: auto; }
a { color: inherit; text-decoration: none; transition: color var(--dur-fast) var(--ease-out); }
a:hover { color: var(--orange); }
button { font: inherit; border: 0; background: transparent; cursor: pointer; color: inherit; }
hr { border: 0; border-top: 1px solid var(--rule); margin: var(--space-7) 0; }
hr.accent { border-top: 1px solid var(--rule-accent); }
::selection { background: var(--orange); color: var(--ink-deep); }

/* No editorial italics — em/i and .accent render as the orange accent. */
em, i, .accent {
  font-style: normal;
  color: var(--orange);
  font-weight: 600;
}

.skip-link {
  position: absolute; left: -9999px; top: -9999px;
  background: var(--orange); color: var(--ink-deep);
  padding: 12px 18px; border-radius: 0; z-index: 999;
  font-family: var(--font-mono); font-size: var(--text-xs); letter-spacing: 0.06em; text-transform: uppercase;
}
.skip-link:focus { left: 16px; top: 16px; outline: 2px solid var(--ivory); outline-offset: 3px; }

/* ---------- 3. Typography ----------------------------------------------- */
h1, h2, h3, h4, h5 {
  font-family: var(--font-display);
  font-weight: 600;
  line-height: 1.2;
  letter-spacing: -0.025em;
  color: var(--fg);
  margin: 0;
}
/* H1 + H2 take the Anton display face. Weight is pinned to 400 (its only cut)
   and the tracking is relaxed from the -0.035em the old grotesque needed —
   Anton is condensed by design and turns muddy when pulled tighter. */
h1, h2 {
  font-family: var(--font-heading);
  font-weight: 400;
  letter-spacing: -0.005em;
}
h1 { font-size: var(--text-4xl); line-height: 1.02; }
h2 { font-size: var(--text-3xl); line-height: 1.06; }
/* Accent words inside a heading inherit the heading's weight. Without this the
   `em / .accent` rule sets 600, which Anton has to fake — the orange words
   would render visibly heavier and blurrier than the ivory ones beside them. */
h1 em, h1 i, h1 .accent, h1 .serif-em,
h2 em, h2 i, h2 .accent, h2 .serif-em {
  font-weight: inherit;
}
h3 { font-size: var(--text-2xl); line-height: 1.18; letter-spacing: -0.025em; font-weight: 700; }
h4 { font-size: var(--text-xl); line-height: 1.25; letter-spacing: -0.015em; font-weight: 600; }
/* Card headings are <h3> for document rank — they sit under a section <h2>, so
   an <h4> there would skip a level and break the outline for screen readers
   and crawlers alike. This class restores the smaller visual size that the
   rank no longer implies. Size and rank are separate concerns. */
h3.card-title {
  font-size: var(--text-xl);
  line-height: 1.25;
  letter-spacing: -0.015em;
  font-weight: 600;
}
h5 { font-size: var(--text-lg); line-height: 1.3; font-family: var(--font-body); font-weight: 600; letter-spacing: -0.005em; }
p  { margin: 0 0 1em; color: var(--fg-soft); }

.accent { color: var(--orange); }
.mono,
.mono-cap {
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ivory-mute);
  font-weight: 500;
}
.mono-num {
  font-family: var(--font-mono);
  font-weight: 500;
  font-feature-settings: 'tnum';
}

.eyebrow {
  display: inline-flex; align-items: center; gap: 10px;
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--orange);
  font-weight: 500;
}
.eyebrow .dot {
  width: 6px; height: 6px; border-radius: 0;
  background: var(--orange);
  display: inline-block;
}
.eyebrow.on-light { color: var(--orange-deep); }
.eyebrow.on-light .dot { background: var(--orange-deep); }

.text-accent { color: var(--orange); }
.text-ivory { color: var(--ivory); }
.text-mute  { color: var(--ivory-mute); }
.text-balance { text-wrap: balance; }

/* ---------- 4. Layout primitives ---------------------------------------- */
.container {
  width: 100%;
  max-width: var(--container);
  margin: 0 auto;
  padding-left: var(--gutter);
  padding-right: var(--gutter);
  position: relative;
}
.container--wide   { max-width: var(--container-wide); }
.container--narrow { max-width: var(--container-narrow); }

.section { padding: var(--space-9) 0; position: relative; background: var(--bg); }
.section--tight { padding: var(--space-7) 0; }
.section--deep  { padding: var(--space-10) 0; }
.section--ink   { background: var(--ink-deep); }
.section--bone,
.section--paper,
.section--soft,
.section--warm  { background: var(--ink); }
.section--card  { background: var(--ink-card); }
.section--elev  { background: var(--ink-elev); }
.section--warm-dark { background: var(--bg-warm); }

/* Brand indigo section ground. Indigo can't be text on dark (1.78:1), so this
   is how the second brand colour earns its place on the page. Verified on
   --indigo-deep #141833: orange 7.68:1 AAA, ivory 13.66:1 AAA, mute 5.66:1 AA.
   Surfaces sitting on it lift to --indigo-card so cards stay legible. */
.section--indigo { background: var(--indigo-deep); }
.section--indigo .service-card,
.section--indigo .mvv-card,
.section--indigo .audience-card,
.section--indigo .price-card,
.section--indigo .brick-item,
.section--indigo .partner-item,
.section--indigo .client-group,
.section--indigo .testimonial,
.section--indigo .book-card .cover,
.section--indigo .booking-card,
.section--indigo .media {
  background-color: var(--indigo-card);
  border-color: var(--indigo-line);
}
.section--indigo .price-card.is-featured,
.section--indigo .partner-item,
.section--indigo .testimonial { border-color: var(--rule-accent); }
.section--indigo .booking-card input,
.section--indigo .booking-card select,
.section--indigo .booking-card textarea { background: var(--indigo-deep); }
.section--indigo .why-item { border-top-color: var(--indigo-line); }
.section--indigo .spec-table th,
.section--indigo .spec-table td { border-bottom-color: var(--indigo-line); }
.section--night { background: var(--ink-deep); }

.section-head {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-4);
  margin-bottom: var(--space-7);
  align-items: end;
}
@media (min-width: 900px) {
  .section-head { grid-template-columns: minmax(0, 1.4fr) minmax(0, 1fr); gap: var(--space-7); }
  .section-head p { max-width: 46ch; justify-self: end; }
}
.section-head h2 { margin-top: 14px; }
.section-head p { color: var(--fg-soft); }

.rule-row {
  display: flex; align-items: center; gap: 16px;
  color: var(--ivory-mute);
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  letter-spacing: 0.16em;
  text-transform: uppercase;
}
.rule-row::after {
  content: ''; flex: 1; height: 1px; background: var(--rule-strong);
}
.rule-row.accent,
.rule-row.orange {
  color: var(--orange);
}
.rule-row.accent::after,
.rule-row.orange::after { background: var(--rule-accent); }

/* Brutalist numeric markers */
.big-num {
  font-family: var(--font-display);
  font-size: clamp(3.5rem, 2.5rem + 4vw, 5.5rem);
  line-height: 1.05;
  letter-spacing: -0.025em;
  color: var(--orange);
}

/* ---------- 5. Buttons -------------------------------------------------- */
/* Buttons are the ONLY rounded thing on the site — the pill shape is what
   marks something as clickable now that every container is square. The three
   rules carrying a radius are .btn, .nav-toggle and .drawer-close (both
   <button> elements). Links, chips, tags and form fields are square: they are
   not buttons, and rounding them would blur that signal. */
/* Sizing is fluid, not stepped. The type scale was already clamped while the
   padding stayed at fixed pixels, so a button kept its full desktop box on a
   375px screen and read as enormous — two of them ate a whole hero. Every
   dimension below (padding, gap, font) now interpolates with the viewport, so
   a button shrinks continuously instead of holding one size and then jumping
   at a breakpoint. The clamp ceilings are the previous fixed values, so
   nothing changes from ~1200px up. */
.btn {
  display: inline-flex;
  align-items: center;
  gap: clamp(7px, 0.3rem + 0.5vw, 10px);
  padding: clamp(10px, 0.5rem + 0.55vw, 14px) clamp(16px, 0.55rem + 2vw, 24px);
  border-radius: 999px;
  font-family: var(--font-body);
  font-weight: 500;
  font-size: clamp(0.9375rem, 0.91rem + 0.14vw, 1.0625rem);
  letter-spacing: -0.005em;
  cursor: pointer;
  transition: background var(--dur-fast) var(--ease-out),
              color var(--dur-fast) var(--ease-out),
              border-color var(--dur-fast) var(--ease-out),
              transform var(--dur-fast) var(--ease-out);
  border: 1.5px solid transparent;
  white-space: nowrap;
  max-width: 100%;
}
/* nowrap keeps buttons tidy at desktop, but a long label ("Request an
   Integrated Communication Audit") cannot then fit a 360px screen and pushes
   the page sideways. Let labels wrap on small viewports. */
@media (max-width: 560px) {
  .btn { white-space: normal; text-wrap: balance; }
}
/* A CTA pair that no longer fits one line stacks — but stacks deliberately:
   equal full-width buttons read as a designed pair, where a plain flex wrap
   leaves two ragged, mismatched widths.

   The trigger is the COLUMN's width, not the viewport's, and that distinction
   is the whole point. The pages break at different widths because the labels
   differ — "Request a monitoring brief / See what's included" needs 608px,
   "Start my journey / Discover more" only 463px — so no viewport breakpoint
   can be right for all eleven heroes at once. Worse, the hero column narrows
   when .hero-grid splits, so the same pair could fit at 1024, break at 1100
   and fit again at 1440. A container query asks the only question that
   actually matters: is there room here?

   :not(:only-child) leaves the single-button heroes (contact, services, team)
   at their natural width — one 600px-wide button is not a designed pair. */
.hero-cta-row { container-type: inline-size; container-name: cta; }
@container cta (max-width: 620px) {
  .hero-cta-row .btn:not(:only-child) { flex: 1 1 100%; justify-content: center; }
}
/* Pre-2023 browsers with no container query support: fall back to the viewport
   threshold that covers the widest pair. */
@supports not (container-type: inline-size) {
  @media (max-width: 700px) {
    .hero-cta-row .btn:not(:only-child) { flex: 1 1 100%; justify-content: center; }
  }
}
.btn--primary {
  background: var(--orange);
  color: var(--ink-deep);
  border-color: var(--orange);
}
.btn--primary:hover { background: var(--orange-soft); border-color: var(--orange-soft); color: var(--ink-deep); }
.btn--ink {
  background: var(--ink-deep);
  color: var(--ivory);
  border-color: var(--ivory);
}
.btn--ink:hover { background: var(--orange); border-color: var(--orange); color: var(--ink-deep); }
.btn--ghost {
  background: transparent;
  border-color: var(--rule-strong);
  color: var(--ivory);
}
.btn--ghost:hover { background: var(--ivory); color: var(--ink-deep); border-color: var(--ivory); }
.btn--outline-accent,
.btn--outline-orange {
  background: transparent;
  border-color: var(--orange);
  color: var(--orange);
}
.btn--outline-accent:hover,
.btn--outline-orange:hover { background: var(--orange); color: var(--ink-deep); }
.btn--bone,
.btn--cream,
.btn--orange {
  background: var(--orange);
  color: var(--ink-deep);
  border-color: var(--orange);
}
.btn--bone:hover,
.btn--cream:hover,
.btn--orange:hover { background: var(--orange-soft); border-color: var(--orange-soft); }
.btn--lg {
  padding: clamp(12px, 0.5rem + 1.05vw, 18px) clamp(20px, 0.6rem + 2.9vw, 32px);
  font-size: clamp(1rem, 0.96rem + 0.28vw, 1.25rem);
}
/* --sm is the header CTA, which is hidden below 520px. Its floor is its
   current size — shrinking it further would put the tap target under 40px. */
.btn--sm {
  padding: 10px clamp(14px, 0.5rem + 1.3vw, 18px);
  font-size: var(--text-sm);
}

.btn .arrow { transition: transform var(--dur-fast) var(--ease-out); }
.btn:hover .arrow { transform: translateX(3px); }

.link-arrow {
  display: inline-flex; align-items: center; gap: 8px;
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--orange);
  padding-bottom: 3px;
  border-bottom: 1px solid var(--rule-accent);
  font-weight: 500;
  transition: color var(--dur-fast) var(--ease-out), border-color var(--dur-fast) var(--ease-out);
}
.link-arrow:hover { color: var(--orange-soft); border-color: var(--orange); }

/* ---------- 6. Header / nav -------------------------------------------- */
/* The header is the deliberate exception to "nothing is pinned" — it stays put
   while the page scrolls, so the nav is always reachable on the long pages.
   Everything else that used to hold still (the editorial aside, the reveal
   animations) does not.
   The translucent fill and backdrop-filter belong with sticky: content passes
   underneath, and blurring it is what stops the nav labels colliding with
   whatever is behind them. --header-h feeds scroll-padding-top so anchor links
   do not land underneath this bar. */
.site-header {
  position: sticky; top: 0; z-index: 50;
  background: rgba(15, 15, 16, 0.78);
  backdrop-filter: saturate(140%) blur(18px);
  -webkit-backdrop-filter: saturate(140%) blur(18px);
  border-bottom: 1px solid var(--ink-line);
}
.nav {
  display: grid;
  grid-template-columns: auto 1fr auto;
  align-items: center;
  gap: var(--space-5);
  padding: 18px 0;
}
/* Below the desktop breakpoint the header carries only two things — the logo
   and the toggle — and they belong at the edges of the screen, not inside the
   body text gutter (~30px at 375px, which left both floating inboard). This is
   the one place the header deliberately breaks alignment with the content
   below it. 16px is the tightest inset that keeps the 42px toggle circle off
   the bezel; take it to 0 for a true full-bleed. */
@media (max-width: 999px) {
  .site-header .container {
    padding-left: var(--space-4);
    padding-right: var(--space-4);
  }
}
.brand {
  display: inline-flex;
  align-items: center;
  flex: none;
}

/* The TisuMazwi wordmark, reversed for dark grounds: the indigo letterforms
   are recoloured to white (18.4:1 here), the orange M-apex is left as drawn.
   Source of truth for both variants:
     assets/img/wordmark-logo-reversed.svg  — white + orange, for dark grounds
     assets/img/wordmark-logo.svg           — indigo + orange, for light grounds
   The indigo original reads 1.78:1 on this header — never use it here.
   Intrinsic box is 250x60 (aspect 4.167); height drives, width follows. */
.brand-logo {
  height: 34px;
  width: auto;
  display: block;
}
@media (max-width: 640px) { .brand-logo { height: 28px; } }
.footer-brand .brand-logo { height: 42px; }

.nav-links {
  display: none;
  justify-self: center;
  /* Fluid, not a flat 2rem. With Home added the row is eight links, and at a
     flat 32px gap they ran straight through the "Get in touch" button between
     900 and ~1080px — the middle grid track is 1fr, so the links overflow it
     rather than compressing. The clamp buys back ~56px across the seven gaps
     where it is needed and relaxes to the full 32px on wide screens. */
  gap: clamp(24px, 0.6rem + 1.2vw, 32px);
  font-size: var(--text-sm);
  font-weight: 400;
  letter-spacing: -0.005em;
}
.nav-links a {
  color: var(--ivory-soft);
  padding: 8px 4px;
  position: relative;
}
.nav-links a:hover { color: var(--orange); }
.nav-links a[aria-current="page"] { color: var(--ivory); }
.nav-links a[aria-current="page"]::after {
  content: '';
  position: absolute; left: 4px; right: 4px; bottom: 0;
  height: 1px; background: var(--orange);
}
@media (min-width: 1000px) {
  .nav-links { display: inline-flex; }
}

/* grid-column: 3 is load-bearing, not decoration. .nav-links is display:none
   below 900px, which removes it from the grid entirely — so with only two
   items left, auto-placement dropped this row into column 2 (the 1fr track)
   and it sat bunched up against the logo instead of at the right edge.
   Pinning it to the last column keeps the empty 1fr between the two, so the
   logo and the toggle stay justified apart at every width. */
.nav-cta-row {
  display: inline-flex;
  align-items: center;
  gap: 12px;
  min-width: 0;
  grid-column: 3;
  justify-self: end;
}
/* Below 520px the brand + CTA + toggle no longer fit inside the gutter, and the
   row pushes past the right edge. Drop the CTA button — it is redundant there:
   the drawer carries Contact, and every page opens with the same call to
   action in the hero. The toggle stays. */
@media (max-width: 520px) {
  .nav-cta-row .btn { display: none; }
}
.nav-toggle {
  display: inline-grid; place-items: center;
  width: 42px; height: 42px;
  border-radius: 999px;
  background: var(--orange);
  color: var(--ink-deep);
}
.nav-toggle span,
.nav-toggle span::before,
.nav-toggle span::after {
  content: ''; display: block;
  width: 18px; height: 1.5px; background: currentColor;
  position: relative;
}
.nav-toggle span::before { position: absolute; top: -6px; left: 0; width: 18px; }
.nav-toggle span::after  { position: absolute; top: 6px;  left: 0; width: 18px; }
@media (min-width: 1000px) { .nav-toggle { display: none; } }

/* Sized against viewport HEIGHT, not width, because height is what runs out.
   At a flat 96px padding / 2rem type / 24px gap the menu stood 674px tall, so
   on any phone shorter than that the last link sat below the fold — and the
   drawer is a fixed overlay with nothing to scroll, so "Contact" was not
   merely off-screen, it was unreachable. That was already true of the seven
   original links on a 320x568; adding Home made it true of a 375x667 too.

   The clamps let it fit down to 320x480 without scrolling while keeping the
   full 2rem display type on a normal modern phone (>=844px tall).
   overflow-y is the guarantee behind them: whatever the clamps do not
   anticipate — huge OS text sizes, landscape on a small phone — the menu can
   still be scrolled to reach the last link. overscroll-behavior stops that
   scroll chaining through to the page underneath. */
.mobile-drawer {
  position: fixed; inset: 0;
  background: var(--ink-deep);
  color: var(--ivory);
  z-index: 100;
  /* Top padding reserves the band the absolutely-positioned close button sits
     in; the link list starts below it. */
  padding: clamp(64px, 10vh, 88px) var(--gutter) var(--space-6);
  display: flex; flex-direction: column;
  font-family: var(--font-display);
  font-size: clamp(1.375rem, 3.8vh, 2rem);
  letter-spacing: -0.025em;
  /* The drawer itself does NOT scroll — .drawer-links does. Keeping the scroll
     container one level in is what lets the close button stay put and keep a
     flat 24px from the screen edge: it is outside the scrolling box, so a
     visible scrollbar cannot narrow the box its position is measured against. */
  overflow: hidden;
  transform: translateY(-100%);
  /* visibility, not just transform. A transform-only hide leaves the drawer's
     links in the tab order, so a keyboard user tabs into an invisible menu —
     and aria-hidden="true" over focusable elements is an ARIA violation.
     visibility:hidden removes them from the tab order; the delayed transition
     keeps it visible for the length of the slide-out so the animation still
     plays on close. */
  visibility: hidden;
  transition: transform var(--dur-base) var(--ease-in-out),
              visibility 0s linear var(--dur-base);
}
/* The scrolling region. min-height:0 is what makes it able to scroll at all —
   a flex item's default min-height:auto refuses to shrink below its content,
   so the list would push the drawer taller instead of overflowing inside it.
   The scrollbar is left visible: it is the honest affordance that there are
   more links below, and because it lives in here it cannot move the close
   button. */
.drawer-links {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
  display: flex;
  flex-direction: column;
  gap: clamp(10px, 2.2vh, 24px);
}
.mobile-drawer[aria-hidden="false"] {
  transform: translateY(0);
  visibility: visible;
  transition: transform var(--dur-base) var(--ease-in-out),
              visibility 0s linear 0s;
}
.mobile-drawer a { color: var(--ivory); }
.mobile-drawer a:hover { color: var(--orange); }
/* Current page. Set by main.js, not baked into the markup — the header is one
   shared component and its HTML is identical on every page. The desktop nav's
   1px underline does not read under 2rem display type, so the drawer marks the
   current page with a leading orange rule instead. Orange on --ink-deep is
   AAA. */
.mobile-drawer a[aria-current="page"] { color: var(--orange); }
.mobile-drawer a[aria-current="page"]::before {
  content: '';
  display: inline-block;
  width: 26px;
  height: 2px;
  margin-right: 14px;
  vertical-align: middle;
  background: var(--orange);
}
/* Absolute against the drawer, and crucially OUTSIDE .drawer-links. It is not
   in the scrolling box, so it cannot scroll away, and its 24px is measured
   against the drawer's own padding box rather than a content box that a
   visible scrollbar has narrowed by 15px. That is what lets the scrollbar stay
   visible and the button stay at a flat 24px at every viewport size. */
.drawer-close {
  position: absolute;
  top: 24px;
  right: 24px;
  z-index: 1;
  color: var(--ivory);
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  padding: 8px 14px;
  border: 1px solid var(--orange);
  border-radius: 999px;
}

/* ---------- 7. Hero ----------------------------------------------------- */
.hero {
  padding: var(--space-7) 0 var(--space-8);
  background: var(--ink-deep);
  position: relative;
  overflow: hidden;
}
.hero::before {
  content: '';
  position: absolute; right: -10%; top: -20%;
  width: 50%; aspect-ratio: 1;
  background: radial-gradient(circle, var(--orange-glow) 0%, transparent 65%);
  pointer-events: none;
}
.hero::after {
  content: '';
  position: absolute; left: -15%; bottom: -20%;
  width: 50%; aspect-ratio: 1;
  background: radial-gradient(circle, rgba(44, 56, 144, 0.30) 0%, transparent 65%);
  pointer-events: none;
}
.hero .container { position: relative; }
.hero-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-6);
  align-items: center;
}
/* 1100, not 980. Splitting at 980 made the hero text column NARROWER than the
   single-column layout it replaced — 640px became 404px — so the page got
   worse as the screen got bigger, and the CTA pair wrapped raggedly between
   980 and ~1080 while sitting on one line either side of that band. 1100 is
   the first width where the split column clears 450px and the pair fits, so
   the hero holds one layout the whole way up. */
@media (min-width: 1100px) {
  .hero-grid { grid-template-columns: 1.05fr 1fr; gap: var(--space-7); }
}
.hero-text { display: flex; flex-direction: column; gap: var(--space-5); justify-content: center; justify-self: start; max-width: 640px; }
.hero-headline {
  font-family: var(--font-heading);
  font-size: var(--text-mega);
  line-height: 1.0;
  letter-spacing: -0.005em;
  color: var(--ivory);
  font-weight: 400;              /* Anton's only weight — see --font-heading */
  text-wrap: balance;
  max-width: 20ch;               /* condensed face fits more per line than 16ch */
}
.hero-sub {
  font-size: var(--text-lg);
  color: var(--fg-soft);
  max-width: 54ch;
  line-height: 1.55;
}
.hero-cta-row { display: flex; flex-wrap: wrap; gap: 14px; }

.hero-meta {
  display: flex; flex-wrap: wrap; align-items: center; gap: 12px var(--space-5);
  margin-top: var(--space-4);
  color: var(--ivory-mute);
  font-size: var(--text-sm);
  font-family: var(--font-mono);
  letter-spacing: 0.02em;
}
.hero-meta strong { color: var(--ivory); font-weight: 600; }
@media (max-width: 700px) {
  .floating-tag.ft-top    { left: 10px; top: 10px; }
  .floating-tag.ft-bottom { right: 10px; bottom: 10px; }
}

/* Hero stat strip */
.vintage-strip {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: var(--space-5);
  margin-top: var(--space-7);
  padding-top: var(--space-6);
  border-top: 1px solid var(--rule-accent);
}
.vintage-strip > div { display: flex; flex-direction: column; gap: 6px; }
.vintage-strip .v {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: clamp(1.65rem, 1.1rem + 1.6vw, 2.5rem);
  letter-spacing: -0.02em;
  color: var(--ivory);
  line-height: 1.15;
}
.vintage-strip > div:nth-child(2) .v { color: var(--orange); }
.vintage-strip .l {
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ivory-mute);
}
@media (max-width: 720px) {
  .vintage-strip { grid-template-columns: repeat(2, 1fr); }
}

/* ---------- 8. Editorial / asymmetric blocks ---------------------------- */
.editorial {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-6);
  align-items: start;
}
@media (min-width: 900px) {
  .editorial { grid-template-columns: minmax(0, 5fr) minmax(0, 7fr); gap: var(--space-8); }
}
/* Scrolls with its column. This was sticky, so the numeral and its caption
   hung in place while the body text ran past them — the classic two-speed
   editorial effect, and exactly the "one thing fixed, the rest moving" the
   design no longer wants. The top offset it used (gutter + 70px) was clearing
   the sticky header, which is also gone. */
.editorial-aside {
  display: flex; flex-direction: column; gap: var(--space-5);
}
.editorial-aside .number {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: clamp(3rem, 1.8rem + 4vw, 5rem);
  line-height: 1.05;
  color: var(--orange);
  letter-spacing: -0.025em;
}
.editorial-body p {
  font-size: var(--text-lg);
  line-height: 1.65;
  color: var(--fg-soft);
  max-width: 60ch;
}
.editorial-body p + p { margin-top: 1.25em; }
.editorial-body .lede {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: clamp(1.25rem, 1rem + 0.75vw, 1.55rem);
  line-height: 1.4;
  letter-spacing: -0.02em;
  color: var(--ivory);
  margin-bottom: 1.4em;
  text-wrap: balance;
}
.editorial-body .pull-quote {
  margin: var(--space-7) 0;
  padding: var(--space-5) 0 var(--space-5) var(--space-6);
  border-left: 2px solid var(--orange);
  font-family: var(--font-display);
  font-weight: 600;
  font-size: clamp(1.25rem, 0.9rem + 1vw, 1.65rem);
  line-height: 1.4;
  letter-spacing: -0.02em;
  color: var(--ivory);
}
.editorial-body .pull-quote cite {
  display: block;
  font-family: var(--font-mono);
  font-style: normal;
  font-weight: 500;
  font-size: var(--text-xs);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ivory-mute);
  margin-top: var(--space-3);
}

/* ---------- 12. Estate / split image-text ------------------------------ */
.split-image {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-6);
  align-items: center;
}
@media (min-width: 900px) {
  .split-image { grid-template-columns: 1fr 1fr; gap: var(--space-8); }
  .split-image.is-reversed > :first-child { order: 2; }
}
.split-image figure {
  margin: 0;
  border-radius: var(--radius-lg);
  overflow: hidden;
  aspect-ratio: 5 / 6;
  position: relative;
  border: 1px solid var(--ink-line);
}
.split-image figure img { width: 100%; height: 100%; object-fit: cover; }
.split-image figcaption {
  position: absolute; bottom: 16px; left: 16px;
  background: var(--ink-deep);
  color: var(--orange);
  padding: 8px 14px;
  border-radius: 0;
  font-family: var(--font-mono);
  font-weight: 500;
  font-size: var(--text-xs);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  border: 1px solid var(--rule-accent);
}
.split-image .body { display: flex; flex-direction: column; gap: var(--space-4); }
.split-image .body h2 { margin-top: 10px; color: var(--ivory); }
.split-image .body p { font-size: var(--text-lg); line-height: 1.6; color: var(--fg-soft); max-width: 52ch; }

.facts-list {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: var(--space-4) var(--space-5);
  margin-top: var(--space-5);
  padding-top: var(--space-5);
  border-top: 1px solid var(--rule-strong);
}
.facts-list dt {
  font-family: var(--font-mono);
  font-weight: 500;
  font-size: var(--text-xs);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ivory-mute);
}
.facts-list dd {
  margin: 4px 0 0;
  font-family: var(--font-display);
  font-weight: 400;
  font-size: 1.2rem;
  line-height: 1.25;
  color: var(--orange);
  letter-spacing: -0.015em;
}

/* ---------- 13. Booking card ------------------------------------------ */
.booking-card {
  background: var(--ink-card);
  border: 1px solid var(--rule-accent);
  border-radius: var(--radius);
  padding: var(--space-6);
  display: flex; flex-direction: column; gap: var(--space-4);
  box-shadow: var(--shadow-accent);
}
.booking-card .row {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-4);
}
/* A `1fr 1fr` grid resolves each track's min size to min-content, and an
   <input>'s min-content is its default `size` (~20 chars) — which refuses to
   shrink and blows the row past the viewport. Stack below 620px, and pin
   min-width:0 so the tracks may shrink at every width. */
@media (min-width: 620px) {
  .booking-card .row { grid-template-columns: 1fr 1fr; }
}
.booking-card .row > * { min-width: 0; }
.booking-card input,
.booking-card select,
.booking-card textarea { width: 100%; min-width: 0; }
.booking-card label {
  display: flex; flex-direction: column; gap: 6px;
  font-family: var(--font-mono);
  font-weight: 500;
  font-size: var(--text-xs);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ivory-mute);
}
.booking-card input,
.booking-card select,
.booking-card textarea {
  font: inherit;
  font-family: var(--font-body);
  font-size: var(--text-base);
  color: var(--ivory);
  padding: 12px 14px;
  border-radius: 0;
  border: 1px solid var(--ink-rule);
  background: var(--ink-deep);
  transition: border-color var(--dur-fast) var(--ease-out), box-shadow var(--dur-fast) var(--ease-out);
  letter-spacing: normal;
  text-transform: none;
  font-weight: 400;
}
.booking-card input::placeholder,
.booking-card textarea::placeholder { color: var(--ivory-mute); opacity: 0.6; }
.booking-card input:focus,
.booking-card select:focus,
.booking-card textarea:focus {
  outline: none;
  border-color: var(--orange);
  box-shadow: 0 0 0 3px var(--orange-glow);
}
.booking-card textarea { min-height: 110px; resize: vertical; }
.booking-card .actions { display: flex; align-items: center; justify-content: space-between; gap: var(--space-4); flex-wrap: wrap; }

/* Honeypot. Moved out of the viewport rather than display:none or
   visibility:hidden — bots skip fields hidden those ways, which is exactly the
   fields we want them to fill. aria-hidden + tabindex=-1 on the markup keep it
   away from screen readers and the tab order. */
.booking-card .hp {
  position: absolute;
  left: -9999px;
  width: 1px;
  height: 1px;
  overflow: hidden;
}

/* Send result. Empty until main.js writes to it, and it carries no box, no
   colour and no space in that state — an empty bordered panel sitting under
   every form would read as a rendering fault. */
.form-status {
  margin-top: var(--space-4);
  font-size: var(--text-sm);
  line-height: 1.5;
}
.form-status:empty { display: none; }
.form-status[data-state] {
  padding: 12px 16px;
  border-left: 2px solid currentColor;
  background: rgba(232, 228, 214, 0.04);
}
/* Orange on the dark grounds is AAA; both states are also prefixed with a word
   ("Thank you" / "Sorry"), so the meaning does not rest on colour alone. */
.form-status[data-state="ok"]      { color: var(--orange); }
.form-status[data-state="pending"] { color: var(--ivory-soft); }
.form-status[data-state="error"]   { color: var(--ivory); background: rgba(91, 26, 31, 0.35); }

/* ---------- 15. Footer ------------------------------------------------ */
.site-footer {
  background: #08080A;
  color: var(--ivory);
  padding: var(--space-8) 0 var(--space-6);
  position: relative;
  border-top: 1px solid var(--rule-accent);
}
/* Three stages, not two. Going straight from one column to four at 900px left
   everything between 560 and 900 as a single tall stack — three link lists
   under each other on a tablet, with the whole right half of the footer empty.
   The middle stage puts the brand on its own row and lets the three link
   columns share the width. */
.footer-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-6);
}
@media (min-width: 560px) {
  .footer-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .footer-brand { grid-column: 1 / -1; }
}
@media (min-width: 900px) {
  .footer-grid { grid-template-columns: 1.4fr repeat(3, minmax(0, 1fr)); }
  .footer-brand { grid-column: auto; }
}
.footer-brand { display: flex; flex-direction: column; gap: var(--space-3); }
.footer-brand p { color: rgba(232, 228, 214, 0.65); max-width: 36ch; font-size: var(--text-sm); }
/* <address> is the right element for a contact block but browsers italicise it,
   which fights the rest of the footer. */
.footer-address {
  font-style: normal;
  font-size: var(--text-sm);
  line-height: 1.7;
  color: rgba(232, 228, 214, 0.65);
}
.footer-address a { color: var(--orange); }
.footer-address a:hover { color: var(--orange-soft); }
/* Wraps rather than scrolls: five labels do not fit one line on a phone. */
.social-links {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  gap: 8px 14px;
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  letter-spacing: 0.12em;
  text-transform: uppercase;
}
.social-links a {
  color: var(--ivory-soft);
  padding-bottom: 2px;
  border-bottom: 1px solid var(--rule-accent);
}
.social-links a:hover { color: var(--orange); border-color: var(--orange); }
.footer-col-title {
  font-family: var(--font-mono);
  font-weight: 600;
  font-size: var(--text-xs);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--orange);
  margin-bottom: var(--space-4);
}
.footer-col ul { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 10px; }
.footer-col a { color: rgba(232, 228, 214, 0.7); font-size: var(--text-sm); }
.footer-col a:hover { color: var(--orange); }
/* Current page. Set by main.js — the footer markup is shared and identical on
   every page. Underlined as well as orange so it does not read as a stuck
   hover state, which colour alone would. */
.footer-col a[aria-current="page"] {
  color: var(--orange);
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 4px;
}
.footer-legal {
  margin-top: var(--space-7);
  padding-top: var(--space-5);
  border-top: 1px solid rgba(232, 228, 214, 0.10);
  display: flex; flex-wrap: wrap; gap: var(--space-4);
  align-items: center; justify-content: space-between;
  font-family: var(--font-mono);
  font-weight: 500;
  font-size: var(--text-xs);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: rgba(232, 228, 214, 0.45);
}
.footer-legal a:hover { color: var(--orange); }
/* 0.16em of tracking on uppercase mono is ~38 characters of legal line, which
   is wider than a 320px screen can hold. Ease it off rather than let the line
   break mid-phrase, and drop space-between so the two items read as a stack
   instead of one pushed to each edge of a very narrow column. */
@media (max-width: 560px) {
  .footer-legal {
    letter-spacing: 0.1em;
    gap: var(--space-3);
    justify-content: flex-start;
  }
}

/* ---------- 16. Misc utilities ---------------------------------------- */
/* .reveal is retired. It faded and slid each block up 20px as it entered the
   viewport, driven by an IntersectionObserver in main.js — a scroll-triggered
   effect, which the design no longer uses. The observer is gone and every
   `class="reveal"` has been stripped from the markup; the rule stays only as a
   safety net, so that if a `reveal` class survives somewhere (or is pasted
   back in from an old page, or arrives via the CMS), the element is plainly
   visible rather than stuck at opacity 0 with nothing left to un-hide it. */
.reveal { opacity: 1; transform: none; transition: none; }

.tag {
  display: inline-flex; align-items: center; gap: 8px;
  padding: 6px 12px;
  border-radius: 0;
  background: var(--ink-card);
  color: var(--orange);
  border: 1px solid var(--rule-accent);
  font-family: var(--font-mono);
  font-weight: 500;
  font-size: var(--text-xs);
  letter-spacing: 0.12em;
  text-transform: uppercase;
}

/* ---------- 17. TisuMazwi components ---------------------------------- */
/* Text-led blocks used where photography would otherwise sit. Each one is
   self-contained so a real photograph can replace it without touching the
   surrounding grid. */

/* Inline links inside body copy — keep the gold accent legible on ink. */
.hero-sub a,
.editorial-body p a,
.split-image .body p a,
.promise-body a {
  color: var(--orange);
  border-bottom: 1px solid var(--rule-accent);
  padding-bottom: 1px;
  transition: color var(--dur-fast) var(--ease-out),
              border-color var(--dur-fast) var(--ease-out);
}
.hero-sub a:hover,
.editorial-body p a:hover,
.split-image .body p a:hover,
.promise-body a:hover { color: var(--orange-soft); border-color: var(--orange); }

/* Etymology card — the typographic panel that anchors the hero. */
.etymology-card {
  background: var(--ink-card);
  border: 1px solid var(--rule-accent);
  border-radius: var(--radius-lg);
  padding: var(--space-6);
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  box-shadow: var(--shadow-accent);
  justify-self: end;
  width: 100%;
  max-width: 480px;
  align-self: center;
}
.etymology-word {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: clamp(2.5rem, 1.6rem + 3.4vw, 3.75rem);
  line-height: 1.05;
  letter-spacing: -0.04em;
  color: var(--ivory);
  margin: var(--space-2) 0 0;
}
.etymology-gloss {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: var(--text-xl);
  line-height: 1.3;
  letter-spacing: -0.02em;
  color: var(--orange);
  margin: 0;
}
.etymology-note {
  color: var(--fg-soft);
  font-size: var(--text-sm);
  line-height: 1.6;
  margin: 0;
  max-width: 40ch;
}
.etymology-card hr.accent { margin: var(--space-4) 0; }
@media (max-width: 979px) {
  .etymology-card { justify-self: start; max-width: 100%; }
}

/* Leader card — stands in for the founder portrait. */
.leader-card {
  background: var(--ink-card);
  border: 1px solid var(--ink-line);
  border-radius: var(--radius-lg);
  padding: var(--space-6);
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  aspect-ratio: 5 / 6;
  justify-content: center;
}
.leader-name {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: clamp(2rem, 1.3rem + 2.6vw, 3rem);
  line-height: 1.05;
  letter-spacing: -0.04em;
  color: var(--ivory);
  margin: var(--space-2) 0 0;
  text-wrap: balance;
}
.leader-role {
  color: var(--fg-soft);
  max-width: 34ch;
  margin: 0;
}
.leader-card .facts-list { grid-template-columns: repeat(2, 1fr); }
@media (max-width: 899px) {
  .leader-card { aspect-ratio: auto; }
}

/* Service cards — the three disciplines. */
.service-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-5);
}
@media (min-width: 760px)  { .service-grid { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 1100px) { .service-grid { grid-template-columns: repeat(3, 1fr); } }

.service-card {
  background: var(--ink-card);
  border: 1px solid var(--ink-line);
  border-radius: var(--radius);
  padding: var(--space-6);
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  transition: transform var(--dur-base) var(--ease-out),
              box-shadow var(--dur-base) var(--ease-out),
              border-color var(--dur-base) var(--ease-out);
}
.service-card:hover {
  transform: translateY(-4px);
  box-shadow: var(--shadow-md);
  border-color: var(--rule-accent);
}
.service-card .big-num {
  font-size: clamp(2.5rem, 1.8rem + 2.4vw, 3.5rem);
  line-height: 1;
  opacity: 0.55;
}
.service-card h3 { margin-top: var(--space-2); text-wrap: balance; }
.service-card h3 a { color: var(--ivory); }
.service-card h3 a:hover { color: var(--orange); }
.service-card p { flex: 1; margin-bottom: var(--space-4); }
.service-card .link-arrow { align-self: flex-start; }

/* Partner list — public and development sector collaborations. */
.partner-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-5);
}
@media (min-width: 900px) { .partner-list { grid-template-columns: repeat(2, 1fr); } }

.partner-item {
  background: var(--ink-deep);
  border: 1px solid var(--ink-line);
  border-left: 2px solid var(--orange);
  border-radius: var(--radius);
  padding: var(--space-6);
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}
.partner-item .card-title { color: var(--ivory); margin-top: var(--space-2); }
.partner-item .tag { align-self: flex-start; }
.partner-item p { margin: 0; max-width: 52ch; }

.partner-note {
  margin: var(--space-7) 0 0;
  padding-top: var(--space-5);
  border-top: 1px solid var(--rule-accent);
  font-family: var(--font-display);
  font-weight: 600;
  font-size: clamp(1.15rem, 0.9rem + 0.8vw, 1.45rem);
  line-height: 1.4;
  letter-spacing: -0.02em;
  color: var(--ivory);
  max-width: 60ch;
  text-wrap: balance;
}

/* Book grid — published work.
   Cover art varies between 0.68 and 0.75 aspect, so the frame is a fixed 2/3
   box and the image is contained rather than cropped: cropping a cover cuts
   the title off. */
.books-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: var(--space-5);
}
@media (min-width: 720px)  { .books-grid { grid-template-columns: repeat(3, 1fr); } }
@media (min-width: 1100px) { .books-grid { grid-template-columns: repeat(5, 1fr); } }

.book-card {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}
.book-card .cover {
  aspect-ratio: 2 / 3;
  border-radius: var(--radius-sm);
  overflow: hidden;
  background: var(--ink-elev);
  border: 1px solid var(--ink-line);
  display: grid;
  place-items: center;
  padding: var(--space-3);
  transition: transform var(--dur-base) var(--ease-out),
              box-shadow var(--dur-base) var(--ease-out),
              border-color var(--dur-base) var(--ease-out);
}
.book-card .cover img {
  width: 100%;
  height: 100%;
  object-fit: contain;
  border-radius: 0;
  box-shadow: var(--shadow-sm);
}
.book-card:hover .cover {
  transform: translateY(-4px);
  box-shadow: var(--shadow-md);
  border-color: var(--rule-accent);
}
.book-card .card-title {
  font-size: var(--text-base);
  line-height: 1.3;
  font-weight: 600;
  color: var(--ivory);
  letter-spacing: -0.01em;
  text-wrap: balance;
}
.book-card .author {
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  letter-spacing: 0.06em;
  color: var(--ivory-mute);
  font-weight: 500;
}
.book-card.is-report .cover { border-color: var(--rule-accent); }

/* Our Promise */
.promise-body {
  font-size: var(--text-lg);
  line-height: 1.65;
  color: var(--fg-soft);
  max-width: 62ch;
  margin: var(--space-6) auto 0;
  text-align: center;
}

/* ---------- 18. Standalone images ------------------------------------- */
/* For images that are NOT inside a frame that already sizes them. .bento,
   .gallery-item .frame, .post-card .thumb and .team-card .portrait each lock
   their own aspect and style their <img> at 100%/cover, so images in those
   need no class at all. This is for the rest — the hero and split-image
   photographs, which set their own box.

   Every photograph on the site is a real <img> from the start, pointed at
   assets/img/placeholder.svg until the file it names in data-image is linked.
   That keeps the box, the aspect and the layout identical before and after,
   and leaves one attribute to change rather than a block of markup.

   width:100% + aspect-ratio, never a height: let width come from the
   container and let aspect set the height. With width:auto, aspect-ratio
   back-computes the WIDTH from the height instead — .is-wide (21/9) resolved
   to 467px that way and blew past a 360px viewport. */
.media {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 16 / 10;
  object-fit: cover;
  border-radius: var(--radius);
  background-color: var(--ink-card);
}
/* Fit the whole photo inside the box instead of filling it. For images whose
   subject reaches the edges — someone holding a book up to camera — cropping
   removes the point of the picture. The card ground fills what is left, so the
   letterboxing reads as a frame rather than a gap. */
.media.is-contain {
  object-fit: contain;
  background: var(--ink-card);
  border-radius: var(--radius);
}

/* The box takes the PHOTO's aspect instead of imposing one.
   is-contain avoids cropping but pays for it in letterbox bars; this avoids
   both. The element still has width:100%, and height follows from the width and
   height attributes on the tag, so the picture is edge to edge with nothing cut
   and nothing padded, at every screen size.
   The width/height attributes therefore stop being a hint and start driving
   layout — if you swap the src, update them or the box will hold the old
   shape and the browser will squash the new photo into it. */
.media.is-natural {
  aspect-ratio: auto;
  height: auto;
  object-fit: fill;
}
.media.is-portrait { aspect-ratio: 4 / 5; }
.media.is-square   { aspect-ratio: 1 / 1; }
.media.is-wide     { aspect-ratio: 21 / 9; }
.media.is-tall     { aspect-ratio: 5 / 6; }

/* ---------- 18a. Hero image bracket ----------------------------------- */
/* Two orange L-brackets, top-left and bottom-right, sitting just outside the
   hero image block. Applied ONLY to the image in each page's top section —
   putting it on every photograph would turn a mark into wallpaper.
   It goes on a WRAPPER, never on the <img> itself: an image cannot carry
   ::before/::after, so a bare <img class="media"> is wrapped in
   <figure class="hero-frame">. The bento is already a div, so it takes the
   class directly.
   The brackets are drawn outside the image, so the offset has to stay inside
   the page gutter or they push the page sideways on a phone. --bracket-out is
   clamped well below the gutter's own minimum (20px) for that reason. */
.hero-frame {
  --bracket-out: clamp(7px, 1.4vw, 18px);
  --bracket-len: clamp(26px, 4.4vw, 58px);
  --bracket-weight: 3px;
  position: relative;
  margin: 0;
}
.hero-frame::before,
.hero-frame::after {
  content: '';
  position: absolute;
  width: var(--bracket-len);
  height: var(--bracket-len);
  border: var(--bracket-weight) solid var(--orange);
  pointer-events: none;
}
.hero-frame::before {
  top: calc(-1 * var(--bracket-out));
  left: calc(-1 * var(--bracket-out));
  border-right-color: transparent;
  border-bottom-color: transparent;
}
.hero-frame::after {
  bottom: calc(-1 * var(--bracket-out));
  right: calc(-1 * var(--bracket-out));
  border-left-color: transparent;
  border-top-color: transparent;
}
/* The bento sets its own max-width and justify-self; as .hero-frame it keeps
   both, and the brackets hang off the block as a whole rather than any cell. */
.bento.hero-frame { margin: 0; }

/* ---------- 18b. Bento grid ------------------------------------------- */
/* Five cells: one 2x2 hero, one tall, three small. Sits in the hero's right
   column in place of a single image. Each cell holds an <img>, which the
   cell sizes and crops (object-fit: cover) whatever its aspect. */
.bento {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  grid-template-rows: repeat(3, 1fr);
  grid-template-areas:
    "a a b"
    "a a c"
    "d e c";
  gap: var(--space-3);
  aspect-ratio: 1 / 1;
  width: 100%;
  max-width: 560px;
  justify-self: end;
  align-self: center;
}
/* Four-cell variant: one 16:9 lead, two squares under it, one tall image down
   the right. The base .bento is a fixed 1:1 board of five cells, which forces
   every photo into a square-ish hole; this one sets the aspect on each cell so
   the cells fit the photographs instead.

   aspect-ratio: auto on the container is what allows that — the rows size to
   their content rather than to a third of a square. The tall cell has no
   aspect of its own: it spans both rows, so its shape is whatever the other
   two rows add up to (~0.449 wide-to-tall), and book-handover-tall.jpg is cut
   to exactly that. Change the lead cell's aspect and that number moves. */
.bento.is-feature {
  grid-template-columns: repeat(3, 1fr);
  grid-template-rows: auto auto;
  grid-template-areas:
    "a a c"
    "b d c";
  aspect-ratio: auto;
}
.bento.is-feature > *:nth-child(1) { grid-area: a; aspect-ratio: 16 / 9; }
.bento.is-feature > *:nth-child(2) { grid-area: b; aspect-ratio: 1 / 1; }
.bento.is-feature > *:nth-child(3) { grid-area: d; aspect-ratio: 1 / 1; }
.bento.is-feature > *:nth-child(4) { grid-area: c; height: 100%; }

/* margin:0 because the cells are <figure>, which carries a UA margin.
   min-*:0 stops a cell's content forcing its track wider than 1fr. */
.bento > * {
  margin: 0;
  border-radius: var(--radius);
  overflow: hidden;
  min-width: 0;
  min-height: 0;
  position: relative;
}
.bento > *:nth-child(1) { grid-area: a; }
.bento > *:nth-child(2) { grid-area: b; }
.bento > *:nth-child(3) { grid-area: c; }
.bento > *:nth-child(4) { grid-area: d; }
.bento > *:nth-child(5) { grid-area: e; }

.bento img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform var(--dur-slow) var(--ease-out);
}
.bento > *:hover img { transform: scale(1.04); }

/* A cell's image fills the cell, so any standalone .media aspect must yield to
   the grid track. Carried on the class rather than the element so a .media
   image dropped into a bento cell behaves the same as a bare one. */
.bento .media {
  aspect-ratio: auto;
  height: 100%;
  border-radius: 0;
}

@media (max-width: 1100px) {
  .bento { justify-self: start; max-width: 100%; }
}
@media (max-width: 560px) {
  .bento { gap: var(--space-2); }
}

/* Contact details in the hero's text column (the right column is the bento).
   Own class rather than .facts-list, which is tuned for the split-image body
   and forces two columns. */
.contact-facts {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-5) var(--space-7);
  margin: var(--space-5) 0 0;
  padding-top: var(--space-5);
  border-top: 1px solid var(--rule-accent);
}
.contact-facts dt {
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ivory-mute);
  font-weight: 500;
}
.contact-facts dd {
  margin: 6px 0 0;
  font-family: var(--font-display);
  font-size: 1.15rem;
  line-height: 1.3;
  letter-spacing: -0.015em;
  color: var(--orange);
}
.contact-facts a { color: inherit; border-bottom: 1px solid var(--rule-accent); }
.contact-facts a:hover { color: var(--orange-soft); }

/* Deep-dive callout — the bidirectional cross-link from a parent service page
   to its child page (PR -> Media Monitoring, Publishing -> Thought Leadership). */
.deep-dive-callout {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4) var(--space-6);
  margin-top: var(--space-6);
  padding: var(--space-5) var(--space-6);
  background: var(--ink-card);
  border: 1px solid var(--ink-line);
  border-left: 2px solid var(--orange);
}
.deep-dive-callout h3 { color: var(--ivory); max-width: 44ch; }
.deep-dive-callout .btn { flex: none; }

/* Linked service name in a spec-table row, and the inline "detail ->" link. */
.spec-table th a { color: var(--ivory); border-bottom: 1px solid var(--rule-accent); }
.spec-table th a:hover { color: var(--orange); }
.inline-more {
  display: inline-block;
  margin-top: 6px;
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  letter-spacing: 0.06em;
  color: var(--orange);
  white-space: nowrap;
}
.inline-more:hover { color: var(--orange-soft); }

/* A package bullet that links out (e.g. "Media PR" -> Thought Leadership). */
.price-card ul li a { color: var(--orange); border-bottom: 1px solid var(--rule-accent); }
.price-card ul li a:hover { color: var(--orange-soft); }

/* Breadcrumb — shows a deep-dive page's place in the hierarchy (Services /
   Parent / This page). Sits above the hero heading. Mirrored in BreadcrumbList
   JSON-LD so search engines read the same hierarchy. */
.breadcrumb {
  margin-bottom: var(--space-5);
}
.breadcrumb ol {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px 10px;
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  letter-spacing: 0.1em;
  text-transform: uppercase;
}
.breadcrumb li {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  color: var(--ivory-mute);
}
.breadcrumb li:not(:last-child)::after {
  content: '/';
  color: var(--ivory-deep);
}
.breadcrumb a { color: var(--ivory-soft); }
.breadcrumb a:hover { color: var(--orange); }
.breadcrumb li[aria-current="page"] { color: var(--orange); }

/* ---------- 19. Mission / Vision / Values ----------------------------- */
.mvv-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-5);
}
@media (min-width: 900px) { .mvv-grid { grid-template-columns: repeat(3, 1fr); } }

.mvv-card {
  background: var(--ink-card);
  border: 1px solid var(--ink-line);
  border-top: 2px solid var(--orange);
  border-radius: var(--radius);
  padding: var(--space-6);
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}
.mvv-card h3 { color: var(--ivory); }
.mvv-card p { margin: 0; }

.brick-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-4);
}
@media (min-width: 760px)  { .brick-list { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 1200px) { .brick-list { grid-template-columns: repeat(5, 1fr); } }

.brick-item {
  background: var(--ink-deep);
  border: 1px solid var(--ink-line);
  border-radius: var(--radius);
  padding: var(--space-5);
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.brick-item .letter {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 2.25rem;
  line-height: 1;
  color: var(--orange);
  letter-spacing: -0.03em;
}
.brick-item .card-title { font-size: var(--text-base); color: var(--ivory); font-weight: 600; }
.brick-item p { margin: 0; font-size: var(--text-sm); }

/* ---------- 20. Audience / Why ---------------------------------------- */
.audience-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-4);
}
@media (min-width: 700px)  { .audience-grid { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 1100px) { .audience-grid { grid-template-columns: repeat(5, 1fr); } }

.audience-card {
  background: var(--ink-card);
  border: 1px solid var(--ink-line);
  border-radius: var(--radius);
  padding: var(--space-5);
  display: flex;
  flex-direction: column;
  gap: 10px;
  transition: border-color var(--dur-base) var(--ease-out),
              transform var(--dur-base) var(--ease-out);
}
.audience-card:hover { border-color: var(--rule-accent); transform: translateY(-3px); }
.audience-card .card-title { font-size: var(--text-lg); color: var(--ivory); }
.audience-card p { margin: 0; font-size: var(--text-sm); }

.why-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-5);
  counter-reset: why;
}
@media (min-width: 760px)  { .why-grid { grid-template-columns: repeat(2, 1fr); } }

.why-item {
  display: flex;
  gap: var(--space-4);
  align-items: flex-start;
  padding: var(--space-5) 0;
  border-top: 1px solid var(--rule);
}
.why-item::before {
  counter-increment: why;
  content: counter(why, decimal-leading-zero);
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  letter-spacing: 0.16em;
  color: var(--orange);
  padding-top: 4px;
  flex: none;
}
.why-item .card-title { color: var(--ivory); margin-bottom: 6px; font-size: var(--text-lg); }
.why-item p { margin: 0; font-size: var(--text-sm); }

/* ---------- 21. Pricing ----------------------------------------------- */
/* A `1fr` track floors at min-content, so a price card cannot shrink below its
   longest word + padding. Going 3-across at 760px overflowed; the breakpoints
   below are set where the cards actually fit, and min-width:0 lets the tracks
   shrink rather than push the page sideways. */
.pricing-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-5);
}
@media (min-width: 640px)  { .pricing-grid { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 960px)  { .pricing-grid { grid-template-columns: repeat(3, 1fr); } }
.pricing-grid.is-five { grid-template-columns: 1fr; }
@media (min-width: 640px)  { .pricing-grid.is-five { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 1000px) { .pricing-grid.is-five { grid-template-columns: repeat(3, 1fr); } }
@media (min-width: 1500px) { .pricing-grid.is-five { grid-template-columns: repeat(5, 1fr); } }

.price-card {
  background: var(--ink-card);
  border: 1px solid var(--ink-line);
  border-radius: var(--radius);
  padding: var(--space-6);
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  min-width: 0;
  overflow-wrap: anywhere;
  transition: border-color var(--dur-base) var(--ease-out),
              transform var(--dur-base) var(--ease-out),
              box-shadow var(--dur-base) var(--ease-out);
}
.price-card:hover {
  border-color: var(--rule-accent);
  transform: translateY(-4px);
  box-shadow: var(--shadow-md);
}
.price-card .tier {
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--orange);
  font-weight: 500;
}
.price-card .amount {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: clamp(2rem, 1.5rem + 1.6vw, 2.75rem);
  line-height: 1.05;
  letter-spacing: -0.035em;
  color: var(--ivory);
}
.price-card .amount sub {
  font-family: var(--font-mono);
  font-size: 0.34em;
  font-weight: 500;
  color: var(--ivory-mute);
  letter-spacing: 0.08em;
  margin-left: 6px;
  bottom: 0;
}
.price-card .best-for {
  font-size: var(--text-sm);
  color: var(--ivory-soft);
  padding-bottom: var(--space-3);
  border-bottom: 1px solid var(--rule);
  margin: 0;
}
.price-card ul {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 10px;
  flex: 1;
}
.price-card ul li {
  font-size: var(--text-sm);
  color: var(--fg-soft);
  padding-left: 20px;
  position: relative;
  line-height: 1.5;
}
.price-card ul li::before {
  content: '';
  position: absolute;
  left: 0; top: 8px;
  width: 8px; height: 8px;
  border-radius: 0;
  border: 1.5px solid var(--orange);
}
.price-card .btn { margin-top: var(--space-4); justify-content: center; }
.price-card.is-featured {
  background: var(--bg-warm);
  border-color: var(--rule-accent);
  box-shadow: var(--shadow-accent);
}
.price-note {
  margin-top: var(--space-5);
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  letter-spacing: 0.06em;
  color: var(--ivory-mute);
}

/* Service spec table — "Service / What We Do" rows */
.spec-table {
  width: 100%;
  border-collapse: collapse;
  margin-top: var(--space-5);
  text-align: left;
}
.spec-table caption {
  text-align: left;
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--orange);
  padding-bottom: var(--space-4);
}
.spec-table th,
.spec-table td {
  padding: var(--space-4) var(--space-4) var(--space-4) 0;
  border-bottom: 1px solid var(--rule);
  vertical-align: top;
}
.spec-table thead th {
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ivory-mute);
  font-weight: 500;
  border-bottom-color: var(--rule-accent);
}
.spec-table tbody th {
  color: var(--ivory);
  font-family: var(--font-body);
  font-weight: 600;
  font-size: var(--text-base);
  width: 34%;
}
.spec-table td { color: var(--fg-soft); font-size: var(--text-sm); }
.table-scroll { overflow-x: auto; }
@media (max-width: 640px) {
  .spec-table tbody th { width: 40%; }
}

/* ---------- 22. Team --------------------------------------------------- */
.team-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-5);
}
@media (min-width: 620px)  { .team-grid { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 1000px) { .team-grid { grid-template-columns: repeat(3, 1fr); } }

.team-card {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
}
.team-card .portrait {
  aspect-ratio: 4 / 5;
  border-radius: var(--radius);
  overflow: hidden;
  border: 1px solid var(--ink-line);
  background: var(--ink-elev);
}
.team-card .portrait img { width: 100%; height: 100%; object-fit: cover; }
.team-card h3 { font-size: var(--text-xl); color: var(--ivory); }
.team-card .role {
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--orange);
  font-weight: 500;
}
.team-card p { margin: 0; font-size: var(--text-sm); }
.team-card .expertise {
  display: flex; flex-wrap: wrap; gap: 6px;
  margin-top: 4px;
}
.team-card .expertise span {
  padding: 4px 10px;
  border: 1px solid var(--ink-rule);
  border-radius: 0;
  font-family: var(--font-mono);
  font-size: 11px;
  color: var(--ivory-soft);
}

/* ---------- 23. Gallery ------------------------------------------------ */
.gallery-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-4);
}
@media (min-width: 620px)  { .gallery-grid { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 1000px) { .gallery-grid { grid-template-columns: repeat(3, 1fr); } }

.gallery-item { display: flex; flex-direction: column; gap: var(--space-3); }
.gallery-item .frame {
  aspect-ratio: 4 / 3;
  border-radius: var(--radius);
  overflow: hidden;
  border: 1px solid var(--ink-line);
  background: var(--ink-elev);
}
.gallery-item .frame img { width: 100%; height: 100%; object-fit: cover; }
/* Some subjects must not be cropped. A newspaper clipping loses its headline
   and a vertical photo of someone holding a book loses the book — which is the
   entire reason the photograph exists. Same decision the book covers already
   make: contain them in the frame and let the card ground show around them,
   rather than fill the box and cut the subject out. */
.gallery-item .frame.is-contain {
  background: var(--ink-card);
}
.gallery-item .frame.is-contain img {
  object-fit: contain;
  padding: 6px;
}
.gallery-item figcaption { display: flex; flex-direction: column; gap: 4px; }
.gallery-item .cap { color: var(--ivory); font-size: var(--text-sm); font-weight: 500; }
.gallery-item .ctx {
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  letter-spacing: 0.06em;
  color: var(--ivory-mute);
}

/* ---------- 24. Blog --------------------------------------------------- */
.category-chips {
  display: flex; flex-wrap: wrap; gap: 10px;
  margin-bottom: var(--space-7);
}
.category-chips a {
  padding: 8px 16px;
  border-radius: 0;
  border: 1px solid var(--ink-rule);
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ivory-soft);
  font-weight: 500;
  transition: border-color var(--dur-fast) var(--ease-out),
              color var(--dur-fast) var(--ease-out),
              background var(--dur-fast) var(--ease-out);
}
.category-chips a:hover,
.category-chips a[aria-current="true"] {
  border-color: var(--orange);
  color: var(--ink-deep);
  background: var(--orange);
}

.post-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-6);
}
@media (min-width: 720px)  { .post-grid { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 1100px) { .post-grid { grid-template-columns: repeat(3, 1fr); } }

.post-card { display: flex; flex-direction: column; gap: var(--space-4); }
.post-card .thumb {
  aspect-ratio: 16 / 10;
  border-radius: var(--radius);
  overflow: hidden;
  border: 1px solid var(--ink-line);
  background: var(--ink-elev);
}
.post-card .thumb img { width: 100%; height: 100%; object-fit: cover; }
/* Same escape hatch the gallery frames have. A 16:10 thumb crops a portrait
   photo by half, which is how a two-person handover shot ends up with its
   heads sliced off. Contain it instead and let the card ground show. */
.post-card .thumb.is-contain { background: var(--ink-card); }
.post-card .thumb.is-contain img { object-fit: contain; padding: 6px; }
.post-card .cat {
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--orange);
  font-weight: 500;
}
.post-card h3 { font-size: var(--text-xl); color: var(--ivory); text-wrap: balance; }
.post-card h3 a:hover { color: var(--orange); }
.post-card p { margin: 0; font-size: var(--text-sm); }
.post-card .byline {
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  color: var(--ivory-mute);
  letter-spacing: 0.06em;
}

/* ---------- 25. Clients / testimonials -------------------------------- */
.client-groups {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-6);
}
@media (min-width: 820px)  { .client-groups { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 1200px) { .client-groups { grid-template-columns: repeat(3, 1fr); } }

.client-group {
  background: var(--ink-card);
  border: 1px solid var(--ink-line);
  border-radius: var(--radius);
  padding: var(--space-6);
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}
.client-group h3 { font-size: var(--text-xl); color: var(--ivory); }
.client-group > p { margin: 0 0 var(--space-2); font-size: var(--text-sm); }
.client-group ul {
  list-style: none; margin: 0; padding: 0;
  display: flex; flex-direction: column; gap: var(--space-4);
}
.client-group li { border-top: 1px solid var(--rule); padding-top: var(--space-4); }
.client-group li .name { color: var(--ivory); font-weight: 600; font-size: var(--text-sm); display: block; }
.client-group li .what {
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  color: var(--ivory-mute);
  line-height: 1.5;
  margin-top: 4px;
  display: block;
}

.testimonial-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-5);
}
@media (min-width: 900px) { .testimonial-grid { grid-template-columns: repeat(3, 1fr); } }

.testimonial {
  background: var(--ink-card);
  border: 1px solid var(--ink-line);
  border-left: 2px solid var(--orange);
  border-radius: var(--radius);
  padding: var(--space-6);
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
}
.testimonial blockquote {
  margin: 0;
  font-family: var(--font-display);
  font-weight: 600;
  font-size: var(--text-lg);
  line-height: 1.45;
  letter-spacing: -0.015em;
  color: var(--ivory);
}
.testimonial figcaption { display: flex; flex-direction: column; gap: 2px; }
.testimonial .who { color: var(--ivory); font-size: var(--text-sm); font-weight: 600; }
.testimonial .where {
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  color: var(--ivory-mute);
  letter-spacing: 0.06em;
}

/* ---------- 26. Reduced motion --------------------------------------- */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
    scroll-behavior: auto !important;
  }
}
