/* Home page components (Figma Portfolio / Home 1328:8455, components 1330:1730).
   Load order: css/tokens.css, css/ds.css, this file (D25). Tokens only
   (scripts/check-tokens.sh). Motion lives in js/home/*.js: components get
   .is-live when their script has set them up, so without JS, under reduced
   motion, or if a script fails, everything stays in normal flow and readable. */

/* ---------- page grid: the shared layout tokens (D13), measured per breakpoint ---------- */
body {
  position: relative; /* the centre line is positioned against the whole document */
  --content: min(var(--layout-content-width), 100% - 2 * var(--layout-margin));
  --col: calc((var(--content) - (var(--layout-columns) - 1) * var(--layout-gutter)) / var(--layout-columns));
  /* text block: 4 columns (desktop/tablet columns 2–5 = Figma's 580px at 1280; phone all 4) */
  --text-width: calc(4 * var(--col) + 3 * var(--layout-gutter));
}
@media (prefers-reduced-motion: no-preference) {
  html { scroll-behavior: smooth; }
}
.home { position: relative; z-index: 1; } /* above the centre line */
.home > section, .home > .work { scroll-margin-top: calc(var(--size-header) - var(--layout-section-padding-y)); }
.home > .hero, .home > .contact { scroll-margin-top: 0; }

/* ---------- header (1328:9259): fixed, the page scrolls behind its fade ---------- */
.site-header {
  position: fixed; inset: 0 0 auto; z-index: var(--site-header-z);
  height: var(--size-header);
  display: grid; grid-template-columns: minmax(max-content, 1fr) auto minmax(max-content, 1fr); align-items: center;
  padding-inline: var(--inset-wide);
  background: var(--site-header-gradient);
  pointer-events: none; /* the fade doesn't block clicks on the content under it */
}
.site-header > * { pointer-events: auto; }
.site-header__end { display: flex; justify-content: flex-end; }

/* ---------- location widget (1330:1746) ---------- */
.location-widget { display: flex; align-items: center; gap: var(--gap-group); }
.location-widget__photo { width: var(--location-widget-photo); height: var(--location-widget-photo); border-radius: var(--radius-compact); display: block; flex-shrink: 0; }
.location-widget__text { color: var(--text-secondary); white-space: nowrap; }
.location-widget__text time { font-variant-numeric: tabular-nums; }
.location-widget__sep:has(+ time:empty) { display: none; } /* no JS: place only */
.location-widget--inline { display: none; } /* phones: the widget moves from the header into the hero */

/* ---------- top navigation (1330:1736, items 1334:802, avatar 1334:809) ---------- */
.top-nav {
  position: relative; /* holds the sliding pill */
  display: flex; align-items: center; gap: var(--gap-group);
  padding: var(--inset-compact) var(--inset-panel) var(--inset-compact) var(--inset-compact);
  border-radius: var(--radius-pill);
  background: var(--surface-tint);
}
.top-nav__avatar { position: relative; display: block; width: var(--top-nav-avatar); height: var(--top-nav-avatar); border-radius: var(--radius-pill); flex-shrink: 0; }
.top-nav__avatar-img { position: absolute; inset: 0; width: 100%; height: 100%; display: block; }
.top-nav__avatar-img--hover, .top-nav__avatar-img--active { opacity: 0; }
/* Active (1334:807): the export is 42px with its ring, 2.5% outside the 40px box. Shown while the hero is in view. */
.top-nav__avatar-img--active { inset: -2.5%; width: 105%; height: 105%; }
.top-nav__avatar:hover .top-nav__avatar-img--hover { opacity: 1; }
.top-nav__avatar[aria-current] .top-nav__avatar-img--active { opacity: 1; }
.top-nav__list { display: flex; align-items: center; gap: var(--gap-group); list-style: none; margin: 0; padding: 0; }
.top-nav__item {
  display: block; padding: var(--spacing-4) var(--inset-compact);
  border-radius: var(--radius-pill);
  color: var(--text-secondary); text-decoration: none; white-space: nowrap;
}
.top-nav__item:hover { color: var(--text-body); }
.top-nav__item { position: relative; z-index: 1; } /* above the pill */
.top-nav__item[aria-current] { color: var(--text-body); background: var(--surface-tint); }
/* With the pill (js/home/nav.js adds .is-live) the item itself has no background;
   the pill slides to the active item. */
.top-nav__pill { display: none; }
.top-nav.is-live .top-nav__pill {
  display: block; position: absolute; left: 0; top: 0; width: 0; height: 0; z-index: 0;
  border-radius: var(--radius-pill); background: var(--surface-tint);
  pointer-events: none; opacity: 0; transition: opacity var(--duration-200) var(--ease-fast-out-slow-in);
}
.top-nav.is-live .top-nav__item[aria-current] { background: none; }

/* ---------- buttons (1328:8911): primary accent, secondary tint; optional 16px icon ---------- */
.btn {
  position: relative; isolation: isolate; /* holds the hover layer under the label */
  display: inline-flex; align-items: center; justify-content: center; gap: var(--spacing-6);
  min-width: 140px; /* @raw-ok measure: one-off, the buttons' minimum width (Figma 1328:8911) */
  height: var(--size-touch); padding: var(--inset-compact) var(--spacing-16);
  border-radius: var(--radius-pill);
  color: var(--text-body); text-decoration: none; white-space: nowrap;
  transition: background-color var(--duration-200) var(--ease-fast-out-slow-in);
}
.btn--primary { background: var(--surface-accent); }
.btn--secondary { background: var(--surface-tint); }
/* Hover (1360:9352): primary takes gradient/angular-accent, stretched to the button like
   Figma's fill (the PNG is baked in normalised space); it cross-fades in. */
.btn--primary::before {
  content: ''; position: absolute; inset: 0; z-index: -1; border-radius: inherit;
  background: var(--gradient-angular-accent) center / 100% 100% no-repeat;
  opacity: 0; transition: opacity var(--duration-200) var(--ease-fast-out-slow-in);
}
.btn--primary:is(:hover, :focus-visible)::before { opacity: 1; }
.btn--secondary:is(:hover, :focus-visible) { background: var(--surface-tint-hover); } /* 1360:9358 */
.btn__icon { width: var(--size-icon-16); height: var(--size-icon-16); display: block; flex-shrink: 0; }

/* Icon swap: the arrow leaves along its own direction (to the top right) while a
   second copy enters from the bottom left, on hover or keyboard focus. */
.icon-swap { position: relative; display: block; overflow: hidden; }
.icon-swap > img { display: block; width: 100%; height: 100%; transition: transform var(--duration-500) var(--ease-out-quint); }
.icon-swap > img + img { position: absolute; inset: 0; transform: translate(-100%, 100%); }
:is(a, button):is(:hover, :focus-visible) > .icon-swap > img:first-child { transform: translate(100%, -100%); }
:is(a, button):is(:hover, :focus-visible) > .icon-swap > img + img { transform: none; }

/* ---------- hint: a name on hover (graph stickers, colophon logos) ----------
   No Figma design; built from existing tokens (D26, D28). Hover only: the alt text
   or aria-label of the item carries the same name for screen readers. */
.hint-host { position: relative; display: inline-flex; }
.hint {
  position: absolute; left: 50%; transform: translateX(-50%); z-index: var(--hint-z);
  padding: var(--inset-tight) var(--inset-compact);
  border-radius: var(--radius-compact);
  background: var(--surface-tint); color: var(--text-body);
  white-space: nowrap; pointer-events: none; opacity: 0;
  transition: opacity var(--duration-200) var(--ease-fast-out-slow-in);
}
.hint--above { bottom: calc(100% + var(--gap-tight)); } /* the colophon is at the page end */
.hint-host:hover > .hint { opacity: 1; }

/* ---------- section heads (label → text 20; heading → text 12, or 20 in hero/contact) ---------- */
.section-head {
  position: relative; z-index: 0; /* holds the axis mask below its text */
  display: flex; flex-direction: column; align-items: center; gap: var(--gap-group);
  width: var(--text-width); margin-inline: auto; text-align: center;
}
.section-head__text { display: flex; flex-direction: column; gap: var(--gap-stack); width: 100%; text-align: center; }
.section-head__text--loose { gap: var(--gap-group); }
.section-label { color: var(--text-accent); text-align: center; }
.section-label__type { display: inline-block; } /* js/home/label-type.js clips this to type the label in */
/* Axis mask: a page-coloured bar behind the text block hides the centre line, with
   24px clear above and below; it moves with the text, so it masks while pinned too. */
.section-head::before, .work__label::before {
  content: ''; position: absolute; z-index: -1; left: 50%;
  top: calc(-1 * var(--spacing-24)); bottom: calc(-1 * var(--spacing-24));
  width: calc(4 * var(--border-2)); margin-left: calc(-2 * var(--border-2));
  background: var(--surface-page);
}

/* ---------- emoji: played once when they cross the middle of the screen ----------
   js/home/emoji.js adds .is-played; same approach as the Gemstones meta-card and round icons
   (css/work-gemstones.css), original motions in the spirit of Noto Emoji Animation, not its assets.
   Reduced motion: none of them move. */
.emoji { display: inline-block; }
.emoji[data-emoji="wave"] { transform-origin: 70% 70%; }  /* the wrist */
.emoji[data-emoji="brush"] { transform-origin: 80% 20%; } /* the handle end */
.emoji[data-emoji="pop"] { transform-origin: 50% 60%; }
.emoji.is-played[data-emoji="wave"] { animation: emoji-wave 700ms ease-in-out 1; } /* @raw-ok motion: icon choreography timing */
/* 👩🏻‍💻 types (D38): two copies of the glyph cut at one seam, the top of the laptop. The upper part (head and
   shoulders) rocks, the lower part (laptop and sleeves) taps. Prototype: experiments/emoji-typing/. */
.emoji[data-emoji="type"] { position: relative; }
.emoji__up, .emoji__low { display: block; }
.emoji__low { position: absolute; inset: 0; clip-path: inset(56% 0 0 0); } /* @raw-ok artwork: the seam, measured on the glyph */
.emoji__up { clip-path: inset(0 0 44% 0); transform-origin: 50% 56%; } /* @raw-ok artwork: the seam, measured on the glyph */
.emoji.is-played[data-emoji="type"] > .emoji__up { animation: emoji-type-rock 900ms ease-in-out 1; } /* @raw-ok motion: icon choreography timing */
.emoji.is-played[data-emoji="type"] > .emoji__low { animation: emoji-type-tap 900ms linear 1; } /* @raw-ok motion: icon choreography timing */
.emoji.is-played[data-emoji="brush"] { animation: emoji-brush 800ms ease-in-out 1; } /* @raw-ok motion: icon choreography timing */
.emoji.is-played[data-emoji="pop"] { animation: emoji-pop 650ms var(--ease-out-quint); } /* @raw-ok motion: icon choreography timing */
/* 👩🏻‍🦱 smirks (D38): a second copy of the glyph cut to a patch of plain skin around the mouth lifts one corner
   (skew, a little wider and higher) and settles. Prototype: experiments/emoji-smirk/. */
.emoji[data-emoji="smirk"] { position: relative; }
.emoji__base { display: block; }
.emoji__mouth { position: absolute; inset: 0; display: block; clip-path: inset(69% 33% 15% 33%); transform-origin: 50% 75%; } /* @raw-ok artwork: the mouth patch, measured on the glyph */
.emoji.is-played[data-emoji="smirk"] > .emoji__mouth { animation: emoji-smirk 1100ms ease-in-out 1; } /* @raw-ok motion: icon choreography timing */
@keyframes emoji-wave { /* the Gemstones hat wave */
  0%, 100% { transform: rotate(0deg); }
  25% { transform: rotate(14deg); }
  50% { transform: rotate(-10deg); }
  75% { transform: rotate(8deg); }
}
@keyframes emoji-type-rock { /* the shoulders rock, the head follows */
  0%, 100% { transform: rotate(0deg); }
  15% { transform: rotate(-2.2deg); }
  35% { transform: rotate(1.8deg); }
  55% { transform: rotate(-1.8deg); }
  75% { transform: rotate(1.4deg); }
}
@keyframes emoji-type-tap { /* quick taps, 0.8px */
  0%, 16%, 32%, 52%, 70%, 90%, 100% { transform: translateY(0); }
  8%, 24%, 44%, 62%, 82% { transform: translateY(-0.8px); }
}
@keyframes emoji-brush { /* a stroke: swings out, back past rest, settles */
  0%, 100% { transform: rotate(0deg); }
  30% { transform: rotate(-18deg); }
  65% { transform: rotate(12deg); }
}
@keyframes emoji-smirk {
  0%, 100% { transform: none; }
  25%, 70% { transform: translate(0.8%, -0.9%) skewY(-8deg) scaleX(1.12); } /* @raw-ok motion: one corner up */
}
@keyframes emoji-pop { /* the Gemstones party popper */
  0% { transform: scale(1) rotate(0deg); }
  40% { transform: scale(1.35) rotate(-14deg); }
  70% { transform: scale(0.95) rotate(6deg); }
  100% { transform: scale(1) rotate(0deg); }
}
@media (prefers-reduced-motion: reduce) {
  .emoji.is-played[data-emoji], .emoji.is-played[data-emoji] > * { animation: none; }
}

/* ---------- 01 hero ---------- */
.hero {
  min-height: 100vh; min-height: 100svh;
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: var(--spacing-24);
  padding-block: var(--size-header);
}
.hero__intro { display: flex; flex-direction: column; align-items: center; gap: var(--gap-block); width: var(--text-width); }

/* ---------- 02/03 work: one label + two scenes (1330:1673, 1330:1674) ---------- */
.work { position: relative; padding-top: var(--layout-section-padding-y); }
/* In flow (phones, reduced motion, no JS): label, Case studies, its cards, Other work, its cards */
.work__label { position: relative; z-index: 2; }
.scene { position: relative; }
.scene__copy { z-index: 1; }
.scene + .scene { margin-top: var(--layout-section-padding-y); }
.work__label + .scene .scene__copy { margin-top: var(--gap-group); }
.scene__cards { display: grid; grid-template-columns: 1fr 1fr; gap: var(--layout-gutter); width: var(--content); margin: var(--gap-block) auto 0; }
.scene__track { display: flex; flex-direction: column; align-items: center; gap: var(--gap-block); }
.card {
  display: block; width: min(var(--card-size), 100%); aspect-ratio: 1 / 1;
  background: var(--surface-raised); border-radius: var(--radius-card);
  opacity: var(--o); transform: rotate(calc(var(--r) / 2)); /* half the Figma tilt while in flow */
  color: inherit; text-decoration: none;
}
/* The Case studies covers (Figma 1466:18098): three squares in one row, 40 apart, the width of the content.
   js/home/scenes.js sets --cover (the size, smaller on short screens) and --row-top. */
.scene__cards--row { display: block; }
.scene__row { display: flex; align-items: flex-start; justify-content: center; gap: var(--gap-block); width: 100%; }
.card--cover { --r: 0deg; --o: var(--opacity-80); width: auto; min-width: 0; transform: none;
  flex: 0 1 var(--cover, 270px); /* @raw-ok measure: one-off, the Case studies covers (Figma 1466:18106) */ }

/* Pinned (≥781, motion allowed; js/home/scenes.js adds .is-live to .work and pins the stage).
   The label and the Case studies text are centred together as one block (--label-top,
   --copy-top, measured by the script); both text blocks sit at --copy-top and the script
   moves them with transforms. Text scrolls inside a window that starts just under the
   label, so Case studies leaves under it when Other work pushes it up. */
.work.is-live { padding-top: 0; scroll-margin-top: 0; } /* Work in the nav lands on the pin start */
.work.is-live .work__stage { position: relative; height: 100vh; height: 100svh; }
/* No axis shows below the Work text while the stage is pinned (Ioli, D38): a page-coloured bar from the text slot to the
   stage bottom, like the other axis masks; once Other work is in place it ends at that text, so the axis restarts right
   under it. The line above the label stays. */
.work.is-live .work__stage::before {
  content: ''; position: absolute; z-index: -1; left: 50%; top: var(--axis-bar-top, var(--copy-top, 0)); height: var(--axis-bar-h, calc(100% - var(--copy-top, 0px))); /* js/home/scenes.js ends it at the Other work text */
  width: calc(4 * var(--border-2)); margin-left: calc(-2 * var(--border-2));
  background: var(--surface-page);
}
.work.is-live .work__label { position: absolute; left: 0; width: 100%; top: var(--label-top, 0); }
.work.is-live .scene { position: static; margin: 0; }
.work.is-live .scene__copy { position: absolute; left: 50%; top: var(--copy-top, 0); margin: 0 0 0 calc(var(--text-width) / -2); }
.work.is-live .scene__cards {
  position: absolute; inset: 0; z-index: 2; display: block; width: auto; margin: 0;
  overflow: clip; /* the card layer clips itself; no ancestor gets overflow, so pinning keeps working */
  pointer-events: none;
}
.work.is-live .scene__track { position: absolute; top: 0; bottom: 0; display: block; }
.work.is-live .card { position: absolute; top: 0; transform: none; opacity: 0; pointer-events: auto; }
/* Case study cover (Figma 1466:18108, Project=Gemstones|Search, State=Default|Hover). Default: the work on the raised
   surface (Gemstones: the shipped landing page and the mega-menu, cropped by the square). Hover or keyboard focus: an
   overlay with the title fades in. The title is always in the page, so the link keeps its name. */
.cover { --o: 1; position: relative; overflow: clip; text-decoration: none; }
.cover__collage { position: absolute; top: 0; bottom: 0; left: 50%; transform: translateX(-50%); display: block; aspect-ratio: 1200 / 630; } /* @raw-ok artwork: the hero collage, Figma 1466:18110 */
.cover__shot { position: absolute; display: block; overflow: hidden; border-radius: var(--radius-6); box-shadow: var(--shadow-md); transform: translateY(-50%); }
.cover__shot img { display: block; width: 100%; height: 100%; object-fit: cover; }
.cover__shot--page { left: 20.23%; right: 33.07%; top: 79.07%; aspect-ratio: 240 / 389; } /* @raw-ok artwork: Figma 1466:18111, 78.5px under the middle of 270 */
.cover__shot--menu { left: 44.36%; right: 11.48%; top: 40.16%; aspect-ratio: 1160 / 597; } /* @raw-ok artwork: Figma 1466:18112, 26.6px over the middle of 270 */
/* Search (Figma 1466:18113): a 1200 x 630 collage centred on the square, two phones and the search dialog; percentages of that 1200 x 630 */
.cover__collage--search { top: -66.667%; bottom: -66.667%; } /* @raw-ok artwork: 180px over and under 270, Figma 1489:4256 */
.cover__shot--phone, .cover__shot--dialog { border-radius: var(--radius-card); }
.cover__shot--phone { left: 40.08%; width: 10.17%; aspect-ratio: 122 / 264; } /* @raw-ok artwork: Figma 1489:4257, 1489:4259 */
.cover__shot--phone img { object-fit: fill; }
.cover__shot--phone-top { top: 27.78%; } /* @raw-ok artwork: Figma 1489:4257 */
.cover__shot--phone-bottom { top: 72.22%; } /* @raw-ok artwork: Figma 1489:4259 */
.cover__shot--dialog { left: 46.58%; width: 30.25%; top: 50%; aspect-ratio: 363 / 210; } /* @raw-ok artwork: Figma 1489:4258 */
.cover__shot--dialog img { position: absolute; left: -0.01%; top: 0.06%; width: 100.01%; height: 131.55%; object-fit: fill; } /* @raw-ok artwork: Figma crop */
.cover__overlay {
  position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; padding: var(--inset-card);
  background: color-mix(in srgb, var(--surface-raised) 80%, transparent); /* @raw-ok artwork: the overlay is the raised surface at 80% (Figma 1466:18121) */
  opacity: 0; transition: opacity var(--duration-200) var(--ease-fast-out-slow-in);
}
.cover__title { flex: 1 0 0; min-width: 0; margin: 0; font: var(--type-statement); color: var(--text-body); }
.cover:is(:hover, :focus-visible) .cover__overlay { opacity: 1; }

.work.is-live .scene__row { position: absolute; left: 50%; top: var(--row-top, 0); width: var(--content); transform: translateX(-50%); }
.work.is-live .card--cover { position: relative; top: auto; opacity: var(--o); pointer-events: auto; }
@media (min-width: 1280px) {
  /* Figma's two-track foreground: 1120 wide (page minus 80px each side), 440px tracks, 240px apart */
  .work.is-live .scene__track { width: calc((var(--layout-page-width) - 5 * var(--spacing-80)) / 2); }
  .work.is-live .scene__track--l { left: calc(50% - var(--layout-page-width) / 2 + var(--spacing-80)); }
  .work.is-live .scene__track--r { right: calc(50% - var(--layout-page-width) / 2 + var(--spacing-80)); }
  .work.is-live .card { left: calc(var(--x) * 100%); }
}
@media (min-width: 781px) and (max-width: 1279px) {
  /* tablet: the tracks are columns 1–3 and 4–6 of the content */
  .work.is-live .scene__track { width: calc(3 * var(--col) + 2 * var(--layout-gutter)); }
  .work.is-live .scene__track--l { left: calc(50% - var(--content) / 2); }
  .work.is-live .scene__track--r { right: calc(50% - var(--content) / 2); }
  .work.is-live .card { width: min(var(--card-size), 70%); left: calc(var(--x) * 30%); }
}

/* ---------- 04 about ---------- */
.about { padding-block: var(--layout-section-padding-y); }
/* Pinned (≥781, motion allowed; js/home/about.js adds .is-live and pins the stage): the label and
   heading stay still, centred with the full text; the two paragraphs and the button rise from the
   bottom of the stage one after the other. The stage clips them until they arrive. */
.about.is-live { padding-block: 0; scroll-margin-top: 0; }
.about.is-live .about__stage { height: 100vh; height: 100svh; display: flex; align-items: center; justify-content: center; overflow: clip; }
.about__bio { display: flex; flex-direction: column; gap: var(--gap-stack); } /* Figma 1360:9307: paragraphs 12 apart */

/* ---------- 05 latest updates: graph (1330:1694) ---------- */
.graph {
  position: relative; overflow: clip;
  height: max(100vh, 10 * var(--graph-sticker)); height: max(100svh, 10 * var(--graph-sticker));
}
.graph__edges { position: absolute; inset: 0; width: 100%; height: 100%; pointer-events: none; }
.graph__edges line { stroke: var(--border-color-graph); stroke-width: var(--border-1); }
.graph__nodes { list-style: none; margin: 0; padding: 0; }
/* Rest position: Figma's node centre (--fx/--fy) as a share of the stage */
.graph__node {
  position: absolute; z-index: 1;
  left: calc(var(--fx) * 100%); top: calc(var(--fy) * 100%);
  width: var(--graph-sticker); height: var(--graph-sticker);
  margin-left: calc(var(--graph-sticker) / -2); margin-top: calc(var(--graph-sticker) / -2);
}
.graph__node img { width: 100%; height: 100%; display: block; user-select: none; -webkit-user-drag: none; }
.graph__node:hover, .graph__node.is-dragging { z-index: 2; }
.graph__node:hover .hint, .graph__node.is-dragging .hint { opacity: 1; }
.graph__hint { top: calc(100% + var(--gap-tight)); } /* under the sticker */
/* Live: js/home/graph.js places each node with a transform and makes it draggable */
.graph.is-live .graph__node { left: 0; top: 0; margin: 0; will-change: transform; cursor: grab; touch-action: none; }
.graph.is-live .graph__node.is-dragging { cursor: grabbing; }
.graph__message {
  position: absolute; z-index: 1; left: 50%; top: 43.3%; /* above the edges; Figma: centre at 433/1002 of the stage */
  transform: translate(-50%, -50%);
  width: var(--text-width);
  display: flex; flex-direction: column; align-items: center; gap: var(--gap-group); text-align: center;
}

/* ---------- 06 contact (1328:8534) + colophon (1328:8538) ---------- */
.contact {
  min-height: 100vh; min-height: 100svh;
  display: flex; flex-direction: column; align-items: center; gap: var(--gap-section);
  padding-block: var(--layout-section-padding-y);
  background: var(--gradient-linear-accent) 0 calc(var(--contact-hold, 0) * 1px) / 100% 100% no-repeat var(--surface-page); /* @raw-ok measure: js/home/contact.js sets --contact-hold in px; the strip it uncovers is the page colour, the gradient's solid top */
}
.contact__body { display: flex; flex-direction: column; align-items: center; gap: var(--gap-group); width: var(--text-width); } /* Figma 1466:17765 */
.contact__emoji { display: block; }
.contact__actions { display: flex; flex-wrap: wrap; justify-content: center; gap: var(--gap-stack); list-style: none; margin: 0; padding: 0; } /* Figma 1466:17775: email chip, LinkedIn chip */
/* Picture stack (Figma 1466:17769): a back card behind one of eight pictures with their label chips, on a 580 × 300
   stage; a click shows the next one, and after the last comes the first (js/home/pic-stack.js). */
.pic-stack {
  position: relative; display: block; width: 100%;
  height: 300px; /* @raw-ok measure: one-off, the stage of the picture stack (Figma 1466:17769) */
  padding: 0; border: 0; background: none; color: inherit; font: inherit; cursor: pointer;
}
.pic-stack__back {
  position: absolute; left: 50%; top: 50%; display: block;
  width: var(--bw, 232.627px); height: var(--bh, 174.471px); /* @raw-ok artwork: Figma 1466:17770, the darker frame behind; it follows the picture's orientation (D40) */
  border-radius: var(--radius-card); background: var(--surface-page);
  transform: translate(calc(-50% + 15.9px), calc(-50% - 19.2px)) rotate(3.94deg); /* @raw-ok artwork: Figma 1466:17770, up and to the right of the picture's centre */
}
/* The eight pictures of the component set (Figma pic-stack 1466:18158), shown one at a time, in the order of the set.
   Each item is centred on the first picture's centre and carries its own back card (portrait pictures get a portrait one); its size, the crop of
   its image and the place and tilt of its label are Figma's, set as custom properties on the item (--pw/--ph size,
   --lx/--ly label centre from the picture's top-left, --lr tilt, --ix/--iy/--iw/--ih image crop). */
.pic-stack__item { position: absolute; left: 50%; width: var(--pw); height: var(--ph); margin-left: calc(var(--pw) / -2); margin-top: calc(var(--ph) / -2); opacity: 0; }
.pic-stack__item { top: 151px; } /* @raw-ok artwork: the centre of the first picture on the 300px stage, Figma 1466:18152 */
.pic-stack__item.is-active { opacity: 1; }
.pic-stack__pic { position: absolute; inset: 0; display: block; overflow: hidden; border-radius: var(--radius-card); background: var(--surface-accent); }
.pic-stack__img { position: absolute; left: var(--ix, 0); top: var(--iy, 0); width: var(--iw, 100%); height: var(--ih, 100%); max-width: none; display: block; object-fit: cover; }
.pic-stack__label {
  position: absolute; left: var(--lx); top: var(--ly); display: block;
  padding: 10px; /* @raw-ok measure: one-off, Figma 1410:12879 */
  border-radius: var(--radius-card); background: var(--surface-light); color: var(--text-on-light);
  font: var(--type-statement); white-space: var(--ws, nowrap); text-align: center;
  transform: translate(-50%, -50%) rotate(var(--lr, 0deg));
}
/* LinkedIn (1466:18057, hover 1466:18059): the filled logo alone on the light surface; hover = the tint surface. Figma keeps the
   dark logo on hover (about 1.3:1 on the tint); here it turns to the body colour so the icon stays visible (WCAG 1.4.11). */
.action-chip--active { background: var(--surface-light); color: var(--text-on-light); padding-inline: var(--inset-tight); }
.action-chip--active:hover { background: var(--surface-tint); color: var(--text-body); }
/* Copied (1360:9364): the label and icon sit centred as a group in the chip's held width (no resize); the Check replaces Copy */
.action-chip__icon[hidden] { display: none; }
/* Colophon (1328:8538): how the site was made, at the foot of the contact gradient.
   Contact pins its text at mid-screen while this scrolls up into view (js/home/contact.js). */
.colophon {
  margin-top: var(--colophon-gap, auto); width: calc(100% - 2 * var(--inset-wide)); /* js/home/contact.js sets --colophon-gap while the block is pinned */
  padding-top: var(--gap-block);
  border-top: var(--border-0-5) solid var(--border-color-subtle);
  display: flex; flex-direction: column; align-items: center; gap: var(--gap-stack);
}
.colophon__text { display: flex; flex-wrap: wrap; align-items: center; justify-content: center; gap: var(--gap-hair); color: var(--text-body); text-align: center; }
.colophon__text img { width: var(--colophon-logo); height: var(--colophon-logo); display: block; }
.colophon__emoji { width: var(--colophon-logo); height: var(--colophon-logo); align-items: center; justify-content: center; color: var(--text-body); } /* Figma 1360:9410: 40px box, h3 emoji */
/* Open-Meteo credit: no Figma placement yet (D27) */
.colophon__credit a { color: var(--text-body); }

/* ---------- centre line (1328:8463): one continuous line, filled by the scroll ---------- */
/* It starts at an arrow (Figma 1461:17394, 22.627 × 43.485) whose tip is 100px above the
   bottom of the first screen at any viewport height, and ends above the ABOUT label.
   js/home/center-line.js sets its length and fills it in with the scroll: the arrow
   rides up with the page; where the line starts to extend (the lit front at 60% of the
   screen) the arrow's head goes and the lit line carries on from its shaft. The
   unfilled part is invisible (no track). Full opacity throughout. */
.scroll-line {
  position: absolute; left: 50%; z-index: 0;
  top: calc(100vh - var(--spacing-100) - 43.485px); top: calc(100svh - var(--spacing-100) - 43.485px); /* @raw-ok artwork: the arrow's height, Figma 1461:17394 */
  width: var(--border-2); height: var(--line-height, 0);
  margin-left: calc(var(--border-2) / -2); pointer-events: none;
}
.scroll-line__lit {
  position: absolute; inset: 0; background: var(--scroll-line-gradient);
  transform-origin: top;
} /* fully drawn unless js/home/center-line.js scrubs scaleY (reduced motion: drawn, still) */
/* The arrow: Figma's path as a mask over gradient/angular-accent; hidden without the script */
.scroll-line__arrow {
  display: none; position: absolute; top: 0; left: 50%;
  width: 22.627px; height: 43.485px; margin-left: -11.3137px; /* @raw-ok artwork: Figma 1461:17394, its shaft centred on the 2px line */
  background: var(--gradient-angular-accent) center / 100% 100% no-repeat;
  -webkit-mask: url("data:image/svg+xml,%3Csvg%20width%3D%2722.6275%27%20height%3D%2743.4854%27%20viewBox%3D%270%200%2022.6275%2043.4854%27%20fill%3D%27none%27%20xmlns%3D%27http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%27%3E%3Cpath%20d%3D%27M%2012.3113%2039.6592%20L%2021.2134%2030.7571%20L%2022.6275%2032.1711%20L%2011.3142%2043.4854%20L%200%2032.1711%20L%201.4141%2030.7571%20L%2010.3113%2039.6543%20L%2010.3113%200%20L%2012.3113%200%20L%2012.3113%2039.6592%20Z%27%20fill%3D%27black%27%2F%3E%3C%2Fsvg%3E") center / 100% 100% no-repeat; /* @raw-ok artwork: assets/icons/scroll-arrow.svg inlined, a file mask is blocked on file:// */
  mask: url("data:image/svg+xml,%3Csvg%20width%3D%2722.6275%27%20height%3D%2743.4854%27%20viewBox%3D%270%200%2022.6275%2043.4854%27%20fill%3D%27none%27%20xmlns%3D%27http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%27%3E%3Cpath%20d%3D%27M%2012.3113%2039.6592%20L%2021.2134%2030.7571%20L%2022.6275%2032.1711%20L%2011.3142%2043.4854%20L%200%2032.1711%20L%201.4141%2030.7571%20L%2010.3113%2039.6543%20L%2010.3113%200%20L%2012.3113%200%20L%2012.3113%2039.6592%20Z%27%20fill%3D%27black%27%2F%3E%3C%2Fsvg%3E") center / 100% 100% no-repeat; /* @raw-ok artwork: same */
}
.scroll-line.has-arrow .scroll-line__arrow { display: block; }
.scroll-line.has-arrow:not(.is-extending) .scroll-line__lit { opacity: 0; } /* the arrow is the line's start */
.scroll-line.has-arrow.is-extending .scroll-line__arrow { display: none; }  /* its head is lost; the lit line carries on */

/* Reduced motion: the pill and the hints change state without fading */
@media (prefers-reduced-motion: reduce) {
  .top-nav.is-live .top-nav__pill, .hint, .btn, .btn--primary::before, .cover__overlay { transition: none; }
}

/* ---------- phones (≤780) ---------- */
@media (max-width: 780px) {
  .scene__row { flex-direction: column; align-items: center; }
  .card--cover { flex: none; width: min(270px, 100%); } /* @raw-ok measure: one-off, the Case studies covers (Figma 1466:18106) */
  /* no side padding: the nav pill must fit a 320px screen */
  .site-header { grid-template-columns: 1fr; justify-items: center; padding-inline: 0; }
  .site-header__start, .site-header__end { display: none; }
  .location-widget--inline { display: flex; }
  .graph__node { left: calc(var(--px) * 100%); top: calc(var(--py) * 100%); }
  .graph__message { top: 50%; }
}
@media (max-width: 480px) {
  /* Figma's pill is 335px wide; below 480 its gaps tighten so it fits 320px (no phone frame yet) */
  .top-nav, .top-nav__list { gap: var(--gap-hair); }
}
