@import url("https://fonts.googleapis.com/css2?family=Host+Grotesk:wght@700..800&display=swap");
/* =========================================================
   Gallery Maker - 2026 portfolio theme
   ---------------------------------------------------------
   Thesis (from reference/design-trends-2026.md):
   calm, disciplined STRUCTURE; bold expressive TYPE and one
   scarce saturated GRADIENT carry the personality. The page
   embodies the 2026 direction it's built on.

   Three presets sit along the calm <-> expressive axis.
   Pick ONE and paste its tokens into :root below.
   The base layout stays calm regardless of preset - only the
   accent intensity and display face change.
   ========================================================= */

/* ---------- PRESET A - Editorial Calm (default; product/UX work)
   Restrained, type-forward, gradient used sparingly.
:root{
  --paper:#F4F6F4; --paper-2:#FFFFFF; --ink:#15161B; --ink-soft:#5B606B;
  --line:#E2E5E0;
  --grad-a:#4324FF; --grad-b:#FF2E93;     // accent gradient (scarce)
  --accent:#4324FF;
  --f-display:"Bricolage Grotesque",system-ui,sans-serif;
}
   ---------- PRESET B - Expressive Gradient (brand/visual work)
   Louder. Saturated gradient on hero + every hover. Y2K/dopamine energy.
:root{
  --paper:#0E0F14; --paper-2:#15161B; --ink:#F4F6F4; --ink-soft:#A2A7B3;
  --line:#262833;
  --grad-a:#4324FF; --grad-b:#FF2E93;
  --accent:#FF2E93;
  --f-display:"Bricolage Grotesque",system-ui,sans-serif;
}
   ---------- PRESET C - Mono Minimal (systems/engineering-adjacent)
   Near-monochrome, single restrained accent, mono-forward labeling.
:root{
  --paper:#FAFAF8; --paper-2:#FFFFFF; --ink:#101010; --ink-soft:#666;
  --line:#E6E6E2;
  --grad-a:#101010; --grad-b:#4324FF;
  --accent:#4324FF;
  --f-display:"Space Grotesk",system-ui,sans-serif;
}
   ========================================================= */

/* Active tokens - defaults to PRESET A (Editorial Calm). */
:root{
  /* white cards on the dot grid */
  --card-pad:32px;
  --card-gap:clamp(40px,4.5vw,64px);   /* space between modules */
  /* Four type roles, and nothing else on a page: 12, 16, 25, 56. Title and
     headline clamp down on narrow screens so a long title still fits. */
  --t-title:clamp(2.2rem,5.5vw,63px);
  --t-head:clamp(1.25rem,2.4vw,25px);
  --t-body:16px;
  /* A lead is a headline set as a sentence, so it takes the headline size
     rather than a fifth one of its own. */
  --t-lead:var(--t-head);
  --t-cap:12px;
  --card-radius:8px;

  /* dot grid background */
  --dot:#EEEEEE;            /* dot colour */
  --dot-gap:16px;           /* grid spacing */
  --dot-r:1.5px;            /* dot radius, so 3px across */
  /* diagonal rule background */
  --rule:#EEEEEE;           /* line colour */
  --rule-w:1px;             /* line thickness */
  --rule-gap:14px;          /* centre to centre, measured across the lines */
  --paper:#F4F6F4;
  --paper-2:#FFFFFF;
  --ink:#15161B;
  --ink-soft:#5B606B;
  --line:#E2E5E0;
  --grad-a:#4324FF;   /* electric indigo */
  --grad-b:#FF2E93;   /* hot magenta     */
  --accent:#4324FF;
  --mint:#00CFA1;
  --amber:#FFB422;
  --deep:#15161B;     /* always-dark surface: Stage panels + switcher bar */
  --on-deep:#FFFFFF;
  --on-grad:#FFFFFF;  /* text that sits on a gradient fill */
  --placeholder:#ECEEEA;   /* empty image-rectangle fill */
  --placeholder-2:#E6E9E4; /* its hover state */

  --maxw:1280px;
  --gutter:clamp(20px,5vw,64px);

  /* Text that sits on the page rather than on a card. Same as the body ink
     until the dark variant moves the page out from under it. */
  --chrome-ink:var(--ink);
  --chrome-soft:var(--ink-soft);

  --f-display:"Bricolage Grotesque",system-ui,sans-serif;
  --f-body:"Inter",system-ui,sans-serif;
  --f-mono:"Space Mono",ui-monospace,monospace;
}

/* =========================================================
   Visual styles (color + typography). Toggle with
   <html data-style="N"> in each page. Style 1 = base above.
   Currently shipping: 2 (Mono B/W).

   TYPE: Inter only, everywhere. Each page loads Inter 400 to 800 and
   nothing else. The font tokens the styles below declare are all
   overridden by the "Inter only" block at the end of this file.
   ========================================================= */
/* 2 - Paper: warm editorial, serif display, flat ink-red accent */
/* 2 - Mono B/W: white paper, black ink, no colour anywhere */
:root[data-style="2"]{
  /* Page and cards share one warm off-white. The cards still cover the
     diagonal rules, so each module reads as a clear field cut out of the
     lined page rather than as a white panel sitting on it. The rules are a
     step darker than the paper. */
  --paper:#F3F1EC;--paper-2:#F3F1EC;
  --rule:#E4E0D7;--dot:#E4E0D7;
  /* Navy ink rather than black. Light mode is navy on cream, dark mode is
     cream on navy, the same two colors swapped. The accent is the ink for
     now; a brand color goes here once it is settled. */
  --ink:#111C33;--ink-soft:#5C6373;--line:#D8D5CE;
  --grad-a:#111C33;--grad-b:#111C33;--accent:#111C33;
  --deep:#111C33;--on-deep:#F3F1EC;--on-grad:#F3F1EC;
  --placeholder:#EAE7E0;--placeholder-2:#E2DFD7;
  --f-display:"Scoutie Sans",system-ui,-apple-system,sans-serif;
  --f-body:"Inter",system-ui,sans-serif;
  --f-mono:"Space Mono",ui-monospace,monospace;
}
/* 3 - Mono: light systems/IDE, monospace headings, electric blue */
:root[data-style="3"]{
  --paper:#F6F7F4;--paper-2:#FFFFFF;--ink:#14181C;--ink-soft:#5A6470;--line:#DCE0DB;
  --grad-a:#2563EB;--grad-b:#06B6D4;--accent:#1D4ED8;
  --deep:#0C1116;--on-deep:#E8EEF4;--on-grad:#FFFFFF;
  --f-display:"JetBrains Mono",ui-monospace,monospace;
  --f-body:"Inter",system-ui,sans-serif;
  --f-mono:"JetBrains Mono",ui-monospace,monospace;
}
/* 4 - Brutalist: stark, black hairlines, condensed display, ultramarine */
:root[data-style="4"]{
  --paper:#F1EFEA;--paper-2:#FFFFFF;--ink:#0A0A0A;--ink-soft:#3F3F3F;--line:#0A0A0A;
  --grad-a:#1500D6;--grad-b:#3D00FF;--accent:#1500D6;
  --deep:#0A0A0A;--on-deep:#F1EFEA;--on-grad:#FFFFFF;
  --f-display:"Anton",Impact,sans-serif;
  --f-body:"Inter",system-ui,sans-serif;
  --f-mono:"Space Mono",ui-monospace,monospace;
}
/* 5 - Noir: dark luxe, refined grotesque, muted gold */
:root[data-style="5"]{
  --paper:#0E0F12;--paper-2:#16181D;--ink:#F0EFEA;--ink-soft:#9B9D99;--line:#272B31;
  --grad-a:#E8C37E;--grad-b:#C9A86A;--accent:#E8C37E;
  --deep:#000000;--on-deep:#F0EFEA;--on-grad:#1A150A;
  --placeholder:#20242A;--placeholder-2:#2A2F36;
  --f-display:"Schibsted Grotesk",system-ui,sans-serif;
  --f-body:"Inter",system-ui,sans-serif;
  --f-mono:"Space Mono",ui-monospace,monospace;
}

/* Dark, reached by the switch in the top bar, which puts data-theme="dark" on
   the <html> tag. Every surface moves, cards included, so a dark page has no
   white panels sitting in it. Sits after the data-style blocks because it is
   the same specificity, so order is what decides. */
:root[data-theme="dark"]{
  color-scheme:dark;
  --paper:#111C33;         /* the page: the navy that is the ink in light mode */
  --paper-2:#1D273D;       /* the card each module sits on, a step lighter */
  --ink:#F3F1EC;           /* the cream that is the page in light mode */
  --ink-soft:#9DA0A6;
  --line:#2E3950;
  --line-soft:#2E3950;     /* the hairline drawn around a capture */
  --grad-a:#F3F1EC;--grad-b:#F3F1EC;--accent:#F3F1EC;
  --deep:#0B1426;--on-deep:#F3F1EC;--on-grad:#111C33;
  --placeholder:#1D273D;--placeholder-2:#25304A;
  --rule:#1A2540;          /* diagonal page lines, a texture not a stripe */
  --dot:#1A2540;
}

*{box-sizing:border-box;}
html{overflow-x:hidden;}
html{-webkit-text-size-adjust:100%;}

/* Dot grid, off by default and kept as an option: put data-bg="dots" on the
   <html> tag of any page to switch it back on. --dot-gap is the tile and
   --dot-r the radius of each circle, both set with the palette above. */
:root[data-bg="dots"] body{
  background-image:radial-gradient(circle at center,
    var(--dot) var(--dot-r), transparent calc(var(--dot-r) + 0.5px));
  background-size:var(--dot-gap) var(--dot-gap);
  background-position:center top;
  background-attachment:fixed;   /* the grid stays put while the page scrolls */
}

/* Diagonal rules, the other background option: put data-bg="lines" on the
   <html> tag. 135deg points the gradient down and to the right, so the bands
   of colour run across it, top right to bottom left. --rule-gap is measured
   perpendicular to the lines, which is what the eye reads as the spacing. */
:root[data-bg="lines"] body{
  background-image:repeating-linear-gradient(135deg,
    var(--rule) 0 var(--rule-w), transparent var(--rule-w) var(--rule-gap));
  background-attachment:fixed;   /* the rules stay put while the page scrolls */
}
body{
  margin:0;
  background-color:var(--paper);
  color:var(--ink);
  font-family:var(--f-body);
  font-size:var(--t-body);
  line-height:1.6;
  -webkit-font-smoothing:antialiased;
  text-rendering:optimizeLegibility;
}
a{color:inherit;}
img{display:block;max-width:100%;}

.wrap{max-width:var(--maxw);margin:0 auto;padding:0 var(--gutter);}

/* ---------- top bar ---------- */
.topbar{
  position:sticky;top:0;z-index:50;
  background:color-mix(in srgb,var(--paper) 88%,transparent);
  backdrop-filter:saturate(140%) blur(10px);
}
.topbar__row{display:flex;align-items:center;justify-content:space-between;
  height:var(--topbar-h);padding-block:0;
  /* Full window width, inset to match the homepage strip, rather than
     stopping at the centred content column. */
  max-width:none;padding-inline:32px;}
.brand{
  display:inline-flex;align-items:center;gap:8px;
  font-family:var(--f-mono);font-weight:400;
  font-size:var(--t-cap);letter-spacing:.16em;text-transform:uppercase;
  text-decoration:none;color:var(--chrome-ink);
}
.brand b{font-weight:700;}
/* The Sixagon mark sits in front of the name. The gaps between its panels
   are drawn in the page color rather than white, so they read as gaps on the
   cream page and on the navy one alike. */
.brand__mark{flex:0 0 auto;width:46px;height:40px;margin-right:6px;color:var(--paper);}
/* In dark mode the logo's navy panels would sink into the navy page, leaving
   three coral shards. They swap to the cream ink instead, the same swap the
   rest of the site makes between the two modes. */
:root[data-theme="dark"] .brand__ink{fill:var(--ink);}
.brand__studio{display:inline-flex;align-items:center;gap:8px;}
.brand b{white-space:nowrap;}
.brand__sep{opacity:.45;font-weight:400;}
.nav{display:flex;align-items:center;gap:6px;}
/* Light and dark, as a switch rather than a label, so the control says which
   way it moves. It is a button with role="switch", and aria-checked carries
   the state that the knob's position shows. */
.switch{
  position:relative;flex:0 0 auto;margin-left:6px;
  width:44px;height:24px;padding:0;
  border:1px solid var(--chrome-soft);border-radius:999px;
  background:transparent;cursor:pointer;
  transition:border-color .2s;
}
.switch__knob{
  position:absolute;top:3px;left:3px;width:16px;height:16px;border-radius:50%;
  background:var(--chrome-soft);
  transition:transform .22s cubic-bezier(.2,.7,.2,1),background .2s;
}
.switch[aria-checked="true"] .switch__knob{
  transform:translateX(20px);background:var(--chrome-ink);
}
.switch:hover{border-color:var(--chrome-ink);}
.switch:hover .switch__knob{background:var(--chrome-ink);}
.switch:focus-visible{outline:2px solid var(--chrome-ink);outline-offset:2px;}
@media (prefers-reduced-motion:reduce){.switch__knob{transition:none;}}
.nav a{
  font-family:var(--f-mono);font-size:var(--t-cap);letter-spacing:.1em;
  text-transform:uppercase;text-decoration:none;
  padding:8px 14px;border-radius:999px;color:var(--chrome-soft);
  transition:color .2s,background .2s;
}
.nav a:hover{color:var(--chrome-ink);}
/* aria-current="page" on the page itself, "true" on the other case studies:
   Work stays marked across the whole section, not only on its first page. */
.nav a[aria-current]{
  color:var(--on-grad);
  background:linear-gradient(135deg,var(--grad-a),var(--grad-b));
}

/* ---------- hero ---------- */
.hero{padding-top:clamp(56px,9vw,120px);padding-bottom:clamp(40px,6vw,72px);}
.eyebrow{
  font-family:var(--f-mono);font-size:.74rem;letter-spacing:.22em;
  text-transform:uppercase;color:var(--ink-soft);margin:0 0 22px;
}
.hero h1{
  font-family:var(--f-display);font-weight:800;
  /* sized so the headline sits on one line at desktop widths;
     wraps naturally below ~900px rather than shrinking to unreadable */
  font-size:clamp(1.6rem,3.2vw,2.8rem);
  line-height:1.04;letter-spacing:-.022em;margin:0 0 28px;max-width:none;
}
.hero h1 .grad{
  background:linear-gradient(105deg,var(--grad-a),var(--grad-b));
  -webkit-background-clip:text;background-clip:text;color:transparent;
}
.hero p{font-size:clamp(1.05rem,1.6vw,1.3rem);color:var(--ink-soft);max-width:none;margin:0;line-height:1.5;}

/* ---------- the gallery (editorial alternating list) ---------- */
.list{padding-bottom:clamp(60px,9vw,120px);}
.entry{
  display:grid;
  grid-template-columns:minmax(0,5fr) minmax(0,7fr);
  gap:clamp(24px,4vw,56px);
  align-items:start;
  padding:clamp(34px,5vw,56px) 0;
  position:relative;
}
/* hand-drawn section rules: above each project, and above the career arc.
   The SVG is used as a mask, so the line takes its colour from --rule-ink.
   Tune weight with --rule-alpha, wobble with the path coordinates. */
:root{ --rule-ink:var(--line); --rule-alpha:.9; --rule-gap:clamp(48px,7vw,88px); --topbar-pad:16px;
  /* Header and footer are the same bar: one height, one vertical padding. */
  --bar-h:68px;
  /* The top bar's exact height. The case study prev/next bar sticks directly
     beneath it, so this has to be a fixed number rather than whatever the
     contents add up to. */
  --topbar-h:72px; }
.entry::before,
.arc::before,
.note::before{
  content:"";position:absolute;left:0;right:0;height:12px;pointer-events:none;
  background-color:var(--rule-ink);opacity:var(--rule-alpha);
  -webkit-mask-image:url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 120 12'><path d='M0,6.4 C8,4.4 16,8.2 24,6.6 C32,5 40,7.8 48,6.2 C56,4.8 64,8.4 72,6.8 C80,5.2 88,7.6 96,6 C104,4.6 112,8 120,6.4' fill='none' stroke='black' stroke-width='1.05' stroke-linecap='round'/><path d='M0,6.8 C9,5.2 17,8.4 26,7 C35,5.6 43,8 52,6.6 C61,5.4 69,8.6 78,7.2 C87,5.8 95,7.8 104,6.4 C112,5.2 116,7.6 120,6.8' fill='none' stroke='black' stroke-width='.7' stroke-linecap='round' opacity='.55'/></svg>");mask-image:url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 120 12'><path d='M0,6.4 C8,4.4 16,8.2 24,6.6 C32,5 40,7.8 48,6.2 C56,4.8 64,8.4 72,6.8 C80,5.2 88,7.6 96,6 C104,4.6 112,8 120,6.4' fill='none' stroke='black' stroke-width='1.05' stroke-linecap='round'/><path d='M0,6.8 C9,5.2 17,8.4 26,7 C35,5.6 43,8 52,6.6 C61,5.4 69,8.6 78,7.2 C87,5.8 95,7.8 104,6.4 C112,5.2 116,7.6 120,6.8' fill='none' stroke='black' stroke-width='.7' stroke-linecap='round' opacity='.55'/></svg>");
  -webkit-mask-repeat:repeat-x;mask-repeat:repeat-x;
  -webkit-mask-size:120px 12px;mask-size:120px 12px;
}
.entry::before,
.arc::before,
.note::before{top:-6px;}

/* .note is a full-bleed section with a .wrap inside, so its rule needs the
   wrap's own measurements to line up with the content above and below it. */
.note::before{
  left:0;right:0;margin-inline:auto;
  width:min( calc(var(--maxw) - var(--gutter) * 2), calc(100% - var(--gutter) * 2) );
}

.thumb{
  position:relative;aspect-ratio:3/2;border-radius:14px;overflow:hidden;
  background:var(--placeholder);border:1px solid var(--line);
}
.thumb svg{width:100%;height:100%;display:block;}
.thumb img{
  width:100%;height:100%;object-fit:cover;object-position:top center;
  transition:transform .5s cubic-bezier(.2,.7,.2,1);
}
a.entry__media:hover .thumb img{transform:scale(1.035);}
.entry__media{display:block;text-decoration:none;}

/* TOEFL leads with a phone at real mobile size rather than a landscape crop.
   The whole screenshot shows, matted by 10px inside the rule. */
.thumb--device{
  aspect-ratio:auto;overflow:visible;box-shadow:none;
  width:fit-content;margin-inline:auto;padding:10px;
  background:var(--paper);
  border:1px solid var(--line);border-radius:20px;
}
.thumb--device img{
  display:block;width:300px;max-width:100%;height:auto;
  object-fit:fill;border:0;border-radius:12px;
}
a.entry__media:hover .thumb--device img{transform:none;}
@media (max-width:760px){
  .thumb--device{padding:10px;}
  .thumb--device img{width:250px;}
}

/* screenshot fallback tile (revealed if a real shot fails to load) */
.shot-fallback{
  position:absolute;inset:0;display:none;align-items:flex-end;padding:18px;
  background:
    radial-gradient(120% 90% at 12% 12%,#2a2a2a 0%,transparent 55%),
    radial-gradient(120% 90% at 88% 88%,#1a1a1a 0%,transparent 55%),
    #111111;
}
.thumb[data-failed="true"] img{display:none;}
.thumb[data-failed="true"] .shot-fallback{display:flex;}
.shot-fallback span{
  font-family:var(--f-mono);font-size:.82rem;letter-spacing:.04em;
  color:#fff;opacity:.92;word-break:break-all;
}

.entry__body{min-width:0;}
.entry__title{
  font-family:var(--f-display);font-weight:700;
  font-size:clamp(1.45rem,2.6vw,2.05rem);
  line-height:1.08;letter-spacing:-.015em;margin:0 0 6px;
}
.entry__title a{text-decoration:none;}
.entry__title a:hover{
  background:linear-gradient(105deg,var(--grad-a),var(--grad-b));
  -webkit-background-clip:text;background-clip:text;color:transparent;
}
.entry__role{font-family:var(--f-mono);font-size:.92rem;letter-spacing:.03em;color:var(--accent);margin:0 0 16px;}

/* compact fact line - packs real detail without breaking the calm base */
.entry__meta{
  font-family:var(--f-mono);font-size:.7rem;letter-spacing:.04em;line-height:1.5;
  color:var(--ink-soft);margin:20px 0 0;
}

/* OUTCOME-FIRST: the result leads each entry. Make it visually distinct. */
.entry__outcome{
  font-family:var(--f-display);font-weight:600;
  font-size:clamp(1.02rem,1.5vw,1.18rem);line-height:1.25;
  color:var(--ink);margin:0 0 12px;max-width:54ch;
}
.entry__outcome .num{
  background:linear-gradient(105deg,var(--grad-a),var(--grad-b));
  -webkit-background-clip:text;background-clip:text;color:transparent;
}
.entry__desc{margin:0 0 10px;color:var(--ink-soft);max-width:54ch;}
/* three supporting points under each blurb */
.entry__points{margin:0;padding:0;list-style:none;max-width:54ch;color:var(--ink-soft);}
.entry__points li{position:relative;padding-left:16px;margin:0 0 4px;line-height:1.5;}
.entry__points li::before{content:'\2022';position:absolute;left:0;top:0;color:var(--ink-soft);}
.entry__link{
  font-family:var(--f-mono);font-size:.78rem;letter-spacing:.06em;
  text-decoration:none;color:var(--ink);
  display:inline-flex;align-items:center;gap:8px;
  border-bottom:1px solid var(--ink);padding-bottom:3px;
}
.entry__link .arrow{transition:transform .2s;}
.entry__link:hover .arrow{transform:translateX(4px);}

/* alternate the media side on desktop for editorial rhythm */
@media (min-width:760px){
  .entry:nth-child(even) .entry__media{order:2;}
  .entry:nth-child(even) .entry__body{order:1;}
}

/* ---------- closing note ---------- */
.note{position:relative;padding:var(--rule-gap) 0 clamp(56px,8vw,96px);}
.note h2{
  font-family:var(--f-display);font-weight:700;
  font-size:clamp(1.5rem,3vw,2.2rem);letter-spacing:-.015em;
  margin:0 0 24px;max-width:none;line-height:1.05;
}
.note p{color:var(--ink-soft);max-width:none;margin:0 0 18px;}
.note p:last-child{margin-bottom:0;}

/* ---------- footer ---------- */
.foot__row{
  display:flex;flex-wrap:wrap;gap:16px;justify-content:space-between;align-items:center;
  padding-top:28px;padding-bottom:28px;font-family:var(--f-mono);font-size:.72rem;letter-spacing:.08em;
  text-transform:uppercase;color:var(--chrome-soft);
}
.foot a{color:var(--chrome-soft);text-decoration:none;}
.foot a:hover{color:var(--chrome-ink);}

/* The live editor sometimes leaves an empty heading or paragraph behind when
   a line is split or merged. Collapse any that slip through, so a stray tag
   can never open a gap above a module's text. */
main h1:empty,main h2:empty,main h3:empty,main p:empty,main li:empty,main figcaption:empty{display:none;}

/* ---------- accent in running text ---------- */
/* Links inside copy get a heavier underline in the accent color instead of
   the browser's, so everything a reader can act on is marked the same way. */
.home-intro a,.cs-split__body a,.cs-section a{
  text-decoration:none;border-bottom:2px solid var(--accent);}
.home-intro a:hover,.cs-split__body a:hover,.cs-section a:hover{
  background:color-mix(in srgb,var(--accent) 22%,transparent);}
/* ---------- about ---------- */
/* The portrait is a photograph, so no hairline: the case study captures get
   one because they are white UI on a white card, and a photo has its own
   edge. */
.about__photo{display:block;width:100%;height:auto;}
.cs-hero__col > p:not([class]){margin:0 0 14px;max-width:60ch;}
/* Contact sits under the bio as a row of labelled columns, ruled off from
   the text above it, so the ways to reach him read as their own block. */
.contact__list{list-style:none;margin:28px 0 0;padding:20px 0 0;
  border-top:1px solid var(--line);
  display:grid;grid-template-columns:repeat(auto-fit,minmax(200px,1fr));gap:20px;}
.contact__list li{display:grid;gap:4px;align-content:start;}
/* Linked to from the homepage as about.html#contact. The top bar is sticky,
   so leave room for it or the jump lands with the list tucked underneath. */
.contact__list{scroll-margin-top:calc(var(--bar-h) + 48px);}
.contact__label{font-family:var(--f-mono);font-size:var(--t-cap);
  letter-spacing:.16em;text-transform:uppercase;color:var(--ink-soft);}
.contact__list a{text-decoration:none;border-bottom:2px solid var(--accent);
  padding-bottom:2px;justify-self:start;overflow-wrap:anywhere;}
.contact__list a:hover{border-bottom-color:var(--ink);}

/* ---------- responsive ---------- */
@media (max-width:760px){
  .entry{grid-template-columns:1fr;gap:20px;}
  .nav a{padding:7px 11px;}
  .hero h1{max-width:none;}
}

/* ---------- motion (state-aware, reduced-motion safe) ---------- */
.reveal{opacity:0;transform:translateY(18px);}
.reveal.in{opacity:1;transform:none;transition:opacity .6s ease,transform .6s cubic-bezier(.2,.7,.2,1);}
@media (prefers-reduced-motion:reduce){
  .reveal{opacity:1;transform:none;}
  .reveal.in{transition:none;}
  .thumb img{transition:none;}
}

/* visible keyboard focus */
a:focus-visible{outline:2px solid var(--accent);outline-offset:3px;border-radius:4px;}

/* ---------- clickable empty thumbs (gallery -> case study) ---------- */
a.entry__media .thumb{transition:border-color .2s;}
a.entry__media:hover .thumb{border-color:var(--accent);}

/* =========================================================
   Case study (detail) page
   ========================================================= */

/* Each block is a white card on the dot grid, 32px of white all round. */
.cs-hero,.cs-section,.cs-split{
  position:relative;
  background:var(--paper-2);
  padding:var(--card-pad);
  margin:0 0 var(--card-gap);
}
/* ---------- home, horizontal ---------- */
/* A full-bleed strip the reader scrolls sideways. Breaks out of .wrap, so the
   images run to both edges of the window rather than stopping at the gutter. */
.hstrip{
  width:100vw;margin-left:calc(50% - 50vw);
  /* No gap: the cards butt together into one white band, so the page
     background never shows through between them. Each card's own 32px of
     padding still holds the captures 64px apart. */
  display:flex;gap:0;
  overflow-x:auto;overscroll-behavior-x:contain;
  scroll-snap-type:x proximity;
  /* The inset sits on the scroll container, so scroll-padding has to match or
     a snapped item lands under the edge instead of beside it. */
  padding-left:32px;padding-right:32px;scroll-padding-left:32px;
  padding-bottom:4px;
  scrollbar-width:thin;scrollbar-color:var(--chrome-soft) transparent;
}
.hstrip:focus-visible{outline:2px solid var(--chrome-ink);outline-offset:-2px;}
.hstrip::-webkit-scrollbar{height:8px;}
.hstrip::-webkit-scrollbar-thumb{background:var(--chrome-soft);border-radius:4px;border:2px solid var(--paper);}
.hstrip::-webkit-scrollbar-track{background:transparent;}
/* Each project sits on its own white card, the same 32px of white as a case
   study block, so the page background runs between them rather than behind
   them. The width is the card, so the capture inside keeps the tile clamp. */
.hstrip__item{
  flex:0 0 auto;width:clamp(260px,30vw,400px);
  box-sizing:content-box;
  background:var(--paper-2);
  padding:var(--card-pad);
  scroll-snap-align:start;
  text-decoration:none;color:inherit;
  display:block;
}
.hstrip__label{
  font-family:var(--f-mono);font-size:var(--t-cap);letter-spacing:.12em;
  text-transform:uppercase;color:var(--ink-soft);
  margin:0 0 10px;padding:0 2px;
}
.hstrip__shot{
  /* height:auto is required: the img height attribute is a presentational
     hint, and without this it wins and aspect-ratio never computes. */
  display:block;width:100%;height:auto;aspect-ratio:3/4;
  object-fit:cover;object-position:center top;
  background:var(--paper-2);
}
.hstrip__item:focus-visible .hstrip__shot{outline:2px solid var(--ink);outline-offset:3px;}
/* The strip is the page's own scroll region, so the intro keeps the gutter. */
.home-intro--wide{margin-bottom:clamp(40px,7vw,96px);}
@media (max-width:760px){
  .hstrip__item{width:clamp(220px,72vw,300px);}
}

/* ---------- home ---------- */
/* The index reuses the case study card: .cs-section gives it the white panel
   and the hand-drawn stroke, so only the grid and the link styling live here. */
.home-grid{
  display:grid;grid-template-columns:repeat(2,minmax(0,1fr));
  gap:var(--card-gap);
  margin:0 0 var(--card-gap);
}
.home-grid > .cs-section{margin:0;}      /* the grid owns the spacing */
a.cs-section{
  display:block;text-decoration:none;color:inherit;
  transition:transform .18s ease;
}
a.cs-section:hover{transform:translateY(-2px);}
a.cs-section:focus-visible{outline:2px solid var(--ink);outline-offset:4px;}
/* The five captures differ in height, so the grid went ragged. One ratio with
   cover crops them to a single shape and the names line up across a row. */
.home-shot{
  display:block;width:100%;height:auto;
  aspect-ratio:1300/900;object-fit:cover;object-position:center top;
  border:1px solid var(--line);
}
/* Scoped to .home-grid so these beat `.cs-section p`, which the cards inherit
   and which would otherwise set the margins. */
.home-grid .home-item__name{
  font-family:var(--f-display);font-weight:700;
  font-size:1.15rem;line-height:1.25;letter-spacing:-.01em;
  margin:24px 0 6px;color:var(--ink);
}
.home-grid .home-item__meta{
  font-family:var(--f-mono);font-size:var(--t-cap);letter-spacing:.12em;
  text-transform:uppercase;color:var(--ink-soft);margin:0;
}
.home-intro p{font-size:var(--t-lead);color:var(--ink-soft);margin:0;line-height:1.6;}
/* The greeting sits flush with the top of its module, at the same 32px as
   every case study title, and close to the paragraph it introduces. */
/* Two classes, so it outranks the shared .cs-hero h1 rule further down. */
.cs-hero.home-intro h1{margin:0 0 16px;}
@media (max-width:760px){
  .home-grid{grid-template-columns:minmax(0,1fr);}
}

.cs-hero{padding-top:var(--card-pad);}
/* Titles are set in Host Grotesk (Element Type, 2024), Inter everywhere else.
   It is uniwidth and tuned for interfaces, so it sits close to Inter; the
   size does the work; at 700 the strokes leave room between letters, which 800 did not, since a uniwidth face cannot widen to fit heavier strokes. It is
   loaded by the @import at the top of this file, so no page had to change. */
.cs-hero h1{
  font-family:"Host Grotesk",var(--f-display);font-weight:700;
  font-size:var(--t-title);line-height:1.0;letter-spacing:.01em;
  margin:18px 0 24px;
}
/* Credit line under the title: role, company and dates, in the site's meta
   voice rather than the body face. */
.cs-meta{
  font-family:var(--f-mono);font-size:var(--t-cap);letter-spacing:.12em;
  text-transform:uppercase;color:var(--ink-soft);
  margin:0 0 clamp(18px,2.4vw,26px);
}
/* The credit belongs to the title, so it sits closer to it than a paragraph
   would. Halves the h1's 24px bottom margin, and only where a credit follows,
   which leaves the homepage title spacing alone. */
.cs-hero h1:has(+ .cs-meta){margin-bottom:12px;}

.cs-lead{
  font-family:var(--f-display);font-weight:600;
  font-size:var(--t-lead);line-height:1.4;letter-spacing:-.01em;
  color:var(--ink);margin:0 0 22px;
}
.cs-lead .num{
  background:linear-gradient(105deg,var(--grad-a),var(--grad-b));
  -webkit-background-clip:text;background-clip:text;color:transparent;
}
.cs-intro{font-size:var(--t-lead);color:var(--ink-soft);margin:0;line-height:1.5;}

.cs-facts{
  display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:18px 28px;
  margin:clamp(28px,4vw,40px) 0;padding:24px 0;
  border-top:1px solid var(--line);border-bottom:1px solid var(--line);
}
.cs-fact dt{font-family:var(--f-mono);font-size:11px;letter-spacing:.12em;text-transform:uppercase;color:var(--ink-soft);margin:0 0 6px;}
.cs-fact dd{margin:0;font-size:.94rem;line-height:1.35;color:var(--ink);}

.cs-figure{margin:clamp(20px,3vw,28px) 0 0;}
.cs-figure:first-child{margin-top:0;}
.cs-figure .ph{width:100%;aspect-ratio:16/10;background:var(--placeholder);border:1px solid var(--line);}
/* Captures are never rounded: the UI inside them has its own square edges. */
.cs-figure img{
  display:block;width:100%;height:auto;
}
.cs-figure figcaption{font-family:var(--f-mono);font-size:var(--t-cap);letter-spacing:.03em;color:var(--ink-soft);margin-top:12px;}

.cs-section{max-width:none;}
.cs-section > p{max-width:62ch;}
.cs-section h3{
  font-family:var(--f-display);font-weight:700;
  font-size:var(--t-head);letter-spacing:-.01em;line-height:1.1;margin:24px 0 16px;
}
/* The homepage cards reuse .cs-section, where the panel's own padding already
   sets the space above the title. */
.home-grid .cs-section h3{margin-top:0;}
.cs-section p{margin:0 0 16px;color:var(--ink);}
.cs-section p:last-child{margin-bottom:0;}
.cs-section ul{margin:0 0 16px;padding-left:1.1em;color:var(--ink);}
.cs-section li{margin:0 0 8px;}

/* The case study nav is pinned to the bottom of the window. main gets matching
   bottom padding so the last card can scroll clear of it. */
/* Navigation at the top of a case study rather than a fixed footer. It sits
   inside main.wrap, so it already matches the content block's width. */
.cs-nav{
  margin:0 0 var(--card-gap);padding:0;
  background:var(--paper-2);
  font-family:var(--f-mono);font-size:var(--t-cap);letter-spacing:.06em;text-transform:uppercase;
}
.cs-nav__row{
  max-width:none;margin:0;padding-inline:var(--card-pad);
  min-height:var(--bar-h);padding-block:var(--topbar-pad);
  display:flex;flex-wrap:wrap;gap:16px;justify-content:space-between;align-items:center;
}
main.wrap{padding-top:calc(32px + 6px);padding-bottom:96px;}
/* Plain inline text, not flex: the project name sits in its own span so a
   phone can hide it, and as a flex item it picked up the gap and printed
   "Prev : Study AI". The spaces around the arrows are in the markup. */
.cs-nav a{color:var(--ink-soft);text-decoration:none;display:inline-block;}
.cs-nav a:hover{color:var(--ink);}
/* The same bar repeated at the foot of the page, so the next project is
   reachable without scrolling back up. Desktop only: on a phone the top bar
   is sticky instead, so a second copy would be redundant. */
.cs-nav--end{margin:var(--card-gap) 0 0;}

/* =========================================================
   Career arc - 20 years compressed, screenshot-free
   ========================================================= */
.arc{padding-top:clamp(48px,7vw,88px);padding-bottom:var(--rule-gap);position:relative;}
.arc__head{
  font-family:var(--f-display);font-weight:700;
  font-size:clamp(1.5rem,3vw,2.2rem);letter-spacing:-.015em;line-height:1.05;
  margin:0 0 18px;max-width:none;
}
.arc__intro{color:var(--ink-soft);max-width:none;margin:0 0 clamp(26px,4vw,40px);font-size:clamp(1.02rem,1.3vw,1.15rem);line-height:1.5;}
.arc__list{list-style:none;margin:0;padding:0;}
.arc__item{
  display:grid;grid-template-columns:minmax(110px,150px) 1fr;gap:clamp(14px,3vw,40px);
  padding:22px 0;
}
.arc__when{font-family:var(--f-mono);font-size:.74rem;letter-spacing:.06em;color:var(--ink-soft);padding-top:5px;}
.arc__role{
  font-family:var(--f-display);font-weight:700;
  font-size:clamp(1.05rem,1.6vw,1.25rem);letter-spacing:-.01em;line-height:1.15;margin:0 0 6px;
}
.arc__note{margin:0;color:var(--ink-soft);max-width:60ch;}
.arc__foot{
  font-family:var(--f-mono);font-size:.72rem;letter-spacing:.04em;line-height:1.7;color:var(--ink-soft);
  margin:clamp(26px,4vw,40px) 0 0;
}
.arc__foot b{display:block;text-transform:uppercase;letter-spacing:.12em;font-size:11px;margin-bottom:6px;color:var(--ink);font-weight:700;}
.arc__foot + .arc__foot{margin-top:18px;}
@media (max-width:600px){.arc__item{grid-template-columns:1fr;gap:5px;}.arc__when{padding-top:0;}}

/* ---------- variant switcher (evaluation aid; not part of the portfolio) ---------- */
.vbar{
  display:flex;flex-wrap:wrap;gap:6px 14px;align-items:center;
  padding:8px clamp(20px,5vw,64px);background:var(--deep);color:var(--on-deep);
  font-family:var(--f-mono);font-size:11px;letter-spacing:.08em;text-transform:uppercase;
}
.vbar__label{opacity:.55;margin-right:4px;}
.vbar a,.vbar button{color:var(--on-deep);opacity:.6;text-decoration:none;}
.vbar button{background:none;border:0;cursor:pointer;font:inherit;padding:0;letter-spacing:inherit;text-transform:inherit;}
.vbar a:hover,.vbar button:hover{opacity:1;}
.vbar a[aria-current="page"],.vbar button[aria-pressed="true"]{opacity:1;text-decoration:underline;text-underline-offset:3px;}
.vbar--style{border-top:1px solid rgba(255,255,255,.12);}

/* Split block: media on the left, text on the right. The media column is about
   half the width of a full-bleed figure, so the image reads as half-size. */
/* The screenshot carries the section, so it takes the wider column. The text
   column still holds a comfortable measure at the widest the page gets. */
.cs-split{
  display:grid;grid-template-columns:minmax(0,1.3fr) minmax(0,1fr);
  gap:clamp(28px,4vw,56px);align-items:start;
}
.cs-split__media{margin:0;}   /* browsers put 40px side margins on <figure> */
.cs-split__body{max-width:none;}
/* 24px above every module headline, so the text block sits at the same
   offset from the top of its image in every section. Paired with the matching
   rule on .cs-section h3 below, which keeps a section without an image in
   step with the ones that have one. */
.cs-split__body h3{
  font-family:var(--f-display);font-weight:700;
  font-size:var(--t-head);letter-spacing:-.01em;line-height:1.1;margin:24px 0 16px;
}
.cs-split__body p{margin:0 0 14px;}
.cs-split__body p:last-child{margin-bottom:0;}
.cs-split figcaption{
  font-family:var(--f-mono);font-size:var(--t-cap);letter-spacing:.03em;
  color:var(--ink-soft);margin-top:12px;
}
/* Captions sit under the left edge of their image, wherever the image is. */
figcaption{text-align:left;}

/* The hero photo sits at three quarters of the content width, centred. */
/* Hero with the lead-in points: capture on the left, the points beside it.
   Each point is the focus of one section further down the page. */
.cs-hero__row{
  display:grid;grid-template-columns:minmax(0,1.87fr) minmax(0,1fr);
  gap:clamp(28px,4vw,56px);align-items:start;
}
.cs-hero__row .cs-figure{margin:0;max-width:none;}

/* A hero whose capture is a portrait tile, the same size as a homepage strip
   item, rather than a wide screenshot. Same clamp, so the two match. */
/* Text first, tile on the right. Explicit placement rather than reordering,
   because the DOM keeps the figure first for reading order. */
.cs-hero__row--tile{grid-template-columns:minmax(0,1fr) clamp(260px,30vw,400px);}
.cs-hero__row--tile > .cs-figure{grid-column:2;grid-row:1;}
.cs-hero__row--tile > .cs-hero__col{grid-column:1;grid-row:1;}
/* The About page mirrors the case study hero: the portrait leads on the left
   and the text follows. Stacking on a narrow screen is still handled by the
   --tile and --full rules in the media query below, which come later. */
.cs-hero__row--left{grid-template-columns:clamp(260px,30vw,400px) minmax(0,1fr);}
.cs-hero__row--left > .cs-figure{grid-column:1;}
.cs-hero__row--left > .cs-hero__col{grid-column:2;}
/* Not a --full row, so on a phone the portrait stacks above the name rather
   than under the contact details. The title still sits flush with the top of
   the photo on wide screens. */
.cs-hero__row--left > .cs-hero__col > h1{margin-top:0;}
/* A hero whose text column carries the title too, so the tile stands beside
   the whole block rather than beside the lead alone. The h1 loses its top
   margin so its cap height lines up with the top of the capture. */
.cs-hero__row--full > .cs-hero__col > h1{margin-top:0;}
/* The hero's right-hand column: lead, then the numbered points. */
.cs-hero__col .cs-lead{margin-top:0;}
.cs-hero__col .cs-points{margin-top:clamp(20px,2.4vw,28px);}

.shot-tile{
  /* No forced ratio: a capture 800x1066 is 0.7505, not 0.75, and cover would
     shave half a pixel off each side, taking the window's own 2px rule with
     it. The homepage strip still crops to 3/4, where uniformity matters more
     than the edge of any one shot. */
  display:block;width:100%;height:auto;
  /* The capture's own 2px rule renders below 1px at this size, so the edge
     comes from CSS and holds at any width. Matches the rule inside the
     captures themselves, which sample #EDEDED. */
  border:1px solid var(--line-soft,#EEEEEE);
  /* Same clamp as a homepage strip item, so a tile is the same size wherever
     it appears: in a hero column or inside a split. */
  max-width:clamp(260px,30vw,400px);
}
@media (max-width:760px){
  .cs-hero__row--tile{grid-template-columns:minmax(0,1fr);}
  /* One column: drop the placement so they stack in DOM order. */
  .cs-hero__row--tile > .cs-figure{grid-column:1;grid-row:1;}
  .cs-hero__row--tile > .cs-hero__col{grid-column:1;grid-row:2;}
  /* When the title lives in the text column, that column leads. Otherwise a
     stacked page would open on the capture with the title below it. */
  .cs-hero__row--full > .cs-hero__col{grid-row:1;}
  .cs-hero__row--full > .cs-figure{grid-row:2;}
}

.cs-points{list-style:none;margin:0;padding:0;counter-reset:cs-point;}
.cs-points li{
  position:relative;padding-left:24px;
  margin:0 0 clamp(16px,2vw,22px);
  font-size:var(--t-body);line-height:1.5;color:var(--ink);
}
.cs-points li:last-child{margin-bottom:0;}
/* Numbered, not bulleted: each point is the heading of a section below, so the
   number is the reference. Counter rather than the native marker, to sit it on
   the first line's baseline at a size of its own. */
.cs-points li::before{
  counter-increment:cs-point;content:counter(cs-point);
  position:absolute;left:0;top:0;
  font-family:var(--f-mono);font-size:.8em;font-weight:600;
  line-height:1.9;color:var(--ink-soft);
}
@media (max-width:760px){
  .cs-hero__row{grid-template-columns:minmax(0,1fr);gap:24px;}
}

/* Hero figure on its own, with no points beside it. */
.cs-hero > .cs-figure{max-width:75%;margin-inline:auto;}

/* Mirror: text left, image hard against the right edge. Explicit placement,
   because justify-self resolves against the grid's direction and rtl flips it. */
.cs-split--right{grid-template-columns:minmax(0,1fr) minmax(0,1.3fr);}
/* A split whose capture is a portrait tile, not a wide screenshot. The tile
   caps at 400px, so the wide media column left a band of dead space between
   the image and the text. Give that column the tile's own width instead. */
.cs-split:has(.shot-tile){grid-template-columns:clamp(260px,30vw,400px) minmax(0,1fr);}
.cs-split--right:has(.shot-tile){grid-template-columns:minmax(0,1fr) clamp(260px,30vw,400px);}
.cs-split--right .cs-split__body {grid-column:1;grid-row:1;}
.cs-split--right .cs-split__media{grid-column:2;grid-row:1;width:100%;margin-left:0;}

/* No width cap: the capture fills its grid column, so the space between a
   screenshot and its text is the grid gap and nothing else. */
.shot-still{display:block;width:100%;height:auto;
  border:1px solid var(--line-soft,#EEEEEE);}
/* Video in a split column, sized like a still. */
.shot-video{display:block;width:100%;max-width:460px;height:auto;background:var(--paper-2);}

@media (max-width:760px){
  .cs-split--right .cs-split__body,
  .cs-split--right .cs-split__media{grid-column:1;}
  .cs-split--right .cs-split__media{grid-row:1;width:100%;margin-left:0;}
  .cs-split--right .cs-split__body{grid-row:2;}
}

/* Two stacked images, the top one fading in and out over the one beneath. */
.xfade{position:relative;aspect-ratio:var(--xf-ratio,1893/1915);overflow:hidden;
  border:1px solid var(--line-soft,#EEEEEE);}

/* Landscape frames, e.g. a full browser window rather than a tall question card. */
.xfade--wide{--xf-ratio:2732/1800;}
.xfade--course{--xf-ratio:900/560;}
/* A question, then the same question answered with the tutor's help below
   it. Both frames are exported at this size, so nothing is cropped. */
.xfade--practice{--xf-ratio:1941/2143;}

/* A long page shown whole: a fixed window the reader scrolls through by hand.
   The image sits at its natural height inside, so the window scrolls it. */
.scrollframe{position:relative;overflow-y:auto;overflow-x:hidden;
  aspect-ratio:16/10;background:var(--paper-2);
  scrollbar-width:thin;scrollbar-color:var(--ink-soft,#777) transparent;}
.scrollframe img{display:block;width:100%;height:auto;}
.scrollframe:focus-visible{outline:2px solid var(--ink);outline-offset:2px;}
.scrollframe::-webkit-scrollbar{width:8px;}
.scrollframe::-webkit-scrollbar-thumb{
  background:var(--ink-soft,#777);border-radius:4px;
  border:2px solid var(--paper-2);}
.scrollframe::-webkit-scrollbar-track{background:transparent;}

.xfade img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;display:block;}
.xfade img + img{animation:cs-xfade 9s ease-in-out infinite;}

/* A capture that already carries its own frame does not want a second one
   drawn around it. Add the --bare modifier to drop the CSS rule on that
   image, whichever of the three capture classes it uses. Sits after all
   three base rules: same specificity, so it has to come last to win. */
.shot-tile--bare,.shot-still--bare,.xfade--bare{border:0;}

/* A thumbnail whose banner area plays on rollover. The video sits exactly on
   the square inside the capture: that square is 686x686 at (32,32) in a
   750x1038 image, so the insets below are those pixels as percentages and
   hold at any rendered size. */
.thumb-play{
  position:relative;display:block;
  width:100%;max-width:clamp(260px,30vw,400px);
}
.thumb-play .shot-tile{max-width:none;}
.thumb-play__video{
  position:absolute;
  left:4.2667%;top:3.0829%;width:91.4667%;height:66.0886%;
  object-fit:cover;display:block;
  opacity:0;transition:opacity .18s ease;
  pointer-events:none;   /* the still underneath keeps the cursor */
}
.thumb-play:hover .thumb-play__video{opacity:1;}
/* On the homepage the strip crops every thumbnail to 3/4 for uniformity. The
   crop lives on this wrapper instead of on the image, so the capture keeps
   its own 750x1038 ratio inside and the square the video covers stays exactly
   where it is on the case study page. */
.thumb-crop{display:block;aspect-ratio:3/4;overflow:hidden;}
.thumb-crop .thumb-play{
  max-width:none;
  /* The capture's own ratio, so the box is the right height before a lazy
     image has loaded and the video has something to be positioned against. */
  aspect-ratio:750/1038;
}
.thumb-crop .hstrip__shot{aspect-ratio:auto;}
@media (prefers-reduced-motion:reduce){
  /* The reader has asked for stillness, so the rollover does nothing. */
  .thumb-play:hover .thumb-play__video{opacity:0;}
}
@keyframes cs-xfade{
  0%,38%   {opacity:0}
  50%,88%  {opacity:1}
  100%     {opacity:0}
}
@media (prefers-reduced-motion:reduce){
  .xfade img + img{animation:none;opacity:1;}
}
@media (max-width:760px){
  .cs-split{grid-template-columns:1fr;gap:20px;}
}

/* Inter only, everywhere. Same specificity as the :root[data-style="N"] variants
   above and later in the file, so it wins whichever style is active. */
:root,:root[data-style]{
  --f-display:"Inter",system-ui,-apple-system,sans-serif;
  --f-body:"Inter",system-ui,-apple-system,sans-serif;
  --f-mono:"Inter",system-ui,-apple-system,sans-serif;
}

/* One ink for now: captions, labels and the navigation all read as black
   rather than grey. Delete this block to bring the soft grey back. */
:root,:root[data-style]{ --ink-soft:var(--ink); }

/* =========================================================
   Lightbox - a larger look at a screenshot, about half the
   window so the page stays visible behind it
   ========================================================= */
:root{ --lb-size:52vw; }
.lb{
  position:fixed;inset:0;z-index:100;   /* above the sticky header (50) and project bar (40) */
  display:flex;align-items:center;justify-content:center;
  background:rgba(17,17,17,.72);
  cursor:zoom-out;
}
.lb__frame{cursor:default;}
/* The frame takes focus only so Escape reaches the handler; it is not a
   control, so it should not draw the browser's focus ring around itself. */
.lb__frame:focus{outline:none;}
.lb[hidden]{display:none;}
/* The enlarged shot sits in the same window as on the page: 24px of white
   mat all round, with the caption underneath it inside the frame. */
.lb__frame{
  margin:0;padding:24px;
  background:var(--paper-2);
  border-radius:14px;
  box-shadow:0 24px 60px rgba(0,0,0,.35);
  max-height:88vh;overflow:auto;
}
.lb__img{
  display:block;
  max-width:var(--lb-size);max-height:70vh;
  width:auto;height:auto;
}
/* A cloned crossfade: its images are absolutely positioned, so the clone needs
   a width to size against, and the aspect ratio it carries does the rest. The
   width is the smaller of the usual enlarged size and whatever keeps a tall
   frame within 70% of the window. Capping the height instead squashed the box
   and the frames cropped to fill it, cutting off the top of the question. */
.lb__xfade{width:min(var(--lb-size), calc(70vh * var(--xf-ratio,1893/1915)));}
/* A cloned scroll frame. Same width as any other enlarged shot, so the whole
   set reads at one size, but it scrolls inside the window rather than being
   shrunk to fit. The page's own .scrollframe caps the height much shorter,
   which is why this sets its own. */
.lb__scroll{
  width:var(--lb-size);max-width:100%;
  max-height:70vh;height:70vh;
  overflow-y:auto;overflow-x:hidden;
  background:var(--paper-2);
  overscroll-behavior:contain;
}
.lb__scroll img{display:block;width:100%;height:auto;}
.lb__scroll:focus-visible{outline:2px solid var(--ink);outline-offset:2px;}
.lb__cap{
  font-family:var(--f-mono);font-size:var(--t-cap);letter-spacing:.03em;
  color:var(--ink-soft);margin-top:16px;
  max-width:var(--lb-size);
}
.lb__cap[hidden]{display:none;}
body.lb-open{overflow:hidden;}
.is-zoomable{cursor:zoom-in;}
/* Expand control in the caption. Sized in em so it matches whatever the
   caption is set in, and nudged up to sit on the text's optical centre. */
.cap-expand{
  display:inline-flex;align-items:center;justify-content:center;
  /* font:inherit first, or em resolves against the button's own 13.3px
     default rather than the caption's type. */
  font:inherit;
  width:1em;height:1em;padding:0;margin-left:.5em;
  vertical-align:-.12em;
  background:none;border:0;color:var(--ink-soft);cursor:zoom-in;
}
.cap-expand svg{width:100%;height:100%;display:block;}
.cap-expand:hover{color:var(--ink);}
.cap-expand:focus-visible{outline:2px solid var(--accent);outline-offset:2px;}
@media (max-width:760px){
  :root{ --lb-size:92vw; }
}

/* ---------- phone ---------- */
/* One block for the phone layout, last in the file so it wins over the
   desktop rules above without raising their specificity. */
@media (max-width:760px){
  /* Smaller type. The desktop lead size read as a headline at 375px wide. */
  :root{--t-body:15px;--t-head:18px;--card-pad:16px;--topbar-h:60px;}

  /* Header: the mark, Work, About and the switch, all on one row. The name
     stays in the link for screen readers but is not drawn. */
  .topbar__row{flex-wrap:nowrap;padding-inline:var(--card-pad);}
  .brand b,.brand__studio{
    position:absolute;width:1px;height:1px;overflow:hidden;
    clip:rect(0 0 0 0);white-space:nowrap;}
  .brand__mark{width:40px;height:35px;margin:0;}
  .nav{flex:0 0 auto;margin:0;}

  /* Modules run edge to edge. No gutter and no gap, so the lined page does
     not show between them; a hairline marks where one module ends. */
  main.wrap{padding-inline:0;padding-top:0;}
  .cs-hero,.cs-section,.cs-split,.cs-nav{margin-bottom:0;}
  .cs-section,.cs-split{border-top:1px solid var(--line);}

  /* Prev/next sticks under the header while the case study scrolls, in the
     same translucent paper, so the two read as one header. It keeps to one
     row, just Prev and Next without the project names, and the copy at the
     foot of the page is dropped since this one never leaves the screen. */
  .cs-nav{position:sticky;top:var(--topbar-h);z-index:40;
    background:color-mix(in srgb,var(--paper-2) 88%,transparent);
    backdrop-filter:saturate(140%) blur(10px);
    border-bottom:1px solid var(--line);}
  .cs-nav__name{display:none;}
  .cs-nav__row{flex-wrap:nowrap;min-height:0;padding-block:10px;}
  .cs-nav--end{display:none;}

  /* No enlarge on a phone (lightbox.js skips it at this width too), so the
     caption icon and the zoom cursor go with it. */
  .cap-expand{display:none;}
  .is-zoomable{cursor:auto;}

  /* One 16px margin all the way around: header, text and captures all sit
     the same distance from the edge of the screen. Captures fill that
     column, including the portrait tiles that cap at 400px on desktop. The
     selectors match the desktop rules they undo. */
  .cs-split .cs-split__media,
  .cs-split--right .cs-split__media,
  .cs-hero__row .cs-figure{width:auto;max-width:none;margin-left:0;margin-right:0;}
  .shot-tile,.shot-still,.xfade,.scrollframe,.thumb-play{width:100%;max-width:none;}

  /* The same on the homepage cards. */
  .hstrip__item > .hstrip__shot,.hstrip__item > .thumb-crop{width:100%;max-width:none;}

  /* A split holding a portrait tile sizes its media column to the tile with
     :has(), which outranks the plain .cs-split one-column rule. Match it here
     or that split stays two columns on a phone and the text shrinks to a
     word per line. */
  .cs-split:has(.shot-tile),
  .cs-split--right:has(.shot-tile){grid-template-columns:minmax(0,1fr);}

  /* Projects stack down the page instead of scrolling sideways. Each card
     takes the full width, so the capture is shown as large as it can be. */
  .hstrip{display:grid;grid-template-columns:minmax(0,1fr);
    overflow:visible;scroll-snap-type:none;padding:0;}
  .hstrip__item{width:auto;box-sizing:border-box;
    border-top:1px solid var(--line);}
  .home-intro--wide{margin-bottom:0;}
}

