/* =========================================================================
   BREAKPOINT SCALE — use these six widths and no others.
     1080  wide desktop     shell stops growing
      915  nav collapse     primary nav -> hamburger  (min complement: 916)
      820  tablet           two-column layouts go single  (min complement: 821)
      720  small tablet     galleries and multi-column cards go single
      560  phone            edge-to-edge, stacked
      480  small phone      last-resort collapses
   Anything outside this set is drift: pick the nearest tier instead of
   inventing a seventh. (max-height queries are a separate axis.)
   ========================================================================= */
@font-face {
    font-family: "Figtree Variable";
    src: url("../fonts/figtree-latin-wght-normal.woff2") format("woff2");
    font-style: normal;
    font-weight: 300 900;
    font-display: swap;
}

@font-face {
    font-family: "Figtree Variable";
    src: url("../fonts/figtree-latin-wght-italic.woff2") format("woff2");
    font-style: italic;
    font-weight: 300 900;
    font-display: swap;
}

@font-face {
    font-family: "Fraunces";
    src: url("../fonts/fraunces-latin-500-normal.woff2") format("woff2");
    font-style: normal;
    font-weight: 400 600;
    font-display: swap;
}
@font-face {
    font-family: "Fraunces";
    src: url("../fonts/fraunces-latin-500-italic.woff2") format("woff2");
    font-style: italic;
    font-weight: 400 600;
    font-display: swap;
}

@font-face {
    font-family: "Cookie";
    src: url("../fonts/cookie-latin-400-normal.woff2") format("woff2");
    font-style: normal;
    font-weight: 400;
    font-display: swap;
}

:root {
    /* Secondary text scale, declared once and used by name throughout. */
    --accent: #ff4fd8;
    --accent-rgb: 255, 79, 216;
    --accent-2: #805cff;
    --accent-3: #ff8b62;
}

*, *::before, *::after { box-sizing: border-box; }
html { scroll-behavior: smooth; scroll-padding-top: 88px; background: var(--bg); }
body { margin: 0; overflow-x: hidden; background: var(--bg); color: var(--ink); font-family: var(--sans); line-height: 1.55; -webkit-font-smoothing: antialiased; }
body.menu-open { overflow: hidden; }
img { display: block; max-width: 100%; }
a { color: inherit; text-decoration: none; }
button { font: inherit; color: inherit; }
button, a { -webkit-tap-highlight-color: transparent; }
[hidden] { display: none !important; }
:focus-visible { outline: 2px solid var(--accent); outline-offset: 4px; }
/* The gate precedes the lens choice, so its focus ring must not be an accent. */
.entry :focus-visible { outline-color: #c8c5cf; }
::selection { background: var(--accent); color: var(--bg); }
/* The gate comes before the lens choice, so dragging to select text there would
   flash an accent the visitor has not picked yet. Both selectors are needed:
   the descendant one does not match the element carrying the rule. */
.entry::selection, .entry ::selection { background: #c8c5cf; color: var(--bg); }
.shell { width: min(calc(100% - 96px), var(--shell)); margin-inline: auto; }
.anchor-alias { position: relative; top: -88px; display: block; height: 0; visibility: hidden; }
.skip-link { position: fixed; z-index: 9999; top: 12px; left: 12px; padding: 10px 14px; border-radius: 999px; background: #fff; color: #000; font-size: 13px; font-weight: 800; transform: translateY(-160%); transition: transform 160ms ease; }
.skip-link:focus { transform: none; }

/* Unified actions */
.button { display: inline-flex; align-items: center; justify-content: space-between; gap: 10px; min-height: 54px; padding: 0 21px; border: 1px solid var(--line-strong); border-radius: 999px; background: transparent; color: var(--ink); font-size: 13px; font-weight: 720; letter-spacing: .015em; cursor: pointer; transition: transform 220ms var(--ease), border-color 220ms ease, background 220ms ease, color 220ms ease, box-shadow 220ms ease; }
.button span { font-size: 16px; line-height: 1; transition: transform 220ms var(--ease); }
.button:hover { transform: translateY(-3px); }
.button:hover span { transform: translate(3px, -2px); }
.button--primary { border-color: var(--accent); background: var(--accent); color: var(--bg); box-shadow: 0 0 0 0 rgba(var(--accent-rgb), .2); }
.button--primary:hover { box-shadow: 0 13px 42px rgba(var(--accent-rgb), .24); }
.button--secondary { background: rgba(255,255,255,.045); backdrop-filter: blur(12px); }
.button--secondary:hover { border-color: var(--accent); background: rgba(var(--accent-rgb), .08); }
.text-link { display: inline-flex; align-items: center; gap: 7px; width: fit-content; margin-top: 20px; padding: 0 0 5px; border: 0; border-bottom: 1px solid currentColor; background: transparent; font-size: 13px; font-weight: 750; cursor: pointer; }

/* Viewpoint entry */
.entry { position: fixed; inset: 0; z-index: 5000; display: grid; grid-template-rows: auto minmax(0, 1fr) auto; gap: clamp(20px, 3vh, 36px); min-height: 100dvh; padding: clamp(24px, 3.6vw, 58px); overflow: auto; background: radial-gradient(circle at 50% -15%, #242333 0, transparent 36%), var(--bg); color: #fff; transition: opacity 550ms var(--ease), transform 700ms var(--ease); }
.entry::before { content: ""; position: absolute; inset: 0; opacity: .25; background-image: linear-gradient(rgba(255,255,255,.018) 1px, transparent 1px), linear-gradient(90deg, rgba(255,255,255,.018) 1px, transparent 1px); background-size: 32px 32px; pointer-events: none; }
.entry.is-leaving { opacity: 0; transform: scale(1.02); pointer-events: none; }
.entry-head, .portals, .entry-note { position: relative; z-index: 1; }
.entry-head { display: flex; }
/* Scoped to the eyebrow. The title and the note are direct <p> children too, so
   an unscoped rule handed them its uppercase, its display:flex (which strips the
   whitespace between inline children) and its gray. All three were bugs. */
.entry-head > p:first-child { display: flex; align-items: center; margin: 0; color: var(--ink-4); font-family: var(--mono); letter-spacing: .07em; text-transform: uppercase; }

/* .entry-head > p:first-child above is written for the eyebrow, but the title and the note
   are direct <p> children too, so they inherited both its uppercase and its
   display:flex. Flex drops the whitespace between inline children, which is what
   ran "One portfolio," into "seen three ways." */
/* A deliberate gray: it reads well against the white emphasis. */
.entry-head .entry-title { display: block; margin: 0; color: var(--ink-4); font-weight: 520; text-transform: none; }
/* Vertical rhythm comes from .entry-head's gap. The eyebrow's own 7px bottom
   margin made the space above the title larger than the space below it, which is
   backwards for a line that size. */
.entry-head .entry-note { margin: 8px 0 0; }
/* White rather than an accent: the gate sits before a lens is chosen, so an
   accent here would promise a color the rest of the site may not use. The
   serif italic carries the emphasis on its own. */
/* Matched to .speaking-title ("Designing from the margins"): Fraunces at weight
   400 with WONK at 0, the house default. The Strategy highlight sets WONK 1 for
   its wonky h m n s &, which is the slight difference between the two. Stated
   explicitly so it cannot drift if the lens face changes. */
.entry-head .entry-title em { color: #fff; font-family: var(--serif); font-style: italic; font-weight: 400; font-variation-settings: "WONK" 0; }
.portals { display: grid; grid-template-columns: repeat(3, 1fr); gap: 14px; min-height: 0; perspective: 1600px; }
.portal { position: relative; display: grid; min-width: 0; padding: 26px; overflow: hidden; border: 1px solid var(--line); border-radius: var(--radius); background: rgba(18,18,27,.76); color: #fff; text-align: left; cursor: pointer; backdrop-filter: blur(18px); transform-style: preserve-3d; transition: transform 420ms var(--ease), border-color 300ms ease, background 300ms ease, box-shadow 300ms ease; }
.portal::before { content: ""; position: absolute; z-index: -1; inset: -35%; opacity: 0; background: radial-gradient(circle at 50% 50%, var(--portal-color), transparent 58%); transition: opacity 400ms ease; }
.portal--creative { --portal-color: rgba(255,79,216,.24); --portal-accent: #ff4fd8; }
.portal--visionary { --portal-color: rgba(255,178,44,.22); --portal-accent: #ffb22c; }
.portal--engineer { --portal-color: rgba(94,231,211,.2); --portal-accent: #5ee7d3; }
.portal:hover, .portal:focus-visible { z-index: 2; border-color: color-mix(in srgb, var(--portal-accent) 60%, white 10%); background: rgba(24,24,35,.92); box-shadow: 0 28px 80px rgba(0,0,0,.4); transform: translateY(-8px) rotateX(var(--portal-rx)) rotateY(var(--portal-ry)); }
.portal-copy { transform: translateZ(28px); }
.portal:hover::before, .portal:focus-visible::before { opacity: 1; }
/* No top margin: .portal's 26px padding carries the gap above the art, so all
   four sides match. */
.portal-art { position: relative; display: block; margin: 0 0 20px; overflow: hidden; border: 1px solid rgba(255,255,255,.075); border-radius: calc(var(--radius) - 8px); background: #0a0a10; transform: translate(var(--portal-x), var(--portal-y)) translateZ(40px); transition: transform 260ms var(--ease); }
.portal-art--creative { background: radial-gradient(circle at 42% 48%, rgba(255,79,216,.18), transparent 34%), radial-gradient(circle at 68% 36%, rgba(128,92,255,.16), transparent 30%), #0a0a10; }
.portal-art--visionary { background: radial-gradient(circle at 56% 45%, rgba(255,178,44,.18), transparent 32%), linear-gradient(180deg, #0a0a10, #120c0b); }
/* No CSS grid here: the canvas draws its dots on the same grid the traces route on. */
.portal-art--engineer { background: radial-gradient(circle at 55% 45%, rgba(94,231,211,.13), transparent 34%), #0a0a10; }
.portal-art canvas { position: absolute; inset: 0; width: 100%; height: 100%; }
.portal-copy small, .portal-copy strong, .portal-copy > span { display: block; }
.portal-copy small { margin-bottom: 11px; color: var(--portal-accent); font-family: var(--mono); font-size:12px; letter-spacing: .08em; text-transform: uppercase; }
.portal-copy strong { font-size: clamp(32px, 3.2vw, 51px); font-weight: 540; letter-spacing: -.06em; line-height: .92; }
.portal--engineer .portal-copy strong { font-family: var(--mono); font-size: clamp(25px, 2.65vw, 41px); letter-spacing: -.045em; }
/* 15px matches the card descriptions it is the gate's equivalent of:
   .project-sub, .skill-card p and .method-head p are all 15px. */
.portal-copy > span { max-width: 56ch; margin-top: 16px; color: var(--ink-4); font-size: 15px; line-height: 1.55; }
.entry-note { gap: 8px; }

/* Header */
.site { min-height: 100vh; background: var(--bg); color: var(--ink); }
.site-header { position: fixed; z-index: 1000; top: 0; right: 0; left: 0; display: flex; align-items: center; gap: 14px; height: 66px; padding-inline: max(28px, calc((100vw - var(--shell)) / 2)); border-bottom: 1px solid transparent; transition: background 300ms ease, border-color 300ms ease, backdrop-filter 300ms ease, top 300ms ease, padding 320ms var(--ease); }
/* Once scrolled the whole navbar narrows to a floating pill (well inside the content width) */
.site-header.is-scrolled { top: 12px; padding-inline: max(24px, calc((100vw - 940px) / 2)); }
/* Nav pill: transparent + edge-to-edge at the top, glass pill once scrolled. Lens dropdown lives outside it. */
.nav-pill { display: flex; align-items: center; gap: clamp(14px, 2.4vw, 32px); flex: 1 1 auto; min-width: 0; padding: 6px 0; border: 1px solid transparent; border-radius: 999px; transition: background 300ms ease, border-color 300ms ease, box-shadow 300ms ease, backdrop-filter 300ms ease, padding 300ms var(--ease); }
.nav-pill .primary-nav { margin-inline: auto; }
.is-scrolled .nav-pill { padding: 7px 22px; background: rgba(17,17,26,.55); border-color: var(--line); box-shadow: inset 0 1px 0 rgba(255,255,255,.07), 0 10px 34px rgba(0,0,0,.32); backdrop-filter: saturate(165%) blur(24px); }
/* Lens dropdown: same dark glass + height as the nav pill once scrolled, so they sit as a matched pair */
.is-scrolled .view-button { min-height: 52px; padding: 0 18px; background: rgba(17,17,26,.55); border-color: var(--line); box-shadow: inset 0 1px 0 rgba(255,255,255,.07), 0 10px 34px rgba(0,0,0,.32); backdrop-filter: saturate(165%) blur(24px); }
.brand { display: inline-flex; align-items: center; gap: 11px; width: fit-content; }
.brand span { display: grid; place-items: center; width: 38px; height: 38px; border: 1px solid var(--line-strong); border-radius: 50%; background: linear-gradient(135deg, rgba(var(--accent-rgb),.3), rgba(255,255,255,.035)); font-size:12px; font-weight: 850; }
.brand b { font-size: 14px; font-weight: 670; }
.primary-nav { display: flex; border-radius: 999px; }
.primary-nav a { font-weight: 620; transition: background 160ms ease, color 160ms ease; }
.view-switcher { position: relative; justify-self: end; }
.view-button { display: flex; align-items: center; gap: 8px; min-height: 40px; padding: 7px 12px; border: 1px solid var(--line); border-radius: 999px; background: rgba(255,255,255,.035); cursor: pointer; }
.view-symbol { color: var(--accent); }
.view-button > span:not(.view-symbol) { font-size:12px; font-weight: 750; letter-spacing: .06em; text-transform: uppercase; }
/* direct child only: the label now wraps per-lens spans, which inherit this */
.view-button svg { width: 9px; fill: none; stroke: currentColor; stroke-width: 1.4; transition: transform 180ms ease; }
.view-button[aria-expanded="true"] svg { transform: rotate(180deg); }
.view-menu { position: absolute; right: 0; width: 200px; padding: 7px; border-radius: 16px; }
.view-menu button { display: flex; align-items: center; gap: 10px; width: 100%; padding: 11px; border: 0; border-radius: 10px; background: transparent; color: var(--ink); text-align: left; cursor: pointer; }
.view-menu button:hover { background: rgba(255,255,255,.06); }
.view-menu button[aria-checked="true"] { color: var(--accent); }
.view-menu button span { font-family: var(--mono); }
.menu-button, .mobile-menu { display: none; }

/* Shared data-driven copy */
[data-world-copy] { display: none; }
html[data-viewpoint="creative"] [data-world-copy="creative"],
html[data-viewpoint="visionary"] [data-world-copy="visionary"],
html[data-viewpoint="engineer"] [data-world-copy="engineer"] { display: inline; }
.hero-description [data-world-copy] { display: none; }
html[data-viewpoint="creative"] .hero-description [data-world-copy="creative"],
html[data-viewpoint="visionary"] .hero-description [data-world-copy="visionary"],
html[data-viewpoint="engineer"] .hero-description [data-world-copy="engineer"] { display: block; }

/* Hero */
.world-hero { position: relative; overflow: hidden; background: var(--bg); }
.world-hero::before { content: ""; position: absolute; z-index: 1; inset: 0; background: linear-gradient(90deg, rgba(8,8,12,.98) 0, rgba(8,8,12,.84) 38%, rgba(8,8,12,.2) 70%, rgba(8,8,12,.65) 100%), linear-gradient(180deg, transparent 58%, var(--bg) 96%); pointer-events: none; }
.hero-field { position: absolute; z-index: 0; inset: 0; width: 100%; height: 100%; opacity: .92; }
/* The lens art fades out behind the hero copy. Done as a mask so the
   compositor handles it, instead of erasing part of the canvas every frame. */
@media (min-width: 821px) {
    .hero-field { -webkit-mask-image: linear-gradient(90deg, transparent 0, rgba(0,0,0,.3) 33%, #000 55%); mask-image: linear-gradient(90deg, transparent 0, rgba(0,0,0,.3) 33%, #000 55%); }
}
.hero-grid { position: relative; z-index: 2; display: grid; grid-template-columns: minmax(0, 1.15fr) minmax(360px, .7fr); }
.hero-copy h1 { margin: 0; font-weight: 520; text-wrap: balance; }
.hero-copy h1 em { color: var(--accent); font-family: var(--serif); font-weight: 400; }
html[data-viewpoint="engineer"] .hero-copy h1 em { font-family: var(--mono); font-size: 1em; font-style: normal; }
.hero-description p { max-width: 680px; margin: 32px 0 0; color: #b4b1bb; line-height: 1.65; }
.hero-actions { display: flex; flex-wrap: wrap; gap: 10px; }
.hero-profile { position: relative; }
.profile-card { z-index: 2; margin: 0; overflow: hidden; border: 1px solid rgba(255,255,255,.2); border-radius: 32px; background: #171720; box-shadow: 0 35px 90px rgba(0,0,0,.48); }
.profile-card::before { content: ""; position: absolute; z-index: 1; inset: 0; border-radius: inherit; box-shadow: inset 0 0 0 1px rgba(255,255,255,.04); pointer-events: none; }
.profile-card img { width: 100%; aspect-ratio: .82; object-fit: cover; }
.profile-card figcaption { position: absolute; z-index: 2; right: 12px; bottom: 12px; left: 12px; padding: 15px; border: 1px solid rgba(255,255,255,.16); border-radius: 18px; background: rgba(11,11,16,.65); backdrop-filter: blur(16px); }
.profile-card figcaption span, .profile-card figcaption strong, .profile-card figcaption small { display: block; }
.profile-card figcaption span { display: flex; align-items: center; gap: 7px; color: var(--ink-2); font-family: var(--mono); font-size:12px; letter-spacing: .06em; text-transform: uppercase; }
.profile-card figcaption span i { width: 6px; height: 6px; border-radius: 50%; background: var(--accent); box-shadow: 0 0 10px var(--accent); }
.profile-card figcaption strong { margin-top: 7px; font-size: 18px; font-weight: 650; letter-spacing: -.03em; }
.profile-card figcaption small { color: #aaa7b1; font-size: 12px; }
.profile-orbit { position: absolute; z-index: 1; inset: 0; display: grid; place-items: center; }
.profile-orbit span { position: absolute; width: 90%; aspect-ratio: 1; border: 1px solid rgba(var(--accent-rgb), .18); border-radius: 50%; animation: orbit-spin 20s linear infinite; }
.profile-orbit span:nth-child(2) { width: 72%; border-style: dashed; animation-direction: reverse; animation-duration: 14s; }
.profile-orbit span:nth-child(3) { width: 112%; border-color: rgba(255,255,255,.06); animation-duration: 28s; }
@keyframes orbit-spin { to { transform: rotate(360deg); } }
@keyframes marquee { to { transform: translateX(-50%); } }

/* Section system */
.work, .method, .voice, .assistant { padding: clamp(110px, 12vw, 175px) 0; }
.work { background: linear-gradient(180deg, #0b0b11, #101018); }
.method { background: #09090e; }
.voice { background: linear-gradient(180deg, #101018, #0b0b11); }
.section-intro, .method-head { display: grid; align-items: start; margin-bottom: 72px; }
.section-intro h2, .method-head h2 { max-width: 900px; margin: 0; font-size: clamp(34px, 4vw, 58px); font-weight: 520; letter-spacing: -.07em; line-height: .9; }
.section-intro h2 { text-wrap: balance; }
.method-head h2 em, .career-head h2 em { color: var(--accent); font-family: var(--serif); font-weight: 400; }
.section-intro > div > p { max-width: 640px; margin: 23px 0 0; color: var(--muted); font-size: 15px; }

/* Work */
.project { flex-direction: column; min-width: 0; }

/* The lens reorders editorial priority; every project stays in the grid, and the
   lens's lead project becomes the featured, full-width tile. Order per lens: */
/* Per-lens ordering lives in home.js (ORDER), which moves the actual DOM nodes.
   CSS `order` was used here before; it reorders tiles visually while leaving the
   tab sequence in source order, so keyboard users traversed a different gallery
   than the one on screen (WCAG 2.4.3). Do not reintroduce `order` on .project. */
/* the lead project of each lens = featured full-width banner (spans both columns) */
/* The lens's lead tile. home.js sets .is-featured on whichever case order.js
   lists first, so this never names a project. */
.project.is-featured{grid-column:1 / -1;aspect-ratio:5 / 2;}

/* Method and career */
.method-list { display: grid; gap: 10px; margin-top: 0; padding: 0; list-style: none; }
.method-list li { position: relative; display: grid; grid-template-columns: 70px minmax(270px,.85fr) 1.15fr; gap: 28px; align-items: center; padding: 30px 28px; overflow: hidden; border: 1px solid var(--line); border-radius: var(--radius-sm); background: rgba(255,255,255,.018); transition: border-color 220ms ease, background 220ms ease, transform 250ms var(--ease); }
.method-list li::before { content: ""; position: absolute; inset: 0 auto 0 0; width: 3px; background: var(--accent); transform: scaleY(0); transition: transform 250ms var(--ease); }
/* No hover state: the constants are not clickable. */
.method-list li > span { color: var(--accent); font-family: var(--mono); }
.method-list h3, .method-list p { margin: 0; }
.method-list h3 { font-size: clamp(23px, 2.4vw, 35px); font-weight: 580; letter-spacing: -.045em; line-height: 1; }
.method-list p { max-width: 620px; color: var(--ink-3); font-size: 15px; line-height: 1.72; }
.career-head { display: grid; grid-template-columns: .9fr 1.1fr; gap: clamp(50px, 10vw, 145px); align-items: end; }
.career-head h2 { margin: 17px 0 0; font-size: clamp(34px, 4vw, 56px); font-weight: 520; letter-spacing: -.073em; line-height: .84; }
.career-head > div:last-child { max-width: 620px; padding-bottom: 4px; }
.career-summary p { margin: 0 0 22px; color: var(--ink-3); font-size: 16px; line-height: 1.72; }
.career-head a { display: inline-block; padding-bottom: 4px; border-bottom: 1px solid currentColor; font-size: 13px; font-weight: 700; }
.career-timeline ol { position: relative; display: flex; margin: 76px 0 0; padding: 0; list-style: none; }
.career-timeline li::before { content: ""; position: absolute; top: 0; bottom: 0; left: -37px; width: 1px; background: linear-gradient(180deg, var(--a), var(--b)); }
/* One smooth accent→faded gradient across the whole timeline (newest = accent).
   Each role's segment interpolates between two adjacent stops, and neighbours
   share a stop, so the colour is continuous with no visible steps — even across
   an omitted role, whose flat segment uses its start colour (= the previous
   role's end colour) so the line enters it seamlessly. */
.career-timeline li{ --a: var(--s0); --b: var(--s1); }
.career-timeline li:nth-child(2){ --a: var(--s1); --b: var(--s2); }
.career-timeline li:nth-child(3){ --a: var(--s2); --b: var(--s3); }
.career-timeline li:nth-child(4){ --a: var(--s3); --b: var(--s4); }
.career-timeline li:nth-child(5){ --a: var(--s4); --b: var(--s5); }
.career-timeline li:nth-child(6){ --a: var(--s5); --b: var(--s6); }
.career-timeline li:nth-child(7){ --a: var(--s6); --b: var(--s7); }
.career-timeline li { position: relative; flex: 1; min-width: 0; padding-right: clamp(16px,2.2vw,32px); }
.career-timeline time { display: block; color: var(--ink-5); font-family: var(--mono); font-size:12px; letter-spacing: .06em; }
.timeline-marker { position: relative; z-index: 1; width: 12px; height: 12px; margin: 24px 0 28px; border: 3px solid var(--bg); border-radius: 50%; background: #555260; box-shadow: 0 0 0 1px var(--line-strong); }
.career-timeline h3 { max-width: 190px; margin: 0; font-size: clamp(17px, 1.7vw, 24px); font-weight: 610; letter-spacing: -.035em; line-height: 1.08; }
.career-timeline strong { display: block; margin-top: 9px; color: var(--accent); font-size: 13px; }
.career-timeline li p { max-width: 230px; margin: 15px 0 0; color: var(--ink-4); line-height: 1.68; }
.career-timeline .is-current .timeline-marker { background: var(--accent); box-shadow: 0 0 0 1px var(--accent), 0 0 22px rgba(var(--accent-rgb), .58); }
.career-timeline .is-current time { color: var(--accent); }
/* A role outside the active lens keeps a short slot, and its bar segment turns
   dotted — small gaps in the line that read as "…" in the line's own colour. */
html[data-viewpoint="creative"] .career-timeline li:not([data-lenses~="creative"]),
html[data-viewpoint="visionary"] .career-timeline li:not([data-lenses~="visionary"]),
html[data-viewpoint="engineer"] .career-timeline li:not([data-lenses~="engineer"]) { flex: 0 0 26px; padding-right: 0; }
html[data-viewpoint="creative"] .career-timeline li:not([data-lenses~="creative"]) > *,
html[data-viewpoint="visionary"] .career-timeline li:not([data-lenses~="visionary"]) > *,
html[data-viewpoint="engineer"] .career-timeline li:not([data-lenses~="engineer"]) > * { display: none; }
/* An omitted role keeps the SAME continuous gradient line (its ::before background
   is never recoloured, so there is no colour step anywhere). A mask simply punches
   three even dashes out of the line — same colour as the line, evenly spaced.
   Default (a role mid-run) shows the full solid segment; only the last role of a
   run carries the dashes. Base = vertical; the desktop block flips them. */
html[data-viewpoint="creative"] .career-timeline li:not([data-lenses~="creative"]):not(:has(+ li:not([data-lenses~="creative"]))):not(:last-child)::before,
html[data-viewpoint="visionary"] .career-timeline li:not([data-lenses~="visionary"]):not(:has(+ li:not([data-lenses~="visionary"]))):not(:last-child)::before,
html[data-viewpoint="engineer"] .career-timeline li:not([data-lenses~="engineer"]):not(:has(+ li:not([data-lenses~="engineer"]))):not(:last-child)::before {
  -webkit-mask:
    linear-gradient(#000,#000) center top / 100% 2px no-repeat,
    linear-gradient(#000,#000) center 5px / 100% 2px no-repeat,
    linear-gradient(#000,#000) center 10px / 100% 2px no-repeat,
    linear-gradient(#000,#000) center 15px / 100% 2px no-repeat,
    linear-gradient(#000,#000) center bottom / 100% 2px no-repeat;
  mask:
    linear-gradient(#000,#000) center top / 100% 2px no-repeat,
    linear-gradient(#000,#000) center 5px / 100% 2px no-repeat,
    linear-gradient(#000,#000) center 10px / 100% 2px no-repeat,
    linear-gradient(#000,#000) center 15px / 100% 2px no-repeat,
    linear-gradient(#000,#000) center bottom / 100% 2px no-repeat;
}
/* TRAILING omission (nothing shown after it): dashes at the very end, nothing after. */
html[data-viewpoint="creative"] .career-timeline li:not([data-lenses~="creative"]):last-child::before,
html[data-viewpoint="visionary"] .career-timeline li:not([data-lenses~="visionary"]):last-child::before,
html[data-viewpoint="engineer"] .career-timeline li:not([data-lenses~="engineer"]):last-child::before {
  -webkit-mask:
    linear-gradient(#000,#000) center top / 100% 7px no-repeat,
    linear-gradient(#000,#000) center 10px / 100% 2px no-repeat,
    linear-gradient(#000,#000) center 15px / 100% 2px no-repeat,
    linear-gradient(#000,#000) center bottom / 100% 2px no-repeat;
  mask:
    linear-gradient(#000,#000) center top / 100% 7px no-repeat,
    linear-gradient(#000,#000) center 10px / 100% 2px no-repeat,
    linear-gradient(#000,#000) center 15px / 100% 2px no-repeat,
    linear-gradient(#000,#000) center bottom / 100% 2px no-repeat;
}
html[data-viewpoint="creative"] .career-summary [data-world-copy="creative"],
html[data-viewpoint="visionary"] .career-summary [data-world-copy="visionary"],
html[data-viewpoint="engineer"] .career-summary [data-world-copy="engineer"] { display: block; }
.talk { display: grid; grid-template-columns: .58fr 1.1fr .78fr; gap: clamp(30px,5vw,72px); align-items: end; min-height: 430px; padding: clamp(32px,4.5vw,58px); overflow: hidden; border-radius: 32px; background: radial-gradient(circle at 88% 5%, rgba(255,255,255,.38), transparent 23%), linear-gradient(145deg, var(--accent), var(--accent-2)); color: var(--bg); }

/* AI companion */
.assistant-stage { display: grid; grid-template-columns: .92fr 1.08fr; gap: clamp(48px, 8vw, 110px); align-items: center; }
.assistant-copy h2 { margin: 22px 0 0; font-size: clamp(32px, 3.8vw, 54px); font-weight: 520; letter-spacing: -.075em; line-height: .87; }
.assistant-copy h2 [data-world-copy] { display: none; }
html[data-viewpoint="creative"] .assistant-copy h2 [data-world-copy="creative"],
html[data-viewpoint="visionary"] .assistant-copy h2 [data-world-copy="visionary"],
html[data-viewpoint="engineer"] .assistant-copy h2 [data-world-copy="engineer"] { display: inline; }
.assistant-copy > p:not(.section-index,.chat-status) { max-width: 570px; margin: 27px 0 0; color: var(--muted); font-size: 15px; line-height: 1.72; }
.chat-status { min-height: 19px; margin: 10px 0 0; color: var(--ink-6); font-size: 12px; }
.question-examples > p { display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between; gap: 8px; margin: 0 0 12px; color: var(--accent); font-family: var(--mono); font-size:12px; font-weight: 650; letter-spacing: .07em; text-transform: uppercase; }
.question-hint { color: #8b8894; font-weight: 500; letter-spacing: .04em; text-transform: none; }
.question-examples li { position: relative; }
.question-examples li[data-lenses] { display: none; }
html[data-viewpoint="creative"] .question-examples li[data-lenses~="creative"],
html[data-viewpoint="visionary"] .question-examples li[data-lenses~="visionary"],
html[data-viewpoint="engineer"] .question-examples li[data-lenses~="engineer"] { display: block; }
.question-examples button[data-ask-question] { position: relative; width: 100%; border: 0; background: transparent; color: #d0cdd5; font-family: var(--sans); font-size: 17px; line-height: 1.45; text-align: left; cursor: pointer; transition: color 180ms ease, padding 220ms var(--ease); }
.question-examples button[data-ask-question]::before { content: "“"; position: absolute; color: var(--accent); font-family: var(--serif); font-size: 23px; }
.question-examples button[data-ask-question]::after { position: absolute; color: var(--accent); font-size: 15px; transition: opacity 180ms ease, transform 220ms var(--ease); }
.question-examples button[data-ask-question]:hover,
.question-examples button[data-ask-question]:focus-visible { color: var(--ink); }
.question-examples button[data-ask-question].is-copied::after { transform: translateY(-50%); letter-spacing: .04em; }

/* Proof section — fixed testimonials + auto-scrolling logo marquee.
   Kept tone-frugal on purpose: everything sits on the page background, cards are outlined
   (no fill), logos scroll on the same bg — no extra dark panels stacked in one view. */
.clients { padding: clamp(70px, 10vw, 128px) 0; border-top: 1px solid var(--line); overflow: hidden; }
.clients .section-intro { margin-bottom: clamp(34px, 4.5vw, 60px); }

/* Fixed testimonial cards */
.praise-cards { list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: repeat(3, 1fr); gap: 18px; }
.praise-cards li { display: flex; flex-direction: column; gap: 22px; padding: clamp(24px, 2vw, 32px); border: 1px solid var(--line); border-radius: var(--radius-sm); }
.praise-cards blockquote { position: relative; margin: 0; color: #d9d7e0; font-size: clamp(15px, 1.15vw, 16.5px); line-height: 1.6; }
.praise-cards blockquote::before { content: "\201C"; color: var(--accent); font-family: var(--serif); font-size: 1.7em; line-height: 0; margin-right: 4px; vertical-align: -.28em; }
.praise-cards .praise-by { margin-top: auto; display: flex; flex-direction: column; gap: 3px; }
.praise-cards .praise-by strong { font-size: 14px; font-weight: 780; letter-spacing: .01em; }
.praise-cards .praise-by span { color: var(--ink-6); font-family: var(--mono); }
@media (max-width: 820px) { .praise-cards { grid-template-columns: 1fr; } }

/* Auto-scrolling logo marquee */
.logo-marquee { position: relative; margin-top: clamp(48px, 6vw, 84px); overflow: hidden;
    -webkit-mask: linear-gradient(90deg, transparent, #000 7%, #000 93%, transparent);
    mask: linear-gradient(90deg, transparent, #000 7%, #000 93%, transparent); }
.logo-track { display: flex; align-items: center; width: max-content; animation: logo-scroll 46s linear infinite; will-change: transform; transform: translateZ(0); backface-visibility: hidden; }
.logo-marquee:hover .logo-track { animation-play-state: paused; }
/* Each logo sits in a wide padded cell, so the hover target is much larger than the artwork itself. */
.logo-item { display: flex; align-items: center; justify-content: center; padding: 0 clamp(32px, 4.5vw, 68px); min-height: 116px; cursor: default; }
/* translateZ(0) alone keeps the layer warm enough to avoid the first-hover repaint;
   will-change here would promote 16 images permanently for a hover that may never happen. */
.logo-item img { display: block; max-height: var(--lh, 40px); width: auto; object-fit: contain; filter: brightness(0) invert(1); opacity: .5; transform: translateZ(0); backface-visibility: hidden; transition: opacity 220ms ease, filter 220ms ease; }
.logo-item:hover img { opacity: 1; }
/* Optical sizing: heights set by equal visual area so every logo reads at a similar size. */
.logo-item[data-logo="ibm"] { --lh: 40px; }
.logo-item[data-logo="zendesk"] { --lh: 54px; }
.logo-item[data-logo="deloitte"] { --lh: 43px; }
.logo-item[data-logo="outsystems"] { --lh: 60px; }
.logo-item[data-logo="tap"] { --lh: 47px; }
.logo-item[data-logo="edp"] { --lh: 39px; }
.logo-item[data-logo="inesc"] { --lh: 36px; }
.logo-item[data-logo="ezcater"] { --lh: 32px; }
/* Reveal true brand colours on hover (Zendesk stays white — its brand colour is near-black). */
.logo-item[data-logo="ibm"]:hover img,
.logo-item[data-logo="tap"]:hover img,
.logo-item[data-logo="edp"]:hover img,
.logo-item[data-logo="inesc"]:hover img,
.logo-item[data-logo="deloitte"]:hover img,
.logo-item[data-logo="outsystems"]:hover img,
.logo-item[data-logo="ezcater"]:hover img { filter: none; }
@keyframes logo-scroll { from { transform: translateX(0); }
  to { transform: translateX(-50%); } }
@media (prefers-reduced-motion: reduce) { .logo-track { animation: none; flex-wrap: wrap; justify-content: center; }
  .logo-item { min-height: 92px; }
  .logo-item.logo-dup { display: none; } }

/* Contact and footer */
.contact { padding: clamp(100px, 12vw, 170px) 0; text-align: center; }
.contact h2 { margin: 18px 0 0; font-weight: 520; }
.contact > div > p:not(.section-index) { margin: 34px auto 0; color: var(--muted); }
.contact > div > div { display: flex; justify-content: center; gap: 10px; margin-top: 27px; }
.site-footer { padding: 29px 0; border-top: 1px solid var(--line); background: var(--bg); }
.site-footer > div { display: grid; grid-template-columns: 1fr auto auto; align-items: center; gap: 34px; }
.site-footer strong, .site-footer span { display: block; }
/* ...but the year sits inline inside the copyright, not on its own line. */
.site-footer small span { display: inline; }
.site-footer strong { font-size: 15px; }
.site-footer span, .site-footer small { color: #a4a1ab; font-size: 13px; }
.site-footer small { white-space: normal; }
.site-footer small a { color: inherit; border-bottom: 1px solid var(--line); transition: color .2s, border-color .2s; }
.site-footer small a:hover { color: var(--ink); border-bottom-color: currentColor; }
.site-footer nav { display: flex; align-items: center; gap: 18px; }
.site-footer nav a { display: inline-flex; align-items: center; color: var(--ink-2); transition: color .2s ease; }
.site-footer nav a svg { width: 20px; height: 20px; display: block; }
.site-footer nav a:hover { color: var(--accent); }
.floating-ai { cursor: pointer; }
.floating-ai em { color: var(--accent); font-size: 19px; font-style: normal; transition: transform 220ms var(--ease); }
.floating-ai:hover em { transform: translate(4px,-4px); }
@property --ai-angle { syntax: "<angle>"; initial-value: 0deg; inherits: false; }
@keyframes ai-border { to { --ai-angle: 360deg; } }
@keyframes ai-pulse { 50% { opacity: .25; transform: scale(1.14); } }

/* Reveals */
.has-js .reveal-target { opacity: 0; transform: translateY(25px); transition: opacity 650ms ease, transform 800ms var(--ease); transition-delay: var(--reveal-delay,0ms); }
.has-js .reveal-target.is-revealed { opacity: 1; transform: none; }

@media (max-width: 1080px) {
    .hero-grid { grid-template-columns: minmax(0,1fr) 360px; gap: 42px; }
    .hero-copy h1 { font-size: clamp(38px, 6vw, 60px); }
    .method-list li { grid-template-columns: 55px .9fr 1.1fr; }
    .assistant-stage { grid-template-columns: 1fr; }
    .assistant-copy { max-width: 800px; }
}

/* Header collapses to the hamburger menu below 915px (nav links, lens dropdown and CV move into the mobile menu) */
@media (max-width: 915px) {
    .site-header { height: 66px; padding-inline: 16px; }
    .primary-nav, .view-switcher { display: none; }
    /* With the links, lens switcher and CV all hidden, the pill held only the
       brand but still stretched (flex:1 1 auto) and drew its full scrolled
       background. Hug the brand instead. */
    .nav-pill { flex: 0 0 auto; }
    .is-scrolled .nav-pill { padding-inline: 16px; }
    .nav-cv { display: none; }
    .menu-button { display: flex; flex-direction: column; justify-content: center; gap: 7px; width: 44px; height: 44px; padding: 10px; border: 1px solid var(--line); border-radius: 50%; background: rgba(255,255,255,.035); }
    .menu-button span { height: 1px; background: currentColor; transition: transform 180ms ease; }
    .menu-button[aria-expanded="true"] span:first-child { transform: translateY(4px) rotate(45deg); }
    .menu-button[aria-expanded="true"] span:last-child { transform: translateY(-4px) rotate(-45deg); }
    .mobile-menu { position: fixed; z-index: 999; inset: 0; display: flex; flex-direction: column; padding: 86px 16px 30px; background: var(--bg); color: var(--ink); overflow-y: auto; overscroll-behavior: contain; }
    .mobile-menu[hidden] { display: none; }
    .mobile-menu > a { padding: 12px 0; border-bottom: 1px solid var(--line); font-family: var(--serif); font-size: 36px; line-height: 1; }
    .mobile-views { display: grid; grid-template-columns: repeat(3,1fr); gap: 6px; margin-top: auto; }
    .mobile-views button { display: flex; align-items: center; justify-content: center; gap: 7px; padding: 12px 4px; border: 1px solid var(--line); border-radius: 999px; background: transparent; font-family: var(--mono); font-size: 12px; text-transform: uppercase; }
    .mobile-views button > span { font-size: 13px; line-height: 1; opacity: .85; }
}

@media (max-width: 820px) {
    .shell { width: min(calc(100% - 32px), var(--shell)); }
    .entry { padding: 22px 16px; gap: clamp(16px, 2.2vh, 24px); }
    /* No display:block here. The base rule lays .entry-head out as a centered
       flex column with gap:16px; overriding display made that gap inert, so the
       three header lines fell back to whatever stray margins they had. */
    .entry-head .entry-title { font-size: 37px; }
    /* align-content:center stops the three cards stretching to fill the 1fr row
       .entry gives .portals. They were content-light on mobile (the description
       is hidden) and inflating to fill the viewport. */
    .portals { grid-template-columns: 1fr; grid-template-rows: repeat(3, minmax(0, 1fr)); }
    .portal { grid-template-rows: auto; gap: 11px; min-height: 138px; padding: 12px; border-radius: 20px; }
    .portal-art { margin: 0; border-radius: 14px; }
  .portal-copy > span { display: none; }
    .portal-copy small { font-size:12px; }
    .portal-copy strong { font-size: 28px; }
    .portal--engineer .portal-copy strong { font-size: 22px; }
    .world-hero { padding-top: 105px; }
    .world-hero::before { background: linear-gradient(180deg, rgba(8,8,12,.8), rgba(8,8,12,.96) 62%, var(--bg)); }
    .hero-field { opacity: .65; }
    .hero-grid { grid-template-columns: 1fr; min-height: auto; }
    .hero-copy h1 { font-size: clamp(36px, 10vw, 58px); }
    .hero-profile { width: min(100%, 570px); margin-left: auto; }
    .profile-card { right: 8%; }
    .section-intro, .method-head, .career-head { grid-template-columns: 1fr; gap: 23px; }
    .section-intro h2, .method-head h2 { font-size: clamp(48px, 11vw, 76px); }
    .project-grid { flex-direction: column; }
    .method-list li { grid-template-columns: 42px 1fr; }
    .method-list p { grid-column: 2; }
    .career-head { gap: 34px; }
    .career-timeline ol { display: block; margin-top: 55px; padding-left: 42px; }
    /* vertical connector segments handled by the base .career-timeline li::before */
    /* collapsed (lens-omitted) roles: compact "···" row in the vertical layout */
    html[data-viewpoint="creative"] .career-timeline li:not([data-lenses~="creative"]),
    html[data-viewpoint="visionary"] .career-timeline li:not([data-lenses~="visionary"]),
    html[data-viewpoint="engineer"] .career-timeline li:not([data-lenses~="engineer"]) { flex: none; padding: 0 0 22px; }
    .timeline-marker { position: absolute; top: 1px; left: -42px; margin: 0; }
    .career-timeline h3, .career-timeline li p { max-width: 620px; }
    .talk { grid-template-columns: 1fr 1fr; min-height: 500px; }
    .contact h2 { font-size: clamp(78px, 20vw, 130px); }
    .site-footer > div { grid-template-columns: 1fr auto; }
    .site-footer nav { grid-column: 1/-1; grid-row: 2; }
    .site-footer small { grid-column: 2; grid-row: 1; }
}

@media (max-width: 560px) {
    .entry-head .entry-title { font-size: 32px; }
    .portal { grid-template-columns: 22px 78px 1fr auto; min-height: 108px; }
    .portal-art { min-height: 84px; }
    .portal-copy strong { font-size: 24px; }
    .portal--engineer .portal-copy strong { font-size: 18px; }
    .brand b { display: none; }
    .hero-copy h1 { font-size: clamp(34px, 11vw, 52px); }
    .hero-description { min-height: 115px; }
    .hero-description p { font-size: 15px; }
    .hero-actions { align-items: stretch; }
    .hero-actions .button { width: 100%; }
    .hero-profile { min-height: 425px; }
    .profile-card { right: 5%; width: 76%; border-radius: 25px; }
    .work, .method, .voice, .assistant { padding: 90px 0; }
    .section-intro { margin-bottom: 52px; }
    .method-list li { display: block; padding: 25px 20px; }
    .method-list h3 { margin-top: 11px; }
    .method-list p { margin-top: 12px; }
    .career-timeline { margin-top: 95px; }
    .career-head h2 { font-size: 58px; }
    .assistant-copy h2 { font-size: clamp(49px, 14vw, 69px); }
    .contact > div > div { flex-direction: column; }
    .contact .button { width: 100%; }
    .talk { grid-template-columns: 1fr; gap: 28px; min-height: 590px; }
    .floating-ai { right: 12px; bottom: max(12px,env(safe-area-inset-bottom)); grid-template-columns: 40px auto; gap: 9px; min-height: 60px; }
    .floating-ai em { display: none; }
}

/* Lens entry → hero shared-element handoff */
/* A radial mask lets the edges dissolve into the page instead of ending on an
   outline. */
.lens-ghost { position: fixed; z-index: 6000; overflow: hidden; pointer-events: none;
  transform-origin: 50% 50%; border-radius: 20px; will-change: transform, opacity;
  -webkit-mask-image: radial-gradient(closest-side, #000 55%, rgba(0,0,0,.4) 80%, transparent 100%);
  mask-image: radial-gradient(closest-side, #000 55%, rgba(0,0,0,.4) 80%, transparent 100%); }
.lens-ghost::after { content: ""; position: absolute; inset: 0; opacity: 0; background: radial-gradient(circle at 50% 50%, rgba(var(--accent-rgb),.5), transparent 70%); transition: opacity 640ms ease; }
.lens-ghost.is-flaring::after { opacity: 1; }

/* In-page lens switch: accent-colored iris reveal instead of the default crossfade */
::view-transition-group(root) { animation-duration: 620ms; }
::view-transition-old(root) { animation: lens-iris-out 620ms cubic-bezier(.2,.8,.2,1) both; }
::view-transition-new(root) { animation: lens-iris-in 620ms cubic-bezier(.2,.8,.2,1) both; }
@keyframes lens-iris-out { to { opacity: 0; filter: saturate(1.4) blur(6px); clip-path: circle(0% at var(--view-origin-x, 82%) var(--view-origin-y, 18%)); } }
@keyframes lens-iris-in { from { opacity: 0; filter: saturate(1.4) blur(6px); clip-path: circle(0% at var(--view-origin-x, 82%) var(--view-origin-y, 18%)); }
  to { opacity: 1; filter: none; clip-path: circle(150% at var(--view-origin-x, 82%) var(--view-origin-y, 18%)); } }

@media (prefers-reduced-motion: reduce) {
    .lens-ghost { display: none; }
    ::view-transition-group(root), ::view-transition-old(root), ::view-transition-new(root) { animation: none !important; }
}

@media (prefers-reduced-motion: reduce) {
    *, *::before, *::after { scroll-behavior: auto !important; animation-duration: .01ms !important; animation-iteration-count: 1 !important; transition-duration: .01ms !important; }
}

@media (forced-colors: active) {.portal,
.view-button,
.button,
.project,
.floating-ai { border: 1px solid ButtonText; }
}
/* icons: distinct external vs download */
.ico{width:14px;height:14px;display:inline-block;vertical-align:-2px;flex:none;}
.button .ico {width:16px;height:16px;}

/* lens entry: centered title, no numbers/badges/enter labels */
.entry-head{flex-direction:column;align-items:center;justify-content:center;gap:16px;text-align:center;}
.entry-head > p:first-child{justify-content:center;}
.entry-head .entry-title{max-width:none;}
.portal{ --portal-x: 0px; --portal-y: 0px; --portal-rx: 0deg; --portal-ry: 0deg; isolation: isolate;grid-template-rows:minmax(160px,1fr) auto;}
.portal-art{min-height:210px;}

/* hero: clean profile image (no floating tags, no glass caption) */
.hero-profile{display:grid;place-items:center;}

/* AI: sleek chip (navbar + hero) + sleek floating launcher */
.header-right{display:flex;align-items:center;gap:12px;justify-self:end;}

.floating-ai:hover{border-color:var(--accent);background:rgba(20,20,28,.96);}

/* career timeline: latest first (HTML) + bullets centred on the line (desktop) */
@media (min-width: 821px){
  /* connector segments flip horizontal on the wide (≥901px) layout — see rule below */
  .career-timeline li{position:relative;padding-top:0;}
  .career-timeline time{display:block;height:34px;line-height:34px;}
  .timeline-marker{position:absolute;top:40px;left:0;margin:0;}
  .career-timeline h3{margin-top:30px;}
}
.writing-card{flex-direction:column;border-right:1px solid var(--line);transition:background .2s ease;}
.writing-card:last-child{border-right:0;}
.writing-card:hover{background:rgba(var(--accent-rgb),.05);}

@media (max-width: 820px){
  .portal{grid-template-columns:115px 1fr;}
  .portal-art{min-height:120px;}
  .speaking-card{gap:26px;}
  .writing-card{border-right:0;border-bottom:1px solid var(--line);}
  .writing-card:last-child{border-bottom:0;}
  .header-right{gap:8px;}
}
/* AI: travelling-light glow pill (navbar "Let's talk", hero + floating) */
.ai-glow{position:relative;display:inline-flex;align-items:center;gap:10px;height:44px;padding:0 19px;border:1px solid transparent;border-radius:999px;color:var(--ink);font-size:13px;font-weight:750;letter-spacing:.01em;cursor:pointer;
  background:linear-gradient(rgba(14,14,20,.94),rgba(14,14,20,.94)) padding-box,
    conic-gradient(from var(--ai-angle),var(--accent),var(--accent-2),var(--accent-3),var(--accent)) border-box;
  box-shadow:0 0 22px rgba(var(--accent-rgb),.14);animation:ai-border 4.6s linear infinite;
  transition:transform .22s var(--ease),box-shadow .22s ease;}
.ai-glow:hover{transform:translateY(-2px);box-shadow:0 10px 32px rgba(var(--accent-rgb),.28);}
.ai-glow__dot{flex:none;width:9px;height:9px;border-radius:50%;background:var(--accent);box-shadow:0 0 12px 1px rgba(var(--accent-rgb),.9);animation:ai-pulse 2s ease-in-out infinite;}
.ai-glow--lg{height:54px;padding:0 22px;font-size:14px;}
.header-right .ai-glow{height:40px;padding:0 16px;font-size:12.5px;}
.floating-ai{position:fixed;z-index:900;right:22px;bottom:max(22px,env(safe-area-inset-bottom));display:inline-flex;align-items:center;gap:10px;min-width:0;min-height:0;height:50px;padding:0 20px;grid-template-columns:none;
  border:1px solid transparent;border-radius:999px;color:var(--ink);
  background:linear-gradient(rgba(14,14,20,.94),rgba(14,14,20,.94)) padding-box,
    conic-gradient(from var(--ai-angle),var(--accent),var(--accent-2),var(--accent-3),var(--accent)) border-box;
  box-shadow:0 18px 50px rgba(0,0,0,.45),0 0 30px rgba(var(--accent-rgb),.16);backdrop-filter:blur(14px);
  opacity:0;transform:translateY(18px);pointer-events:none;animation:ai-border 4.6s linear infinite;
  transition:opacity .2s,transform .3s var(--ease),box-shadow .22s;}
.floating-ai.is-visible{opacity:1;transform:none;pointer-events:auto;}
.floating-ai:hover{transform:translateY(-3px);box-shadow:0 24px 60px rgba(0,0,0,.5),0 0 42px rgba(var(--accent-rgb),.26);}
.floating-ai b{font-size:13px;font-weight:750;}
@media (max-width: 560px){.header-right .ai-glow{display:none;}}

/* Work: full-photo cards, title over image, subtitle on hover */
.project-grid{display:grid;grid-template-columns:repeat(2,1fr);gap:18px;}
.project{position:relative;display:block;grid-column:auto;overflow:hidden;aspect-ratio:16/11;border:1px solid var(--line);border-radius:var(--radius);background:var(--panel);color:inherit;}
.project-photo{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;transition:transform .8s var(--ease);}
.project:hover .project-photo{transform:scale(1.045);}
.project-scrim{position:absolute;inset:0;background:linear-gradient(180deg,rgba(8,8,12,.05),rgba(8,8,12,.35) 48%,rgba(8,8,12,.92));transition:background .4s ease;}
.project:hover .project-scrim{background:linear-gradient(180deg,rgba(8,8,12,.12),rgba(8,8,12,.5) 42%,rgba(8,8,12,.96));}
.project-overlay{position:absolute;inset:0;z-index:2;display:flex;flex-direction:column;justify-content:flex-end;padding:clamp(22px,2.6vw,36px);text-shadow:0 1px 16px rgba(0,0,0,.6);}
.project-tagline{color:var(--ink-2);font-family:var(--sans);font-size:15px;font-weight:550;letter-spacing:.005em;line-height:1.35;}
.project-overlay h3{margin:9px 0 0;font-weight:590;letter-spacing:-.05em;line-height:1;}
/* size comes from --fs-h3 */
.project-sub{max-width:46ch;color:#c8c5cf;font-size:15px;line-height:1.5;max-height:0;margin-top:0;opacity:0;transform:translateY(6px);overflow:hidden;transition:max-height .45s var(--ease),opacity .35s ease,margin-top .45s var(--ease),transform .45s var(--ease);}
.project:hover .project-sub,.project:focus-visible .project-sub{max-height:130px;opacity:1;margin-top:12px;transform:none;}
.project-open{display:inline-flex;align-items:center;gap:7px;margin-top:0;max-height:0;overflow:hidden;color:var(--accent);font-size:13px;font-weight:750;opacity:0;transform:translateY(6px);transition:opacity .35s ease,transform .45s var(--ease),max-height .45s var(--ease),margin-top .45s var(--ease);}
.project:hover .project-open,.project:focus-visible .project-open{opacity:1;transform:none;max-height:44px;margin-top:14px;}
.project-open i{font-style:normal;}
@media (max-width: 820px){
  .project-grid{display:grid;grid-template-columns:1fr;gap:16px;}
  .project,html[data-viewpoint] .project[data-project]{aspect-ratio:16/11;grid-column:auto;}
  .project-sub{max-height:130px;opacity:1;margin-top:12px;transform:none;}
  .project-open{opacity:1;transform:none;max-height:44px;margin-top:14px;}
}

/* Writing cards: same full-photo overlay language as work */
.writing-cards{display:grid;grid-template-columns:repeat(3,1fr);gap:16px;}
.writing-card{position:relative;display:block;overflow:hidden;aspect-ratio:4/3;border:1px solid var(--line);border-radius:var(--radius-sm);background:var(--panel);}
.writing-photo{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;transition:transform .6s var(--ease);}
.writing-card:hover .writing-photo{transform:scale(1.05);}
.writing-scrim{position:absolute;inset:0;background:linear-gradient(180deg,rgba(8,8,12,.1),rgba(8,8,12,.42) 50%,rgba(8,8,12,.93));}
.writing-overlay{position:absolute;inset:0;z-index:2;display:flex;flex-direction:column;justify-content:flex-end;gap:8px;padding:22px;}
.writing-overlay small{color:#cbc8d2;font-family:var(--mono);}
.writing-overlay strong{font-size:18px;font-weight:570;letter-spacing:-.02em;line-height:1.24;}
.writing-card:hover .writing-overlay strong{color:var(--accent);}
@media (max-width: 820px){.writing-cards{grid-template-columns:1fr;}}

/* Speaking: make it delightful — gradient stage + animated equaliser */
.speaking-card{align-items:stretch;}

.speaking-meta{font-family:var(--mono);}

/* AI section: no bar; question hover shows a copy icon, not an arrow */
.question-examples button[data-ask-question]::after{content:"";width:16px;height:16px;right:12px;top:50%;background:var(--accent);opacity:0;transform:translateY(-50%) translateX(-4px);
  -webkit-mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2'%3E%3Crect x='9' y='9' width='11' height='11' rx='2'/%3E%3Cpath d='M5 15V5a2 2 0 0 1 2-2h10'/%3E%3C/svg%3E") center/contain no-repeat;
  mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2'%3E%3Crect x='9' y='9' width='11' height='11' rx='2'/%3E%3Cpath d='M5 15V5a2 2 0 0 1 2-2h10'/%3E%3C/svg%3E") center/contain no-repeat;}
.question-examples button[data-ask-question]:hover::after,.question-examples button[data-ask-question]:focus-visible::after{opacity:1;transform:translateY(-50%) translateX(0);}
.question-examples button[data-ask-question].is-copied::after{content:"✓ copied";width:auto;height:auto;background:none;-webkit-mask:none;mask:none;color:var(--accent);font-family:var(--sans);font-size:12.5px;font-weight:600;opacity:1;}

/* career links now use the button hierarchy (primary CV, secondary LinkedIn) */

/* tighter hero + calmer rhythm */
.hero-copy{max-width:760px;}
/* hero should not exceed the viewport */

/* career buttons: centred content, vertically aligned */

/* Voice: quiet editorial labels instead of boxed headers */
.voice-body{display:grid;gap:clamp(46px,7vw,86px);}
.speaking-quote::before{content:"“";}
.speaking-quote::after{content:"”";}

/* AI section: breathing room + questions as a clean list (no card) */
.assistant-cta{margin-top:clamp(30px,4vw,40px);}
.chat-status{margin-top:14px;}
.question-examples{padding:0;}
.aq-lead{display:flex;flex-wrap:wrap;justify-content:space-between;gap:8px;margin:0 0 4px;color:var(--accent);font-family:var(--mono);font-size:12px;font-weight:650;letter-spacing:.08em;text-transform:uppercase;}
.aq-lead .question-hint{color:#8b8894;font-weight:500;letter-spacing:.03em;text-transform:none;}
.question-examples ul{margin:0;padding:0;list-style:none;}
.question-examples li{border-top:1px solid var(--line);}
.question-examples li:last-child{border-bottom:1px solid var(--line);}
.question-examples button[data-ask-question]{padding-left:0;}
/* Hero: fit within the viewport. */
@media (max-width: 1080px){.hero-profile{min-height:0;}}
@media (max-width: 820px){.hero-profile{min-height:0;} }

/* AI questions: give the opening quote its own gutter (no overlap with text) */
.question-examples button[data-ask-question]{padding:15px 34px 15px 27px;}
.question-examples button[data-ask-question]::before{left:2px;top:15px;}
.question-examples button[data-ask-question]:hover,.question-examples button[data-ask-question]:focus-visible{padding-left:33px;}

/* Career buttons: clean, centred, single line, icon locked to the text */
/* Hero: portrait height is tied to the viewport, everything else tightened */
.world-hero{min-height:auto;padding:86px 0 38px;}
.hero-grid{min-height:0;gap:clamp(28px,4vw,64px);align-items:center;}
.hero-description{min-height:0;}
.hero-description p{margin-top:16px;}
.hero-actions{margin-top:22px;}
.hero-profile{min-height:0;}
.profile-card{position:relative;top:auto;right:auto;width:auto;height:clamp(270px,44svh,430px);}
.profile-card img{height:100%;}

@media (max-width: 820px){
  .profile-card{width:min(72%,340px);height:auto;}
  .profile-card img{height:auto;}
}

/* Career buttons: robustly centred, single line, icon locked to the baseline */
.career-links{display:flex;flex-wrap:wrap;align-items:center;gap:12px;}
.career-links .button{display:inline-flex;width:auto;}
.career-links .button .ico{vertical-align:middle;display:block;flex:none;width:15px;height:15px;}

/* career buttons: fixed height so the label is truly centred */
.career-links .button{align-items:center;}

/* career/speaking buttons == standard secondary button; consistent lens glyphs */
.career-links .button{justify-content:space-between;gap:22px;height:auto;min-height:54px;padding:0 21px;white-space:normal;line-height:1.2;font-size:13px;letter-spacing:.015em;}
.career-links .button .ico,.speaking-card .button .ico{width:16px;height:16px;}
.view-menu button span{font-size:12px;}
.view-symbol{font-size:12px;line-height:1;}
/* consistent lens glyph sizes (star renders small, "</>" renders wide) */
.view-menu button span{display:inline-block;width:22px;text-align:center;line-height:1;}
.view-menu button[data-change-view="creative"] span{font-size:15px;}
.view-menu button[data-change-view="visionary"] span{font-size:12px;}
.view-menu button[data-change-view="engineer"] span{font-size:9px;letter-spacing:-.5px;}
html[data-viewpoint="creative"] .view-symbol{font-size:15px;}
html[data-viewpoint="visionary"] .view-symbol{font-size:12px;}
html[data-viewpoint="engineer"] .view-symbol{font-size:10px;letter-spacing:-.5px;}

/* timeline: draw the connecting line in on entrance */
.career-timeline li::before{transform:scaleX(0);transform-origin:left center;transition:transform .9s var(--ease) .05s;}
.career-timeline ol.line-drawn li::before{transform:scaleX(1);}
@media (max-width: 820px){
  .career-timeline li::before{transform:scaleY(0);transform-origin:top center;}
  .career-timeline ol.line-drawn li::before{transform:scaleY(1);}
}

/* project cards: internal-style arrow (not the external-link glyph) */
.project-open .arrow-r{width:16px;height:16px;display:inline-block;vertical-align:-3px;transition:transform .25s var(--ease);}
.project:hover .project-open .arrow-r,.project:focus-visible .project-open .arrow-r{transform:translateX(4px);}

/* Speaking: richer, less bland — big accent quote mark + pulsing, mouse-reactive glow */
@keyframes glow-breathe{0%,100%{opacity:.6}
  50%{opacity:1}}
.speaking-card{padding:clamp(36px,5vw,66px);}
.speaking-card::before{content:"";position:absolute;inset:0;z-index:0;pointer-events:none;background:radial-gradient(circle at 86% -14%, rgba(var(--accent-rgb),.24), transparent 46%);transform:translate(calc(var(--gx,0)*34px),calc(var(--gy,0)*22px));animation:glow-breathe 7.5s ease-in-out infinite;}
.speaking-card > *{position:relative;z-index:1;}
.speaking-quote{letter-spacing:-.03em;text-wrap:balance;}
.speaking-quote::before,.speaking-quote::after{content:none;}
.speaking-card .button{align-self:flex-end;}

/* ambient glows: gentle pulse + subtle mouse parallax (Still curious + AI) */
.contact::before{animation:glow-breathe 7s ease-in-out infinite;}
.assistant::before{animation:glow-breathe 8.5s ease-in-out infinite;}
@media (prefers-reduced-motion: reduce){
  .speaking-card::before,.contact::before,.assistant::before{animation:none;transform:none;}
  .career-timeline li::before{transform:none!important;transition:none;}
}
/* Glows: oversize far beyond the container so the clipped edge never shows
   near the border (borders stay perfectly static; only the soft centre drifts) */
.contact::before{position:absolute;z-index:0;inset:-40%;}
.assistant::before{position:absolute;z-index:0;inset:-40%;}

/* Lens dropdown: identical glass to the navbar, and clear the header */
.view-menu{top:calc(100% + 6px);border:1px solid var(--line);}

/* Speaking: bold poster — vibrant title panel + quote aside (no pulsing dot) */
.speaking-title{position:relative;}
.contact::before,.assistant::before{position:absolute;z-index:0;}
.contact::before{pointer-events:none;transform:translate(calc(var(--gx,0)*26px),calc(var(--gy,0)*18px));left:12%;right:12%;bottom:-28%;height:78%;background:radial-gradient(ellipse at center, rgba(var(--accent-rgb),.20), transparent 66%);filter:blur(64px);}
.contact > .shell{position:relative;z-index:1;}
.assistant::before{pointer-events:none;content:"";transform:translate(calc(var(--gx,0)*22px),calc(var(--gy,0)*14px));right:-6%;top:-8%;width:62%;height:80%;background:radial-gradient(ellipse at center, rgba(var(--accent-rgb),.16), transparent 66%);filter:blur(64px);}
.assistant-stage{position:relative;z-index:1;}
@media (prefers-reduced-motion: reduce){.contact::before,.assistant::before{filter:blur(64px);}}
.contact::before,.assistant::before{pointer-events:none;display:none !important;content:none !important;}
.site-header{background:transparent;backdrop-filter:none;}
.site-header::before{content:"";position:absolute;inset:0;z-index:-1;pointer-events:none;background:linear-gradient(rgba(8,8,12,.88),rgba(8,8,12,.55),transparent);transition:background .3s ease,backdrop-filter .3s ease;}
.site-header.is-scrolled{background:transparent;backdrop-filter:none;border-color:transparent;}

/* Portrait: static presentation (it is not interactive). */
.profile-card img{filter:saturate(.95) contrast(1.03);transform:none;transition:none;}
/* No hover state: the profile photo isn't clickable. */
/* portrait: pointer-driven 3D tilt (perspective on the stage) */
.hero-profile{perspective:1000px;}
.profile-card{aspect-ratio:.82;transform:rotateX(var(--tilt-x,0deg)) rotateY(var(--tilt-y,0deg));transform-style:preserve-3d;transition:transform .35s var(--ease);will-change:transform;}

/* Selected work: category filters */
.work-filters{display:flex;flex-wrap:wrap;gap:8px;margin-bottom:36px;}
.work-filters button{min-height:44px;padding:9px 16px;border:1px solid var(--line);border-radius:999px;background:transparent;color:var(--muted);font-family:var(--sans);font-size:12.5px;font-weight:650;letter-spacing:.02em;cursor:pointer;transition:border-color .2s,color .2s,background .2s;}
.work-filters button:hover{color:var(--ink);border-color:var(--line-strong);}
.work-filters button[aria-pressed="true"]{color:var(--bg);background:var(--accent);border-color:var(--accent);}
.project.is-hidden{display:none !important;}
.work-more a{display:inline-flex;align-items:center;gap:8px;color:var(--muted);font-size:14px;font-weight:650;transition:color .2s;}
.work-more a:hover{color:var(--accent);}

/* AI buttons: a bright gradient that TRAVELS around a dim border (comet), not a full recolor */
.ai-glow,.floating-ai{
  animation:ai-border 5.5s linear infinite;
}
@media (prefers-reduced-motion: reduce){.ai-glow,.floating-ai{animation:none;}}
/* The travelling border animates a custom property, which repaints on the main
   thread every frame. Promote the buttons to their own layers so that repaint
   stays the size of the button instead of dragging the animated hero canvas
   behind it along. */
.ai-glow,.floating-ai{
  background:
    linear-gradient(rgba(14,14,20,.94),rgba(14,14,20,.94)) padding-box,
    conic-gradient(from var(--ai-angle),
      rgba(var(--accent-rgb),.15) 0deg,
      var(--accent) 44deg,
      var(--accent-2) 92deg,
      var(--accent-3) 140deg,
      rgba(var(--accent-rgb),.15) 196deg,
      rgba(var(--accent-rgb),.15) 360deg) border-box;will-change:transform;}
.hero-field{will-change:transform;}
/* While the floating button is hidden, it should cost nothing: no blur of the
   (animated) page behind it, no border animation. */
.floating-ai:not(.is-visible){backdrop-filter:none;-webkit-backdrop-filter:none;animation:none;}
.speaking-card::before {display:none !important;}
.speaking-card{position:relative;display:grid;grid-template-columns:.92fr 1.08fr;gap:0;min-height:0;overflow:hidden;border:1px solid var(--line);border-radius:32px;background:var(--panel);}
.speaking-photo{position:relative;margin:0;min-height:260px;overflow:hidden;}
.speaking-photo img{width:100%;height:100%;}
.speaking-body{display:flex;flex-direction:column;justify-content:center;padding:clamp(34px,4.4vw,60px);}
.speaking-kicker{color:var(--accent);font-family:var(--mono);font-size:12px;font-weight:750;letter-spacing:.1em;text-transform:uppercase;}
.speaking-title{margin:16px 0 0;font-family:var(--serif);font-weight:400;color:var(--ink);}
.speaking-quote{margin:26px 0 32px;padding-left:20px;border-left:2px solid var(--accent);font-family:var(--serif);font-size:clamp(20px,2.2vw,29px);font-weight:400;font-style:italic;line-height:1.34;color:var(--ink);max-width:34ch;}
.speaking-body .button{align-self:flex-start;}
@media (max-width: 820px){.speaking-card{grid-template-columns:1fr;}
  .speaking-photo{min-height:280px;}}
.work-more{text-align:center;}
.work-toggle{display:inline-flex;align-items:center;gap:9px;padding:13px 24px;border:1px solid var(--line-strong);border-radius:999px;background:transparent;color:var(--ink);font-family:var(--sans);font-size:13px;font-weight:700;letter-spacing:.01em;cursor:pointer;transition:border-color .2s,background .2s,transform .2s;}
.work-toggle:hover{border-color:var(--accent);background:rgba(var(--accent-rgb),.06);transform:translateY(-2px);}
.work-toggle .count{color:var(--accent);font-family:var(--mono);}

/* speaking: frame the FEUP photo on João */
/* AI gradient: harmonious analogous trio per lens (no clashing 3rd colour) */
html[data-viewpoint="creative"]{--accent-2:#9a5cff;--accent-3:#6f7bff;}
/* pink → purple → periwinkle */
html[data-viewpoint="visionary"]{
    --accent: #ffb22c;
    --accent-rgb: 255, 178, 44;--accent-2:#ff9a3d;--accent-3:#ffe07a;}
/* amber → warm orange → gold */
html[data-viewpoint="engineer"]{
    --accent: #5ee7d3;
    --accent-rgb: 94, 231, 211;--accent-2:#45cdf0;--accent-3:#5b8cff;}
/* teal → cyan → blue */

/* Speaking photo: stronger, lens-tinted duotone treatment */

/* Navbar: links live directly on the bar (no pill container) */
.primary-nav{border:0;background:transparent;padding:0;}
.primary-nav a{padding:6px 2px;border-radius:0;color:#cbc8d2;}
.primary-nav a:hover{background:transparent;color:var(--ink);}

/* Glass (navbar + dropdown): lighter + more saturated so it reads as glass;
   kept dark enough for AA-legible light text over a blurred backdrop */
.site-header.is-scrolled::before{background:transparent;backdrop-filter:none;box-shadow:none;}
/* Mobile header: the same glass as the desktop nav pill, applied to the full-
   width bar instead of a floating pill.
   Two things make the desktop treatment fail below 915px. The links, lens
   switcher and CV are hidden, so the nav pill that normally spans the bar and
   carries the scrolled backdrop shrinks to just the brand. And .is-scrolled
   drops the header to top:12px so the pill reads as an island, which leaves a
   12px strip of live page above it. So the bar sits flush to the top edge and
   takes the glass itself; the pill goes clear so the two do not stack. */
@media (max-width: 915px){
  .site-header, .site-header.is-scrolled{ top:0; }
  .site-header::before,
  .site-header.is-scrolled::before{
    background:rgba(17,17,26,.55);
    backdrop-filter:saturate(165%) blur(24px);
    border-bottom:1px solid var(--line);
    box-shadow:inset 0 1px 0 rgba(255,255,255,.07), 0 10px 34px rgba(0,0,0,.32);
  }
  /* backdrop-filter has to go too. Clearing only the background left the pill
     still blurring and saturating what was behind it, in its 999px shape and on
     top of the bar's own blur, which showed as a halo around the brand. */
  .is-scrolled .nav-pill{ background:transparent; border-color:transparent; box-shadow:none; backdrop-filter:none; }
  /* .header-right is empty here (the lens switcher is hidden), so without this
     the menu button sits next to the brand rather than at the far edge. */
  .menu-button{ margin-left:auto; }
}
.view-menu{background:rgba(17,17,26,.62);backdrop-filter:saturate(165%) blur(24px);box-shadow:0 24px 70px rgba(0,0,0,.42), inset 0 1px 0 rgba(255,255,255,.07);}

/* AI skills section */
.skills{padding:clamp(110px,12vw,175px) 0;background:linear-gradient(180deg,#101018,#0b0b11);}
.skills-grid{display:grid;grid-template-columns:repeat(2,1fr);gap:16px;}
.skill-card{position:relative;display:flex;flex-direction:column;padding:clamp(26px,3vw,38px);border:1px solid var(--line);border-radius:var(--radius-sm);background:var(--panel);transition:border-color .25s,transform .25s var(--ease);}
/* No card-level hover: the skill card isn't clickable — only the GitHub link inside is. */
.skill-icon{width:46px;height:46px;display:grid;place-items:center;margin-bottom:22px;border:1px solid var(--line-strong);border-radius:13px;color:var(--accent);font-size:21px;}
.skill-card h3{margin:0;font-size:22px;font-weight:600;letter-spacing:-.03em;}
.skill-card p{margin:12px 0 0;color:var(--ink-3);font-size:15px;line-height:1.62;}
.skill-tag{align-self:flex-start;margin-top:22px;padding:5px 11px;border:1px solid rgba(var(--accent-rgb),.35);border-radius:999px;color:var(--accent);font-family:var(--mono);font-size:12px;letter-spacing:.06em;text-transform:uppercase;}
@media (max-width: 820px){
  .skills-grid{grid-template-columns:1fr;}
}
.skill-foot{display:flex;align-items:center;justify-content:space-between;gap:14px;margin-top:auto;padding-top:24px;}
.skill-foot .skill-tag{margin-top:0;}
.skill-link{display:inline-flex;align-items:center;gap:7px;color:var(--muted);font-size:13px;font-weight:650;transition:color .2s;}
.skill-link:hover{color:var(--accent);}
.skill-link svg{opacity:.85;}
.speaking-photo img{object-fit:cover;}
/* fade the inner edge into the card (seamless, editorial) + a soft base vignette — no colour cast */
.speaking-photo img{filter:contrast(1.06) saturate(1.06) brightness(.99);object-position:36% 16%;}
@media (max-width: 820px){
  .speaking-photo img{object-position:36% 14%;}
}
.speaking-photo::after{content:"";position:absolute;inset:0;mix-blend-mode:normal;
  background:
    linear-gradient(100deg, transparent 24%, rgba(17,17,26,.68) 66%, var(--panel) 94%),
    radial-gradient(58% 60% at 100% 82%, rgba(var(--accent-rgb),.20), transparent 60%),
    linear-gradient(0deg, rgba(8,8,12,.30), transparent 42%);
}
@media (max-width: 820px){
  .speaking-photo::after{
    background:
      linear-gradient(0deg, var(--panel) 0%, rgba(17,17,26,.64) 20%, transparent 52%),
      radial-gradient(70% 52% at 50% 100%, rgba(var(--accent-rgb),.15), transparent 60%);
  }
}
.work{padding-bottom:clamp(56px,6.5vw,88px);}
.work-more{margin-top:38px;}
.skills{padding-top:clamp(60px,7vw,92px);}
.talk-topics-lead{font-weight:650;}
.section-intro,.method-head{grid-template-columns:1fr;gap:0;}
/* In a single column the gap would become a 110px row gap. */
.section-intro{margin-bottom:clamp(40px,5vw,58px);}
.project-grid{position:relative;}

/* =========================================================================
   Botpress webchat: hide its default launcher. The site's own AI buttons (nav,
   assistant section, floating) open it through the API instead.
   ========================================================================= */
/* Precise token match ONLY. A [class*="bpFab"] substring selector also hid the
   webchat container, so the bot opened invisibly. If the widget still does not
   appear, delete this single line to restore Botpress' own launcher. */
.bpFab{display:none!important;}

/* =========================================================================
   Mobile AI buttons: keep every assistant entry point reachable on phones.
   ========================================================================= */
.ai-glow__short{display:none;}
@media (max-width: 560px){
  /* large in-page CTAs: let the label wrap instead of overflowing */
  .ai-glow--lg{height:auto;min-height:52px;padding:13px 20px;font-size:13.5px;line-height:1.35;
               white-space:normal;text-align:left;max-width:100%;}
  /* floating pill: swap the long label for a short one so it stays compact */
  .floating-ai{min-width:0;height:50px;padding:0 17px;}
  .floating-ai b{display:none;}
  .floating-ai .ai-glow__short{display:inline;font-size:13px;font-weight:750;}
}

/* =========================================================================
   Mobile header + hero CTA alignment
   ========================================================================= */
/* Brand and menu button share one row. Below 820px .primary-nav is hidden but
   .header-right remains as an (empty) grid item, so a 2-column track pushed
   .menu-button onto a second row. Give it its own track instead. */

/* The hero's AI CTA is .ai-glow (not .button), so it missed the full-width
   treatment its sibling gets. Match it — hero only; other AI buttons unchanged. */
@media (max-width: 560px){
  .hero-actions .ai-glow{width:100%;}
}

/* ---- level 1: primary statements ---- */
.hero-copy h1{margin-top:0;font-size:var(--fs-display);line-height:1.04;letter-spacing:-.022em;}
.contact h2{font-size:var(--fs-closing);line-height:1.05;letter-spacing:-.02em;}
.entry-head .entry-title{font-size:var(--fs-entry);line-height:1.08;letter-spacing:-.018em;}

/* ---- level 2: section headings (all of them, same size) ---- */
.section-intro h2,.method-head h2,.career-head h2,.assistant-copy h2{
  font-size:var(--fs-h2);line-height:1.08;letter-spacing:-.018em;}
.speaking-title {
  font-size:var(--fs-h3-lg);line-height:1.1;letter-spacing:-.014em;}
.project-overlay h3,
.method-list h3 {
  font-size:var(--fs-h3);line-height:1.16;letter-spacing:-.01em;}
.skill-card h3,
.career-timeline h3 {
  font-size:var(--fs-h4);line-height:1.25;letter-spacing:-.004em;}

/* ---- vertical rhythm ---- */
.work,.method,.voice,.assistant,.skills,.contact{padding:var(--section-pad) 0;}
.talk,
.speaking-card {
  border-radius:var(--radius-lg);}
.profile-card,.portal{border-radius:var(--radius);}

/* =========================================================================
   Lens typography — ligatures + a distinct highlight face per lens
   ========================================================================= */
/* Standard ligatures on wherever a serif highlight is used.
   Scoped to the serif lenses so the mono highlight (Systems) is untouched. */
html[data-viewpoint="creative"] .hero-copy h1 em,
html[data-viewpoint="visionary"] .hero-copy h1 em,
html[data-viewpoint="creative"] .doc h1 em,
html[data-viewpoint="visionary"] .doc h1 em,
.entry-head .entry-title em,
.portal--creative .portal-copy strong{
  /* Standard ligatures only (f_i, f_l, f_f_i, f_f_l); the face has no
     discretionary set. */
  font-variant-ligatures:common-ligatures contextual;
  font-feature-settings:"liga" 1,"clig" 1;
}

/* One highlight face per lens: Experience uses Cookie at 1.42em with no
   tracking (expressive, human); Strategy uses Fraunces at 1.04em with WONK 1
   (editorial, considered); Systems uses mono at 1em, -.01em (technical).
   Adding a highlight selector means adding it to all three groups below. */

/* Optical size-matching for the serif highlight: scaled up slightly with open
   tracking so it sits as an equal next to Figtree. The Systems mono highlight
   does the inverse (.88em), since mono runs wide. */
html[data-viewpoint="creative"] .hero-copy h1 em,
html[data-viewpoint="visionary"] .hero-copy h1 em{
  font-size:1.09em;
  letter-spacing:.015em;
}

/* Strategy highlight: Fraunces is drawn wide, so it needs none of the optical
   bump above. */
html[data-viewpoint="visionary"] .hero-copy h1 em{
  color:var(--accent);
  font-family:var(--serif);
  font-style:italic;
  /* real Fraunces italic is loaded; not a synthesized slant */
  font-weight:500;
  font-size:1.04em;
  letter-spacing:-.005em;
  font-variation-settings:"WONK" 1;
  /* wonky h m n s & ; house serif stays at the 0 default */
}

/* Experience highlight: Cookie — a connecting script, so the letterforms join.
   Metrics-derived sizing: Figtree x-height .500 vs Cookie .375 (measured from
   outlines, not OS/2, which is unset in several script fonts),
   i.e. ~1.52em to match. Pulled slightly under, since script ascenders and
   flourishes read larger than their x-height suggests.
   letter-spacing MUST stay 0 — any tracking pulls the joins apart. */
html[data-viewpoint="creative"] .hero-copy h1 em{
  color:var(--accent);
  font-family:"Cookie",cursive;
  font-style:normal;
  font-weight:400;
  font-size:1.48em;
  letter-spacing:0;
  line-height:1;
  font-variant-ligatures:common-ligatures contextual discretionary-ligatures;
  font-feature-settings:"calt" 1,"liga" 1,"clig" 1,"dlig" 1,"init" 1,"fina" 1;
}

/* =========================================================================
   Lens gate: each portal title uses that lens's own face
   ========================================================================= */
/* Experience -> Cookie. Base rule tracks -.06em, which would crush the script's
   joins, so tracking is reset and the size scaled by the x-height ratio (~1.47). */
.portal--creative .portal-copy strong{
  font-family:"Cookie",cursive;
  font-style:normal;
  font-weight:400;
  font-size:clamp(41px,4.1vw,65px);
  letter-spacing:0;
  line-height:.72;
  font-variant-ligatures:common-ligatures contextual discretionary-ligatures;
  font-feature-settings:"calt" 1,"liga" 1,"clig" 1,"dlig" 1,"init" 1,"fina" 1;
}
/* Strategy -> Fraunces. Drawn wide, so the tight base tracking is relaxed. */
.portal--visionary .portal-copy strong{
  font-family:"Fraunces",Georgia,serif;
  font-style:normal;
  font-weight:500;
  letter-spacing:-.035em;
  /* Same wonky h m n s & the Strategy lens highlight uses, so the portal
     previews the face the visitor is about to choose. */
  font-variation-settings:"WONK" 1;
}

/* =========================================================================
   Lens signature applied to every highlight, not just the hero. Written per
   lens so these match the specificity of the html[data-viewpoint] rules and
   win on source order. Sizing: Cookie's x-height is low, so lowercase-led
   words use the x-height basis (~1.4em), while the capital-led portal title
   "Experience" uses the cap-height basis.
   ========================================================================= */
html[data-viewpoint="creative"] .hero-copy h1 em,
html[data-viewpoint="creative"] .section-intro h2 em,
html[data-viewpoint="creative"] .assistant-copy h2 em,
html[data-viewpoint="creative"] .contact h2 em,
html[data-viewpoint="creative"] .method-head h2 em,
html[data-viewpoint="creative"] .career-head h2 em,
html[data-viewpoint="creative"] .case-title em,
html[data-viewpoint="creative"] .case-ai-title em,
html[data-viewpoint="creative"] .mcp h3 em,
html[data-viewpoint="creative"] .doc h1 em{
  color:var(--accent);
  font-family:"Cookie",cursive;
  font-style:normal;
  font-weight:400;
  font-size:1.42em;
  letter-spacing:0;
  line-height:1;
  font-variant-ligatures:common-ligatures contextual discretionary-ligatures;
  font-feature-settings:"calt" 1,"liga" 1,"clig" 1,"dlig" 1,"init" 1,"fina" 1;
}
html[data-viewpoint="visionary"] .hero-copy h1 em,
html[data-viewpoint="visionary"] .section-intro h2 em,
html[data-viewpoint="visionary"] .assistant-copy h2 em,
html[data-viewpoint="visionary"] .contact h2 em,
html[data-viewpoint="visionary"] .method-head h2 em,
html[data-viewpoint="visionary"] .career-head h2 em,
html[data-viewpoint="visionary"] .case-title em,
html[data-viewpoint="visionary"] .case-ai-title em,
html[data-viewpoint="visionary"] .mcp h3 em,
html[data-viewpoint="visionary"] .doc h1 em{
  color:var(--accent);
  font-family:var(--serif);
  font-style:italic;
  /* real Fraunces italic is loaded; not a synthesized slant */
  font-weight:500;
  font-size:1.04em;
  letter-spacing:-.005em;
  font-variation-settings:"WONK" 1;
  /* wonky h m n s & ; house serif stays at the 0 default */
}
html[data-viewpoint="engineer"] .hero-copy h1 em,
html[data-viewpoint="engineer"] .section-intro h2 em,
html[data-viewpoint="engineer"] .assistant-copy h2 em,
html[data-viewpoint="engineer"] .contact h2 em,
html[data-viewpoint="engineer"] .method-head h2 em,
html[data-viewpoint="engineer"] .career-head h2 em,
html[data-viewpoint="engineer"] .case-title em,
html[data-viewpoint="engineer"] .case-ai-title em,
html[data-viewpoint="engineer"] .mcp h3 em,
html[data-viewpoint="engineer"] .doc h1 em{
  color:var(--accent);
  font-family:var(--mono);
  font-style:normal;
  font-size:1em;
  letter-spacing:-.01em;
}

/* sub-text under the Method heading, matching the section-intro treatment */
.method-head p{max-width:640px;margin:23px 0 0;color:var(--muted);font-size:15px;}

/* =========================================================================
   Lens gate: equal title line-boxes, so the 1fr art row is the same height in
   every card regardless of each lens's title face.
   ========================================================================= */
.portal--engineer .portal-copy strong{line-height:1.14;}
/* mobile: the base rule drops to a fixed size, so scale Experience to match it
   (its higher specificity would otherwise keep the desktop clamp) */
@media (max-width: 820px){
  .portal--creative .portal-copy strong{font-size:36px;line-height:.72;}
  .portal--engineer .portal-copy strong{line-height:1.14;}
}
@media (max-width: 560px){
  .portal--creative .portal-copy strong{font-size:31px;line-height:.72;}
}

/* =========================================================================
   "Choose again" — reopens the lens gate. Deliberately demoted: separated by a
   rule and set in muted type so it never reads as a fourth lens.
   ========================================================================= */
.view-menu-sep{height:1px;margin:7px 4px;background:var(--line);}
.view-menu button.view-menu-back{color:var(--muted);font-size:13px;}
.view-menu button.view-menu-back:hover{color:var(--ink);background:rgba(255,255,255,.06);}
.mobile-gate-back{display:flex;align-items:center;justify-content:center;gap:8px;width:100%;margin-top:22px;padding:16px 0 4px;
  border:0;border-top:1px solid var(--line);background:transparent;color:var(--muted);font-family:inherit;font-size:14px;cursor:pointer;}
.mobile-gate-back:hover{color:var(--ink);}
/* reading text */
.section-intro > div > p,
.method-head p{font-size:16px;}
/* secondary reading text */
.career-timeline li p{font-size:14px;}
/* micro-labels */
.portal-copy small,
.entry-note span,
.profile-card figcaption span{font-size:11.5px;}
.entry-head > p:first-child {font-size:12px;}
/* nav */
.primary-nav a{font-size:13px;}
/* hero lead matches the section descriptions (16px) */
.hero-description p{font-size:17px;}

/* more air under the gate heading so it stops crowding the portals */
.entry-head{padding-bottom:clamp(10px,2.4vh,26px);}
/* Mobile stacks the gate, so the gap .entry already puts between the header
   and the portals is enough; the full desktop value on top of it left a hole. */
@media (max-width: 820px){.entry-head{padding-bottom:clamp(2px,0.6vh,8px);}}

/* The gate must never show a scrollbar. Content is still reachable by
   scroll/keyboard on very short viewports — the bar itself is just hidden. */
.entry{scrollbar-width:none;-ms-overflow-style:none;overscroll-behavior:contain;}
.entry::-webkit-scrollbar{width:0;height:0;display:none;}

/* =========================================================================
   Gate: no flash on reload, and no page scrollbar while it is up
   ========================================================================= */
/* Set by a blocking script in <head>, so this applies on the very first paint. */
html.lens-saved .entry{display:none!important;}
html.lens-saved #site[hidden]{display:block!important;}

/* While the gate covers the screen the document behind it must not scroll —
   otherwise the browser shows its scrollbar down the right edge. */
html.gate-open,html.gate-open body{overflow:hidden;}

/* =========================================================================
   Gate re-entry. Only when reopened via "Choose again" — never on first load,
   since the class is added by openGate() alone. Scaling DOWN into place reads
   as the layer settling on top of the page, mirroring the .is-leaving exit
   (which scales up and fades away).

   fill-mode is `backwards`, NOT `both`: a forwards fill would leave
   `transform:none` applied by the animation, which outranks the declaration in
   .portal:hover and would kill the 3D tilt afterwards.
   ========================================================================= */
@keyframes gate-drop{from{opacity:0;transform:scale(1.05);}
  to{opacity:1;transform:none;}}
@keyframes gate-fade{from{opacity:0;}
  to{opacity:1;}}

.entry.is-returning{animation:gate-fade 240ms var(--ease) backwards;}
.entry.is-returning .entry-head{animation:gate-drop 400ms var(--ease) backwards;}
.entry.is-returning .portal{animation:gate-drop 440ms var(--ease) backwards;}
.entry.is-returning .portal:nth-child(1){animation-delay:50ms;}
.entry.is-returning .portal:nth-child(2){animation-delay:85ms;}
.entry.is-returning .portal:nth-child(3){animation-delay:120ms;}
.entry.is-returning .entry-note{animation:gate-fade 520ms var(--ease) backwards;}

@media (prefers-reduced-motion: reduce){
  .entry.is-returning,.entry.is-returning *{animation:none!important;}
}

/* =========================================================================
   Arriving in a lens — the inverse of the gate's re-entry. You dive INTO the
   card (the ghost expands), so the page it delivers you to grows from slightly
   small into place rather than simply appearing.

   #main is scaled rather than #site: .site-header is position:fixed and a
   direct child of #site, and a transform on an ancestor re-anchors fixed
   descendants, which would visibly displace the header mid-animation.
   ========================================================================= */

@keyframes lens-arrive{from{opacity:0;transform:scale(.97);}
  to{opacity:1;transform:none;}}
@keyframes lens-arrive-fade{from{opacity:0;}
  to{opacity:1;}}

/* origin at the top edge: scaling from the middle pushed content upward,
   which read as movement rather than expansion */
.site.is-arriving #main{animation:lens-arrive 640ms var(--ease) backwards;transform-origin:50% 0;}
.site.is-arriving .site-header{animation:lens-arrive-fade 520ms var(--ease) 140ms backwards;}

@media (prefers-reduced-motion: reduce){
  .site.is-arriving #main,.site.is-arriving .site-header{animation:none!important;}
}

/* =========================================================================
   Reserve the scrollbar gutter permanently. The gate locks scrolling while it
   is up; without a reserved gutter, releasing that lock would shift the whole
   layout sideways by the scrollbar's width.
   ========================================================================= */
html{scrollbar-gutter:stable;}

/* =========================================================================
   The CV is an action, not a section anchor, so it sits in the header's
   controls rather than in .primary-nav, which holds only in-page links.
   ========================================================================= */
.nav-cv{display:inline-flex;align-items:center;gap:7px;height:40px;padding:0 15px;
  border:1px solid var(--line-strong);border-radius:999px;background:rgba(255,255,255,.035);
  color:var(--ink);font-size:12.5px;font-weight:700;letter-spacing:.02em;text-decoration:none;
  transition:border-color 200ms ease,background 200ms ease,color 200ms ease,transform 200ms var(--ease);}
.nav-cv .ico{width:14px;height:14px;flex:none;}
.nav-cv:hover{background:rgba(255,255,255,.075);border-color:rgba(255,255,255,.34);
  transform:translateY(-1px);}
.nav-cv:focus-visible{outline:2px solid var(--accent);outline-offset:3px;}
/* below 820px the mobile menu already carries "Download CV" */
@media (max-width: 820px){.nav-cv{display:none;}}

/* =========================================================================
   Lens switcher states — it sits beside the CV pill and looked identical at
   rest, yet had no hover and signalled "open" only by rotating its chevron.

   Hover is kept NEUTRAL rather than accent-tinted: CV is an action (accent
   hover reads as "do this"), while the switcher is a menu control, and its
   symbol is already accent-coloured. The open state is where accent belongs.

   Resting border aligned to --line-strong so the two pills match; they were
   .12 vs .22 alpha, a mismatch that read as sloppiness at these sizes.
   ========================================================================= */
.view-button{border-color:var(--line-strong);
  transition:border-color 200ms ease,background 200ms ease,transform 200ms var(--ease);}
.view-button:hover{background:rgba(255,255,255,.075);border-color:rgba(255,255,255,.34);}
/* lift only while closed — the menu is anchored to the button, so lifting it
   while open would drag the panel with it */
.view-button:not([aria-expanded="true"]):hover{transform:translateY(-1px);}
.view-button:focus-visible{outline:2px solid var(--accent);outline-offset:3px;}
/* open: unmistakably active for as long as the panel is showing */
.view-button[aria-expanded="true"]{border-color:rgba(var(--accent-rgb),.5);
  background:rgba(var(--accent-rgb),.10);}

/* =========================================================================
   Two identical-looking pills behave identically on hover. Accent marks only a
   genuinely distinct state: the lens menu while it is open.
   ========================================================================= */
[tabindex="-1"]:focus,[tabindex="-1"]:focus-visible{outline:none;}

/* writing card external-link indicator, inline after the title (articles open
   in a new tab). Inherits the title colour, so it turns accent on hover too. */
.writing-ext{display:inline-block;width:16px;height:16px;margin-left:9px;vertical-align:-2px;opacity:.85;}
.speaking-desc{margin:14px 0 0;color:var(--muted);font-size:15px;line-height:1.6;max-width:54ch;}
.speaking-actions{display:flex;flex-wrap:wrap;align-items:center;gap:14px 24px;margin-top:26px;}
.speaking-actions .text-link{margin-top:0;}
.talk-topics{margin-top:clamp(24px,3.2vw,38px);}
.talk-topics-lead{margin:0 0 13px;color:var(--ink-5);font-family:var(--mono);font-size:12px;letter-spacing:.06em;text-transform:uppercase;}
.topic-list{list-style:none;margin:0;padding:0;display:flex;flex-wrap:wrap;gap:11px;}
/* non-interactive tags: no hover (nothing to click), but substantial enough to read as content */
.topic-list li{display:inline-flex;align-items:center;gap:9px;padding:12px 17px;border:1px solid var(--line);
  border-radius:12px;background:rgba(255,255,255,.04);color:var(--ink);font-size:14px;font-weight:600;letter-spacing:-.01em;}
.topic-list li::before{content:"";flex:none;width:6px;height:6px;border-radius:50%;background:var(--accent);}

/* speaking: talk meta line + logo-backed testimonial */
.speaking-meta{margin:10px 0 0;color:var(--muted);line-height:1.5;}
.speaking-meta a{color:var(--accent);text-decoration:none;border-bottom:1px solid rgba(var(--accent-rgb),.4);}
.speaking-meta a:hover{border-bottom-color:var(--accent);}
.speaking-quote .speaking-cite,.speaking-cite{display:flex;flex-direction:column;align-items:flex-start;gap:11px;margin-top:26px;font-style:normal;}
.speaking-cite .cite-logo{display:block;height:22px;width:auto;opacity:.82;}
.speaking-cite span{font-family:var(--sans);font-size:12px;font-weight:600;letter-spacing:.06em;color:#9a97a2;}

/* =========================================================================
   Image protection. Prevent drag-out and the iOS long-press "save/open image"
   callout on every image, EXCEPT case-study body figures (#case-body img),
   which stay fully interactive. (Right-click "open image in new tab" is
   additionally blocked in JS — see the contextmenu handler.)
   Note: this is a deterrent, not real protection; anyone can still reach the
   file via devtools or the network tab.
   ========================================================================= */
img{-webkit-user-drag:none;-khtml-user-drag:none;user-select:none;-webkit-touch-callout:none;}
#case-body img{-webkit-user-drag:auto;user-select:auto;-webkit-touch-callout:default;}

/* =========================================================================
   SECTIONING SYSTEM: section backgrounds and hairline borders. The page-order
   alternation itself is declared once, in the Homepage rhythm block at the
   end.
   ========================================================================= */
:root{
    --bg: #08080c;
    --panel: #13131c;
    --ink: #f5f3ee;
    --muted: #9d9aa8;
    --ink-2: #bbb8c2;
    --ink-3: #b0adb7;
    --ink-4: #aaa7b2;
    --ink-5: #a6a3ad;
    --ink-6: #96939e;
    --line: rgba(255, 255, 255, 0.12);
    --line-strong: rgba(255, 255, 255, 0.22);
    --shell: 1120px;
    --sans: "Figtree Variable", Inter, "Helvetica Neue", Arial, sans-serif;
    --serif: "Fraunces", Georgia, serif;
    --mono: "SFMono-Regular", Consolas, "Liberation Mono", monospace;
    --radius: 26px;
    --radius-sm: 16px;
    --ease: cubic-bezier(.2, .8, .2, 1);
  --fs-display: clamp(38px, 5.6vw, 62px);
  --fs-closing: clamp(36px, 5.2vw, 58px);
  --fs-entry: clamp(30px, 4.3vw, 48px);
  --fs-h2: clamp(28px, 4vw, 44px);
  --fs-h3-lg: clamp(24px, 3.1vw, 34px);
  --fs-h3: clamp(20px, 2.4vw, 26px);
  --fs-h4: clamp(17px, 1.8vw, 20px);
  --section-pad: clamp(80px, 8.5vw, 124px);
  --radius-lg: 32px; --surface:#0e0e15; }

.work,.method,.clients{ background:var(--surface); }
.skills,.voice,.contact{ background:var(--bg); }

/* the single accent moment (glow kept; base unified to --surface) */
.assistant{ overflow:hidden;
  }

/* contact: calm close — no competing glow, no top border */
.contact{ border-top:0; }
.contact::before{ display:none!important; content:none!important; }

/* remove the lone separator above the timeline; let spacing group it into Method */
.career-timeline{
  --s0: var(--accent);
  --s1: color-mix(in srgb, var(--accent) 84%, rgba(255,255,255,.13));
  --s2: color-mix(in srgb, var(--accent) 66%, rgba(255,255,255,.13));
  --s3: color-mix(in srgb, var(--accent) 50%, rgba(255,255,255,.13));
  --s4: color-mix(in srgb, var(--accent) 36%, rgba(255,255,255,.13));
  --s5: color-mix(in srgb, var(--accent) 24%, rgba(255,255,255,.13));
  --s6: color-mix(in srgb, var(--accent) 15%, rgba(255,255,255,.13));
  --s7: rgba(255,255,255,.13); margin-top:clamp(64px,8vw,110px); padding-top:0; border-top:0; }

/* A playing <video> in a project card must not be scaled on hover — re-rastering
   a decoding frame every tick is what caused the lag. The card still responds
   via its scrim; only the zoom is dropped for video. */
video.project-photo{transition:none;}
.project:hover video.project-photo,.project:focus-visible video.project-photo{transform:none;}

/* Lens gate: reassurance note under the portals (legible sentence, not a micro-label) */
.entry-note{display:flex;justify-content:center;margin:26px 0 0;}
.entry-note span{padding:9px 16px;border:1px solid rgba(255,255,255,.12);border-radius:999px;color:#b4b1bb;text-transform:none;}
@media (max-width: 915px){.entry-note{display:flex;margin-top:20px;padding:0 18px;text-align:center;}
  .entry-note span{font-size:12.5px;}}

/* Wide layout: timeline connector segments run horizontally, centred on the row.
   Omitted roles have no segment (display:none above), leaving a clean break in the bar. */
@media (min-width: 916px){
  .career-timeline li::before{top:46px;bottom:auto;left:0;right:0;width:auto;height:1px;background:linear-gradient(90deg, var(--a), var(--b));}
  /* MID omission: dashes centred on the row, solid line resuming on both sides so
     it reaches the next bullet */
  html[data-viewpoint="creative"] .career-timeline li:not([data-lenses~="creative"]):not(:has(+ li:not([data-lenses~="creative"]))):not(:last-child)::before,
  html[data-viewpoint="visionary"] .career-timeline li:not([data-lenses~="visionary"]):not(:has(+ li:not([data-lenses~="visionary"]))):not(:last-child)::before,
  html[data-viewpoint="engineer"] .career-timeline li:not([data-lenses~="engineer"]):not(:has(+ li:not([data-lenses~="engineer"]))):not(:last-child)::before{
    -webkit-mask:
      linear-gradient(#000,#000) left center / 4px 100% no-repeat,
      linear-gradient(#000,#000) 7px center / 2px 100% no-repeat,
      linear-gradient(#000,#000) 12px center / 2px 100% no-repeat,
      linear-gradient(#000,#000) 17px center / 2px 100% no-repeat,
      linear-gradient(#000,#000) right center / 4px 100% no-repeat;
    mask:
      linear-gradient(#000,#000) left center / 4px 100% no-repeat,
      linear-gradient(#000,#000) 7px center / 2px 100% no-repeat,
      linear-gradient(#000,#000) 12px center / 2px 100% no-repeat,
      linear-gradient(#000,#000) 17px center / 2px 100% no-repeat,
      linear-gradient(#000,#000) right center / 4px 100% no-repeat;
  }
  /* TRAILING omission: dashes at the very end of the row, nothing after them */
  html[data-viewpoint="creative"] .career-timeline li:not([data-lenses~="creative"]):last-child::before,
  html[data-viewpoint="visionary"] .career-timeline li:not([data-lenses~="visionary"]):last-child::before,
  html[data-viewpoint="engineer"] .career-timeline li:not([data-lenses~="engineer"]):last-child::before{
    -webkit-mask:
      linear-gradient(#000,#000) left center / 11px 100% no-repeat,
      linear-gradient(#000,#000) 14px center / 2px 100% no-repeat,
      linear-gradient(#000,#000) 19px center / 2px 100% no-repeat,
      linear-gradient(#000,#000) 24px center / 2px 100% no-repeat;
    mask:
      linear-gradient(#000,#000) left center / 11px 100% no-repeat,
      linear-gradient(#000,#000) 14px center / 2px 100% no-repeat,
      linear-gradient(#000,#000) 19px center / 2px 100% no-repeat,
      linear-gradient(#000,#000) 24px center / 2px 100% no-repeat;
  }
}

/* Skills: quiet shortcut to the full AI-workflow case study */
.skills-more{margin-top:clamp(26px,3vw,40px);text-align:center;}
.skills-more a{display:inline-flex;align-items:center;gap:8px;color:var(--muted);font-size:15px;font-weight:650;transition:color .2s ease;}
.skills-more a:hover{color:var(--accent);}
.skills-more a b{font-style:normal;transition:transform .2s ease;}
.skills-more a:hover b{transform:translateX(3px);}

/* Career section (#method) is the timeline; "The constants" is its own
   section right after it. */
.method > .career-timeline{margin-top:0;padding-top:0;border-top:0;}
.constants{padding:var(--section-pad) 0;border-top:1px solid var(--line);}

/* "Read the article" link: colour shift on hover */
.text-link{transition:color .2s ease;}
.text-link:hover{color:var(--accent);}

/* Writing: its own section (title + intro text, then the article cards) */
.writing-section{padding:clamp(110px,12vw,175px) 0;background:var(--surface);}
.writing-section .section-intro{margin-bottom:clamp(34px,4.5vw,58px);}
.writing-more{margin-top:clamp(24px,3vw,40px);text-align:right;}
.writing-more .ico{width:14px;height:14px;}

/* Consistent hairline separator on every section boundary */
.work,.skills,.method,.clients,.voice,.writing-section,.assistant,.contact{border-top:1px solid var(--line);}
@media (max-width: 820px){
  /* Lens gate cards: vertically centre the copy against the art */
  .portal{align-items:center;align-content:center;}
  /* The artwork grows with the card so the extra height reads as a taller panel
     rather than padding around a fixed square. */
  .portal-art{align-self:stretch;}
}

/* Portal descriptions on mobile, but only where they genuinely fit.
   The copy column is ~208px wide on a 390px phone, so these ~110-character
   descriptions run to four lines. Clamped to three, each card needs ~146px, and
   three of those plus the header, note, Skip and gaps needs ~745px of viewport.
   Below that the gate would scroll, which defeats a one-screen chooser, so the
   description stays hidden and the cards keep their two-line form. */
@media (max-width: 915px) and (min-height: 750px){
  .portal-copy > span{
    display:-webkit-box;
    -webkit-box-orient:vertical;
    -webkit-line-clamp:3;
    overflow:hidden;
    margin-top:10px;
    font-size:14.5px;
    line-height:1.5;
  }
}

@media (max-width: 820px){
  .portal-copy{align-self:center;display:flex;flex-direction:column;justify-content:center;}

  /* Project cards: darker scrim on small screens */
  .project-scrim{background:linear-gradient(180deg,rgba(8,8,12,.10),rgba(8,8,12,.52) 38%,rgba(8,8,12,.97));}
  .project:hover .project-scrim{background:linear-gradient(180deg,rgba(8,8,12,.18),rgba(8,8,12,.58) 36%,rgba(8,8,12,.98));}

  /* Career timeline: one consistent vertical rhythm between fields + entries */
  .career-timeline li{padding:0 0 44px;}
  .career-timeline time{margin-bottom:7px;}
  .career-timeline h3{margin:0;}
  .career-timeline strong{margin-top:7px;}
  .career-timeline li p{margin:12px 0 0;}

  /* Speaking card: tighter side padding on the text */
  .speaking-body{padding:26px 20px 32px;}
}
.entry-head > p:first-child,
.portal-copy small,
.speaking-kicker,
.skill-tag,
.aq-lead,
.talk-topics-lead,
.praise-cards .praise-by span,
.method-list li > span { font-family: var(--sans); }
.entry-head > p:first-child,
.portal-copy small,
.speaking-kicker,
.skill-tag,
.talk-topics-lead,
.aq-lead { font-size: 13px; letter-spacing: .05em; font-weight: 650; }
.praise-cards .praise-by span { font-size: 13px; letter-spacing: .005em; }
.method-list li > span { font-size: 16px; font-weight: 650; letter-spacing: -.01em; }
.writing-overlay small,
.work-toggle .count,
.aq-lead .question-hint,
.speaking-meta { font-family: var(--sans); }
.writing-overlay small { font-size: 12.5px; letter-spacing: .02em; }
.work-toggle .count { font-size: 13px; }
.aq-lead .question-hint { font-size: 12.5px; }
.speaking-meta { text-transform: none; letter-spacing: 0; font-size: 13px; }
.assistant{ position:relative; background: var(--bg); }
.contact{ position: relative; overflow: hidden;
  background: radial-gradient(circle at 50% 116%, rgba(var(--accent-rgb),.17), transparent 44%), var(--surface); }

/* =========================================================================
   MCP: "bring it into your own AI" — a proper feature card, full width under
   the assistant stage (so expanding it never shifts the question panel).
   ========================================================================= */
.mcp{ margin-top: clamp(52px,6vw,88px); padding: clamp(28px,3.4vw,44px); border: 1px solid transparent; border-radius: var(--radius);
  animation: ai-border 7s linear infinite; }
@media (prefers-reduced-motion: reduce){
  .mcp{ animation: none; }
}
.mcp-head{ max-width: 720px; }
.mcp-badge{ display: inline-flex; align-items: center; gap: 8px; padding: 6px 13px; border: 1px solid rgba(var(--accent-rgb),.4); border-radius: 999px; color: var(--accent); font-family: var(--sans); font-size: 12.5px; font-weight: 700; letter-spacing: .07em; text-transform: uppercase; }
.mcp-badge i{ width: 6px; height: 6px; border-radius: 50%; background: var(--accent); box-shadow: 0 0 10px 1px rgba(var(--accent-rgb),.85); animation: ai-pulse 2.4s ease-in-out infinite; }
.mcp-head h3{ margin: 18px 0 0; font-size: var(--fs-h3-lg); font-weight: 540; letter-spacing: -.03em; line-height: 1.06; }
/* .mcp h3 em is themed by the per-lens highlight groups above. Declaring a face
   here as well left it on the serif regardless of the active lens. */
.mcp-head > p{ margin: 14px 0 0; max-width: 62ch; color: var(--muted); font-size: 15px; line-height: 1.7; }
.mcp-connect{ display: flex; flex-wrap: wrap; align-items: center; gap: 12px; margin-top: clamp(22px,2.6vw,30px); }
.mcp-url{ flex: 1 1 340px; min-width: 0; display: flex; align-items: baseline; gap: 12px; padding: 13px 18px; border: 1px solid var(--line); border-radius: 14px; background: rgba(8,8,12,.6); }
.mcp-url-label{ flex: none; color: #8f8c98; font-family: var(--sans); font-size: 12px; font-weight: 650; letter-spacing: .05em; text-transform: uppercase; }
.mcp-url code{ min-width: 0; color: var(--ink); font-family: var(--mono); font-size: 14px; overflow-wrap: anywhere; }
.mcp-copy{ flex: none; min-height: 48px; padding: 0 20px; border: 1px solid var(--accent); border-radius: 999px; background: var(--accent); color: var(--bg); font-family: var(--sans); font-size: 13px; font-weight: 720; cursor: pointer; transition: box-shadow .22s ease, transform .22s var(--ease); }
.mcp-copy:hover{ transform: translateY(-2px); box-shadow: 0 12px 34px rgba(var(--accent-rgb),.26); }
.mcp-copy.is-copied{ background: transparent; color: var(--accent); }
.mcp-toggle{ flex: none; display: inline-flex; align-items: center; gap: 9px; min-height: 48px; padding: 0 18px; border: 1px solid var(--line-strong); border-radius: 999px; background: transparent; color: var(--ink); font-family: var(--sans); font-size: 13px; font-weight: 700; cursor: pointer; transition: border-color .2s ease, background .2s ease; }
.mcp-toggle:hover{ border-color: var(--accent); background: rgba(var(--accent-rgb),.06); }
.mcp-toggle .chev{ transition: transform .25s var(--ease); }
.mcp-toggle[aria-expanded="true"] .chev{ transform: rotate(180deg); }
.mcp-panel{ margin-top: 22px; padding-top: 22px; border-top: 1px solid var(--line); }
.mcp-panel[hidden]{ display: none; }
.mcp-clients{ list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: repeat(3,1fr); gap: 18px; }
.mcp-clients li{ display: flex; flex-direction: column; gap: 7px; padding-left: 15px; border-left: 2px solid rgba(var(--accent-rgb),.45); }
.mcp-clients strong{ font-size: 14.5px; font-weight: 680; }
.mcp-clients span{ color: #a8a5b0; font-size: 13.5px; line-height: 1.55; }
.mcp-note{ margin: 20px 0 0; color: #8f8c98; font-size: 13px; line-height: 1.6; }
@media (max-width: 820px){ .mcp-clients{ grid-template-columns: 1fr; }
  .mcp-copy, .mcp-toggle{ flex: 1 1 auto; justify-content: center; } }

/* =========================================================================
   Reduced-motion coverage for the remaining decorative animations. The parent
   rules above stop the travelling borders and the marquee; these are the child
   pulses/loops that kept running regardless of the OS setting.
   ========================================================================= */
@media (prefers-reduced-motion: reduce){.ai-glow__dot,
.floating-ai .ai-glow__dot,
.mcp-badge i,
.profile-orbit span { animation: none !important; }
}

/* Gate: a quiet way past the lens choice. Enters on the Strategy lens, the same
   default the page uses when nobody has chosen, so the gate is an offer rather
   than a toll. */
.entry-skip{ display: inline-flex; align-items: center; justify-content: center; gap: 8px; margin: 0 auto;
  min-height: 44px; padding: 8px 14px; border: 0; background: transparent; color: #8b8894; font-family: var(--sans);
  font-size: 15px; font-weight: 600; letter-spacing: .01em; cursor: pointer; transition: color 200ms ease; }
.entry-skip:hover, .entry-skip:focus-visible{ color: var(--ink); }
.entry-skip b{ font-style: normal; transition: transform 200ms var(--ease); }
.entry-skip:hover b{ transform: translateX(3px); }
.entry-note span{ font-family: var(--sans); font-size: 12.5px; letter-spacing: .04em; font-weight: 600; }

/* =========================================================================
   Outside work: a short personal strip before Contact. Same in every lens;
   only the heading highlight follows the lens. Labels are sans on purpose
   (mono is reserved for the Systems lens).
   ========================================================================= */
/* Same rhythm as its neighbours, and the dark base so it alternates with the
   lighter Contact band below instead of merging into it. */
.outside{padding:var(--section-pad) 0;background:var(--bg);border-top:1px solid var(--line);}
.outside .section-intro{margin-bottom:clamp(34px,4.5vw,58px);}
.outside-links{margin-top:10px!important;}
.outside-grid{display:grid;grid-template-columns:minmax(0,2fr) minmax(0,1fr);gap:16px;align-items:stretch;}
.outside-card{position:relative;margin:0;overflow:hidden;border:1px solid var(--line);border-radius:var(--radius-sm);background:var(--panel);}
.outside-photo{display:block;width:100%;height:100%;object-fit:cover;}
.outside-card--club{display:grid;grid-template-columns:minmax(0,.9fr) minmax(0,1.1fr);}
.outside-media{position:relative;margin:0;min-height:360px;}
.outside-media .outside-photo{position:absolute;inset:0;object-position:50% 8%;}
/* photo is pre-cropped around the runner; keep the head in frame */
.outside-media figcaption{position:absolute;right:12px;bottom:12px;left:12px;padding:10px 14px;border:1px solid rgba(255,255,255,.14);border-radius:12px;background:rgba(11,11,16,.62);color:#e7e5ec;font-size:12.5px;font-weight:600;letter-spacing:.02em;}
.outside-body{display:flex;flex-direction:column;justify-content:center;gap:12px;padding:clamp(24px,2.8vw,36px);}
.outside-card small{color:var(--muted);font-size:12px;font-weight:650;letter-spacing:.08em;text-transform:uppercase;}
.outside-card h3{margin:0;font-size:clamp(20px,1.9vw,26px);font-weight:600;letter-spacing:-.02em;line-height:1.2;}
.outside-card p{margin:0;max-width:58ch;color:#b4b1bb;font-size:15.5px;line-height:1.65;}
.outside-links{display:flex;flex-wrap:wrap;gap:8px 22px;margin-top:6px;}
.outside-card--games{display:flex;flex-direction:column;justify-content:flex-end;gap:12px;padding:clamp(24px,2.8vw,36px);}
/* A small strategy-map motif fills the top of the Games card; it fades out
   before the text. Generic hex tiles, tinted with the lens accent. */
.outside-hexes{position:absolute;top:0;left:0;width:100%;height:46%;color:var(--accent);opacity:.75;pointer-events:none;
  -webkit-mask-image:linear-gradient(180deg,#000 10%,transparent 92%);mask-image:linear-gradient(180deg,#000 10%,transparent 92%);}
.outside-card--games > :not(.outside-hexes){position:relative;}
.outside-tags{display:flex;flex-wrap:wrap;gap:6px;margin:2px 0 0;padding:0;list-style:none;}
.outside-tags li{padding:5px 11px;border:1px solid var(--line-strong);border-radius:999px;color:#d6d4dc;font-size:12.5px;font-weight:600;line-height:1.2;}
.outside-body .outside-tags{margin:0 0 2px;}
@media (max-width: 720px){
  .outside{padding:90px 0;}
  .outside-grid{grid-template-columns:1fr;}
  .outside-card--club{grid-template-columns:1fr;}
  .outside-media{min-height:0;aspect-ratio:4/5;}
  /* Stacked, the Games card is short: give the hex motif its own room above the text. */
  .outside-card--games{padding-top:120px;}
  .outside-hexes{height:120px;}
}
.outside-links .text-link{margin-top:0;}

/* =========================================================================
   Homepage rhythm: sections alternate between the two darks, top to bottom.
   Declared once, here, in page order, so adding or moving a section means
   updating this list rather than hunting for per-section overrides.
   hero bg · work surface · skills bg · career (method) surface ·
   constants bg · clients surface · voice bg · writing surface ·
   outside bg · assistant surface · contact bg · footer surface
   ========================================================================= */
.world-hero,.skills,.constants,.voice,.outside,.contact{background-color:var(--bg);}
.work,.method,.clients,.writing-section,.assistant,.site-footer{background-color:var(--surface);}

/* Speaking card: the photo only ever matches the text column's height, so the
   card's height is set by the copy. The copy gets most of the width and a
   restrained pull quote, and the photo bleeds to the card's edges. */
@media (min-width: 821px){
  .speaking-card{grid-template-columns:.64fr 1.36fr;padding:0;}
  .speaking-body{padding:clamp(34px,3.6vw,50px);}
  /* even on all four sides */
  /* The description uses the full column, and the attribution sits on one
     line next to the logo. */
  .speaking-desc{max-width:none;}
  .speaking-quote .speaking-cite,.speaking-cite{flex-direction:row;align-items:center;gap:12px;margin-top:16px;}
  .speaking-cite .cite-logo{height:18px;}
  .speaking-actions{margin-top:4px;}
  .speaking-quote{font-size:clamp(18px,1.6vw,22px);margin:18px 0 22px;}
}

/* The Ask AI section sits on --surface, so the MCP card takes the other
   dark to stand off it (both the animated and the reduced-motion border). */
.mcp{background:
    linear-gradient(var(--bg), var(--bg)) padding-box,
    conic-gradient(from var(--ai-angle),
      rgba(var(--accent-rgb),.10) 0deg, var(--accent) 65deg, var(--accent-2) 120deg,
      var(--accent-3) 175deg, rgba(var(--accent-rgb),.10) 240deg,
      rgba(var(--accent-rgb),.10) 360deg) border-box;}
@media (prefers-reduced-motion: reduce){
  .mcp{background:linear-gradient(var(--bg),var(--bg)) padding-box,linear-gradient(160deg, rgba(var(--accent-rgb),.5), rgba(var(--accent-rgb),.12)) border-box;}
}

/* Six nav items: never let a label or the name break onto two lines; the gaps
   shrink instead. */
.primary-nav a,.nav-pill .brand b,.nav-cv{white-space:nowrap;}
.primary-nav{gap:clamp(12px,1.9vw,30px);}
