/* ==========================================================================
   Tokens
   ========================================================================== */

/* New system (Figma "Pofo"): grey field, Noi Grotesk, 12-col grid with 24px margin and
   gutter, 4px radius. Used by the header, intro, index and case pages. */
:root {
  --surface: #f2f2f2;
  --text: #000000;            /* Figma colour style "Black" */
  --text-1: #595959;          /* "Grey 30": confidential rows in the list */
  --text-2: #727272;          /* "Grey 40": secondary: labels, card services */
  --text-3: #a7a7a7;          /* "Grey 60": inactive nav / toggles, section rules */
  --placeholder: #dadada;     /* Figma used #d9d9d9 and #dddddd; unified */
  --line: #d6d6d6;
  --cursor-bg: rgb(0 0 0 / 0.55);

  --font-text: "Noi Grotesk", "Helvetica Neue", Helvetica, Arial, sans-serif;

  /* Type scale = Figma text styles. Display sizes are set at the 1728px frame and scale with
     the viewport (between a phone minimum and a large-screen maximum). */
  --word-space: 0.28em;                                 /* Noi Grotesk's word space: text → arrow */
  /* Noi Grotesk's stylistic sets (Santeri, 2026-11-02): ss12 double-storey g everywhere; display
     sizes add ss02 no-tail a and ss09 headline letters (set at the end of this file). ss05 creamy
     alternates and ss04 creamy f and t were on for a while; he turned both off (2026-11-02 and
     2026-11-04). A child's font-feature-settings replaces its parent's, so the display list
     repeats ss12 */
  --ss-text: "ss12";
  --ss-display: "ss12", "ss02", "ss09";
  --fs-body: 16px;                                      /* title xs */
  --fs-ui: 20px;                                        /* title sm */
  --fs-display-md: clamp(36px, 3.704vw, 80px);          /* display md: 64px at 1728 */
  --fs-display-lg: clamp(44px, 4.977vw, 108px);         /* display lg: 86px at 1728 */
  --fs-display-m: min(48px, 11.94vw);                   /* Mobile/display m: 48px at 402 */
  --lh-display-m: 0.875;                                /* 42 / 48 */
  --lh-text: 1.2;                                       /* Figma "auto" for Noi Grotesk */
  --lh-display-md: 0.875;                               /* 56 / 64 */
  --lh-display-lg: 0.884;                               /* 76 / 86 */
  --lh-body-sm: 1.25;                                   /* body sm (case project info): 16 / 20 */
  --track-body-sm: 0.01em;                              /* … +1% */
  --track-display: -0.05em;
  --fs-intro: var(--fs-display-md);

  --space-1: 12px;
  --space-2: 24px;
  --space-3: 32px;
  --space-4: 128px;           /* between case sections and gallery rows */
  --space-5: 192px;
  --space-6: 256px;           /* above and below the case info block */
  --gutter: 24px;
  --g: var(--gutter);         /* 12-col grid: 24px margin, 24px gutter */
  --col-gap: var(--gutter);
  --nav-pull-max: var(--gutter); /* extra header inset once the page scrolls */
  --nav-h: 74px;              /* header height; case content starts below it */
  /* --screen-h: the screen height, measured by app.js (falls back to 100svh). Phones slide their
     toolbars in and out as you scroll, which resizes the viewport in some browsers; anything sized
     by the screen uses this fixed measure so the page below it doesn't shift. */
  --radius: 4px;

  --ease: cubic-bezier(.77, 0, .18, 1);
  --ease-out: cubic-bezier(.16, 1, .3, 1);

  color-scheme: light;
}

/* Noi Grotesk Regular by Studio Feixen, the only weight the site uses (web licence bought 2026-11-02) */
@font-face {
  font-family: "Noi Grotesk";
  src: url("fonts/NoiGrotesk/NoiGrotesk-Regular.woff2") format("woff2"),
       url("fonts/NoiGrotesk/NoiGrotesk-Regular.woff") format("woff");
  font-weight: 400; font-style: normal; font-display: swap;
}

/* Type styles (Figma names) */
.t-xs { font-size: var(--fs-body); line-height: var(--lh-text); }
.t-sm { font-size: var(--fs-ui); line-height: var(--lh-text); }
.t-display-md { font-size: var(--fs-display-md); line-height: var(--lh-display-md); letter-spacing: var(--track-display); }
.t-display-lg { font-size: var(--fs-display-lg); line-height: var(--lh-display-lg); letter-spacing: var(--track-display); }

/* Arrows are Noi Grotesk's own → ← ↑ ↓ (app.js arrowHTML), set like letters a word space from
   their words (inline-block so the hover nudge can move them) */
.arrow { display: inline-block; flex: none; }
.with-arrow { display: inline-flex; align-items: baseline; gap: var(--word-space); }

/* ==========================================================================
   Base
   ========================================================================== */

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

/* No grey tap flash on touch screens: iOS can leave it stuck on a card when a swipe starts on it.
   Hover effects throughout are for mouse and trackpad only (@media (hover: hover) and
   (pointer: fine)); a touch screen keeps its last tapped element "hovered" otherwise. */
html { -webkit-text-size-adjust: 100%; -webkit-tap-highlight-color: transparent; }

/* Every page sits on the Figma surface (#f2f2f2) */
body {
  margin: 0;
  background: var(--surface);
  color: var(--text);
  font: 400 var(--fs-body)/var(--lh-text) var(--font-text);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  overflow-x: hidden;
}

[hidden] { display: none !important; }
img, video { display: block; max-width: 100%; }
picture { display: contents; }   /* only picks the phone or desktop image; the img lays out */
a { color: inherit; text-decoration: none; }
button { font: inherit; color: inherit; background: none; border: 0; padding: 0; cursor: pointer; }
p { margin: 0; }
h1, h2, h3 { margin: 0; font-weight: 400; }
ul { list-style: none; margin: 0; padding: 0; }
em { font-style: normal; }

:focus-visible { outline: 1px solid currentColor; outline-offset: 4px; }

/* Skip link: clipped to nothing until a keyboard reaches it. Moved off-screen instead, iOS could
   still show it at the top while scrolling, and taps there count as :focus but not :focus-visible */
.skip {
  position: fixed; left: var(--gutter); top: calc(env(safe-area-inset-top, 0px) + 8px); z-index: 200;
  background: var(--text); color: var(--surface); padding: 6px 10px;
}
.skip:not(:focus-visible) { clip-path: inset(50%); width: 1px; height: 1px; padding: 0; overflow: hidden; white-space: nowrap; }

::selection { background: var(--text); color: var(--surface); }

/* ==========================================================================
   Helpers
   ========================================================================== */

/* Masks for split text: clip-path (not overflow), open a little at the sides for the tracking */
.mask { display: inline-block; clip-path: inset(-0.35em -0.25em -0.3em -0.25em); }
.mask > span { display: inline-block; }

/* Words that fade up one after another (app.js wordsPlain) */
.wd { display: inline-block; }

/* Vertical text swap: two lines in a one-line window, swapped word by word (.w, see header) */
.swap { position: relative; display: block; overflow: hidden; height: calc(var(--lh-text) * 1em); }
.swap > span { display: block; white-space: nowrap; }
.swap > span + span { position: absolute; left: 0; top: 0; }

/* ==========================================================================
   Header
   ========================================================================== */

.site-header {
  --nav-pull: 0;                    /* 0 → 1 once scrolled; tweened by app.js */
  --nav-in: calc(var(--nav-pull-max) * var(--nav-pull));
  position: fixed; inset: 0 0 auto 0; z-index: 60;
  display: grid; grid-template-columns: 1fr auto 1fr; align-items: center; column-gap: var(--space-2);
  padding: calc(env(safe-area-inset-top, 0px) + var(--space-2)) var(--gutter) var(--space-2);
  font: 400 var(--fs-ui)/var(--lh-text) var(--font-text);
  /* Black on the light field, light over imagery. The text is drawn black and inverted to white
     before the difference blend: white text blended this way is smoothed as light-on-dark and
     reads a weight heavier than the Regular it is. */
  color: #000;
  filter: invert(1);
  mix-blend-mode: difference;
  pointer-events: none;
}
.site-header > * { pointer-events: auto; }
/* The pull moves the sides in by --nav-in. It's a translate on their own layers, not padding:
   relaid out every frame, each word landed on its own whole pixel and they drifted apart. */
.nav-left { translate: var(--nav-in) 0; }
.contact-link, .menu-btn { translate: calc(-1 * var(--nav-in)) 0; }
.nav-left, .contact-link, .menu-btn { will-change: transform; }

/* Scroll lock (contact panel): the scrollbar's width is added back so the layout doesn't shift */
html.is-locked { overflow: hidden; padding-right: var(--sbw, 0px); }
html.is-locked .site-header { padding-right: calc(var(--gutter) + var(--sbw, 0px)); }

/* Left slot: title (with hover swap to the name) or, on case pages, "← Back" */
/* Clipped top and bottom for the slot swap, and open to the left so the Back arrow's
   hover nudge isn't cut */
.nav-left {
  position: relative; justify-self: start; min-width: 0;
  height: calc(var(--lh-text) * 1em); clip-path: inset(0 0 0 -0.5em);
}
/* The slots and lines stay put; their words (.w) move one after another (app.js, "Left slot").
   Each word is a full line tall so ±105% clears the window. The inner span is left free for
   the entrance rise. */
.nav-slot { display: block; white-space: nowrap; }
.nav-slot.back-link { position: absolute; left: 0; top: 0; visibility: hidden; }
.nav-left .w, .nav-left .w > span {
  display: inline-flex; align-items: center; height: calc(var(--lh-text) * 1em);
}
.back-link .with-arrow { display: flex; }   /* block-level, so Back sits on the title's line, not a baseline lower */
/* The Back arrow sits on the baseline like a letter (cap height), not centred in the line */
.back-link .w > span { display: block; line-height: var(--lh-text); }
.back-link .arrow { display: inline-block; vertical-align: baseline; }

.site-nav { display: flex; gap: 10px; }
.site-nav a { opacity: .33; transition: opacity .35s; }   /* .33 white in difference ≈ #a7a7a7 on the field */
.site-nav a.is-active { opacity: 1; }
@media (hover: hover) and (pointer: fine) { .site-nav a:hover { opacity: 1; } }
/* Hi Shine opens in a new tab: on hover its ↗ (the profile links' arrow) appears a word space after
   the name, outside the layout so the nav doesn't move, easing in along its own diagonal */
.nav-ext .hm > span { position: relative; }
.nav-ext .arrow {
  position: absolute; top: 0; left: calc(100% + var(--word-space));
  opacity: 0; translate: -2px 2px; transition: opacity .35s, translate .6s var(--ease-out);
}
@media (hover: hover) and (pointer: fine) { .nav-ext:hover .arrow { opacity: 1; translate: 0 0; } }

/* Header entrance masks: each item rises through its own window */
.site-header .hm { display: block; clip-path: inset(0 0 0 -0.5em); } /* vertical mask; left open for the arrow nudge */
.site-header .nav-ext .hm { clip-path: inset(0 -1.5em 0 -0.5em); }   /* right open too, for the hover ↗ */
.site-header .hm > * { display: block; }
.site-header .hm > .with-arrow { display: inline-flex; }

/* "Contact →" and "Close ✕" share one mask, Close right-aligned over it (app.js swaps them) */
.contact-link { justify-self: end; white-space: nowrap; }
.cl-swap { position: relative; }
.cl { display: flex; gap: var(--word-space); }
/* Each piece a full line tall, so moving it by ±105% clears the mask (the arrow and the cross
   alone are shorter than the line and would leave their tips showing) */
.cl > span { display: block; height: calc(var(--lh-text) * 1em); line-height: var(--lh-text); white-space: nowrap; }
.cl-close { position: absolute; top: 0; right: 0; }
/* The cross sits on the text's baseline like a letter, at its Figma size */
.cl svg { display: inline-block; vertical-align: baseline; }
.close-icon { width: 0.653em; height: 0.653em; fill: currentColor; }   /* the cross of an 18px icon at 20 */
/* Open at the sides as well, so the arrow's hover nudge isn't cut at the right edge */
.contact-link .hm { clip-path: inset(0 -0.5em); }
.contact-link .arrow, .back-link .arrow { transition: translate .6s var(--ease-out); }
@media (hover: hover) and (pointer: fine) {
  .contact-link:hover .arrow { translate: 3px 0; }
  .back-link:hover .arrow { translate: -3px 0; }
}

/* Contact panel open (Figma "Contact Close"): over the black, every header item in #d9d9d9, the
   active one included (white at 85% in the blend) */
.site-header { transition: opacity .4s; }
.is-contact .site-header { opacity: .85; }
.is-contact .site-nav a { opacity: 1; }

/* Mobile menu button (Figma "Icon Menu" ↔ "Icon Close"); app.js draws and morphs the shapes */
.menu-btn { display: none; justify-self: end; }
.menu-icon { display: block; width: 24px; height: 24px; fill: currentColor; }

/* ==========================================================================
   Mobile menu
   ========================================================================== */

/* A dark curtain under the header (which turns light over it through its blend);
   links sit at the bottom in display xl, the current page in white */
.menu {
  position: fixed; inset: 0; z-index: 58;
  display: none; flex-direction: column; justify-content: flex-end;
  padding: 0 var(--gutter) calc(env(safe-area-inset-bottom, 0px) + var(--gutter));
  background: #0d0d0d; color: var(--text-1);   /* inactive links in Grey 30, as in Figma */
  clip-path: inset(0% 0% 100% 0%);
  touch-action: none; overscroll-behavior: contain;
}
.menu-nav { display: flex; flex-direction: column; align-items: flex-start; gap: 4px; }
.menu-nav a, .menu-nav button {
  font: 400 var(--fs-display-m)/var(--lh-display-m) var(--font-text);   /* Mobile/display m */
  letter-spacing: -0.04em; text-align: left;
  color: inherit; transition: color .35s;
}
.menu-nav .is-active { color: #fff; }
/* Masks clip at the line box (rows are 4px apart) but not at the sides, where the
   negative tracking would cut the last letter */
.mm { display: block; clip-path: inset(0 -0.2em); }
.mm > span { display: block; }

/* ==========================================================================
   Intro
   ========================================================================== */

.intro {
  position: fixed; inset: 0; z-index: 90;
  background: var(--surface);
  display: none;
  font: 400 var(--fs-intro)/var(--lh-display-md) var(--font-text);   /* display md */
  letter-spacing: var(--track-display);
  color: var(--text);
}
.is-intro .intro { display: block; }
/* Everything is placed from the centre and moved with transforms (no reflow → no letter jitter) */
.intro-row { position: absolute; inset: 0; --intro-gap: var(--space-2); }
.intro-word, .intro-frame { position: absolute; left: 50%; top: 50%; }
.intro-word { white-space: nowrap; will-change: transform; }
.intro-word .mask > span { will-change: transform; backface-visibility: hidden; }  /* letters stay composited: no pixel snapping as they settle */
.intro-frame {
  width: 1.25em; height: 1.5625em;                 /* pill: 80 × 100 at 64px */
  overflow: hidden; border-radius: 0.625em;
  background: var(--placeholder);
}
.intro-frame > * {
  position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover;
  opacity: 0;
}
.intro-frame > .is-on { opacity: 1; }

/* ==========================================================================
   Index
   ========================================================================== */

.view-index {
  font: 400 var(--fs-body)/var(--lh-text) var(--font-text);
  color: var(--text);
}

/* Card title: the shared two-line label (title + optional secondary row) */
.ctitle { display: flex; flex-direction: column; }
.ctitle .sub { color: var(--text-2); }
.ctitle .role { white-space: nowrap; }   /* a role never breaks inside itself, only between roles */

/* Hero / reel: pinned, so Selected Work rises over it */
.hero {
  position: sticky; top: 0; z-index: 0;
  height: var(--screen-h, 100svh); min-height: 520px;
  display: grid; place-items: center;
  padding: 96px var(--gutter) 80px;
}
.reel { width: min(64.35vw, calc((var(--screen-h, 100svh) - 250px) * 1.7778)); display: flex; flex-direction: column; gap: var(--space-1); transform-origin: 50% 45%; }
.reel-media {
  position: relative; aspect-ratio: 16 / 9; max-width: 100%;
  border-radius: var(--radius); overflow: hidden; background: var(--placeholder);
}
.reel-media video, .reel-media img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.scroll-cue {
  position: absolute; left: 50%; bottom: var(--gutter);
  transform: translateX(-50%);
  font-size: var(--fs-ui); padding: 8px;
}
.scroll-cue span { display: block; animation: cue 2.4s var(--ease) infinite; }
@keyframes cue { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(5px); } }

/* Selected work: covers the pinned hero. At least a screen tall with the footer included, so a
   short list (list mode) leaves the footer on screen and nothing more to scroll. */
.work {
  position: relative; z-index: 1;
  display: flex; flex-direction: column;
  min-height: var(--screen-h, 100svh);
  padding: 117px var(--gutter) 0;
  background: var(--surface);
}
.work-head {
  display: flex; justify-content: space-between; align-items: baseline; gap: var(--space-2);
  margin-bottom: var(--space-3);
}
.work-title { font-size: var(--fs-ui); }
.work-toggle { display: flex; gap: 12px; }
.work-toggle button { color: var(--text-3); transition: color .35s; }
.work-toggle button.is-active { color: var(--text); }
@media (hover: hover) and (pointer: fine) { .work-toggle button:hover { color: var(--text); } }

.cards { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--space-2); }
.card { display: flex; flex-direction: column; gap: var(--space-1); min-width: 0; }
.card-media {
  position: relative; aspect-ratio: 4 / 5; max-width: 100%;
  border-radius: var(--radius); overflow: hidden; background: var(--placeholder);
}
.card-media .cm-in { position: absolute; inset: 0; }   /* scroll-in scale lives here, hover zoom on the image */
.card-media img, .card-media video {
  position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover;
  transition: transform 1.4s var(--ease-out);
}
.card:focus-visible .card-media img { transform: scale(1.035); }
@media (hover: hover) and (pointer: fine) { .card:hover .card-media img { transform: scale(1.035); } }
/* A square master (data.js square) zooms in 1.25× on its centre in the 4:5 thumbnails (cards, list
   preview, intro, and the next project on phones), for a tighter crop than a plain 4:5 cover; the
   flight to the case eases it back to the hero's 1× (landFlip). A case can set its own --zoom, and
   pin it to the top or bottom edge with --anchor (data.js square). Hover zooms on top of it */
.is-master { --zoom: 1.25; }
.card-media.is-master img, .list-peek.is-master img, .intro-frame > .is-master {
  transform: scale(var(--zoom)); transform-origin: 50% var(--anchor, 50%);
}
.card:focus-visible .card-media.is-master img { transform: scale(calc(var(--zoom) * 1.035)); }
@media (hover: hover) and (pointer: fine) { .card:hover .card-media.is-master img { transform: scale(calc(var(--zoom) * 1.035)); } }
.card:focus-visible { outline: none; }
.card:focus-visible .card-media { outline: 1px solid var(--text); outline-offset: 4px; }

/* List mode (Figma "Work List"): column labels, then a 56px row per project. Type, Contribution
   and Year keep their widths from the 1728 frame (260 / 402 / 118) and Project takes the rest, so
   a narrower screen narrows only the project column, which has the most room to give. */
.work-list { margin-top: 28px; }       /* labels 60px below the heading, with the heading's margin */
.wl-head, .wl-row {
  display: grid; grid-template-columns: minmax(0, 1fr) 260px 402px 118px; column-gap: var(--gutter);
}
/* Once Project is down to the Type column's width it holds there, and the other columns give up
   their spare room instead, never narrower than their longest entry (until the list stacks
   below 1000) */
@media (max-width: 1175px) {
  .wl-head, .wl-row {
    grid-template-columns: 260px minmax(max-content, 260px) minmax(max-content, 402px) minmax(max-content, 118px);
  }
}
.wl-head { margin-bottom: var(--space-3); color: var(--text-2); }
.wl-row {
  padding: 15.5px 0; border-top: 1px solid var(--text-3);
  font-size: var(--fs-ui);
}
.wl-row:last-child { border-bottom: 1px solid var(--text-3); }
/* Confidential rows sit a step back from the linked ones, the note further still */
.wl-row.is-confidential { color: var(--text-1); }
.wl-note { color: var(--text-3); }   /* "(Confidential)" */
/* Hovering a row fades the others' text by the same proportion whatever its colour (black,
   the confidential grey, the note), leaving the rules alone. The column labels don't count. */
.wl-row > * { transition: opacity .35s; }
@media (hover: hover) and (pointer: fine) {
  .work-list:has(.wl-row:hover) .wl-row:not(:hover) > * { opacity: .5; }
}

.list-peek {
  position: fixed; left: 0; top: 0; z-index: 50;
  width: clamp(160px, 14vw, 240px); aspect-ratio: 4 / 5;
  border-radius: var(--radius); overflow: hidden; background: var(--placeholder);
  pointer-events: none; opacity: 0; transform: scale(.9);
  will-change: transform;   /* its own layer: following the pointer moves it without a repaint */
}
.list-peek img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
/* Confidential work: the "no entry" mark on the grey ground; a 1px line at the peek's size
   (Figma's 2px on its 544-wide frame comes out under 1px here) */
.list-peek .peek-confidential { position: absolute; inset: 0; width: 100%; height: 100%; }
.peek-confidential > * { fill: none; stroke: var(--text-3); stroke-width: 1px; vector-effect: non-scaling-stroke; }

/* Hover cursor over cases: the wrapper follows the pointer, the pill scales in and out */
.cursor-view { position: fixed; left: 0; top: 0; z-index: 80; pointer-events: none; will-change: transform; }
.cursor-pill {
  display: block;
  padding: 30px 40px; border-radius: 999px;
  background: var(--cursor-bg); color: #fff;
  font: 400 var(--fs-ui)/var(--lh-text) var(--font-text);
  white-space: nowrap;
  visibility: hidden;
}
@media (hover: hover) and (pointer: fine) {
  .has-cursor .card { cursor: none; }
}

/* ==========================================================================
   Shared view scaffolding + 12-col grid (24px margin, 24px gutter)
   ========================================================================== */

#app { outline: none; }
.view { position: relative; }

.grid {
  display: grid; grid-template-columns: repeat(12, minmax(0, 1fr));
  column-gap: var(--col-gap); padding: 0 var(--g);
}

/* Rounded media box with reveal support (case gallery, about band) */
.g-item { position: relative; overflow: hidden; background: var(--placeholder); margin: 0; aspect-ratio: var(--ar, 16 / 9); max-width: 100%; border-radius: var(--radius); }
.g-item .g-in { position: absolute; inset: 0; }
.g-item img, .g-item video { width: 100%; height: 100%; object-fit: cover; }
.g-item .clip-still { display: none; }   /* a clip's phone still (data.js `clip`, phone: 'still') */

/* Click-to-play film: the poster with Figma's "PlayButton" (PLAY on a dark pill, 85 × 52, the same
   on phones), then the film with the browser's controls */
.g-film .g-in > video { position: absolute; inset: 0; background: var(--matte, #000); opacity: 0; transition: opacity .4s; }   /* --matte: data.js film */
.g-film.is-playing .g-in > video { opacity: 1; }
/* An embedded film (Vimeo): Vimeo's own player, with its own thumbnail and play button (Santeri,
   2026-11-02), so no PLAY pill (is-vimeo, app.js). Our poster covers the box while the player
   loads, opaque so a half-loaded player never shows through, and fades once it's ready (is-armed) */
.g-embed .g-in > iframe { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; }
.g-embed .g-in > img, .g-embed .g-in > picture { display: block; position: absolute; inset: 0; z-index: 1; background: var(--placeholder); transition: opacity .4s; }
.g-embed.is-vimeo.is-armed .g-in > img, .g-embed.is-vimeo.is-armed .g-in > picture { opacity: 0; pointer-events: none; }
.g-film.is-vimeo .film-play { display: none; }
.film-play {
  position: absolute; inset: 0; z-index: 2; display: grid; place-items: center;
  padding: 0; border: 0; background: none; color: #fff; cursor: pointer; transition: opacity .4s;   /* white, as in Figma */
}
.film-play .play-pill {
  padding: 16px 24px; border-radius: 999px; background: rgb(26 26 26 / 0.75);   /* #1a1a1a at 75% */
  font: 400 16px/20px var(--font-text); letter-spacing: 0.005em;                  /* label sm */
  transition: background-color .6s var(--ease-out);                               /* as Button's */
}
/* PlayButton State=Hover, anywhere over the film: the pill lightens a step, #404040 at 75% */
@media (hover: hover) and (pointer: fine) {
  .film-play:not(:disabled):hover .play-pill { background: rgb(64 64 64 / 0.75); }
}
.g-film.is-playing .film-play { opacity: 0; pointer-events: none; }
/* A tinted film's own controls (app.js filmControls), in its colour (--tint) with no shade behind
   them. Icons 20px in 40px targets, pulled out so the icons line up with the bar's ends */
.film-ui {
  position: absolute; left: 0; right: 0; bottom: 0; z-index: 3; padding: 0 24px 20px;
  color: var(--tint); opacity: 0; pointer-events: none; transition: opacity .4s var(--ease-out);
}
.g-film.is-playing .film-ui.is-shown { opacity: 1; pointer-events: auto; }
.g-film.is-playing.is-idle { cursor: none; }
.fu-row { display: flex; align-items: center; margin: 0 -10px; }
.fu-btn { display: grid; place-items: center; width: 40px; height: 40px; padding: 0; border: 0; background: none; color: inherit; cursor: pointer; }
.fu-btn svg { display: block; width: 20px; height: 20px; }
.fu-btn .fu-b, .film-ui.is-paused .fu-play .fu-a, .film-ui.is-muted .fu-mute .fu-a, .film-ui.is-full .fu-full .fu-a { display: none; }
.film-ui.is-paused .fu-play .fu-b, .film-ui.is-muted .fu-mute .fu-b, .film-ui.is-full .fu-full .fu-b { display: block; }
.fu-time { margin-left: 4px; font: 400 16px/20px var(--font-text); letter-spacing: 0.005em; font-variant-numeric: tabular-nums; }
.fu-mute { margin-left: auto; }
.fu-bar { position: relative; height: 16px; cursor: pointer; touch-action: none; }
.fu-bar::before, .fu-bar span { content: ""; position: absolute; left: 0; top: 6px; height: 4px; border-radius: 2px; background: currentColor; }
.fu-bar::before { right: 0; opacity: .2; }
.fu-bar span { width: calc(var(--p, 0) * 100%); }
/* Full screen: the whole film on its matte (white for Finnair's, so its blue controls stay legible) */
.g-film:fullscreen { border-radius: 0; clip-path: none; background: var(--matte, #000); }
.g-film:fullscreen .g-in { transform: none; }
.g-film:fullscreen .g-in > video { object-fit: contain; }
@media (max-width: 640px) {
  .film-ui { padding: 0 16px 10px; }
}

/* ==========================================================================
   Case study (Figma "Case")
   Hero + display lg title fill the first screen; the rest sits on columns 2–11.
   ========================================================================== */

.view-case {
  padding-top: var(--nav-h);
  font: 400 var(--fs-body)/var(--lh-text) var(--font-text);
  color: var(--text);
}
.view-case .lbl { color: var(--text-1); }   /* labels in Grey 30 */
/* Footer 256px below the next project (Figma Frame 41), inside its own 24px padding */
.view-case .site-foot { padding-top: calc(var(--space-6) + var(--gutter)); }

.case-hero {
  position: relative; margin: 0 var(--gutter);
  /* 1680 × 929 at the 1728 frame, capped so the title still fits on the first screen */
  height: min(calc((100vw - 2 * var(--gutter)) * 0.553),
              calc(var(--screen-h, 100svh) - var(--nav-h) - 2 * var(--space-2) - var(--fs-display-lg) * var(--lh-display-lg)));
  min-height: 280px;
  overflow: hidden; border-radius: var(--radius); background: var(--placeholder);
}
.case-hero .hero-media { position: absolute; inset: 0; }
.case-hero .hero-media img, .case-hero .hero-media video { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
/* A square master's own hero zoom and anchor (data.js square: hero / heroPhone; Figma's Hero zoom and
   Hero anchor): the anchor pins that edge of the square to the box's edge, at any zoom */
.case-hero .hero-media img { object-position: 50% var(--ha, 50%); transform: scale(var(--hz, 1)); transform-origin: 50% var(--ha, 50%); }
@media (max-width: 640px) {
  .case-hero .hero-media img { object-position: 50% var(--ha-m, 50%); transform: scale(var(--hz-m, 1)); transform-origin: 50% var(--ha-m, 50%); }
}

.case-title { margin: var(--space-2) var(--gutter) 0; text-wrap: balance; }

/* Info block on columns 4–9, centred between the title and the gallery:
   Client / Role / Disciplines stacked on 4–5, project info on 6–9 */
.case-info { padding-top: var(--space-6); row-gap: var(--space-3); align-items: start; }
.case-info .ci-meta { grid-column: 4 / span 2; display: flex; flex-direction: column; gap: var(--space-2); }
.case-info .ci-text { grid-column: 6 / span 4; }
.case-info .ci-text > div { line-height: var(--lh-body-sm); letter-spacing: var(--track-body-sm); }   /* the text in body sm */
.case-info .ctitle p + p { margin-top: calc(var(--lh-body-sm) * 1em); }   /* a blank line between paragraphs, as in Figma */
/* After the gallery (or the info block when there's none) 256px, as between all the case sections
   in Figma; credits → next project is 128 */
.case-info + .case-rule, .case-gallery + .case-rule { padding-top: var(--space-6); }

.case-gallery { padding-top: var(--space-6); display: flex; flex-direction: column; gap: var(--space-4); }
.case-row { align-items: start; }
.case-row .cr-in { grid-column: 2 / span 10; display: grid; gap: var(--col-gap); align-items: start; }
.case-row.pair .cr-in { grid-template-columns: 1fr 1fr; }
.case-row.trio .cr-in { grid-template-columns: 1fr 1fr 1fr; }
.case-row.center .cr-in { grid-column: 4 / span 6; }
.case-row.only-phone { display: none; }   /* a row for one layout only (data.js `only`); videos in a hidden row never load */

/* Credits and next project: rule-topped rows on columns 2–11, label left, content from column 7 */
.case-rule { padding-top: var(--space-4); }
.case-rule .cr-in {
  grid-column: 2 / span 10;
  display: grid; grid-template-columns: 1fr 1fr; column-gap: var(--col-gap);
  padding-top: var(--space-2); border-top: 1px solid var(--text-3);
}
.case-credits ul { display: flex; flex-direction: column; gap: var(--space-2); }

.case-next a.cr-in { color: inherit; }
.case-next .cn-left { display: flex; flex-direction: column; justify-content: space-between; gap: var(--space-3); }
.next-media {
  position: relative; aspect-ratio: 16 / 9; max-width: 100%;
  overflow: hidden; border-radius: var(--radius); background: var(--placeholder);
}
.next-media img, .next-media video { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; transition: transform 1.4s var(--ease-out); }
@media (hover: hover) and (pointer: fine) {
  .case-next a:hover .next-media img, .case-next a:hover .next-media video { transform: scale(1.035); }
}
@media (hover: hover) and (pointer: fine) { .has-cursor .case-next a { cursor: none; } }

/* ==========================================================================
   About (Figma "About Desktop")
   The title on columns 1–7 with the portrait on 11–12; below it labelled rows, the label on
   the left and the content from column 7 (where "Work with me", display md, also sits).
   Two views fill the screen at any height:
   - at the top: the title block centred above the About row, which ends 35px above the fold;
   - scrolled to the bottom: from the About row, 35px under the header, down to the footer. The
     space above and below "Work with me" shares out what's left, 256 : 160 as in Figma.
   ========================================================================== */

.view-about {
  padding-top: var(--nav-h);
  font: 400 var(--fs-ui)/var(--lh-text) var(--font-text);   /* title sm */
  color: var(--text);
  --lh-about: 1.4;   /* the rows' content in body md: 20 / 28 */
}
.view-about .lbl { color: var(--text-1); font: inherit; }   /* section labels are h2s */

.about-top { align-items: start; }
.about-title { grid-column: 1 / span 7; text-wrap: balance; }
.about-portrait { grid-column: 11 / span 2; }

/* First screen: the About row at the bottom, the title and portrait centred above it (with at
   least 64px clear above and below on short screens) */
.about-first {
  min-height: calc(var(--screen-h, 100svh) - var(--nav-h) - 35px);
  display: flex; flex-direction: column;
}
.about-first .about-top { flex: 1 0 auto; align-content: center; padding-block: 64px; }
/* The rest, from 64px under the About row to the bottom of the page: tall enough that, scrolled to
   the end, the About row sits 35px under the header (app.js measures --about-row-h). On short
   screens the gaps keep at least 64 and 48 and the page simply runs longer. */
.about-rest {
  min-height: calc(var(--screen-h, 100svh) - var(--nav-h) - 35px - var(--about-row-h, 0px));
  display: flex; flex-direction: column;
}
.about-gap.above { flex: 256 1 0; min-height: 64px; }
.about-gap.below { flex: 160 1 0; min-height: 48px; }
.about-rows { padding-top: 64px; display: flex; flex-direction: column; gap: 64px; }
.about-row { align-items: start; }
.about-row .lbl { grid-column: 1 / span 4; }
.about-content { grid-column: 7 / span 6; margin: 0; line-height: var(--lh-about); }
.about-content p { margin: 0; }
.list-name { white-space: nowrap; }   /* comma lists, everywhere: a name with its comma never breaks (app.js nameList) */
.about-content p + p { margin-top: calc(var(--lh-about) * 1em); }   /* a blank line between paragraphs */
/* Work history: the years on column 7, the role from column 8 */
.about-history { list-style: none; padding: 0; }
.about-history li { display: grid; grid-template-columns: minmax(5.9em, calc((100% - 5 * var(--col-gap)) / 6)) minmax(0, 1fr); column-gap: var(--col-gap); }

.about-cta .about-content { display: flex; flex-direction: column; align-items: flex-start; }
.about-cta p { margin-top: 16px; font-size: var(--fs-body); line-height: 1.25; letter-spacing: 0.01em; }   /* body sm: 16 / 20, +1% */
.about-cta .pill { margin-top: 48px; }
/* Footer 128px below, inside its own 24px padding (and the page's 24) */
.view-about .site-foot { padding-top: var(--gutter); }   /* the gap above it is .about-gap.below */

/* Figma "Button" (Pill, sm): a grey pill with the label and an arrow, darker on hover */
.pill {
  display: inline-flex; align-items: baseline; gap: var(--word-space);   /* label, word space, arrow */
  padding: 8px 16px; border: 0; border-radius: 999px;
  background: var(--placeholder); color: var(--text);
  font: 400 16px/var(--lh-text) var(--font-text); letter-spacing: 0;   /* title xs (16 on phones too) */
  cursor: pointer; transition: background-color .6s var(--ease-out);
}
/* With a scroll fade-in, [data-fade]'s transition (set later) would replace the colour's */
.pill[data-fade] { transition: opacity 1.1s var(--ease-out), transform 1.1s var(--ease-out), background-color .6s var(--ease-out); }
.pill .arrow { transition: translate .6s var(--ease-out); }
@media (hover: hover) and (pointer: fine) {
  .pill:hover { background: var(--text-3); }
  .pill:hover .arrow { translate: 3px 0; }   /* the nudge the header's Contact arrow has */
}

/* ==========================================================================
   Footer, contact, FLIP layer, reveals
   ========================================================================== */

.clock { white-space: nowrap; }
.clock .blink { animation: blink 1s steps(1) infinite; }
@keyframes blink { 50% { opacity: 0; } }

/* Figma "Footer": Back to top · the clock · the name and year, at title xs (16px, on phones too).
   Each page sets the space above it. */
.site-foot {
  display: grid; grid-template-columns: 1fr auto 1fr; gap: 1em; align-items: baseline;
  padding: var(--gutter) var(--gutter) calc(env(safe-area-inset-bottom, 0px) + var(--gutter));
  font-size: 16px; line-height: var(--lh-text);
}
.site-foot .clock { justify-self: center; }
.site-foot .copy { justify-self: end; color: var(--text-3); }
.to-top { justify-self: start; }
/* Index: the footer closes the work section, at least 160px below the work, pushed to the
   bottom of the screen when the work is shorter */
.view-index .site-foot {
  margin-top: auto;
  padding: 160px 0 calc(env(safe-area-inset-bottom, 0px) + var(--gutter));
}

/* Contact panel (Figma "Contact Desktop"): bg/page in Dark (Grey 5), under the header (which its blend turns light).
   The title on columns 1–6, the details in three columns on 7–12, centred between the header and
   the footer with 128px clear of each */
.contact {
  position: fixed; inset: 0; z-index: 58;
  background: #0d0d0d; color: #d9d9d9;
  font: 400 var(--fs-ui)/var(--lh-text) var(--font-text);   /* title sm */
  overflow-y: auto; overscroll-behavior: contain;
  clip-path: inset(0 0 100% 0);
}
.contact-inner { min-height: 100%; display: flex; flex-direction: column; }
.contact-main { flex: 1; align-items: center; align-content: center; row-gap: 48px; padding-block: calc(var(--nav-h) + 128px) 128px; }
.contact-title { grid-column: 1 / span 6; }
.contact-info { grid-column: 7 / span 6; display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); column-gap: var(--col-gap); }
.ci { display: flex; flex-direction: column; gap: 12px; min-width: 0; }
.ci-lbl { color: var(--text-1); }
.contact-email { text-align: left; overflow-wrap: anywhere; }
.contact-hint { color: var(--text-1); }   /* the email's next line, in its size (title sm), text/secondary */
.contact a, .contact-email { transition: color .35s; }
/* A profile link's ↗ (it opens in a new tab), a word after the name; it reveals with the words */
.contact a .arrow { transition: translate .6s var(--ease-out); }
@media (hover: hover) and (pointer: fine) {
  .contact a:hover, .contact-email:hover { color: #fff; }
  .contact a:hover .arrow { translate: 2px -2px; }   /* the other arrows' nudge, along its own direction */
}
.contact .site-foot .copy { color: var(--text-1); }

.flip-layer { position: fixed; inset: 0; z-index: 55; pointer-events: none; }
.flip-layer > div { position: fixed; overflow: hidden; background: var(--placeholder); }
.flip-layer .px { position: absolute; inset: 0 -9%; }
.flip-layer .hero-media { position: absolute; inset: 0; }   /* destination media riding along */
.flip-layer img, .flip-layer video { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.flip-layer picture { display: block; position: absolute; inset: 0; }   /* a box, so a frozen zoom applies */

[data-fade] { opacity: 0; transform: translateY(28px); transition: opacity 1.1s var(--ease-out), transform 1.1s var(--ease-out); }
[data-fade].is-in { opacity: 1; transform: none; }
/* Reveal clips carry the corner radius so the rounding shows during the wipe too */
.g-item[data-clip] { clip-path: inset(12% 0 0 0 round var(--radius)); transition: clip-path 1.3s var(--ease-out); }
.g-item[data-clip] .g-in { transform: scale(1.12); transition: transform 1.6s var(--ease-out); }
.g-item[data-clip].is-in { clip-path: inset(0 0 0 0 round var(--radius)); }
.g-item[data-clip].is-in .g-in { transform: none; }

body.is-loading .site-header, body.is-loading #app { visibility: hidden; }

/* ==========================================================================
   Responsive
   ========================================================================== */

@media (max-width: 1000px) {
  /* Below 1000 the desktop nav would run into the title, and the list's four columns into
     each other: the mobile nav (Figma "Navigation Mobile": title and menu icon; the nav and
     contact move into the menu) and the stacked list take over here, ahead of the phone layout */
  :root { --nav-pull-max: 0px; }
  .site-header { grid-template-columns: minmax(0, 1fr) auto; column-gap: 16px; }
  .site-nav, .contact-link { display: none; }
  .menu-btn { display: block; }
  .menu { display: flex; }
  /* List (Figma "Work List Mobile"): 32 under the heading, each row the project over the
     contribution (title sm, 20 / 24) and the year 8 below (title xs, 16 / 20), all in the row's
     colour, 24 inside the rules */
  .work-list { margin-top: 0; }
  .wl-head { display: none; }
  .wl-row { grid-template-columns: minmax(0, 1fr); padding: 24px 0; font-size: 20px; line-height: 24px; letter-spacing: -0.005em; }
  .wl-row > .wl-type { display: none; }
  .wl-row > .wl-year { margin-top: 8px; font-size: 16px; line-height: 20px; letter-spacing: 0.005em; }

  .cards { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .reel { width: 100%; }
  .case-info .ci-meta { grid-column: 1 / span 4; }
  .case-info .ci-text { grid-column: 5 / span 8; }
  .case-row .cr-in, .case-row.center .cr-in, .case-rule .cr-in { grid-column: 1 / -1; }
  :root { --space-4: 96px; --space-5: 128px; --space-6: 160px; }
  /* About: the content moves out to column 5, the title takes nine columns */
  .about-title { grid-column: 1 / span 9; }
  .about-portrait { grid-column: 10 / span 3; }
  .about-content { grid-column: 5 / span 8; }
  /* Contact: the title over the details */
  .contact-title, .contact-info { grid-column: 1 / -1; }
}

@media (max-width: 640px) {
  /* Mobile type styles: title xs 18, title sm 22, display md 48 / 42, display lg 56 / 50 (-4%) */
  :root { --gutter: 16px; --fs-ui: 22px; --fs-body: 18px; --nav-h: 59px; --space-4: 72px; --space-5: 88px; --space-6: 96px; }
  :root { --lh-text: 1.2222; }   /* Figma "auto" rounds the 18px lines to 22 */
  :root { --lh-body-sm: 1.2222; --track-body-sm: 0.005em; }   /* Mobile/body sm: 18 / 22, +0.5% */
  :root { --fs-display-md: min(48px, 11.94vw); --fs-display-lg: min(56px, 13.93vw); --lh-display-lg: 0.893; }
  .t-display-md, .t-display-lg { letter-spacing: -0.04em; }
  /* Figma "Navigation Mobile" spacing (the mobile nav itself starts at 1000) */
  .site-header { padding-block: calc(env(safe-area-inset-top, 0px) + 16px) 16px; }
  /* Figma "Intro Mobile": the name stacks at display md (64px at 402) and the frame opens between the lines */
  .intro { --fs-intro: min(64px, 16vw); }
  /* Figma "Index + Work Mobile", in the phone reel's own 4:5: full width between the gutters and
     centred, narrowing on short screens so it keeps at least 124px above and below */
  .hero { padding: 124px var(--gutter); }
  .reel { width: min(100%, calc((var(--screen-h, 100svh) - 248px) * 0.8)); }
  .reel-media { aspect-ratio: 4 / 5; }
  .scroll-cue { font-size: 20px; }  /* the ↓ at title sm, as on desktop */
  /* Figma "Index + Work Mobile" (GalleryCard "Mobile 2col"): two columns 16 apart, rows 24 apart,
     titles in Mobile/title xxs (13px) with the role line in Grey 30 */
  .cards { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 24px 16px; }
  .card .ctitle { font-size: 13px; }
  .card .ctitle .sub { color: var(--text-1); }
  .card .ctitle .role { display: block; }   /* one role per line */
  .card .ctitle .sep { display: none; }
  .work { padding-top: 76px; }
  .work-toggle { gap: 16px; }
  .view-index .site-foot { padding-top: calc(64px + var(--gutter)); }   /* 64 under the work, as in both phone frames */
  /* Figma "Case … Mobile": the hero and title fill the first screen under the nav (its own 16px
     padding is the gap above the hero), 16px apart and 16px off the bottom. Then 64px between the
     sections, and the gallery as one column 16px apart, each image in its phone crop and order */
  .case-top { display: flex; flex-direction: column; gap: 16px; height: calc(var(--screen-h, 100svh) - var(--nav-h)); padding-bottom: 16px; }
  .case-hero { flex: 1 1 auto; height: auto; min-height: 240px; }
  .case-title { margin: 0 var(--gutter); font-size: var(--fs-display-md); line-height: var(--lh-display-md); }   /* display md on phones */
  .case-info { padding-top: 64px; row-gap: var(--space-2); }
  .case-info .ci-meta, .case-info .ci-text { grid-column: 1 / -1; }
  .case-gallery { padding: 64px var(--gutter) 0; gap: 16px; }
  .case-row, .case-row .cr-in, .case-row.only-phone { display: contents; }
  .case-row.only-desktop { display: none; }
  /* data.js `phone: 'pair'`: a row that stays two-up, 16px apart (Distance's icons) */
  .case-row.m-pair { display: block; padding: 0; order: var(--mo, 0); }
  .case-row.m-pair .cr-in { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
  .case-gallery .g-item { aspect-ratio: var(--ar-m, var(--ar)); order: var(--mo, 0); }
  .g-item .clip-still { display: block; }
  .g-item .clip-still + video { display: none; }
  .case-rule, .case-info + .case-rule, .case-gallery + .case-rule { padding-top: 64px; }
  .case-rule .cr-in { grid-template-columns: minmax(0, 1fr); row-gap: var(--space-2); }
  /* Next project: the label, the 4:5 cover, then the name */
  .case-next .cn-left { display: contents; }
  .case-next .next-media { order: 1; aspect-ratio: 370 / 462; }
  /* …with a square master zoomed 1.25× like the cards (desktop's 16:9 box isn't zoomed) */
  .case-next .next-media.is-master img { transform: scale(var(--zoom)); transform-origin: 50% var(--anchor, 50%); }
  @media (hover: hover) and (pointer: fine) { .case-next a:hover .next-media.is-master img { transform: scale(calc(var(--zoom) * 1.035)); } }
  .case-next .cn-left > .t-display-md { order: 2; }
  .view-case .site-foot { padding-top: calc(64px + var(--gutter)); }
  /* Figma "About Mobile": the portrait over the title (display lg), the two centred on the first
     screen (app.js measures --about-hero-h); the About row 116 below. The rows at body xs (16px, +1%),
     32 apart with each label 12 above its content; 96 to "Work with me" (display md), 64 to the footer */
  .view-about { padding-top: 0; font-size: 16px; line-height: 1.25; letter-spacing: 0.01em; --lh-about: 1.25; }   /* body xs: 16 / 20, +1% */
  .about-first { min-height: 0; }
  .about-first .about-top { padding-block: max(calc(var(--nav-h) + 24px), calc((var(--screen-h, 100svh) - var(--about-hero-h, 404px)) / 2)) 0; }
  .about-portrait { grid-column: 1 / span 6; order: -1; }
  .about-title { grid-column: 1 / -1; margin-top: 27px; }
  .about-first .about-row { margin-top: 116px; }
  .about-rows { padding-top: 32px; gap: 32px; }
  .about-row { row-gap: 12px; }
  .about-row .lbl, .about-content { grid-column: 1 / -1; }
  /* As in Figma: the roles a column as wide as the longest one, against the right edge */
  .about-history { display: grid; grid-template-columns: minmax(max-content, 1fr) minmax(0, auto); column-gap: 12px; }
  .about-history li { display: contents; }
  .about-cta p { font-size: 16px; }
  .about-cta .pill { margin-top: 48px; }
  .about-rest { min-height: 0; }   /* too long to fill one phone screen: fixed gaps */
  .about-gap.above { flex: none; height: 96px; }
  .about-gap.below { flex: none; height: 64px; }
  /* Figma "Contact Mobile": the title (display lg) over the details at title sm, 32 apart with each
     label 12 above; "Tap to copy" as a second line; centred between the nav and the footer */
  .contact-main { padding-block: calc(var(--nav-h) + 32px) 32px; row-gap: 64px; }
  .contact-info { grid-template-columns: minmax(0, 1fr); row-gap: 32px; }
  /* Figma "Footer" (Platform=Mobile): Back to top and the name, no clock */
  .site-foot { grid-template-columns: 1fr 1fr; }
  .site-foot .clock { display: none; }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation-duration: 0.001ms !important; transition-duration: 0.001ms !important; }
  [data-fade], .g-item[data-clip], .g-item[data-clip] .g-in { opacity: 1; transform: none; clip-path: none; }
}

/* ==========================================================================
   Stylistic sets (tokens --ss-text / --ss-display at the top). Set on every element rather than
   inherited, and !important, because each `font:` shorthand above resets font-feature-settings to
   normal. Display sizes (the classes, the intro name, the phone menu) and everything inside them
   get the display sets.
   ========================================================================== */
*, *::before, *::after { font-feature-settings: var(--ss-text) !important; }
:is(.t-display-md, .t-display-lg, .intro, .menu-nav a, .menu-nav button),
:is(.t-display-md, .t-display-lg, .intro, .menu-nav a, .menu-nav button) * { font-feature-settings: var(--ss-display) !important; }
