/* ==========================================================================
   Mirko · personal landing page
   Structure follows Relume / Client-First:
   page-wrapper › main-wrapper › section_[name] › padding-global ›
   container-large › padding-section-[size] › [name]_component
   Lines marked ▸ are handoff notes. Every value maps to a token in :root.
   ========================================================================== */

/* --------------------------------------------------------------------------
   1. TOKENS
   -------------------------------------------------------------------------- */
:root {
  /* ▸ Colour: brief palette only. Alphas derive from these hues; no new hues. */
  --color-primary: #1A73E8;        /* CTAs, links on WHITE only, focus ring · white text 4.51:1 */
  --color-primary-hover: #1557B0;  /* primary hover + pressed · white text 6.95:1 */
  --color-white: #FFFFFF;
  --color-surface: #F8F9FA;        /* alternating sections, media wells, tags */
  --color-text: #5F6368;           /* body · 6.05:1 on white, 5.74:1 on surface */
  --color-heading: #202124;        /* headings, pressed filter · 16.1:1 */
  --color-available: #1E8E3E;      /* availability dot ONLY · 4.21:1 = passes 3:1 graphics, fails 4.5:1 text */

  --color-hairline: rgb(32 33 36 / 0.08);
  --color-hairline-strong: rgb(32 33 36 / 0.16);
  --color-primary-tint: rgb(26 115 232 / 0.12);

  /* ▸ Type: one family, variable weight. Serif is a single hero accent, not a system font. */
  --font-sans: "Google Sans Flex", "Google Sans", system-ui, -apple-system, "Segoe UI", sans-serif;
  --font-serif: "Newsreader", Georgia, "Times New Roman", serif;
  --text-h1: 2.25rem;      /* 36 → 48 at ≥768 → 56 at ≥1024 */
  --text-h2: 2rem;         /* 32 → 40 at ≥768 */
  --text-h3: 1.5rem;       /* 24 → 28 at ≥768 */
  --text-body: 1.125rem;   /* 18 */
  --text-small: 1rem;      /* 16 */
  --text-xs: 0.875rem;     /* 14 */
  --text-eyebrow: 0.8125rem; /* 13 */
  --leading-body: 1.5;
  --weight-regular: 400;
  --weight-medium: 500;
  --weight-heading: 560;

  /* ▸ Spacing: 8pt grid; 4pt half-step only inside small components */
  --space-0-5: 0.25rem; /* 4 */
  --space-1: 0.5rem;    /* 8 */
  --space-2: 1rem;      /* 16 */
  --space-3: 1.5rem;    /* 24 */
  --space-4: 2rem;      /* 32 */
  --space-5: 2.5rem;    /* 40 */
  --space-6: 3rem;      /* 48 */
  --space-8: 4rem;      /* 64 */
  --space-10: 5rem;     /* 80 */
  --space-12: 6rem;     /* 96 */
  --space-16: 8rem;     /* 128 */

  /* ▸ Layout */
  --container: 75rem;           /* 1200 max width */
  --gutter: var(--space-3);     /* 24 → 32 → 40 */
  /* Distance from the screen edge to the container's content edge. A full-width
     carousel track pads and scroll-pads itself by this, so its first card rests on
     the container edge (assets/carousel.js). The % resolves where it is used. */
  --carousel-edge: max(var(--gutter), calc((100% - var(--container)) / 2));
  --section-y: var(--space-8);  /* 64 → 96 → 128 */
  --grid-gap: var(--space-3);   /* 24 → 24 → 32 */
  /* ▸ Column system (7 Oct 2026): 4 columns below 48em, 8 from 48em, 12 from 64em;
     the gutter between columns is --grid-gap, the outer margin is --gutter, and
     every spacing value sits on the 8px scale (4px half-steps only for micro
     details like chips and dots). --grid-12 is the track list for a 12-column layout:
     use it with column-gap: var(--grid-gap), then place children with
     grid-column: N / span M. */
  --cols: 4;
  --grid-12: repeat(12, minmax(0, 1fr));
  /* ▸ Components are sized by the columns, not by pixels or percentages. A component that
     cannot use grid-column (a carousel card, the hero portrait) sets --n, the number of
     columns it spans, and takes its width from --span, which is n columns plus the n-1
     gutters between them, measured against a box one container wide:
       flex: 0 0 var(--span)   or   width: var(--span)
     so a card is exactly 3 columns, or 4, or 6, wide, and a carousel step moves exactly n
     columns. See the "Components on the columns" rule at the top of this file's §14b. */
  --nav-offset: var(--space-1); /* 8 → 16: gap above the floating nav */
  --nav-height: 3.5rem;         /* 56 */

  /* ▸ Shape: bezel cards = outer radius − 8px padding = inner radius */
  --bezel: var(--space-1);      /* 8 */
  --radius-inner: 1rem;         /* 16 */
  --radius-outer: 1.5rem;       /* 24 */
  --radius-xl: 2rem;            /* 32 (portrait) */
  --radius-pill: 8px;

  /* ▸ Elevation: diffused, low-alpha heading colour */
  --ring: 0 0 0 1px var(--color-hairline);
  --shadow-1: 0 1px 2px rgb(32 33 36 / 0.04), 0 8px 24px -12px rgb(32 33 36 / 0.12);
  --shadow-2: 0 2px 4px rgb(32 33 36 / 0.04), 0 24px 48px -20px rgb(32 33 36 / 0.22);
  --shadow-primary: 0 1px 2px rgb(26 115 232 / 0.24), 0 8px 24px -8px rgb(26 115 232 / 0.5);

  /* ▸ Motion: transform + opacity only */
  --ease-out: cubic-bezier(0.32, 0.72, 0, 1);  /* default for UI and reveals */
  --ease-soft: cubic-bezier(0.22, 1, 0.36, 1); /* colour and shadow fades */
  --dur-fast: 200ms;
  --dur-base: 400ms;
  --dur-slow: 600ms;
  --dur-reveal: 800ms;
}

@media (min-width: 48em) {
  :root {
    --cols: 8;
    --gutter: var(--space-4);
    --section-y: var(--space-12);
    --text-h1: 3rem;
    --text-h2: 2.5rem;
    --text-h3: 1.75rem;
  }
}

@media (min-width: 64em) {
  :root {
    --cols: 12;
    --gutter: var(--space-5);
    --section-y: var(--space-16);
    --grid-gap: var(--space-4);
    --nav-offset: var(--space-2);
    --text-h1: 3.5rem;
  }
}

/* ▸ Large-screen step, ~1700px (106em × 16px base = 1696px, 212 × 8 — closest
   round value to 1700 that still lands on the 8pt grid). Past 1024 the layout
   was otherwise static all the way to infinity; this gives big monitors a
   wider container and a bit more breathing room instead of just more side
   margin, and lets the hero portrait (capped at 32rem since the ≥64em rule)
   grow further before it hits its ceiling. */
@media (min-width: 106em) {
  :root {
    --container: 90rem;  /* 1440 = 180 × 8 */
    --gutter: var(--space-6); /* 48 */
  }
}

/* --------------------------------------------------------------------------
   2. BASE
   -------------------------------------------------------------------------- */
*, *::before, *::after { box-sizing: border-box; }
/* ▸ Baseline grid (7 Oct 2026). Every element's line-height is its ratio times its OWN
   font size, rounded to the nearest 4px, so every line of text sits on the 4px baseline
   (an 18px paragraph at 1.5 is 28px, not 27; a 40px heading at 1.15 is 48px, not 46).
   Components set the ratio with --lh (it inherits exactly like a unitless line-height
   used to); this one rule turns it into px. A browser without CSS round() keeps the
   plain ratio from the first declaration. */
*, *::before, *::after {
  line-height: var(--lh, 1.5);
  line-height: round(nearest, calc(var(--lh, 1.5) * 1em), 4px);
}


html {
  -webkit-text-size-adjust: 100%;
  scroll-padding-top: calc(var(--nav-offset) + var(--nav-height) + var(--space-3));
}

@media (prefers-reduced-motion: no-preference) {
  html { scroll-behavior: smooth; }
}

html.is-locked { overflow: hidden; }

body {
  margin: 0;
  background: var(--color-white);
  color: var(--color-text);
  font-family: var(--font-sans);
  font-size: var(--text-body);
  font-weight: var(--weight-regular);
  --lh: var(--leading-body);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-rendering: optimizeLegibility;
}

/* ▸ Column grid lines: a fixed, page-spanning hint of the container's own
   column structure (4 columns, matching --container so it lines up with every
   section's own edges), at Mirko's request after seeing BYQ's reference site.
   Fixed (not absolute) so it holds still as a constant reference while content
   scrolls past it, like a print register mark; hairline-only, no fill, so it
   never competes with real content — visible mainly in the side gutters and
   any open space, and quietly covered by every section's own opaque background
   elsewhere (nothing extra needed for that: painted first, z-index: -1). */
body::before {
  content: "";
  position: fixed;
  inset: 0;
  z-index: -1;
  max-width: var(--container);
  margin-inline: auto;
  pointer-events: none;
  background-image: repeating-linear-gradient(
    to right,
    var(--color-hairline) 0,
    var(--color-hairline) 1px,
    transparent 1px,
    transparent 25%
  );
}

h1, h2, h3, p, ul, ol, figure, blockquote { margin: 0; }
ul[role="list"], ol[role="list"] { list-style: none; padding: 0; }
img, svg { display: block; max-width: 100%; }
button { font: inherit; color: inherit; }
code { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 0.875em; }

a { color: var(--color-primary); }

/* ▸ Focus: 2px primary ring, 3px offset, on every interactive element */
:focus-visible,
.is-focus {
  outline: 2px solid var(--color-primary);
  outline-offset: 3px;
}

main:focus { outline: none; }

.visually-hidden {
  position: absolute !important;
  width: 1px; height: 1px;
  margin: -1px; padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

.skip-link {
  position: fixed;
  top: var(--space-1);
  left: var(--space-1);
  z-index: 40;
  padding: var(--space-1) var(--space-3);
  min-height: 44px;
  display: inline-flex;
  align-items: center;
  border-radius: var(--radius-pill);
  background: var(--color-heading);
  color: var(--color-white);
  font-weight: var(--weight-medium);
  text-decoration: none;
  transform: translateY(-200%);
  transition: transform var(--dur-base) var(--ease-out);
}
.skip-link:focus { transform: none; }

.nav-sentinel {
  position: absolute;
  top: 0; left: 0;
  width: 1px; height: var(--space-3);
  pointer-events: none;
}

/* --------------------------------------------------------------------------
   3. CLIENT-FIRST LAYOUT UTILITIES
   -------------------------------------------------------------------------- */
.padding-global { padding-inline: var(--gutter); }
.container-large { max-width: var(--container); margin-inline: auto; }

.padding-section-large {
  --pad-top: var(--section-y);
  --pad-bottom: var(--section-y);
  padding-block: var(--pad-top) var(--pad-bottom);
}
.padding-section-hero {
  --pad-top: var(--space-6);     /* 48 → 64 → 80 */
  --pad-bottom: var(--space-8);  /* 64 → 80 → 96 */
  padding-block: var(--pad-top) var(--pad-bottom);
}
.padding-section-small {
  --pad-top: var(--space-2);     /* 16 → 16 → 24 */
  --pad-bottom: var(--space-8);  /* 64 → 80 → 96 */
  padding-block: var(--pad-top) var(--pad-bottom);
}
@media (min-width: 48em) {
  .padding-section-hero { --pad-top: var(--space-8); --pad-bottom: var(--space-10); }
  .padding-section-small { --pad-bottom: var(--space-10); }
}
@media (min-width: 64em) {
  .padding-section-hero { --pad-top: var(--space-10); --pad-bottom: var(--space-12); }
  .padding-section-small { --pad-top: var(--space-3); --pad-bottom: var(--space-12); }
}

.background-color-surface { background: var(--color-surface); }

/* --------------------------------------------------------------------------
   4. TYPOGRAPHY
   -------------------------------------------------------------------------- */
.heading-style-h1,
.heading-style-h2,
.heading-style-h3 {
  color: var(--color-heading);
  font-weight: var(--weight-heading);
  text-wrap: balance;
}
.heading-style-h1 { font-size: var(--text-h1); --lh: 1.1; letter-spacing: -0.025em; text-wrap: balance; }
.heading-style-h2 { font-size: var(--text-h2); --lh: 1.15; letter-spacing: -0.02em; }
.heading-style-h3 { font-size: var(--text-h3); --lh: 1.25; letter-spacing: -0.01em; }

.text-size-medium { font-size: var(--text-body); }
.text-size-small { font-size: var(--text-small); }

.eyebrow {
  display: inline-flex;
  align-items: center;
  min-height: 28px;
  padding: var(--space-0-5) var(--space-2);
  border-radius: var(--radius-pill);
  background: var(--color-white);
  box-shadow: var(--ring);
  color: var(--color-text);
  font-size: var(--text-eyebrow);
  font-weight: var(--weight-medium);
  letter-spacing: 0.12em;
  text-transform: uppercase;
}
.background-color-surface .eyebrow { background: var(--color-white); }

.section-heading {
  display: grid;
  justify-items: start;
  gap: var(--space-2);
  max-width: 40rem;
}
.section-heading .eyebrow { margin-bottom: var(--space-1); }
.section-heading.is-centered { justify-items: center; text-align: center; margin-inline: auto; }

.text-link {
  padding-block: 0.75rem;   /* inline padding grows the hit area to 44+ without moving the line */
  color: var(--color-primary);
  font-weight: var(--weight-medium);
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 3px;
  transition: color var(--dur-fast) var(--ease-soft);
}
.text-link:hover { color: var(--color-primary-hover); text-decoration-thickness: 2px; }

/* ▸ Placeholder content: must be replaced before launch (see handoff.html) */
.placeholder {
  padding: 0 var(--space-0-5);
  border-radius: 4px;
  background: var(--color-surface);
  outline: 1px dashed rgb(95 99 104 / 0.55);
  outline-offset: -1px;
  -webkit-box-decoration-break: clone;
  box-decoration-break: clone;
}
.background-color-surface .placeholder { background: var(--color-white); }

/* --------------------------------------------------------------------------
   5. BUTTONS
   ▸ States: rest · hover · focus-visible · active (pressed, scale .98)
   -------------------------------------------------------------------------- */
.button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  min-height: 48px;
  padding: 0 var(--space-3);
  border: 0;
  border-radius: var(--radius-pill);
  font-size: var(--text-small);
  font-weight: var(--weight-medium);
  --lh: 1.2;
  text-decoration: none;
  white-space: nowrap;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  transition:
    background-color var(--dur-fast) var(--ease-soft),
    box-shadow var(--dur-base) var(--ease-soft),
    transform var(--dur-fast) var(--ease-out);
}
.button:active, .button.is-pressed { transform: scale(0.98); }

.button.is-primary {
  padding-right: var(--space-1);
  background: var(--color-primary);
  color: var(--color-white);
  box-shadow: var(--shadow-primary);
}
.button.is-primary:hover, .button.is-primary.is-hover { background: var(--color-primary-hover); }

/* ▸ Button-in-button: trailing icon sits in its own circle, flush right */
.button_icon {
  display: grid;
  place-items: center;
  width: 32px;
  height: 32px;
  flex: none;
  border-radius: 50%;
  background: rgb(255 255 255 / 0.18);
  transition: transform var(--dur-base) var(--ease-out), background-color var(--dur-fast) var(--ease-soft);
}
.button_icon svg {
  width: 16px; height: 16px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.5;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.button.is-primary:hover .button_icon,
.button.is-primary.is-hover .button_icon {
  transform: translateX(2px) scale(1.06);
  background: rgb(255 255 255 / 0.24);
}

.button.is-secondary {
  background: var(--color-white);
  color: var(--color-heading);
  box-shadow: inset 0 0 0 1px var(--color-hairline-strong);
}
.button.is-secondary:hover, .button.is-secondary.is-hover {
  background: var(--color-surface);
  box-shadow: inset 0 0 0 1px var(--color-hairline-strong), var(--shadow-1);
}
.button_arrow-down { display: inline-block; transition: transform var(--dur-base) var(--ease-out); }
.button.is-secondary:hover .button_arrow-down,
.button.is-secondary.is-hover .button_arrow-down { transform: translateY(3px); }

.button.is-large { min-height: 56px; font-size: var(--text-body); }
.button.is-large.is-primary { padding-left: var(--space-4); padding-right: var(--space-1); }
.button.is-large .button_icon { width: 40px; height: 40px; }
.button.is-large.is-secondary { padding-inline: var(--space-4); gap: var(--space-1); }

.button.is-small { min-height: 44px; padding: 0 var(--space-3); font-size: var(--text-xs); }
.button.is-small.is-primary { padding-right: var(--space-3); box-shadow: none; }

.button.is-block { width: 100%; justify-content: space-between; padding-left: var(--space-3); }

.button-group { display: flex; flex-wrap: wrap; gap: var(--space-2); }

/* --------------------------------------------------------------------------
   6. BEZEL (double-bezel card: outer shell + inner core)
   -------------------------------------------------------------------------- */
.bezel {
  padding: var(--bezel);
  border-radius: var(--radius-outer);
  background: var(--color-white);
  box-shadow: var(--ring), var(--shadow-1);
}
.bezel_core {
  border-radius: var(--radius-inner);
  overflow: hidden;
}
.bezel.is-xl { border-radius: var(--radius-xl); box-shadow: var(--ring), var(--shadow-2); }
.bezel.is-xl > .bezel_core { border-radius: calc(var(--radius-xl) - var(--bezel)); }

/* --------------------------------------------------------------------------
   6b. BLUR PHOTO — motion-blur look from an ordinary photo
   ▸ blur() runs in the image's own space, then the non-uniform scale stretches
     it into streaks and rotate() sets their direction. Filter + transform only,
     so it stays on the GPU. Knobs per instance: --angle, --sx, --sy, --blur.
   ▸ Placeholder photos: Unsplash nature, hot-linked (see handoff.html).
   -------------------------------------------------------------------------- */
.blur-photo {
  position: absolute;
  inset: 0;
  overflow: hidden;
  border-radius: inherit;
}
.blur-photo img {
  position: absolute;
  top: -25%;
  left: -25%;
  width: 150%;
  height: 150%;
  max-width: none;
  object-fit: cover;
  filter: blur(var(--blur, 5px)) saturate(1.25);
  transform:
    rotate(var(--angle, -14deg))
    scale(calc(var(--sx, 1.9) * var(--zoom, 1)), calc(var(--sy, 1.1) * var(--zoom, 1)));
  transition: transform 1200ms var(--ease-out);
}

/* ▸ Blur field: a blur photo that feathers into the page on every side */
.blur-field {
  position: absolute;
  z-index: 0;
  pointer-events: none;
  -webkit-mask-image: radial-gradient(closest-side, #000 42%, transparent 100%);
  mask-image: radial-gradient(closest-side, #000 42%, transparent 100%);
}
.blur-field .blur-photo { border-radius: 0; }

/* --------------------------------------------------------------------------
   7. NAVBAR (Relume Navbar)
   ▸ Sticky floating island, white, 56 high. Links ≥768; hamburger <768.
   -------------------------------------------------------------------------- */
.navbar_component {
  position: sticky;
  top: 0;
  z-index: 30;
  padding-top: var(--nav-offset);
  pointer-events: none;
}

.navbar_container {
  position: relative;
  z-index: 2;
  display: flex;
  align-items: center;
  gap: var(--space-1);
  height: var(--nav-height);
  padding: 0 var(--space-1) 0 var(--space-3);
  border-radius: var(--radius-pill);
  /* A diagonal transparency gradient rather than a flat tint, so the glass
     reads as one smooth surface instead of a solid colour with blur bolted
     on: near-opaque by the logo, easing through a pale blue, closer to the
     page by the CTA. The inset highlight is a thin top-edge catch-light,
     the usual cue that reads as "glass" rather than "tinted plastic". */
  background: linear-gradient(135deg, rgb(255 255 255 / 0.88) 0%, rgb(237 242 253 / 0.62) 55%, rgb(26 115 232 / 0.16) 100%);
  box-shadow: var(--ring), var(--shadow-1), inset 0 1px 0 rgb(255 255 255 / 0.6);
  pointer-events: auto;
  transition: box-shadow var(--dur-slow) var(--ease-soft), background-color var(--dur-slow) var(--ease-soft);
}
@supports (backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px)) {
  .navbar_container {
    -webkit-backdrop-filter: blur(16px) saturate(1.8);
    backdrop-filter: blur(16px) saturate(1.8);
  }
}
.navbar_component.is-scrolled .navbar_container { box-shadow: var(--ring), var(--shadow-2), inset 0 1px 0 rgb(255 255 255 / 0.6); }

/* ▸ The pill itself blurs what's directly behind it, but whatever scrolls
   past on either side of it (a logo, a heading) was staying perfectly sharp
   right next to a blurred pill, which read as a blurred island floating in
   an otherwise untouched strip rather than one smooth transition. This band
   runs the full width of the viewport behind the whole header, softening
   and fading everything that passes underneath it, pill included, before
   dissolving to nothing; the mask fades the blur itself, not just the
   tint, so the blur doesn't end on a visible line either. */
.navbar_component::before {
  content: "";
  position: absolute;
  z-index: 0;
  inset: 0 0 auto 0;
  height: calc(var(--nav-offset) + var(--nav-height) + var(--space-10));
  background: linear-gradient(to bottom,
    rgb(255 255 255 / 0.55) 0%,
    rgb(237 242 253 / 0.4) 45%,
    rgb(26 115 232 / 0.1) 70%,
    transparent 100%);
  -webkit-backdrop-filter: blur(14px);
  backdrop-filter: blur(14px);
  -webkit-mask-image: linear-gradient(to bottom, #000 0%, #000 55%, transparent 100%);
  mask-image: linear-gradient(to bottom, #000 0%, #000 55%, transparent 100%);
  pointer-events: none;
}

.navbar_logo {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  min-height: 44px;
  margin-right: auto;
  color: var(--color-heading);
  font-size: 1.25rem;
  font-weight: 600;
  letter-spacing: -0.02em;
  text-decoration: none;
  border-radius: var(--space-1);
}
.navbar_logo img {
  width: 40px;
  height: 40px;
  flex: none;
}

.navbar_menu { display: none; }

.navbar_link {
  position: relative;
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  padding: 0 var(--space-2);
  border-radius: var(--radius-pill);
  color: var(--color-text);
  font-size: var(--text-xs);
  font-weight: var(--weight-medium);
  text-decoration: none;
  transition: color var(--dur-fast) var(--ease-soft), background-color var(--dur-fast) var(--ease-soft);
}
.navbar_link:hover, .navbar_link.is-hover { color: var(--color-heading); background: var(--color-surface); }
/* ▸ Current section: darker text + surface fill + dot (not colour alone) */
.navbar_link[aria-current="true"] { color: var(--color-heading); background: var(--color-surface); }
.navbar_link[aria-current="true"]::after {
  content: "";
  position: absolute;
  left: 50%;
  bottom: 6px;
  width: 4px; height: 4px;
  margin-left: -2px;
  border-radius: 50%;
  background: var(--color-primary);
}

.navbar_cta { flex: none; }

.navbar_menu-button {
  display: grid;
  place-items: center;
  width: 44px;
  height: 44px;
  flex: none;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: var(--color-surface);
  cursor: pointer;
  transition: background-color var(--dur-fast) var(--ease-soft);
}
.navbar_menu-button:hover { background: rgb(32 33 36 / 0.06); }

.navbar_menu-icon { position: relative; width: 18px; height: 12px; }
.navbar_menu-icon > span {
  position: absolute;
  left: 0;
  width: 100%;
  height: 1.5px;
  border-radius: 2px;
  background: var(--color-heading);
  transition: transform var(--dur-base) var(--ease-out);
}
.navbar_menu-icon > span:first-child { top: 2px; }
.navbar_menu-icon > span:last-child { bottom: 2px; }
/* ▸ Hamburger morphs to X: both lines translate to centre, rotate ±45° */
.navbar_menu-button[aria-expanded="true"] .navbar_menu-icon > span:first-child { transform: translateY(3.25px) rotate(45deg); }
.navbar_menu-button[aria-expanded="true"] .navbar_menu-icon > span:last-child { transform: translateY(-3.25px) rotate(-45deg); }

@media (min-width: 48em) {
  .navbar_container { padding-left: var(--space-3); }
  .navbar_logo { margin-right: 0; }
  .navbar_menu {
    display: flex;
    gap: var(--space-0-5);
    margin-inline: auto;
  }
  .navbar_menu-button { display: none; }
}

/* ▸ Mobile overlay: full-screen, heavy glass (blur allowed: fixed layer) */
.navbar_overlay {
  position: fixed;
  inset: 0;
  z-index: 1;
  display: flex;
  flex-direction: column;
  padding: calc(var(--nav-offset) + var(--nav-height) + var(--space-5)) var(--gutter) var(--space-5);
  background: rgb(255 255 255 / 0.94);
  -webkit-backdrop-filter: blur(24px);
  backdrop-filter: blur(24px);
  overflow-y: auto;
  pointer-events: auto;
  opacity: 0;
  transition: opacity var(--dur-base) var(--ease-soft);
}
.navbar_overlay[hidden] { display: none; }
.navbar_overlay.is-open { opacity: 1; }

.navbar_overlay-list { display: grid; gap: var(--space-1); }
.navbar_overlay-link {
  display: flex;
  align-items: center;
  min-height: 56px;
  color: var(--color-heading);
  font-size: var(--text-h2);
  font-weight: var(--weight-heading);
  letter-spacing: -0.02em;
  text-decoration: none;
  border-radius: var(--space-1);
}
.navbar_overlay-footer { display: grid; gap: var(--space-2); margin-top: auto; padding-top: var(--space-5); }

/* Staggered mask reveal */
.navbar_overlay-list > li,
.navbar_overlay-footer {
  opacity: 0;
  transform: translateY(var(--space-5));
  transition: opacity var(--dur-slow) var(--ease-out), transform var(--dur-slow) var(--ease-out);
  transition-delay: calc(80ms + var(--i, 0) * 60ms);
}
.navbar_overlay.is-open .navbar_overlay-list > li,
.navbar_overlay.is-open .navbar_overlay-footer { opacity: 1; transform: none; }

@media (min-width: 48em) {
  .navbar_overlay { display: none !important; }
}

/* --------------------------------------------------------------------------
   8. HERO (Relume Header, split)
   -------------------------------------------------------------------------- */
/* ▸ overflow-x only (not overflow: clip on both axes) — matches .section_contact's
   own rule below for the same reason: contain horizontal bleed from the blur-field
   and the atmosphere blobs so neither causes a horizontal scrollbar, but leave the
   vertical axis open so the hero_word watermark and the blur-field's soft radial
   fade can bleed past the section's own bottom edge instead of being hard-cut. */
.section_header { position: relative; overflow-x: clip; }

/* ▸ Atmosphere: two soft colour-photo glows + an oversized watermark word, all
   decorative and behind the content, at Mirko's request after seeing BYQ's
   grainy pink/blue glow behind its product card. Originally both glows
   sampled the headshot photo itself (a case-study screenshot was tried first
   but those are mostly white dashboard UI, so blurring one just washed out to
   a pale grey smear); swapped for a dedicated abstract gradient-mesh photo
   (hero-atmosphere.jpg, "a blurry image of a purple and blue background" by
   MagicPattern on Unsplash, free Unsplash License) chosen specifically for
   this job, so the two crops no longer have to compromise with a portrait's
   actual content. Blur alone shapes the edge (no mask-image), and only the x-axis is clipped
   (below) so nothing here gets a hard-cut edge on the axis that matters for a
   glow. Must be overflow-x: CLIP, not hidden — a lone `hidden` on one axis
   forces the browser to silently compute the other axis as `auto` (which
   still clips), so `hidden` here re-created the exact hard-cut bug this
   section exists to avoid; `clip` is exempt from that forced-auto rule.
   Sized down on phones (media query below) — at the same rem size the
   photo-based glow reads as a much heavier, muddier wash than the old flat-
   colour blobs did on a narrow screen, since a real photo blurred at 60px
   still carries more visual weight than a 10%-alpha gradient did. */
.hero_atmosphere {
  position: absolute;
  inset: 0;
  z-index: 0;
  overflow-x: clip;
  pointer-events: none;
}
.hero_blob {
  position: absolute;
  border-radius: 50%;
  background-image: url('photos/hero-atmosphere.jpg');
  background-size: 250% auto;
  filter: blur(60px) saturate(1.4);
}
.hero_blob-a {
  top: -6rem;
  left: -4rem;
  width: 16rem;
  height: 16rem;
  background-position: 12% 15%; /* the deep blue corner */
  opacity: 0.35;
}
.hero_blob-b {
  bottom: -8rem;
  right: -5rem;
  width: 18rem;
  height: 18rem;
  background-position: 78% 10%; /* the pink/magenta diagonal highlight */
  opacity: 0.32;
}
@media (min-width: 48em) {
  .hero_blob-a { top: -10rem; left: -6rem; width: 26rem; height: 26rem; }
  .hero_blob-b { bottom: -12rem; right: -8rem; width: 30rem; height: 30rem; }
}

/* ▸ Animated wash: an abstract looping gradient clip (Pixabay Content
   License — free for commercial use, no attribution required) standing in
   for the two static blobs above when the visitor allows motion. Hidden by
   default so the blobs are both the prefers-reduced-motion fallback and
   the initial-paint state before this swap applies; full-bleed, blurred
   and faded via mask so it reads as a soft colour wash behind the copy,
   not a loud foreground video. */
.hero_motion {
  display: none;
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  opacity: 0.3;
  filter: blur(48px) saturate(1.3);
  -webkit-mask-image: linear-gradient(to bottom, #000 0%, #000 45%, transparent 85%);
  mask-image: linear-gradient(to bottom, #000 0%, #000 45%, transparent 85%);
}
.hero_word {
  display: none;
  position: absolute;
  right: -0.03em;
  bottom: -0.14em;
  font-size: clamp(6rem, 20vw, 15rem);
  font-weight: 700;
  --lh: 1;
  letter-spacing: -0.04em;
  color: rgb(32 33 36 / 0.045);
  white-space: nowrap;
}
@media (min-width: 64em) {
  .hero_word { display: block; }
}
@media (prefers-reduced-motion: no-preference) {
  .hero_blob-a, .hero_blob-b { display: none; }
  .hero_motion { display: block; }
}

.header_component {
  position: relative;
  z-index: 1;
  display: grid;
  gap: var(--space-6);
}

/* ▸ Hero heading: sized on its own clamp (independent of --text-h1's bigger
   floor) and capped at 2.75rem so the full headline + lede + primary action
   fit above the fold at 1280×720 — the previous 56–64px heading pushed the
   button row to ~760px, below the viewport (1 Oct 2026 fix). The closing
   clause breaks into a serif-italic accent for editorial contrast and is
   joined to "too." with a non-breaking space in the markup so it never
   orphans onto its own line. */
#hero-heading {
  max-width: 30ch;
  font-size: clamp(2.25rem, 1.6rem + 2.6vw, 2.75rem);
  --lh: 1.18;
  font-weight: 700;
}
.hero_accent {
  display: inline;
  font-family: var(--font-serif);
  font-style: italic;
  font-weight: 500;
  letter-spacing: -0.01em;
  color: var(--color-primary);
}
.header_content {
  display: grid;
  justify-items: start;
  gap: var(--space-2);
}
.header_lede { max-width: 38rem; }
.header_microcopy { font-size: var(--text-small); }

@media (max-width: 47.99em) {
  .header_content .button-group { width: 100%; }
  .header_content .button-group > .button { flex: 1 1 100%; }
  .header_content .button.is-primary { justify-content: space-between; padding-left: var(--space-3); }
}

@media (min-width: 48em) {
  .header_content { gap: var(--space-3); }
}

@media (min-width: 64em) {
  /* ▸ Hero-only padding override: the section's own pad-bottom (--space-12,
     96px) is trimmed to --space-8 (64px) here so the button row clears
     720px-tall viewports without touching the shared padding-section-hero
     token other sections also use. */
  .section_header .padding-section-hero { --pad-bottom: var(--space-8); }
  .header_component {
    /* ▸ 12-column grid (7 Oct 2026): text on columns 1–7, portrait on 8–12, portrait
       right-aligned on column 12 and capped at 22rem — enough column width for the
       headline to read as 2–3 confident lines instead of being squeezed against an
       oversized portrait. Replaces 1.45fr : 1fr with a 64px gap, which put neither
       edge of either block on a column line. */
    grid-template-columns: var(--grid-12);
    column-gap: var(--grid-gap);
    align-items: center;
  }
  .header_content { grid-column: 1 / span 7; gap: var(--space-3); }
  .header_portrait { grid-column: 9 / span 4; }
  .header_content .button-group { margin-top: var(--space-1); }
  .header_microcopy { margin-top: calc(var(--space-1) * -1); }
}

/* ▸ Availability pill: green dot + text (never colour alone). Pulse off for reduced motion. */
.status-pill {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  min-height: 36px;
  padding: var(--space-0-5) var(--space-2) var(--space-0-5) 12px;
  border-radius: var(--radius-pill);
  background: var(--color-white);
  box-shadow: var(--ring), 0 1px 2px rgb(32 33 36 / 0.04);
  color: var(--color-heading);
  font-size: var(--text-xs);
  font-weight: var(--weight-medium);
}
.status-pill_dot {
  position: relative;
  width: 8px;
  height: 8px;
  flex: none;
  border-radius: 50%;
  background: var(--color-available);
}
.status-pill_dot::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: 50%;
  background: var(--color-available);
  animation: pulse 2.4s var(--ease-out) infinite;
}
@keyframes pulse {
  0% { transform: scale(1); opacity: 0.55; }
  70%, 100% { transform: scale(3); opacity: 0; }
}

/* ▸ Portrait: the same floating card at every width (badge + blur glow +
   overlaid caption), just scaled down below 1024 instead of swapped out for
   a plain inline thumbnail+name row. Mobile-first: these are the small-
   screen values; the ≥64em block below only repositions/enlarges them for
   the wider two-column row. */
/* ▸ Width from the columns (7 Oct 2026): 4 of 4 on a phone (the full container), 4 of 8 on a
   tablet (centred, which lands on columns 3–6), and from 64em the four columns 9–12 of the
   hero grid. It was capped at 19, 20.5, 22 and 26rem, none of which is a column count. */
.header_portrait {
  --n: 4;
  --span: calc(var(--n) * (100% - (var(--cols) - 1) * var(--grid-gap)) / var(--cols) + (var(--n) - 1) * var(--grid-gap));
  position: relative;
  width: var(--span);
  margin-inline: auto;
}
.header_portrait > .bezel { width: auto; border-radius: var(--radius-xl); position: relative; z-index: 1; }
.header_portrait > .bezel > .bezel_core { border-radius: calc(var(--radius-xl) - var(--bezel)); }
.header_portrait-media {
  display: grid;
  place-items: center;
  aspect-ratio: 4 / 5;
  background: var(--color-surface);
}
.header_portrait-media img { width: 100%; height: 100%; object-fit: cover; }
.header_portrait-monogram {
  color: rgb(32 33 36 / 0.1);
  font-size: 5rem;
  font-weight: 600;
  --lh: 1;
  letter-spacing: -0.04em;
}

.header_portrait .blur-field {
  display: block;
  inset: -22% -24% -22% -12%;
  opacity: 0.6;
  -webkit-mask-image: radial-gradient(closest-side, #000 28%, transparent 100%);
  mask-image: radial-gradient(closest-side, #000 28%, transparent 100%);
}

/* ▸ Stamp badge: a small rotated tag on the portrait corner. */
.hero_badge {
  display: inline-flex;
  position: absolute;
  z-index: 2;
  top: calc(var(--space-2) * -1);
  right: calc(var(--space-1) * -1);
  padding: var(--space-0-5) var(--space-2);
  border-radius: var(--radius-pill);
  background: var(--color-heading);
  color: var(--color-white);
  font-size: var(--text-xs);
  font-weight: var(--weight-medium);
  letter-spacing: 0.04em;
  box-shadow: var(--ring), var(--shadow-2);
  transform: rotate(-6deg);
}

/* ▸ Caption: floats over the bottom edge of the card at every width, same as
   desktop, just inset from the card's own edges instead of overhanging to
   the left (there's no room to overhang on a narrow, centred card). */
.header_portrait-caption {
  position: absolute;
  z-index: 2;
  left: var(--space-2);
  right: var(--space-2);
  bottom: var(--space-2);
  display: grid;
  gap: 4px;
  padding: 12px var(--space-2);
  border-radius: var(--radius-inner);
  background: var(--color-white);
  box-shadow: var(--ring), var(--shadow-2);
}
.header_portrait-name { color: var(--color-heading); font-size: var(--text-small); font-weight: var(--weight-medium); }
.header_portrait-role { font-size: var(--text-xs); }

@media (min-width: 64em) {
  .hero_badge {
    top: calc(var(--space-3) * -1);
    right: calc(var(--space-3) * -1);
  }
  .header_portrait .blur-field { inset: -30% -35% -30% -15%; opacity: 0.7; }
  /* Fills its grid area exactly: columns 9–12. */
  .header_portrait { width: 100%; margin-inline: 0; }
  .header_portrait-monogram { font-size: 8rem; color: rgb(32 33 36 / 0.08); }
  .header_portrait-caption {
    left: calc(var(--space-5) * -1);
    right: auto;
    bottom: var(--space-5);
    min-width: 15rem;
    padding: 12px var(--space-2);
  }
}

/* --------------------------------------------------------------------------
   9. LOGO STRIP (Relume Logo, marquee)
   ▸ Real logos vary from 1:1 (HSBC) to 11:1 (Freshfields), so a fixed-column
     grid left uneven gaps around the square marks. A single-row marquee gives
     every logo its natural width at one fixed height instead.
   ▸ Rest: grey. Hover/focus: full brand colour (baked into each SVG) + pause.
   -------------------------------------------------------------------------- */
/* ▸ Same padding top and bottom, 7 Oct 2026: half a section's padding, so it
   follows the page's own rhythm token (32 → 48 → 64). It was 24 top / 96 bottom
   at desktop, which pushed the strip up against its top hairline and left a
   large empty gap under it. --pad-top / --pad-bottom stay defined for the
   spec-mode bands and .logo_context below. */
.section_logo {
  --pad-top: calc(var(--section-y) / 2);
  --pad-bottom: var(--pad-top);
  padding-top: var(--pad-top);
  padding-bottom: var(--pad-bottom);
}

/* ▸ Row: mobile keeps the original stacked, centred layout (label above,
   full-width marquee below) so the long label has room to wrap. From 48em up
   it becomes a single inline row — label left, logos immediately after it —
   with the breakout inset moved here (margin-left) so both the label and the
   logo track start flush with .container-large's own left edge; the marquee
   itself (flex: 1) then fills the rest of the row out to the real viewport
   edge, same bleed as before, just driven by flex instead of its own margin. */
.logo_row {
  display: flex;
  flex-direction: column;
  align-items: center;
  padding-inline: var(--gutter);
}
@media (min-width: 48em) {
  .logo_row {
    flex-direction: row;
    align-items: center;
    gap: var(--space-6);
    padding-inline: 0;
    margin-left: max(var(--gutter), calc((100% - var(--container)) / 2));
  }
}

.logo_heading {
  flex: none;
  margin-bottom: var(--space-4);
  color: var(--color-text);
  font-size: var(--text-eyebrow);
  font-weight: var(--weight-medium);
  letter-spacing: 0.12em;
  text-align: center;
  text-transform: uppercase;
}
@media (min-width: 48em) {
  .logo_heading {
    max-width: 14rem;
    margin-bottom: 0;
    text-align: left;
  }
}

.logo_marquee {
  position: relative;
  overflow: hidden;
  width: 100%;
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 var(--space-3), #000 calc(100% - var(--space-3)), transparent);
  mask-image: linear-gradient(90deg, transparent, #000 var(--space-3), #000 calc(100% - var(--space-3)), transparent);
}
@media (min-width: 48em) {
  .logo_marquee {
    flex: 1 1 auto;
    min-width: 0;
    -webkit-mask-image: linear-gradient(90deg, transparent, #000 var(--space-3), #000 100%);
    mask-image: linear-gradient(90deg, transparent, #000 var(--space-3), #000 100%);
  }
}
/* ▸ Seam (fixed 5 Oct 2026): the space between the two duplicated <ul>s is
   each list's own trailing margin, not a `gap` on the track. With a track gap
   the track is 2 × list + 1 gap wide, so translateX(-50%) fell half a gap
   short of one full copy and every loop jumped 32px. With a trailing margin on
   both copies the track is exactly 2 × (list + gap), so -50% is one seamless loop. */
.logo_track {
  display: flex;
  width: max-content;
  gap: 0;
  will-change: transform;
}
.logo_track { animation: logo-marquee 36s linear infinite; }
.logo_marquee:hover .logo_track,
.logo_marquee:focus-within .logo_track,
.logo_marquee.is-paused .logo_track { animation-play-state: paused; }

@keyframes logo-marquee {
  from { transform: translateX(0); }
  to { transform: translateX(-50%); } /* the list is duplicated once, so -50% is exactly one loop */
}

.logo_list {
  display: flex;
  align-items: center;
  flex: none;
  gap: var(--space-6);
  margin-right: var(--space-6); /* the loop seam: must equal the gap between logos (see .logo_track) */
  /* ▸ No edge padding here: `ul[role="list"] { padding: 0 }` in Base beats a single class on
     specificity, so it would be silently dropped anyway. The mask on .logo_marquee handles edge
     softening instead. */
}
.logo_item {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 44px;
  flex: none;
}
/* ▸ Calibrated, not guessed: matches the ~32px/~34px full glyph height every trimmed logo SVG
     renders at (see "Logo optical sizing" in handoff.html), so the one non-vector placeholder
     (British Land) doesn't read as smaller than its neighbours. */
.logo_wordmark {
  color: rgb(32 33 36 / 0.3);
  font-size: 1.75rem;
  font-weight: 600;
  letter-spacing: -0.01em;
  --lh: 1;
  white-space: nowrap;
  transition: color var(--dur-base) var(--ease-soft);
}
/* British Land placeholder (no free-use logo file yet): darkens like a logo would, no brand colour claimed */
.logo_item:hover .logo_wordmark { color: var(--color-heading); }

/* ▸ Rest: every mark forced to the same flat light grey regardless of its own
   brand colour (brightness(0) silhouettes it, opacity lightens that
   silhouette) so Santander red, HSBC red, BNP green etc. all read as one
   consistent tone instead of a mismatched rainbow row. Hover: real brand
   colour (baked into each SVG) + the marquee pauses. */
.logo_item img {
  height: var(--logo-h);
  width: auto;
  filter: brightness(0);
  opacity: 0.3;
  transition: opacity var(--dur-base) var(--ease-soft), filter var(--dur-base) var(--ease-soft);
}
.logo_item:hover img { filter: none; opacity: 1; }
/* ▸ Optical sizes (re-measured 5 Oct 2026). One shared height gives equal glyph
   height, not equal weight: counting each mark's solid pixels at the shared
   height, Freshfields (an 11:1 bold wordmark) carried about 2× the ink of the
   median logo, while the compact marks, Toyota and HSBC, carried about half.
   Multipliers of the shared --logo-h pull the eight real marks into roughly
   0.6–1.2× of the median (counted again after the change; the compact emblems
   stay at the light end on purpose, since a tall emblem would break the 44px
   row). Santander, BNP Paribas, Kia and Linklaters were already close and
   stay at 1×. */
.logo_list { --logo-h: 32px; }
.logo_item img[src*="freshfields"] { height: calc(var(--logo-h) * 0.5); }
.logo_item img[src*="toyota"] { height: calc(var(--logo-h) * 1.3); }
.logo_item img[src*="hsbc"] { height: calc(var(--logo-h) * 1.18); }
.logo_item img[src*="ford"] { height: calc(var(--logo-h) * 1.06); }

@media (min-width: 48em) {
  .logo_list { --logo-h: 34px; gap: var(--space-8); margin-right: var(--space-8); }
  .logo_wordmark { font-size: 1.875rem; }
}

/* Static fallback: no JS, or prefers-reduced-motion (main.js adds .is-static and hides the duplicate list) */
.logo_marquee.is-static {
  margin-left: 0;
  overflow: visible;
  -webkit-mask-image: none;
  mask-image: none;
}
.logo_marquee.is-static .logo_track { width: 100%; animation: none; }
.logo_marquee.is-static .logo_list {
  flex-wrap: wrap;
  justify-content: center;
  width: 100%;
  margin-right: 0;
  padding-inline: var(--gutter);
  row-gap: var(--space-3);
}
.logo_marquee.is-static .logo_list[aria-hidden] { display: none; }
html:not(.js) .logo_marquee { margin-left: 0; overflow: visible; -webkit-mask-image: none; mask-image: none; }
html:not(.js) .logo_track { width: 100%; animation: none; }
html:not(.js) .logo_list { flex-wrap: wrap; justify-content: center; width: 100%; margin-right: 0; padding-inline: var(--gutter); row-gap: var(--space-3); }
html:not(.js) .logo_list[aria-hidden] { display: none; }

/* ▸ Credibility line: states which names are direct clients/employers vs.
   brands reached through a platform, so the logo strip above never implies
   a direct engagement that didn't happen (1 Oct 2026). */
.logo_context {
  max-width: 44rem;
  margin: calc(var(--pad-bottom, var(--space-8)) * -1) auto var(--space-8);
  text-align: center;
  font-size: var(--text-small);
}
.logo_context strong { color: var(--color-heading); font-weight: 600; }

/* --------------------------------------------------------------------------
   10. FOCUS AREAS — bespoke bridge section, not a stock Relume component.
   Sits between the brand marquee and the case studies to name the four
   kinds of problem each portfolio piece sits under. Card look deliberately
   echoes §14 STACK below (ring + shadow-1 card, rotated glowing icon
   sticker) since both are "a grid of equal, parallel things" — but kept in
   its own .focus_* class family per this file's one-family-per-section
   convention, so either can be retuned without touching the other.
   -------------------------------------------------------------------------- */
.focus_list {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--grid-gap);
  list-style: none;
  margin: var(--space-6) 0 0;
  padding: 0;
}
@media (min-width: 48em) { .focus_list { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (min-width: 64em) { .focus_list { grid-template-columns: repeat(4, minmax(0, 1fr)); } }

.focus_card {
  height: 100%;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--space-1);
  padding: var(--space-3);
  border-radius: var(--radius-outer);
  background: var(--color-white);
  box-shadow: var(--ring), var(--shadow-1);
  transition: transform var(--dur-slow) var(--ease-out), box-shadow var(--dur-slow) var(--ease-soft);
}
.focus_card:hover { box-shadow: var(--ring), var(--shadow-2); }

.focus_top {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  width: 100%;
}

/* ▸ Icon badge: same rotated sticker + colour-glow treatment as .stack_logo,
   holding a custom stroke icon (fill: none, stroke 1.5, round caps — see
   §4 TYPOGRAPHY's .button_icon svg for the same stroke recipe) instead of
   a brand logo image. */
.focus_icon {
  display: grid;
  place-items: center;
  width: 48px;
  height: 48px;
  flex: none;
  padding: 12px;
  border-radius: 14px;
  background: var(--color-white);
  box-shadow: var(--ring), 0 10px 20px -8px var(--glow, rgb(32 33 36 / 0.16)), var(--shadow-1);
  transform: rotate(var(--rotate, 0deg));
  transition: transform var(--dur-slow) var(--ease-out), box-shadow var(--dur-slow) var(--ease-soft);
}
.focus_icon svg {
  width: 100%;
  height: 100%;
  fill: none;
  stroke: var(--color-heading);
  stroke-width: 1.5;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.focus_card:hover .focus_icon {
  transform: rotate(0deg) translateY(-3px);
  box-shadow: var(--ring), 0 16px 28px -8px var(--glow, rgb(32 33 36 / 0.2)), var(--shadow-2);
}

.focus_name { margin-top: var(--space-1); color: var(--color-heading); font-size: var(--text-body); font-weight: var(--weight-medium); }
.focus_desc { color: var(--color-text); font-size: var(--text-small); }
.focus_proof {
  margin-top: auto;
  padding-top: var(--space-2);
  color: var(--color-text);
  font-size: var(--text-xs);
}

/* ▸ Scope block: "what I can build" breakdown, sitting under the focus-area
   cards in the same section (1 Oct 2026, replacing the old 7-tool bento
   grid). A definition list reads better than cards here since each row is
   a claim + its concrete evidence, not a parallel/equal set of things. */
.scope {
  margin-top: var(--space-8);
  padding-top: var(--space-8);
  border-top: 1px solid var(--color-hairline-strong);
}
.scope_intro { max-width: 40rem; margin-bottom: var(--space-4); }
.scope_intro h3 { margin-bottom: var(--space-1); }
.scope_list { display: grid; gap: var(--space-3); }
@media (min-width: 48em) { .scope_list { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--space-4); } }
.scope_row dt { margin-bottom: 4px; color: var(--color-heading); font-weight: var(--weight-medium); }
.scope_row dd { margin: 0; color: var(--color-text); font-size: var(--text-small); }
.scope_tools {
  margin-top: var(--space-5);
  padding-top: var(--space-3);
  border-top: 1px dashed var(--color-hairline-strong);
  color: var(--color-text);
  font-size: var(--text-small);
}
.scope_tools span { display: block; margin-bottom: 4px; color: var(--color-heading); font-size: var(--text-xs); font-weight: 600; letter-spacing: 0.08em; text-transform: uppercase; }

/* --------------------------------------------------------------------------
   11. PORTFOLIO (Relume Portfolio)
   -------------------------------------------------------------------------- */
.portfolio_header {
  display: grid;
  gap: var(--space-4);
  margin-bottom: var(--space-5);
}
@media (min-width: 48em) {
  .portfolio_header { justify-items: start; margin-bottom: var(--space-6); }
}
@media (min-width: 64em) {
  /* Heading on columns 1–8, filter on 9–12 and right-aligned to column 12. */
  .portfolio_header { grid-template-columns: var(--grid-12); column-gap: var(--grid-gap); align-items: end; }
  .portfolio_header > :first-child { grid-column: 1 / span 8; }
  .portfolio_header > .filter_group { grid-column: 9 / span 4; justify-self: end; }
}

/* ▸ Filter: aria-pressed toggle buttons; sliding thumb = transform only */
.filter_group {
  position: relative;
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  padding: var(--space-0-5);
  border-radius: var(--radius-pill);
  background: var(--color-white);
  box-shadow: var(--ring), var(--shadow-1);
}
html:not(.js) .filter_group { display: none; }

.filter_thumb {
  position: absolute;
  top: var(--space-0-5);
  bottom: var(--space-0-5);
  left: var(--space-0-5);
  width: calc((100% - var(--space-1)) / 3);
  border-radius: var(--radius-pill);
  background: var(--color-heading);
  transition: transform var(--dur-slow) var(--ease-out);
}
.filter_group[data-active="1"] .filter_thumb { transform: translateX(100%); }
.filter_group[data-active="2"] .filter_thumb { transform: translateX(200%); }

.filter_button {
  position: relative;
  z-index: 1;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-1);
  min-height: 44px;
  min-width: 6.5rem;
  padding: 0 var(--space-2);
  border: 0;
  border-radius: var(--radius-pill);
  background: transparent;
  color: var(--color-text);
  font-size: var(--text-small);
  font-weight: var(--weight-medium);
  cursor: pointer;
  transition: color var(--dur-base) var(--ease-soft);
}
.filter_button:hover, .filter_button.is-hover { color: var(--color-heading); }
.filter_button[aria-pressed="true"] { color: var(--color-white); }
.filter_count {
  min-width: 20px;
  padding: 0 8px;
  border-radius: var(--radius-pill);
  background: rgb(32 33 36 / 0.06);
  font-size: 0.75rem;
  line-height: 20px;
  font-variant-numeric: tabular-nums;
  transition: background-color var(--dur-base) var(--ease-soft);
}
.filter_button[aria-pressed="true"] .filter_count { background: rgb(255 255 255 / 0.16); }

.portfolio_list {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--grid-gap);
}
@media (min-width: 48em) { .portfolio_list { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (min-width: 64em) { .portfolio_list { grid-template-columns: repeat(3, minmax(0, 1fr)); } }

.portfolio_item[hidden] { display: none; }

/* ▸ Card states: rest · hover (shadow-2, backdrop photo zoom, link underline + arrow 4px; no lift)
     · focus-visible (2px primary ring around whole card) */
.portfolio_card {
  position: relative;
  display: flex;
  flex-direction: column;
  height: 100%;
  padding: var(--bezel);
  border-radius: var(--radius-outer);
  background: var(--color-white);
  box-shadow: var(--ring), var(--shadow-1);
  transition: transform var(--dur-slow) var(--ease-out), box-shadow var(--dur-slow) var(--ease-soft);
}
.portfolio_card:hover, .portfolio_card.is-hover {
  box-shadow: var(--ring), var(--shadow-2);
}
.portfolio_card:has(.portfolio_link:focus-visible),
.portfolio_card.is-focus {
  outline: 2px solid var(--color-primary);
  outline-offset: 4px;
}

/* ▸ Media: blurred photo well + floating product window (real screenshot goes in the window) */
.portfolio_media {
  position: relative;
  isolation: isolate;
  aspect-ratio: 16 / 10;
  overflow: hidden;
  border-radius: var(--radius-inner);
  background: var(--fallback, var(--color-surface));
}
/* ▸ Thumb frame: a translucent glass mat around the real screenshot, centred with
   equal left/right margin and flush (zero-padding, square-cornered) against the
   card's bottom edge, so the window reads as cropped there rather than floating.
   The blurred copy of the same photo peeks out through the mat at the top/sides. */
.portfolio_media .thumb-frame {
  position: absolute;
  inset: 9% 8% 0 8%;
  z-index: 1;
  padding: 8px;
  background: rgb(255 255 255 / 0.32);
  border: 1px solid rgb(255 255 255 / 0.5);
  border-radius: 10px 10px 0 0;
  overflow: hidden;
  /* ▸ box-shadow, not filter: drop-shadow — a filter on a rounded, clipped
     (overflow: hidden) element gets rasterised as its own layer before that
     clip is applied, leaving a faint ghost seam just inside the curve. A
     plain box-shadow paints outside the border box instead, so it isn't
     subject to the element's own overflow clip and has no such seam. */
  box-shadow: 0 16px 24px rgb(0 0 0 / 0.22);
  transition: transform var(--dur-reveal) var(--ease-out);
}
.portfolio_media .thumb {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  border-radius: 4px 4px 0 0;
}
/* Hover depth: the backdrop photo drifts in, and the screenshot zooms a touch inside its window.
   The window, the card and the backdrop stay put, so nothing moves up (8 Oct 2026). */
.portfolio_card:hover .blur-photo img, .portfolio_card.is-hover .blur-photo img { --zoom: 1.1; }
@media (prefers-reduced-motion: no-preference) {
  .portfolio_media .thumb { transform-origin: 50% 0; transition: transform var(--dur-slow) var(--ease-out); }
  .portfolio_card:hover .thumb, .portfolio_card.is-hover .thumb,
  .portfolio_card:has(.portfolio_link:focus-visible) .thumb { transform: scale(1.05); }
}

.portfolio_body {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--space-1);
  flex: 1;
  padding: var(--space-3) var(--space-2) var(--space-1);
}
.portfolio_body .heading-style-h3 { margin-top: var(--space-1); }

/* ▸ Two tags per card: category (filled) + status (outlined) — status is set
   per project rather than one blanket "every project shipped" claim, since
   that wasn't true once Amigo Screen and TOKO (both ongoing) were added. */
.portfolio_tags { display: flex; flex-wrap: wrap; gap: 8px; }

.tag {
  display: inline-flex;
  align-items: center;
  min-height: 24px;
  padding: 0 12px;
  border-radius: var(--radius-pill);
  background: var(--color-surface);
  color: var(--color-heading);
  font-size: 0.8125rem;
  font-weight: var(--weight-medium);
  letter-spacing: 0.02em;
}
.tag.is-status {
  background: transparent;
  box-shadow: inset 0 0 0 1px var(--color-hairline-strong);
  color: var(--color-text);
  font-weight: var(--weight-regular);
}

/* ▸ Card body copy: sized down from default body text so a 3–4 sentence
   outcome (problem + role + evidence) stays readable without ballooning
   card height past the screenshot above it. */
.portfolio_outcome { font-size: var(--text-small); --lh: 1.5; color: var(--color-text); }
.portfolio_shows { font-size: var(--text-xs); color: var(--color-text); }
.portfolio_shows span { color: var(--color-heading); font-weight: 600; }

.portfolio_link {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  min-height: 44px;
  margin-top: auto;
  color: var(--color-primary);
  font-size: var(--text-small);
  font-weight: var(--weight-medium);
  text-decoration: none;
  text-underline-offset: 3px;
  transition: color var(--dur-fast) var(--ease-soft);
}
/* Stretched link: whole card is the hit area */
.portfolio_link::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: var(--radius-outer);
}
.portfolio_link-arrow { display: inline-block; transition: transform var(--dur-base) var(--ease-out); }
.portfolio_card:hover .portfolio_link,
.portfolio_card.is-hover .portfolio_link { color: var(--color-primary-hover); text-decoration: underline; }
.portfolio_card:hover .portfolio_link-arrow,
.portfolio_card.is-hover .portfolio_link-arrow { transform: translateX(4px); }
@supports selector(:has(*)) {
  .portfolio_link:focus-visible { outline: none; }
}

/* --------------------------------------------------------------------------
   12. REVIEWS (Relume Testimonial, masonry via CSS columns)
   ▸ DOM order = reading order (top→bottom per column) at ≥48em. Below that,
     §12b turns this into a swipeable carousel along with Focus and Work.
   -------------------------------------------------------------------------- */
.section_testimonial .section-heading { margin-bottom: var(--space-6); }

.testimonial_list { columns: 1; column-gap: var(--grid-gap); }
@media (min-width: 48em) { .testimonial_list { columns: 2; } }
@media (min-width: 64em) { .testimonial_list { columns: 3; } }

.testimonial_item {
  break-inside: avoid;
  margin-bottom: var(--grid-gap);
}
.testimonial_card { background: var(--color-surface); box-shadow: var(--ring); }
.testimonial_core {
  display: grid;
  gap: var(--space-3);
  padding: var(--space-3);
  background: var(--color-white);
  box-shadow: var(--ring);
}
@media (min-width: 64em) { .testimonial_core { padding: var(--space-4); } }

.testimonial_mark {
  height: 24px;
  color: var(--color-primary);
  font-size: 3.5rem;
  font-weight: 600;
  --lh: 1;
}
.testimonial_core blockquote p { color: var(--color-heading); font-size: var(--text-body); --lh: 1.5; }
.testimonial_author { display: flex; align-items: center; gap: 12px; }
.testimonial_author > span:last-child { display: grid; gap: 4px; }
.testimonial_avatar {
  width: 40px;
  height: 40px;
  flex: none;
  border-radius: 50%;
  background: var(--color-surface);
  box-shadow: var(--ring);
  object-fit: cover;
}
/* ▸ Initials fallback: for a reviewer with no photo on hand */
.testimonial_avatar.is-initials {
  display: grid;
  place-items: center;
  background: #FFE1A8;
  color: #B35900;
  font-size: var(--text-xs);
  font-weight: var(--weight-medium);
}
.testimonial_name { justify-self: start; color: var(--color-heading); font-size: var(--text-small); font-weight: var(--weight-medium); }
.testimonial_role { font-size: var(--text-xs); }

/* --------------------------------------------------------------------------
   12b. MOBILE CARD CAROUSELS (Focus areas, Selected work, Reviews)
   ▸ Below 48em (the breakpoint where each of these three becomes its normal
     multi-column grid/masonry), swap the vertical stack for a native
     horizontal swipe + scroll-snap row instead: each card peeks the next one.
     No arrows (thumb-friendly swipe covers that), but main.js §5c injects a
     small dot row after each list so a swipe shows how many cards are left —
     same pattern as the dots under each case-study page's own project
     carousel. The row bleeds past the section's own gutter to the real screen
     edge so the peeking card reaches further than the content column would
     allow; the first card's own padding-inline keeps it flush with the
     page's normal left margin. Portfolio filtering (.portfolio_item[hidden])
     and the focus/portfolio reveal-in animations are untouched — hiding/
     animating a flex item works the same as a grid item. Equal card height
     per row comes for free from flex's default align-items: stretch, same
     mechanism .focus_card/.portfolio_card's own height: 100% already relied
     on in the grid layout. */

/* ▸ Dots: injected by assets/carousel.js right after each carousel list; hidden outside
   the mobile breakpoint below since the grid/masonry layout there has no
   scroll position to indicate (the media query below turns this on). */
.carousel_dots {
  display: none;
  justify-content: center;
  flex-wrap: wrap;
  gap: var(--space-1);
  margin-top: var(--space-3);
}
.carousel_dot {
  width: 8px;
  height: 8px;
  padding: 0;
  border: 0;
  border-radius: var(--radius-pill);
  background: var(--color-hairline-strong);
  cursor: pointer;
  transition: background-color var(--dur-base) var(--ease-soft), transform var(--dur-base) var(--ease-soft);
}
.carousel_dot.is-active { background: var(--color-primary); transform: scale(1.3); }

@media (max-width: 47.9375em) {
  /* ▸ Selectors scoped to each section (not bare .focus_list etc.) only for the
     padding: `ul[role="list"] { padding: 0 }` in Base (line ~172) has
     specificity (0,1,1), which beats a single class (0,1,0) regardless of
     source order and was silently dropping this padding entirely — the same
     trap §9's own comment already flags for .logo_list. Scoping to the parent
     section (0,2,0) settles it for good instead of relying on a source-order
     tie-break. */
  .section_focus .focus_list,
  .section_portfolio .portfolio_list,
  .section_testimonial .testimonial_list {
    display: flex;
    flex-wrap: nowrap;
    columns: initial;
    overflow-x: auto;
    overscroll-behavior-x: contain;
    scroll-snap-type: x mandatory;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
    gap: var(--grid-gap);
    margin-inline: calc(var(--gutter) * -1);
    padding: 16px var(--gutter) var(--space-2);
    /* Snap points honour the gutter: without this every swipe or dot tap
       parked the card flush against the screen edge, not at the page margin
       the first card starts on (fixed 5 Oct 2026). */
    scroll-padding-inline: var(--gutter);
    -webkit-mask-image: linear-gradient(90deg, #000 0%, #000 94%, transparent 100%);
    mask-image: linear-gradient(90deg, #000 0%, #000 94%, transparent 100%);
  }
  .focus_list::-webkit-scrollbar,
  .portfolio_list::-webkit-scrollbar,
  .testimonial_list::-webkit-scrollbar { display: none; }

  .focus_item,
  .portfolio_item,
  .testimonial_item {
    flex: 0 0 var(--span); /* 3 of the 4 columns, see .experience_item */
    scroll-snap-align: start;
    margin-bottom: 0;
  }

  .carousel_dots { display: flex; }
}

/* --------------------------------------------------------------------------
   13. BOOK A CALL (Relume Contact, split)
   ▸ Section is surface grey → links here use heading colour (blue fails 4.5:1 on surface)
   -------------------------------------------------------------------------- */
.contact_component { display: grid; gap: var(--space-6); }
@media (min-width: 64em) {
  .contact_component {
    /* Copy on columns 1–5, booking embed on 6–12 (was 5fr : 7fr with a 64px gap). */
    grid-template-columns: var(--grid-12);
    column-gap: var(--grid-gap);
    align-items: start;
  }
  .contact_content { grid-column: 1 / span 5; position: sticky; top: calc(var(--nav-offset) + var(--nav-height) + var(--space-5)); }
  .contact_visual { grid-column: 6 / span 7; }
}

.contact_content { display: grid; justify-items: start; gap: var(--space-3); }
.contact_content .eyebrow { margin-bottom: calc(var(--space-1) * -1); }

.contact_steps { display: grid; gap: var(--space-2); margin-top: var(--space-1); }
.contact_steps li {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  color: var(--color-heading);
  font-weight: var(--weight-medium);
}
.contact_step-num {
  display: grid;
  place-items: center;
  width: 32px;
  height: 32px;
  flex: none;
  border-radius: 50%;
  background: var(--color-white);
  box-shadow: var(--ring);
  color: var(--color-primary);
  font-size: var(--text-xs);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}

.contact_risk {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-top: var(--space-1);
  padding: var(--space-2) var(--space-3) var(--space-2) var(--space-2);
  border-radius: var(--radius-inner);
  background: var(--color-white);
  box-shadow: var(--ring);
  color: var(--color-heading);
  font-weight: var(--weight-medium);
}
.contact_risk-icon {
  width: 24px;
  height: 24px;
  flex: none;
  fill: none;
  stroke: var(--color-primary);
  stroke-width: 1.5;
  stroke-linecap: round;
  stroke-linejoin: round;
}

/* ▸ Direct contact row: email, LinkedIn and CV sit next to the steps, not
   buried in the footer, so the "review experience → contact" journey never
   depends on the Calendly embed loading (1 Oct 2026). */
.contact_direct {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-1) var(--space-3);
  margin-top: var(--space-2);
}
.contact_direct-link {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  min-height: 44px;
  color: var(--color-heading);
  font-weight: var(--weight-medium);
  text-decoration: none;
}
.contact_direct-link svg { width: 18px; height: 18px; flex: none; fill: none; stroke: var(--color-primary); stroke-width: 1.6; stroke-linecap: round; stroke-linejoin: round; }
.contact_direct-link:hover { color: var(--color-primary-hover); text-decoration: underline; text-underline-offset: 3px; }

/* ▸ Calendly failure fallback: main.js shows this (and hides the skeleton)
   if the real widget hasn't mounted a few seconds after its script loads,
   so a broken embed is never a tall empty panel. */
.contact_cal-fallback {
  display: none;
  flex-direction: column;
  gap: var(--space-2);
  align-items: flex-start;
  justify-content: center;
  height: 100%;
  min-height: 28rem;
  padding: var(--space-4);
  text-align: left;
}
.contact_cal-fallback:not([hidden]) { display: flex; }
.contact_cal-fallback-title { color: var(--color-heading); font-weight: var(--weight-medium); }
.contact_cal-fallback-text { max-width: 28rem; }

/* ▸ Calendar card floats on a feathered blur field (overflow-x: clip keeps the sticky column working) */
.section_contact { overflow-x: clip; }
.contact_visual { position: relative; isolation: isolate; }
.contact_visual .blur-field { inset: -16% -12%; }
.contact_embed { position: relative; z-index: 1; }
@media (min-width: 64em) {
  .contact_visual .blur-field { inset: -22% -18% -22% -24%; }
}

.contact_embed-core {
  display: flex;
  flex-direction: column;
  background: var(--color-white);
}
@media (min-width: 48em) { .contact_embed-core { min-height: 35rem; } } /* 560 */
@media (min-width: 64em) { .contact_embed-core { min-height: 40rem; } } /* 640 */

.contact_embed-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--space-2);
  padding: var(--space-3) var(--space-3) var(--space-2);
}
.contact_embed-title { color: var(--color-heading); font-weight: var(--weight-medium); }
.contact_embed-sub { font-size: var(--text-small); }

.contact_cal {
  position: relative;
  flex: 1;
  display: flex;
  flex-direction: column;
  min-height: 30rem;          /* 480 → 520 ≥768; see main.js §7 — read as a literal
                                  px value and handed to Calendly's own init code,
                                  which otherwise can't tell how tall its iframe
                                  should be. 21rem/26rem left the day-grid clipped
                                  on mobile once the real calendar rendered. */
  margin: 0 var(--space-2);
  border-radius: var(--radius-inner);
  background: var(--color-surface);
  overflow: hidden;
}
@media (min-width: 48em) { .contact_cal { min-height: 32.5rem; } }
.contact_cal.is-live { background: var(--color-white); overflow: visible; }
.contact_cal-note {
  position: absolute;
  left: 50%;
  bottom: var(--space-3);
  width: max-content;
  max-width: calc(100% - var(--space-4));
  transform: translateX(-50%);
  font-size: var(--text-xs);
  text-align: center;
}
.contact_cal-note .placeholder { background: var(--color-white); padding: var(--space-0-5) var(--space-1); }

/* Preview skeleton (aria-hidden) until data-calendly-link is set */
.cal-skeleton {
  display: grid;
  gap: var(--space-3);
  padding: var(--space-3);
}
@media (min-width: 48em) { .cal-skeleton { grid-template-columns: minmax(0, 1fr) 9rem; } }
.cal-skeleton_head { display: flex; align-items: center; justify-content: space-between; margin-bottom: var(--space-2); }
.cal-skeleton_bar { height: 10px; border-radius: 5px; background: rgb(32 33 36 / 0.12); }
.cal-skeleton_arrows { display: flex; gap: var(--space-1); }
.cal-skeleton_arrows span { width: 28px; height: 28px; border-radius: 50%; background: var(--color-white); box-shadow: var(--ring); }
.cal-skeleton_grid { display: grid; grid-template-columns: repeat(7, minmax(0, 1fr)); gap: 8px; }
.cal-skeleton_grid span { aspect-ratio: 1; border-radius: 10px; background: rgb(32 33 36 / 0.04); }
.cal-skeleton_grid span.is-dow { aspect-ratio: auto; height: 6px; margin: 0 25%; background: rgb(32 33 36 / 0.1); }
.cal-skeleton_grid span.is-open { background: var(--color-primary-tint); }
.cal-skeleton_grid span.is-selected { background: var(--color-primary); }
.cal-skeleton_slots { display: none; gap: var(--space-1); align-content: start; }
@media (min-width: 48em) { .cal-skeleton_slots { display: grid; } }
.cal-skeleton_slots span { height: 36px; border-radius: 10px; background: var(--color-white); box-shadow: var(--ring); }
.cal-skeleton_slots span:first-child { height: 10px; width: 60%; margin-bottom: var(--space-1); background: rgb(32 33 36 / 0.12); box-shadow: none; }

.contact_fallback {
  padding: var(--space-2) var(--space-3) var(--space-3);
  font-size: var(--text-small);
}
.contact_fallback a {
  display: inline-block;
  padding-block: 12px;
  color: var(--color-primary);
  font-weight: var(--weight-medium);
  text-underline-offset: 3px;
}
.contact_fallback a:hover { color: var(--color-primary-hover); text-decoration-thickness: 2px; }

/* --------------------------------------------------------------------------
   14. STACK (bespoke toolkit bento grid — not a stock Relume component)
   ▸ 1 col mobile → 2 col ≥768, featured tile spans both → 4 col ≥1024, featured
     tile spans 2 of 4. Cards reuse the .bezel look (ring + shadow-1, lift on
     hover); each real logo sits in a small rotated "sticker" badge that settles
     flat and lifts on hover, glowing in that brand's own colour.
   -------------------------------------------------------------------------- */
.stack_list {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--grid-gap);
  list-style: none;
  margin: var(--space-6) 0 0;
  padding: 0;
}
@media (min-width: 48em) {
  .stack_list { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .stack_item.is-featured { grid-column: span 2; }
}
@media (min-width: 64em) {
  .stack_list { grid-template-columns: repeat(4, minmax(0, 1fr)); }
}

.stack_card {
  position: relative;
  height: 100%;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--space-1);
  padding: var(--space-3);
  border-radius: var(--radius-outer);
  background: var(--color-white);
  box-shadow: var(--ring), var(--shadow-1);
  transition: transform var(--dur-slow) var(--ease-out), box-shadow var(--dur-slow) var(--ease-soft);
}
.stack_card:hover, .stack_card.is-hover {
  box-shadow: var(--ring), var(--shadow-2);
}

.stack_top {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  width: 100%;
}

/* ▸ Logo badge: a small "sticker" pinned at a slight angle, glowing in the
   brand's own colour (set per instance via --glow), that settles flat and
   lifts toward the viewer on hover — see the inline --glow/--rotate values
   in index.html for each tool's colour. */
.stack_logo {
  display: grid;
  place-items: center;
  width: 48px;
  height: 48px;
  flex: none;
  padding: 12px;
  border-radius: 14px;
  background: var(--color-white);
  box-shadow: var(--ring), 0 10px 20px -8px var(--glow, rgb(32 33 36 / 0.16)), var(--shadow-1);
  transform: rotate(var(--rotate, 0deg));
  transition: transform var(--dur-slow) var(--ease-out), box-shadow var(--dur-slow) var(--ease-soft);
}
.stack_logo img { width: 100%; height: 100%; object-fit: contain; }
.stack_card:hover .stack_logo, .stack_card.is-hover .stack_logo {
  transform: rotate(0deg) translateY(-3px);
  box-shadow: var(--ring), 0 16px 28px -8px var(--glow, rgb(32 33 36 / 0.2)), var(--shadow-2);
}

.stack_name { margin-top: var(--space-1); color: var(--color-heading); font-size: var(--text-body); font-weight: var(--weight-medium); }
.stack_desc { color: var(--color-text); font-size: var(--text-small); }

.stack_item.is-featured .stack_card { background: var(--color-surface); }
.stack_item.is-featured .stack_logo { width: 64px; height: 64px; padding: 12px; border-radius: 18px; }
.stack_item.is-featured .stack_name { font-size: var(--text-h3); }
@media (min-width: 64em) {
  .stack_item.is-featured .stack_desc { max-width: 30rem; }
}

/* --------------------------------------------------------------------------
   14b. EXPERIENCE (bespoke carousel — not a stock Relume component)
   ▸ A carousel at every breakpoint, not just the mobile fallback §12b uses for
     Focus/Work/Reviews: swipe + scroll-snap always, dots always, arrows from
     48em up once there's room for them. The card track bleeds past the
     container's right edge the same way §9's logo row does — left pinned to
     .container-large's own edge via the row's margin-left, right left
     unconstrained — so cards reach toward the real screen edge instead of
     stopping at the content column. Arrows live in the header row next to the
     heading (not overlaid on the bleeding track itself), which sidesteps any
     fuss about where an overlay button would sit against an edge that moves
     with viewport width. */
.section_experience {
  padding-top: var(--section-y);
  padding-bottom: var(--section-y);
}

.experience_header {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: var(--space-4);
}

.experience_linkedin {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  color: var(--color-primary);
  font-weight: var(--weight-medium);
  text-decoration: none;
  text-underline-offset: 3px;
  transition: color var(--dur-fast) var(--ease-soft);
}
.experience_linkedin:hover { color: var(--color-primary-hover); text-decoration: underline; }

.experience_nav { display: none; flex: none; gap: var(--space-2); }
@media (min-width: 48em) {
  .experience_nav { display: flex; }
}
.experience_arrow {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  padding: 0; /* the UA gives <button> 6px of side padding */
  border: 0;
  border-radius: var(--radius-pill);
  background: var(--color-white);
  box-shadow: var(--ring), var(--shadow-1);
  color: var(--color-heading);
  cursor: pointer;
  transition: background-color var(--dur-fast) var(--ease-soft), box-shadow var(--dur-fast) var(--ease-soft), transform var(--dur-fast) var(--ease-soft);
}
.experience_arrow:hover { background: var(--color-surface); box-shadow: var(--ring), var(--shadow-2); }
.experience_arrow:active { transform: scale(0.96); }

/* ▸ Full-bleed (7 Oct 2026): the row runs from screen edge to screen edge, so cards
   sliding off to the left are no longer clipped at the content column. The first
   card still starts on the container edge via the track's padding + scroll-padding
   (--carousel-edge). Trailing space, so the LAST card can reach that edge too, is
   added by assets/carousel.js. */
.experience_row {
  position: relative;
  margin-top: var(--space-6);
}
.experience_track {
  display: flex;
  gap: var(--grid-gap);
  list-style: none;
  margin: 0;
  overflow-x: auto;
  overscroll-behavior-x: contain;
  scroll-snap-type: x mandatory;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
  -webkit-mask-image: linear-gradient(90deg, #000 0%, #000 94%, transparent 100%);
  mask-image: linear-gradient(90deg, #000 0%, #000 94%, transparent 100%);
  scroll-padding-inline-start: var(--carousel-edge);
}
/* ▸ Scoped to .experience_row (0,2,0), not bare .experience_track (0,1,0), so
   it actually beats Base's `ul[role="list"] { padding: 0 }` (0,1,1) — same
   specificity trap §12b's own comment flags for those three carousels.
   ▸ Padding on both sides = the container offset, so the content box is exactly one
   container wide: card 1 starts on the container edge, and the cards' column-based
   widths (below) resolve against the container, not the screen. The trailing space that
   lets the LAST card reach the container edge is added by assets/carousel.js. */
.experience_row .experience_track {
  padding: 16px var(--carousel-edge) var(--space-2);
}
.experience_track::-webkit-scrollbar { display: none; }

/* ▸ Components on the columns (7 Oct 2026): a card is a whole number of columns wide.
   3 of 4 on a phone (the next card peeks into column 4), 4 of 8 on a tablet (two across),
   4 of 12 on desktop (three across). The gap between cards is the gutter, so a card
   starts on a column line and every carousel step moves exactly that many columns. */
.experience_item, .focus_item, .portfolio_item, .testimonial_item {
  --n: 3;
  --span: calc(var(--n) * (100% - (var(--cols) - 1) * var(--grid-gap)) / var(--cols) + (var(--n) - 1) * var(--grid-gap));
}
@media (min-width: 48em) {
  .experience_item { --n: 4; }
}
.experience_item {
  flex: 0 0 var(--span);
  scroll-snap-align: start;
}

.experience_card {
  height: 100%;
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  padding: var(--space-3);
  border-radius: var(--radius-outer);
  background: var(--color-white);
  box-shadow: var(--ring), var(--shadow-1);
  transition: transform var(--dur-slow) var(--ease-out), box-shadow var(--dur-slow) var(--ease-soft);
}
.experience_card:hover { box-shadow: var(--ring), var(--shadow-2); }

.experience_top { display: flex; align-items: center; gap: var(--space-2); }
/* ▸ Logo: same rotated sticker + colour-glow chrome as .focus_icon/.stack_logo,
   holding initials instead of an icon or image — these are past employers,
   not the client-logo-style marks in the §9 strip, so a flat monogram badge
   avoids sourcing a real logo file for each one. */
.experience_logo {
  display: grid;
  place-items: center;
  overflow: hidden;
  width: 40px;
  height: 40px;
  flex: none;
  border-radius: 12px;
  background: var(--color-white);
  box-shadow: var(--ring), 0 10px 20px -8px var(--glow, rgb(32 33 36 / 0.16)), var(--shadow-1);
  transform: rotate(var(--rotate, 0deg));
  color: var(--color-heading);
  font-size: var(--text-xs);
  font-weight: 700;
}
.experience_logo img { width: 100%; height: 100%; object-fit: cover; }
.experience_company { color: var(--color-heading); font-size: var(--text-body); font-weight: var(--weight-medium); }
.experience_period { color: var(--color-text); font-size: var(--text-xs); }
.experience_role { color: var(--color-text); font-size: var(--text-small); font-weight: var(--weight-medium); }
.experience_summary { color: var(--color-text); font-size: var(--text-small); }

/* Dots always on here (unlike §12b's mobile-only ones), since this carousel
   is the design at every width, not a fallback for a grid. */
.experience_track + .carousel_dots { display: flex; margin-top: var(--space-3); }

/* --------------------------------------------------------------------------
   15. FAQ (Relume FAQ, accordion)
   ▸ States: collapsed (surface) · hover (icon fills) · expanded (white card, icon rotates 45° to ×) · focus-visible
   -------------------------------------------------------------------------- */
.faq_component { display: grid; gap: var(--space-5); }
@media (min-width: 64em) {
  .faq_component {
    /* Heading on columns 1–4, questions on 5–12 (was 4fr : 8fr with a 64px gap). */
    grid-template-columns: var(--grid-12);
    column-gap: var(--grid-gap);
    align-items: start;
  }
  .faq_header { grid-column: 1 / span 4; position: sticky; top: calc(var(--nav-offset) + var(--nav-height) + var(--space-5)); }
  .faq_list { grid-column: 5 / span 8; }
}
.faq_header { display: grid; justify-items: start; gap: var(--space-2); }
.faq_header .eyebrow { margin-bottom: var(--space-1); }

.faq_list { display: grid; gap: var(--space-1); }

.faq_item {
  border-radius: var(--radius-outer);
  background: var(--color-surface);
  transition: background-color var(--dur-base) var(--ease-soft), box-shadow var(--dur-base) var(--ease-soft);
}
.faq_item.is-open { background: var(--color-white); box-shadow: var(--ring), var(--shadow-1); }

.faq_heading { font-size: inherit; font-weight: inherit; }

.faq_question {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2);
  width: 100%;
  min-height: var(--space-8);
  padding: var(--space-2) var(--space-2) var(--space-2) var(--space-3);
  border: 0;
  border-radius: var(--radius-outer);
  background: transparent;
  color: var(--color-heading);
  font-size: var(--text-body);
  font-weight: var(--weight-medium);
  --lh: 1.4;
  text-align: left;
  cursor: pointer;
}

.faq_icon {
  position: relative;
  width: 36px;
  height: 36px;
  flex: none;
  border-radius: 50%;
  background: var(--color-white);
  box-shadow: var(--ring);
  transition: transform var(--dur-base) var(--ease-out), background-color var(--dur-base) var(--ease-soft);
}
.faq_icon::before,
.faq_icon::after {
  content: "";
  position: absolute;
  top: 50%;
  left: 50%;
  width: 12px;
  height: 1.5px;
  margin: -0.75px 0 0 -6px;
  border-radius: 2px;
  background: var(--color-heading);
  transition: background-color var(--dur-base) var(--ease-soft);
}
.faq_icon::after { transform: rotate(90deg); }

.faq_question:hover .faq_icon,
.faq_question.is-hover .faq_icon { background: var(--color-heading); }
.faq_question:hover .faq_icon::before, .faq_question:hover .faq_icon::after,
.faq_question.is-hover .faq_icon::before, .faq_question.is-hover .faq_icon::after { background: var(--color-white); }

.faq_question[aria-expanded="true"] .faq_icon { transform: rotate(45deg); background: var(--color-heading); }
.faq_question[aria-expanded="true"] .faq_icon::before,
.faq_question[aria-expanded="true"] .faq_icon::after { background: var(--color-white); }

.faq_answer { padding: 0 var(--space-8) var(--space-3) var(--space-3); }
.faq_answer p { max-width: 38rem; }
.faq_answer[hidden] { display: none; }

/* --------------------------------------------------------------------------
   16. FOOTER (Relume Footer, minimal)
   -------------------------------------------------------------------------- */
.footer_inner { padding-block: var(--space-8); }
@media (min-width: 64em) { .footer_inner { padding-block: var(--space-10); } }

.footer_top { display: grid; justify-items: start; gap: var(--space-3); }
@media (min-width: 48em) {
  .footer_top { grid-template-columns: minmax(0, 1fr) auto; align-items: center; }
}
.footer_name { color: var(--color-heading); font-size: 1.5rem; font-weight: 600; letter-spacing: -0.02em; }
.footer_line { margin-top: var(--space-0-5); text-wrap: balance; }

.footer_links {
  display: flex;
  flex-wrap: wrap;
  gap: 0 var(--space-3);
  margin-top: var(--space-4);
}
.footer_link {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  min-width: 44px;
  color: var(--color-heading);
  font-size: var(--text-small);
  font-weight: var(--weight-medium);
  text-decoration: underline;
  text-decoration-color: var(--color-hairline-strong);
  text-decoration-thickness: 1px;
  text-underline-offset: 4px;
  border-radius: 4px;
  transition: text-decoration-color var(--dur-fast) var(--ease-soft);
}
.footer_link:hover, .footer_link.is-hover { text-decoration-color: currentColor; text-decoration-thickness: 2px; }

.footer_legal {
  margin-top: var(--space-4);
  padding-top: var(--space-3);
  box-shadow: inset 0 1px 0 var(--color-hairline);
  font-size: var(--text-xs);
}

/* --------------------------------------------------------------------------
   17. MOTION: scroll reveal + filter entry (off for prefers-reduced-motion)
   -------------------------------------------------------------------------- */
@media (prefers-reduced-motion: no-preference) {
  .js .reveal {
    opacity: 0;
    transform: translateY(var(--space-3));
    transition:
      opacity var(--dur-reveal) var(--ease-out),
      transform var(--dur-reveal) var(--ease-out);
    transition-delay: calc(var(--i, 0) * 80ms);
  }
  .js .reveal.is-in { opacity: 1; transform: none; }

  /* Portfolio card keeps its own hover transition once revealed */
  .js .portfolio_card.reveal.is-in {
    transition:
      opacity var(--dur-reveal) var(--ease-out),
      transform var(--dur-slow) var(--ease-out),
      box-shadow var(--dur-slow) var(--ease-soft);
  }

  .portfolio_item.is-entering { animation: card-in var(--dur-slow) var(--ease-out) both; animation-delay: calc(var(--i, 0) * 60ms); }
  @keyframes card-in {
    from { opacity: 0; transform: translateY(var(--space-2)) scale(0.98); }
    to { opacity: 1; transform: none; }
  }

  .faq_answer:not([hidden]) { animation: faq-in var(--dur-base) var(--ease-out); }
  @keyframes faq-in {
    from { opacity: 0; transform: translateY(-4px); }
    to { opacity: 1; transform: none; }
  }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    transition-delay: 0ms !important;
  }
  .status-pill_dot::after { animation: none; opacity: 0; }
}

/* --------------------------------------------------------------------------
   18. SPEC MODE (index.html?spec) — handoff overlay, never shipped visible
   -------------------------------------------------------------------------- */
.spec-on [data-spec] { position: relative; outline: 1px dashed rgb(26 115 232 / 0.6); outline-offset: -1px; }
.spec-on .navbar_component[data-spec] { position: sticky; }
.spec-on [data-spec]::before {
  content: attr(data-spec);
  position: absolute;
  top: 0;
  left: 0;
  z-index: 5;
  max-width: 100%;
  padding: 2px 8px;
  border-radius: 0 0 6px 0;
  background: var(--color-primary);
  color: var(--color-white);
  font: 500 11px/1.5 ui-monospace, SFMono-Regular, Menlo, monospace;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  pointer-events: none;
}
.spec-on .navbar_component[data-spec]::before { left: auto; right: 0; border-radius: 0 0 0 6px; }
.spec-on .padding-section-large,
.spec-on .padding-section-hero,
.spec-on .padding-section-small {
  box-shadow:
    inset 0 var(--pad-top) 0 rgb(26 115 232 / 0.07),
    inset 0 calc(var(--pad-bottom) * -1) 0 rgb(26 115 232 / 0.07);
}
.spec-on .container-large { outline: 1px solid rgb(26 115 232 / 0.25); }
.spec-on .placeholder,
.spec-on [data-todo] { outline: 2px solid var(--color-primary); outline-offset: 1px; }

.spec-legend {
  position: fixed;
  right: var(--space-2);
  bottom: var(--space-2);
  z-index: 35;
  display: grid;
  gap: var(--space-1);
  max-width: 22rem;
  padding: var(--space-2);
  border-radius: var(--radius-inner);
  background: var(--color-heading);
  color: var(--color-white);
  font-size: 0.8125rem;
  --lh: 1.45;
  box-shadow: var(--shadow-2);
}
.spec-legend strong { font-weight: 600; }
.spec-legend a { color: var(--color-white); }
