/* ==========================================================================
   victoriaenglert.com — clean rebuild of the Framer site (v3 brand system)
   Reusable as a template: all design decisions live in the tokens below.

   Values measured off the live site. Easing curves extracted from the
   Framer Motion bundle so hover/reveal timing matches the original.
   ========================================================================== */

/* ---- Webfonts ---- */
@font-face {
  font-family: "Inter";
  src: url("assets/fonts/InterVariable.woff2") format("woff2");
  font-weight: 100 900; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "Inter";
  src: url("assets/fonts/InterVariable-Italic.woff2") format("woff2");
  font-weight: 100 900; font-style: italic; font-display: swap;
}
@font-face {
  font-family: "Libre Caslon Condensed";
  src: url("assets/fonts/LibreCaslonCondensed-400.woff2") format("woff2");
  font-weight: 400; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "Libre Caslon Condensed";
  src: url("assets/fonts/LibreCaslonCondensed-500.woff2") format("woff2");
  font-weight: 500; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "Libre Caslon Condensed";
  src: url("assets/fonts/LibreCaslonCondensed-600.woff2") format("woff2");
  font-weight: 600; font-style: normal; font-display: swap;
}

/* ---- Design tokens ---- */
:root {
  /* grounds */
  --mist: #F5F6FA;
  --white: #FFFFFF;
  --ink: #2D333B;          /* Grey-700 — primary text + dark surfaces */
  --muted: #52575E;        /* Grey-600 — body copy */
  --faint: #6E7277;        /* Grey-500 — dates, tertiary labels */
  --hairline: #DFDFE1;

  /* accents */
  --periwinkle: #BFCEEB;   /* skills line on dark cards, gradient top */
  --periwinkle-dark: #2E3F66;  /* competency card on hover */
  --link: #306CBA;         /* Blue-600 — the only interactive colour */
  --accent-pill: #FFCBFA;  /* pink pill behind the Contact nav link */

  /* Further steps of the SAME v3 ramps the template already pulls from — the
     palette is unchanged, these are just the neighbouring stops (brand-tokens.md).
     Pink is only ever used on white. */
  --periwinkle-100: #D7DFF2;
  --pink-50:  #FEF2FC;
  --pink-line: #BB308E;    /* Pink-600 — rules, markers, small marks */
  --pink-text: #97006C;    /* Pink-700 — the only pink that carries text */
  --blue-700: #0047A4;

  /* Elevation E2/E3 from the brand system (the template only ever needed E1). */
  --shadow-e2: 0 6px 16px rgba(45, 51, 59, .10);
  --shadow-e3: 0 16px 40px rgba(45, 51, 59, .12);

  /* type */
  --font-display: "Libre Caslon Condensed", "Libre Caslon Text", Georgia, serif;
  --font-body: "Inter", -apple-system, "Segoe UI", Helvetica, Arial, sans-serif;

  --h1-size: 47.808px;
  --h2-size: 33.184px;
  --h3-size: 24.8px;
  --body-size: 16px;
  --small-size: 13.328px;

  /* shape */
  --radius-card: 32px;
  --radius-card-sm: 24px;
  --radius-photo: 48px;
  --radius-header: 28px;
  --radius-pill: 100px;

  /* elevation — the original uses these two only */
  --shadow-card: 0 2px 6px rgba(45, 51, 59, .06);
  --shadow-card-featured: 0 2px 6px rgba(45, 51, 59, .34);
  --shadow-btn: 0 2px 6px rgba(45, 51, 59, .06), 0 1px 2px rgba(45, 51, 59, .04);
  --shadow-btn-hover: 5px 5px 8px 0 rgba(45, 51, 59, .1);   /* from the hover variants */
  /* Framer paints hairlines with an inset overlay, so they never affect layout.
     An inset box-shadow does the same job in plain CSS. */
  --hairline-box: inset 0 0 0 1px var(--hairline);
  --hairline-top: inset 0 1px 0 var(--hairline);
  --hairline-bottom: inset 0 -1px 0 var(--hairline);

  /* motion — curves lifted from the site's Framer Motion bundle */
  --ease-color: cubic-bezier(.44, 0, .56, 1);   /* link/nav colour, 0.4s */
  --ease-hover: cubic-bezier(.27, 0, .51, 1);   /* hover transforms, 0.2s */
  --ease-reveal: cubic-bezier(.35, 0, 0, 1);    /* header variant, 0.6s */
  --dur-color: .4s;
  --dur-hover: .2s;
  --dur-reveal: .6s;
  /* Button hover is a physics spring in Framer: stiffness 500, damping 60, mass 1,
     delay 0. That is overdamped (zeta 1.34), so it settles without bounce in ~0.5s.
     The bezier below is fitted to that step response. */
  --dur-spring: .5s;
  --ease-spring: cubic-bezier(.25, .6, .35, 1);
  /* Competency-card hover: a tween, not a spring — duration .2s, ease [.2,0,0,1]. */
  --dur-card: .2s;
  --ease-card: cubic-bezier(.2, 0, 0, 1);
  /* Nav Contact pill hover: tween .2s, ease-in cubic-bezier(.5, 0, .88, .77). */
  --dur-nav: .2s;
  --ease-nav: cubic-bezier(.5, 0, .88, .77);

  /* layout */
  --content-max: 1120px;
  --page-gutter: 24px;      /* outer gutter, outside the page wrapper */
  --header-max: 1250px;
  /* The footer is full width; the 1095px an earlier pass recorded was its scaled
     resting state, not its real size. */
  --header-top: 29px;
  --header-h: 60px;
  --section-gap: 150px;
}

/* ---- Reset / base ---- */
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }

html { scroll-behavior: smooth; scroll-padding-top: 120px; }

body {
  font-family: var(--font-body);
  font-size: var(--body-size);
  line-height: 1.45;
  letter-spacing: -0.02em;
  color: var(--muted);
  background-color: var(--mist);
  padding: 0 var(--page-gutter);
  /* signature gradient: periwinkle melting into mist behind the hero.
     Percentage stop (not a pixel stop) so it scales with page length — matches the
     original, and keeps working when this template is reused on other pages. */
  background-image: linear-gradient(180deg, var(--periwinkle) 0%, var(--mist) 9.134%);
  background-repeat: no-repeat;
  -webkit-font-smoothing: antialiased;
  /* The problem cards tilt in 3D as they pass through the viewport, and
     perspective projection makes a tilted card render wider than its box —
     1449px against a 1062px flat card at 1280, which would put a horizontal
     scrollbar on the page for the whole animation. The reference solves this
     with `overflow: hidden` on its section; `clip` is the better tool, because
     `hidden` would make the element a scroll container and the card's view()
     timeline would then resolve against it instead of the document. */
  overflow-x: clip;
}

img { display: block; max-width: 100%; }

h1, h2, h3 { font-family: var(--font-display); color: var(--ink); letter-spacing: 0; }
h1 { font-size: var(--h1-size); font-weight: 400; line-height: 1.4; }
h2 { font-size: var(--h2-size); font-weight: 500; line-height: 1.2; }
h3 { font-size: var(--h3-size); font-weight: 600; line-height: 1.15; }

p + p { margin-top: 1.25em; }   /* 20px at the 16px body size, as on the original */

/* Three section headings are centred on the original; the rest are left-aligned. */
h2.is-centered { text-align: center; }

a { color: var(--link); text-decoration: none; transition: color var(--dur-color) var(--ease-color); }
.prose a:hover { text-decoration: underline; }

/* ==========================================================================
   Header — fixed white pill with a hairline border
   ========================================================================== */
.site-header {
  position: fixed;
  top: var(--header-top);
  left: 0;
  right: 0;
  z-index: 10;
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: 0 25px;
  pointer-events: none;               /* let the gutter pass clicks through */
}
.site-header > * { pointer-events: auto; }

.site-header__pill {
  width: 100%;
  max-width: var(--header-max);
  height: var(--header-h);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  padding: 0 20px;
  background: var(--white);
  border-radius: var(--radius-header);
  box-shadow: var(--hairline-box);
}

.site-header__brand {
  font-family: var(--font-display);
  font-size: 32px;
  font-weight: 400;
  line-height: 1.2;
  letter-spacing: normal;
  color: var(--ink);
  white-space: nowrap;
}
.site-header__brand:hover { color: var(--link); }

.site-nav { display: flex; align-items: center; }
.site-nav a {
  padding: 5px 17.5px;
  font-size: 16px;
  font-weight: 400;
  color: var(--ink);
}
/* The original's plain nav links have a hover variant, but it only restates their
   resting values - no visible change.

   DELIBERATE ADDITION (not in the original): the other nav items go bold on hover,
   using the Contact pill's easing. Inter is loaded as a variable font here, so the
   weight axis interpolates rather than snapping.

   The ::after carries a hidden bold copy of the label, which holds each link at its
   widest width so the row cannot shift as you move across it. `data-label` is filled
   in by main.js from the link's own text, so there is nothing to keep in sync. */
.site-nav a:not(.nav-cta) {
  display: inline-flex;
  flex-direction: column;
  align-items: center;
  font-variation-settings: "wght" 400;
  transition: font-variation-settings var(--dur-nav) var(--ease-nav),
              color var(--dur-nav) var(--ease-nav);
}
.site-nav a:not(.nav-cta)::after {
  content: attr(data-label);
  height: 0;
  overflow: hidden;
  visibility: hidden;
  pointer-events: none;
  font-variation-settings: "wght" 700;
}
.site-nav a:not(.nav-cta):hover { font-variation-settings: "wght" 700; }

.site-nav a.is-active { color: var(--link); }

/* The Contact link sits in a pink pill on the original, with its own small shadow.
   On hover the pill inverts: fill goes to ink, label to white. Nothing else moves -
   the radius and shadow stay put, unlike the other nav variants. */
.site-nav a.nav-cta {
  background: var(--accent-pill);
  border-radius: 20px;
  box-shadow: 0 1px 2px 0 rgba(45, 51, 59, .25);
  transition: background-color var(--dur-nav) var(--ease-nav),
              color var(--dur-nav) var(--ease-nav);
}
.site-nav a.nav-cta:hover {
  background: var(--ink);
  color: var(--white);
}

/* hamburger */
.nav-toggle {
  display: none;
  flex-direction: column;
  justify-content: center;
  gap: 5px;
  width: 40px;
  height: 40px;
  padding: 0 8px;
  background: none;
  border: 0;
  cursor: pointer;
}
.nav-toggle span {
  display: block;
  height: 1.5px;
  background: var(--ink);
  border-radius: 2px;
  transition: transform var(--dur-hover) var(--ease-hover), opacity var(--dur-hover) var(--ease-hover);
}
.nav-toggle[aria-expanded="true"] span:nth-child(1) { transform: translateY(6.5px) rotate(45deg); }
.nav-toggle[aria-expanded="true"] span:nth-child(2) { opacity: 0; }
.nav-toggle[aria-expanded="true"] span:nth-child(3) { transform: translateY(-6.5px) rotate(-45deg); }

/* mobile panel */
.mobile-nav {
  display: none;
  flex-direction: column;
  width: 100%;
  max-width: var(--header-max);
  margin-top: 8px;
  padding: 8px;
  background: var(--white);
  border: 1px solid var(--hairline);
  border-radius: var(--radius-header);
  overflow: hidden;
  max-height: 0;
  opacity: 0;
  transform: translateY(-8px);
  transition: max-height var(--dur-reveal) var(--ease-reveal),
              opacity var(--dur-hover) var(--ease-hover),
              transform var(--dur-hover) var(--ease-hover);
}
.mobile-nav.is-open { max-height: 340px; opacity: 1; transform: translateY(0); }
.mobile-nav a { padding: 12px 16px; font-size: 16px; color: var(--ink); border-radius: 16px; }
.mobile-nav a:hover { background: var(--mist); }

/* ==========================================================================
   Page frame
   ========================================================================== */
.page {
  max-width: calc(var(--content-max) + 160px);
  margin: 0 auto;
  padding: 160px 80px 124px;
  display: flex;
  flex-direction: column;
  gap: var(--section-gap);
}

/* ==========================================================================
   Buttons — the four variants of the Framer `Button` component
     .btn--primary-light   ink pill, white label      (Framer: "Primary Light")
     .btn--primary-dark    pink pill, ink label       (Framer: "Primary Dark")
     .btn--secondary       white pill, ink label      (Framer: "Secondary")
     .link-tertiary        no pill, blue label        (Framer: "Tertiary")

   Shared by all four: 16px/700, -0.02em, line-height 1, 8px gap to a 16px arrow.
   The three pill variants are 54px tall and share one resting shadow.

   The pill you see is 144x44; it sits top-left inside a 154x54 layout box, and the
   10px of slack on the right and bottom is reserved so the hover shadow does not
   shift anything.

   Hover, on all four variants: gap 8 -> 16 and padding-right -8, so the arrow slides
   outward and the box width does not change. The three pill variants also deepen the
   shadow to `5px 5px 8px rgba(45,51,59,.1)`. Animated with a spring — stiffness 500,
   damping 60, mass 1.
   ========================================================================== */
.btn {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 14px 34px 14px 26px;   /* the visible pill is 144x44 */
  margin: 0 10px 10px 0;          /* reserved room so the hover shadow shifts nothing */
  border: 0;
  border-radius: var(--radius-pill);
  font-family: var(--font-body);
  font-size: 16px;
  font-weight: 700;
  letter-spacing: -0.02em;
  line-height: 1;
  cursor: pointer;
  box-shadow: var(--shadow-btn);
  transition: gap var(--dur-spring) var(--ease-spring),
              padding-right var(--dur-spring) var(--ease-spring),
              box-shadow var(--dur-spring) var(--ease-spring);
}
/* Hover: the gap grows by 8 and the right padding shrinks by 8, so the arrow slides
   outward while the pill's width stays put. Plus the deeper shadow. */
.btn:hover {
  gap: 16px;
  padding-right: 26px;
  box-shadow: var(--shadow-btn-hover);
}

.btn--primary-light { background: var(--ink); color: var(--white); }
.btn--primary-dark  { background: var(--accent-pill); color: var(--ink); }
.btn--secondary     { background: var(--white); color: var(--ink);
                      box-shadow: var(--shadow-btn), var(--hairline-box); }
.btn--secondary:hover { box-shadow: var(--shadow-btn-hover), var(--hairline-box); }

/* Arrow icon — from the original's SVG sprite (24x24 box, 2px round stroke),
   rendered at 16px and inheriting the label colour. */
.arrow { width: 16px; height: 16px; flex: none; display: block; color: currentColor; }

/* Tertiary: bold blue label, no pill, no shadow, no hover change. The 9px of
   trailing space is in the original's component box too. */
/* Tertiary uses the same trick with no pill: gap 8 -> 16, padding-right 8 -> 0. */
.link-tertiary {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding-right: 8px;
  font-size: 16px;
  font-weight: 700;
  line-height: 1;
  color: var(--link);
  transition: gap var(--dur-spring) var(--ease-spring),
              padding-right var(--dur-spring) var(--ease-spring);
}
.link-tertiary:hover { gap: 16px; padding-right: 0; }

/* The form's submit is not one of the four — it is its own smaller control:
   full width of the form column, 36px tall, 25px radius, 14px/600, no arrow. */
.btn-submit {
  display: block;
  width: 100%;
  height: 36px;
  border: 0;
  border-radius: 25px;
  background: var(--white);
  color: var(--ink);
  font-family: var(--font-body);
  font-size: 14px;
  font-weight: 600;
  line-height: 1.2;
  cursor: pointer;
  transition: box-shadow var(--dur-hover) var(--ease-hover);
}
.btn-submit:hover { box-shadow: var(--shadow-btn-hover); }

/* ---- Hero ---- */
/* The hero row is a fixed 1072px on desktop — wider than the 1057px content column at
   1280px, narrower than it above ~1420px. The calc keeps it centred either way.
   The section is a fixed 600px tall and the photo is bound to that height. */
.hero {
  display: flex;
  flex-direction: row;
  gap: 48px;
  align-items: center;
  width: 1072px;
  margin-inline: calc((100% - 1072px) / 2);
  height: 600px;
}
.hero__text { flex: 1 0 0; display: flex; flex-direction: column; gap: 37px; align-items: stretch; }
.hero__descriptor { font-size: 20.8px; color: var(--ink); line-height: 1.3; letter-spacing: normal; margin-top: -14px; }
.hero__intro { /* no max-width: the original runs to the full text column */ }
.hero__actions { display: flex; gap: 10px; flex-wrap: wrap; align-self: flex-start; }
/* Use a portrait 4:5 crop on the right-hand side of the hero. */
.hero__photo {
  flex: 0 0 580px;
  align-self: stretch;
  max-width: 100%;
}
.hero__photo img {
  width: 100%;
  height: 100%;
  aspect-ratio: 4 / 5;
  object-fit: cover;
  object-position: 53.2% 32.1%;   /* focal point */
  border-radius: var(--radius-photo);
}

/* ---- Generic section ---- */
.section { display: flex; flex-direction: column; gap: 20px; }
.section--wide-gap { gap: 40px; }
/* Measured trailing space inside these two sections on the original (106px and 20px
   below the last painted element, vs 32px and 0px from the content's own padding). */
.section--pad-74 { padding-bottom: 74px; }
.section--pad-20 { padding-bottom: 20px; }

/* ---- White card sections (Story, Philosophy, Outside) — no shadow ---- */
.card-section {
  background: var(--white);
  border-radius: var(--radius-card);
  box-shadow: var(--hairline-box);
  padding: 80px;
  display: flex;
  flex-direction: column;
  gap: 40px;
}
.card-section--narrow-pad { padding: 80px 40px; }
.card-section .prose { /* no max-width on the original */ }
.card-section .prose h3 { margin-bottom: 12px; }

/* ---- Core competencies ---- */
.competencies {
  display: grid;
  grid-template-columns: 1fr 1fr;
  column-gap: 25px;
  row-gap: 40px;
  align-items: start;
}
.comp-card {
  background: var(--ink);
  border-radius: var(--radius-card-sm);
  padding: 45px 32px 32px;
  box-shadow: var(--shadow-card);
  display: flex;
  flex-direction: column;
  height: 100%;
}
/* The template's reveal co-selectors are gone with the reveal itself, so this is a
   single plain rule. The hover spec below is the original's, measured. */
.comp-card {
  transition: transform var(--dur-card) var(--ease-card),
              background-color var(--dur-card) var(--ease-card),
              box-shadow var(--dur-card) var(--ease-card);
}
.comp-card--featured { box-shadow: var(--shadow-card-featured); }

/* Hover, from the component's `whileHover`: fill goes to Periwinkle Dark, the card
   lifts 1px and scales to 1.02, and the shadow becomes 0 6px 16px rgba(45,51,59,.1).
   Applies to all four cards, including the featured one, whose resting shadow it
   replaces. */
.comp-card:hover {
  background: var(--periwinkle-dark);
  box-shadow: 0 6px 16px 0 rgba(45, 51, 59, .1);
  transform: scale(1.02) translateY(-1px);
}
.comp-card h3 { color: var(--white); margin-bottom: 16px; }
.comp-card p { color: var(--white); }
/* `.comp-card p` was out-specifying `.comp-card__skills`, which is why this line
   rendered white instead of periwinkle. */
.comp-card p.comp-card__skills {
  margin-top: auto;
  padding-top: 40px;
  font-size: var(--small-size);
  line-height: 1.2;
  letter-spacing: -0.01em;
  color: var(--periwinkle);
}

/* ---- Projects / Media split layout ---- */
.split { display: flex; gap: 48px; align-items: stretch; }
/* The original sizes this column from the text beside it and crops the photo to fit.
   The image is taken out of flow so it cannot drive the row height itself. */
.split__media { flex: 0 0 480px; position: relative; }
.split__media img {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  object-fit: cover;
  border-radius: var(--radius-card);
}
.split__media--small { flex: 0 0 300px; }
.split__media--small img { border-radius: var(--radius-card-sm); }
.split__list { flex: 1; display: flex; flex-direction: column; }
.project { padding: 20px 0; display: flex; flex-direction: column; gap: 10px; align-items: stretch; }
#projects .project { box-shadow: var(--hairline-top); }
.project .link-tertiary, .project .btn, .project__links { align-self: flex-start; }
.project__links { display: flex; flex-direction: column; gap: 10px; }

/* ---- Contact ---- */
.contact { gap: 20px; }
.contact .prose { /* no max-width on the original */ }

/* ---- Footer ---- */
.site-footer {
  background: var(--ink);
  border-radius: 22px 22px 0 0;
  padding: 61px 80px;
  color: var(--mist);
}

/* Footer entrance. Restored 2026-08-23 — taken from the original's own inline style,
   which reads `opacity: 0.5; transform: translateY(52px) scale(0.9)` while the footer is
   out of view. It grows to full size on entering the viewport and retracts on leaving,
   so it is reversible rather than one-shot.
   Spring: stiffness 500, damping 60, mass 1 — the same overdamped curve as the buttons.
   Gated behind .js-inview so the footer sits at full size when JS is off. */
.js-inview .site-footer {
  opacity: .5;
  transform: translateY(52px) scale(.9);
  will-change: transform;
  transition: opacity var(--dur-spring) var(--ease-spring),
              transform var(--dur-spring) var(--ease-spring);
}
.js-inview .site-footer.is-inview { opacity: 1; transform: none; }
.site-footer__inner {
  display: flex;
  gap: 0;
  flex-wrap: wrap;
}
.site-footer h4 {
  font-family: var(--font-body);
  font-size: 18.4px;
  /* The original is Aspekta with `font-variation-settings: "wght" 800`. Inter is
     loaded as a variable font here, so the same axis value carries over. */
  font-weight: 800;
  font-variation-settings: "wght" 800;
  line-height: 1.3;
  letter-spacing: normal;
  color: var(--mist);
  margin-bottom: 20px;
}
.footer-links { list-style: none; display: flex; flex-direction: column; gap: 12px; }
.footer-links a {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  color: var(--white);
  font-size: 16px;
}
/* The footer uses a different arrow from the buttons: a filled north-east arrow at
   14.4px, not the 16px stroked one. */
.arrow-ne { width: 14.4px; height: 14.4px; flex: none; display: block; fill: currentColor; }
.footer-links a:hover { color: var(--periwinkle); }
.footer-col--links { flex: 1 0 0; }

.site-footer .copyright {
  margin: 48px 0 0;
  font-size: 16px;
  color: var(--mist);
}

/* ==========================================================================
   Responsive — breakpoints mirror the original (1280 / 768)
   ========================================================================== */
/* ==========================================================================
   Breakpoints — the three Framer defines:
     Desktop  1280px and up   (the base rules above)
     Tablet   768–1279px
     Mobile   below 768px
   Hero values below are measured off the real app at 1024px and 390px.
   ========================================================================== */

@media (max-width: 1279.98px) {
  /* the original drops body copy to 14.4px below desktop (line-height stays 1.45) */
  :root { --h1-size: 42.224px; --body-size: 14.4px; --small-size: 12px; }
  .page { padding: 109px 29px 90px; gap: 110px; }
  .site-header__brand { font-size: 26px; }
  .site-nav a { padding: 5px 12px; font-size: 15px; }
  /* tablet: 420px tall, 29px gap, photo/text 280 : 594 */
  .hero { width: auto; margin-inline: 0; height: 420px; gap: 29px; }
  .hero__photo { flex: 0.471 0 0; }
  .hero__text { gap: 17px; }
  .hero h1 { line-height: 1; }
  .hero__descriptor { font-size: 18.4px; margin-top: 0; }
  .card-section { padding: 56px 40px; }
  .split__media { flex: 0 0 380px; }
}

@media (max-width: 767.98px) {
  :root {
    --h1-size: 38.256px;
    --h2-size: 26.544px;
    --h3-size: 21.6px;
    --section-gap: 50px;
    --small-size: 10.672px;
    --radius-photo: 32px;
    --page-gutter: 0px;     /* the 24px inset moves onto .page at this width */
  }
  /* Keep the first heading below the fixed header: 29px top offset + 60px
     header height + 24px breathing room. Deriving this from the shared header
     variables prevents the two elements from drifting back into overlap. */
  .page {
    padding: calc(var(--header-top) + var(--header-h) + 24px) 24px 64px;
    gap: var(--section-gap);
  }

  /* Buttons and tertiary links drop to 12.8px at this width. */
  .btn, .link-tertiary { font-size: 12.8px; }

  .site-nav { display: none; }
  .nav-toggle { display: flex; }
  .mobile-nav { display: flex; }
  .site-header { padding: 0 12px; }
  .site-header__pill { padding: 0 8px 0 16px; }
  .site-header__brand { font-size: 22px; }

  /* mobile: stacked, photo first, section height driven by content.
     Measured on the real app at 390px: row gap 26px, text-column gap 21px,
     buttons stacked and centred with a 10px gap. */
  .hero { flex-direction: column; align-items: stretch; width: auto; margin-inline: 0; height: auto; gap: 26px; }
  .hero__photo { flex: none; align-self: stretch; }
  .hero__photo img { height: auto; aspect-ratio: 327 / 382; }
  .hero__text { gap: 21px; }
  .hero h1 { line-height: 1; }
  .hero__descriptor { font-size: 17.6px; margin-top: 0; }
  .hero__actions { flex-direction: column; align-items: center; align-self: stretch; gap: 10px; flex-wrap: nowrap; }
  .hero__actions .btn { align-self: center; }

  /* Competency cards at mobile: tighter padding, 20px between cards, and the
     skills line loses its 40px spacer and drops to 11.2px. */
  .competencies { grid-template-columns: 1fr; gap: 20px; }
  .comp-card { padding: 32px 24px; }
  .comp-card p.comp-card__skills { padding-top: 0; font-size: 11.2px; }
  .split { flex-direction: column; }
  .split__media, .split__media--small { flex: none; align-self: stretch; position: static; }
  .split__media img { position: static; width: 100%; height: auto; aspect-ratio: 327 / 360; object-fit: cover; }
  .split__media--small img { aspect-ratio: auto; }

  .card-section, .card-section--narrow-pad { padding: 40px 16px; border-radius: 24px; gap: 30px; }
  .site-footer { padding: 40px 24px; }
  .site-footer__inner { gap: 40px; }
  /* The copyright drops to 12.48px and turns pure white at this width. */
  .site-footer .copyright { font-size: 12.48px; line-height: 1.2; color: var(--white); }
}

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  * { transition-duration: .01ms !important; animation-duration: .01ms !important; }
}


/* ==========================================================================
   SYSTEMIQUE COMPONENT LAYER
   Everything above this line is the victoriaenglert.com template, unchanged
   apart from the removals recorded in README.md. Everything below is new, and
   is built only from the tokens above.

   MOTION POLICY: there is no scroll-triggered animation anywhere on this page.
   Motion is limited to (a) one hero entrance that runs on load, and (b) direct
   responses to a click, hover or focus. All of it sits inside the brand's
   300-600ms gentle-ease-out envelope with no bounce, and all of it is switched
   off under prefers-reduced-motion.
   ========================================================================== */

/* ---- Shared section furniture ---- */
/* ---- Section labels ----
   Restored 2026-08-23: without them it was not obvious which section you were
   in. `brand-tokens.md` specifies this treatment directly — "Eyebrows/labels:
   Inter, uppercase, weight 600, letter-spacing +0.14em" — so it is the brand's
   own label style rather than a decorative kicker.

   The wording matches the nav exactly, so clicking a nav item lands you on a
   section whose label repeats the word you clicked. Grey-600 rather than
   Grey-500: at 11px this is small text, and Muted clears 7:1 on Mist where
   Faint only just clears 4.5:1. */
.eyebrow {
  font-family: var(--font-body);
  font-size: 11px;
  font-weight: 600;
  line-height: 1.2;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--muted);
}
/* A label belongs to the heading beneath it, so it should sit closer than the
   heading sits to its own lede. Each container has a different gap, so each is
   pulled back to a consistent ~10px. */
.section-head .eyebrow        { margin-bottom: -4px; }
.newsletter__text .eyebrow    { margin-bottom: -4px; }
.contact-block__intro .eyebrow{ margin-bottom: -8px; }
#why-me .split__list .eyebrow { margin-bottom: -12px; }
/* On the ink newsletter band the label flips to periwinkle for contrast. It also
   has to restate size and tracking: `.newsletter__text p` is (0,1,1) and the
   label is a <p>, so it out-specifies the (0,1,0) `.eyebrow` rule and was
   rendering the label at 17px body size. */
.newsletter__text .eyebrow {
  color: var(--periwinkle);
  font-size: 11px;
  letter-spacing: .14em;
  line-height: 1.2;
}

.section-head { display: flex; flex-direction: column; gap: 14px; }
/* The lede is used inside .section-head and standalone (Why me), so it is
   scoped to the class rather than the parent. */
.lede { font-size: 18px; line-height: 1.6; color: var(--muted); letter-spacing: -0.01em; }
.section-head.is-centered { align-self: center; text-align: center; align-items: center; }

/* Copy inside the new components runs at the brand's 1.6 line-height, looser
   than the template's 1.45, because these blocks are read rather than scanned. */
.prose-relaxed { line-height: 1.6; }
.prose-relaxed p + p { margin-top: 1em; }

/* The one list marker used across the page: a small Pink-600 dot. */
.bullets { list-style: none; display: flex; flex-direction: column; gap: 11px; }
.bullets li { position: relative; padding-left: 22px; line-height: 1.55; color: var(--muted); }
.bullets li::before {
  content: "";
  position: absolute;
  left: 0; top: .55em;
  width: 6px; height: 6px;
  border-radius: var(--radius-pill);
  background: var(--pink-line);
}

/* ---- Hero ---- */
/* Reuses .hero from the template (photo left, text right, same gaps and
   heights). Only the eyebrow and tagline elements below are new. */
.hero h1 { line-height: 1.05; }
.hero__tagline {
  font-family: var(--font-display);
  font-size: 26px;
  font-weight: 400;
  line-height: 1.25;
  letter-spacing: 0;
  color: var(--ink);
}
.hero .hero__intro p { font-size: 17px; line-height: 1.6; }

/* Hero entrance — the page's only automatic motion. Runs once on load, not on
   scroll. `both` fill keeps the pre-animation state applied before it starts. */
@keyframes riseIn {
  from { opacity: 0; transform: translateY(18px); }
  to   { opacity: 1; transform: none; }
}
.hero__photo,
.hero__text > * {
  animation: riseIn var(--dur-reveal) var(--ease-reveal) both;
}
.hero__photo                 { animation-delay: 0ms; }
.hero__text > *:nth-child(1) { animation-delay: 80ms; }
.hero__text > *:nth-child(2) { animation-delay: 150ms; }
.hero__text > *:nth-child(3) { animation-delay: 220ms; }
.hero__text > *:nth-child(4) { animation-delay: 290ms; }

/* ---- Tabs (How I can help) ---- */
.tabs { display: flex; flex-direction: column; gap: 36px; }
.tabs__scroller {
  overflow-x: auto;
  scrollbar-width: none;
  -ms-overflow-style: none;
  padding: 2px 2px 8px;
  margin: -2px -2px -8px;
}
.tabs__scroller::-webkit-scrollbar { display: none; }
.tabs__list {
  position: relative;
  display: inline-flex;
  gap: 4px;
  padding: 5px;
  background: var(--white);
  border-radius: var(--radius-pill);
  box-shadow: var(--shadow-card), var(--hairline-box);
}
/* The sliding pill behind the selected tab. JS sets its width and translateX.
   If the JS never runs the marker stays 0-wide and the selected tab still reads
   as selected through its own colour, so nothing breaks. */
.tabs__marker {
  position: absolute;
  top: 5px; left: 0;
  height: calc(100% - 10px);
  width: 0;
  border-radius: var(--radius-pill);
  background: var(--ink);
  /* This animates `width`, which a generic lint flags as layout thrash. Kept
     deliberately: the marker is `position: absolute` with no children, so it is
     out of flow and resizing it reflows nothing else. The transform-only
     alternative (fixed width + scaleX) stretches the 20px corner radius into a
     visible ellipse, which is a worse result than the cost it avoids. */
  transition: transform .42s var(--ease-reveal), width .42s var(--ease-reveal);
  pointer-events: none;
}
.tabs__btn {
  position: relative;
  z-index: 1;
  border: 0;
  background: none;
  cursor: pointer;
  padding: 11px 22px;
  border-radius: var(--radius-pill);
  font-family: var(--font-body);
  font-size: 15px;
  font-weight: 600;
  letter-spacing: -0.01em;
  line-height: 1;
  white-space: nowrap;
  color: var(--muted);
  transition: color var(--dur-color) var(--ease-color);
}
.tabs__btn:hover { color: var(--ink); }
.tabs__btn[aria-selected="true"],
.tabs__btn[aria-selected="true"]:hover { color: var(--white); }
/* The chevron only exists for the mobile accordion layout below — invisible
   here so it does not intrude on the pill bar's own text-only buttons. */
.tabs__chevron { display: none; }

.tabs__panel[hidden] { display: none; }
@keyframes panelIn {
  from { opacity: 0; transform: translateY(8px); }
  to   { opacity: 1; transform: none; }
}
.tabs__panel { animation: panelIn .4s var(--ease-reveal) both; }

/* The closing "the aim is..." line, set apart by a pink rule rather than a box.
   .help-panel__body's column is often taller than its own content (it stretches
   to match .artifact's height), which used to leave this packed right under the
   bullets with dead space below it. Equal auto margins above and below centre
   it in whatever space is left after the bullet list instead, so it reads as a
   deliberate closing note rather than a leftover line — more breathing room,
   and more visual weight now that it isn't hugging the list above it. */
.help-panel__aim {
  margin-top: auto;
  margin-bottom: auto;
  padding-left: 20px;
  box-shadow: inset 2px 0 0 var(--pink-line);
  font-style: italic;
  line-height: 1.6;
  color: var(--ink);
}

/* ---- Accordion (Why me, FAQs) ---- */
.acc { display: flex; flex-direction: column; }
.acc__item { box-shadow: var(--hairline-top); }
.acc__item:last-child { box-shadow: var(--hairline-top), var(--hairline-bottom); }
.acc__btn {
  width: 100%;
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 28px;
  padding: 26px 4px;
  border: 0;
  background: none;
  cursor: pointer;
  text-align: left;
  font-family: var(--font-display);
  font-size: 21px;
  font-weight: 500;
  line-height: 1.3;
  letter-spacing: 0;
  color: var(--ink);
  transition: color var(--dur-color) var(--ease-color);
}
.acc__btn:hover { color: var(--pink-text); }
/* Plus/minus drawn from two bars, so it can fold into a minus on open. */
.acc__icon {
  position: relative;
  flex: none;
  width: 22px; height: 22px;
  margin-top: 2px;
  border-radius: var(--radius-pill);
  background: var(--mist);
  transition: background-color var(--dur-color) var(--ease-color);
}
.acc__icon::before,
.acc__icon::after {
  content: "";
  position: absolute;
  left: 50%; top: 50%;
  width: 10px; height: 1.5px;
  border-radius: 2px;
  background: var(--ink);
  transform: translate(-50%, -50%);
  transition: transform .42s var(--ease-reveal), opacity .42s var(--ease-reveal);
}
.acc__icon::after { transform: translate(-50%, -50%) rotate(90deg); }
.acc__item.is-open .acc__icon { background: var(--accent-pill); }
.acc__item.is-open .acc__icon::after { transform: translate(-50%, -50%) rotate(0deg); opacity: 0; }

/* Height animation with nothing measured in JS: the panel is a one-column grid
   whose row goes 0fr -> 1fr. The inner wrapper carries the overflow clip. */
.acc__panel {
  display: grid;
  grid-template-rows: 0fr;
  transition: grid-template-rows .45s var(--ease-reveal);
}
.acc__item.is-open .acc__panel { grid-template-rows: 1fr; }
.acc__panel > div { overflow: hidden; }
.acc__body {
  padding: 0 60px 30px 4px;
  display: flex;
  flex-direction: column;
  gap: 14px;
  line-height: 1.65;
}
.acc__body p { line-height: 1.65; }

/* ---- Proof pills (Why me) ---- */
.proof { display: flex; flex-wrap: wrap; gap: 10px; }
.proof span {
  display: inline-flex;
  align-items: center;
  padding: 9px 18px;
  border-radius: var(--radius-pill);
  background: var(--white);
  box-shadow: var(--shadow-card), var(--hairline-box);
  font-size: 14px;
  font-weight: 500;
  color: var(--ink);
  letter-spacing: -0.01em;
}

/* ---- Ways to work ---- */
.ways { display: grid; grid-template-columns: repeat(3, 1fr); gap: 24px; align-items: stretch; }
.way {
  display: flex;
  flex-direction: column;
  gap: 25px;
  padding: 60px 30px;
  background: var(--white);
  border-radius: var(--radius-card-sm);
  box-shadow: var(--shadow-card), var(--hairline-box);
  transition: transform var(--dur-card) var(--ease-card),
              box-shadow var(--dur-card) var(--ease-card);
}
.way:hover { transform: translateY(-1px); box-shadow: var(--shadow-e2), var(--hairline-box); }
.way h3 { font-size: 22px; line-height: 1.25; margin-bottom: 10px; }
.way__best { line-height: 1.6; }
.way__meta { margin-top: auto; padding-top: 22px; display: flex; flex-direction: column; gap: 10px; }
.way__meta > div { display: flex; gap: 12px; padding-top: 10px; box-shadow: var(--hairline-top); }
.way__meta dt {
  flex: 0 0 84px;
  font-size: 10.5px;
  font-weight: 600;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--faint);
  padding-top: 2px;
}
.way__meta dd { flex: 1; font-size: 14px; line-height: 1.45; color: var(--ink); }

/* ---- Newsletter ---- */
.newsletter {
  display: flex;
  gap: 56px;
  align-items: center;
  padding: 56px 60px;
  background: var(--white);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-card), var(--hairline-box);
}
.newsletter__text { flex: 1 1 0; display: flex; flex-direction: column; gap: 14px; }
.newsletter__text p { line-height: 1.6; }
/* The privacy shell occupies Substack's native 480x150 footprint. It initially
   contains an explanation and load button; main.js replaces those with the
   cross-origin iframe only after an explicit click. At 900px the newsletter
   stacks, and the shell can use the full available width. */
.newsletter__embed-shell {
  flex: 0 0 480px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 14px;
  width: 100%;
  max-width: 480px;
  min-height: 150px;
  padding: 20px 24px;
  border-radius: var(--radius-card-sm);
  background: var(--white);
  box-shadow: var(--shadow-card), var(--hairline-box);
  text-align: center;
}
.newsletter__embed-shell.is-loaded { padding: 0; overflow: hidden; }
.newsletter__embed-shell p {
  color: var(--muted);
  font-size: 14px;
  line-height: 1.45;
}
.newsletter__embed {
  display: block;
  width: 100%;
  height: 150px;
  border: 0;
  background: var(--white);
}

/* Form fields: 10px radius per the brand rule, hairline not border, no hard edges. */
.field {
  width: 100%;
  padding: 13px 16px;
  border: 0;
  border-radius: 10px;
  background: var(--mist);
  box-shadow: var(--hairline-box);
  font-family: var(--font-body);
  font-size: 15px;
  line-height: 1.4;
  letter-spacing: -0.01em;
  color: var(--ink);
  outline: none;
  transition: box-shadow var(--dur-color) var(--ease-color),
              background-color var(--dur-color) var(--ease-color);
}
.field::placeholder { color: var(--faint); }
.field:focus {
  background: var(--white);
  box-shadow: inset 0 0 0 1px var(--link), 0 0 0 3px rgba(48, 108, 186, .14);
}
textarea.field { min-height: 104px; resize: vertical; }
select.field {
  cursor: pointer;
  appearance: none;
  -webkit-appearance: none;
  background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8'%3E%3Cpath d='M1 1l5 5 5-5' fill='none' stroke='%2352575E' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 16px center;
  padding-right: 40px;
}

/* ---- Contact ---- */
.contact-block { display: flex; gap: 56px; align-items: flex-start; }
.contact-block__intro { flex: 0 0 340px; display: flex; flex-direction: column; gap: 18px; }
.contact-block__intro p { line-height: 1.6; }
.contact-card {
  flex: 1 1 0;
  min-width: 0;
  padding: 40px 40px 36px;
  background: var(--white);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-card), var(--hairline-box);
}
.lead-form { display: flex; flex-direction: column; gap: 18px; }
/* Author `display` beats the UA stylesheet's `[hidden] { display: none }` at
   equal specificity, so main.js setting leadForm.hidden = true on a
   successful submit did nothing without this — confirmed live, the form sat
   there showing "Sending…" right next to its own success message. */
.lead-form[hidden] { display: none; }
.lead-form__row { display: flex; gap: 18px; }
.lead-form__row > * { flex: 1 1 0; min-width: 0; }
.form-field { display: flex; flex-direction: column; gap: 7px; }
.form-field > label {
  font-size: 12px;
  font-weight: 600;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--faint);
}
.lead-form .btn { align-self: flex-start; margin-top: 6px; }
.lead-form .btn[disabled] { opacity: .6; cursor: not-allowed; }

/* Submission result, sitting outside <form> in the markup so it stays visible
   once main.js hides the form on success. Same "closing note" treatment as
   .help-panel__aim — a pink rule rather than a box — for the good outcome;
   the error state swaps that for a plain hairline, since there is nothing to
   emphasise about a failed request. */
.lead-form__status {
  margin-top: 22px;
  padding-left: 20px;
  box-shadow: inset 2px 0 0 var(--pink-line);
  font-size: 15px;
  font-style: italic;
  line-height: 1.6;
  color: var(--ink);
}
.lead-form__status.is-error { box-shadow: inset 2px 0 0 var(--hairline); font-style: normal; color: var(--muted); }

/* Channel list beside the form. */
.channels { display: flex; flex-direction: column; gap: 12px; align-items: flex-start; }
.channels a {
  display: inline-flex; align-items: center; gap: 8px;
  font-size: 15px; font-weight: 600; color: var(--link);
  transition: gap var(--dur-spring) var(--ease-spring);
}
.channels a:hover { gap: 14px; }
.channels svg { width: 15px; height: 15px; flex: none; }

/* ==========================================================================
   Systemique responsive — layered on the template's own breakpoints
   ========================================================================== */
@media (max-width: 1279.98px) {
  .newsletter { padding: 44px; gap: 36px; }
  .help-panel { gap: 36px; }
  .contact-block { gap: 36px; }
  .contact-block__intro { flex: 0 0 280px; }
  .contact-card { padding: 32px; }
  .ways { gap: 18px; }
  .way { padding: 28px 24px; }
}

@media (max-width: 900px) {
  .help-panel { flex-direction: column; gap: 30px; }
  .ways { grid-template-columns: 1fr; }
  .newsletter { flex-direction: column; align-items: stretch; gap: 28px; }
  .newsletter__embed-shell { flex: none; max-width: none; }
  .contact-block { flex-direction: column; }
  .contact-block__intro { flex: none; }
}

@media (max-width: 767.98px) {
  .newsletter { padding: 32px 22px; border-radius: 24px; }
  .contact-card { padding: 26px 20px; border-radius: 24px; }
  .lead-form__row { flex-direction: column; gap: 18px; }
  .acc__btn { font-size: 18px; gap: 16px; padding: 22px 2px; }
  .acc__body { padding: 0 8px 26px 2px; }
  .section-head .lede { font-size: 16px; }
  .hero__tagline { font-size: 21px; }
  /* The desktop pill bar (.tabs__scroller > .tabs__list) scrolls horizontally,
     but at this width the three labels add up to more than a screen's width
     with no visual cue that there's more to scroll — the last pill just runs
     off the edge and reads as broken. Below, instead of chasing that with a
     fade mask, the same three buttons and three panels become a single-open
     accordion: all three headers visible at once, tapping one both reveals
     its own content directly underneath and closes whichever was open, using
     the exact same aria-selected/hidden state main.js already drives — this
     is a pure restyle, no JS changes.

     display: contents on the wrapper divs removes their own boxes (and with
     them the pill bar's background/scrolling) while keeping the buttons and
     marker in the DOM, so grid-area can place each button as a direct grid
     item of .tabs, immediately followed by its own panel — impossible while
     they were still nested a level down inside .tabs__scroller/.tabs__list. */
  .tabs {
    display: grid;
    grid-template-columns: 1fr;
    grid-template-areas:
      "btn-acc" "panel-acc"
      "btn-know" "panel-know"
      "btn-task" "panel-task";
    gap: 0;
  }
  .tabs__scroller, .tabs__list { display: contents; }
  .tabs__marker { display: none; }

  #tab-accountability   { grid-area: btn-acc; }
  #panel-accountability { grid-area: panel-acc; }
  #tab-knowledge        { grid-area: btn-know; }
  #panel-knowledge      { grid-area: panel-know; }
  #tab-task             { grid-area: btn-task; }
  #panel-task           { grid-area: panel-task; }

  .tabs__btn {
    position: relative;
    display: flex;
    align-items: center;
    width: 100%;
    justify-content: space-between;
    gap: 16px;
    padding: 20px 22px;
    border-radius: 0;
    box-shadow: var(--hairline-top);
    font-size: 16px;
    white-space: normal;
    text-align: left;
  }
  /* :last-of-type (by tag, not the #tab-task id) so this stays at the same
     specificity as the selected-state rule below and loses to its
     box-shadow: none when Task management is the open item — an id selector
     here would out-specify that override and leave a stray hairline framing
     the ink block. */
  .tabs__btn:last-of-type { box-shadow: var(--hairline-top), var(--hairline-bottom); }
  /* No hover state at all here — this is a tap target on a touch-first
     layout, and a lingering desktop :hover read as a stray highlight on
     hybrid devices. */
  .tabs__btn:hover { color: var(--muted); }
  .tabs__btn[aria-selected="true"],
  .tabs__btn[aria-selected="true"]:hover {
    background: var(--ink);
    color: var(--white);
    font-weight: 700;
    box-shadow: none;
  }

  /* Reuses the same right-pointing arrow as the buttons elsewhere (#icon-arrow,
     see .arrow) rather than a hand-drawn shape — points down at rest (closed,
     "expand downward") and flips to point up once open. */
  .tabs__chevron {
    display: block;
    width: 16px;
    height: 16px;
    flex: none;
    color: currentColor;
    transform: rotate(90deg);
    transition: transform .42s var(--ease-reveal);
  }
  .tabs__btn[aria-selected="true"] .tabs__chevron { transform: rotate(-90deg); }

  /* The header text takes on the content's own h3 styling (size/weight/family
     only — color stays whatever .tabs__btn's selected state already sets, so
     it keeps turning white on ink), and the panel's own h3 is hidden — without
     that, opening an item repeated its title twice in a row: once as the
     header you tapped, once again as the first line of content right under it. */
  .tabs__btn-label {
    font-family: var(--font-display);
    font-weight: 400;
    line-height: 1.2;
    letter-spacing: 0;
    font-size: 23px;
  }
  .help-panel__body > h3 { display: none; }

  /* 22px left/right to match .tabs__btn's own horizontal padding — previously
     2px, which left the body copy sitting well left of the header text above
     it instead of sharing its edge. */
  .tabs__panel { padding: 24px 22px 28px; }
}

/* Reduced motion: switch the hero entrance off outright, rather than let the
   template's 0.01ms override run it as a flash. */
@media (prefers-reduced-motion: reduce) {
  .hero__photo, .hero__text > *, .tabs__panel { animation: none !important; }
}

/* ==========================================================================
   Small additions the markup depends on
   ========================================================================== */

/* Hero: the eyebrow and H1 travel together as one stagger step, and the text
   column centres against the full-height photo instead of hanging from the top. */
.hero__text { justify-content: center; }

/* Screen-reader-only labels (the two email inputs whose placeholder is the label). */
.visually-hidden {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* Legal links in the footer's copyright line. Blue-600 is too dark on Grey-700,
   so these take Mist with an underline instead — per the on-dark rule. */
.site-footer .copyright a {
  color: var(--mist);
  text-decoration: underline;
  text-underline-offset: 2px;
  margin-left: 18px;
}
.site-footer .copyright a:hover { color: var(--periwinkle); }
.privacy-settings {
  margin-left: 18px;
  padding: 0;
  border: 0;
  background: none;
  color: var(--mist);
  font: inherit;
  text-decoration: underline;
  text-underline-offset: 2px;
  cursor: pointer;
}
.privacy-settings:hover { color: var(--periwinkle); }
.privacy-settings:focus-visible {
  outline: 2px solid var(--periwinkle);
  outline-offset: 3px;
  border-radius: 2px;
}

/* ==========================================================================
   Fixes to inherited template behaviour
   ========================================================================== */

/* The template's footer form column is `flex: 0 0 381px` — a basis that also
   refuses to shrink. Below roughly 430px of viewport that pushes 30px of the
   footer past the right edge and gives the whole page a horizontal scrollbar.
   Measured at 390px: document scrollWidth 405 against a 375px client width.
   The column keeps its design; it is only allowed to shrink now. */

   the controls row would stay visible when the JS hides it for a single-page
   carousel. Only reachable if the testimonial count ever drops to one screenful. */


/* ==========================================================================
   BOLDER PASS
   The first draft read flat because it opted out of the template's strongest
   moves and rebuilt everything as quiet white cards. This pass puts the
   template's own vocabulary back to work — the ink card that fills
   periwinkle-dark on hover, the pink pill, the white card section, the split
   layout — and gives the page a light/dark rhythm instead of one even tone.

   No new colour, font, radius or shadow primitive is introduced. Every value
   below already existed in the token block. Still no scroll animation.
   ========================================================================== */

/* ---- Browser surfaces ----
   The parts nobody draws: selection, caret, focus ring, scrollbars, numerals.
   They ship as browser defaults belonging to no design system, so they get
   themed from the palette like everything else. */
::selection { background: var(--accent-pill); color: var(--ink); }
:root { caret-color: var(--link); accent-color: var(--link); }

:focus-visible {
  outline: 2px solid var(--link);
  outline-offset: 3px;
  border-radius: 4px;
}
.btn:focus-visible, .tabs__btn:focus-visible,
.site-footer :focus-visible { outline-color: var(--periwinkle); }

* { scrollbar-width: thin; scrollbar-color: var(--periwinkle) transparent; }
*::-webkit-scrollbar { width: 10px; height: 10px; }
*::-webkit-scrollbar-track { background: transparent; }
*::-webkit-scrollbar-thumb {
  background: var(--periwinkle);
  border-radius: var(--radius-pill);
  border: 3px solid var(--mist);
}
*::-webkit-scrollbar-thumb:hover { background: var(--periwinkle-dark); }

/* Links get a considered underline rather than the browser's baseline-crossing one. */
.prose a, .acc__body a { text-underline-offset: 3px; text-decoration-thickness: 1px; }

/* ---- Hero ---- */
/* The hero sits on the signature gradient, and the brand's rule for a primary
   button on the gradient is the pink pill, not the grey one. The first draft
   used grey here, which is the on-light variant. */
.hero .hero__intro p { /* uncapped: its own column already holds it at ~68 chars */ }

/* ==========================================================================
   PROBLEM CARDS — after the Mariq (mariq.webflow.io) project card
   Replaces the six full-width ink rows. Rebuilt from that component's actual
   mechanics rather than from a screenshot:

     - Its focus effect is pure LAYOUT: `padding: 220px 20px` on a full-width
       card, so one card fills the viewport and you read them one at a time.
       No sticky stacking, no scroll pinning.
     - Its animation is pointer-driven, not scroll-driven. Webflow interaction
       e-213 is a MOUSE_MOVE continuous action on the X and Y axes with
       `smoothing: 90`, `restingState: 50` and a 500ms duration, plus a
       MOUSE_OVER/MOUSE_OUT pair. Nothing about it reads the scrollbar.

   What is deliberately NOT copied: the photographic background, the condensed
   uppercase display face, and the "User Engagement" kicker above the heading.
   The first two are off-brand; the third is an eyebrow, which is out.

   The photo is replaced by a flat ink card that darkens to periwinkle on
   hover, with the text drifting slightly against the pointer for the same
   sense of the card being alive. Smoothing is a CSS transition on a
   transform rather than a rAF loop, which is both cheaper and closer to
   Webflow's own 500ms tween.
   ========================================================================== */
.pain-stack { display: flex; flex-direction: column; gap: 40px; }

/* Progress pill: six cards is a lot of scrolling to take on faith, so this
   sits fixed to the viewport like a mini nav bar — not sticky, which only
   holds within its own parent's box and would have scrolled off with the
   section-head above .pain-stack. Hidden by default and faded in only once
   the first card actually reaches the viewport's vertical centre (main.js
   drives .is-visible, .is-active and the count off the same observer that
   watches each card cross that line), so it never appears while the section
   heading above the cards is still on screen.

   Right-aligned on desktop/tablet so it sits beside the card stack rather
   than floating dead-centre over it. `right` lines the pill up with the
   card's own padding edge — where the h3/p text actually starts — not the
   card's outer boundary, which sat 80px further out (.pain-card's own
   `padding: 180px 80px`) and read as overhanging past the content. That 80px
   is folded into the base value below (104px card-edge + 80px card padding
   = 184px); the tablet override folds in its own 32px `.pain-card` padding
   the same way. Recentred on mobile, where the cards run edge-to-edge and
   there is no "beside" to sit in.

   Translucent rather than solid white — the cards themselves are full-bleed
   ink, so a solid pill would read as an opaque label sitting on top of the
   page; the glass treatment lets the card colour bleed through instead. */
.pain-progress {
  position: fixed;
  top: calc(var(--header-top) + var(--header-h) + 16px);
  right: max(184px, calc((100vw - var(--content-max) - 160px) / 2 + 160px));
  z-index: 9;
  display: inline-flex;
  align-items: center;
  gap: 12px;
  padding: 10px 16px;
  background: rgba(255, 255, 255, .6);
  backdrop-filter: blur(16px) saturate(160%);
  -webkit-backdrop-filter: blur(16px) saturate(160%);
  border-radius: var(--radius-pill);
  box-shadow: var(--shadow-card), var(--hairline-box);
  opacity: 0;
  pointer-events: none;
  transform: translateY(-12px);
  transition: opacity var(--dur-color) var(--ease-color),
              transform var(--dur-color) var(--ease-color);
}
.pain-progress.is-visible {
  opacity: 1;
  transform: translateY(0);
}
.pain-progress__track { display: flex; align-items: center; gap: 6px; }
.pain-progress__step {
  width: 8px;
  height: 8px;
  border-radius: var(--radius-pill);
  background: var(--hairline);
  transition: width var(--dur-color) var(--ease-color),
              background-color var(--dur-color) var(--ease-color);
}
.pain-progress__step.is-active { width: 22px; background: var(--periwinkle); }
.pain-progress__count {
  font-size: 13px;
  font-weight: 600;
  letter-spacing: -0.01em;
  color: var(--muted);
  white-space: nowrap;
}
.pain-progress__current { color: var(--ink); }

.pain-card-wrap { position: relative; }

/* --------------------------------------------------------------------------
   The scroll pass. This is the effect Victoria pointed at, and it is a genuine
   scroll-linked animation — the one category the original brief excluded, added
   back deliberately on request.

   Lifted from Mariq's Webflow interaction, which is a SCROLLING_IN_VIEW
   continuous action on `.project-card-wrap` (smoothing 50, starts on entering,
   ends on exiting) driving its `.project-card` child through three keyframes:

       0%    translateY(100vh)  rotateX(100deg)
       51%   translateY(0)      rotateX(0)
       100%  scale(.9)

   with `perspective: 100vw` on the wrapper. The card tilts up out of the floor,
   lies flat as it crosses the middle of the screen, then shrinks slightly as it
   leaves — which is why an exiting card sits a little inset from a arriving one.

   Built here as a native CSS scroll-driven animation instead of JS: it runs on
   the compositor, costs no script, and cannot jank. The wrapper carries the
   named view timeline precisely because it stays untransformed — driving a
   timeline from an element you are also transforming risks a feedback loop.

   Gated twice. `@supports` means a browser without scroll-driven animations
   shows plain static cards rather than playing the keyframes once on load, and
   the reduced-motion query keeps it off for anyone who has asked for less.
   -------------------------------------------------------------------------- */
@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    .pain-card-wrap {
      perspective: 100vw;
      view-timeline: --pain-pass block;

      /* --- the two dials that set the rhythm ---------------------------------
         `--pain-rise` is how far below its own slot a card starts. It is the
         single biggest influence on how quickly cards arrive: while a card is
         still translated down it is off-screen, so a large rise leaves a long
         empty gap between one card and the next.

         Mariq uses 100vh, which on a 900px viewport keeps each card hidden for
         roughly the first 35% of its pass. 30vh brings that down to about 12%,
         so the cards follow each other far more briskly while keeping the same
         tilt-up-off-the-floor move.

         Raise `--pain-rise` for a slower, more spaced-out cadence; lower it
         towards 0 for cards that arrive almost immediately. */
      --pain-rise: 30vh;
      --pain-tilt: 100deg;
    }
    .pain-card {
      animation-name: pain-pass;
      animation-duration: auto;
      animation-timing-function: linear;
      animation-fill-mode: both;
      animation-timeline: --pain-pass;
    }
    /* The settle point is a keyframe selector, so it cannot be a variable.
       Mariq settles at 51%; 40% here, which lands the card flat sooner and
       leaves more of the pass for the quiet scale-out. */
    @keyframes pain-pass {
      0%   { transform: translateY(var(--pain-rise, 30vh))
                        rotateX(var(--pain-tilt, 100deg)) scale(1); }
      40%  { transform: translateY(0) rotateX(0deg) scale(1); }
      100% { transform: translateY(0) rotateX(0deg) scale(.9); }
    }
  }
}

.pain-card {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  justify-content: center;
  text-align: left;
  padding: 180px 80px;
  background: var(--ink);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-card);
  transition: background-color var(--dur-spring) var(--ease-spring),
              box-shadow var(--dur-spring) var(--ease-spring);
}
.pain-card:hover { background: var(--periwinkle-dark); box-shadow: var(--shadow-e2); }

/* The parallax layer. Webflow moves this against the pointer; --px / --py are
   written by main.js as plain numbers in the range -0.5 … 0.5. */
.pain-card__body {
  position: relative;
  z-index: 1;
  display: flex;
  flex-direction: column;
  gap: 50px;
  width: 100%;
  transform: translate3d(calc(var(--px, 0) * -10px), calc(var(--py, 0) * -10px), 0);
  transition: transform .5s var(--ease-hover);
}

.pain-card__icon {
  width: 35px;
  height: 35px;
  color: var(--periwinkle);
  flex-shrink: 0;
}

.pain-card h3 {
  color: var(--white);
  font-size: 40px;
  font-weight: 400;
  line-height: 1.15;
  /* No measure cap. The 749px body is the constraint, exactly as on the
     site-clone's `.comp-card h3` and on Mariq's own card, which caps only the
     description. A `ch` cap here squeezed the heading to 25 characters over
     five lines — see the note at the foot of this file. */
}
.pain-card p {
  width: 80%;
  color: var(--periwinkle-100);
  font-size: 17px;
  line-height: 1.65;
}

@media (max-width: 1279.98px) {
  .pain-stack { gap: 28px; }
  .pain-card { padding: 130px 32px; }
  .pain-card h3 { font-size: 33px; }
  /* .page no longer hits its max-width here, so it just runs edge to edge
     inside the body's own 24px gutter with 29px of its own padding — a flat
     53px from the viewport edge to the card's outer edge, not the desktop
     calc() above — plus this breakpoint's own .pain-card padding (32px) to
     land on the card's content edge instead. */
  .pain-progress { right: 85px; }
}

@media (max-width: 767.98px) {
  .pain-stack { gap: 18px; }
  .pain-card { padding: 76px 22px; border-radius: 24px; }
  .pain-card h3 { font-size: 25px; }
  .pain-card p { font-size: 16px; }
  /* The parallax is pointer-only anyway, but make sure nothing is left offset. */
  .pain-card__body { transform: none; }
  /* Cards run edge to edge here, so there is no "beside the card" to sit in —
     back to centred, like the desktop/tablet rule before this was moved. */
  .pain-progress {
    right: auto;
    left: 50%;
    padding: 8px 12px;
    gap: 10px;
    transform: translate(-50%, -12px);
  }
  .pain-progress.is-visible { transform: translate(-50%, 0); }
  .pain-progress__count { font-size: 12px; }
}

/* ==========================================================================
   The statement
   Plain `.card-section`, exactly as the template uses it for My Story and Ideas
   I Live By: left-aligned, heading and prose both filling the same column, the
   template's 80px padding and 40px gap, template type sizes. The only addition
   is the ink text colour, which mirrors what the template does for #story.
   ========================================================================== */
.statement .prose p { color: var(--ink); }

/* ==========================================================================
   How I can help — the artifact beside the words
   Each tab panel is now the template's `.split`: an authored SVG of what the
   work actually produces on the left, the explanation and the list on the
   right. The artifact names the job; it is not decoration.
   ========================================================================== */
.help-panel { display: flex; gap: 56px; align-items: stretch; }
.artifact {
  /* Width is no longer a fixed basis — with align-self: stretch giving it a
     definite height and no width of its own, flex-basis:auto resolves the
     width from aspect-ratio instead, so the box itself is the illustrations'
     native 2:3 crop rather than a wider frame they have to be fitted into.
     That also hands the freed-up width straight to help-panel__body (flex:
     1 1 0), which is the point: more room for the text column. Unset again
     below once .help-panel drops to a stacked column, where a tall 2:3 box
     stretched to 100% width would be enormous. */
  flex: 0 0 auto;
  aspect-ratio: 2 / 3;
  position: relative;
  align-self: stretch;
  min-height: 420px;
  background: var(--white);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-card), var(--hairline-box);
  overflow: hidden;
}
.artifact svg { position: absolute; inset: 0; width: 100%; height: 100%; display: block; }
/* Cover, not contain: now that .artifact's own box is the same 2:3 ratio as
   the source images, cover fills it exactly with no crop — and once the
   stacked mobile layout below overrides the box back to a wide/short shape,
   cover is what crops the portrait image down to that banner instead of
   letterboxing or stretching it. */
.artifact img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; display: block; }
.help-panel__body { flex: 1 1 0; display: flex; flex-direction: column; gap: 22px; min-width: 0; }
.help-panel__body > h3 { font-size: 30px; font-weight: 400; line-height: 1.2; }
.help-panel__body p { font-size: 17px; line-height: 1.6; }
.help-panel__cols { display: grid; grid-template-columns: 1fr 1fr; gap: 10px 36px; }

/* ==========================================================================
   Why work with me — inside a white card section, with the split layout
   ========================================================================== */
#why-me .split__media { flex: 0 0 320px; min-height: 380px; }
#why-me .split__list { gap: 22px; justify-content: center; }
#why-me h2 { font-size: 38px; line-height: 1.15; }

/* Proof pills carry real numbers, so they get tabular figures and a bit more
   presence than a plain tag would. */
.proof span { font-variant-numeric: tabular-nums; padding: 10px 20px; font-size: 14.5px; }

/* ==========================================================================
   Ways to work — one committed choice, not three equal ones
   The flagship offer takes the ink treatment (and the pink pill); the other
   two stay white. Three identical cards said nothing about which to pick.
   ========================================================================== */
.ways { align-items: stretch; }
.way__cta { margin-top: 34px; align-self: flex-start; }

.way--featured {
  background: var(--ink);
  box-shadow: var(--shadow-card-featured);
}
.way--featured:hover { background: var(--periwinkle-dark); box-shadow: var(--shadow-e2); }
.way--featured,
.way--featured .way__best,
.way--featured .bullets li { color: var(--periwinkle-100); }
.way--featured h3 { color: var(--white); }
.way--featured .bullets li::before { background: var(--accent-pill); }
.way--featured .way__meta > div { box-shadow: inset 0 1px 0 rgba(245, 246, 250, .22); }
.way--featured .way__meta dt { color: var(--periwinkle); }
.way--featured .way__meta dd { color: var(--mist); }
.way--featured .link-tertiary { color: var(--accent-pill); }

/* ==========================================================================
   Newsletter — the second dark beat
   ========================================================================== */
.newsletter { background: var(--ink); box-shadow: var(--shadow-card-featured); }
.newsletter h2 { color: var(--mist); font-size: 36px; line-height: 1.15; }
.newsletter__text p { color: var(--periwinkle-100); font-size: 17px; }

/* ==========================================================================
   Testimonials — the quote is the content, so it gets the room
   Italic is the brand's one emphasis mechanism, and it warms a wall of quotes
   without setting body copy in the display face.
   ========================================================================== */

/* ==========================================================================
   Contact
   ========================================================================== */
.contact-block__intro h2 { font-size: 38px; line-height: 1.15; }
.channels a { font-size: 15.5px; }

/* ==========================================================================
   Responsive for the bolder pass
   ========================================================================== */
@media (max-width: 1279.98px) {
  /* Still row layout here — just a shorter floor for the aspect-ratio width
     to derive from, same as the base rule. */
  .artifact { min-height: 380px; }
  .help-panel { gap: 36px; }
  .help-panel__body > h3 { font-size: 26px; }
  #why-me .split__media { flex: 0 0 260px; min-height: 320px; }
  #why-me h2, .newsletter h2, .contact-block__intro h2 { font-size: 32px; }
}

@media (max-width: 900px) {
  .help-panel { flex-direction: column; }
  /* Stacked now, so the 2:3 box from the row layout would be enormous at full
     width — back to a fixed-height banner, cropped by .artifact img's cover. */
  .artifact { flex: none; width: 100%; aspect-ratio: auto; min-height: 300px; }
  #why-me .split { flex-direction: column; }
  #why-me .split__media { flex: none; width: 100%; min-height: 260px; }
}

@media (max-width: 767.98px) {
  .help-panel__cols { grid-template-columns: 1fr; gap: 11px; }
  .help-panel__body > h3 { font-size: 23px; }
  #why-me h2, .newsletter h2, .contact-block__intro h2 { font-size: 26px; }
  .artifact { min-height: 240px; }
  .way__cta { margin-top: 24px; }
}

/* The quiet line introducing each bullet list — copybook copy, not a label. */
.help-panel__intro { font-size: 16px; line-height: 1.6; color: var(--muted); }

/* ==========================================================================
   NOTE — text blocks run full width by design (2026-08-23)
   Every measure cap was removed on request: headings and body copy fill their
   container. If any of them is ever capped again, do NOT use `ch` on Libre
   Caslon Condensed — `ch` is the advance width of "0", and in this face the
   digits are far narrower than the average letter, so a `20ch` cap on a 40px
   heading resolves to ~406px and fits about 25 characters, not 20. Use `em`.
   `ch` is fine on Inter, where it is close to a real character.
   ========================================================================== */

/* ==========================================================================
   Testimonials — auto-scrolling marquee
   Modelled on Mariq's, whose Webflow interaction is literally titled
   "Testimonial Marquee": each row translates X by -100% over 50s, linear, on
   an infinite loop, with gradient overlay divs (250px, narrowing to 60px on
   mobile) masking both ends. Rebuilt here as a plain CSS animation.

   Two details that make it work:
     - The columns are duplicated in the markup. The track animates by exactly
       one set plus one gap, so at the moment it snaps back the clone is
       sitting precisely where the original was and the seam is invisible.
       `-50%` alone is not enough: the gap between the two sets means one set
       measures 50% + half a gap, hence the `- (gap / 2)` below.
     - The clone carries `aria-hidden`, so the six quotes are not announced
       twice to a screen reader.

   The marquee viewport follows the content column. The moving track may be
   wider, but it fades and clips at the same max-width as the rest of the page.
   ========================================================================== */
.quote-marquee {
  --marquee-gap: 24px;
  --marquee-duration: 36s;   /* ~35px/sec, matching the reference's pace */
  position: relative;
  width: 100%;
  max-width: var(--content-max);
  margin-inline: auto;
  overflow: hidden;
}

.quote-marquee__track {
  display: flex;
  gap: var(--marquee-gap);
  width: max-content;
  padding: 4px 0;                    /* room for the cards' hover lift */
  animation: quote-marquee var(--marquee-duration) linear infinite;
}
@keyframes quote-marquee {
  from { transform: translateX(0); }
  to   { transform: translateX(calc(-50% - (var(--marquee-gap) / 2))); }
}

/* Give people a way to stop it and read. Focus counts too, so a keyboard user
   tabbing into a card is not chasing it across the screen. */
.quote-marquee:hover .quote-marquee__track,
.quote-marquee:focus-within .quote-marquee__track { animation-play-state: paused; }

.quote-col {
  display: flex;
  flex-direction: column;
  gap: var(--marquee-gap);
  flex: none;
  width: min(400px, 78vw);           /* on mobile the next card peeks in */
}

/* The end fades. Mist rather than white, because that is the page ground here.
   The zero stop is written as rgba of the same colour rather than
   `transparent`, which some engines interpolate through transparent BLACK and
   leave a grey haze across the gradient. */
.quote-marquee__fade {
  position: absolute;
  top: 0; bottom: 0;
  width: 250px;
  pointer-events: none;
  z-index: 1;
}
.quote-marquee__fade.is-left  { left: 0;  background-image: linear-gradient(90deg,  var(--mist), rgba(245, 246, 250, 0)); }
.quote-marquee__fade.is-right { right: 0; background-image: linear-gradient(270deg, var(--mist), rgba(245, 246, 250, 0)); }

.quote-card {
  display: flex;
  flex-direction: column;
  gap: 20px;
  padding: 40px 35px;
  margin: 0;
  background: var(--white);
  border-radius: var(--radius-card-sm);
  box-shadow: var(--shadow-card), var(--hairline-box);
  transition: transform var(--dur-card) var(--ease-card),
              box-shadow var(--dur-card) var(--ease-card);
}
.quote-card:hover { transform: translateY(-1px); box-shadow: var(--shadow-e2), var(--hairline-box); }

/* Italic is the brand's one emphasis mechanism, and it warms a wall of quotes
   without setting body copy in the display face. */
.quote-card__quote { flex: 1; font-size: 16.5px; font-style: italic; line-height: 1.65; color: var(--ink); }
.quote-card__quote p + p { margin-top: .8em; }

/* Attribution sits at the foot of the card, under a hairline. */
.quote-card__who { margin-top: auto; padding-top: 18px; box-shadow: var(--hairline-top); }
.quote-card__who strong { display: block; color: var(--ink); font-weight: 600; font-size: 15.5px; line-height: 1.3; }
.quote-card__who span   { display: block; margin-top: 4px; font-size: 13px; line-height: 1.4; color: var(--faint); }

@media (max-width: 1279.98px) { .quote-marquee__fade { width: 160px; } }
@media (max-width: 767.98px) {
  .quote-marquee__fade { width: 60px; }
  .quote-card { padding: 26px 22px; }
}

/* Reduced motion: stop the loop, drop the duplicate set, and hand the row over
   as something the reader scrolls themselves. */
@media (prefers-reduced-motion: reduce) {
  .quote-marquee__track { animation: none; width: auto; }
  .quote-marquee { overflow-x: auto; }
  .quote-col[aria-hidden="true"] { display: none; }
}

/* ==========================================================================
   Cookie consent
   Always `display: flex` — never `[hidden]` or `display: none` toggled by a
   class — and shown/hidden purely through opacity + an off-screen transform,
   the same technique as .pain-progress. That sidesteps the bug found earlier
   this build where an authored `display` on an element silently beat the
   browser's own `[hidden] { display: none }` rule and the element never
   actually disappeared.
   ========================================================================== */
.cookie-banner {
  position: fixed;
  right: 24px;
  bottom: 24px;
  z-index: 50;
  max-width: 380px;
  display: flex;
  flex-direction: column;
  gap: 14px;
  padding: 22px;
  background: var(--white);
  border-radius: var(--radius-card-sm);
  box-shadow: var(--shadow-card-featured), var(--hairline-box);
  opacity: 0;
  pointer-events: none;
  transform: translateY(calc(100% + 40px));
  transition: opacity var(--dur-reveal) var(--ease-reveal),
              transform var(--dur-reveal) var(--ease-reveal);
}
.cookie-banner.is-visible { opacity: 1; pointer-events: auto; transform: translateY(0); }
.cookie-banner p { font-size: 14px; line-height: 1.5; color: var(--muted); }
.cookie-banner a { color: var(--link); text-decoration: underline; }
.cookie-banner__actions { display: flex; gap: 10px; flex-wrap: wrap; }
/* These sit inside a 380px card, well under the .btn base's four-variant
   layout box (154px each incl. reserved hover margin) — no override needed,
   they just wrap onto their own row at this width regardless. */

@media (max-width: 767.98px) {
  .cookie-banner { left: 16px; right: 16px; bottom: 16px; max-width: none; }
}

/* ==========================================================================
   404
   ========================================================================== */
.error-page {
  min-height: 55vh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  gap: 20px;
  padding: 40px 24px;
}
.error-page .eyebrow { color: var(--periwinkle-dark); }
.error-page .lede { max-width: 480px; }
.error-page .btn { margin-top: 10px; }

/* ==========================================================================
   Legal pages (Imprint, Privacy)
   Long-form reading, unlike the rest of the site's scannable sections — a
   capped measure keeps lines from running the full 960px content width.
   ========================================================================== */
.legal-page { gap: 28px; }
.legal-page .prose { max-width: 640px; }
.legal-page .prose p { line-height: 1.65; }
.legal-page__block h3 { font-size: 18px; margin-bottom: 10px; }
