/* ==========================================================================
   Yosef Sonlace — site stylesheet
   Tokens are locked by brand_context.md §12. Never write a raw hex in a rule
   below :root. If a value is not a token here, it is not in the brand.
   ========================================================================== */

/* --------------------------------------------------------------------------
   1 · Fonts — self-hosted. No CDN, no @import, no Helvetica/Arial in any stack.
   Nourd is a commercial face (Hanken Design Co). The webfont licence is
   UNCONFIRMED and blocks production. See README.
   -------------------------------------------------------------------------- */

@font-face {
  font-family: "Nourd";
  src: url("../fonts/nourd_medium.ttf") format("truetype");
  font-weight: 500;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "Nourd";
  src: url("../fonts/nourd_bold.ttf") format("truetype");
  font-weight: 700;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "IBM Plex Sans";
  src: url("../fonts/IBMPlexSans-Regular.ttf") format("truetype");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "IBM Plex Sans";
  src: url("../fonts/IBMPlexSans-Medium.ttf") format("truetype");
  font-weight: 500;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "IBM Plex Sans";
  src: url("../fonts/IBMPlexSans-SemiBold.ttf") format("truetype");
  font-weight: 600;
  font-style: normal;
  font-display: swap;
}
/* IBM Plex Mono and Lora were both removed on 2026-08-06. Two faces ship now
   and two only — Nourd for display, IBM Plex Sans for everything else. The
   files are still in assets/fonts/ and are no longer referenced or served.

   Mono was doing structural work, not decoration: the decode-on-hover swaps a
   character for a random glyph, and a fixed advance width was what stopped the
   label jittering under the pointer. That job moved into ys.js, which measures
   each character once and pins its box before the first swap. */

/* --------------------------------------------------------------------------
   2 · Tokens
   -------------------------------------------------------------------------- */

:root {
  /* Colour — brand_context.md §12. Ratios are against paper unless noted. */
  --ys-paper: #f5f3f7;
  --ys-ink: #100c16;
  --ys-violet-900: #2a1d3d; /* 14.20 AAA */
  --ys-violet-700: #443062; /* 10.32 AAA — primary */
  --ys-violet-500: #6b4f93; /*  6.02 AA  */
  --ys-violet-300: #a893c4; /* borders; text on ink only, 7.03 AAA */
  --ys-violet-100: #e6e0ef; /* surface only */
  --ys-brass: #b8862b; /*  5.97 AA on ink — NEVER text on light */
  --ys-brass-600: #8a6318; /*  4.91 AA on paper — brass as text on light */

  /* Semantic — forms and system messages only. Never brand expression. */
  --ys-success: #15653f;
  --ys-warning: #8a4f09;
  --ys-error: #9b1c15;

  /* Input surface. Interface only, not a brand colour — form fields need to
     read as fields against a paper page. Ink on white is 19.33 AAA. */
  --ys-surface: #ffffff;

  /* Type — two faces, locked 2026-08-06. Nourd for display, IBM Plex Sans for
     everything else. `--ys-label` is the small uppercase letterspaced setting
     used for eyebrows, table labels, nav links and CTA labels; it is the body
     face, and it is a separate token only so those rules read as one system. */
  --ys-display: "Nourd", system-ui, sans-serif;
  --ys-body: "IBM Plex Sans", system-ui, sans-serif;
  --ys-label: "IBM Plex Sans", system-ui, sans-serif;

  --ys-step-0: 1rem; /* 16 body    */
  --ys-step-1: 1.25rem; /* 20 lead    */
  --ys-step-2: 1.5625rem; /* 25 h4      */
  --ys-step-3: 1.95rem; /* 31 h3      */
  --ys-step-4: 2.44rem; /* 39 h2      */
  --ys-step-5: 3.05rem; /* 49 h1      */
  --ys-step-6: 3.8rem; /* 61 display */
  --ys-step--1: 0.875rem; /* 14 small   */
  --ys-step--2: 0.8125rem; /* 13 label   */

  /* Layout — 8px rhythm */
  --ys-measure: 68ch;
  --ys-container: 71.25rem; /* 1140px */
  --ys-gutter: clamp(1.25rem, 4vw, 2.5rem);
  --ys-section: clamp(3.5rem, 8vw, 7rem);
  --ys-space-1: 0.5rem;
  --ys-space-2: 1rem;
  --ys-space-3: 1.5rem;
  --ys-space-4: 2rem;
  --ys-space-5: 3rem;
  --ys-space-6: 4rem;

  /* 3.75rem as of 2026-08-07, up from 3.5. The CTA block in the bar grew to
     2.75rem on the same day and 3.5 left it touching both edges of the strip
     in the tightened state. The bar still tightens on scroll — 3.75 to 3.25,
     the same 0.5rem it always moved. */
  --ys-nav-h: 3.75rem;

  /* Square. Every clickable thing on the site lost its radius on
     2026-08-05 — the pill shape went with the old careers page. */
  --ys-radius: 0;

  /* The footer's cards, and the bottom edge of the curtain above them. One
     token so the two cannot drift apart. */
  --ys-footer-radius: 1.5rem;
  --ys-hairline: 1px;

  /* The one easing used by every hover: the CTA swap, the shear, the row
     underline. */
  --ys-ease-hover: cubic-bezier(0.65, 0, 0.35, 1);
}

/* --------------------------------------------------------------------------
   3 · Reset and base
   -------------------------------------------------------------------------- */

*,
*::before,
*::after {
  box-sizing: border-box;
}

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  scroll-padding-top: calc(var(--ys-nav-h) + 1rem);
  /* Set here as well as on <body> so nothing can fall back to the browser's
     default serif — the two-face lock is sitewide, and <html> is a surface
     too. */
  font-family: var(--ys-body);
}

/* Scrollbars hidden sitewide, 2026-08-07. Hidden, never disabled: the wheel,
   the trackpad, the touch drag, the keyboard and the scroll-to-anchor all
   still work, and the scrollbar is only taken out of the paint.

   Three declarations because the three engines disagree — `scrollbar-width`
   is Firefox and current Chrome, `-ms-overflow-style` is old Edge, and the
   pseudo-element is WebKit and Safari, including every iPhone. iOS and
   Android already overlay their bars and reclaim no space, so the phone was
   never the problem; a desktop window is.

   It changes the layout by about 15px: the two solved type sizes on this
   site — the hero headline and the footer email — divide a 100vw figure that
   used to count a scrollbar the column did not have. Both formulas were set
   with that slack built in, so with the bar gone they simply run a shade
   wider than they were measured for, and neither line can now overflow. */
html {
  scrollbar-width: none;
  -ms-overflow-style: none;
}

html::-webkit-scrollbar,
body::-webkit-scrollbar,
.ys-tablewrap::-webkit-scrollbar {
  width: 0;
  height: 0;
  display: none;
}

.ys-tablewrap {
  scrollbar-width: none;
  -ms-overflow-style: none;
}

/* Lenis owns the scroll position while it is running — 2026-08-11, rewritten
   for 1.3.26. The library ships its own stylesheet (assets/css/lenis.css,
   linked ahead of this one) which handles the height and overscroll rules, so
   the only thing left here is the conflict it does not know about: our own
   `scroll-behavior: smooth` above is the browser doing the same job, and two
   things easing the same scroll fight each other into a stutter. */
html.lenis.lenis-smooth {
  scroll-behavior: auto;
}

/* lenis.css takes pointer events off every iframe so the page keeps its wheel.
   That is right for an embed nobody interacts with and wrong for a map. The
   map is marked data-lenis-prevent in ys.js, which hands its wheel back; this
   gives it the pointer back too. */
.lenis.lenis-smooth iframe[data-lenis-prevent] {
  pointer-events: auto;
}

@media (prefers-reduced-motion: reduce) {
  html {
    scroll-behavior: auto;
  }
}

body {
  margin: 0;
  background: var(--ys-paper);
  color: var(--ys-ink);
  font-family: var(--ys-body);
  font-size: var(--ys-step-0);
  line-height: 1.65;
  font-weight: 400;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  overflow-x: hidden;
}

img,
svg {
  max-width: 100%;
  height: auto;
  display: block;
}

/* --------------------------------------------------------------------------
   4 · Type
   -------------------------------------------------------------------------- */

h1,
h2,
h3,
h4 {
  font-family: var(--ys-display);
  margin: 0 0 var(--ys-space-3);
  color: var(--ys-ink);
  text-wrap: balance;
}

h1 {
  font-weight: 700;
  font-size: clamp(2.1rem, 5.4vw, var(--ys-step-5));
  line-height: 1.1;
  letter-spacing: -0.02em;
}

.ys-display {
  font-weight: 700;
  font-size: clamp(2.35rem, 6.4vw, var(--ys-step-6));
  line-height: 1.05;
  letter-spacing: -0.02em;
}

h2 {
  font-weight: 500;
  font-size: clamp(1.75rem, 4.2vw, var(--ys-step-4));
  line-height: 1.15;
  letter-spacing: -0.01em;
}

h3 {
  font-weight: 500;
  font-size: clamp(1.45rem, 3.2vw, var(--ys-step-3));
  line-height: 1.2;
  letter-spacing: -0.01em;
}

h4 {
  font-weight: 500;
  font-size: var(--ys-step-2);
  line-height: 1.3;
}

p {
  margin: 0 0 var(--ys-space-3);
  max-width: var(--ys-measure);
}

.ys-lead {
  font-size: var(--ys-step-1);
  line-height: 1.6;
  max-width: 60ch;
}

.ys-small {
  font-size: var(--ys-step--1);
  line-height: 1.5;
}

strong,
b {
  font-weight: 600;
}

/* Was Lora italic. Lora went with the two-face lock on 2026-08-06, and IBM
   Plex Sans Italic is not one of the files we hold — asking for italic here
   would have the browser slant the upright, which is worse than no italic at
   all. The quote is set apart by size and measure instead. */
.ys-quote {
  font-family: var(--ys-body);
  font-size: var(--ys-step-1);
  line-height: 1.55;
  margin: 0;
  max-width: 56ch;
  color: var(--ys-violet-700);
}

.ys-num {
  font-family: var(--ys-label);
  font-variant-numeric: tabular-nums;
}

/* Links in prose — brass-600 is the only brass that passes as text on light. */
a {
  color: var(--ys-brass-600);
  text-underline-offset: 0.2em;
  text-decoration-thickness: 1px;
}

a:hover {
  color: var(--ys-violet-700);
}

.ys-dark a {
  color: var(--ys-brass);
}

.ys-dark a:hover {
  color: var(--ys-paper);
}

/* Brass-600 is text-safe on paper (4.91) but drops to 4.20 on the violet-100
   card and tint surfaces, which fails AA. Accessibility outranks the palette
   in one direction only: the USE changes, never the colour. On violet-100,
   link text is violet-700 (9.35). Brass stays as the ornament rule and the
   list marker, which are non-text and only need 3:1. */
.ys-tint a:not(.ys-btn),
.ys-card a:not(.ys-btn),
.ys-tint .ys-link,
.ys-card .ys-link {
  color: var(--ys-violet-700);
}

.ys-tint a:not(.ys-btn):hover,
.ys-card a:not(.ys-btn):hover {
  color: var(--ys-violet-900);
}

/* --------------------------------------------------------------------------
   5 · Focus — visible everywhere. Never removed.
   -------------------------------------------------------------------------- */

:focus-visible {
  outline: 2px solid var(--ys-violet-700);
  outline-offset: 3px;
  border-radius: var(--ys-radius);
}

.ys-dark :focus-visible,
.ys-btn:focus-visible {
  outline-color: var(--ys-brass);
}

.ys-skip {
  position: absolute;
  left: var(--ys-gutter);
  top: -100%;
  z-index: 200;
  background: var(--ys-violet-700);
  color: var(--ys-paper);
  padding: 0.75rem 1.25rem;
  font-weight: 500;
  text-decoration: none;
  transition: top 150ms ease-out;
}

.ys-skip:focus {
  top: 0.75rem;
  color: var(--ys-paper);
}

/* --------------------------------------------------------------------------
   6 · Layout primitives
   -------------------------------------------------------------------------- */

.ys-wrap {
  width: 100%;
  max-width: var(--ys-container);
  margin-inline: auto;
  padding-inline: var(--ys-gutter);
}

.ys-section {
  padding-block: var(--ys-section);
}

.ys-section--tight {
  padding-block: clamp(2.5rem, 5vw, 4rem);
}

.ys-prose > * {
  max-width: var(--ys-measure);
}

.ys-stack > * + * {
  margin-top: var(--ys-space-3);
}

.ys-rule {
  border: 0;
  border-top: var(--ys-hairline) solid var(--ys-violet-300);
  margin: var(--ys-space-5) 0;
  max-width: none;
}

.ys-dark .ys-rule {
  border-top-color: var(--ys-violet-500);
}

/* Dark sections sit on ink. Never on violet 700 — nothing but white and
   paper passes on violet as body text. */
.ys-dark {
  background: var(--ys-ink);
  color: var(--ys-paper);
}

.ys-dark h1,
.ys-dark h2,
.ys-dark h3,
.ys-dark h4 {
  color: var(--ys-paper);
}

.ys-dark .ys-secondary {
  color: var(--ys-violet-300);
}

.ys-dark .ys-accent {
  color: var(--ys-brass);
}

.ys-tint {
  background: var(--ys-violet-100);
}

/* --------------------------------------------------------------------------
   7 · The one ornament — a brass rule above a section label.
   Used only here. Nothing else on the site is decorated.
   -------------------------------------------------------------------------- */

.ys-eyebrow {
  font-family: var(--ys-body);
  font-size: var(--ys-step--2);
  font-weight: 500;
  line-height: 1.4;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ys-violet-500);
  margin: 0 0 var(--ys-space-3);
  padding-top: var(--ys-space-2);
  border-top: 2px solid var(--ys-brass-600);
  display: inline-block;
  max-width: none;
}

.ys-dark .ys-eyebrow {
  color: var(--ys-violet-300);
  border-top-color: var(--ys-brass);
}

/* --------------------------------------------------------------------------
   8 · Buttons — one CTA style sitewide. One CTA per section, maximum.
   -------------------------------------------------------------------------- */

.ys-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 3rem; /* 48px — above the 44px minimum */
  padding: 0.75rem 1.75rem;
  background: var(--ys-violet-700);
  color: var(--ys-paper);
  font-family: var(--ys-body);
  font-size: var(--ys-step-0);
  font-weight: 500;
  line-height: 1.3;
  text-decoration: none;
  border: 0;
  border-radius: var(--ys-radius);
  cursor: pointer;
  transition: background-color 180ms ease-out;
}

.ys-btn:hover {
  background: var(--ys-violet-900);
  color: var(--ys-paper);
}

.ys-btn:active {
  background: var(--ys-ink);
}

.ys-btn[disabled],
.ys-btn[aria-disabled="true"] {
  background: var(--ys-violet-100);
  color: var(--ys-violet-500);
  cursor: not-allowed;
}

.ys-dark .ys-btn {
  background: var(--ys-paper);
  color: var(--ys-ink);
}

.ys-dark .ys-btn:hover {
  background: var(--ys-violet-100);
  color: var(--ys-ink);
}

/* Secondary actions are links, never a second button style. */
.ys-link {
  display: inline-block;
  min-height: 2.75rem;
  line-height: 2.75rem;
  color: var(--ys-brass-600);
  font-weight: 500;
  text-decoration: underline;
  text-underline-offset: 0.25em;
}

.ys-link:hover {
  color: var(--ys-violet-700);
}

.ys-dark .ys-link {
  color: var(--ys-brass);
}

.ys-dark .ys-link:hover {
  color: var(--ys-paper);
}

/* --------------------------------------------------------------------------
   9 · Header and navigation
   Rebuilt 2026-08-05 on the reference bar in the navbar video. One markup,
   two states:

     solid  — a paper bar with a hairline under it. Ink wordmark, ink links,
              the CTA block carrying a violet hairline so it still reads as a
              block on a light ground. Every page but the homepage ships in
              this state and never leaves it.
     clear  — transparent over the homepage hero. Paper wordmark, paper
              links, the same CTA block with no border. The homepage starts
              here and crosses to solid once the page has moved, which is
              what the reference bar does and what keeps the links legible
              over the light sections further down.

   Link hover is a per-character scramble (assets/js/ys.js) plus a colour
   shift to brass — brass-600 on paper at 4.91, brass on ink at 5.97. The
   script pins each character's box before the first swap, so a scrambled
   glyph occupies exactly the width of the one it replaced and nothing in the
   bar moves. That used to be mono's job; mono is gone.

   2026-08-06: the clear-then-solid behaviour went to every page, and the
   wordmark on the solid bar went violet-700.

     --clear     every page. Transparent over the hero, crossing to solid.
     --onlight   added when that hero is light, which is every page but the
                 homepage and careers. Starts in violet type instead of paper,
                 because paper on paper is nothing.
   -------------------------------------------------------------------------- */

.ys-header {
  position: sticky;
  top: 0;
  z-index: 100;
  background: transparent;
  color: var(--ys-ink);
  /* The bar has no surface of its own — the pod is the only thing in it that
     paints. Left as it was, the transparent strip still swallowed every click
     across the full width of the page. The pod takes its own events back. */
  pointer-events: none;
}

.ys-navpod,
.ys-mobilenav {
  pointer-events: auto;
}

/* Full-bleed row, gutter only — the wordmark sits near the left edge of the
   viewport rather than the left edge of a centred container, per the
   2026-08-04 revision. The reference bars run edge to edge the same way. */
.ys-header .ys-wrap {
  position: relative;
  z-index: 1;
  max-width: none;
}

/* Fixed rather than sticky. The pinned story stage takes over the scroll on the
   homepage and a sticky bar inside that flow rides away with it. */
.ys-header--clear {
  position: fixed;
  inset-inline: 0;
  top: 0;
}

/* Pages whose hero is light get the bar's height back as padding on <body>;
   the full-bleed hero pages need none.

   On <body>, not on <main>: the breadcrumb is rendered above <main> on eight
   pages, and padding on <main> leaves it under the bar. */
body.ys-underbar {
  padding-top: calc(var(--ys-nav-h) + var(--ys-hairline));
}

/* Three tracks: mark, empty, pod. The middle track is what pushes the pod to
   the right edge whatever the wordmark measures. */
/* One thing in the row now — the pod. The wordmark is its own fixed layer
   above, so the three-track grid the brand used to sit in is gone. */
.ys-header__inner {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  min-height: var(--ys-nav-h);
}

/* -- The wordmark, on its own layer -----------------------------------------
   It is not in the header any more. The header is fixed with a z-index, which
   makes it an isolated group, and anything blending inside an isolated group
   can only see that group's own backdrop — which here is nothing. The mark
   would have blended against transparency and come out plain white.

   So it is a sibling of <header>, fixed in its own right, exactly as the
   reference does it: one fixed layer for the mark at 101, another for the nav
   at 100, both children of <body>, so the mark's backdrop is the whole page.
   DOM order is unchanged — mark first, then the nav — so the tab order is too. */
.ys-brand {
  position: fixed;
  top: 0;
  left: var(--ys-gutter);
  z-index: 101;
  display: inline-flex;
  align-items: center;
  height: var(--ys-nav-h);
  text-decoration: none;
  color: currentColor;
  pointer-events: auto;
}

.ys-brand__stack {
  display: grid;
}

.ys-brand__stack > * {
  grid-area: 1 / 1;
}

.ys-brand__stack > img {
  transition: opacity 300ms ease-out;
}

/* -- The two-file fallback -------------------------------------------------
   Still here, still driven by data-ground, and it is what runs if the blend
   below is unsupported. Both files are approved colourways of the same mark. */
.ys-brand__wordmark--paper {
  opacity: 0;
}

.ys-header[data-ground="dark"] ~ .ys-brand .ys-brand__wordmark--paper,
.ys-brand[data-ground="dark"] .ys-brand__wordmark--paper {
  opacity: 1;
}

.ys-brand[data-ground="dark"] .ys-brand__wordmark--solid {
  opacity: 0;
}

@media (prefers-reduced-motion: reduce) {
  .ys-brand__stack > img {
    transition: none;
  }
}

.ys-brand__wordmark {
  display: block;
  height: 1.125rem;
  width: auto;
  min-width: 7.5rem;
}

@media (min-width: 48rem) {
  .ys-brand__wordmark {
    height: 1.375rem;
  }
}

/* -- The blend -------------------------------------------------------------
   Rebuilt 2026-08-11 (third pass). Reading the section under the bar gave the
   mark the right colour but not the behaviour Miguel actually pointed at: in
   the reference the mark inverts per pixel, so where it crosses a headline the
   overlap knocks out. That is `mix-blend-mode: difference`, and nothing else
   reproduces it.

   The one problem with it here is ours, not the reference's. Difference with a
   white source returns the inverse of whatever is behind, and the inverse of
   violet-700 is #BBCF9D — a pale sage. The reference site is cream and near
   black throughout, so its inverses are always neutral; ours would not be, and
   a fifth hue arriving on scroll is the one thing §10a says never loosens.

   So the backdrop is desaturated before it is inverted. The grey layer sits
   directly under the blend layer, masked to the same glyphs, and greys only
   the pixels the mark actually covers. Inverting a grey returns a grey, so the
   mark reads black over paper, near-white over ink and violet, and knocks out
   over type exactly as the reference does — with no hue introduced anywhere.

   To take the desaturation off and get the literal reference blend, delete the
   .ys-brand__stack::before rule. Nothing else has to change. */
.ys-brand__blend {
  display: none;
  background: #fff;
  -webkit-mask: url("../img/wordmark-paper.svg") left center / contain no-repeat;
  mask: url("../img/wordmark-paper.svg") left center / contain no-repeat;
  mix-blend-mode: difference;
  pointer-events: none;
}

@supports (mix-blend-mode: difference) {
  .ys-brand__blend {
    display: block;
  }

  /* The two colourways stay in the box so it keeps its measured size, and go
     to zero so only the blended layer paints. */
  .ys-brand__stack > img {
    opacity: 0 !important;
  }
}

/* The desaturating layer. Separate from the blend because backdrop-filter has
   to read the page before the blend inverts it, and an element cannot do both
   to itself. */
@supports ((backdrop-filter: grayscale(1)) or (-webkit-backdrop-filter: grayscale(1))) {
  .ys-brand__stack::before {
    content: "";
    grid-area: 1 / 1;
    -webkit-backdrop-filter: grayscale(1);
    backdrop-filter: grayscale(1);
    -webkit-mask: url("../img/wordmark-paper.svg") left center / contain no-repeat;
    mask: url("../img/wordmark-paper.svg") left center / contain no-repeat;
    pointer-events: none;
  }
}

.ys-header__end {
  display: flex;
  align-items: center;
}

/* -- The pod ---------------------------------------------------------------
   The reference bar has no bar. There is a wordmark at one end and, at the
   other, one dark block holding the links with the CTA inset inside it. That
   block is the only surface in the header, which is why the links never have
   to change colour over a light section and why the header itself can stay
   transparent the whole way down.

   Square, not the reference's rounded corners. --ys-radius went to 0 on
   2026-08-05 for everything clickable on this site and that decision is not
   downstream of this one. Raised with Miguel.

   The 0.25rem of padding is the frame: it is what makes the CTA read as a
   block sitting inside the pod rather than a button stuck to the end of it. */
.ys-navpod {
  display: flex;
  align-items: center;
  padding: 0.25rem;
  background: var(--ys-ink);
  border: var(--ys-hairline) solid transparent;
}

/* On an ink section the pod is ink on ink. A violet-500 hairline is enough to
   hold its edge without turning it into a second object. */
.ys-header[data-ground="dark"] .ys-navpod {
  border-color: var(--ys-violet-500);
}

/* -- The collapse ----------------------------------------------------------
   Scrolling down folds the links away and leaves the burger and the CTA;
   scrolling back up brings them out again. Driven by direction, not by depth,
   so the links are one flick away anywhere on the page.

   The clip is managed rather than declared. The Services dropdown is a child
   of the list, so a permanent `overflow: hidden` here would cut the panel off
   at the pod's bottom edge. It is on while the width is moving and off once
   the pod has settled open — ys.js writes data-settled on transitionend. */
.ys-nav {
  display: none;
  overflow: hidden;
  max-width: 44rem;
  transition:
    max-width 420ms var(--ys-ease-hover),
    opacity 200ms ease-out;
}

.ys-navpod[data-settled="expanded"] .ys-nav {
  overflow: visible;
}

@media (min-width: 62rem) {
  .ys-nav {
    display: block;
  }

  .ys-navpod[data-collapsed="true"] .ys-nav {
    max-width: 0;
    opacity: 0;
    visibility: hidden;
  }

  /* Peek — the folded pod under a pointer. The links come back and the burger
     stays, which is the difference between this and the fully open state. */
  .ys-navpod[data-collapsed="true"][data-peek="true"] .ys-nav {
    max-width: 44rem;
    opacity: 1;
    visibility: visible;
  }
}

@media (prefers-reduced-motion: reduce) {
  .ys-nav {
    transition: none;
  }
}

.ys-nav__list {
  display: flex;
  align-items: center;
  gap: clamp(0.75rem, 1.8vw, 1.5rem);
  list-style: none;
  margin: 0;
  padding: 0 0.75rem;
  white-space: nowrap;
}

/* Paper on ink in every state, because the pod is ink in every state. 12.86:1.
   0.75rem, set in caps at 0.1em, so it reads a size larger than the number. */
.ys-nav__link {
  display: inline-flex;
  align-items: center;
  min-height: 2.25rem;
  padding: 0 0.5rem;
  color: var(--ys-paper);
  font-family: var(--ys-label);
  font-size: 0.75rem;
  font-weight: 400;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  text-decoration: none;
  border-bottom: 1px solid transparent;
  transition:
    color 160ms ease-out,
    background-color 160ms ease-out;
}

/* The reference marks the hovered link with a darker block behind it rather
   than a colour change. Violet-900 on ink is a 1.4:1 step — visible as a
   shape, and it leaves the label at its full 12.86:1. */
.ys-nav__link:hover,
.ys-nav__link:focus-visible {
  background: var(--ys-violet-900);
  color: var(--ys-paper);
}

.ys-nav__link[aria-current="page"],
.ys-nav__link[aria-current="true"] {
  border-bottom-color: var(--ys-brass);
}

/* -- The burger ------------------------------------------------------------
   Always present under 62rem. Above it, only when the pod is collapsed — it
   crossfades against the links rather than sitting beside them, which is what
   keeps the pod one object through the fold. */
.ys-navpod__burger {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: none;
  width: 2.75rem;
  height: 2.25rem;
  padding: 0;
  background: none;
  border: 0;
  border-radius: 0;
  color: var(--ys-paper);
  cursor: pointer;
  transition:
    width 420ms var(--ys-ease-hover),
    opacity 200ms ease-out;
}

.ys-navpod__burger:hover {
  background: var(--ys-violet-900);
}

@media (min-width: 62rem) {
  .ys-navpod[data-collapsed="false"]:not([data-peek="true"]) .ys-navpod__burger {
    width: 0;
    opacity: 0;
    visibility: hidden;
  }
}

@media (prefers-reduced-motion: reduce) {
  .ys-navpod__burger {
    transition: none;
  }
}

.ys-navpod__bars {
  display: block;
  width: 1.125rem;
  height: 2px;
  background: currentColor;
  position: relative;
}

.ys-navpod__bars::before,
.ys-navpod__bars::after {
  content: "";
  position: absolute;
  left: 0;
  width: 100%;
  height: 2px;
  background: currentColor;
}

.ys-navpod__bars::before {
  top: -6px;
}

.ys-navpod__bars::after {
  top: 6px;
}

/* -- Services dropdown, added 2026-08-06 -----------------------------------
   Hover and focus-within, no script. The panel is always in the flow of the
   accessibility tree; only its visibility changes, and `visibility` rather
   than `display` so it can transition and so a tab into it still lands.

   The bridge is the reason the panel does not close between the link and the
   first item: the panel opens clear of the pod, and a gap with no hover target
   in it drops the hover the moment the pointer crosses it. */

.ys-nav__item {
  position: relative;
}

.ys-nav__caret {
  width: 0;
  height: 0;
  margin-left: 0.45rem;
  border-inline: 0.25rem solid transparent;
  border-top: 0.3rem solid currentColor;
  transition: transform 160ms var(--ys-ease-hover);
}

.ys-nav__item--has-sub:hover .ys-nav__caret,
.ys-nav__item--has-sub:focus-within .ys-nav__caret {
  transform: translateY(1px) rotate(180deg);
}

.ys-nav__sub {
  position: absolute;
  top: 100%;
  left: 50%;
  z-index: 1;
  min-width: 15rem;
  padding-top: 0.85rem; /* the bridge — hoverable, and invisible */
  transform: translateX(-50%) translateY(-0.35rem);
  opacity: 0;
  visibility: hidden;
  transition:
    opacity 160ms ease-out,
    transform 160ms var(--ys-ease-hover),
    visibility 0s linear 160ms;
}

.ys-nav__item--has-sub:hover .ys-nav__sub,
.ys-nav__item--has-sub:focus-within .ys-nav__sub {
  opacity: 1;
  visibility: visible;
  transform: translateX(-50%) translateY(0);
  transition-delay: 0s, 0s, 0s;
}

/* The panel is its own surface and does not inherit the pod's ink. */
.ys-nav__sub ul {
  list-style: none;
  margin: 0;
  padding: 0.375rem 0;
  background: var(--ys-paper);
  border: var(--ys-hairline) solid var(--ys-violet-100);
  box-shadow: 0 12px 32px rgb(16 12 22 / 0.14);
}

.ys-nav__sub a {
  display: block;
  padding: 0.625rem 1.125rem;
  color: var(--ys-violet-700);
  font-size: var(--ys-step--1);
  line-height: 1.4;
  text-decoration: none;
  transition:
    background-color 140ms ease-out,
    color 140ms ease-out;
}

.ys-nav__sub a:hover,
.ys-nav__sub a:focus-visible {
  background: var(--ys-violet-700);
  color: var(--ys-paper);
}

.ys-nav__sub a[aria-current="page"] {
  font-weight: 600;
}

@media (prefers-reduced-motion: reduce) {
  .ys-nav__sub,
  .ys-nav__caret {
    transition: none;
  }
}

/* Mobile: the children are listed under their parent, indented, rather than
   hidden behind a disclosure. */
.ys-mobilenav a.ys-mobilenav__sub {
  padding-left: var(--ys-space-3);
  font-size: var(--ys-step--1);
  font-weight: 400;
  color: var(--ys-violet-300);
}

/* -- The CTA, inset in the pod ---------------------------------------------
   The reference puts its accent block inside the dark capsule with the dark
   showing as a frame around it. Ours is violet-700 on ink: 2.15:1 as two
   adjacent fills, which is a visible edge, and the label inside it is paper at
   10.32:1 on the violet. The block is what carries the contrast, not the type.

   The label size is unchanged from 2026-08-07 and must stay unchanged. */
.ys-navpod__cta {
  flex: none;
  gap: 0.55rem;
  min-height: 2.25rem;
  padding: 0.5rem 1.4rem;
  background: var(--ys-violet-700);
  color: var(--ys-paper);
  font-family: var(--ys-label);
  font-size: var(--ys-step--2);
  font-weight: 400;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  white-space: nowrap;
  border: 0;
  border-radius: 0;
  transition: background-color 180ms ease-out;
}

.ys-navpod__cta:hover {
  background: var(--ys-violet-900);
  color: var(--ys-paper);
}

.ys-navpod__cta-ico {
  font-size: 1.1em;
  line-height: 1;
  transition: transform 180ms var(--ys-ease-hover);
}

.ys-navpod__cta:hover .ys-navpod__cta-ico {
  transform: translateX(2px);
}

@media (prefers-reduced-motion: reduce) {
  .ys-navpod__cta-ico {
    transition: none;
  }
}

/* -- The row on a phone ----------------------------------------------------
   Three things share 350px at 390 wide, and one of them cannot move: the
   wordmark's 120px floor is the published minimum for a lockup carrying type,
   so it is the CTA that gives.

   Measured at 390: brand 120 + gaps 48 + pod 206 = 374 against 350 available,
   so the pod was hanging 24px past the gutter. Trimming the two gaps, the
   burger and the CTA's own padding, and dropping the icon, brings the pod to
   about 158 and clears 360 as well.

   The CTA stays in the bar rather than being hidden until 62rem as it was
   before. It is the one action on the page and a phone is where most of this
   traffic reads. */
@media (max-width: 47.999rem) {
  .ys-navpod__burger {
    width: 2.5rem;
  }

  .ys-navpod__cta {
    padding-inline: 0.75rem;
    letter-spacing: 0.06em;
  }

  .ys-navpod__cta-ico {
    display: none;
  }
}

/* Paper ring on the ink pod, in both grounds — the pod is the surface a focus
   ring in this bar is ever drawn on. */
.ys-navpod :focus-visible {
  outline: 2px solid var(--ys-paper);
  outline-offset: 2px;
}

.ys-brand:focus-visible {
  outline: 2px solid var(--ys-violet-700);
  outline-offset: 3px;
}

.ys-header[data-ground="dark"] .ys-brand:focus-visible {
  outline-color: var(--ys-paper);
}

.ys-mobilenav {
  display: none;
  position: relative;
  z-index: 1;
  background: var(--ys-violet-900);
  border-top: var(--ys-hairline) solid var(--ys-violet-500);
  padding-block: var(--ys-space-2) var(--ys-space-4);
}

.ys-mobilenav[data-open="true"] {
  display: block;
}

.ys-mobilenav ul {
  list-style: none;
  margin: 0 0 var(--ys-space-3);
  padding: 0;
}

.ys-mobilenav a:not(.ys-btn) {
  display: block;
  min-height: 3rem;
  line-height: 3rem;
  color: var(--ys-paper);
  font-weight: 500;
  text-decoration: none;
  border-bottom: var(--ys-hairline) solid var(--ys-violet-500);
}

.ys-mobilenav a:not(.ys-btn):hover {
  color: var(--ys-brass);
}

.ys-mobilenav .ys-btn {
  width: 100%;
}

/* The panel used to be forced off above 62rem, because above 62rem there was
   no way to open it. There is now: the pod folds its links away on scroll down
   and the burger it leaves behind opens this. So the panel is available at
   every width, and what hides it is the same thing that always hid it —
   data-open being false. Rewritten 2026-08-11. */
@media (min-width: 62rem) {
  .ys-mobilenav .ys-wrap {
    max-width: none;
  }
}

/* --------------------------------------------------------------------------
   10 · Hero
   -------------------------------------------------------------------------- */

.ys-hero {
  padding-block: clamp(3rem, 9vw, 7.5rem) clamp(3rem, 8vw, 6rem);
}

.ys-hero h1 {
  max-width: 18ch;
}

.ys-hero .ys-lead {
  max-width: 54ch;
  margin-bottom: var(--ys-space-4);
}

.ys-hero__cta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--ys-space-3);
}

/* --------------------------------------------------------------------------
   10a · Hero scene — the mark, told as a scroll

   Three registered layers from one 1440x810 composition: sky, the reaching
   hand, the holding hand. At rest the hands sit where the artwork puts them,
   a few millimetres apart. Scroll drives them apart-to-together; the headline
   arrives as they close.

   Everything below degrades to the rest state. No JS, no GSAP, reduced
   motion, or a failed CDN all land on the same readable composed hero.
   -------------------------------------------------------------------------- */

/* Column flex, not `place-items: center`. Centred grid items shrink-wrap,
   which collapses a width:100% child to min-content and makes the section
   measure zero wide — ScrollTrigger then pins against a junk height. */
.ys-heroscene {
  position: relative;
  min-height: 100svh;
  display: flex;
  flex-direction: column;
  justify-content: center;
  overflow: hidden;
  background: var(--ys-ink);
  isolation: isolate;
}

/* Stage holds the 1440:810 artwork ratio and covers the viewport, so the
   three layers stay registered to each other at every screen shape. */
.ys-heroscene__stage {
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
}

.ys-heroscene__frame {
  position: absolute;
  top: 50%;
  left: 50%;
  width: max(100vw, calc(100svh * 1440 / 810));
  height: max(100svh, calc(100vw * 810 / 1440));
  transform: translate(-50%, -50%);
}

.ys-heroscene__layer {
  position: absolute;
  display: block;
  will-change: transform;
}

/* Geometry read straight off the source artwork transforms — do not round.
   Changing a number here breaks registration between the hands. */
.ys-heroscene__layer--bg {
  left: 0;
  top: -0.0266%;
  width: 100%;
  height: 100.0925%;
}

.ys-heroscene__layer--left {
  left: 0.0266%;
  top: 44.5267%;
  width: 47.1779%;
  height: 55.4465%;
}

.ys-heroscene__layer--right {
  left: 51.1358%;
  top: 0;
  width: 48.8542%;
  height: 67.5203%;
}

/* Flat ink veil. Not a shadow and not a gradient — it is the brand's own
   dark-section pairing, and it is the only thing that carries paper type
   over a photograph at AA. 0.62 measures 4.6:1 against the scene's
   brightest pixel. */
.ys-heroscene__veil {
  position: absolute;
  inset: 0;
  z-index: 1;
  background: var(--ys-ink);
  opacity: 0.62;
  pointer-events: none;
}

.ys-heroscene__content {
  position: relative;
  z-index: 2;
  flex: none;
  width: 100%;
  padding-block: clamp(3rem, 9vw, 7.5rem);
  text-align: center;
}

.ys-heroscene__content h1 {
  max-width: 18ch;
  margin-inline: auto;
  color: var(--ys-paper);
}

.ys-heroscene__content .ys-lead {
  max-width: 54ch;
  margin-inline: auto;
  margin-bottom: var(--ys-space-4);
  color: var(--ys-paper);
}

.ys-heroscene__content .ys-hero__cta {
  justify-content: center;
}

/* Scroll cue. One ornament, and it leaves once the visitor has scrolled. */
.ys-heroscene__cue {
  position: absolute;
  z-index: 2;
  bottom: var(--ys-space-4);
  left: 50%;
  transform: translateX(-50%);
  margin: 0;
  color: var(--ys-violet-300);
  font-family: var(--ys-label);
  font-size: 0.8125rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

/* Only the JS path hides anything. If the script never runs the headline is
   already visible — which is also what a crawler sees. */
.ys-heroscene[data-scene="ready"] .ys-heroscene__content {
  opacity: 0;
}

@media (prefers-reduced-motion: reduce) {
  .ys-heroscene[data-scene="ready"] .ys-heroscene__content {
    opacity: 1;
  }
  .ys-heroscene__cue {
    display: none;
  }
}

/* --------------------------------------------------------------------------
   11 · Grids and cards
   -------------------------------------------------------------------------- */

.ys-grid {
  display: grid;
  gap: var(--ys-space-4);
}

@media (min-width: 48rem) {
  .ys-grid--2 {
    grid-template-columns: repeat(2, 1fr);
  }
  .ys-grid--3 {
    grid-template-columns: repeat(3, 1fr);
  }
}

@media (min-width: 48rem) and (max-width: 61.99rem) {
  .ys-grid--3 > :last-child:nth-child(odd) {
    grid-column: 1 / -1;
    max-width: calc(50% - var(--ys-space-2));
  }
}

.ys-card {
  background: var(--ys-violet-100);
  border: var(--ys-hairline) solid var(--ys-violet-300);
  border-radius: var(--ys-radius);
  padding: var(--ys-space-4);
  display: flex;
  flex-direction: column;
}

.ys-card h3,
.ys-card h4 {
  margin-bottom: var(--ys-space-2);
}

.ys-card p {
  max-width: none;
}

.ys-card > :last-child {
  margin-bottom: 0;
}

/* Only a trailing link is floated to the card foot, so a row of cards keeps
   its links on one line. Body copy stays under its heading. */
.ys-card > .ys-link:last-child {
  margin-top: auto;
  padding-top: var(--ys-space-2);
}

.ys-card--plain {
  background: transparent;
  border: 0;
  border-top: 2px solid var(--ys-violet-700);
  border-radius: 0;
  padding: var(--ys-space-3) 0 0;
}

/* In a plain card the heading is a lead-in sentence, not a section title. */
.ys-card--plain h3 {
  font-size: var(--ys-step-2);
  line-height: 1.3;
  letter-spacing: 0;
}

/* Cards in a 3-up grid are narrow enough to drop the measure cap. Plain cards
   are also used full-width in a stack, where the cap has to come back or the
   line runs past 100 characters. */
.ys-card--plain p {
  max-width: var(--ys-measure);
}

.ys-dark .ys-card {
  background: transparent;
  border-color: var(--ys-violet-500);
}

.ys-dark .ys-card--plain {
  border-top-color: var(--ys-brass);
  border-inline: 0;
  border-bottom: 0;
}

/* A numbered step — used for the method and the "what happens next" blocks. */
.ys-step__n {
  font-family: var(--ys-label);
  font-size: var(--ys-step--1);
  color: var(--ys-violet-500);
  display: block;
  margin-bottom: var(--ys-space-1);
}

.ys-dark .ys-step__n {
  color: var(--ys-brass);
}

/* --------------------------------------------------------------------------
   12 · Tables — service standards, coverage, comparison
   -------------------------------------------------------------------------- */

.ys-tablewrap {
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
}

.ys-table {
  width: 100%;
  min-width: 34rem;
  border-collapse: collapse;
  text-align: left;
}

.ys-table caption {
  text-align: left;
  font-size: var(--ys-step--1);
  color: var(--ys-violet-500);
  padding-bottom: var(--ys-space-2);
}

.ys-table th,
.ys-table td {
  padding: var(--ys-space-3) var(--ys-space-2);
  border-bottom: var(--ys-hairline) solid var(--ys-violet-300);
  vertical-align: top;
}

.ys-table th {
  font-family: var(--ys-body);
  font-size: var(--ys-step--2);
  font-weight: 500;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ys-violet-500);
}

.ys-table tbody th {
  font-size: var(--ys-step-0);
  letter-spacing: 0;
  text-transform: none;
  color: var(--ys-ink);
  font-weight: 600;
  width: 34%;
}

.ys-table td {
  font-size: var(--ys-step-0);
}

.ys-table tbody tr:last-child th,
.ys-table tbody tr:last-child td {
  border-bottom: 0;
}

.ys-dark .ys-table th,
.ys-dark .ys-table td {
  border-bottom-color: var(--ys-violet-500);
}

.ys-dark .ys-table thead th {
  color: var(--ys-violet-300);
}

.ys-dark .ys-table tbody th {
  color: var(--ys-paper);
}

/* Comparison table — "most outsourcing" vs "Yosef Sonlace".
   The muted column is violet-500 on light (6.02 on paper, 5.53 on violet-100).
   Violet-300 fails as text on any light surface — it is a border and a
   dark-background colour, nothing else. */
.ys-table--compare tbody th {
  font-weight: 400;
  color: var(--ys-violet-500);
  width: 50%;
}

.ys-dark .ys-table--compare tbody th {
  color: var(--ys-violet-300);
}

.ys-table--compare tbody td {
  font-weight: 500;
  width: 50%;
}

/* --------------------------------------------------------------------------
   13 · Lists
   -------------------------------------------------------------------------- */

.ys-list {
  list-style: none;
  margin: 0 0 var(--ys-space-3);
  padding: 0;
  max-width: var(--ys-measure);
}

.ys-list li {
  position: relative;
  padding-left: 1.5rem;
  margin-bottom: var(--ys-space-2);
}

.ys-list li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0.65em;
  width: 0.5rem;
  height: 2px;
  background: var(--ys-brass-600);
}

.ys-dark .ys-list li::before {
  background: var(--ys-brass);
}

.ys-deflist {
  margin: 0;
}

.ys-deflist dt {
  font-weight: 600;
  margin-top: var(--ys-space-3);
}

.ys-deflist dd {
  margin: var(--ys-space-1) 0 0;
  max-width: var(--ys-measure);
}

/* --------------------------------------------------------------------------
   14 · Boundary block — what we own / stays with you / when we get it wrong
   -------------------------------------------------------------------------- */

.ys-boundary {
  display: grid;
  gap: var(--ys-space-4);
}

@media (min-width: 55rem) {
  .ys-boundary {
    grid-template-columns: repeat(3, 1fr);
  }
}

.ys-boundary__col {
  border-top: 2px solid var(--ys-violet-700);
  padding-top: var(--ys-space-3);
}

.ys-boundary__col:last-child {
  border-top-color: var(--ys-brass-600);
}

.ys-dark .ys-boundary__col {
  border-top-color: var(--ys-violet-300);
}

.ys-dark .ys-boundary__col:last-child {
  border-top-color: var(--ys-brass);
}

.ys-boundary__col h3 {
  font-size: var(--ys-step-2);
  margin-bottom: var(--ys-space-2);
}

.ys-boundary__col p {
  max-width: none;
  margin-bottom: 0;
}

/* --------------------------------------------------------------------------
   15 · FAQ (details/summary — no JS required)
   -------------------------------------------------------------------------- */

.ys-faq {
  max-width: var(--ys-measure);
  border-top: var(--ys-hairline) solid var(--ys-violet-300);
}

.ys-faq details {
  border-bottom: var(--ys-hairline) solid var(--ys-violet-300);
}

.ys-faq summary {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--ys-space-3);
  min-height: 3.5rem;
  padding: var(--ys-space-2) 0;
  cursor: pointer;
  font-family: var(--ys-display);
  font-weight: 500;
  font-size: var(--ys-step-1);
  line-height: 1.35;
  list-style: none;
}

.ys-faq summary::-webkit-details-marker {
  display: none;
}

.ys-faq summary::after {
  content: "+";
  font-family: var(--ys-label);
  font-size: var(--ys-step-1);
  color: var(--ys-violet-500);
  flex: none;
}

.ys-faq details[open] summary::after {
  content: "\2212";
}

.ys-faq summary:hover {
  color: var(--ys-violet-700);
}

.ys-faq__a {
  padding-bottom: var(--ys-space-3);
}

.ys-faq__a > :last-child {
  margin-bottom: 0;
}

/* --------------------------------------------------------------------------
   16 · Forms
   -------------------------------------------------------------------------- */

.ys-form {
  max-width: 34rem;
}

.ys-field {
  margin-bottom: var(--ys-space-3);
}

.ys-field label {
  display: block;
  font-size: var(--ys-step--1);
  font-weight: 500;
  margin-bottom: 0.375rem;
}

.ys-field__hint {
  display: block;
  font-size: var(--ys-step--1);
  font-weight: 400;
  color: var(--ys-violet-500);
  margin-bottom: 0.375rem;
}

.ys-field input,
.ys-field select,
.ys-field textarea {
  width: 100%;
  min-height: 3rem;
  padding: 0.75rem;
  background: var(--ys-surface);
  border: var(--ys-hairline) solid var(--ys-violet-500);
  border-radius: var(--ys-radius);
  color: var(--ys-ink);
  font-family: var(--ys-body);
  font-size: var(--ys-step-0);
  line-height: 1.5;
}

.ys-field textarea {
  min-height: 8rem;
  resize: vertical;
}

.ys-field input:hover,
.ys-field select:hover,
.ys-field textarea:hover {
  border-color: var(--ys-violet-700);
}

.ys-field input[aria-invalid="true"],
.ys-field select[aria-invalid="true"],
.ys-field textarea[aria-invalid="true"] {
  border-color: var(--ys-error);
  border-width: 2px;
}

.ys-field__error {
  display: none;
  margin-top: 0.375rem;
  font-size: var(--ys-step--1);
  font-weight: 500;
  color: var(--ys-error);
}

.ys-field__error[data-shown="true"] {
  display: block;
}

.ys-form__status {
  margin-top: var(--ys-space-3);
  padding: var(--ys-space-3);
  border-left: 3px solid var(--ys-success);
  background: var(--ys-violet-100);
  display: none;
}

.ys-form__status[data-state="ok"],
.ys-form__status[data-state="error"],
.ys-form__status[data-state="unconfigured"] {
  display: block;
}

.ys-form__status[data-state="error"] {
  border-left-color: var(--ys-error);
}

.ys-form__status[data-state="unconfigured"] {
  border-left-color: var(--ys-warning);
}

.ys-form__status p {
  margin: 0;
}

/* --------------------------------------------------------------------------
   17 · Placeholders and build flags — DEVELOPMENT ONLY.
   Every one is visible on purpose. A marked placeholder is honest; a stock
   photo is not. Delete the block, never the marking.
   To hide all of them for a client preview: body[data-flags="off"]
   -------------------------------------------------------------------------- */

.ys-ph {
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: var(--ys-space-1);
  min-height: 14rem;
  padding: var(--ys-space-4);
  background: var(--ys-violet-100);
  border: 2px dashed var(--ys-violet-500);
  border-radius: var(--ys-radius);
  color: var(--ys-violet-700);
}

.ys-ph__label {
  font-family: var(--ys-body);
  font-size: var(--ys-step--2);
  font-weight: 500;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

.ys-ph p {
  margin: 0;
  font-size: var(--ys-step--1);
  max-width: 48ch;
}

.ys-ph--portrait {
  min-height: 16rem;
  aspect-ratio: 4 / 5;
}

.ys-flag {
  margin: var(--ys-space-4) 0;
  padding: var(--ys-space-3) var(--ys-space-4);
  background: var(--ys-paper);
  border: var(--ys-hairline) solid var(--ys-warning);
  border-left: 4px solid var(--ys-warning);
  border-radius: var(--ys-radius);
}

.ys-flag__label {
  font-family: var(--ys-label);
  font-size: var(--ys-step--2);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ys-warning);
  margin: 0 0 var(--ys-space-1);
  max-width: none;
}

.ys-flag p:last-child,
.ys-flag ul:last-child {
  margin-bottom: 0;
}

.ys-flag .ys-list li::before {
  background: var(--ys-warning);
}

body[data-flags="off"] .ys-flag {
  display: none;
}

/* --------------------------------------------------------------------------
   18 · Footer
   -------------------------------------------------------------------------- */

/* -- The curtain ----------------------------------------------------------
   2026-08-06, second pass. The footer does not arrive from below — it is
   already there, fixed at the bottom of the viewport, and the page scrolls up
   off it. The last section of the page is the curtain and the footer is what
   is behind it.

   Three parts, and all three are needed:

     .ys-page          one opaque plane holding everything above the footer.
                       Without its own background the fixed footer shows
                       through the entire page.
     .ys-footer-slot   reserves the scroll distance the fixed footer occupies,
                       since a fixed element takes no space in the flow.
     .ys-footer        fixed, at z-index 0, under the plane at z-index 1.

   It is off until ys.js turns it on, and ys.js only turns it on when the
   footer is comfortably shorter than the viewport. A fixed block taller than
   the window can never be scrolled to — its top would be unreachable — and
   that is a real risk on a phone, where the two cards stack. Off, the page is
   exactly the in-flow footer it was before, which is also what a visitor with
   no JavaScript gets. */

.ys-page {
  position: relative;
  z-index: 1;
  background: var(--ys-paper);
}

.ys-footer-slot {
  display: none;
  pointer-events: none;
}

[data-ys-curtain="on"] .ys-footer-slot {
  display: block;
}

[data-ys-curtain="on"] .ys-footer {
  position: fixed;
  inset-inline: 0;
  bottom: 0;
  z-index: 0;
}

/* The bottom edge of the curtain, rounded as it is in the reference so the
   last section reads as a panel lifting rather than the page simply ending.
   Applied to the section itself, not to .ys-page: clipping the plane would
   need overflow:hidden on it, and that kills the sticky positioning the
   pinned homepage sections depend on. */
[data-ys-curtain="on"] .ys-page main > :last-child {
  border-end-start-radius: var(--ys-footer-radius);
  border-end-end-radius: var(--ys-footer-radius);
  overflow: hidden;
}

/* Rebuilt 2026-08-06 on the footer in the supplied recording. Two cards on a
   paper ground, then the email set enormous under both.

   The ground is a shade under paper so the white panel reads as a card and
   not as the page. It is the only place on the site with a surface tint
   below paper, and it is 1.5% — enough to separate two light planes, not
   enough to read as a second colour. */
.ys-footer {
  --ys-footer-ground: #efecf3;

  position: relative;
  background: var(--ys-footer-ground);
  border-top: var(--ys-hairline) solid var(--ys-violet-100);
  /* Tighter than --ys-section, and that is structural. The curtain only runs
     while the footer is shorter than the window; every block of padding in
     here is a laptop that falls back to a static footer instead. Trimmed
     again on 2026-08-06 to bring a 1440x700 window — an ordinary laptop with
     browser chrome — back inside the threshold. */
  padding-block: clamp(2.25rem, 4vw, 2.5rem) 0;
  overflow: hidden;
}

.ys-footer__cards {
  display: grid;
  gap: var(--ys-space-3);
}

/* The mark column is content-width now, not a 32% fraction. It held 0.47fr
   while the violet was a CSS block that had to fill a card; the block is the
   approved file itself as of 2026-08-06, so it carries its own 127:182 shape
   and anything wider than that is dead ground. The panel takes the rest. */
@media (min-width: 55rem) {
  .ys-footer__cards {
    grid-template-columns: auto 1fr;
    align-items: stretch;
  }
}

/* No background, no padding, no radius. BRAND MARK.svg *is* the brand-colour
   block — violet card, paper border, paper hands — so wrapping it in a violet
   div printed a border inside a block. The cell only positions it now. */
.ys-footer__mark-card {
  display: flex;
  align-items: center;
  justify-content: flex-start;
}

/* The height has to be an explicit length, and that is not a style choice.
   The mark column is an `auto` track, so the grid sizes it from the image's
   own width — and with `height: 100%` the image reports its intrinsic 482px,
   which then makes it 687px tall and drags the whole row up with it. Measured
   at 687.1px before this was pinned. A definite height breaks the loop: width
   follows from the 127:182 ratio, the track lands at 0.70 of the height, and
   the row is set by the panel as intended.

   17.9375rem is the 287px Miguel marked up. The panel computes within a pixel
   or two of the same figure, so any mismatch is centred out and invisible.

   Below 55rem the cards stack, so the mark's height is added to the panel's
   rather than sitting beside it, and the 12.5rem cap is what keeps that stack
   inside the window. Left on 30vw it reached 264px at 879 — the widest the
   stack ever gets — and put the footer at 99.8% of a 730px window, which is
   over the curtain's threshold and drops the whole effect one pixel short of
   the breakpoint. Capped, 879 measures 91% and the curtain runs. */
.ys-footer__mark {
  display: block;
  width: auto;
  height: clamp(9rem, 30vw, 12.5rem);
  margin: 0;
}

@media (min-width: 55rem) {
  .ys-footer__mark {
    height: 17.9375rem;
  }
}

/* Padding and gap both came down on 2026-08-06 (third pass) — 32px each to
   24 and 20 — as part of taking the card row from 352px to the 287px Miguel
   marked up. The row height is the panel's height; every value in here is a
   pixel of it. */
.ys-footer__panel {
  display: flex;
  flex-direction: column;
  gap: 1.25rem;
  padding: clamp(var(--ys-space-2), 3vw, var(--ys-space-3));
  background: var(--ys-surface);
  border-radius: var(--ys-footer-radius);
}

.ys-footer__cols {
  display: grid;
  gap: var(--ys-space-4);
}

@media (min-width: 34rem) {
  .ys-footer__cols {
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: var(--ys-space-5);
  }
}

/* 2.25rem, down from 2.5 and 2.75 before that, on 2026-08-06 (third pass).
   36px is still half again the 24px minimum target size, and the Company
   column carries four rows, so every 4px here is 16px off the card row. */
.ys-footer li a,
.ys-footer address a,
.ys-footer__legal a {
  min-height: 2.25rem;
  line-height: 2.25rem;
}

/* Contact column: text links with an outward arrow, per the reference. */
.ys-footer__out span {
  color: var(--ys-violet-500);
}

.ys-footer h2 {
  font-family: var(--ys-body);
  font-size: var(--ys-step--2);
  font-weight: 500;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ys-violet-500);
  margin-bottom: var(--ys-space-1);
}

.ys-footer ul {
  list-style: none;
  margin: 0;
  padding: 0;
}

/* Height is set once, at 2.5rem, in the target-size rule above. It used to be
   restated here at 2.75rem — same specificity, later in the file, so it won
   and the 2026-08-06 trim never reached the page. Do not put a height back in
   this block; change the rule above instead. */
.ys-footer li a,
.ys-footer address a {
  display: inline-block;
  color: var(--ys-ink);
  text-decoration: none;
}

.ys-footer li a:hover,
.ys-footer address a:hover {
  color: var(--ys-violet-700);
  text-decoration: underline;
}

.ys-footer address {
  font-style: normal;
  font-size: var(--ys-step--1);
  line-height: 1.7;
}

.ys-footer__legal {
  margin-top: auto;
  border-top: var(--ys-hairline) solid var(--ys-violet-100);
  padding-top: var(--ys-space-2);
  display: flex;
  flex-wrap: wrap;
  gap: var(--ys-space-2) var(--ys-space-4);
  align-items: center;
  justify-content: space-between;
  font-size: var(--ys-step--1);
  color: var(--ys-violet-500);
}

.ys-footer__legal ul {
  display: flex;
  flex-wrap: wrap;
  gap: var(--ys-space-3);
}

/* Height from the target-size rule above, same as the column links. */
.ys-footer__legal a {
  display: inline-block;
  color: var(--ys-violet-500);
}

/* -- The oversized email --------------------------------------------------
   Full bleed, one line, never wrapping, whole — nothing clipped. The earlier
   pass cut the descender against the page edge the way the reference does;
   Miguel asked on 2026-08-06 for the whole string to show with space under it,
   so the negative margin and the clip are gone and the block carries its own
   bottom padding.

   The size is solved from the string, not guessed. "info@yosefsonlace.com"
   measures 11.48em wide in Nourd bold at -0.03em, read off the rendered page.
   The line has 94vw to sit in — 3vw of gutter each side — less about 15px for
   a desktop scrollbar, which 100vw counts and the layout does not. Dividing
   that by 11.75 (11.48 plus air) is the largest size at which it still fits.
   Change the address and this number changes: measure it, do not nudge it.

   Opacity, and the open item on it. 0.58 was the AA floor: violet-700 at 0.58
   over the footer ground measures 3.18:1, and 3:1 is the minimum for text this
   size. Miguel asked on 2026-08-06 for it fainter, so it is 0.40 — 2.11:1,
   which does not meet AA. That is a deliberate, recorded exception and it is
   the only contrast failure on the site.

   It closes one of two ways, not by nudging the number:
     - put info@yosefsonlace.com back in the footer contact column at full
       violet-700, then make this one decorative — aria-hidden, not a link — or
     - return this to 0.58.
   There is no third option. The faintness *is* the contrast; any value that
   looks meaningfully lighter than 0.58 fails, whichever colour it is set in.
   Checked: ink at 0.45 measures 3.01 but renders no lighter than violet at
   0.58, so switching colour buys nothing.

   ys.js upgrades the click to a clipboard copy; with scripting off it opens a
   mail client, which is the behaviour a big email address promises. */
.ys-footer__mailwrap {
  position: relative;
  margin-top: clamp(var(--ys-space-3), 3vw, 1.5rem);
  padding-bottom: clamp(var(--ys-space-3), 3vw, 1.25rem);
}

.ys-footer__mail {
  display: block;
  margin: 0;
  padding-inline: 3vw;
  color: var(--ys-violet-700);
  opacity: 0.4;
  font-family: var(--ys-display);
  font-weight: 700;
  font-size: calc((94vw - 1.5rem) / 11.75);
  line-height: 1;
  letter-spacing: -0.03em;
  white-space: nowrap;
  text-decoration: none;
  transition: opacity 200ms ease-out;
}

.ys-footer__mail:hover {
  opacity: 0.66;
}

.ys-footer__mail:focus-visible {
  opacity: 1;
  outline-offset: -4px;
}

/* The pill that follows the pointer. Positioned by ys.js in the wrapper's own
   coordinates, so it never leaves the block and never causes a scroll. */
.ys-footer__tip {
  position: absolute;
  top: 0;
  left: 0;
  z-index: 2;
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  padding: 0.45rem 0.8rem;
  background: var(--ys-ink);
  color: var(--ys-paper);
  border-radius: 999px;
  font-family: var(--ys-label);
  font-size: var(--ys-step--2);
  line-height: 1.2;
  white-space: nowrap;
  pointer-events: none;
  opacity: 0;
  transform: translate(-50%, -50%) scale(0.92);
  transition:
    opacity 140ms ease-out,
    transform 140ms var(--ys-ease-hover);
}

.ys-footer__tip[data-on="true"] {
  opacity: 1;
  transform: translate(-50%, -50%) scale(1);
}

.ys-footer__tipicon {
  flex: none;
}

@media (prefers-reduced-motion: reduce) {
  .ys-footer__mail,
  .ys-footer__tip {
    transition: none;
  }
}

/* --------------------------------------------------------------------------
   19 · Breadcrumb
   -------------------------------------------------------------------------- */

.ys-crumb {
  padding-top: var(--ys-space-4);
  font-size: var(--ys-step--1);
  color: var(--ys-violet-500);
}

.ys-crumb ol {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  list-style: none;
  margin: 0;
  padding: 0;
}

.ys-crumb li + li::before {
  content: "/";
  margin-right: 0.5rem;
  color: var(--ys-violet-300);
}

.ys-crumb a {
  display: inline-block;
  min-height: 2.75rem; /* 44px — the target rule applies here too */
  line-height: 2.75rem;
  color: var(--ys-violet-500);
  text-decoration: none;
}

.ys-crumb a:hover {
  color: var(--ys-violet-700);
  text-decoration: underline;
}

.ys-crumb ol {
  align-items: center;
}

.ys-crumb li > span {
  display: inline-block;
  line-height: 2.75rem;
}

/* --------------------------------------------------------------------------
   20 · Motion — fade and short translate only, under 250ms.
   -------------------------------------------------------------------------- */

@media (prefers-reduced-motion: no-preference) {
  .ys-reveal {
    opacity: 0;
    transform: translateY(8px);
  }
  .ys-reveal[data-shown="true"] {
    animation: ys-fade 220ms ease-out both;
  }
}

@keyframes ys-fade {
  from {
    opacity: 0;
    transform: translateY(8px);
  }
  to {
    opacity: 1;
    transform: none;
  }
}

/* --------------------------------------------------------------------------
   21 · Editorial layout — the long-page grammar
   Composition taken from the Meridian services reference (2026-08-04): a dark
   full-bleed lede, alternating paper / tint / ink bands, chamfered panels, an
   offset dark panel, and a hairline-ruled tile row. The reference's ornaments
   are NOT taken — no serif display, no amber, no numbers we cannot produce.
   The eyebrow stays the brass rule it already is; that is the one ornament.
   -------------------------------------------------------------------------- */

/* Chamfered corner. The one shape borrowed from the reference, applied to
   panels and image frames only. Never to the mark, never to a button. */
.ys-notch {
  clip-path: polygon(0 0, calc(100% - 2.25rem) 0, 100% 2.25rem, 100% 100%, 0 100%);
}

.ys-notch--bl {
  clip-path: polygon(0 0, 100% 0, 100% 100%, 2.25rem 100%, 0 calc(100% - 2.25rem));
}

/* Dark lede — a full-width ink band that opens a page. Replaces the
   reference's photographic hero, which we have no honest photograph for. */
.ys-lede {
  background: var(--ys-ink);
  color: var(--ys-paper);
  padding-block: clamp(3.5rem, 10vw, 8rem) clamp(3rem, 8vw, 6rem);
}

.ys-lede h1 {
  color: var(--ys-paper);
  max-width: 16ch;
}

.ys-lede .ys-lead {
  color: var(--ys-paper);
  max-width: 52ch;
  margin-bottom: var(--ys-space-4);
}

.ys-lede .ys-eyebrow {
  color: var(--ys-violet-300);
  border-top-color: var(--ys-brass);
}

.ys-lede .ys-btn {
  background: var(--ys-paper);
  color: var(--ys-ink);
}

.ys-lede .ys-btn:hover {
  background: var(--ys-violet-100);
  color: var(--ys-ink);
}

.ys-lede .ys-link {
  color: var(--ys-brass);
}

.ys-lede .ys-link:hover {
  color: var(--ys-paper);
}

.ys-lede :focus-visible {
  outline-color: var(--ys-brass);
}

/* Commitments strip — sits under the lede where the reference puts its
   results numbers. These are published commitments, not performance claims:
   what we undertake to do, checkable on day one. No client data goes here. */
.ys-commit {
  background: var(--ys-violet-100);
  padding-block: clamp(2.5rem, 5vw, 3.5rem);
}

.ys-commit__grid {
  display: grid;
  gap: var(--ys-space-4);
}

@media (min-width: 48rem) {
  .ys-commit__grid {
    grid-template-columns: 1.1fr repeat(3, 1fr);
    gap: var(--ys-space-5);
    align-items: start;
  }
}

.ys-commit h2 {
  font-size: clamp(1.45rem, 3vw, var(--ys-step-3));
  margin: 0;
  max-width: 14ch;
}

.ys-commit__item {
  border-top: 2px solid var(--ys-brass-600);
  padding-top: var(--ys-space-2);
}

.ys-commit__v {
  display: block;
  font-family: var(--ys-display);
  font-weight: 500;
  font-size: var(--ys-step-2);
  line-height: 1.2;
  margin-bottom: var(--ys-space-1);
}

.ys-commit__k {
  display: block;
  font-size: var(--ys-step--2);
  font-weight: 500;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ys-violet-500);
  margin: 0;
  max-width: none;
}

/* Feature — image one side, argument the other. Alternates with --flip. */
.ys-feature {
  display: grid;
  gap: var(--ys-space-5);
  align-items: center;
}

@media (min-width: 55rem) {
  .ys-feature {
    grid-template-columns: 1fr 1fr;
    gap: var(--ys-space-6);
  }
  .ys-feature--flip .ys-feature__media {
    order: 2;
  }
}

.ys-feature__body > :last-child {
  margin-bottom: 0;
}

.ys-feature h2 {
  max-width: 14ch;
}

/* Tiles — a hairline-ruled row. The rule under each tile is the reference's
   move and it costs nothing: it groups the column without adding a box. */
.ys-tiles {
  display: grid;
  gap: var(--ys-space-4);
}

@media (min-width: 48rem) {
  .ys-tiles {
    grid-template-columns: repeat(3, 1fr);
    gap: var(--ys-space-4);
  }
}

.ys-tile {
  display: flex;
  flex-direction: column;
  border-bottom: var(--ys-hairline) solid var(--ys-violet-300);
  padding-bottom: var(--ys-space-3);
}

.ys-tile__media {
  margin-bottom: var(--ys-space-3);
}

.ys-tile h3 {
  font-size: var(--ys-step-2);
  margin-bottom: var(--ys-space-2);
}

.ys-tile p {
  max-width: none;
}

.ys-tile > .ys-link:last-child {
  margin-top: auto;
}

/* Offset panel — a dark card set against a light band, half media, half
   argument. Where the reference sells a gated download, we point at the
   method, which is the only thing we have to give away and the whole
   substitute for a client list. */
.ys-panel {
  display: grid;
  gap: 0;
  background: var(--ys-ink);
  color: var(--ys-paper);
  overflow: hidden;
}

@media (min-width: 55rem) {
  .ys-panel {
    grid-template-columns: 0.85fr 1.15fr;
  }
}

.ys-panel__media {
  padding: var(--ys-space-4);
}

.ys-panel__media .ys-ph {
  background: var(--ys-violet-900);
  border-color: var(--ys-violet-500);
  color: var(--ys-violet-300);
  height: 100%;
}

.ys-panel__body {
  padding: clamp(var(--ys-space-4), 4vw, var(--ys-space-6));
}

.ys-panel__body h2,
.ys-panel__body h3 {
  color: var(--ys-paper);
}

.ys-panel__body .ys-eyebrow {
  color: var(--ys-violet-300);
  border-top-color: var(--ys-brass);
}

.ys-panel__body .ys-btn {
  background: var(--ys-paper);
  color: var(--ys-ink);
}

.ys-panel__body .ys-btn:hover {
  background: var(--ys-violet-100);
}

.ys-panel__body > :last-child {
  margin-bottom: 0;
}

.ys-panel__body :focus-visible {
  outline-color: var(--ys-brass);
}

/* Button with the arrow set in its own square — the reference's CTA shape.
   Kept to one per section, same as every other button on this site. */
.ys-btn--arrow {
  padding-right: 0.5rem;
  gap: var(--ys-space-3);
}

.ys-btn--arrow::after {
  content: "\2197";
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2rem;
  height: 2rem;
  flex: none;
  background: var(--ys-paper);
  color: var(--ys-ink);
  font-size: var(--ys-step--1);
  line-height: 1;
}

.ys-lede .ys-btn--arrow::after,
.ys-panel__body .ys-btn--arrow::after,
.ys-dark .ys-btn--arrow::after {
  background: var(--ys-ink);
  color: var(--ys-paper);
}

/* --------------------------------------------------------------------------
   22 · Careers — rebuilt 2026-08-04 on the Mito Group career hub.

   Structure, proportion and interaction are taken from mito.group/career and
   the saved snapshot in Output/web/INSPIRATIONS/. Their page is a dark tonal
   ladder of plums with one warm accent; ours is the same arrangement in the
   locked palette:

     #351B2A  hero, hiring, gallery box   ->  --ys-ink          #100C16
     #4B2C3E  lead-in and benefits band   ->  --ys-violet-900   #2A1D3D
     #6B485C  the open-positions band     ->  --ys-violet-700   #443062
     #E44718  the orange accent           ->  --ys-brass        #B8862B

   Text on ink and violet-900 is paper, with violet-300 secondary and brass
   accents. **On violet-700 the only permitted type colour is paper** —
   brand_context.md §12 is explicit that nothing else passes on it, so the
   open-roles band carries no secondary or brass text anywhere.
   -------------------------------------------------------------------------- */

/* Two display steps above the standard scale. The reference runs its hero at
   71px and its section headings at 47.6px; --ys-step-6 tops out at 61. */
:root {
  --ys-step-7: 4.4rem; /* 70 — hero */
  --ys-step-4h: 2.95rem; /* 47 — section heads */
}

/* Every band on this page is dark, so links, focus rings and buttons all need
   the dark-surface treatment without each section opting in individually. */
.ys-band {
  background: var(--ys-ink);
  color: var(--ys-paper);
}

.ys-band h1,
.ys-band h2,
.ys-band h3,
.ys-band h4,
.ys-band h5,
.ys-band h6 {
  color: var(--ys-paper);
}

.ys-band--900 {
  background: var(--ys-violet-900);
}

.ys-band--700 {
  background: var(--ys-violet-700);
}

.ys-band :focus-visible {
  outline-color: var(--ys-brass);
}

/* Anything that carries its own surface — a pill, a chip — is excluded here,
   because the band's background is not the one its text is measured against. */
.ys-band a:not(.ys-btn):not(.ys-pill):not(.ys-tag) {
  color: var(--ys-brass);
}

.ys-band a:not(.ys-btn):not(.ys-pill):not(.ys-tag):hover {
  color: var(--ys-paper);
}

/* Nothing except paper passes on violet-700. */
.ys-band--700 a:not(.ys-btn):not(.ys-pill):not(.ys-tag),
.ys-band--700 a:not(.ys-btn):not(.ys-pill):not(.ys-tag):hover {
  color: var(--ys-paper);
}

.ys-band--700 :focus-visible {
  outline-color: var(--ys-paper);
}

/* The reference's eyebrow: small, bold, accent-coloured, no rule. This is the
   one place the brass rule above a label is dropped — the whole page is dark
   and the rule reads as a scratch rather than an ornament. */
.ys-kicker {
  display: block;
  font-family: var(--ys-body);
  font-size: 1rem;
  font-weight: 600;
  line-height: 1.4;
  color: var(--ys-brass);
  margin: 0 0 var(--ys-space-3);
  max-width: none;
}

.ys-band--700 .ys-kicker {
  color: var(--ys-paper);
}

/* Pill buttons, 30px radius in the reference. Primary is a violet fill on ink;
   on the violet band it inverts to paper so it stays visible. */
.ys-pill {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.625rem;
  min-height: 3.5rem;
  padding: 0.75rem 1.5rem;
  background: var(--ys-violet-700);
  color: var(--ys-paper);
  font-family: var(--ys-body);
  font-size: 1rem;
  font-weight: 600;
  line-height: 1.4;
  text-decoration: none;
  border: var(--ys-hairline) solid transparent;
  border-radius: var(--ys-radius);
  cursor: pointer;
  transition: background-color 180ms ease-out, color 180ms ease-out;
}

.ys-pill:hover {
  background: var(--ys-paper);
  color: var(--ys-ink);
}

.ys-band--700 .ys-pill {
  background: var(--ys-paper);
  color: var(--ys-ink);
}

.ys-band--700 .ys-pill:hover {
  background: transparent;
  color: var(--ys-paper);
  border-color: var(--ys-paper);
}

/* Tertiary is text in a pill's clothing — no fill, no border. */
.ys-pill--ghost {
  background: transparent;
  color: var(--ys-brass);
  padding-inline: 0.25rem;
}

.ys-pill--ghost:hover {
  background: transparent;
  color: var(--ys-paper);
  text-decoration: underline;
  text-underline-offset: 0.3em;
}

.ys-band--700 .ys-pill--ghost {
  background: transparent;
  color: var(--ys-paper);
}

.ys-pill__arrow {
  font-size: 1.125rem;
  line-height: 1;
}

.ys-pills {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--ys-space-3);
  margin: 0;
  max-width: none;
}

/* -- Hero ------------------------------------------------------------------
   800px in the reference, opening on 200px of top padding. */

.ys-chero {
  padding-block: clamp(3.5rem, 11vw, 8.5rem) clamp(3rem, 8vw, 6rem);
}

.ys-chero__grid {
  display: grid;
  gap: var(--ys-space-5);
  align-items: center;
}

@media (min-width: 62rem) {
  .ys-chero__grid {
    grid-template-columns: 1fr 1fr;
    gap: var(--ys-space-6);
  }
}

.ys-chero h1 {
  font-weight: 500;
  font-size: clamp(2.5rem, 6.6vw, var(--ys-step-7));
  line-height: 1.15;
  letter-spacing: -0.02em;
  max-width: 13ch;
  margin-bottom: var(--ys-space-3);
}

.ys-chero__desc {
  color: var(--ys-violet-300);
  max-width: 46ch;
  margin-bottom: var(--ys-space-4);
}

.ys-chero__media .ys-ph {
  background: var(--ys-violet-900);
  border-color: var(--ys-violet-500);
  color: var(--ys-violet-300);
  min-height: 20rem;
}

/* -- Lead-in band above the roles ------------------------------------------ */

.ys-cintro {
  padding-block: clamp(3rem, 7vw, 5rem);
}

.ys-cintro__grid {
  display: grid;
  gap: var(--ys-space-5);
}

@media (min-width: 62rem) {
  .ys-cintro__grid {
    grid-template-columns: 1fr 1fr;
    gap: var(--ys-space-6);
  }
}

.ys-cintro h2 {
  font-weight: 500;
  font-size: clamp(1.6rem, 3.4vw, 2.2rem);
  line-height: 1.25;
  letter-spacing: -0.01em;
  max-width: 20ch;
  margin: 0;
}

.ys-cintro p {
  color: var(--ys-violet-300);
  margin-bottom: var(--ys-space-4);
}

/* Anchor chips. Plain links, so they work with JavaScript off. */
.ys-tags {
  display: flex;
  flex-wrap: wrap;
  gap: 0.625rem;
  list-style: none;
  margin: 0;
  padding: 0;
}

.ys-tag {
  display: inline-flex;
  align-items: center;
  min-height: 2.75rem;
  padding: 0.25rem 1.25rem;
  background: var(--ys-violet-700);
  border-radius: var(--ys-radius);
  color: var(--ys-paper);
  font-size: var(--ys-step--1);
  font-weight: 500;
  text-decoration: none;
}

.ys-tag:hover {
  background: var(--ys-paper);
  color: var(--ys-ink);
}

/* -- The open roles, grouped by function -----------------------------------
   Two equal columns with a 20px gutter in the reference: the function on the
   left, its roles stacked on the right. */

.ys-roles {
  padding-block: clamp(3rem, 7vw, 5rem) clamp(4rem, 9vw, 6.5rem);
}

.ys-rgroup + .ys-rgroup {
  margin-top: clamp(3rem, 6vw, 4.5rem);
}

.ys-rgroup__grid {
  display: grid;
  gap: var(--ys-space-4);
}

@media (min-width: 62rem) {
  .ys-rgroup__grid {
    grid-template-columns: 1fr 1fr;
    gap: var(--ys-space-5);
  }
}

.ys-rgroup h3 {
  font-weight: 500;
  font-size: clamp(1.9rem, 4vw, var(--ys-step-4h));
  line-height: 1.15;
  letter-spacing: -0.01em;
  margin-bottom: var(--ys-space-3);
}

.ys-rgroup__info p {
  max-width: 36ch;
  margin-bottom: var(--ys-space-4);
}

.ys-rgroup__list {
  list-style: none;
  margin: 0;
  padding: 0;
  border-top: var(--ys-hairline) dashed var(--ys-violet-300);
}

.ys-rgroup__list > li {
  border-bottom: var(--ys-hairline) dashed var(--ys-violet-300);
}

/* The row itself. 40px/30px padding in the reference; the arrow leads. */
.ys-role {
  display: flex;
  align-items: baseline;
  gap: var(--ys-space-3);
  padding: var(--ys-space-4) var(--ys-space-3);
  text-decoration: none;
  transition: background-color 180ms ease-out, padding-left 180ms ease-out;
}

.ys-role:hover,
.ys-role:focus-visible {
  background: var(--ys-violet-900);
  padding-left: var(--ys-space-4);
}

.ys-role__arrow {
  flex: none;
  font-size: 1.125rem;
  line-height: 1.4;
}

.ys-role__title {
  font-family: var(--ys-display);
  font-weight: 500;
  font-size: var(--ys-step-1);
  line-height: 1.3;
}

.ys-role__note {
  display: block;
  font-family: var(--ys-body);
  font-weight: 400;
  font-size: var(--ys-step--1);
  line-height: 1.5;
  margin-top: 0.25rem;
  opacity: 0.85;
}

/* -- Open application ------------------------------------------------------ */

/* Tightened 2026-08-05 — the band was running two sections deep on a block
   that is one heading, two paragraphs and one action. It hugs its content
   now. */
.ys-open {
  padding-block: clamp(2.5rem, 5vw, 3.5rem);
}

.ys-open__grid {
  display: grid;
  gap: var(--ys-space-5);
}

@media (min-width: 62rem) {
  .ys-open__grid {
    grid-template-columns: 1fr 1fr;
    gap: var(--ys-space-6);
  }
}

.ys-open h2 {
  font-weight: 500;
  font-size: clamp(1.6rem, 3.4vw, 2.2rem);
  line-height: 1.25;
  letter-spacing: -0.01em;
  margin: 0;
  max-width: 18ch;
}

.ys-open h2 strong {
  font-weight: 500;
  color: var(--ys-brass);
}

.ys-open p {
  color: var(--ys-violet-300);
  margin-bottom: var(--ys-space-4);
}

/* -- Numbered accordion — the hiring process -------------------------------
   details/summary, so it opens with JavaScript off and every panel is
   reachable by keyboard for free. */

.ys-steps {
  padding-block: clamp(3.5rem, 8vw, 5.5rem);
}

.ys-steps h2 {
  font-weight: 500;
  font-size: clamp(1.9rem, 4vw, var(--ys-step-4h));
  line-height: 1.15;
  letter-spacing: -0.01em;
  margin-bottom: var(--ys-space-5);
  max-width: 14ch;
}

.ys-steps__list {
  border-top: var(--ys-hairline) solid var(--ys-violet-500);
}

.ys-steps__list > details {
  border-bottom: var(--ys-hairline) solid var(--ys-violet-500);
}

.ys-steps__list summary {
  display: flex;
  align-items: baseline;
  gap: var(--ys-space-3);
  padding: var(--ys-space-4) 0;
  cursor: pointer;
  list-style: none;
}

.ys-steps__list summary::-webkit-details-marker {
  display: none;
}

.ys-steps__n {
  flex: none;
  font-family: var(--ys-label);
  font-size: var(--ys-step-1);
  color: var(--ys-brass);
}

.ys-steps__t {
  flex: 1;
  font-family: var(--ys-display);
  font-weight: 500;
  font-size: clamp(1.35rem, 2.6vw, 1.75rem);
  line-height: 1.25;
  letter-spacing: -0.01em;
}

.ys-steps__list summary::after {
  content: "+";
  flex: none;
  font-family: var(--ys-label);
  font-size: var(--ys-step-2);
  line-height: 1;
  color: var(--ys-violet-300);
}

.ys-steps__list details[open] summary::after {
  content: "\2212";
}

.ys-steps__list summary:hover .ys-steps__t {
  color: var(--ys-brass);
}

.ys-steps__panel {
  padding: 0 0 var(--ys-space-4);
  max-width: var(--ys-measure);
  color: var(--ys-violet-300);
}

@media (min-width: 48rem) {
  .ys-steps__panel {
    padding-left: calc(var(--ys-step-1) + var(--ys-space-3));
  }
}

.ys-steps__panel > :last-child {
  margin-bottom: 0;
}

/* -- Vertical tabs — what working here is like -----------------------------
   The reference's benefits block: headings down the left, one panel on the
   right. Radio inputs drive it, so it needs no JavaScript at all and arrow
   keys move between tabs natively. */

.ys-tabs {
  padding-block: clamp(3.5rem, 8vw, 5.5rem);
}

.ys-tabs h2 {
  font-weight: 500;
  font-size: clamp(1.9rem, 4vw, var(--ys-step-4h));
  line-height: 1.15;
  letter-spacing: -0.01em;
  margin-bottom: var(--ys-space-5);
  max-width: 12ch;
}

.ys-tabs__grid {
  display: grid;
  gap: var(--ys-space-4);
}

@media (min-width: 55rem) {
  .ys-tabs__grid {
    grid-template-columns: 1fr 1fr;
    gap: clamp(var(--ys-space-5), 7vw, 7rem);
    align-items: start;
  }
}

.ys-tabs__list {
  display: flex;
  flex-direction: column;
  gap: 0;
  margin: 0;
  padding: 0;
  border: 0;
}

.ys-tabs__list legend {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
}

.ys-tabs__radio {
  position: absolute;
  width: 1px;
  height: 1px;
  opacity: 0;
  pointer-events: none;
}

.ys-tabs__label {
  display: flex;
  align-items: center;
  gap: var(--ys-space-2);
  min-height: 3.25rem;
  padding: 0.5rem 0;
  border-bottom: var(--ys-hairline) solid var(--ys-violet-500);
  color: var(--ys-violet-300);
  font-weight: 600;
  cursor: pointer;
  transition: color 180ms ease-out;
}

.ys-tabs__label::before {
  content: "";
  flex: none;
  width: 0.75rem;
  height: 2px;
  background: currentColor;
  transition: width 180ms ease-out;
}

.ys-tabs__label:hover {
  color: var(--ys-paper);
}

.ys-tabs__radio:checked + .ys-tabs__label {
  color: var(--ys-brass);
}

.ys-tabs__radio:checked + .ys-tabs__label::before {
  width: 1.75rem;
}

.ys-tabs__radio:focus-visible + .ys-tabs__label {
  outline: 2px solid var(--ys-brass);
  outline-offset: 3px;
}

/* Panels stack in the same cell; the checked radio reveals its own. */
.ys-tabs__panels {
  display: grid;
}

.ys-tabs__panel {
  grid-area: 1 / 1;
  visibility: hidden;
  opacity: 0;
}

.ys-tabs__panel > :last-child {
  margin-bottom: 0;
}

.ys-tabs__panel h3 {
  font-weight: 500;
  font-size: var(--ys-step-2);
  line-height: 1.3;
  margin-bottom: var(--ys-space-2);
}

.ys-tabs__panel p {
  color: var(--ys-violet-300);
}

.ys-tabs__panel .ys-list li::before {
  background: var(--ys-brass);
}

#ys-tab-1:checked ~ .ys-tabs__panels .ys-tabs__panel:nth-child(1),
#ys-tab-2:checked ~ .ys-tabs__panels .ys-tabs__panel:nth-child(2),
#ys-tab-3:checked ~ .ys-tabs__panels .ys-tabs__panel:nth-child(3),
#ys-tab-4:checked ~ .ys-tabs__panels .ys-tabs__panel:nth-child(4),
#ys-tab-5:checked ~ .ys-tabs__panels .ys-tabs__panel:nth-child(5) {
  visibility: visible;
  opacity: 1;
}

/* No JavaScript is involved, but a visitor who prefers less motion still gets
   an instant swap rather than a fade. */
@media (prefers-reduced-motion: no-preference) {
  .ys-tabs__panel {
    transition: opacity 180ms ease-out;
  }
}

/* -- Gallery — "snapshots from our life" -----------------------------------
   The reference runs a masonry carousel. Ours is a static masonry grid of
   marked placeholders: there are no photographs yet, and a carousel of empty
   frames is worse than a grid of honest ones. */

.ys-gallery {
  padding-block: clamp(3.5rem, 8vw, 5.5rem);
}

.ys-gallery__grid {
  display: grid;
  gap: var(--ys-space-3);
}

@media (min-width: 48rem) {
  .ys-gallery__grid {
    grid-template-columns: repeat(3, 1fr);
  }
}

@media (min-width: 62rem) {
  .ys-gallery__grid {
    grid-template-columns: repeat(4, 1fr);
    grid-auto-rows: 9rem;
  }
  .ys-gallery__box {
    grid-column: span 2;
    grid-row: span 2;
  }
  .ys-gallery__cell--tall {
    grid-row: span 2;
  }
}

.ys-gallery__box {
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding: var(--ys-space-4);
  background: var(--ys-violet-900);
}

.ys-gallery__box h2 {
  font-weight: 500;
  font-size: clamp(1.5rem, 3vw, 2rem);
  line-height: 1.2;
  letter-spacing: -0.01em;
  margin-bottom: var(--ys-space-2);
}

.ys-gallery__box p {
  color: var(--ys-violet-300);
  margin-bottom: 0;
}

.ys-gallery__cell .ys-ph {
  height: 100%;
  min-height: 9rem;
  background: var(--ys-violet-900);
  border-color: var(--ys-violet-500);
  color: var(--ys-violet-300);
}

/* -- Close ----------------------------------------------------------------- */

.ys-cclose {
  padding-block: clamp(3.5rem, 8vw, 5.5rem);
  text-align: center;
}

.ys-cclose h2 {
  font-weight: 500;
  font-size: clamp(1.9rem, 4.4vw, var(--ys-step-4h));
  line-height: 1.15;
  letter-spacing: -0.01em;
  max-width: 20ch;
  margin-inline: auto;
}

.ys-cclose p {
  color: var(--ys-violet-300);
  max-width: 52ch;
  margin-inline: auto;
}

.ys-cclose .ys-pills {
  justify-content: center;
  margin-top: var(--ys-space-4);
}

.ys-cclose__tagline {
  font-family: var(--ys-display);
  font-weight: 500;
  font-size: var(--ys-step-1);
  color: var(--ys-brass);
  margin: var(--ys-space-5) 0 0;
  max-width: none;
}


/* --------------------------------------------------------------------------
   24 · Homepage — rebuilt 2026-08-04 on housemait.com

   Their mechanic, section by section:

     .lv-story    a tall section with a `position:sticky; top:0; height:100vh`
                  stage inside it. Scroll progress through the section drives
                  what the stage shows. Ours runs the hero animation, dissolves
                  it into the layered hands composition, then walks the hands
                  together one step per problem statement.
     .lv-touches  a card grid where each card carries data-lv-reveal and a
                  staggered transition-delay, going from translateY(28px) and
                  opacity 0 to rest.
     .lv-privacy  a dark panel, heading over an icon grid, same staggered
                  reveal. Icons rather than numbers.

   Everything here degrades. No JavaScript, a failed CDN, or reduced motion all
   land on a readable stacked page with every word visible — the pin is an
   enhancement, never the only way to read the section.
   -------------------------------------------------------------------------- */

/* -- Reveal, lifted from housemait's data-lv-reveal ------------------------ */

[data-ys-reveal] {
  transition: opacity 0.7s, transform 0.85s cubic-bezier(0.2, 0.7, 0.2, 1);
}

[data-ys-reveal][data-state="pre"] {
  opacity: 0;
  transform: translateY(28px);
}

[data-ys-reveal][data-state="in"] {
  opacity: 1;
  transform: none;
}

@media (prefers-reduced-motion: reduce) {
  [data-ys-reveal],
  [data-ys-reveal][data-state="pre"] {
    opacity: 1;
    transform: none;
    transition: none;
  }
}

/* -- 1 · Hero -------------------------------------------------------------
   The reaching-hands animation full-bleed, one headline, one CTA. The veil is
   the brand's own ink at a measured opacity rather than a gradient — it is
   what carries paper type over a photograph at AA. */

/* Full viewport. The homepage bar is absolute, so the section starts at the
   top edge on its own — the animation covers the whole viewport, nav strip
   and edges included. The top padding hands the bar's height back so the
   headline never sits under it. */
.ys-fhero {
  position: relative;
  display: flex;
  align-items: flex-end;
  min-height: 100svh;
  padding-block: calc(var(--ys-nav-h) + clamp(3rem, 10vh, 7rem)) clamp(2.5rem, 7vh, 4.5rem);
  overflow: hidden;
  background: var(--ys-ink);
  isolation: isolate;
}

.ys-fhero__media {
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
}

.ys-fhero__media img,
.ys-fhero__vid {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center;
}

/* Off since 2026-08-07. It was an ink veil at 0.62, measured so paper type
   cleared 4.5:1 against the brightest pixel in the artwork; Miguel asked for
   the photograph back at its own exposure, so the headline crossed to violet
   instead and the veil has nothing left to do. Kept in the markup at zero
   rather than deleted — the number above is the one to restore if paper type
   ever returns to this section, and it is not a value to re-derive.

   Violet-700 against the sky measures 5.3:1 and against the cloud highlights
   10.3:1. The one place it drops is directly over the hands' skin, around
   3.1:1, and the headline block does not sit there at any width. */
.ys-fhero__veil {
  position: absolute;
  inset: 0;
  z-index: 1;
  background: var(--ys-ink);
  opacity: 0;
  pointer-events: none;
}

/* Gutter to gutter as of 2026-08-06, not the 1140px container. Two lines is a
   much longer line than five was, and the headline needs every pixel of the
   row to stay at display size — the marked block on Miguel's screenshot runs
   past the container's left edge, which is the same instruction. */
.ys-fhero .ys-wrap {
  position: relative;
  z-index: 2;
  max-width: none;
}

/* Headline low and left at display size, the reading line and the action low
   and right — the arrangement in the reference still. Below 62rem the two
   stack in reading order and the headline stops competing for the width. */
.ys-fhero__grid {
  display: grid;
  gap: clamp(2rem, 5vw, 3.5rem);
  align-items: end;
}

/* The aside is a fixed track now rather than a fraction, so every pixel the
   viewport gains goes to the headline instead of being split with a paragraph
   that is already at its comfortable measure. */
@media (min-width: 62rem) {
  .ys-fhero__grid {
    grid-template-columns: minmax(0, 1fr) minmax(16rem, 17rem);
    column-gap: 3rem;
  }
}

/* Two lines, filling the block Miguel marked on 2026-08-06. The 16ch measure
   is gone — the break is authored in content.js, so a measure would only fight
   it — and the size is driven off the viewport with no upper cap, so the
   longer line runs the full width of its column at every width instead of
   pooling on the left.

   The size is solved, not guessed. "THAT'S HOLDING YOU BACK." is the longer
   of the two lines and measures 14.29em wide in Nourd bold at -0.025em, read
   off the rendered page. The headline's column is the viewport less 25rem —
   two 2.5rem gutters, the 3rem gap, and the 17rem aside — so dividing that by
   14.95 (14.29 plus air) is the largest size at which the line still sits on
   one row. The air is not padding for its own sake: 100vw counts the scrollbar
   and the column does not, so the formula runs about 15px optimistic on every
   desktop window.

   Changing either line, the tracking, or the aside's width changes that
   number. Measure it again; do not nudge it. */
.ys-fhero h1 {
  color: var(--ys-paper);
  max-width: none;
  margin-bottom: 0;
  font-size: clamp(2.35rem, calc((100vw - 25rem) / 14.95), 7.5rem);
  line-height: 0.92;
  letter-spacing: -0.025em;
  text-transform: uppercase;
}

/* Each line is its own block, so the pair can never run together, and a phone
   is still free to wrap inside a line rather than overflow. */
.ys-fhero__line {
  display: block;
  text-wrap: balance;
}

.ys-fhero__aside {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--ys-space-3);
}

/* Who the page is for, in the reference's small right-hand block. Kept to
   four lines at desktop measure.

   Paper, not a dimmed paper and not violet-100: the veil is measured for
   paper and nothing else survives the photograph underneath it. This block
   sits below the headline on size and measure, which is the honest way to
   make it secondary. */
.ys-fhero__who {
  margin: 0;
  max-width: 42ch;
  color: var(--ys-paper);
  font-size: var(--ys-step--1);
  line-height: 1.65;
}

.ys-fhero :focus-visible {
  outline-color: var(--ys-brass);
}

/* -- 2 · The pinned story -------------------------------------------------
   Section height is set in JS from the step count so the copy and the pin can
   never disagree. Without JS the section collapses to its content height and
   the stage stops being sticky — see the no-JS block at the end. */

.ys-story {
  position: relative;
  background: var(--ys-ink);
  color: var(--ys-paper);
}

.ys-story__stage {
  position: sticky;
  top: 0;
  height: 100svh;
  overflow: hidden;
  display: grid;
  place-items: center;
}

/* Both plates sit in the same cell and cross-fade. */
.ys-story__plate {
  grid-area: 1 / 1;
  width: 100%;
  height: 100%;
  position: relative;
}

.ys-story__plate img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* The posed sequence. Five supplied frames stacked in one cell, cross-faded by
   scroll: frame 0 is bare sky, which is what joins the hero animation without
   a seam, and 1 to 4 bring the hands up to meet. Posed by hand, so this is
   better than moving two cut-outs with transforms ever was. */
.ys-story__seq {
  position: absolute;
  inset: 0;
  display: grid;
}

.ys-story__seq img {
  grid-area: 1 / 1;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center;
}

/* Frame 0 carries the section on its own until the driver takes over. */
.ys-story__seq img + img {
  opacity: 0;
}

/* 0.64 puts paper at 5.4:1 over the artwork’s brightest pixel. */
.ys-story__veil {
  position: absolute;
  inset: 0;
  z-index: 2;
  background: var(--ys-ink);
  opacity: 0.64;
  pointer-events: none;
}

/* The section's title card. It rides frame 0 — the bare opening plate — and
   the driver clears it as the first step's copy arrives, so the two never
   share the screen. Above the veil, below the copy. */
.ys-story__title {
  position: absolute;
  z-index: 3;
  top: clamp(6rem, 20vh, 12rem);
  /* Centred by the box, not by a translate — the driver animates this
     element's y, and a translateX(-50%) here would be resolved to a frozen
     pixel value the first time it did. */
  inset-inline: 0;
  margin: 0;
  padding-inline: var(--ys-gutter);
  font-family: var(--ys-display);
  font-weight: 700;
  font-size: clamp(2.35rem, 6.4vw, var(--ys-step-6));
  line-height: 1;
  letter-spacing: -0.02em;
  color: var(--ys-paper);
  text-align: center;
  text-transform: uppercase;
}

/* Copy blocks stack in one cell and are swapped by the driver. */
.ys-story__copy {
  grid-area: 1 / 1;
  position: relative;
  z-index: 3;
  width: 100%;
  display: grid;
  place-items: center;
  padding-inline: var(--ys-gutter);
}

.ys-story__step {
  grid-area: 1 / 1;
  width: 100%;
  max-width: 46rem;
  text-align: center;
}

.ys-story__step h2,
.ys-story__step h3 {
  color: var(--ys-paper);
  margin-bottom: var(--ys-space-3);
}

.ys-story__step h2 {
  font-size: clamp(1.9rem, 5vw, var(--ys-step-5));
  line-height: 1.1;
}

.ys-story__step h3 {
  font-size: clamp(1.6rem, 4.2vw, var(--ys-step-4));
  line-height: 1.15;
}

/* Paper, not violet-300. Secondary text over a photograph measures 2.07:1,
   which no veil short of obliterating the image fixes. The hierarchy is
   carried by size and weight instead. */
.ys-story__step p {
  color: var(--ys-paper);
  font-size: var(--ys-step-1);
  line-height: 1.6;
  max-width: 40ch;
  margin-inline: auto;
  margin-bottom: 0;
}

/* No rule over the story's label — the brass line was removed 2026-08-04.
   Over a photograph the label carries itself; the line read as clutter. */
.ys-story__step .ys-eyebrow {
  color: var(--ys-paper);
  border-top: 0;
  padding-top: 0;
}

/* Progress ticks — one per step, so the reader can see how long this runs. */
.ys-story__ticks {
  position: absolute;
  z-index: 4;
  bottom: var(--ys-space-5);
  left: 50%;
  transform: translateX(-50%);
  display: flex;
  gap: 0.5rem;
  margin: 0;
  padding: 0;
  list-style: none;
}

.ys-story__ticks li {
  width: 2rem;
  height: 2px;
  background: var(--ys-violet-500);
  transition: background-color 220ms ease-out;
}

.ys-story__ticks li[data-on="true"] {
  background: var(--ys-brass);
}

/* JS sets this once it has taken over. Until then the section is a plain
   stack of readable blocks and nothing is hidden. */
.ys-story[data-story="ready"] .ys-story__step {
  opacity: 0;
}

/* Block, not grid. The title is an in-flow element here, and in a grid it
   would land in the same cell as the copy, which pins itself to 1 / 1. */
.ys-story:not([data-story="ready"]) .ys-story__stage {
  position: static;
  height: auto;
  padding-block: var(--ys-section);
  display: block;
}

/* No-JS and reduced motion: the title is the section's opening line in the
   plain stack, not an overlay. Stays positioned so its z-index still holds
   it above the veil. */
.ys-story:not([data-story="ready"]) .ys-story__title {
  position: relative;
  inset: auto;
  padding-inline: 0;
  margin-bottom: var(--ys-space-5);
}

.ys-story:not([data-story="ready"]) .ys-story__copy {
  display: block;
}

.ys-story:not([data-story="ready"]) .ys-story__step {
  max-width: none;
  margin-bottom: var(--ys-space-5);
}

.ys-story:not([data-story="ready"]) .ys-story__step:last-child {
  margin-bottom: 0;
}

.ys-story:not([data-story="ready"]) .ys-story__ticks {
  display: none;
}

/* -- 3 · Cards, from .lv-touches ------------------------------------------ */

.ys-fcards__head {
  max-width: 40rem;
  margin-bottom: var(--ys-space-6);
}

.ys-fcards__grid {
  display: grid;
  gap: var(--ys-space-4);
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 19rem), 1fr));
}

.ys-fcard {
  display: flex;
  flex-direction: column;
  background: var(--ys-surface);
  border: var(--ys-hairline) solid var(--ys-violet-300);
  border-radius: 1.75rem;
  overflow: hidden;
}

.ys-fcard__media {
  position: relative;
  aspect-ratio: 4 / 3;
  overflow: hidden;
  background: var(--ys-violet-100);
}

.ys-fcard__media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center 22%;
  /* The requested softening. One number to tune. */
  filter: blur(5px);
  transform: scale(1.08); /* hides the blurred edge */
}

.ys-fcard__body {
  display: flex;
  flex-direction: column;
  flex: 1;
  padding: var(--ys-space-4) var(--ys-space-4) var(--ys-space-3);
}

.ys-fcard h3 {
  font-size: var(--ys-step-2);
  margin-bottom: var(--ys-space-2);
}

.ys-fcard p {
  max-width: none;
}

.ys-fcard .ys-link {
  margin-top: auto;
}

/* -- 4 · Comparison, check and cross --------------------------------------
   From the supplied reference: the branded column is raised out of the table
   on its own card, the alternative sits flat beside it. */

.ys-vs__head {
  max-width: 40rem;
  margin-bottom: var(--ys-space-6);
}

.ys-vs__wrap {
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  padding-top: var(--ys-space-4);
}

.ys-vs {
  width: 100%;
  min-width: 40rem;
  border-collapse: separate;
  border-spacing: 0;
  text-align: left;
}

.ys-vs th,
.ys-vs td {
  padding: var(--ys-space-3) var(--ys-space-3);
  vertical-align: middle;
}

.ys-vs thead th {
  font-family: var(--ys-body);
  font-size: var(--ys-step--2);
  font-weight: 500;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ys-violet-500);
}

/* Column 2 is ours. It is one continuous ink card behind the cells, lifted
   above the table on both ends. */
.ys-vs thead th:nth-child(2) {
  background: var(--ys-ink);
  color: var(--ys-paper);
  border-radius: 1.25rem 1.25rem 0 0;
  padding-top: var(--ys-space-4);
  text-align: center;
  font-family: var(--ys-display);
  font-size: var(--ys-step-2);
  font-weight: 700;
  letter-spacing: -0.01em;
  text-transform: none;
}

.ys-vs tbody td:first-of-type {
  background: var(--ys-ink);
  color: var(--ys-paper);
  text-align: center;
  font-weight: 500;
}

.ys-vs tbody tr:last-child td:first-of-type {
  border-radius: 0 0 1.25rem 1.25rem;
  padding-bottom: var(--ys-space-4);
}

.ys-vs thead th:nth-child(3) {
  text-align: center;
}

.ys-vs tbody td:last-child {
  text-align: center;
  color: var(--ys-violet-500);
}

.ys-vs tbody th {
  font-family: var(--ys-body);
  font-size: var(--ys-step-0);
  font-weight: 600;
  color: var(--ys-ink);
  width: 34%;
  border-bottom: var(--ys-hairline) solid var(--ys-violet-300);
}

.ys-vs tbody td:last-child {
  border-bottom: var(--ys-hairline) solid var(--ys-violet-300);
}

.ys-vs tbody tr:last-child th,
.ys-vs tbody tr:last-child td:last-child {
  border-bottom: 0;
}

/* The marks. Meaning is carried by the adjacent text, so these are hidden
   from assistive tech rather than read out as punctuation. */
.ys-vs__mark {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.375rem;
  height: 1.375rem;
  flex: none;
  margin-right: 0.5rem;
  border-radius: 999px;
  font-size: 0.8125rem;
  line-height: 1;
  vertical-align: -0.2em;
}

.ys-vs__mark--yes {
  background: var(--ys-brass);
  color: var(--ys-ink);
}

.ys-vs__mark--no {
  background: var(--ys-violet-100);
  color: var(--ys-violet-500);
}

/* -- 5 · Pledge, from .lv-privacy ----------------------------------------- */

.ys-pledge {
  padding-block: var(--ys-section);
}

.ys-pledge__panel {
  position: relative;
  background: var(--ys-ink);
  color: var(--ys-paper);
  border-radius: 1.75rem;
  padding: clamp(var(--ys-space-5), 6vw, var(--ys-space-6));
  overflow: hidden;
}

.ys-pledge__panel h2 {
  color: var(--ys-paper);
  max-width: 18ch;
}

.ys-pledge__sub {
  color: var(--ys-violet-300);
  font-size: var(--ys-step-1);
  line-height: 1.6;
  max-width: 52ch;
  margin-bottom: 0;
}

.ys-pledge__grid {
  display: grid;
  gap: var(--ys-space-5) var(--ys-space-4);
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 15rem), 1fr));
  margin-top: var(--ys-space-6);
}

.ys-pledge__ic {
  display: grid;
  place-items: center;
  width: 2.875rem;
  height: 2.875rem;
  border-radius: 0.875rem;
  background: rgba(245, 243, 247, 0.07);
  color: var(--ys-brass);
}

.ys-pledge__ic svg {
  width: 1.375rem;
  height: 1.375rem;
}

.ys-pledge__grid h3 {
  color: var(--ys-paper);
  font-size: var(--ys-step-2);
  line-height: 1.3;
  margin: var(--ys-space-3) 0 var(--ys-space-2);
}

.ys-pledge__grid p {
  color: var(--ys-violet-300);
  font-size: var(--ys-step--1);
  line-height: 1.6;
  margin-bottom: 0;
  max-width: none;
}

.ys-pledge__panel :focus-visible {
  outline-color: var(--ys-brass);
}

.ys-pledge__panel a {
  color: var(--ys-brass);
}

.ys-pledge__panel a:hover {
  color: var(--ys-paper);
}

/* -- 6 · Place — the statement, the map, the building --------------------- */

.ys-place__lede {
  max-width: 34rem;
  margin-bottom: var(--ys-space-6);
}

.ys-place__lede h2 {
  font-size: clamp(1.75rem, 4vw, var(--ys-step-4));
  line-height: 1.25;
  max-width: 22ch;
}

.ys-place__lede h2 strong {
  font-weight: 700;
}

.ys-place__grid {
  display: grid;
  gap: var(--ys-space-4);
}

@media (min-width: 55rem) {
  .ys-place__grid {
    grid-template-columns: 1fr 1.55fr;
    align-items: stretch;
  }
}

.ys-place__photo {
  border-radius: 1.25rem;
  overflow: hidden;
  min-height: 14rem;
}

.ys-place__photo img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.ys-place__map {
  border-radius: 1.25rem;
  overflow: hidden;
  border: var(--ys-hairline) solid var(--ys-violet-300);
  min-height: 20rem;
}

.ys-place__map iframe {
  display: block;
  width: 100%;
  height: 100%;
  min-height: 20rem;
  border: 0;
}

.ys-place__facts {
  display: grid;
  gap: var(--ys-space-4);
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 14rem), 1fr));
  margin-top: var(--ys-space-5);
  padding-top: var(--ys-space-4);
  border-top: var(--ys-hairline) solid var(--ys-violet-300);
}

.ys-place__facts h3 {
  font-family: var(--ys-body);
  font-size: var(--ys-step--2);
  font-weight: 500;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ys-violet-500);
  margin-bottom: var(--ys-space-2);
}

.ys-place__facts address,
.ys-place__facts p {
  font-style: normal;
  font-size: var(--ys-step--1);
  line-height: 1.7;
  margin-bottom: 0;
}

/* -- 7 · Closing CTA over the footer animation ---------------------------- */

.ys-fcta {
  position: relative;
  padding-block: clamp(4rem, 10vw, 7rem);
  background: var(--ys-ink);
  color: var(--ys-paper);
  overflow: hidden;
  isolation: isolate;
}

.ys-fcta__media {
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
}

.ys-fcta__media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center;
}

/* Heavier than the hero's: this band carries more type and a button. */
.ys-fcta__veil {
  position: absolute;
  inset: 0;
  z-index: 1;
  background: var(--ys-ink);
  opacity: 0.72;
  pointer-events: none;
}

.ys-fcta .ys-wrap {
  position: relative;
  z-index: 2;
}

.ys-fcta h2 {
  color: var(--ys-paper);
}

.ys-fcta .ys-lead {
  color: var(--ys-paper);
}

.ys-fcta .ys-btn {
  background: var(--ys-paper);
  color: var(--ys-ink);
}

.ys-fcta .ys-btn:hover {
  background: var(--ys-violet-100);
}

/* The tagline closes the section where the address block used to sit —
   removed 2026-08-04, sixth pass. Paper, not brass: over the footer artwork
   brass measures 2.47, and §12 lets the use change, never the colour. The
   caps are styling only; the source string keeps its canonical form and its
   period. */
.ys-fcta__tagline {
  font-family: var(--ys-display);
  font-weight: 500;
  font-size: clamp(var(--ys-step-1), 2.6vw, var(--ys-step-3));
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--ys-paper);
  margin: var(--ys-space-5) 0 0;
}

.ys-fcta :focus-visible {
  outline-color: var(--ys-brass);
}

/* Reduced motion swaps every animation for its own first frame. */
@media (prefers-reduced-motion: reduce) {
  .ys-fhero__media img,
  .ys-fcta__media img,
  .ys-story__plate img {
    content: normal;
  }
}

/* --------------------------------------------------------------------------
   25 · Homepage, second pass — 2026-08-04

   Two more pinned sections and a rebuilt comparison.

   Both were replaced on 2026-08-11 from Miguel's recordings. `.ys-reveal-sec`
   — a six-viewport pinned scrub carrying three paragraphs — became `.ys-svc`,
   a tab set that reads in one screen. `.ys-cmp` — a six-row comparison grid —
   became `.ys-diff`, a statement and a claim either side of a rule.
   -------------------------------------------------------------------------- */

/* -- The three functions ---------------------------------------------------
   Rebuilt 2026-08-11 from Miguel's first recording. It replaced a pinned,
   scrubbed section that spent six viewports of scroll on three paragraphs; the
   whole thing is one screen now and reads without moving the page at all.

   The composition is the reference's: image left, the three names stacked
   right with the live one at display size, its copy under them, one button.
   What is not the reference's is the colour — the active name is violet-700
   and the other two are ink, so hierarchy is size and hue, never opacity.
   Fading an inactive service would say it is unavailable, and all three are.

   No-JS is the whole section, open: every image, every name at the same size,
   every paragraph and all three links. The tab behaviour below only starts
   once ys.js has written data-ready. */

.ys-svc {
  padding-block: var(--ys-section);
}

.ys-svc__eyebrow {
  margin: 0 0 clamp(1.75rem, 3.5vw, 2.75rem);
  font-family: var(--ys-body);
  font-size: var(--ys-step-0);
  font-weight: 400;
  line-height: 1.5;
  letter-spacing: 0;
  text-transform: none;
  color: var(--ys-violet-100);
  max-width: 34rem;
}

.ys-svc__eyebrow strong {
  font-weight: 600;
  color: var(--ys-paper);
}

.ys-svc__grid {
  display: grid;
  gap: clamp(2rem, 5vw, 3.5rem);
}

@media (min-width: 62rem) {
  .ys-svc__grid {
    grid-template-columns: minmax(0, 1.08fr) minmax(0, 1fr);
    align-items: start;
    gap: clamp(3rem, 8vw, 8rem);
  }
}

/* One box, three images in it, crossfaded. The box holds its own 4:3 so the
   swap never changes the height of the row. */
.ys-svc__media {
  position: relative;
  display: grid;
  aspect-ratio: 4 / 3;
  background: var(--ys-violet-900);
  overflow: hidden;
}

.ys-svc__img {
  grid-area: 1 / 1;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center;
}

.ys-svc[data-ready="true"] .ys-svc__img {
  opacity: 0;
  transition: opacity 420ms ease-out;
}

.ys-svc[data-ready="true"] .ys-svc__img[data-on="true"] {
  opacity: 1;
}

@media (prefers-reduced-motion: reduce) {
  .ys-svc__img {
    transition: none;
  }
}

.ys-svc__col {
  display: flex;
  flex-direction: column;
  min-width: 0;
}

.ys-svc__list {
  list-style: none;
  margin: 0;
  padding: 0;
}

.ys-svc__item + .ys-svc__item {
  margin-top: clamp(1rem, 2.2vw, 1.75rem);
}

/* The name is a real button, full-width and left-aligned, so the whole line is
   the target rather than the glyphs. The arrow sits in its own fixed gutter —
   set in the flow rather than absolutely, so a name that wraps to two lines
   still hangs off the same left edge. */
.ys-svc__name {
  display: grid;
  grid-template-columns: 2.5rem 1fr;
  align-items: start;
  width: 100%;
  padding: 0;
  background: none;
  border: 0;
  border-radius: 0;
  text-align: left;
  cursor: pointer;
  color: var(--ys-violet-100);
  font-family: var(--ys-display);
  font-weight: 500;
  font-size: clamp(var(--ys-step-2), 2.2vw, var(--ys-step-3));
  line-height: 1.15;
  letter-spacing: -0.01em;
  transition:
    font-size 320ms var(--ys-ease-hover),
    color 320ms ease-out;
}

.ys-svc__item[data-on="true"] .ys-svc__name {
  color: var(--ys-paper);
  font-size: clamp(var(--ys-step-4), 4.4vw, var(--ys-step-5));
  font-weight: 700;
}

.ys-svc__name:hover {
  color: var(--ys-paper);
}

.ys-svc__arrow {
  opacity: 0;
  transform: translateX(-0.35rem);
  transition:
    opacity 240ms ease-out,
    transform 240ms var(--ys-ease-hover);
  /* Optically centred against the first line of a name at display size. */
  line-height: 1.35;
  font-size: 0.72em;
}

.ys-svc__item[data-on="true"] .ys-svc__arrow {
  opacity: 1;
  transform: translateX(0);
}

@media (prefers-reduced-motion: reduce) {
  .ys-svc__name,
  .ys-svc__arrow {
    transition: none;
  }
}

/* The copy hangs off the same left edge as the names, clear of the arrow
   gutter — the reference indents it with them rather than to the section. */
.ys-svc__panels {
  margin-top: clamp(2rem, 4vw, 3rem);
  padding-left: 2.5rem;
}

@media (max-width: 47.999rem) {
  .ys-svc__panels {
    padding-left: 0;
  }
}

.ys-svc__panel p {
  margin: 0 0 var(--ys-space-4);
  max-width: 34rem;
  color: var(--ys-paper);
}

/* Open, stacked and readable until the script says otherwise. Once it has, the
   panels share one cell and crossfade — grid rather than absolute positioning
   so the tallest panel still sets the height and nothing under the section
   moves as the copy changes. */
.ys-svc[data-ready="true"] .ys-svc__panels {
  display: grid;
}

.ys-svc[data-ready="true"] .ys-svc__panel {
  grid-area: 1 / 1;
  opacity: 0;
  visibility: hidden;
  transform: translateY(8px);
  transition:
    opacity 280ms ease-out,
    transform 280ms var(--ys-ease-hover),
    visibility 0s linear 280ms;
}

.ys-svc[data-ready="true"] .ys-svc__panel[data-on="true"] {
  opacity: 1;
  visibility: visible;
  transform: none;
  transition-delay: 0s, 0s, 0s;
}

@media (prefers-reduced-motion: reduce) {
  .ys-svc[data-ready="true"] .ys-svc__panel {
    transition: none;
    transform: none;
  }
}

/* Arrow leads the label, as it does on the reference's button. */
.ys-svc__cta {
  gap: 0.6rem;
}

.ys-svc__cta-ico {
  transition: transform 180ms var(--ys-ease-hover);
}

.ys-svc__cta:hover .ys-svc__cta-ico {
  transform: translateX(3px);
}

@media (prefers-reduced-motion: reduce) {
  .ys-svc__cta-ico {
    transition: none;
  }
}

.ys-svc :focus-visible {
  outline: 2px solid var(--ys-paper);
  outline-offset: 3px;
}

/* -- The difference --------------------------------------------------------
   Rebuilt 2026-08-11 from Miguel's third recording, which is one composition
   used twice: a 50/50 split where the left column holds the short thing and
   the right column holds the long one, above and below a rule.

   Above the rule that inverts — the statement is the large thing and it is on
   the left, with the two tiles on the right. Below it the reading line is
   small on the left and the claim is large on the right. The rule is what
   makes the inversion read as deliberate.

   Ink ground rather than the reference's cream: see the note in index.html. */

.ys-diff {
  padding-block: var(--ys-section);
}

.ys-diff .ys-wrap {
  max-width: none;
}

.ys-diff__top {
  display: grid;
  gap: clamp(2.5rem, 5vw, 4rem);
}

@media (min-width: 62rem) {
  .ys-diff__top {
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    /* The headline sits on the tiles' baseline, not their top. That drop is
       the whole composition — take it out and it is two columns of stuff. */
    align-items: end;
  }
}

.ys-diff__display {
  margin: 0;
  font-family: var(--ys-display);
  font-weight: 700;
  font-size: clamp(var(--ys-step-4), 5.2vw, var(--ys-step-6));
  line-height: 1.05;
  letter-spacing: -0.02em;
  color: var(--ys-paper);
  max-width: 15ch;
}

.ys-diff__tiles {
  display: grid;
  grid-template-columns: 1fr 1fr;
  align-items: start;
  gap: clamp(0.75rem, 1.5vw, 1rem);
}

.ys-diff__tile {
  margin: 0;
  overflow: hidden;
  background: var(--ys-violet-900);
}

.ys-diff__tile img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* 1:1 by default, 5:7 for the tall one — the reference's two heights, and the
   difference between them is what stops the pair reading as a slot. */
.ys-diff__tile {
  aspect-ratio: 1 / 1;
}

.ys-diff__tile--tall {
  aspect-ratio: 5 / 7;
}

.ys-diff__rule {
  height: var(--ys-hairline);
  margin-block: clamp(2.5rem, 5vw, 4rem);
  background: var(--ys-violet-500);
}

/* The same gap as __top, deliberately: the claim below the rule has to start
   on the tall tile's left edge or the two halves of the composition are a few
   pixels out of register with each other. */
.ys-diff__foot {
  display: grid;
  gap: clamp(2.5rem, 5vw, 4rem);
}

@media (min-width: 62rem) {
  .ys-diff__foot {
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    align-items: start;
  }
}

/* Violet-300 on ink is 7.03 AAA. The dot is the reference's marker, kept at
   brass because it is the one ornament this section gets. */
/* violet-100 at 8.81:1, not violet-300. Violet-300 is 4.13 on this ground —
   fine for the hairline above, under AA for a 16px paragraph. §26 names the
   step-down and this is it. */
.ys-diff__label {
  position: relative;
  margin: 0;
  padding-left: 1.5rem;
  max-width: 32rem;
  font-size: var(--ys-step-0);
  line-height: 1.6;
  color: var(--ys-violet-100);
}

.ys-diff__dot {
  position: absolute;
  left: 0;
  top: 0.62em;
  width: 0.5rem;
  height: 0.5rem;
  background: var(--ys-brass);
}

.ys-diff__claim {
  margin: 0;
  max-width: 22ch;
  font-family: var(--ys-display);
  font-weight: 500;
  font-size: clamp(var(--ys-step-2), 2.9vw, var(--ys-step-4));
  line-height: 1.25;
  letter-spacing: -0.01em;
  color: var(--ys-paper);
}

.ys-place__statement {
  padding-block: clamp(var(--ys-space-6), 12vw, 9rem);
}

/* The statement runs the full width of the section — no measure cap, per
   the 2026-08-04 sixth-pass note. The size scales with the viewport so the
   lines fill the space rather than pooling on the left. */
.ys-place__statement h2 {
  font-size: clamp(2rem, 4.6vw, var(--ys-step-6));
  line-height: 1.14;
  letter-spacing: -0.02em;
  max-width: none;
  margin-bottom: var(--ys-space-5);
}

.ys-place__statement h2 strong {
  font-weight: 700;
}

/* The lead-in question, set exactly 15pt (20px) below the statement at every
   viewport width, with a floor so the phone end never drops under 18px. */
.ys-place__ask {
  font-family: var(--ys-display);
  font-weight: 500;
  font-size: max(1.125rem, calc(clamp(2rem, 4.6vw, var(--ys-step-6)) - 20px));
  line-height: 1.15;
  letter-spacing: -0.01em;
  color: var(--ys-violet-500);
  margin: 0 0 var(--ys-space-2);
}

/* --------------------------------------------------------------------------
   23 · Utilities
   -------------------------------------------------------------------------- */

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

.ys-mt-0 {
  margin-top: 0;
}
.ys-mb-0 {
  margin-bottom: 0;
}

/* --------------------------------------------------------------------------
   24 · Added 2026-08-05
   The emphasis run, the swap CTA, and the careers page rebuilt on the
   reference in the second video.
   -------------------------------------------------------------------------- */

/* The one run of copy in a paragraph that carries the weight of the point.
   Caps and bold together, so it lands whether or not weight renders. */
.ys-emph {
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.01em;
}

/* -- The swap CTA ----------------------------------------------------------
   Two blocks: a label and an arrow square, 2px apart. On hover the arrow
   crosses to the other side of the label and turns to point out of the page,
   the whole pair shears once as it goes, and the label decodes character by
   character (assets/js/ys.js).

   The crossing is done by growing a second arrow square on the left while
   the right one collapses — no measuring, no JavaScript, and the label
   slides the exact distance either way. Both squares carry aria-hidden;
   the accessible name is the label alone. */
.ys-swapbtn {
  --ys-swap-ico: 3.25rem;
  --ys-swap-ease: cubic-bezier(0.65, 0, 0.35, 1);

  display: inline-flex;
  align-items: stretch;
  max-width: 100%;
  color: var(--ys-paper);
  text-decoration: none;
  cursor: pointer;
}

.ys-swapbtn__label {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 3.25rem;
  padding: 0.75rem 1.75rem;
  background: var(--ys-violet-700);
  color: var(--ys-paper);
  font-family: var(--ys-label);
  font-size: var(--ys-step--2);
  font-weight: 400;
  letter-spacing: 0.12em;
  line-height: 1.2;
  text-transform: uppercase;
  white-space: nowrap;
  transition: background-color 200ms ease-out;
}

.ys-swapbtn__ico {
  display: flex;
  flex: none;
  align-items: center;
  justify-content: center;
  width: var(--ys-swap-ico);
  overflow: hidden;
  background: var(--ys-violet-700);
  color: var(--ys-paper);
  font-size: 1.125rem;
  line-height: 1;
  transition:
    width 340ms var(--ys-swap-ease),
    margin-inline 340ms var(--ys-swap-ease),
    background-color 200ms ease-out;
}

.ys-swapbtn__ico--lead {
  width: 0;
}

.ys-swapbtn__ico--trail {
  margin-inline-start: 2px;
}

.ys-swapbtn:hover .ys-swapbtn__ico--lead,
.ys-swapbtn:focus-visible .ys-swapbtn__ico--lead {
  width: var(--ys-swap-ico);
  margin-inline-end: 2px;
}

.ys-swapbtn:hover .ys-swapbtn__ico--trail,
.ys-swapbtn:focus-visible .ys-swapbtn__ico--trail {
  width: 0;
  margin-inline-start: 0;
}

.ys-swapbtn:hover .ys-swapbtn__label,
.ys-swapbtn:hover .ys-swapbtn__ico,
.ys-swapbtn:focus-visible .ys-swapbtn__label,
.ys-swapbtn:focus-visible .ys-swapbtn__ico {
  background: var(--ys-violet-900);
}

/* The shear. One pass, matched to the crossing, and it returns to square —
   the pair is never left leaning. */
@keyframes ys-swap-shear {
  0% {
    transform: skewX(0deg);
  }
  42% {
    transform: skewX(-9deg);
  }
  100% {
    transform: skewX(0deg);
  }
}

.ys-swapbtn:hover,
.ys-swapbtn:focus-visible {
  animation: ys-swap-shear 380ms var(--ys-swap-ease);
}

.ys-swapbtn:focus-visible {
  outline: 2px solid var(--ys-brass);
  outline-offset: 3px;
}

/* Reduced motion: the arrow stays where it is, the shear never runs, and the
   hover is a colour change and nothing else. The scramble is skipped in the
   script for the same reason. */
@media (prefers-reduced-motion: reduce) {
  .ys-swapbtn:hover,
  .ys-swapbtn:focus-visible {
    animation: none;
  }
  .ys-swapbtn__label,
  .ys-swapbtn__ico {
    transition: background-color 200ms ease-out;
  }
  .ys-swapbtn:hover .ys-swapbtn__ico--lead,
  .ys-swapbtn:focus-visible .ys-swapbtn__ico--lead {
    width: 0;
    margin-inline-end: 0;
  }
  .ys-swapbtn:hover .ys-swapbtn__ico--trail,
  .ys-swapbtn:focus-visible .ys-swapbtn__ico--trail {
    width: var(--ys-swap-ico);
    margin-inline-start: 2px;
  }
}

/* -- Every clickable label, sitewide ---------------------------------------
   2026-08-05. The decode that started in the nav now runs on every CTA on
   the site, so all of them are set in mono caps: a swapped character has to
   take exactly the width of the one it replaced, and only a fixed advance
   width keeps a label from jittering under the pointer. Links inside prose
   are untouched — they are read, not aimed at.

   The shear comes with it. Same keyframe as the swap CTA, same easing, and
   it returns to square: nothing is left leaning. */

.ys-btn [data-ys-scramble],
.ys-pill [data-ys-scramble],
.ys-link [data-ys-scramble],
.ys-tag [data-ys-scramble],
.ys-job__apply [data-ys-scramble] {
  font-family: var(--ys-label);
  font-weight: 400;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}

/* Mono caps set wider than the sans they replaced; the step down holds the
   button's measure. */
.ys-btn [data-ys-scramble],
.ys-pill [data-ys-scramble] {
  font-size: 0.875em;
}

.ys-btn:hover,
.ys-btn:focus-visible,
.ys-pill:hover,
.ys-pill:focus-visible,
.ys-link:hover,
.ys-link:focus-visible,
.ys-tag:hover,
.ys-tag:focus-visible,
.ys-job__apply:hover,
.ys-job__apply:focus-visible {
  animation: ys-swap-shear 380ms var(--ys-ease-hover);
}

@media (prefers-reduced-motion: reduce) {
  .ys-btn:hover,
  .ys-btn:focus-visible,
  .ys-pill:hover,
  .ys-pill:focus-visible,
  .ys-link:hover,
  .ys-link:focus-visible,
  .ys-tag:hover,
  .ys-tag:focus-visible,
  .ys-job__apply:hover,
  .ys-job__apply:focus-visible {
    animation: none;
  }
}

/* -- One ground for the careers page ---------------------------------------
   Violet-700 under every band, paper on top of it. Paper measures 10.31 on
   violet and is the only text colour that passes there — brass is 3.51 and
   fails outright as text, which is why every brass run on this page is
   remapped below. Secondary copy steps down to violet-100 (8.81), never into
   a colour. */

.ys-page--violet .ys-band,
.ys-page--violet .ys-band--900,
.ys-page--violet .ys-band--700 {
  background: var(--ys-violet-700);
  color: var(--ys-paper);
}

/* Brass fails as text here. Paper is the link colour, and the underline
   carries the affordance instead of the colour. */
.ys-page--violet .ys-band a:not(.ys-btn):not(.ys-pill):not(.ys-tag),
.ys-page--violet .ys-band a:not(.ys-btn):not(.ys-pill):not(.ys-tag):hover {
  color: var(--ys-paper);
}

.ys-page--violet .ys-band :focus-visible {
  outline-color: var(--ys-paper);
}

.ys-page--violet .ys-chero__line,
.ys-page--violet .ys-jobs__head,
.ys-page--violet .ys-job__loc,
.ys-page--violet .ys-job__type,
.ys-page--violet .ys-job__panel p,
.ys-page--violet .ys-job__panel ul,
.ys-page--violet .ys-job__panel h3,
.ys-page--violet .ys-jobs__note,
.ys-page--violet .ys-open p,
.ys-page--violet .ys-gallery p {
  color: var(--ys-violet-100);
}

/* The one-line summary at the top of an open posting leads the panel, so it
   holds full paper while the detail under it steps down. */
.ys-page--violet .ys-job__summary {
  color: var(--ys-paper);
}

/* Emphasis has to read brighter than its heading, not dimmer — so the
   heading steps down and the strong run keeps paper. Reversing that is what
   a brass run used to do, and brass does not pass here. */
.ys-page--violet .ys-open h2 {
  color: var(--ys-violet-100);
}

.ys-page--violet .ys-open h2 strong {
  color: var(--ys-paper);
}

/* Hairlines and the row rule. Non-text, so violet-300 at 4.13 clears the 3:1
   these need; the brass row underline goes to paper, which is what the eye
   follows on this ground. */
.ys-page--violet .ys-job,
.ys-page--violet .ys-jobs__head {
  border-color: var(--ys-violet-300);
}

.ys-page--violet .ys-job__pos::after {
  background: var(--ys-paper);
}

.ys-page--violet .ys-job__row:hover .ys-job__toggle {
  background: var(--ys-violet-900);
}

.ys-page--violet .ys-job__apply:hover {
  color: var(--ys-violet-100);
}

/* The swap CTA is violet-700 on ink everywhere else, which on this page would
   be the ground colour and no button at all. It inverts here: paper ground,
   violet type at 10.32, crossing to violet-900 and paper at 14.20 on hover. */
.ys-page--violet .ys-swapbtn__label,
.ys-page--violet .ys-swapbtn__ico {
  background: var(--ys-paper);
  color: var(--ys-violet-700);
}

.ys-page--violet .ys-swapbtn:hover .ys-swapbtn__label,
.ys-page--violet .ys-swapbtn:hover .ys-swapbtn__ico,
.ys-page--violet .ys-swapbtn:focus-visible .ys-swapbtn__label,
.ys-page--violet .ys-swapbtn:focus-visible .ys-swapbtn__ico {
  background: var(--ys-violet-900);
  color: var(--ys-paper);
}

/* -- Careers hero — the reference's single display word --------------------- */

.ys-chero--display {
  padding-block: clamp(3.5rem, 10vw, 7rem) clamp(2.5rem, 6vw, 4.5rem);
}

.ys-chero--display h1 {
  margin: 0;
  max-width: none;
  font-weight: 700;
  font-size: clamp(3.5rem, 15vw, 11rem);
  line-height: 0.92;
  letter-spacing: -0.035em;
}

.ys-chero__line {
  margin: clamp(2rem, 6vw, 4rem) 0 0;
  max-width: 34ch;
  color: var(--ys-violet-300);
  font-size: var(--ys-step-1);
  line-height: 1.5;
}

/* -- The roles table -------------------------------------------------------
   POSITION / LOCATION / TYPE, one row per role, and the detail underneath in
   a details/summary so it opens with JavaScript switched off and is reachable
   by keyboard without a line of script. */

.ys-jobs {
  padding-block: clamp(3rem, 7vw, 5rem);
}

.ys-jobs__head {
  display: none;
  padding-bottom: var(--ys-space-2);
  border-bottom: var(--ys-hairline) solid var(--ys-violet-500);
  color: var(--ys-violet-300);
  font-family: var(--ys-label);
  font-size: var(--ys-step--2);
  letter-spacing: 0.12em;
  text-transform: uppercase;
}

@media (min-width: 55rem) {
  .ys-jobs__head {
    display: grid;
    grid-template-columns: minmax(0, 1.5fr) minmax(0, 0.9fr) minmax(0, 1.1fr) 2.5rem;
    gap: var(--ys-space-3);
    align-items: center;
  }
}

.ys-jobs__list {
  list-style: none;
  margin: 0;
  padding: 0;
}

.ys-job {
  border-bottom: var(--ys-hairline) solid var(--ys-violet-500);
}

.ys-job__row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 2.5rem;
  gap: var(--ys-space-2) var(--ys-space-3);
  align-items: center;
  padding-block: var(--ys-space-3);
  cursor: pointer;
  list-style: none;
}

.ys-job__row::-webkit-details-marker {
  display: none;
}

@media (min-width: 55rem) {
  .ys-job__row {
    grid-template-columns: minmax(0, 1.5fr) minmax(0, 0.9fr) minmax(0, 1.1fr) 2.5rem;
  }
}

.ys-job__pos {
  position: relative;
  font-family: var(--ys-display);
  font-weight: 500;
  font-size: clamp(1.15rem, 2.2vw, 1.5rem);
  line-height: 1.25;
  color: var(--ys-paper);
}

/* The underline sweeps in from the left rather than appearing — the reference
   draws it, and a drawn line reads as a response to the pointer. */
.ys-job__pos::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: -0.35rem;
  height: 2px;
  background: var(--ys-brass);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform 320ms cubic-bezier(0.65, 0, 0.35, 1);
}

.ys-job__row:hover .ys-job__pos::after,
.ys-job__row:focus-visible .ys-job__pos::after,
.ys-job__d[open] .ys-job__pos::after {
  transform: scaleX(1);
}

@media (prefers-reduced-motion: reduce) {
  .ys-job__pos::after {
    transition: none;
  }
}

.ys-job__loc,
.ys-job__type {
  color: var(--ys-violet-300);
  font-size: var(--ys-step--1);
}

@media (max-width: 54.99rem) {
  .ys-job__loc,
  .ys-job__type {
    grid-column: 1;
  }
  .ys-job__loc {
    margin-top: 0.25rem;
  }
}

.ys-job__toggle {
  display: flex;
  align-items: center;
  justify-content: center;
  grid-row: 1;
  grid-column: -2;
  width: 2.25rem;
  height: 2.25rem;
  border: var(--ys-hairline) solid var(--ys-violet-300);
  border-radius: 999px;
  color: var(--ys-paper);
  font-size: 0.875rem;
  line-height: 1;
  transition:
    background-color 200ms ease-out,
    color 200ms ease-out,
    transform 260ms cubic-bezier(0.65, 0, 0.35, 1);
}

.ys-job__row:hover .ys-job__toggle {
  background: var(--ys-violet-500);
}

.ys-job__d[open] .ys-job__toggle {
  background: var(--ys-paper);
  border-color: var(--ys-paper);
  color: var(--ys-ink);
  transform: rotate(180deg);
}

@media (prefers-reduced-motion: reduce) {
  .ys-job__toggle {
    transition:
      background-color 200ms ease-out,
      color 200ms ease-out;
  }
}

.ys-job__panel {
  padding-bottom: var(--ys-space-5);
}

@media (min-width: 55rem) {
  .ys-job__panel {
    display: grid;
    grid-template-columns: minmax(0, 1.6fr) minmax(0, 2.4fr);
    gap: var(--ys-space-3);
  }
  .ys-job__panel > * {
    grid-column: 2;
  }
}

.ys-job__panel p,
.ys-job__panel ul {
  max-width: 62ch;
  color: var(--ys-violet-300);
}

.ys-job__panel h3 {
  margin: var(--ys-space-3) 0 var(--ys-space-2);
  font-family: var(--ys-label);
  font-weight: 400;
  font-size: var(--ys-step--2);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ys-violet-300);
}

.ys-job__panel ul {
  margin: 0 0 var(--ys-space-3);
  padding-left: 1.25rem;
}

.ys-job__panel li {
  margin-bottom: 0.375rem;
}

.ys-job__apply {
  display: inline-block;
  min-height: 2.75rem;
  line-height: 2.75rem;
  color: var(--ys-paper);
  font-family: var(--ys-label);
  font-size: var(--ys-step--2);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  text-decoration: underline;
  text-underline-offset: 0.4em;
  text-decoration-thickness: 1px;
}

.ys-band .ys-job__apply:hover {
  color: var(--ys-brass);
}

.ys-jobs__note {
  margin: var(--ys-space-5) 0 0;
  max-width: 62ch;
  color: var(--ys-violet-300);
  font-size: var(--ys-step--1);
}

/* ==========================================================================
   26 · One ground — 2026-08-07

   Every band on every page is violet-700 and every word on it is paper. The
   careers page has run this way since 2026-08-05 and it is now the whole
   site: the alternating paper / tint / ink ladder is gone, and so are the
   violet-100 card and the white panel that went with it.

   Two colours do all the work. Paper on violet-700 measures 10.32:1, which is
   AAA at every size, and it is the ONLY type colour that passes there —
   brass is 3.51 and violet-500 is 1.6, so every run of either had to be
   remapped rather than left to look nearly right. Secondary copy steps down
   to violet-100 (8.81:1) and never into a different colour; hairlines and
   ornaments take violet-300 (4.13:1), which is under the 4.5 for text and
   over the 3:1 these need.

   Three things are deliberately not on this ground, and each is a surface
   rather than a section:

     the header      a paper strip once it solidifies, which is what it was
                     before and what carries the violet CTA block.
     the footer      unchanged, on its own near-paper ground. Miguel pointed
                     at the careers page as the target and the careers footer
                     is this footer.
     form fields     a field has to read as a field. White with ink type.

   And two sections keep a light ground because the photograph under them is
   light and was asked to stay that way — the homepage hero and the pinned
   story. They take violet type instead of paper. See the block at the end.

   Scoped to .ys-page, which is the plane holding <main> on every page. The
   header and the footer sit outside it and are untouched by anything here.
   ========================================================================== */

/* -- The ground ----------------------------------------------------------- */

body {
  background: var(--ys-violet-700);
  color: var(--ys-paper);
}

.ys-page {
  background: var(--ys-violet-700);
  color: var(--ys-paper);
}

/* Every band, whatever it used to be. The ink sections, the violet-100 tints
   and the plain paper sections all land on the same colour, so the page is
   one field and the rhythm is carried by spacing alone. */
.ys-page .ys-section,
.ys-page .ys-tint,
.ys-page .ys-dark,
.ys-page .ys-hero,
.ys-page .ys-lede,
.ys-page .ys-commit,
.ys-page .ys-panel,
.ys-page .ys-band,
.ys-page .ys-band--900,
.ys-page .ys-band--700,
.ys-page .ys-svc,
.ys-page .ys-diff,
.ys-page .ys-fcta {
  background: var(--ys-violet-700);
  color: var(--ys-paper);
}

/* -- Type ----------------------------------------------------------------- */

.ys-page :is(h1, h2, h3, h4, h5, h6) {
  color: var(--ys-paper);
}

.ys-page .ys-lead,
.ys-page .ys-quote,
.ys-page .ys-secondary,
.ys-page .ys-accent {
  color: var(--ys-paper);
}

/* Secondary copy steps down in lightness, never sideways into a colour. */
.ys-page .ys-eyebrow,
.ys-page .ys-step__n,
.ys-page .ys-commit__k,
.ys-page .ys-place__ask,
.ys-page .ys-crumb,
.ys-page .ys-crumb a,
.ys-page .ys-table th,
.ys-page .ys-table caption,
.ys-page .ys-table--compare tbody th,
.ys-page .ys-field__hint,
.ys-page .ys-faq summary::after,
.ys-page .ys-ph,
.ys-page .ys-ph__label {
  color: var(--ys-violet-100);
}

.ys-page .ys-table tbody th,
.ys-page .ys-table td {
  color: var(--ys-paper);
}

/* -- Links ---------------------------------------------------------------- */

/* Brass fails on this ground, so the underline carries the affordance and the
   colour stays put. Anything with a fill of its own is excluded — its text is
   measured against that fill, not against the band. */
.ys-page a:not(.ys-btn):not(.ys-link):not(.ys-swapbtn):not(.ys-job__apply) {
  color: var(--ys-paper);
  text-decoration-color: var(--ys-violet-300);
}

.ys-page a:not(.ys-btn):not(.ys-link):not(.ys-swapbtn):not(.ys-job__apply):hover {
  color: var(--ys-paper);
  text-decoration-color: var(--ys-paper);
}

.ys-page .ys-link,
.ys-page .ys-link:hover,
.ys-page .ys-job__apply,
.ys-page .ys-job__apply:hover {
  color: var(--ys-paper);
}

.ys-page :focus-visible {
  outline-color: var(--ys-paper);
}

/* -- Rules, borders and the one ornament ----------------------------------- */

.ys-page .ys-eyebrow {
  border-top-color: var(--ys-paper);
}

.ys-page .ys-list li::before {
  background: var(--ys-paper);
}

.ys-page .ys-card--plain,
.ys-page .ys-boundary__col,
.ys-page .ys-boundary__col:last-child,
.ys-page .ys-commit__item {
  border-top-color: var(--ys-paper);
}

.ys-page .ys-rule,
.ys-page .ys-faq,
.ys-page .ys-place__facts {
  border-top-color: var(--ys-violet-300);
}

.ys-page .ys-faq details,
.ys-page .ys-tile,
.ys-page .ys-table th,
.ys-page .ys-table td {
  border-bottom-color: var(--ys-violet-300);
}

/* The card keeps its edge and loses its fill — a tinted box on a violet band
   is a second surface, and there is only one surface now. */
.ys-page .ys-card {
  background: transparent;
  border-color: var(--ys-violet-300);
}

.ys-page .ys-place__map {
  border-color: var(--ys-violet-300);
}

/* Placeholders stay loud on purpose. They are a build flag, not a surface. */
.ys-page .ys-ph {
  background: transparent;
  border-color: var(--ys-violet-300);
}

/* The build flags are the one thing left deliberately off-ground: they exist
   to be noticed and removed before launch. */
.ys-page .ys-flag {
  color: var(--ys-ink);
}

/* -- The one button --------------------------------------------------------
   Paper fill, violet type, square, no border. Every call to action on the
   site is this block — the ink-band button, the lede button, the closing CTA,
   the service CTA and the swap CTA all collapsed into it on 2026-08-07.

   Hover inverts the same two colours and adds a paper hairline, because a
   violet fill on a violet band has no edge of its own. Nothing else is
   introduced: the resting state and the hover state between them use #f5f3f7
   and #443062 and no third value.

   The header CTA is not in here — it sits on the paper bar and runs the
   inverse of this, which is the same pair the other way round. */

.ys-page .ys-btn,
.ys-page .ys-pill,
.ys-page .ys-tag,
.ys-page .ys-swapbtn__label,
.ys-page .ys-swapbtn__ico {
  background: var(--ys-paper);
  color: var(--ys-violet-700);
  border: var(--ys-hairline) solid var(--ys-paper);
}

.ys-page .ys-btn:hover,
.ys-page .ys-btn:active,
.ys-page .ys-pill:hover,
.ys-page .ys-tag:hover,
.ys-page .ys-swapbtn:hover .ys-swapbtn__label,
.ys-page .ys-swapbtn:hover .ys-swapbtn__ico,
.ys-page .ys-swapbtn:focus-visible .ys-swapbtn__label,
.ys-page .ys-swapbtn:focus-visible .ys-swapbtn__ico {
  background: transparent;
  color: var(--ys-paper);
  border-color: var(--ys-paper);
}

.ys-page .ys-btn[disabled],
.ys-page .ys-btn[aria-disabled="true"] {
  background: transparent;
  color: var(--ys-violet-300);
  border-color: var(--ys-violet-300);
}

/* The arrow square inside the button. It was paper on a violet button; the
   button is paper now, so the square takes the violet. */
.ys-page .ys-btn--arrow::after {
  background: var(--ys-violet-700);
  color: var(--ys-paper);
}

.ys-page .ys-btn--arrow:hover::after {
  background: var(--ys-paper);
  color: var(--ys-violet-700);
}

/* -- Forms ---------------------------------------------------------------- */

/* A field reads as a field: white ground, ink type, unchanged. Only the
   furniture around it moves onto the new ground. */
.ys-page .ys-field label {
  color: var(--ys-paper);
}

.ys-page .ys-form__status {
  background: var(--ys-violet-900);
  color: var(--ys-paper);
}

/* -- The closing CTA and the solution scenes -------------------------------
   Both sections carry a photograph under the copy, so both need a veil, and
   the veil is now the band's own violet rather than ink. 0.80 puts paper at
   6.0:1 over the brightest pixel in either set of artwork — measured, not
   picked. Lower it and the copy fails; there is no room to trade here. */

.ys-page .ys-fcta__veil {
  background: var(--ys-violet-700);
  opacity: 0.8;
}

/* -- The two light sections ------------------------------------------------
   The hero and the pinned story keep their photographs at the exposure they
   were shot at — no veil, no dimming — and the copy over them is violet-700
   rather than paper. That is the one place on the site where the pair runs
   the other way round, and it is the only way to read type over a light sky
   without touching the picture.

   The section's own background is paper rather than violet: it is what shows
   for the moment before the artwork paints, and a violet flash under a pale
   sky is a worse frame than a paper one. */

.ys-page .ys-fhero,
.ys-page .ys-story {
  background: var(--ys-paper);
  color: var(--ys-violet-700);
}

.ys-page .ys-fhero h1,
.ys-page .ys-fhero__who,
.ys-page .ys-story__title,
.ys-page .ys-story__step h2,
.ys-page .ys-story__step h3,
.ys-page .ys-story__step p,
.ys-page .ys-story__step .ys-emph,
.ys-page .ys-story__step .ys-eyebrow {
  color: var(--ys-violet-700);
}

.ys-page .ys-fhero :focus-visible,
.ys-page .ys-story :focus-visible {
  outline-color: var(--ys-violet-700);
}

/* The hero's CTA is the inverse block, same as the one in the bar: a light
   ground takes the violet fill. */
.ys-page .ys-fhero .ys-swapbtn__label,
.ys-page .ys-fhero .ys-swapbtn__ico {
  background: var(--ys-violet-700);
  color: var(--ys-paper);
  border-color: var(--ys-violet-700);
}

.ys-page .ys-fhero .ys-swapbtn:hover .ys-swapbtn__label,
.ys-page .ys-fhero .ys-swapbtn:hover .ys-swapbtn__ico,
.ys-page .ys-fhero .ys-swapbtn:focus-visible .ys-swapbtn__label,
.ys-page .ys-fhero .ys-swapbtn:focus-visible .ys-swapbtn__ico {
  background: var(--ys-violet-900);
  color: var(--ys-paper);
  border-color: var(--ys-violet-900);
}

/* Progress ticks over a light sky. Violet-300 is the spent step, violet-700
   the one being read. */
.ys-page .ys-story__ticks li {
  background: var(--ys-violet-300);
}

.ys-page .ys-story__ticks li[data-on="true"] {
  background: var(--ys-violet-700);
}

/* -- The mobile nav -------------------------------------------------------- */

/* It drops out of the bar, so it takes violet-900 to sit apart from the page
   behind it, and its CTA takes the paper fill the page's buttons use. */
.ys-header--clear .ys-mobilenav[data-open="true"] {
  background: var(--ys-violet-900);
}

.ys-mobilenav .ys-btn {
  background: var(--ys-paper);
  color: var(--ys-violet-700);
}

.ys-mobilenav .ys-btn:hover {
  background: transparent;
  color: var(--ys-paper);
  border: var(--ys-hairline) solid var(--ys-paper);
}

/* -- The footer ------------------------------------------------------------ */

/* Unchanged, and its type has to be stated now that <body> carries paper —
   inheriting into a light footer would leave it invisible. */
.ys-footer {
  color: var(--ys-ink);
}

@media print {
  .ys-header,
  .ys-footer,
  .ys-skip {
    display: none;
  }
  body {
    background: var(--ys-surface);
    color: var(--ys-ink);
  }
}
