/* ===========================================================
   Oldworld Creative - global stylesheet
   Shared across index.html, about.html, contact.html, 404.html,
   and the case-study pages (ministry-of-defence.html,
   lloyds-banking-group.html, pearson-education.html, terraquest.html).
   =========================================================== */

/* ---------- TOKENS ---------- */
:root {
  /* LIGHT MODE - default. Cool palette tokens: white cards on a
     pale cornflower page, Deep Navy ink, Steel Navy mute, Deep
     Cobalt accent (7.30:1 on card / 5.89:1 on page). */
  --cream: #FFFFFF;
  --cream-tint: #D7EAF3;
  --ink: #002B40;
  --ink-soft: #1F4861;
  --mute: #3F5E76;
  --rule: rgba(0, 43, 64, 0.10);
  --rule-strong: #002B40;
  --accent: #0E5A8F;
  --accent-strong: #074870;
  --accent-soft: #82B4D5;
  /* Fixed cream that never flips - for case-study hover row text on accent bg */
  --cream-fixed: #FFFFFF;
  /* Conformance tick green - chosen to clear ≥3:1 non-text contrast
     against the cream-tint top-bar bg in light mode. Dark mode
     swaps to a brighter emerald below. */
  --check: #166534;
  --serif: "Fraunces", "Times New Roman", serif;
  --sans: "Inter", system-ui, -apple-system, "Helvetica Neue", Arial, sans-serif;
  --radius: 8px;
  --shadow-hover: 0 10px 28px rgba(0, 43, 64, 0.12), 0 2px 6px rgba(0, 43, 64, 0.08);
}

/* DARK MODE - applied via three triggers:
   1. The visible theme toggle's checkbox state (works without JS via :has)
   2. The JS-applied .theme-dark class on <html> (persisted choice or OS)
   3. The OS preference media query below (works without JS at all) - scoped
      with :not(.theme-light) so an explicit "light" choice still wins. */
body:has(.theme-toggle:checked),
html.theme-dark body {
  --cream: #002B40;
  --cream-tint: #001A2A;
  --ink: #FFFFFF;
  --ink-soft: #D7EAF3;
  --mute: #82B4D5;
  --rule: rgba(255, 255, 255, 0.10);
  --rule-strong: #FFFFFF;
  --accent: #5BB5EC;
  --accent-strong: #7BC5EF;
  --accent-soft: #1F4861;
  --cream-fixed: #002B40;
  --shadow-hover: 0 10px 28px rgba(0, 0, 0, 0.45), 0 2px 6px rgba(0, 0, 0, 0.30);
  /* Conformance tick - lighter emerald for dark mode (≥10:1 on the
     near-black top-bar bg). */
  --check: #4ADE80;
}
@media (prefers-color-scheme: dark) {
  html:not(.theme-light) body {
    --cream: #002B40;
    --cream-tint: #001A2A;
    --ink: #FFFFFF;
    --ink-soft: #D7EAF3;
    --mute: #82B4D5;
    --rule: rgba(255, 255, 255, 0.10);
    --rule-strong: #FFFFFF;
    --accent: #5BB5EC;
    --accent-strong: #7BC5EF;
    --accent-soft: #1F4861;
    --cream-fixed: #002B40;
    --shadow-hover: 0 10px 28px rgba(0, 0, 0, 0.45), 0 2px 6px rgba(0, 0, 0, 0.30);
    --check: #4ADE80;
  }
}

/* ---------- BASE ---------- */
*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }

/* ---------- SKIP LINK ----------
   Sits at the very top of the document, off-screen by default and
   revealed when keyboard-focused. Lets users bypass the conformance
   bar, nav, and header to jump straight to main content. */
.skip-link {
  position: absolute;
  top: 8px;
  left: 8px;
  z-index: 1000;
  padding: 10px 28px;
  background: var(--ink);
  color: var(--cream);
  font-family: var(--sans);
  font-size: 13px;
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  border-radius: 4px;
  transform: translateY(-150%);
  transition: transform .15s ease;
}
.skip-link:focus {
  transform: translateY(0);
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}
/* Inline skip-link variant - used inside a card to skip past a complex
   widget (e.g. the carousel on case studies). Out of flow (sr-only
   positioned) when unfocused so it doesn't leave a gap between the
   card heading and the widget; brought into flow when keyboard-focused
   and rendered as a standard skip-link pill at its DOM position. */
.skip-link.skip-carousel {
  position: absolute;
  left: -9999px;
  top: auto;
  transform: none;
  width: 1px;
  height: 1px;
  overflow: hidden;
  white-space: nowrap;
  clip: rect(0, 0, 0, 0);
  margin: 0;
  padding: 0;
}
.skip-link.skip-carousel:focus,
.skip-link.skip-carousel:focus-visible {
  position: static;
  left: auto;
  display: inline-block;
  width: auto;
  height: auto;
  overflow: visible;
  white-space: normal;
  padding: 10px 28px;
  margin: 8px 0 16px;
  clip: auto;
}
body {
  margin: 0;
  background: var(--cream-tint);
  color: var(--ink);
  font-family: var(--sans);
  font-size: 17px;
  line-height: 1.55;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  min-height: 100vh;
  padding: 16px;
}
@media (min-width: 768px)  { body { padding: 20px 16px 16px; } }
@media (min-width: 1024px) { body { padding: 32px 24px 24px; } }
::selection { background: var(--accent); color: var(--cream); }
a { color: inherit; text-decoration: none; }
img, svg { display: block; max-width: 100%; }

/* ---------- HERO HOVER ---------- */
/* Hero/cover/card panels lift gently on hover. */
.hero,
.cover,
.card { transition: box-shadow .2s ease; }
.hero:hover,
.cover:hover,
.card:hover { box-shadow: var(--shadow-hover); }

/* ---------- GLOBAL FOCUS RING (WCAG 2.4.7) ----------
   Branded focus ring applied at all conformance levels - keyboard users
   need a visible focus indicator regardless of the WCAG toggle setting.
   The toggle only widens scope/contrast further at AA/AAA. */
:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: 2px;
}


/* ---------- READING-PROGRESS RAIL ----------
   Decorative dotted vertical line in the left page margin, with a
   single brighter dot that tracks the user's scroll position.
   Injected by oldworld.js on every page; the desktop-only media
   query below hides it below 1024px so it doesn't crowd phones or
   tablets. aria-hidden because purely decorative; pointer-events:
   none so it never blocks interaction. */
.progress-rail {
  position: fixed;
  top: 96px;
  bottom: 64px;
  left: 14px;
  width: 4px;
  pointer-events: none;
  z-index: 5;
}
.progress-rail-line {
  position: absolute;
  inset: 0;
  background-image: radial-gradient(circle, var(--accent-soft) 1.5px, transparent 2px);
  background-size: 4px 12px;
  background-repeat: repeat-y;
  background-position: center top;
  opacity: 0.6;
}
.progress-rail-dot {
  position: absolute;
  left: 50%;
  top: 0;
  width: 10px;
  height: 10px;
  background: var(--accent);
  border-radius: 50%;
  transform: translate(-50%, -50%);
  transition: top 0.06s linear;
}
@media (prefers-reduced-motion: reduce) {
  .progress-rail-dot { transition: none; }
}
/* Only show at desktop+ widths — below that the left margin is too
   tight to hold the rail without crowding content. */
@media (max-width: 1023px) {
  .progress-rail { display: none; }
}

/* The theme toggle's real input is visually hidden, so the focus ring
   has to be forwarded to the visible label pill. */
.theme-link:has(.theme-toggle:focus-visible) {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}
.theme-toggle:focus-visible { outline: none; }

/* ---------- TARGET SIZE (WCAG 2.5.8, AA, applied universally) ----------
   Every primary interactive element gets a 24×24 CSS px minimum hit area
   so AA target size passes at all conformance levels without requiring
   the WCAG toggle. The AAA toggle further upgrades these to 44×44. */
.nav-links a,
.top-link,
.back-link,
.nav-toggle {
  min-height: 24px;
  display: inline-flex;
  align-items: center;
}
.foot-col > a {
  min-height: 24px;
}
/* Footer-link top margin in A and AA - scoped to NOT-AAA so it doesn't
   stack with the AAA-specific margin-top on the same element. Negative
   value pulls the link text up to sit on the strapline's baseline. */
html:not(.wcag-aaa) .foot-col > a {
  margin-top: -17px;
}

/* ===========================================================
   WCAG AA OVERRIDES - only applied when the user toggles AA
   via the conformance bar (html.wcag-aa class).
   =========================================================== */

/* 1.4.3 Contrast - the brand vermilion (--accent) is now #C73100, which
   gives 4.7:1 on cream and clears AA normal text in default A as well.
   No AA-specific color override is needed. */

/* 1.4.11 Non-text contrast: conformance pill borders need 3:1. Applied
   at AA and inherited at AAA so the keyline doesn't visibly weaken when
   stepping up a level (1.4.11 is a Level AA criterion that still applies
   at AAA - there's no enhanced version, so the same darker rule is fine). */
html.wcag-aa .conformance-options,
html.wcag-aaa .conformance-options { border-color: var(--mute); }
html.wcag-aa .conformance-option,
html.wcag-aaa .conformance-option { border-right-color: var(--mute); }

/* 2.4.7 Focus visible + 2.5.8 Target Size are handled globally above -
   they apply at all conformance levels. */

/* 1.4.10 Reflow is now handled universally in the .top-bar rules above -
   the bar stacks vertically at narrow widths regardless of WCAG level. */

/* ===========================================================
   WCAG AAA OVERRIDES - only applied when the user toggles AAA
   via the conformance bar (html.wcag-aaa class).
   Brand-vermilion contrast tightening (AAA 1.4.6) is deferred
   until the branding palette is revisited.
   =========================================================== */

/* 2.3.3 Animation from Interactions - honour OS reduced-motion preference
   universally. The OS-level "Reduce motion" setting is an accessibility
   preference that applies regardless of the page's WCAG toggle, so this
   rule is not scoped to .wcag-aaa. */
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    transition-duration: 0.01ms !important;
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    scroll-behavior: auto !important;
  }
}

/* ===========================================================
   SCROLL-REVEAL — fade-up entrance on scroll. Applied to
   elements marked .reveal. Initial state is hidden + nudged
   down; JS adds .is-visible when the element enters the
   viewport, which fires the reveal-in @keyframes animation.

   IMPORTANT: this uses `animation` rather than `transition` by
   design. CSS `transition` is a single shorthand - if .reveal
   declared a `transition: opacity, transform` here, that value
   would override ANY hover transition declared on a .reveal
   element further down the cascade (e.g. .card.reveal's
   `transition: box-shadow`, .next-link.reveal's
   `transition: background-color`). `animation` is a separate
   property and doesn't share that cascade, so any .reveal
   element is free to declare whatever hover transition it
   wants without it being clobbered.

   Scoped under html.js so no-JS users see content immediately
   (no permanently hidden elements if JS fails to load). The
   global prefers-reduced-motion rule above collapses animation
   duration to 0.01ms so reduced-motion users get a near-instant
   appear-in-place rather than a slide.
   =========================================================== */
@keyframes reveal-in {
  from {
    opacity: 0;
    transform: translateY(24px);
  }
  to {
    opacity: 1;
    transform: none;
  }
}
html.js .reveal {
  opacity: 0;
  transform: translateY(24px);
  will-change: opacity, transform;
}
html.js .reveal.is-visible {
  opacity: 1;
  transform: none;
  animation: reveal-in 0.7s ease-out;
}

/* 1.4.6 Contrast (Enhanced) - deepen --mute so muted text clears 7:1
   on cream. Light mode: deeper steel; dark mode: lighter cornflower. */
html.wcag-aaa { --mute: #2F4B5F; }
html.wcag-aaa body:has(.theme-toggle:checked),
html.wcag-aaa.theme-dark body { --mute: #9CC1D6; }
@media (prefers-color-scheme: dark) {
  html.wcag-aaa:not(.theme-light) body { --mute: #9CC1D6; }
}

/* 1.4.6 Contrast (Enhanced) - Option 1 accent split. Vermilion (4.7:1
   on cream) passes AA normal and AAA large text, but not AAA normal
   (7:1). For AAA mode, swap small-text uses of accent to var(--ink)
   so they clear AAA. Large headings, decorative graphics, hover
   backgrounds, and accent rules keep the vermilion. */
/* .brand i is no longer in this AAA swap: at 24px it qualifies as large
   text (≥18pt), where vermilion #C73100 on cream clears AAA's 4.5:1 with
   ~4.65:1 to spare. The foot-brand stays small (~17px) so it still needs
   the swap; same for the body-h italic which is normal-size text. */
/* AAA 1.4.6 reroute - any small-text use of --accent or --accent-strong
   that doesn't clear 7:1 swaps to var(--ink) (16.5:1 light / 15.5:1 dark)
   when AAA is engaged. Resting states and hover states both go to ink so
   the colour never dips below 7:1 mid-interaction; underline thickness
   and position carry the hover affordance instead.
   .body-h .ital is intentionally NOT here - at 24px+ it qualifies as
   large text (≥18pt), where vermilion #C73100 on cream clears AAA's
   4.5:1 with ~4.65:1. Same for the cover-h, hero-h, section-h italics. */
html.wcag-aaa .foot-brand i,
html.wcag-aaa .foot-brand a:hover,
html.wcag-aaa .foot-brand a:hover i,
html.wcag-aaa .top-link,
html.wcag-aaa .top-link:hover,
html.wcag-aaa .top-link .arrow,
html.wcag-aaa .cover-tag a,
html.wcag-aaa .cover-tag a:hover,
html.wcag-aaa .contact-asterisk,
html.wcag-aaa .placeholder-note .ital,
html.wcag-aaa .foot-col > a:hover,
html.wcag-aaa .foot-col > a[target="_blank"]:hover::after,
html.wcag-aaa .theme-link:hover,
html.wcag-aaa .nav-links a:hover,
html.wcag-aaa .back-link:hover,
html.wcag-aaa .back-link:hover .arrow,
html.wcag-aaa .back-link .arrow {
  color: var(--ink);
}
/* The italic "Process" word in the Our Process heading sits on the
   inverted .approach panel - which flips background per mode (Deep
   Navy ink in light mode, white cream in dark mode). Each mode needs
   the accent calibrated against its panel surface:
     - Light mode panel #002B40 Deep Navy: #5BB5EC bright cobalt = 6.53:1 (AAA Lg)
     - Dark  mode panel #FFFFFF white:     #0E5A8F deep cobalt   = 7.30:1 (AAA) */
.approach .section-h .ital {
  color: #5BB5EC;
}
html.theme-dark .approach .section-h .ital,
body:has(.theme-toggle:checked) .approach .section-h .ital {
  color: #0E5A8F;
}
@media (prefers-color-scheme: dark) {
  html:not(.theme-light) .approach .section-h .ital {
    color: #0E5A8F;
  }
}

/* 2.5.5 Target Size (Enhanced) - small interactive elements grow to 44×44. */
html.wcag-aaa .nav-links a,
html.wcag-aaa .top-link,
html.wcag-aaa .back-link,
html.wcag-aaa .foot-col > a,
html.wcag-aaa .brand {
  min-height: 44px;
  padding: 10px 8px;
  display: inline-flex;
  align-items: center;
}
/* .nav-toggle gets the same AAA tap-area treatment but without the
   display: inline-flex - the toggle is display: none by default and
   only flipped to inline-flex at <768px via the .nav-collapsed rule.
   Forcing display here would make it appear at every viewport in AAA. */
html.wcag-aaa .nav-toggle {
  min-height: 44px;
  padding: 10px 8px;
}
/* Brand sits at the nav's left content edge - zero the AAA padding-left
   so it stays visually flush rather than shifting 8px in from the edge. */
html.wcag-aaa .brand { padding-left: 0; }
/* 2.5.5 Target Size (Enhanced) - the conformance pills need min-
   width as well as min-height because their text is narrow ("AA" /
   "AAA"). */
html.wcag-aaa .conformance-option {
  min-height: 44px;
  min-width: 44px;
}
/* Footer LinkedIn: keep flex-row + right-align + top-align so the link
   text both right-aligns within its column and visually top-aligns
   with the tagline. min-height still gives a 44×44 click area below. */
html.wcag-aaa .foot-col > a {
  display: flex;
  justify-content: flex-end;
  align-items: flex-start;
  padding-top: 0;
  margin-top: 4px;
}
/* The footer pipe separator in AAA: reclaim its left gutter, which the
   AAA `padding: 10px 8px` shorthand would otherwise wipe out. The pipe's
   vertical position (top: 4px from the default rule) already aligns with
   the top-of-text in both 24px and 44px anchor heights. */
html.wcag-aaa .foot-col > a + a {
  padding-left: 22px;
}

/* 1.4.8 Visual Presentation - looser line-heights on tight headings so
   wrapped lines breathe more. */
html.wcag-aaa .hero-h,
html.wcag-aaa .cover-h,
html.wcag-aaa .section-h,
html.wcag-aaa .body-h,
html.wcag-aaa .foot-brand,
html.wcag-aaa .case-body h3,
html.wcag-aaa .next-title {
  line-height: 1.25;
}

/* 3.1.4 Abbreviations - <abbr title="..."> markup is added universally in
   the HTML (semantically helpful even at A/AA), but only AAA exposes the
   visual cue (dotted underline) and help cursor. The default rule only
   resets the browser's underline+help-cursor so body-copy abbreviations
   render at the exact same kerning and density as the surrounding text. */
abbr[title] {
  text-decoration: none;
  cursor: inherit;
}
/* Scoped letter-spacing/kerning reset: only abbreviations inside wide-
   tracking parents (uppercase eyebrows, the conformance bar, etc.) need
   to be pinned back to normal so the abbreviation doesn't visually
   stretch with the parent's tracking. Body-copy abbrs are NOT included -
   they inherit normal tracking from the paragraph and stay tight. */
.conformance-label abbr[title],
.case-meta abbr[title],
.quote-attr abbr[title],
.foot-tag abbr[title],
.clients-sector abbr[title] {
  letter-spacing: normal;
  word-spacing: normal;
  font-variant: normal;
  font-feature-settings: normal;
}
html.wcag-aaa abbr[title] {
  text-decoration: underline dotted;
  text-underline-offset: 3px;
  text-decoration-color: var(--mute);
  cursor: help;
}

/* ===========================================================
   WCAG CONFORMANCE BAR - sits at the very top of every page.
   Three segmented pills (A / AA / AAA) - each applies the
   matching level of overrides defined above when selected.
   =========================================================== */
.top-bar {
  display: grid;
  grid-template-columns: auto 1fr;
  align-items: center;
  padding: 10px 0;
  background: var(--cream-tint);
  border-bottom: 0.5px solid var(--rule);
}
.top-bar > .conformance-bar {
  grid-column: 1;
  justify-self: start;
}
.top-bar > .theme-link {
  grid-column: 2;
  justify-self: end;
}
.conformance-bar {
  display: inline-flex;
  align-items: center;
  gap: 12px;
}
/* Conformance bar stays inline at every viewport - pills flush-left,
   "WCAG 2.2" label + tick immediately to their right. The fit is
   tight on narrow phones; .conformance-label uses white-space: nowrap
   below so the label never wraps onto two lines. */
/* Mobile (sub-tablet) - WCAG block sits flush left, theme toggle flush
   right. align-items: start keeps the theme link top-aligned with the
   WCAG label rather than the centre of the (taller, on narrow phones)
   stacked block. */
.conformance-label {
  font-family: var(--sans);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--mute);
  /* Tight line-height so the label's line box is the same height as
     the text itself - lets align-items: center match the visible
     mid-line of the text with the centre of the AA/AAA pill group
     rather than the centre of a taller default line box. */
  line-height: 1;
  /* Keep "WCAG 2.2" + tick on a single line at every viewport so the
     label never wraps under the pill group on narrow phones. */
  white-space: nowrap;
  /* Allow the inline tick SVG to sit on the label's text baseline. */
  display: inline-flex;
  align-items: center;
  gap: 6px;
}
/* Conformance tick — small inline SVG checkmark next to the "WCAG 2.2"
   label, signalling site-wide conformance. Decorative (aria-hidden);
   the visible WCAG / AA-AAA controls carry the actual meaning. The
   colour swaps per mode via --check so it clears WCAG 1.4.11 non-text
   contrast (≥3:1) on every palette × mode top-bar surface. */
.conformance-tick {
  width: 12px;
  height: 12px;
  flex-shrink: 0;
  color: var(--check);
  /* Nudge up 1px so the tick's visual centre aligns with the cap-height
     of the uppercase WCAG label text (flexbox centres against the line
     box, which sits slightly below the cap-line). */
  transform: translateY(-1px);
}
.conformance-options {
  display: inline-flex;
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  overflow: hidden;
}
.conformance-option {
  padding: 5px 12px;
  border: none;
  border-right: 1px solid var(--rule);
  background: transparent;
  font-family: var(--sans);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--mute);
  /* Explicit line-height so the pill button height is deterministic
     across browsers (defaults for <button> can vary). */
  line-height: 1.2;
  cursor: pointer;
  transition: background .2s ease, color .2s ease;
}
.conformance-option:last-child { border-right: none; }
/* Active state is driven from the html.wcag-* class which the inline
   pre-render script in each <head> sets before first paint. AA is the
   baseline (AA subsumes A in WCAG, so no separate A button), AAA is
   the stricter opt-in. */
html:not(.wcag-aaa) .conformance-option[data-level="aa"],
html.wcag-aaa .conformance-option[data-level="aaa"] {
  background: var(--ink);
  color: var(--cream);
  cursor: default;
}
/* Hover only colours the inactive option. */
html:not(.wcag-aaa) .conformance-option[data-level="aaa"]:hover,
html.wcag-aaa .conformance-option[data-level="aa"]:hover {
  color: var(--ink);
}
@media (min-width: 768px) {
  .top-bar { padding: 12px 0; }
  .conformance-bar { gap: 14px; }
  .conformance-label, .conformance-option { font-size: 11px; letter-spacing: 0.16em; }
  .conformance-option { padding: 6px 14px; }
}
@media (min-width: 1024px) {
  .top-bar { padding: 14px 0; }
}

/* ---------- PAGE FRAME ---------- */
.page {
  /* Transparent — body already paints the cream-tint surface and
     the rounded corners are invisible against it. Leaving .page
     unfilled means any future ambient backgrounds applied to body
     show through here too. */
  background: transparent;
  border-radius: var(--radius);
  /* No overflow:hidden here - it was clipping the box-shadow on .hero /
     .cover hover states. The rounded corners on .page are invisible
     against the body's matching cream-tint background anyway, and the
     children that touch .page's edges (.top-bar at top, .foot at bottom)
     also share that bg, so dropping the clip has no visual cost. */
  margin: 0 auto;
  max-width: 720px;
}

/* ===========================================================
   NAV - index.html, about.html, contact.html, 404.html
   (case-study pages use the simpler .topbar below instead)
   =========================================================== */
.nav {
  background: var(--cream);
  display: flex;
  align-items: center;
  justify-content: space-between;
  position: relative;
  /* Asymmetric vertical padding to optically centre the text - serif
     ascenders make Fraunces sit visually high in a box that's centred
     by math. Adding a couple more px above pushes the glyphs down. */
  padding: 16px 18px 12px;
  border-bottom: 0.5px solid var(--rule);
}

/* ---------- MOBILE NAV TOGGLE ----------
   Progressive enhancement: without JS the nav stays inline at all
   widths. With JS, the .nav-collapsed class is added at page load,
   which at < 768px hides the inline links behind a "Menu" button. */
.nav-toggle {
  display: none;
  align-items: center;
  gap: 8px;
  background: none;
  border: 0;
  padding: 8px 4px;
  margin: 0;
  cursor: pointer;
  color: var(--ink);
  font-family: var(--sans);
}
.nav-toggle-text {
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
}
.nav-toggle-text[data-state="open"] { display: none; }
.nav-toggle[aria-expanded="true"] .nav-toggle-text[data-state="closed"] { display: none; }
.nav-toggle[aria-expanded="true"] .nav-toggle-text[data-state="open"] { display: inline; }
.nav-toggle-icon {
  display: inline-flex;
  flex-direction: column;
  justify-content: center;
  gap: 4px;
  width: 20px;
  height: 11px;
}
.nav-toggle-icon span {
  display: block;
  height: 1px;
  width: 100%;
  background: currentColor;
  transition: transform .2s ease, opacity .2s ease;
}
/* Hamburger morphs into a close-X when the menu is open. */
.nav-toggle[aria-expanded="true"] .nav-toggle-icon span:nth-child(1) { transform: translateY(5px) rotate(45deg); }
.nav-toggle[aria-expanded="true"] .nav-toggle-icon span:nth-child(2) { opacity: 0; }
.nav-toggle[aria-expanded="true"] .nav-toggle-icon span:nth-child(3) { transform: translateY(-5px) rotate(-45deg); }
.nav-toggle:hover { color: var(--accent); }

@media (max-width: 767px) {
  .nav.nav-collapsed .nav-toggle { display: inline-flex; }
  /* Default-closed: links hidden */
  .nav.nav-collapsed .nav-links { display: none; }

  /* Open: overlay fills the entire viewport. Body scroll is locked
     via :has() below. The brand + toggle button keep their natural
     positions but are raised above the overlay with z-index. */
  .nav.nav-collapsed .nav-toggle[aria-expanded="true"] + .nav-links {
    display: flex;
    position: fixed;
    inset: 0;
    z-index: 50;
    background: var(--cream);
    padding: 80px 24px 40px;
    flex-direction: column;
    justify-content: center;
    align-items: center;
    gap: 8px;
    overflow-y: auto;
  }
  .nav.nav-collapsed:has(.nav-toggle[aria-expanded="true"]) .brand,
  .nav.nav-collapsed .nav-toggle[aria-expanded="true"] {
    position: relative;
    z-index: 60;
  }
  .nav.nav-collapsed .nav-toggle[aria-expanded="true"] + .nav-links a {
    font-family: var(--serif);
    font-weight: 400;
    font-size: 36px;
    line-height: 1.2;
    letter-spacing: -0.025em;
    color: var(--ink);
    text-align: center;
    padding: 10px 0;
    min-height: 56px;
  }
  .nav.nav-collapsed .nav-toggle[aria-expanded="true"] + .nav-links a:hover { color: var(--accent); }
  /* Lock background scroll while the overlay is open. */
  body:has(.nav-toggle[aria-expanded="true"]) { overflow: hidden; }
}
.brand {
  display: flex;
  align-items: center;
  gap: 8px;
  font-family: var(--serif);
  font-weight: 500;
  font-size: 24px;
  line-height: 1;
  letter-spacing: -0.02em;
  color: var(--ink);
  transition: color .25s ease;
}
.brand:hover { color: var(--accent); }
/* Logo text - the nav brand always shows "OWC" (abbreviated) at every
   breakpoint. The footer brand always shows the full "Oldworld Creative"
   wordmark, so .foot-brand .brand-short stays hidden throughout. */
.brand .brand-full { display: none; }
.brand .brand-short { display: inline; }
.foot-brand .brand-short { display: none; }
.brand-mark {
  position: relative;
  display: inline-block;
  width: 14px;
  height: 14px;
  color: var(--accent);
  flex-shrink: 0;
}
.brand-mark .brand-blot {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
  z-index: 2;
}
.brand-mark .brand-ring {
  position: absolute;
  inset: 0;
  border-radius: 50%;
  border: 1.25px solid currentColor;
  opacity: 0;
  z-index: 1;
  pointer-events: none;
  display: block;
  transform-origin: center center;
}
.brand:hover .brand-ring { animation: brand-ripple .9s ease forwards; }
@keyframes brand-ripple {
  0%   { transform: scale(0.55); opacity: .7; }
  100% { transform: scale(2.6);  opacity: 0;  }
}
@media (prefers-reduced-motion: reduce) {
  .brand:hover .brand-ring {
    animation: none;
    transform: none;
    opacity: 0;
  }
}
.brand i {
  font-style: italic;
  color: var(--accent);
  transition: color .25s ease;
}
.nav-links {
  display: flex;
  gap: 14px;
  font-size: 12px;
  font-weight: 500;
  letter-spacing: 0.02em;
}
.nav-links a {
  position: relative;
  padding-bottom: 3px;
  transition: color .2s ease;
}
.nav-links a:hover { color: var(--accent); }

@media (min-width: 768px) {
  .nav { padding: 18px 24px 14px; }
  .brand { font-size: 28px; gap: 10px; }
  .brand-mark { width: 16px; height: 16px; }
  .nav-links { gap: 24px; font-size: 13px; }
}
@media (min-width: 1024px) {
  .nav { padding: 20px 32px 16px; }
  .brand { font-size: 32px; gap: 12px; }
  .brand-mark { width: 18px; height: 18px; }
  .nav-links { gap: 32px; font-size: 14px; }
}

/* ===========================================================
   TOPBAR + BACK LINK - case-study pages only
   =========================================================== */
.topbar {
  background: var(--cream);
  display: flex;
  align-items: center;
  padding: 14px 18px;
  border-bottom: 0.5px solid var(--rule);
}
.back-link {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  font-family: var(--sans);
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink);
  cursor: pointer;
  transition: color .2s ease;
}
.back-link .arrow {
  font-family: var(--serif);
  font-style: italic;
  font-size: 22px;
  /* At mobile (22px serif italic regular) this is Normal-size text,
     so it needs the AA 4.5:1 floor. The grounded --accent (#C73100)
     only reaches 4.33:1 on the cream-tint page bg. Use --accent-strong
     (5.62:1 light, 5.58:1 dark) so the arrow clears AA Normal at every
     breakpoint. */
  color: var(--accent-strong);
  line-height: 1;
  transform: translateY(-1px);
  transition: transform .25s ease;
}
/* Hover on cream-tint (page bg) - the grounded --accent (#C73100) only
   reaches 4.33:1 there which fails AA Normal at the 12px back-link
   size. Use --accent-strong for the same role here as in the footer
   variant. AAA toggle routes both this hover and the arrow to ink. */
.back-link:hover { color: var(--accent-strong); }
.back-link:hover .arrow { transform: translateX(-4px) translateY(-1px); }
@media (min-width: 768px) {
  .topbar { padding: 18px 32px; }
  .back-link { font-size: 12px; gap: 12px; letter-spacing: 0.16em; }
  .back-link .arrow { font-size: 24px; }
}

/* ===========================================================
   HERO - index.html
   =========================================================== */
.hero {
  background: var(--cream);
  padding: 36px 18px;
  border-bottom-left-radius: var(--radius);
  border-bottom-right-radius: var(--radius);
  /* Sit in the positioned stacking layer so the hover box-shadow paints
     on top of the preceding .nav (which has position: relative). Without
     this, the nav layer paints over the hero's upward-extending shadow. */
  position: relative;
}
.hero-h {
  font-family: var(--serif);
  font-weight: 400;
  font-size: 45px;
  line-height: 1;
  letter-spacing: -0.025em;
  margin: 0;
  color: var(--ink);
}
.hero-h .ital {
  font-style: italic;
  color: var(--accent);
}
.hero-h .marked {
  position: relative;
  display: inline-block;
}
.hero-h .marked svg {
  position: absolute;
  left: -4px;
  right: -4px;
  bottom: -4px;
  width: calc(100% + 8px);
  height: 10px;
  overflow: visible;
}
/* CSS overrides the hardcoded SVG stroke colour so the brushstroke
   follows --accent through theme/mode switches (bright vermilion in
   dark mode, grounded vermilion in light, etc.). */
.hero-h .marked svg path { stroke: var(--accent); }
.hero-h .marked .stack-2,
.hero-h .marked .stack-3 {
  opacity: 0;
  transform: translateY(-4px);
  transition: opacity .15s ease, transform .15s cubic-bezier(.5, 0, .2, 1);
}
.hero-h .marked .stack-2 { transition-delay: .05s; }
.hero-h .marked .stack-3 { transition-delay: .15s; }
.hero-h:hover .marked .stack-2,
.hero-h:hover .marked .stack-3 {
  opacity: 0.8;
  transform: translateY(0);
}
.hero-lede {
  font-family: var(--sans);
  font-size: 15px;
  line-height: 1.55;
  color: var(--ink-soft);
  margin: 26px 0 0;
  max-width: 640px;
}
.hero-lede strong { font-weight: 600; }

/* Hero illustration — sits below the lede inside the hero. Same
   framing as .cover-art on case-study pages so the visual
   language is consistent. */
.hero-art {
  margin: 28px 0 0;
  background: var(--cream);
  border-radius: var(--radius);
  overflow: hidden;
}
.hero-art img {
  display: block;
  width: 100%;
  height: auto;
  /* Same navy tint as .cover-art img — see comment there for the
     filter recipe. */
  filter: sepia(1) hue-rotate(170deg) saturate(0.7) brightness(0.95);
}

@media (min-width: 768px) {
  .hero { padding: 48px 28px; }
  .hero-h { font-size: 60px; letter-spacing: -0.03em; }
  .hero-h .marked svg { bottom: -6px; height: 14px; }
  .hero-lede { font-size: 15px; margin-top: 32px; }
}
@media (min-width: 1024px) {
  .hero { padding: 56px 32px; }
  .hero-h { font-size: 76px; letter-spacing: -0.035em; }
  .hero-h .marked svg { bottom: -8px; height: 18px; left: -6px; right: -6px; width: calc(100% + 12px); }
  .hero-lede { font-size: 16px; margin-top: 44px; }
}

/* ===========================================================
   COVER HERO - about.html and case-study pages
   =========================================================== */
.cover {
  background: var(--cream);
  padding: 36px 18px;
  border-bottom-left-radius: var(--radius);
  border-bottom-right-radius: var(--radius);
  /* Same fix as .hero - keeps the hover shadow visible over the .nav
     on about and contact pages, where .nav (position: relative) would
     otherwise paint over the shadow's upward extension. */
  position: relative;
}
/* When the cover ends with an image, tighten the bottom padding so
   the gap below the image matches the gap on its sides. */
.cover:has(.cover-art) { padding-bottom: 18px; }

/* Cover hero illustration */
.cover-art {
  margin: 0;
  background: var(--cream);
  border-radius: var(--radius);
  overflow: hidden;
}
.cover-art img {
  display: block;
  width: 100%;
  height: auto;
  /* Tint the warm watercolours toward the cool-palette navy.
     sepia() first normalises every tone to a uniform brown, then
     hue-rotate moves that brown band into the navy region, then
     saturate pulls back so it doesn't read as a stained negative.
     Tweak the three numbers below to taste:
       hue-rotate  →  larger = more cyan; smaller = more violet
       saturate    →  lower  = quieter; higher = more vivid
       brightness  →  lower  = darker / moodier */
  filter: sepia(1) hue-rotate(170deg) saturate(0.7) brightness(0.95);
}
.cover-h {
  font-family: var(--serif);
  font-weight: 400;
  font-size: 45px;
  line-height: 1;
  letter-spacing: -0.025em;
  margin: 0 0 14px;
  color: var(--ink);
}
.cover-h .ital { font-style: italic; color: var(--accent); }
.cover-tag {
  font-family: var(--serif);
  font-style: italic;
  font-size: 18px;
  line-height: 1.3;
  color: var(--ink-soft);
  margin: 0 0 28px;
  max-width: 540px;
}
/* External link inside .cover-tag - vermilion underline + the same
   north-east arrow glyph used on the footer LinkedIn link to signal
   "opens in a new tab". The arrow is upright (font-style: normal) so
   it doesn't lean with the surrounding italic body. */
.cover-tag a {
  color: var(--accent);
  text-decoration: underline;
  text-decoration-thickness: 0.5px;
  text-underline-offset: 3px;
  transition: color .2s ease;
}
.cover-tag a:hover { color: var(--accent-strong); }
.cover-tag a[target="_blank"]::after {
  content: "\2197";
  display: inline-block;
  font-family: var(--sans);
  font-style: normal;
  font-size: 0.8em;
  margin-left: 2px;
  line-height: 1;
  transition: transform .25s ease;
}
.cover-tag a[target="_blank"]:hover::after { transform: translate(2px, -2px); }

/* ---------- PLACEHOLDER NOTE ----------
   Quiet, centred message used in place of cards while a section is
   unfinished (e.g. "Case study coming soon."). No card chrome - just
   centred serif type with generous top/bottom breathing room so it
   sits as an aside between the surrounding cards. */
.stack .placeholder-note {
  text-align: center;
  margin: 56px 18px;
  font-family: var(--serif);
  font-weight: 400;
  font-size: 24px;
  line-height: 1.3;
  letter-spacing: -0.015em;
  color: var(--ink-soft);
}
.placeholder-note .ital {
  font-style: italic;
  color: var(--accent);
}
@media (min-width: 768px) {
  .stack .placeholder-note { font-size: 26px; margin: 72px 24px; }
}
@media (min-width: 1024px) {
  .stack .placeholder-note { font-size: 30px; margin: 88px 32px; }
}

/* ---------- CASE IMAGE (case-study page in-line process images)
   Standalone figure between cards in the .stack. Visually treated
   as a card itself - same cream surface, padding, border-radius,
   and hover lift as .card - so it sits in the rhythm of the
   surrounding content rather than reading as a bare image strip.
   The inner <img> gets a slightly smaller border-radius so it
   tucks nicely inside the card's padding. */
.case-image {
  margin: 22px 0;
  background: var(--cream);
  border-radius: var(--radius);
  padding: 28px 18px 24px;
  position: relative;
  transition: box-shadow .2s ease;
}
.case-image:hover { box-shadow: var(--shadow-hover); }
.case-image img {
  display: block;
  width: 100%;
  height: auto;
  border-radius: calc(var(--radius) - 4px);
}
.case-image figcaption {
  margin-top: 14px;
  font-family: var(--sans);
  font-size: 13.5px;
  line-height: 1.55;
  color: var(--ink-soft);
}
.caption-label {
  display: inline-block;
  margin-right: 6px;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--accent);
  vertical-align: baseline;
}
@media (min-width: 768px) {
  .case-image { margin: 28px 0; padding: 32px 24px; }
  .case-image figcaption { margin-top: 18px; font-size: 14px; }
}
@media (min-width: 1024px) {
  .case-image { padding: 40px 32px 36px; }
}

/* ---------- CASE IMAGE GRID (two-up layout for paired image cards) ----------
   Wraps .case-image figures in a responsive grid so they sit side by side
   from tablet up. Margin on individual .case-image figures is zeroed inside
   the grid - the grid gap controls spacing instead. Padding is also
   tightened (vs the standalone .case-image default) so the images and
   captions have more room within each card. */
.case-image-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: 22px;
  margin: 22px 0;
}
.case-image-grid > .case-image {
  margin: 0;
  padding: 12px;
}
.case-image-grid > .case-image figcaption {
  margin-top: 10px;
}
@media (min-width: 768px) {
  .case-image-grid {
    grid-template-columns: repeat(2, 1fr);
    gap: 28px;
    margin: 28px 0;
  }
  .case-image-grid > .case-image { padding: 14px; }
  .case-image-grid > .case-image figcaption { margin-top: 12px; }
}
@media (min-width: 1024px) {
  .case-image-grid > .case-image { padding: 16px; }
  .case-image-grid > .case-image figcaption { margin-top: 14px; }
}

/* ---------- VIDEO TRANSCRIPT (collapsible text alternative for video) ----------
   Satisfies WCAG 1.2.3 Audio Description or Media Alternative (Prerecorded)
   at Level A by providing a text alternative for the video. The native
   <details>/<summary> element gives free keyboard support and screen-reader
   announcements without JavaScript. */
.video-transcript {
  margin-top: 22px;
}
.video-transcript > summary {
  cursor: pointer;
  font-family: var(--sans);
  font-size: 14px;
  font-weight: 600;
  color: var(--accent);
  padding: 8px 0;
  list-style: none;
  display: inline-flex;
  align-items: center;
  gap: 8px;
}
.video-transcript > summary::-webkit-details-marker { display: none; }
.video-transcript > summary::before {
  content: '+';
  display: inline-block;
  font-family: var(--serif);
  font-size: 18px;
  font-style: italic;
  width: 14px;
  transition: transform .2s ease;
}
.video-transcript[open] > summary::before { content: '-'; }
.video-transcript > summary:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 4px;
  border-radius: 4px;
}
.video-transcript > .body-p {
  margin-top: 12px;
}
@media (min-width: 768px) {
  .video-transcript { margin-top: 28px; }
}

/* ---------- VIDEO FRAME (responsive 16:9 iframe wrapper) ----------
   Used to embed video players (YouTube, Vimeo, etc.) inside a .card so
   the iframe scales with the card width while preserving the 16:9
   aspect ratio. The dark fill gives the player a clean letterbox while
   the iframe is loading. */
.video-frame {
  position: relative;
  width: 100%;
  margin-top: 22px;
  aspect-ratio: 16 / 9;
  border-radius: calc(var(--radius) - 4px);
  overflow: hidden;
  background: var(--ink);
}
.video-frame iframe {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  border: 0;
  display: block;
}
@media (min-width: 768px) {
  .video-frame { margin-top: 28px; }
}

/* ---------- CAROUSEL (image + caption slides inside a .card) ----------
   Uses native horizontal scroll-snap so swipe / trackpad / arrow keys
   work without JS. JS enhances with prev/next buttons + dot indicators
   and keeps everything in sync. Respects prefers-reduced-motion. */
.carousel {
  margin-top: 24px;
  position: relative;
}
/* When a carousel sits inside a card (case-study Project artefacts
   pattern), the .body-h above already provides bottom margin so the
   carousel doesn't need its own top margin. The absolutely-positioned
   .skip-carousel link between them can block margin-collapse in some
   browsers, making the gap visibly large; zeroing out the carousel's
   top margin here closes that. */
.card > .carousel {
  margin-top: 0;
}
/* Tighten the heading's bottom margin when it directly precedes a
   carousel widget. The standard .body-h margin-bottom (18-24px) is
   designed for body copy below; for a carousel widget it reads as
   excessive whitespace. Uses :has() to scope to .card sections that
   contain a carousel - falls back gracefully (regular margin) in
   browsers without :has() support. */
.card:has(> .carousel) > .body-h {
  margin-bottom: 8px;
}
.carousel-viewport {
  display: flex;
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  scroll-behavior: smooth;
  scrollbar-width: none;
  -ms-overflow-style: none;
  gap: 0;
  /* Viewport is tabbable (tabindex="0" in markup) so arrow keys can
     drive the carousel. Visible focus indicator is required by WCAG
     2.4.7 - applied via outline with outline-offset so the ring sits
     outside the viewport's overflow boundary. */
  border-radius: calc(var(--radius) - 4px);
  /* Touch hardening - native scroll-snap already makes the viewport
     swipe-able on touch devices; these properties prevent two known
     iOS quirks. overscroll-behavior-x: contain stops a swipe at the
     carousel edge from triggering Safari's back/forward navigation
     gesture. -webkit-overflow-scrolling: touch enables momentum
     scroll on older iOS Safari (modern Safari ignores it harmlessly). */
  overscroll-behavior-x: contain;
  -webkit-overflow-scrolling: touch;
}
.carousel-viewport::-webkit-scrollbar { display: none; }
.carousel-viewport:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 4px;
}
.carousel-slide {
  flex: 0 0 100%;
  min-width: 0;
  scroll-snap-align: start;
  scroll-snap-stop: always;
}
.carousel-slide figure {
  margin: 0;
}
.carousel-slide img {
  display: block;
  width: 100%;
  height: auto;
  border-radius: calc(var(--radius) - 4px);
}
/* CSS-only placeholder for carousel slides whose real image hasn't
   been added yet. Mirrors the carousel-image aspect (672:493) so the
   viewport stays the right height; dashed inner frame + small label
   make the placeholder visually obvious vs a real image. */
.carousel-placeholder {
  aspect-ratio: 672 / 493;
  background: var(--cream-tint);
  border-radius: calc(var(--radius) - 4px);
  display: flex;
  align-items: center;
  justify-content: center;
  position: relative;
}
.carousel-placeholder::before {
  content: '';
  position: absolute;
  inset: 16px;
  border: 2px dashed var(--mute);
  border-radius: calc(var(--radius) - 8px);
  opacity: 0.35;
  pointer-events: none;
}
.carousel-placeholder-label {
  font-family: var(--sans);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--mute);
  position: relative;
}
@media (min-width: 768px) {
  .carousel-placeholder-label { font-size: 12px; }
}
.carousel-slide figcaption {
  margin-top: 14px;
  font-family: var(--sans);
  font-size: 13.5px;
  line-height: 1.55;
  color: var(--ink-soft);
}
/* Controls row: prev | dots | next, centred under the viewport. */
.carousel-controls {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 18px;
  margin-top: 18px;
}
.carousel-btn {
  appearance: none;
  background: transparent;
  border: 1px solid var(--ink-soft);
  color: var(--ink);
  width: 36px;
  height: 36px;
  border-radius: 50%;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  font-family: var(--sans);
  font-size: 14px;
  line-height: 1;
  padding: 0;
  transition: background-color .2s ease, color .2s ease, border-color .2s ease, opacity .2s ease;
}
.carousel-btn:hover {
  background: var(--ink);
  color: var(--cream);
  border-color: var(--ink);
}
.carousel-btn:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}
.carousel-btn .arrow { display: inline-block; line-height: 1; }
.carousel-dots {
  display: inline-flex;
  align-items: center;
  gap: 2px;
}
/* Dot button is 24x24 (WCAG 2.5.8 minimum target size) with the
   visible 10px circle drawn via a pseudo-element. The button itself
   is transparent so the hit area expands without enlarging the dot. */
.carousel-dot {
  appearance: none;
  background: transparent;
  border: none;
  width: 24px;
  height: 24px;
  padding: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  border-radius: 50%;
}
.carousel-dot::before {
  content: '';
  width: 10px;
  height: 10px;
  border-radius: 50%;
  background: transparent;
  border: 1px solid var(--ink-soft);
  transition: background-color .2s ease, border-color .2s ease, transform .2s ease;
}
.carousel-dot:hover::before { border-color: var(--ink); }
.carousel-dot:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 0;
}
.carousel-dot[aria-current="true"]::before {
  background: var(--accent);
  border-color: var(--accent);
  transform: scale(1.15);
}
@media (min-width: 768px) {
  .carousel { margin-top: 28px; }
  .carousel-slide figcaption { margin-top: 18px; font-size: 14px; }
  .carousel-controls { margin-top: 22px; }
}
@media (prefers-reduced-motion: reduce) {
  .carousel-viewport { scroll-behavior: auto; }
  .carousel-btn,
  .carousel-dot,
  .carousel-dot::before { transition: none; }
  .carousel-dot[aria-current="true"]::before { transform: none; }
}

/* ---------- INVERTED CARD VARIANT ----------
   Used for the case-study Project Details card so it visually echoes
   the Our Process panel on index. Dark ink fill + cream type in light
   mode; auto-inverts to a cream panel with dark type in dark mode
   because both background and text colours are token-driven. */
.card.invert {
  background: var(--ink);
  color: var(--cream);
}
.card.invert .body-h { color: var(--cream); }
/* Inverted card vermilion accents need to flip per mode because the
   panel bg flips with --ink. Light mode panel is #111 - bright #FF3D00
   reads 5.32:1 there (AAA Large for the italic). Dark mode panel is
   #F4EFE6 - the bright value only reaches 3.10:1, so swap to the
   grounded #C73100 (4.74:1, AAA Large). This mirrors the .approach
   .section-h .ital treatment elsewhere in the file. */
.card.invert .body-h .ital { color: #5BB5EC; }
.card.invert .top-link { color: var(--cream); }
.card.invert .top-link .arrow { color: #5BB5EC; }
.card.invert .top-link:hover { color: #5BB5EC; }
html.theme-dark .card.invert .body-h .ital,
html.theme-dark .card.invert .top-link .arrow,
html.theme-dark .card.invert .top-link:hover,
body:has(.theme-toggle:checked) .card.invert .body-h .ital,
body:has(.theme-toggle:checked) .card.invert .top-link .arrow,
body:has(.theme-toggle:checked) .card.invert .top-link:hover {
  color: #0E5A8F;
}
@media (prefers-color-scheme: dark) {
  html:not(.theme-light) .card.invert .body-h .ital,
  html:not(.theme-light) .card.invert .top-link .arrow,
  html:not(.theme-light) .card.invert .top-link:hover {
    color: #0E5A8F;
  }
}
/* AAA: the small .top-link arrow + hover inside .card.invert are
   normal text (12-16px) and only reach ~5:1 on the inverted panel.
   Route to var(--cream) (panel fg) so contrast clears 7:1. The
   translateY transform on hover still carries the affordance. */
html.wcag-aaa .card.invert .top-link .arrow,
html.wcag-aaa .card.invert .top-link:hover,
html.wcag-aaa .card.invert .top-link:hover .arrow {
  color: var(--cream);
}
.card.invert .specs dt { color: var(--accent-soft); }
.card.invert .specs dd { color: var(--cream); }
.card.invert .specs .spec.body dd { color: var(--accent-soft); }
/* AAA: the italic accent needs ≥7:1 - swap it to cream so it matches
   the rest of the heading at ~15:1, mirroring the Our Process pattern. */
/* .card.invert .body-h .ital no longer swapped in AAA: the bright
   #FF3D00 vermilion on ink clears AAA large at ~4.82:1. */

/* ---------- PROJECT SPECS GRID ---------- */
/* Used on case-study pages as a project-metadata card directly under the
   hero. Two-column stat-block grid for short fields (Client, Industry,
   Project time, Project delivery) with full-width rows for long-list
   fields (Location, Services, Expertise). Single column at mobile. */
.specs {
  display: grid;
  grid-template-columns: 1fr;
  column-gap: 28px;
  row-gap: 18px;
  margin: 0;
}
.specs .spec.full { grid-column: 1 / -1; }
.specs dt {
  font-family: var(--sans);
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--mute);
  margin: 0 0 6px;
}
.specs dd {
  font-family: var(--serif);
  font-style: italic;
  font-size: 21px;
  line-height: 1.35;
  color: var(--ink);
  margin: 0;
}
.specs .spec.body dd {
  font-family: var(--sans);
  font-style: normal;
  font-size: 16px;
  line-height: 1.55;
  color: var(--ink-soft);
}
@media (min-width: 768px) {
  .specs {
    grid-template-columns: repeat(2, 1fr);
    row-gap: 22px;
  }
}

/* Visually-hidden helper - text reachable by AT but not painted on screen.
   Used inside external links for the "(opens in new tab)" hint. */
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}
@media (min-width: 768px) {
  .cover { padding: 56px 32px; }
  .cover:has(.cover-art) { padding-bottom: 32px; }
  .cover-h { font-size: 76px; letter-spacing: -0.035em; margin-bottom: 18px; }
  .cover-tag { font-size: 22px; margin-bottom: 36px; }
}

/* ===========================================================
   STACK + CARD - all pages
   =========================================================== */
.stack {
  background: var(--cream-tint);
  padding: 18px 0 9px;
}
.card {
  background: var(--cream);
  border-radius: var(--radius);
  padding: 28px 18px 24px;
  position: relative;
}

/* ---------- LEAD CARD VARIANT (Style E) ----------
   "An Introduction" lead card on case-study pages - same cream card
   chrome as the rest, but laid out as a two-column split: heading in a
   narrower left column, body running as a serif italic lede in the
   wider right column. Reads as a magazine pull-quote opener. Collapses
   to a single column at the smallest breakpoint. */
.card.lead {
  display: grid;
  grid-template-columns: 1fr;
  gap: 16px;
  align-items: start;
}
.card.lead .body-h { margin: 0; }
.card.lead .body-p {
  font-family: var(--serif);
  font-style: italic;
  font-size: 18px;
  line-height: 1.5;
}
@media (min-width: 768px) {
  .card.lead {
    grid-template-columns: 1fr 2fr;
    gap: 32px;
  }
  .card.lead .body-p { font-size: 20px; }
}
@media (min-width: 1024px) {
  .card.lead .body-p { font-size: 22px; }
}

/* Top-right anchor link, visible only when the card is hovered.
   Appears in the Recent Work and Design Services cards on index. */
.top-link {
  position: absolute;
  top: 28px;
  right: 18px;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-family: var(--sans);
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--mute);
  opacity: 0;
  transform: translateY(-2px);
  transition: opacity .25s ease, transform .25s ease, color .25s ease;
  pointer-events: none;
  z-index: 2;
}
.top-link .arrow {
  font-family: var(--serif);
  font-style: italic;
  font-size: 16px;
  color: var(--accent);
  line-height: 1;
  transition: transform .25s ease;
}
.top-link:hover .arrow { transform: translateY(-3px); }
.card:hover > .top-link,
.card:focus-within > .top-link {
  opacity: 1;
  transform: translateY(0);
  pointer-events: auto;
}
.top-link:hover { color: var(--accent); }
/* Card spacing in the stack - every direct child of .stack gets a
   top margin separating it from the previous one. */
.stack > * + * { margin-top: 18px; }

@media (min-width: 768px) {
  .stack { padding: 24px 0 12px; }
  .card { padding: 32px 24px; }
  .stack > * + * { margin-top: 24px; }
  .top-link { top: 32px; right: 24px; font-size: 12px; }
}
@media (min-width: 1024px) {
  .stack { padding: 30px 0 15px; }
  .card { padding: 40px 32px 36px; }
  .stack > * + * { margin-top: 30px; }
  .top-link { top: 40px; right: 32px; }
}

/* ===========================================================
   SECTION HEADING - index.html
   =========================================================== */
.section-h {
  margin: 0 0 22px;
  font-family: var(--serif);
  font-weight: 400;
  font-size: 28px;
  line-height: 1.05;
  letter-spacing: -0.025em;
  color: var(--ink);
}
.section-h .ital {
  font-style: italic;
  color: var(--accent);
}
@media (min-width: 768px) {
  .section-h { font-size: 30px; margin-bottom: 28px; }
}
@media (min-width: 1024px) {
  .section-h { font-size: 36px; margin-bottom: 32px; letter-spacing: -0.03em; }
}

/* ===========================================================
   SERVICES - index.html
   =========================================================== */
.services-grid {
  border-top: 1.5px solid var(--rule);
}
.service {
  padding: 20px 0;
  border-bottom: 0.5px solid var(--rule);
}
.service:last-child { border-bottom: none; }
.service h3 {
  margin: 0 0 8px;
  font-family: var(--serif);
  font-weight: 500;
  font-size: 22px;
  line-height: 1.1;
  letter-spacing: -0.02em;
  color: var(--ink);
}
.service h3 em {
  font-style: italic;
}
.service p {
  margin: 0;
  font-family: var(--sans);
  font-size: 14px;
  line-height: 1.55;
  color: var(--ink-soft);
}
@media (min-width: 768px) {
  .services-grid {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
  }
  .service {
    padding: 24px 18px 26px;
    border-bottom: none;
    border-right: 0.5px solid var(--rule);
  }
  .service:last-child { border-right: none; }
  .service:first-child { padding-left: 0; }
  .service:last-child { padding-right: 0; }
  .service h3 { font-size: 22px; }
}
@media (min-width: 1024px) {
  .service { padding: 26px 22px 28px; }
  .service:first-child { padding-left: 0; }
  .service:last-child { padding-right: 0; }
  .service h3 { font-size: 24px; margin-bottom: 10px; }
}

/* ===========================================================
   WORK LIST - index.html
   =========================================================== */
.work-list {
  border-top: 1.5px solid var(--rule);
}
.case {
  display: grid;
  grid-template-columns: 56px 1fr;
  gap: 14px;
  align-items: center;
  padding: 16px 14px;
  border-bottom: 0.5px solid var(--rule);
  cursor: pointer;
  transition: background .25s ease, color .25s ease;
}
.case:hover,
.case:focus-visible {
  background: var(--accent);
  color: var(--cream-fixed);
}
.case:hover .logo,
.case:focus-visible .logo {
  background: var(--cream);
}
.case:hover .logo .case-mark,
.case:focus-visible .logo .case-mark {
  color: var(--accent);
}
.case:hover .case-meta,
.case:focus-visible .case-meta {
  color: var(--cream-fixed);
  opacity: 0.85;
}
.case:hover h3,
.case:focus-visible h3 {
  color: var(--cream-fixed);
}
.logo {
  width: 52px;
  height: 52px;
  background: var(--cream-tint);
  border-radius: 4px;
  display: flex;
  align-items: center;
  justify-content: center;
  position: relative;
  overflow: hidden;
  transition: background .25s ease;
}
/* Brand mark sits inside .logo - SVG inherits color via currentColor so it
   swaps from ink → accent on hover/focus alongside the case background.
   Raster (img) marks keep their own colours via object-fit. */
.logo .case-mark {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  padding: 8%;
  box-sizing: border-box;
  color: var(--ink);
  object-fit: contain;
  transition: color .25s ease;
}
/* When the mark is a raster logo, give the logo container a clean white
   background so the file's own white surround reads as intentional. A
   1 px hairline frames each logo and reads against both the cream-tint
   card and the vermilion hover background. */
.logo.has-logo-img {
  background: #FFFFFF;
  border: 1px solid var(--rule);
  box-sizing: border-box;
}
.case:hover .logo.has-logo-img,
.case:focus-visible .logo.has-logo-img { background: #FFFFFF; }
.case-body h3 {
  margin: 0;
  font-family: var(--serif);
  font-weight: 500;
  font-size: 24px;
  line-height: 1;
  letter-spacing: -0.02em;
  color: var(--ink);
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 8px;
}
.case-body h3 .arrow {
  font-family: var(--serif);
  font-style: italic;
  font-size: 19px;
  flex-shrink: 0;
  transition: transform .25s ease;
}
.case:hover .case-body h3 .arrow,
.case:focus-visible .case-body h3 .arrow { transform: translateX(4px); }
/* Hide the separate end-arrow at mobile so it doesn't drop to a new row */
.case-arrow { display: none; }
.case-meta {
  margin: 4px 0 0;
  font-family: var(--sans);
  font-size: 12px;
  letter-spacing: 0.04em;
  color: var(--mute);
}

@media (min-width: 768px) {
  .case {
    grid-template-columns: 72px 1fr 60px;
    gap: 20px;
    padding: 20px 20px;
  }
  .logo { width: 68px; height: 68px; }
  .case-body h3 { font-size: 28px; display: block; }
  .case-body h3 .arrow { display: none; }
  .case-meta { font-size: 12px; }
  .case-arrow {
    display: block;
    justify-self: end;
    font-family: var(--serif);
    font-style: italic;
    font-size: 24px;
    color: var(--ink);
    transition: color .25s ease, transform .25s ease;
  }
  .case:hover .case-arrow,
  .case:focus-visible .case-arrow { color: var(--cream-fixed); transform: translateX(4px); }
}
@media (min-width: 1024px) {
  .case {
    grid-template-columns: 88px 1fr 60px;
    gap: 24px;
    padding: 22px 24px;
  }
  .logo { width: 80px; height: 80px; border-radius: 6px; }
  .case-body h3 { font-size: 34px; letter-spacing: -0.03em; }
  .case-meta { font-size: 12px; margin-top: 6px; }
  .case-arrow { font-size: 26px; }
}

/* ===========================================================
   RECOVER LIST - 404.html
   Stacked list of recovery links shown on the 404 page. Each row
   uses the same hairline + arrow pattern as the case list, but
   stripped of the logo column.
   =========================================================== */
.recover-list {
  list-style: none;
  margin: 0;
  padding: 0;
  border-top: 1.5px solid var(--rule);
}
.recover-list li {
  border-bottom: 0.5px solid var(--rule);
}
.recover-list a {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 14px 14px;
  font-family: var(--serif);
  font-weight: 500;
  font-size: 20px;
  letter-spacing: -0.015em;
  color: var(--ink);
  text-decoration: none;
  transition: background .25s ease, color .25s ease, padding .25s ease;
}
.recover-list a:hover,
.recover-list a:focus-visible {
  background: var(--accent);
  color: var(--cream-fixed);
}
.recover-list .arrow {
  font-family: var(--serif);
  font-style: italic;
  font-size: 18px;
  flex-shrink: 0;
  transition: transform .25s ease;
}
.recover-list a:hover .arrow,
.recover-list a:focus-visible .arrow { transform: translateX(4px); }
@media (min-width: 768px) {
  .recover-list a { padding: 18px 20px; font-size: 24px; }
  .recover-list .arrow { font-size: 22px; }
}
@media (min-width: 1024px) {
  .recover-list a { padding: 20px 24px; font-size: 28px; }
}

/* ===========================================================
   CONTACT FORM - contact.html
   =========================================================== */
.contact-form { margin: 0; }
.contact-row { margin: 0 0 18px; }
.contact-row:last-child { margin-bottom: 0; }
.contact-label {
  display: block;
  margin: 0 0 6px;
  font-family: var(--sans);
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--mute);
}
.contact-input {
  width: 100%;
  padding: 10px 12px;
  font-family: var(--sans);
  font-size: 16px;
  line-height: 1.5;
  color: var(--ink);
  background: var(--cream-tint);
  /* Border uses --mute (≥4.6:1 on every card surface across both
     palettes) so the input boundary clears WCAG 2.2 SC 1.4.11
     (Non-text Contrast, ≥3:1) at rest. The previous --rule hairline
     at 10% alpha only reached ~1.2:1 against the surrounding card,
     which made the field hard to identify when unfocused. */
  border: 1px solid var(--mute);
  border-radius: var(--radius);
  transition: border-color .2s ease, background .2s ease;
  -webkit-appearance: none;
  appearance: none;
}
.contact-input:hover { border-color: var(--mute); }
.contact-input:focus {
  outline: none;
  border-color: var(--accent);
  background: var(--cream);
}
/* WCAG 2.2 SC 2.4.13 Focus Appearance (AAA) requires the focus
   indicator to be at least 2 CSS pixels thick. The 1px border colour
   change above isn't sufficient on its own, so a keyboard-focused
   input also gets the 2px accent outline (matching the global focus
   ring). Pointer focus keeps the clean borderless look. Placed after
   :focus so source order wins when both pseudo-classes match. */
.contact-input:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}
textarea.contact-input {
  min-height: 140px;
  resize: vertical;
  font-family: var(--sans);
}
.contact-actions {
  display: flex;
  align-items: center;
  gap: 18px;
  flex-wrap: wrap;
  margin-top: 26px;
}
.contact-submit {
  font-family: var(--sans);
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  padding: 11px 22px;
  border: 1px solid var(--ink);
  border-radius: 999px;
  background: var(--ink);
  color: var(--cream);
  cursor: pointer;
  transition: background .25s ease, color .25s ease, border-color .25s ease;
}
.contact-submit:hover { background: var(--accent); border-color: var(--accent); }
.contact-submit:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.contact-note {
  margin: 0;
  font-family: var(--sans);
  font-size: 12px;
  line-height: 1.5;
  color: var(--mute);
  max-width: 280px;
}
.contact-honeypot {
  position: absolute;
  left: -10000px;
  width: 1px;
  height: 1px;
  overflow: hidden;
}
/* Required-field indicator (WCAG 3.3.2 Labels or Instructions).
   The asterisk is decorative (aria-hidden) - the required state is
   communicated to assistive tech via the input's `required` attribute,
   and to sighted users via the visible "*" and the form-level note. */
.contact-required-note {
  margin: 0 0 22px;
  font-family: var(--sans);
  font-size: 12px;
  line-height: 1.5;
  color: var(--mute);
}
.contact-required-note .contact-asterisk { margin: 0 2px; }
.contact-asterisk {
  color: var(--accent);
  font-weight: 600;
  /* Sits inside .card (--cream background) where #C73100 clears AA 4.7:1. */
}
@media (min-width: 768px) {
  .contact-input { font-size: 16px; padding: 11px 14px; }
  .contact-actions { gap: 24px; }
}

/* ===========================================================
   CLIENT LOGO GRID - index.html "Our Clients" card
   =========================================================== */
.client-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 10px;
  list-style: none;
  margin: 0;
  padding: 0;
}
.client-logo {
  margin: 0;
  background: var(--cream-tint);
  border-radius: 4px;
  aspect-ratio: 5 / 2;
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: var(--sans);
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--mute);
  text-align: center;
  padding: 8px;
  line-height: 1.3;
}
@media (min-width: 768px) {
  .client-grid { grid-template-columns: repeat(4, 1fr); gap: 14px; }
}
@media (min-width: 1024px) {
  .client-grid { gap: 16px; }
  .client-logo { font-size: 12px; }
}

/* ===========================================================
   APPROACH - index.html (process inset, nested in Services card)
   Single-column vertical flow with a leading vermilion arrow.
   =========================================================== */
.approach {
  background: var(--ink);
  color: var(--cream);
  padding: 28px 22px 26px;
  border-radius: var(--radius);
  margin-top: 22px;
}
.approach .section-h {
  color: var(--cream);
  font-size: 24px;
  margin-bottom: 20px;
}

/* Vertical rail layout — replaces the SVG loop + bento grid. Each
   row pairs a circle (left) with its content box (right) in a 1fr/2fr
   split. A vertical rail line runs down the centre of the left column,
   passing through every circle's centre; the circles align vertically
   to the centre of their box. Hovering a row fills BOTH the circle
   and the box with the cobalt accent (highlight = fill, not keyline). */
.steps-rail {
  /* --rail-col-w: width of the left rail column (holds the circle).
     Kept just a touch wider than --rail-circle-w so the dotted line
     passing above/below the circle stays vertically inside the column.
     The right column takes the remaining space, so the boxes get the
     bulk of the width. --rail-col-gap controls the gap between the
     rail column edge and the box (breathing room to the right of each
     circle). --rail-circle-w controls the actual circle size and is
     bumped at each breakpoint in lockstep with --rail-col-w. */
  --rail-col-w: 76px;
  --rail-circle-w: 48px;
  --rail-col-gap: 10px;
  position: relative;
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  row-gap: 12px;
}
/* The rail line — a vertical run of chunky round dots, sized and
   coloured to stand out against the dark navy panel. Each dot is a
   radial-gradient tile (6px diameter, edge softened by 0.5px) repeated
   on a 14px vertical pitch; painted in the same cobalt accent that
   fills the active row, so the rail visually previews the highlight
   colour. Sits at the centre of the left 1/3 column: with a 1fr/2fr
   split and a fixed column-gap, that's (100% - gap)/6 from the
   container's left edge. Theme override below swaps to deep cobalt
   on the inverted (white) panel. A mask fades the very top and bottom
   so the line appears to start/end inside the first/last circle
   rather than overshoot. The line is z-index 0; circles + boxes
   sit above it. */
.steps-rail::before {
  content: "";
  position: absolute;
  top: 0;
  bottom: 0;
  left: calc(var(--rail-col-w) / 2);
  transform: translateX(-50%);
  width: 6px;
  background-image: radial-gradient(
    circle,
    #5BB5EC 3px,
    transparent 3.5px
  );
  background-size: 6px 14px;
  background-repeat: repeat-y;
  background-position: center top;
  /* Mask bounds the visible dotted line to the span between the first
     and last circle centres. JS measures the exact pixel positions of
     those circles and sets --rail-start / --rail-end on .steps-rail;
     the percentage fallbacks here give a reasonable approximation when
     JS is unavailable. Sharp stops (no fade) so the line clearly stops
     behind the first and last circles. */
  -webkit-mask-image: linear-gradient(
    to bottom,
    transparent 0,
    transparent var(--rail-start, 12%),
    #000 var(--rail-start, 12%),
    #000 var(--rail-end, 88%),
    transparent var(--rail-end, 88%),
    transparent 100%
  );
          mask-image: linear-gradient(
    to bottom,
    transparent 0,
    transparent var(--rail-start, 12%),
    #000 var(--rail-start, 12%),
    #000 var(--rail-end, 88%),
    transparent var(--rail-end, 88%),
    transparent 100%
  );
  z-index: 0;
  pointer-events: none;
}
/* Dark theme — the .approach panel inverts to white, so the bright
   cobalt washes out. Drop to deep cobalt for sufficient contrast. */
html.theme-dark .steps-rail::before,
body:has(.theme-toggle:checked) .steps-rail::before {
  background-image: radial-gradient(
    circle,
    #0E5A8F 3px,
    transparent 3.5px
  );
}
@media (prefers-color-scheme: dark) {
  html:not(.theme-light) .steps-rail::before {
    background-image: radial-gradient(
      circle,
      #0E5A8F 3px,
      transparent 3.5px
    );
  }
}

.step-row {
  display: grid;
  grid-template-columns: var(--rail-col-w) 1fr;
  column-gap: var(--rail-col-gap);
  align-items: center;
  position: relative;
  z-index: 1;
  cursor: pointer;
}

/* Circle node — sits centred in the left 1/3 column and vertically
   centred to the row (so its centre lines up with the box centre).
   The default fill matches the .approach panel background (var(--ink))
   so the circle sits cleanly *over* the dashed rail line, masking the
   dashes that pass behind it. A 1px currentColor stroke gives the
   keyline outline. The active state swaps the fill to the cobalt
   accent. */
.rail-node {
  width: var(--rail-circle-w);
  height: var(--rail-circle-w);
  border-radius: 50%;
  border: 1px solid currentColor;
  background: var(--ink);
  color: var(--cream);
  font-family: var(--serif);
  font-style: italic;
  font-size: 20px;
  line-height: 1;
  justify-self: center;
  align-self: center;
  display: grid;
  place-content: center;
  text-align: center;
  position: relative;
  z-index: 2;
  transition: background .15s ease, border-color .15s ease, color .15s ease;
}

/* Content box — right 2/3 column. Subtle tinted lift by default;
   fills with cobalt when its row is active. The cream tint reads as
   a faint lift on the dark navy panel in light mode; in dark mode
   the panel inverts to white, so the override below swaps the tint
   to navy at the same low opacity so each box stays visible. */
.step-box {
  background: rgba(244, 239, 230, 0.06);
  border-radius: var(--radius);
  padding: 18px 18px;
  transition: background .15s ease;
}
html.theme-dark .step-box,
body:has(.theme-toggle:checked) .step-box {
  background: rgba(0, 43, 64, 0.08);
}
@media (prefers-color-scheme: dark) {
  html:not(.theme-light) .step-box {
    background: rgba(0, 43, 64, 0.08);
  }
}
.step-box h4 {
  margin: 0 0 6px;
  font-family: var(--serif);
  font-size: 17px;
  font-weight: 500;
  color: var(--cream);
  letter-spacing: -0.01em;
  transition: color .15s ease;
}
.step-box p {
  margin: 0;
  font-family: var(--sans);
  font-size: 13px;
  line-height: 1.55;
  color: var(--accent-soft);
  transition: color .15s ease;
}
/* AAA: --accent-soft in dark mode (#1F4861) doesn't clear 7:1 against
   the inverted light panel at 13px. Route to var(--cream) so contrast
   clears AAA Normal; the h4 size + weight still differentiate title. */
html.wcag-aaa .step-box p { color: var(--cream); }

/* ===== Active / hover state — FILL highlight (not keyline) =====
   When the row is hovered (or JS-flagged .is-active), both the rail
   circle and the content box flood with the cobalt accent. The text
   inside flips to deep navy so it stays readable on the bright fill.
   Light mode (dark navy panel) → bright cobalt #5BB5EC + #002B40 text.
   Dark mode  (white panel)    → deep   cobalt #0E5A8F + #FFFFFF text. */
.step-row:hover .rail-node,
.step-row:focus-within .rail-node,
.step-row.is-active .rail-node {
  background: #5BB5EC;
  border-color: #5BB5EC;
  color: #002B40;
}
.step-row:hover .step-box,
.step-row:focus-within .step-box,
.step-row.is-active .step-box {
  background: #5BB5EC;
}
.step-row:hover .step-box h4,
.step-row:focus-within .step-box h4,
.step-row.is-active .step-box h4,
.step-row:hover .step-box p,
.step-row:focus-within .step-box p,
.step-row.is-active .step-box p {
  color: #002B40;
}
/* Dark mode override — white panel needs deeper cobalt + white text. */
html.theme-dark .step-row:hover .rail-node,
html.theme-dark .step-row:focus-within .rail-node,
html.theme-dark .step-row.is-active .rail-node,
body:has(.theme-toggle:checked) .step-row:hover .rail-node,
body:has(.theme-toggle:checked) .step-row:focus-within .rail-node,
body:has(.theme-toggle:checked) .step-row.is-active .rail-node {
  background: #0E5A8F;
  border-color: #0E5A8F;
  color: #FFFFFF;
}
html.theme-dark .step-row:hover .step-box,
html.theme-dark .step-row:focus-within .step-box,
html.theme-dark .step-row.is-active .step-box,
body:has(.theme-toggle:checked) .step-row:hover .step-box,
body:has(.theme-toggle:checked) .step-row:focus-within .step-box,
body:has(.theme-toggle:checked) .step-row.is-active .step-box {
  background: #0E5A8F;
}
html.theme-dark .step-row:hover .step-box h4,
html.theme-dark .step-row:hover .step-box p,
html.theme-dark .step-row:focus-within .step-box h4,
html.theme-dark .step-row:focus-within .step-box p,
html.theme-dark .step-row.is-active .step-box h4,
html.theme-dark .step-row.is-active .step-box p,
body:has(.theme-toggle:checked) .step-row:hover .step-box h4,
body:has(.theme-toggle:checked) .step-row:hover .step-box p,
body:has(.theme-toggle:checked) .step-row:focus-within .step-box h4,
body:has(.theme-toggle:checked) .step-row:focus-within .step-box p,
body:has(.theme-toggle:checked) .step-row.is-active .step-box h4,
body:has(.theme-toggle:checked) .step-row.is-active .step-box p {
  color: #FFFFFF;
}
@media (prefers-color-scheme: dark) {
  html:not(.theme-light) .step-row:hover .rail-node,
  html:not(.theme-light) .step-row:focus-within .rail-node,
  html:not(.theme-light) .step-row.is-active .rail-node {
    background: #0E5A8F;
    border-color: #0E5A8F;
    color: #FFFFFF;
  }
  html:not(.theme-light) .step-row:hover .step-box,
  html:not(.theme-light) .step-row:focus-within .step-box,
  html:not(.theme-light) .step-row.is-active .step-box {
    background: #0E5A8F;
  }
  html:not(.theme-light) .step-row:hover .step-box h4,
  html:not(.theme-light) .step-row:hover .step-box p,
  html:not(.theme-light) .step-row:focus-within .step-box h4,
  html:not(.theme-light) .step-row:focus-within .step-box p,
  html:not(.theme-light) .step-row.is-active .step-box h4,
  html:not(.theme-light) .step-row.is-active .step-box p {
    color: #FFFFFF;
  }
}
/* AAA: route the active fill to deep cobalt + white in both modes,
   which clears 7.30:1 (AAA Normal). The bright-cobalt + navy pairing
   only reaches 6.31:1, which fails AAA Normal. */
html.wcag-aaa .step-row:hover .rail-node,
html.wcag-aaa .step-row:focus-within .rail-node,
html.wcag-aaa .step-row.is-active .rail-node {
  background: #0E5A8F;
  border-color: #0E5A8F;
  color: #FFFFFF;
}
html.wcag-aaa .step-row:hover .step-box,
html.wcag-aaa .step-row:focus-within .step-box,
html.wcag-aaa .step-row.is-active .step-box {
  background: #0E5A8F;
}
html.wcag-aaa .step-row:hover .step-box h4,
html.wcag-aaa .step-row:hover .step-box p,
html.wcag-aaa .step-row:focus-within .step-box h4,
html.wcag-aaa .step-row:focus-within .step-box p,
html.wcag-aaa .step-row.is-active .step-box h4,
html.wcag-aaa .step-row.is-active .step-box p {
  color: #FFFFFF;
}

@media (min-width: 768px) {
  .steps-rail {
    --rail-col-w: 96px;
    --rail-circle-w: 64px;
    --rail-col-gap: 14px;
    row-gap: 16px;
  }
  .step-box { padding: 22px 22px; }
  .step-box h4 { font-size: 18px; }
  .step-box p { font-size: 13px; }
  .rail-node { font-size: 22px; }
  .approach { padding: 36px 28px 32px; margin-top: 26px; }
  .approach .section-h { font-size: 26px; margin-bottom: 24px; }
}
@media (min-width: 1024px) {
  .steps-rail {
    --rail-col-w: 108px;
    --rail-circle-w: 76px;
    --rail-col-gap: 18px;
    row-gap: 20px;
  }
  .step-box { padding: 26px 26px; }
  .step-box h4 { font-size: 19px; margin-bottom: 8px; }
  .step-box p { font-size: 13.5px; line-height: 1.6; }
  .rail-node { font-size: 24px; }
  .approach { padding: 44px 36px 40px; margin-top: 32px; }
  .approach .section-h { font-size: 30px; margin-bottom: 28px; }
}

/* ===========================================================
   BODY TYPE - about.html and case-study pages
   =========================================================== */
.body-h {
  margin: 0 0 18px;
  font-family: var(--serif);
  font-weight: 400;
  font-size: 24px;
  line-height: 1.1;
  letter-spacing: -0.025em;
  color: var(--ink);
}
.body-h .ital { font-style: italic; color: var(--accent); }
.body-p {
  margin: 0 0 14px;
  font-family: var(--sans);
  font-size: 16px;
  line-height: 1.6;
  color: var(--ink-soft);
}
.body-p:last-child { margin-bottom: 0; }
.body-p strong { font-weight: 600; color: var(--ink); }
/* Inline body links - vermilion with a hairline underline, matching
   the .cover-tag a treatment. AAA swaps both resting and hover to ink
   so normal body text clears 7:1 in every state - the underline
   thickens on hover (see rule below) to carry the affordance instead. */
.body-p a {
  color: var(--accent);
  text-decoration: underline;
  text-decoration-thickness: 0.5px;
  text-underline-offset: 3px;
  transition: color .2s ease, text-decoration-thickness .2s ease;
}
.body-p a:hover { color: var(--accent-strong); }
html.wcag-aaa .body-p a,
html.wcag-aaa .body-p a:hover {
  color: var(--ink);
}
html.wcag-aaa .body-p a:hover { text-decoration-thickness: 1.5px; }
@media (min-width: 768px) {
  .body-h { font-size: 28px; margin-bottom: 24px; }
  .body-p { font-size: 16px; margin-bottom: 16px; }
}

/* ===========================================================
   CLIENTS LIST - about.html
   =========================================================== */
.clients-list {
  list-style: none;
  margin: 0;
  padding: 0;
  font-family: var(--sans);
  font-size: 15px;
  line-height: 1.7;
  color: var(--ink-soft);
}
.clients-list li {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  padding: 4px 0;
  border-bottom: 0.5px solid var(--rule);
}
.clients-sector {
  flex-shrink: 0;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--mute);
}
@media (min-width: 768px) {
  .clients-list {
    column-count: 2;
    column-gap: 32px;
    font-size: 15px;
  }
  .clients-list li { break-inside: avoid; }
  .clients-sector { font-size: 11px; letter-spacing: 0.16em; }
}

/* ===========================================================
   QUOTE - case-study pages
   =========================================================== */
.quote {
  padding: 36px 18px 32px;
  max-width: 520px;
  margin: 0 auto;
}
.quote-mark {
  display: block;
  font-family: var(--serif);
  font-style: italic;
  font-weight: 400;
  font-size: 60px;
  line-height: 0.6;
  color: var(--accent);
  margin-bottom: 16px;
}
.quote-text {
  margin: 0 0 18px;
  font-family: var(--serif);
  font-style: italic;
  font-weight: 400;
  font-size: 24px;
  line-height: 1.35;
  color: var(--ink);
  letter-spacing: -0.01em;
}
.quote-attr {
  margin: 0;
  font-family: var(--sans);
  font-size: 11px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--mute);
  font-weight: 600;
}
/* Override the user-agent italic on <cite> inside quote attribution -
   the name should inherit the same sans / uppercase treatment as the
   role above it. */
.quote-attr cite {
  font-style: normal;
}
@media (min-width: 768px) {
  .quote { padding: 52px 24px 48px; }
  .quote-mark { font-size: 80px; }
  .quote-text { font-size: 28px; margin-bottom: 22px; letter-spacing: -0.015em; }
  .quote-attr { font-size: 11px; letter-spacing: 0.16em; }
}

/* ===========================================================
   CTA card - homepage closing call to action
   Sits in the regular .card surface (cream background, shadow)
   and just centres the heading, lede and button.
   =========================================================== */
.cta-card {
  text-align: center;
}
.cta-lede {
  margin: 18px auto 24px;
  max-width: 560px;
  font-family: var(--sans);
  font-size: 17px;
  line-height: 1.55;
  color: var(--ink-soft);
}
.cta-lede abbr { text-decoration: none; cursor: help; }
.cta-button {
  display: inline-flex;
  align-items: center;
  gap: 14px;
  padding: 14px 24px;
  background: var(--ink);
  color: var(--cream);
  font-family: var(--sans);
  font-size: 15px;
  font-weight: 500;
  letter-spacing: -0.005em;
  border-radius: var(--radius);
  transition: background .25s ease, color .25s ease;
  /* min-height matches the SC 2.5.5 (Enhanced) 44x44 floor without
     needing the AAA reroute - this button is always tappable. */
  min-height: 44px;
}
.cta-button:hover {
  background: var(--accent);
  color: var(--cream-fixed);
}
.cta-button:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 4px;
}
.cta-button .arrow {
  font-family: var(--serif);
  font-style: italic;
  font-size: 20px;
  line-height: 1;
  transition: transform .25s ease;
}
.cta-button:hover .arrow { transform: translateX(4px); }

@media (min-width: 768px) {
  .cta-lede { font-size: 18px; margin: 22px auto 28px; }
  .cta-button { padding: 16px 28px; font-size: 16px; }
}

/* ===========================================================
   NEXT CASE STUDY - case-study pages
   =========================================================== */
.next-link {
  display: grid;
  grid-template-columns: 50px 1fr 40px;
  gap: 16px;
  align-items: center;
  padding: 22px 18px;
  background: var(--cream);
  border-radius: var(--radius);
  cursor: pointer;
  transition: background .25s ease, color .25s ease;
}
.next-link:hover { background: var(--accent); color: var(--cream-fixed); }
.next-link:hover .next-meta { color: var(--cream-fixed); opacity: 0.85; }
.next-link:hover .next-title { color: var(--cream-fixed); }
.next-link:hover .next-arrow { color: var(--cream-fixed); transform: translateX(4px); }
.next-meta {
  margin: 0;
  font-family: var(--sans);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--mute);
  transition: color .25s ease;
}
.next-title {
  margin: 0;
  font-family: var(--serif);
  font-weight: 500;
  font-size: 26px;
  line-height: 1;
  letter-spacing: -0.02em;
  color: var(--ink);
  transition: color .25s ease;
}
.next-arrow {
  justify-self: end;
  font-family: var(--serif);
  font-style: italic;
  font-size: 24px;
  color: var(--accent);
  transition: color .25s ease, transform .25s ease;
}
@media (min-width: 768px) {
  .next-link {
    grid-template-columns: 60px 1fr 60px;
    gap: 20px;
    padding: 28px 24px;
  }
  .next-meta { font-size: 11px; letter-spacing: 0.16em; }
  .next-title { font-size: 28px; }
  .next-arrow { font-size: 26px; }
}

/* ===========================================================
   FOOTER - all pages
   =========================================================== */
.foot {
  background: var(--cream-tint);
  padding: 24px 18px 18px;
  color: var(--ink);
}
.foot-grid {
  padding-bottom: 14px;
  border-bottom: 0.5px solid var(--rule);
}
.foot-brand {
  margin: 0;
  font-family: var(--serif);
  font-size: 18px;
  font-weight: 500;
  line-height: 1;
  letter-spacing: -0.02em;
  color: var(--ink);
}
.foot-brand i {
  font-style: italic;
  /* Footer sits on --cream-tint, where the default --accent (#C73100)
     falls just short of AA 4.5:1. Swap to the stronger variant. */
  color: var(--accent-strong);
  transition: color .25s ease;
}
.foot-brand a {
  display: inline;
  margin: 0;
  font: inherit;
  letter-spacing: inherit;
  color: inherit;
  text-decoration: none;
  transition: color .25s ease;
}
.foot-brand a:hover { color: var(--accent-strong); }
.foot-tag {
  margin: 8px 0 0;
  font-family: var(--sans);
  font-size: 13px;
  color: var(--mute);
  line-height: 1.5;
}
.foot-col > a {
  display: flex;
  /* Top-align so each link's text baseline matches the first column's
     strapline (which starts at the top of its own line), rather than
     centring in the 24px target-size box and appearing to sit lower. */
  align-items: flex-start;
  justify-content: flex-end;
  gap: 8px;
  margin: 0;
  font-family: var(--sans);
  font-size: 13px;
  line-height: 1.55;
  color: var(--mute);
  transition: color .2s ease;
}
/* Inline footer nav - the second .foot-col is the links column; lay it
   out as a flex row so About / Contact / LinkedIn sit side-by-side, with
   thin vertical bars between them acting as "|" separators. */
.foot-col + .foot-col {
  display: flex;
  flex-direction: row;
  flex-wrap: wrap;
  justify-content: flex-end;
  align-items: center;
  row-gap: 4px;
}
/* At the smallest breakpoint the footer collapses to a single column,
   so the link column also left-aligns flush with the brand+strap above.
   Pipe separators are hidden and the desktop baseline-shift hack reset. */
@media (max-width: 767px) {
  .foot-col + .foot-col {
    flex-direction: column;
    align-items: flex-start;
    row-gap: 6px;
    /* Breathing room between the strapline above and the link list below
       now that the two columns stack vertically. */
    margin-top: 20px;
  }
  html:not(.wcag-aaa) .foot-col > a,
  html.wcag-aaa .foot-col > a {
    margin-top: 0;
  }
  /* The anchor itself is flex with justify-content: flex-end on desktop -
     reset to flex-start at mobile so the text-and-arrow run hugs the left
     edge rather than the right. */
  .foot-col > a {
    justify-content: flex-start;
  }
  /* Specificity-match both the A/AA and AAA scoped rules so the 22px
     pipe-gutter doesn't survive at mobile in either branch. */
  html:not(.wcag-aaa) .foot-col > a + a,
  html.wcag-aaa .foot-col > a + a {
    padding-left: 0;
  }
  /* AAA anchors carry padding: 10px 8px for the 44x44 tap area on
     desktop. At mobile with the column left-aligned, that 8px left pad
     visually indents the first link from the strap above - reset it.
     min-width keeps the shortest link ("About" ~33px text + 8px right
     pad = ~41px) at the 44px minimum required by 2.5.5 Target Size
     (Enhanced) once the left padding is gone. */
  html.wcag-aaa .foot-col > a {
    padding-left: 0;
    min-width: 44px;
  }
  /* Extra breathing room between strap and links in AAA mode - the
     larger 44px tap targets need a clearer gap from the body copy
     above to keep the hierarchy legible. */
  html.wcag-aaa .foot-col + .foot-col {
    margin-top: 32px;
  }
  .foot-col > a + a::before {
    display: none;
  }
}
.foot-col > a + a {
  position: relative;
  padding-left: 22px;
}
/* Decorative pipe - empty pseudo-element styled as a 1×12px vertical bar.
   No text content, so screen readers don't announce a "pipe" between
   each link. Pinned near the top of the anchor so it visually centres
   on the first text-line whether the anchor is 24px (default) or 44px
   (AAA target-size mode). */
.foot-col > a + a::before {
  content: "";
  position: absolute;
  left: 10px;
  top: 4px;
  width: 1px;
  height: 12px;
  background: var(--mute);
}
/* Current page link in the footer nav - slightly muted + non-interactive
   cue so the user can see where they are. aria-current="page" carries
   the AT semantics; this is the matching visual treatment. */
.foot-col > a[aria-current="page"] {
  color: var(--ink);
  cursor: default;
}
.foot-col > a[aria-current="page"]:hover { color: var(--ink); }
/* Only external links get the up-right arrow glyph. */
.foot-col > a[target="_blank"]::after {
  content: "\2197"; /* ↗ */
  font-size: 14px;
  line-height: 1;
  color: var(--mute);
  transition: transform .25s ease, color .25s ease;
}
.foot-col > a:hover { color: var(--accent-strong); }
.foot-col > a[target="_blank"]:hover::after { color: var(--accent-strong); transform: translate(2px, -2px); }
.foot-bottom {
  padding-top: 14px;
  font-family: var(--sans);
  font-size: 11px;
  color: var(--mute);
  line-height: 1.5;
  display: flex;
  justify-content: space-between;
  /* Centre alignment seats the bordered palette dropdown chip flush
     with the middle line of the copyright text. Baseline alignment
     (the previous value) pulled the chip down because flex baselines
     fall to the last inline-block child inside. */
  align-items: center;
  gap: 16px;
}
.foot-bottom .sep { display: none; }
.foot-copy { margin: 0; }

/* ---------- Theme toggle - text link ---------- */
.theme-link {
  position: relative;
  display: inline-flex;
  align-items: center;
  cursor: pointer;
  font-family: var(--sans);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink);
  white-space: nowrap;
  flex-shrink: 0;
  transition: color .2s ease;
  /* Tight line-height so the visible text label sits on the same
     mid-line as the AA/AAA pills and the palette dropdown - default
     line-height would give the text a taller line box that drifts
     below their centres. */
  line-height: 1;
}
/* Visible-text label spans - two are present in markup; CSS toggles
   which is displayed. Real text in the DOM means the input's
   accessible name always matches what's on screen (2.5.3). */
.theme-link-text[data-state="light"] { display: none; }
body:has(.theme-toggle:checked) .theme-link-text[data-state="dark"] { display: none; }
body:has(.theme-toggle:checked) .theme-link-text[data-state="light"] { display: inline; }
html.theme-dark .theme-link-text[data-state="dark"] { display: none; }
html.theme-dark .theme-link-text[data-state="light"] { display: inline; }
.theme-link:hover { color: var(--accent-strong); }
.theme-toggle {
  position: absolute;
  inset: 0;
  opacity: 0;
  width: 1px;
  height: 1px;
  margin: 0;
  pointer-events: none;
}

@media (min-width: 768px) {
  .foot { padding: 32px 24px 22px; }
  .foot-grid {
    display: grid;
    /* Right column sizes to its links rather than a fixed 1fr share -
       in AAA mode each anchor gains 16px of horizontal padding for the
       44×44 tap area, which would otherwise force the row to wrap. */
    grid-template-columns: 1fr auto;
    gap: 28px;
    padding-bottom: 18px;
  }
  .foot-tag { max-width: 360px; line-height: 1.55; }
  .foot-bottom { padding-top: 18px; font-size: 11px; }
  .foot-bottom br { display: none; }
  .foot-bottom .sep { display: inline; padding: 0 6px; }
  .theme-link { font-size: 11px; }
  .foot-brand { font-size: 19px; }
  /* Right column top-padded so its first link aligns vertically with
     the start of the foot-tag in the left column. The extra +1px on
     each breakpoint accounts for the small font-metric gap between
     the serif brand line and the sans tag line in column 1. */
  .foot-col + .foot-col { padding-top: 28px; }
}
@media (min-width: 1024px) {
  .foot { padding: 36px 32px 24px; }
  .foot-grid { padding-bottom: 20px; }
  .foot-bottom { padding-top: 20px; }
  .foot-brand { font-size: 22px; }
  .foot-col + .foot-col { padding-top: 31px; }
}

