/* Football pointer (cursor.js), desktop only. The skin rolls with the movement; the light and shade
   on top stay fixed, so it reads as a 3D ball rolling under a lamp. */
html.has-ball, html.has-ball * { cursor:none !important; }
.ball { position:fixed; left:0; top:0; width:30px; height:30px; margin:-15px 0 0 -15px; border-radius:50%; pointer-events:none; z-index:100; opacity:0; will-change:transform;
  filter:drop-shadow(0 6px 8px rgba(0,0,0,.28)) drop-shadow(0 1px 1px rgba(0,0,0,.2)); transition:opacity .2s; }
.ball.on { opacity:1; }
.ball .skin { position:absolute; inset:0; border-radius:50%; background:url('assets/cursor-ball.png') center/cover; will-change:transform; }
.ball::after { content:""; position:absolute; inset:0; border-radius:50%;
  background:radial-gradient(circle at 34% 28%, rgba(255,255,255,.55), rgba(255,255,255,0) 38%), radial-gradient(circle at 50% 50%, rgba(0,0,0,0) 58%, rgba(0,0,0,.32) 100%); }
/* Over something clickable the ball grows a little and turns see-through, so the text under it stays readable. */
.ball.hot .skin, .ball.hot::after { opacity:.7; }
/* Back to top (totop.js): a glass circle, fixed bottom right, shown after about a screen of scrolling. */
.totop { all:unset; box-sizing:border-box; position:fixed; right:max(20px, env(safe-area-inset-right)); bottom:max(20px, env(safe-area-inset-bottom)); z-index:45;
  width:44px; height:44px; border-radius:50%; display:grid; place-items:center; cursor:pointer; color:var(--ink); font-size:18px;
  background:var(--glass); -webkit-backdrop-filter:blur(20px) saturate(1.6); backdrop-filter:blur(20px) saturate(1.6);
  box-shadow:inset 0 1px 0 var(--glass-hi), inset 0 0 0 1px var(--glass-edge), 0 8px 24px -12px var(--glass-shadow);
  opacity:0; transform:translateY(10px) scale(.9); pointer-events:none; transition:opacity .3s, transform .35s cubic-bezier(.16,1,.3,1); }
.totop.show { opacity:1; transform:none; pointer-events:auto; }
@media (hover:hover) and (pointer:fine) { .totop:hover i { transform:translateY(-2px); } }
.totop i { transition:transform .25s cubic-bezier(.16,1,.3,1); }
.totop:active { transform:scale(.94); }
.totop:focus-visible { outline:2px solid var(--ink); outline-offset:3px; }
/* Phones: sit above the bottom bar instead of beside it. */
@media (max-width:720px) { .totop { bottom:calc(80px + env(safe-area-inset-bottom, 0px)); right:16px; } }

:is(.gnav, .pop, .end, .totop) i.ph::before { content:"" !important; }
:is(.gnav, .pop, .end, .totop) i.ph { display:inline-block; width:1.15em; height:1.15em; background:currentColor; -webkit-mask:var(--ico) center/contain no-repeat; mask:var(--ico) center/contain no-repeat; }
:is(.gnav, .pop, .end, .totop) i.ph-squares-four { --ico:url("data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20viewBox%3D'0%200%2024%2024'%20fill%3D'none'%20stroke%3D'black'%20stroke-linecap%3D'round'%20stroke-linejoin%3D'round'%3E%3Cpath%20d%3D'M4.6%204.9L10.2%204.3L10.6%2010.1L4.4%2010.5L4.2%204.1%20M13.7%204.2L19.8%204.6L19.5%2010.4L13.9%2010.2L13.6%203.8%20M4.1%2013.9L10.3%2013.6L10.1%2019.7L4.5%2019.9L4.2%2013.4%20M14%2013.5L19.6%2014.1L19.9%2019.6L13.8%2019.8L13.9%2013.1'%20stroke-width%3D'2'%2F%3E%3Cpath%20d%3D'M4.6%204.9L10.2%204.3L10.6%2010.1L4.4%2010.5L4.2%204.1%20M13.7%204.2L19.8%204.6L19.5%2010.4L13.9%2010.2L13.6%203.8%20M4.1%2013.9L10.3%2013.6L10.1%2019.7L4.5%2019.9L4.2%2013.4%20M14%2013.5L19.6%2014.1L19.9%2019.6L13.8%2019.8L13.9%2013.1'%20stroke-width%3D'1'%20stroke-opacity%3D'.5'%20transform%3D'translate(.5%20-.4)%20rotate(1.4%2012%2012)'%2F%3E%3C%2Fsvg%3E"); }
:is(.gnav, .pop, .end, .totop) i.ph-user { --ico:url("data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20viewBox%3D'0%200%2024%2024'%20fill%3D'none'%20stroke%3D'black'%20stroke-linecap%3D'round'%20stroke-linejoin%3D'round'%3E%3Cpath%20d%3D'M12.6%203.9C9.9%203.4%208.2%205.6%208.6%207.9C9%2010.3%2011.6%2011.4%2013.6%2010.4C15.6%209.4%2015.9%206.4%2014.3%204.8C13.6%204.1%2012.6%203.8%2011.6%204.1%20M4.3%2020.4C5%2016.6%208%2014.6%2011.8%2014.7C15.8%2014.8%2018.9%2017%2019.6%2020.6'%20stroke-width%3D'2'%2F%3E%3Cpath%20d%3D'M12.6%203.9C9.9%203.4%208.2%205.6%208.6%207.9C9%2010.3%2011.6%2011.4%2013.6%2010.4C15.6%209.4%2015.9%206.4%2014.3%204.8C13.6%204.1%2012.6%203.8%2011.6%204.1%20M4.3%2020.4C5%2016.6%208%2014.6%2011.8%2014.7C15.8%2014.8%2018.9%2017%2019.6%2020.6'%20stroke-width%3D'1'%20stroke-opacity%3D'.5'%20transform%3D'translate(.5%20-.4)%20rotate(1.4%2012%2012)'%2F%3E%3C%2Fsvg%3E"); }
:is(.gnav, .pop, .end, .totop) i.ph-at { --ico:url("data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20viewBox%3D'0%200%2024%2024'%20fill%3D'none'%20stroke%3D'black'%20stroke-linecap%3D'round'%20stroke-linejoin%3D'round'%3E%3Cpath%20d%3D'M15.2%2012.2C15.3%2014%2013.8%2015.6%2012%2015.5C10.1%2015.4%208.7%2013.9%208.8%2012C8.9%2010.2%2010.4%208.7%2012.2%208.8C13.6%208.9%2014.8%209.8%2015.1%2011.2%20M15.4%208.9C15.2%2011.5%2015.3%2013.8%2016.1%2015C17.4%2016.7%2020.2%2015.6%2020.3%2012.1C20.5%207%2016.6%203.5%2011.9%203.6C7.1%203.7%203.5%207.6%203.7%2012.3C3.9%2017.1%207.8%2020.5%2012.6%2020.3C14.1%2020.2%2015.4%2019.8%2016.6%2019.1'%20stroke-width%3D'2'%2F%3E%3Cpath%20d%3D'M15.2%2012.2C15.3%2014%2013.8%2015.6%2012%2015.5C10.1%2015.4%208.7%2013.9%208.8%2012C8.9%2010.2%2010.4%208.7%2012.2%208.8C13.6%208.9%2014.8%209.8%2015.1%2011.2%20M15.4%208.9C15.2%2011.5%2015.3%2013.8%2016.1%2015C17.4%2016.7%2020.2%2015.6%2020.3%2012.1C20.5%207%2016.6%203.5%2011.9%203.6C7.1%203.7%203.5%207.6%203.7%2012.3C3.9%2017.1%207.8%2020.5%2012.6%2020.3C14.1%2020.2%2015.4%2019.8%2016.6%2019.1'%20stroke-width%3D'1'%20stroke-opacity%3D'.5'%20transform%3D'translate(.5%20-.4)%20rotate(1.4%2012%2012)'%2F%3E%3C%2Fsvg%3E"); }
:is(.gnav, .pop, .end, .totop) i.ph-linkedin-logo { --ico:url("data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20viewBox%3D'0%200%2024%2024'%20fill%3D'none'%20stroke%3D'black'%20stroke-linecap%3D'round'%20stroke-linejoin%3D'round'%3E%3Cpath%20d%3D'M4.2%204.6L19.6%203.9L20.2%2019.8L4.1%2020.3L3.8%204.1%20M8.1%2010.9L8%2017.1%20M8%207.3L8.3%207.7%20M11.6%2017.2L11.5%2010.8%20M11.6%2013.6C12.2%2011.6%2013.4%2010.7%2014.7%2010.8C16.3%2010.9%2016.9%2012%2016.9%2013.8L17%2017.3'%20stroke-width%3D'2'%2F%3E%3Cpath%20d%3D'M4.2%204.6L19.6%203.9L20.2%2019.8L4.1%2020.3L3.8%204.1%20M8.1%2010.9L8%2017.1%20M8%207.3L8.3%207.7%20M11.6%2017.2L11.5%2010.8%20M11.6%2013.6C12.2%2011.6%2013.4%2010.7%2014.7%2010.8C16.3%2010.9%2016.9%2012%2016.9%2013.8L17%2017.3'%20stroke-width%3D'1'%20stroke-opacity%3D'.5'%20transform%3D'translate(.5%20-.4)%20rotate(1.4%2012%2012)'%2F%3E%3C%2Fsvg%3E"); }
:is(.gnav, .pop, .end, .totop) i.ph-envelope-simple { --ico:url("data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20viewBox%3D'0%200%2024%2024'%20fill%3D'none'%20stroke%3D'black'%20stroke-linecap%3D'round'%20stroke-linejoin%3D'round'%3E%3Cpath%20d%3D'M3.6%206.4L20.3%205.8L20.6%2018.2L3.9%2018.5L3.4%206%20M3.9%206.6C6.8%209%209.4%2011.2%2011.8%2013C14.6%2010.9%2017.5%208.6%2020.4%206.2'%20stroke-width%3D'2'%2F%3E%3Cpath%20d%3D'M3.6%206.4L20.3%205.8L20.6%2018.2L3.9%2018.5L3.4%206%20M3.9%206.6C6.8%209%209.4%2011.2%2011.8%2013C14.6%2010.9%2017.5%208.6%2020.4%206.2'%20stroke-width%3D'1'%20stroke-opacity%3D'.5'%20transform%3D'translate(.5%20-.4)%20rotate(1.4%2012%2012)'%2F%3E%3C%2Fsvg%3E"); }
:is(.gnav, .pop, .end, .totop) i.ph-calendar-blank { --ico:url("data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20viewBox%3D'0%200%2024%2024'%20fill%3D'none'%20stroke%3D'black'%20stroke-linecap%3D'round'%20stroke-linejoin%3D'round'%3E%3Cpath%20d%3D'M4%205.6L20.1%205.1L20.4%2020.2L3.9%2020.6L3.7%205.2%20M3.9%2010C9%209.6%2014.6%209.9%2020.3%209.5%20M8.3%203.2L8.1%207.4%20M15.9%203.4L16.1%207.1%20M7.6%2014.2L7.9%2014.4%20M11.9%2014L12.1%2014.3%20M16.2%2014.1L16.4%2014.4'%20stroke-width%3D'2'%2F%3E%3Cpath%20d%3D'M4%205.6L20.1%205.1L20.4%2020.2L3.9%2020.6L3.7%205.2%20M3.9%2010C9%209.6%2014.6%209.9%2020.3%209.5%20M8.3%203.2L8.1%207.4%20M15.9%203.4L16.1%207.1%20M7.6%2014.2L7.9%2014.4%20M11.9%2014L12.1%2014.3%20M16.2%2014.1L16.4%2014.4'%20stroke-width%3D'1'%20stroke-opacity%3D'.5'%20transform%3D'translate(.5%20-.4)%20rotate(1.4%2012%2012)'%2F%3E%3C%2Fsvg%3E"); }
:is(.gnav, .pop, .end, .totop) i.ph-sun { --ico:url("data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20viewBox%3D'0%200%2024%2024'%20fill%3D'none'%20stroke%3D'black'%20stroke-linecap%3D'round'%20stroke-linejoin%3D'round'%3E%3Cpath%20d%3D'M15.7%2011.6C15.9%2013.9%2014.1%2015.8%2011.9%2015.8C9.7%2015.8%208.1%2014%208.2%2011.9C8.3%209.8%2010%208.1%2012.1%208.2C13.4%208.3%2014.6%209%2015.2%2010.1%20M12%202.6L12.2%205%20M11.8%2019.1L12%2021.4%20M2.8%2011.8L5.1%2012%20M18.9%2012.1L21.3%2011.9%20M5.3%205.2L6.9%206.9%20M17.2%2017.3L18.8%2018.7%20M5.4%2018.6L7.1%2017%20M17%207L18.7%205.3'%20stroke-width%3D'2'%2F%3E%3Cpath%20d%3D'M15.7%2011.6C15.9%2013.9%2014.1%2015.8%2011.9%2015.8C9.7%2015.8%208.1%2014%208.2%2011.9C8.3%209.8%2010%208.1%2012.1%208.2C13.4%208.3%2014.6%209%2015.2%2010.1%20M12%202.6L12.2%205%20M11.8%2019.1L12%2021.4%20M2.8%2011.8L5.1%2012%20M18.9%2012.1L21.3%2011.9%20M5.3%205.2L6.9%206.9%20M17.2%2017.3L18.8%2018.7%20M5.4%2018.6L7.1%2017%20M17%207L18.7%205.3'%20stroke-width%3D'1'%20stroke-opacity%3D'.5'%20transform%3D'translate(.5%20-.4)%20rotate(1.4%2012%2012)'%2F%3E%3C%2Fsvg%3E"); }
:is(.gnav, .pop, .end, .totop) i.ph-moon { --ico:url("data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20viewBox%3D'0%200%2024%2024'%20fill%3D'none'%20stroke%3D'black'%20stroke-linecap%3D'round'%20stroke-linejoin%3D'round'%3E%3Cpath%20d%3D'M19.9%2014.2C18.6%2015%2017%2015.4%2015.4%2015.3C11.2%2015.1%208.2%2011.6%208.6%207.3C8.7%205.9%209.2%204.6%2010%203.6C6.2%204.6%203.6%208.2%203.9%2012.4C4.2%2017%208.2%2020.5%2012.8%2020.2C16%2020%2018.7%2017.6%2020.3%2013.7'%20stroke-width%3D'2'%2F%3E%3Cpath%20d%3D'M19.9%2014.2C18.6%2015%2017%2015.4%2015.4%2015.3C11.2%2015.1%208.2%2011.6%208.6%207.3C8.7%205.9%209.2%204.6%2010%203.6C6.2%204.6%203.6%208.2%203.9%2012.4C4.2%2017%208.2%2020.5%2012.8%2020.2C16%2020%2018.7%2017.6%2020.3%2013.7'%20stroke-width%3D'1'%20stroke-opacity%3D'.5'%20transform%3D'translate(.5%20-.4)%20rotate(1.4%2012%2012)'%2F%3E%3C%2Fsvg%3E"); }
:is(.gnav, .pop, .end, .totop) i.ph-check { --ico:url("data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20viewBox%3D'0%200%2024%2024'%20fill%3D'none'%20stroke%3D'black'%20stroke-linecap%3D'round'%20stroke-linejoin%3D'round'%3E%3Cpath%20d%3D'M4.4%2012.9C6.2%2014.6%207.9%2016.4%209.6%2018.4C12.6%2013.4%2015.9%209%2019.8%205.3'%20stroke-width%3D'2'%2F%3E%3Cpath%20d%3D'M4.4%2012.9C6.2%2014.6%207.9%2016.4%209.6%2018.4C12.6%2013.4%2015.9%209%2019.8%205.3'%20stroke-width%3D'1'%20stroke-opacity%3D'.5'%20transform%3D'translate(.5%20-.4)%20rotate(1.4%2012%2012)'%2F%3E%3C%2Fsvg%3E"); }
:is(.gnav, .pop, .end, .totop) i.ph-arrow-up { --ico:url("data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20viewBox%3D'0%200%2024%2024'%20fill%3D'none'%20stroke%3D'black'%20stroke-linecap%3D'round'%20stroke-linejoin%3D'round'%3E%3Cpath%20d%3D'M12.3%2020.3C12%2015%2011.9%209.6%2012.1%204.4%20M6.2%2010.3C8.2%208.2%2010.2%206.3%2012.1%204.4C14%206.2%2015.9%208.3%2017.8%2010.6'%20stroke-width%3D'2'%2F%3E%3Cpath%20d%3D'M12.3%2020.3C12%2015%2011.9%209.6%2012.1%204.4%20M6.2%2010.3C8.2%208.2%2010.2%206.3%2012.1%204.4C14%206.2%2015.9%208.3%2017.8%2010.6'%20stroke-width%3D'1'%20stroke-opacity%3D'.5'%20transform%3D'translate(.5%20-.4)%20rotate(1.4%2012%2012)'%2F%3E%3C%2Fsvg%3E"); }
html.lenis { scroll-behavior:auto; }
/* Specimen prototype. Type matched to curated.supply (Basier is licensed, so Geist at its metrics): 40/46 -0.03em, 24/26.4 -0.018em, 14/19.6 body. */
:root { --bg:#F0F0EF; --card:#FFFFFF; --ink:#141414; --mute:#6F6F6E; --line:#E4E4E3; --btn:#141414; --btn-ink:#FFFFFF; --sw:#FFFFFF; --sw-hover:#E4E4E3; --grain:.07; --glass:rgba(255,255,255,.52); --glass-edge:rgba(20,20,20,.07); --glass-hi:rgba(255,255,255,.9); --glass-shadow:rgba(20,20,20,.18); --glass-strong:rgba(255,255,255,.82); --panel:rgba(255,255,255,.5); --panel-strong:rgba(255,255,255,.7); --panel-sheen:rgba(255,255,255,.65); --panel-rim:rgba(255,255,255,.95); --panel-edge:rgba(20,20,20,.06); --panel-shadow:rgba(20,20,20,.12); --blob-bg:linear-gradient(180deg, rgba(255,255,255,.92), rgba(255,255,255,.6)); --blob-shadow:inset 0 1px 0 #fff, inset 0 0 0 1px rgba(20,20,20,.09), 0 1px 2px rgba(20,20,20,.08), 0 8px 18px -8px rgba(20,20,20,.32); --blob-sheen:rgba(255,255,255,.7); --sand-hot:#C2964F; --sand-done:#6B5536; --sand-mute:#A08B6C; --field-a:#F0F0EF; --field-b:#EEE4CF; --field-c:#E2C994; --field-d:#F0F0EF; --trail:rgba(194,150,79,.2); color-scheme:light; }
@media (prefers-color-scheme: dark) { :root:not([data-theme="light"]) { --bg:#111111; --card:#181818; --ink:#EEEEEC; --mute:#959593; --line:#262626; --btn:#EEEEEC; --btn-ink:#0E0E0E; --sw:#181818; --sw-hover:#262626; --grain:.09; --glass:rgba(30,30,30,.5); --glass-edge:rgba(255,255,255,.09); --glass-hi:rgba(255,255,255,.16); --glass-shadow:rgba(0,0,0,.6); --glass-strong:rgba(30,30,30,.84); --panel:rgba(26,26,26,.38); --panel-strong:rgba(34,34,34,.55); --panel-sheen:rgba(255,255,255,.06); --panel-rim:rgba(255,255,255,.14); --panel-edge:rgba(255,255,255,.07); --panel-shadow:rgba(0,0,0,.55); --blob-bg:linear-gradient(180deg, rgba(255,255,255,.2), rgba(255,255,255,.07)); --blob-shadow:inset 0 1px 0 rgba(255,255,255,.28), inset 0 0 0 1px rgba(255,255,255,.09), 0 6px 16px -8px rgba(0,0,0,.6); --blob-sheen:rgba(255,255,255,.14); --sand-hot:#DDB46C; --sand-done:#F1E6CE; --sand-mute:#A99779; --field-a:#111111; --field-b:#1E1910; --field-c:#4C3B20; --field-d:#111111; --trail:rgba(221,180,108,.16); color-scheme:dark; } }
:root[data-theme="dark"] { --bg:#111111; --card:#181818; --ink:#EEEEEC; --mute:#959593; --line:#262626; --btn:#EEEEEC; --btn-ink:#0E0E0E; --sw:#181818; --sw-hover:#262626; --grain:.09; --glass:rgba(30,30,30,.5); --glass-edge:rgba(255,255,255,.09); --glass-hi:rgba(255,255,255,.16); --glass-shadow:rgba(0,0,0,.6); --glass-strong:rgba(30,30,30,.84); --panel:rgba(26,26,26,.38); --panel-strong:rgba(34,34,34,.55); --panel-sheen:rgba(255,255,255,.06); --panel-rim:rgba(255,255,255,.14); --panel-edge:rgba(255,255,255,.07); --panel-shadow:rgba(0,0,0,.55); --blob-bg:linear-gradient(180deg, rgba(255,255,255,.2), rgba(255,255,255,.07)); --blob-shadow:inset 0 1px 0 rgba(255,255,255,.28), inset 0 0 0 1px rgba(255,255,255,.09), 0 6px 16px -8px rgba(0,0,0,.6); --blob-sheen:rgba(255,255,255,.14); --sand-hot:#DDB46C; --sand-done:#F1E6CE; --sand-mute:#A99779; --field-a:#111111; --field-b:#1E1910; --field-c:#4C3B20; --field-d:#111111; --trail:rgba(221,180,108,.16); color-scheme:dark; }
:root { --mono:"Geist Mono", ui-monospace, monospace; --ease:cubic-bezier(.16,1,.3,1); }
* { box-sizing:border-box; margin:0; padding:0; }
body { background:var(--bg); color:var(--ink); font:14px/19.6px "Geist", system-ui, sans-serif; -webkit-font-smoothing:antialiased; transition:background .3s, color .3s; }
body::after { content:""; position:fixed; inset:-50%; z-index:50; pointer-events:none; opacity:var(--grain);
    background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='220' height='220'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='3' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E"); }
a { color:inherit; text-decoration:none; }
.sw { display:inline-grid; place-items:center; min-width:30px; height:30px; padding:0 8px; border-radius:8px; border:0; background:var(--sw); color:inherit; font:inherit; font-size:12px; letter-spacing:0.02em; cursor:pointer; transition:background .2s, transform .2s; }
@media (hover:hover) and (pointer:fine) { .sw:hover { background:var(--sw-hover); } }
.sw:active { transform:scale(.96); }
.sw i { font-size:15px; grid-area:1 / 1; transition:opacity .2s, transform .3s; }
/* Show the moon in light mode (go dark) and the sun in dark mode (go light). */
.sw .ph-sun, :root[data-theme="dark"] .sw .ph-moon { opacity:0; transform:rotate(-40deg) scale(.6); }
:root[data-theme="dark"] .sw .ph-sun { opacity:1; transform:none; }
.sw.lang { font-weight:500; }
.sw { height:38px; min-width:38px; border-radius:999px; font-size:13px; }
.wrap { max-width:1440px; margin:0 auto; padding:0 36px; }
/* 78px tall so the name and the switches share a centre line (39px) with the fixed bar (top 14px + 50px tall). */
header { display:grid; grid-template-columns:1fr auto 1fr; align-items:center; height:78px; }
/* Name in Helvetica Neue (system font on Apple devices; Helvetica / Arial elsewhere), tracked -3%. */
header .name { font-family:"Helvetica Neue", Helvetica, Arial, sans-serif; font-size:24px; line-height:28px; font-weight:500; letter-spacing:-0.03em; justify-self:start; }
@media (max-width:720px) { header .name { font-size:20px; line-height:24px; } }
.end { justify-self:end; display:flex; gap:6px; }
.round { width:38px; height:38px; border-radius:50%; background:var(--card); display:grid; place-items:center; font-size:17px; transition:transform .2s var(--ease); }
.round:active { transform:scale(.96); }

/* Glass top bar. Web approximation of a liquid-glass material: backdrop blur and saturation,
   a hairline edge, an inner top highlight and a soft specular sheen. Solid fill where blur is unsupported or transparency is reduced. */
.gnav { position:fixed; top:14px; left:50%; transform:translateX(-50%); z-index:60; display:flex; gap:2px; padding:5px; border-radius:999px;
  background:var(--glass); -webkit-backdrop-filter:blur(16px) saturate(1.8); backdrop-filter:blur(16px) saturate(1.8);
  box-shadow:inset 0 1px 0 var(--glass-hi), inset 0 0 0 1px var(--glass-edge), 0 12px 32px -14px var(--glass-shadow); }
.gnav::before { content:""; position:absolute; inset:0; border-radius:inherit; pointer-events:none;
  background:linear-gradient(180deg, var(--glass-hi), transparent 55%); opacity:.35; mix-blend-mode:overlay; }
@supports not ((backdrop-filter:blur(1px)) or (-webkit-backdrop-filter:blur(1px))) { .gnav, .pop { background:var(--card); } }
@media (prefers-reduced-transparency: reduce) { .gnav, .pop { background:var(--card); -webkit-backdrop-filter:none; backdrop-filter:none; } }
/* Language, theme and back-to-top buttons wear the same glass as the bar, so the floating controls read as one set. */
.end .sw { position:relative; }
.end .sw, .totop { height:44px; min-width:44px; border-radius:999px;
  background:var(--glass); -webkit-backdrop-filter:blur(16px) saturate(1.8); backdrop-filter:blur(16px) saturate(1.8);
  box-shadow:inset 0 1px 0 var(--glass-hi), inset 0 0 0 1px var(--glass-edge), 0 12px 32px -14px var(--glass-shadow); }
.end .sw::before, .totop::before { content:""; position:absolute; inset:0; border-radius:inherit; pointer-events:none;
  background:linear-gradient(180deg, var(--glass-hi), transparent 55%); opacity:.35; mix-blend-mode:overlay; }
@media (hover:hover) and (pointer:fine) { .end .sw:hover, .totop:hover { background:var(--glass-strong); } }
.totop { width:44px; }
@supports not ((backdrop-filter:blur(1px)) or (-webkit-backdrop-filter:blur(1px))) { .end .sw, .totop { background:var(--card); } }
@media (prefers-reduced-transparency: reduce) { .end .sw, .totop { background:var(--card); -webkit-backdrop-filter:none; backdrop-filter:none; } }
/* Phones: language and theme buttons sized to sit with the name (42px). */
@media (max-width:720px) { .end .sw { height:42px; min-width:42px; font-size:14px; } .end .sw i { font-size:18px; } .end { gap:6px; } }
.gi { all:unset; cursor:pointer; position:relative; z-index:1; display:flex; align-items:center; gap:8px; height:40px; padding:0 18px; border-radius:999px; font-size:15px; font-weight:500; letter-spacing:-0.025em; color:var(--ink); transition:color .25s; white-space:nowrap; }
.gi i { font-size:17px; }
.gi.lit { color:var(--ink); }
.gi:focus-visible { outline:2px solid var(--ink); outline-offset:2px; }
.blob { position:absolute; top:5px; left:0; height:40px; width:0; border-radius:999px; opacity:0; transition:opacity .2s; will-change:transform, width;
  /* Frosted liquid: a translucent pane that blurs what is behind it, lit softly from the top edge. */
  background:var(--blob-bg); box-shadow:var(--blob-shadow);
  -webkit-backdrop-filter:blur(14px) saturate(1.6); backdrop-filter:blur(14px) saturate(1.6); }
.blob::after { content:""; position:absolute; inset:2px 12% auto; height:42%; border-radius:999px; background:linear-gradient(180deg, var(--blob-sheen), transparent); pointer-events:none; }
.pop { position:absolute; top:calc(100% + 8px); right:0; min-width:200px; padding:6px; border-radius:18px; display:grid;
  background:var(--glass-strong); -webkit-backdrop-filter:blur(24px) saturate(1.8); backdrop-filter:blur(24px) saturate(1.8);
  box-shadow:inset 0 1px 0 var(--glass-hi), inset 0 0 0 1px var(--glass-edge), 0 16px 40px -16px var(--glass-shadow); animation:pop .28s var(--ease); transform-origin:top right; }
.pop[hidden] { display:none; }
.pop a { display:flex; align-items:center; gap:10px; padding:10px 12px; border-radius:12px; font-size:15px; }
.pop a:focus-visible { background:var(--sw-hover); outline:none; }
@media (hover:hover) and (pointer:fine) { .pop a:hover { background:var(--sw-hover); outline:none; } }
.pop i { font-size:17px; color:var(--mute); }
@keyframes pop { from { opacity:0; transform:translateY(-4px) scale(.97); } }
/* Spring hover highlight and its sand trail (spring-nav.js), on the Socials menu and the About experience list. */
.hl { position:absolute; left:0; top:0; z-index:0; border-radius:12px; background:var(--sw-hover); pointer-events:none; opacity:0; will-change:transform; }
.hl.ghost { background:var(--trail); }
.has-hl > a, .has-hl > div { position:relative; z-index:1; }
.pop.has-hl a:focus-visible { background:transparent; }
@media (hover:hover) and (pointer:fine) { .pop.has-hl a:hover { background:transparent; } }
.pop.has-hl a.hot i { color:var(--ink); }

.head { display:flex; justify-content:space-between; align-items:flex-end; padding:48px 0 28px; gap:24px; }
.crumb { color:var(--mute); font-size:16px; line-height:24px; }
@media (hover:hover) and (pointer:fine) { a.crumb:hover { color:var(--ink); } }
h1 { font-weight:500; font-size:40px; letter-spacing:-0.03em; line-height:46px; margin-top:2px; }
.meta { display:flex; align-items:center; gap:16px; flex:none; }
.meta .k { font-weight:500; font-size:24px; line-height:26.4px; letter-spacing:-0.018em; }
.sep { width:1px; height:24px; background:var(--line); }
.pill { display:inline-flex; align-items:center; gap:6px; background:var(--btn); color:var(--btn-ink); padding:9px 14px; border-radius:999px; font-weight:500; white-space:nowrap; transition:transform .2s var(--ease); }
.pill.ghost { background:var(--card); color:var(--ink); }
.pill:active { transform:scale(.98); }

.stage { background:var(--card); border-radius:20px; height:min(66vh, 640px); position:relative; display:grid; place-items:center; overflow:hidden; padding:40px 40px 120px; }
.object { max-width:min(900px, 100%); max-height:100%; transition:opacity .35s var(--ease), transform .45s var(--ease); }
.object img, .object video { max-width:100%; max-height:calc(min(66vh, 640px) - 160px); display:block; margin:0 auto; border-radius:8px; }
.object.out { opacity:0; transform:translateY(8px); }
/* Swipeable: vertical scrolling stays native, sideways moves belong to the stage. */
.stage { touch-action:pan-y; cursor:grab; user-select:none; -webkit-user-select:none; }
.stage.dragging { cursor:grabbing; }
.stage.dragging .object { transition:none; }
.object img, .object video { -webkit-user-drag:none; }
.pager { position:absolute; bottom:24px; left:50%; transform:translateX(-50%); width:min(640px, calc(100% - 32px)); background:var(--card); border:1px solid var(--line); border-radius:14px; display:grid; grid-template-columns:1fr 1px 1fr; }
.pager button, .pager a { all:unset; cursor:pointer; padding:14px 20px; display:flex; align-items:center; gap:12px; }
.pager :is(button, a):focus-visible { outline:2px solid var(--ink); outline-offset:-2px; border-radius:12px; }
.pager :is(button, a):last-child { justify-content:flex-end; text-align:right; }
.pager small { display:block; color:var(--mute); }
.pager i.d { background:var(--line); }
.dots { position:absolute; top:24px; left:50%; transform:translateX(-50%); display:flex; gap:6px; }
.dots button { all:unset; cursor:pointer; width:6px; height:6px; border-radius:50%; background:var(--line); transition:background .2s, width .3s var(--ease); }
.dots button[aria-current="true"] { background:var(--ink); width:18px; border-radius:3px; }

.specs { display:grid; grid-template-columns:repeat(4, 1fr); margin-top:12px; background:var(--card); border-radius:20px; }
.specs div { padding:22px 24px; border-right:1px solid var(--line); }
.specs div:last-child { border-right:0; }
.specs dt { font-size:12px; line-height:16.8px; color:var(--mute); }
.specs dd { margin-top:6px; font-size:16px; line-height:22px; }
.intro { max-width:560px; padding:56px 0 80px; font-size:16px; line-height:24px; }
.intro span { color:var(--mute); }
.intro a { text-decoration:underline; text-underline-offset:3px; }

/* Home: one block per project. The stage browses that project's images; the breakdown explains it. */
.intro.top { padding:72px 0 8px; }
/* Opening statement: pinned while its words reveal with the scroll (reveal.js). */
.reveal-wrap { height:150vh; }
.statement { position:sticky; top:22vh; max-width:980px; padding:0; font-weight:500; font-size:40px; line-height:46px; letter-spacing:-0.03em; }
.statement span { color:var(--mute); }
/* Revealing words arrive in warm sand gold and cool to cream as the reveal moves past them (reveal.js reads --sand-*). */
.w { transition:opacity .12s linear, color .2s linear; }
/* Project media around the statement (desktop). reveal.js sets --p (0 to 1) on the wrap as you scroll;
   each card fades, rises and settles once --p passes its --at, then drifts slowly for depth. */
.reveal-media { display:none; }
.sand-field { display:none; }
@media (prefers-reduced-motion: no-preference) {
  /* Sand shader (sand-shader.js): full-bleed, pinned with the statement, soft top and bottom edges.
     Hidden at the top of the page; it fades in over the first stretch of scrolling (--p from reveal.js)
     and fades out with the cards (--x). Each canvas also fades in once it has drawn its first frame. */
  html { overflow-x:clip; }
  .sand-field { display:block; position:sticky; top:0; height:100vh; margin-bottom:-100vh; width:100vw; margin-left:calc(50% - 50vw);
                pointer-events:none; opacity:calc(min(1, var(--p, 0) * 5) * (1 - var(--x, 0)));
                /* Lowest layer of the page: behind the statement, the project text and every card. */
                position:sticky; z-index:-1;
                -webkit-mask-image:linear-gradient(180deg, transparent, #000 16%, #000 80%, transparent); mask-image:linear-gradient(180deg, transparent, #000 16%, #000 80%, transparent); }
  .sand-field canvas { position:absolute; inset:0; display:block; width:100%; height:100%; opacity:0; transition:opacity 1.6s ease; }
  .sand-field canvas.on { opacity:var(--on, 1); }
  /* Once revealed, the shader background stays: scrolling back to the top or past the statement no longer fades it. */
  .reveal-wrap.sand-on .sand-field { opacity:1 !important; }
}
/* "Keep scrolling" scribble: pinned at the bottom of the screen once the statement has fully revealed
   (--p near 1) and gone again as the cards clear away (--x). Desktop and tablet only; phones have the bottom bar there. */
.keep-scroll { display:none; }
@media (min-width:768px) and (prefers-reduced-motion: no-preference) {
  .keep-scroll { display:block; position:sticky; top:0; height:100vh; margin-bottom:-100vh; z-index:3; pointer-events:none;
    opacity:calc(clamp(0, (var(--p, 0) - .92) * 12.5, 1) * clamp(0, 1 - var(--x, 0) * 1.6, 1)); }
  .keep-scroll > * { position:absolute; left:50%; }
  .keep-scroll span { bottom:56px; transform:translateX(-50%) rotate(-3deg); padding:.15em .35em; line-height:1.3; white-space:nowrap;
    font:500 26px/1.3 "Caveat", "Bradley Hand", cursive; letter-spacing:0; color:var(--ink); }
  .keep-scroll svg { bottom:22px; width:22px; height:32px; margin-left:-11px; overflow:visible; animation:kbob 1.8s ease-in-out infinite; }
  .keep-scroll path { fill:none; stroke:var(--ink); stroke-width:2.6; stroke-linecap:round; stroke-linejoin:round; }
  @keyframes kbob { 0%, 100% { transform:translateY(0); } 50% { transform:translateY(6px); } }
}
/* Kick-off (kickoff.js adds html.kick-on). Pinned with the statement, shown only at the top of the page. */
.kickoff { display:none; }
html.kick-on .kickoff { display:block; position:sticky; top:0; height:100vh; margin-bottom:-100vh; z-index:2; pointer-events:none;
  /* Also fades with the scroll (--x from reveal.js, like the shader and cards), so a fast scroll can't carry it over the first project. */
  opacity:calc((1 - var(--x, 0) * 1.6) * clamp(0, 1.2 - var(--p, 0) * 10, 1)); }
  /* ...and fades as the reveal brings the project cards in, so the goal never sits on top of them. */
html.kick-on .kickoff.away { opacity:0; transition:opacity .5s; }
.kickoff .goal { position:absolute; right:4%; top:52vh; width:min(300px, 24vw); overflow:visible; }
.kickoff .goal path { fill:none; stroke:var(--ink); stroke-linecap:round; stroke-linejoin:round; }
.kickoff .goal .frame { stroke-width:6; }
.kickoff .goal .mesh { stroke-width:1.2; opacity:.35; }
.kickoff .goal .depth { stroke-width:2; opacity:.55; }
.kickoff .goal .net { transform-box:view-box; transform-origin:150px 130px; }
/* Handwritten labels get padding (offset back with a negative margin) so Safari never clips Caveat's slanted strokes. */
.kickoff .shoot, .kickoff .hint { padding:.15em .35em; margin:-.15em 0 0 -.35em; line-height:1.3; white-space:nowrap; }
.kickoff .shoot, .kickoff .hint, .kickoff .goal-word { position:absolute; font-family:"Caveat", "Bradley Hand", cursive; font-weight:500; letter-spacing:0; color:var(--ink); }
.kickoff .shoot { left:calc(14% + 80px); top:calc(70vh - 4px); font-size:40px; line-height:1; }
.kickoff .arrow { position:absolute; left:calc(14% + 180px); top:70vh; width:min(260px, 22vw); overflow:visible; }
.kickoff .arrow path { fill:none; stroke:var(--ink); stroke-width:4; stroke-linecap:round; stroke-linejoin:round; }
.kickoff .hint { left:14%; top:calc(78vh + 40px); font-size:22px; color:var(--mute); }
.kickoff .kick { all:unset; pointer-events:auto; position:absolute; left:14%; top:72vh; width:60px; height:60px; border-radius:50%; cursor:pointer; will-change:transform; }
.kickoff .kick::after { content:""; position:absolute; left:8%; right:8%; bottom:-10px; height:8px; border-radius:50%; background:rgba(0,0,0,.35); filter:blur(3px); animation:kshadow 2.4s ease-in-out infinite; }
.kickoff .kick:focus-visible { outline:2px solid var(--ink); outline-offset:4px; }
.kickoff .kb { position:absolute; inset:0; border-radius:50%; background:url('assets/cursor-ball.png') center/cover;
  box-shadow:inset -6px -8px 14px rgba(0,0,0,.28), inset 5px 5px 10px rgba(255,255,255,.35); animation:khop 2.4s ease-in-out infinite; }
.kickoff.kicked .kb, .kickoff.kicked .kick::after { animation:none; }
.kickoff.kicked .kick::after { opacity:0; transition:opacity .2s; }
.kickoff.kicked .shoot, .kickoff.kicked .arrow, .kickoff.kicked .hint { opacity:0; transition:opacity .25s; }
/* Padding gives the handwritten word room: Safari clips animated text to its box, and Caveat's "¡" and "!" overflow it. */
.kickoff .goal-word { right:calc(4% + min(300px, 24vw) * .3); top:calc(52vh - 72px); font-size:56px; line-height:1.3; padding:.1em .3em; white-space:nowrap; opacity:0; transform:scale(.6) rotate(-6deg); }
.kickoff.scored .goal-word { animation:kgoal 1s var(--ease) forwards; }
@keyframes khop { 0%, 70%, 100% { transform:translateY(0) rotate(0); } 80% { transform:translateY(-14px) rotate(-25deg); } 90% { transform:translateY(0) rotate(-35deg); } }
@keyframes kshadow { 0%, 70%, 100% { transform:scale(1); opacity:1; } 80% { transform:scale(.7); opacity:.5; } }
@keyframes kgoal { 0% { opacity:0; transform:scale(.6) rotate(-6deg); } 40% { opacity:1; transform:scale(1.08) rotate(-6deg); } 100% { opacity:1; transform:scale(1) rotate(-6deg); } }
@media (max-width:720px) {
  /* Phones: the shader background rests at 5% from the start, rises to full as the statement reveals (--p),
     and still fades out before the first project (--x). */
  /* Short phones (iPhone SE): tighter statement. */
  @media (max-height:700px) {
    .statement { font-size:22px; line-height:27px; } .intro.top.statement { padding-top:8px; }
  }
  .sand-field { opacity:calc((0.05 + 0.95 * min(1, var(--p, 0) * 5)) * (1 - var(--x, 0))); }
}
/* Phones and small tablets (up to 767px, where the bar sits at the bottom): the kick-off is anchored to the bottom bar itself. kickoff.js measures where
   the bar sits inside the pinned box (--bar), so Safari's toolbars can't push the ball and goal under it.
   Everything keeps at least 64px clear of the bar. */
@media (max-width:767px) {
  .kickoff .goal { right:3%; top:calc(var(--bar, 70vh) - 88px - min(40vw, 300px) * .733); bottom:auto; width:min(40vw, 300px); }
  .kickoff .kick { left:6%; top:calc(var(--bar, 70vh) - 156px); bottom:auto; width:48px; height:48px; }
  .kickoff .shoot { left:calc(6% + 60px); top:calc(var(--bar, 70vh) - 150px); bottom:auto; font-size:30px; }
  .kickoff .arrow { display:none; }
  .kickoff .hint { left:6%; top:calc(var(--bar, 70vh) - 96px); bottom:auto; font-size:18px; }
  .kickoff .goal-word { right:8%; top:calc(var(--bar, 70vh) - 88px - min(40vw, 300px) * .733 - 66px); bottom:auto; font-size:40px; }
}
@media (prefers-reduced-motion: reduce) { html.kick-on .kickoff { display:none; } }

@media (min-width:1100px) and (prefers-reduced-motion: no-preference) {
  .reveal-media { display:block; position:sticky; top:0; height:100vh; margin-bottom:-100vh; pointer-events:none; }
  .reveal-wrap { height:210vh; }
  .rm { position:absolute; --l:clamp(0, calc((var(--p, 0) - var(--at)) * 7), 1); opacity:calc(var(--l) * (1 - var(--x, 0)));
        transform:translateY(calc((1 - var(--l)) * 56px - var(--p, 0) * var(--d) * 1px - var(--x, 0) * 60px)) scale(calc(.9 + var(--l) * .1 - var(--x, 0) * .06)) rotate(calc(var(--r) * var(--l)));
        aspect-ratio:var(--ar); border-radius:14px; overflow:hidden; background:var(--card);
        box-shadow:0 0 0 1px var(--line), 0 18px 40px -18px rgba(0,0,0,.45); will-change:transform, opacity; }
  .rm img, .rm video { width:100%; height:100%; object-fit:cover; display:block; }
  /* On narrower desktops the statement scales down with the width (same line breaks) so the side cards never sit on it. */
  .statement { z-index:1; max-width:min(980px, 68vw); font-size:min(40px, 2.78vw); line-height:1.15; }
}

@media (prefers-reduced-motion: reduce) { .reveal-wrap { height:auto; padding:72px 0 24px; } .statement { position:static; } }
.proj { padding-top:40px; scroll-margin-top:72px; }
.proj + .proj { margin-top:48px; border-top:1px solid var(--line); }
.stage:focus-visible { outline:2px solid var(--ink); outline-offset:3px; }
.object { margin:0; }
.pager.three { grid-template-columns:auto 1fr auto; align-items:center; }
.pager.three button { padding:14px 18px; }
.capline { display:flex; justify-content:center; align-items:baseline; gap:10px; min-width:0; padding:0 8px; }
.capline .cap { white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
.capline .count { color:var(--mute); font:12px var(--mono); flex:none; }
.breakdown { display:grid; grid-template-columns:2fr 1fr 1fr; margin-top:12px; background:var(--card); border-radius:20px; }
.breakdown > div { padding:22px 24px; border-right:1px solid var(--line); }
.breakdown > div:last-child { border-right:0; }
.breakdown dt { font-size:12px; line-height:16.8px; color:var(--mute); }
.breakdown dd { margin-top:6px; font-size:16px; line-height:24px; display:grid; gap:6px; }
.breakdown .wide dd { max-width:60ch; }
.breakdown dd span { color:var(--mute); font-size:14px; line-height:20px; }
.breakdown dd a { display:inline-flex; align-items:center; gap:6px; font-size:14px; text-decoration:underline; text-underline-offset:3px; justify-self:start; }

/* Case pages */
.hero { background:var(--card); border-radius:20px; overflow:hidden; }
.hero img, .hero video { width:100%; display:block; max-height:72vh; object-fit:cover; }
.figcap { color:var(--mute); font-size:12px; line-height:16.8px; padding:10px 4px 0; }
.lede { max-width:640px; padding:56px 0 24px; font-weight:500; font-size:24px; line-height:30px; letter-spacing:-0.018em; }
.block { display:grid; grid-template-columns:minmax(0, 1fr) minmax(0, 2fr); gap:24px 48px; padding:56px 0; border-top:1px solid var(--line); }
.block h2 { font-weight:500; font-size:24px; line-height:26.4px; letter-spacing:-0.018em; position:sticky; top:24px; align-self:start; }
.block .txt { max-width:600px; display:grid; gap:14px; font-size:16px; line-height:24px; }
.block .txt a { text-decoration:underline; text-underline-offset:3px; }
.card { background:var(--card); border-radius:20px; padding:32px; display:grid; place-items:center; margin-top:10px; }
.card img, .card video { max-width:100%; max-height:620px; display:block; border-radius:8px; }
.card.pair { grid-template-columns:1fr 1fr; gap:16px; }
.card.pair img { max-height:520px; margin:0 auto; }
/* Device frames (device() / stage). --sh is the height the frame may use; each context sets it.
   MacBook: dark bezel with a camera dot over a 16:10 screen, aluminium base. iPhone: rounded bezel. */
.stage { --sh:calc(min(66vh, 640px) - 160px); }
.card .dev { --sh:min(560px, 62vh); } .card.pair .dev { --sh:min(480px, 56vh); } .hero .dev { --sh:min(560px, 60vh); }
/* Boxes holding a device frame get room around it and never clip it (the MacBook base is wider than its screen). */
.hero:has(.dev) { overflow:visible; display:grid; place-items:center; padding:56px 64px 64px; }
.card:has(.dev.mac) { padding:48px 64px 56px; }
.dev { margin:0 auto; }
.dev .scr { position:relative; overflow:hidden; background:#0b0b0c; }
.dev .scr img, .dev .scr video { width:100%; height:100%; max-width:none; max-height:none; object-fit:cover; object-position:top center; border-radius:0; display:block; margin:0; }
.dev.mac { width:min(100%, 880px, calc(var(--sh) * 1.5)); }
.object .dev.mac { width:min(880px, calc(100vw - 120px), calc(var(--sh) * 1.5)); }
.dev.mac .scr { aspect-ratio:16/10; border:clamp(6px, 1.4vw, 13px) solid #121213; border-bottom-width:clamp(9px, 2vw, 18px); border-radius:clamp(8px, 1.6vw, 16px) clamp(8px, 1.6vw, 16px) 3px 3px;
  box-shadow:0 0 0 1px #3c3c3f, 0 24px 48px -24px rgba(0,0,0,.55); }
.dev.mac .scr::before { content:""; position:absolute; top:calc(clamp(6px, 1.4vw, 13px) * -.62); left:50%; width:5px; height:5px; margin-left:-2.5px; border-radius:50%; background:#2b2b2e; z-index:1; }
.dev.mac .base { height:clamp(8px, 1.4vw, 14px); margin:0 -5%; border-radius:2px 2px 14px 14px / 2px 2px 10px 10px;
  background:linear-gradient(180deg, #e3e3e6, #b9b9bd 55%, #8e8e93); box-shadow:0 10px 20px -10px rgba(0,0,0,.5); position:relative; }
.dev.mac .base::before { content:""; position:absolute; top:0; left:50%; width:16%; height:45%; transform:translateX(-50%); border-radius:0 0 8px 8px; background:#a7a7ab; }
.dev.mac.fit .scr { background:#fff; }
.dev.mac.fit .scr img { object-fit:contain; object-position:center; }
.dev.phone { height:var(--sh); aspect-ratio:9/19.5; width:auto; max-width:100%; padding:clamp(5px, 1vw, 9px); border-radius:24px; /* frame corners capped at 24px */
  background:#0d0d0e; box-shadow:0 0 0 1.5px #3d3d40, 0 0 0 3px #1b1b1d, 0 24px 48px -24px rgba(0,0,0,.6); }
.dev.phone .scr { height:100%; border-radius:calc(24px - clamp(5px, 1vw, 9px)); /* concentric with the frame */ }
.card.pair .dev { margin:0 auto; }
@media (max-width:720px) {
  .stage { --sh:calc(62vh - 170px); }
  .card .dev { --sh:440px; } .card.pair .dev { --sh:300px; } .hero .dev { --sh:440px; }
  .hero:has(.dev) { padding:28px 24px 32px; } .card:has(.dev.mac) { padding:24px 24px 28px; }
  .object .dev.mac { width:min(calc(100vw - 110px), calc(var(--sh) * 1.5)); }
}
.nums { display:grid; grid-template-columns:repeat(2, 1fr); background:var(--card); border-radius:20px; margin-top:10px; }
.nums div { padding:22px 24px; }
.nums div + div { border-left:1px solid var(--line); }
.nums dt { font-size:12px; line-height:16.8px; color:var(--mute); }
.nums dd b { display:block; font-weight:500; font-size:40px; line-height:46px; letter-spacing:-0.03em; margin:6px 0 2px; }
.nums dd span { color:var(--mute); }
.pager.static { position:static; transform:none; width:100%; margin:56px 0 0; }

/* Work index */
/* Title sits above its card; the larger row gap keeps each title with the card below it. */
.grid { display:grid; grid-template-columns:repeat(2, 1fr); gap:48px 12px; padding-bottom:80px; }
.tile { display:block; }
.tile .shot { background:var(--card); border-radius:20px; aspect-ratio:4/3; display:grid; place-items:center; padding:28px; overflow:hidden; }
.tile .shot img, .tile .shot video { max-width:100%; max-height:100%; display:block; border-radius:8px; transition:transform .6s var(--ease); }
@media (hover:hover) and (pointer:fine) { .tile:hover .shot img, .tile:hover .shot video { transform:scale(1.03); } }
.tile .row { display:flex; justify-content:space-between; align-items:baseline; gap:12px; padding:0 4px 14px; }
.tile .row b { font-weight:500; font-size:24px; line-height:26.4px; letter-spacing:-0.018em; }
.tile .row span { color:var(--mute); text-align:right; }

/* About */
.life { display:grid; grid-template-columns:2fr 3fr; gap:12px; }
.life figure { background:var(--card); border-radius:20px; overflow:hidden; display:grid; place-items:center; }
.life .photo img { width:100%; height:auto; display:block; }
.life .kick { padding:48px; }
.life .kick img { width:100%; transform:rotate(-8deg); }
.xp { position:relative; background:var(--card); border-radius:20px; margin-top:12px; }
.xp div { display:grid; grid-template-columns:1.2fr 2fr auto; gap:16px; padding:16px 24px; border-top:1px solid var(--line); font-size:16px; line-height:22px; }
.xp div:first-of-type { border-top:0; }
.xp span { color:var(--mute); }
.xp div { align-items:center; }
.xp b { display:flex; align-items:center; gap:14px; font-weight:500; }
.logo { flex:none; width:32px; height:32px; border-radius:9px; object-fit:contain; padding:3px; background:#fff; box-shadow:inset 0 0 0 1px var(--line); }

footer { display:flex; justify-content:space-between; gap:12px; padding:40px 0 48px; color:var(--mute); }
@media (hover:hover) and (pointer:fine) { footer a:hover { color:var(--ink); } }

@media (max-width:767px) {
  .wrap { padding:0 16px; }
  header { grid-template-columns:1fr auto; height:auto; padding-top:16px; gap:8px; }
  /* On phones the bar sits at the bottom, within thumb reach. */
  .gnav { top:auto; bottom:16px; }
  header > span { display:none; }
  .gi { padding:0 14px; }
  .pop { top:auto; bottom:calc(100% + 8px); transform-origin:bottom right; }
  body { padding-bottom:84px; }
  .head { flex-direction:column; align-items:flex-start; padding-top:28px; }
  h1 { font-size:32px; line-height:38px; }
  /* Phones: tighter year and buttons so a row with two buttons (Timeitt) keeps the page gutter; very narrow screens wrap. */
  .meta { gap:10px; flex-wrap:wrap; row-gap:10px; }
  .meta .pill { padding:9px 12px; }
  .meta:has(.pill + .pill) .sep { display:none; }
  .stage { height:62vh; padding:40px 16px 110px; }
  /* Home on phones: every slide fills the same square, edge to edge, so the stage never letterboxes.
     Photos and videos are cropped to cover it; framed screenshots stand inside it at full height. */
  .stage { height:auto; display:block; padding:44px 16px 92px; --sh:calc(100vw - 104px); }
  .stage .object { width:100%; max-width:none; max-height:none; aspect-ratio:1/1; display:grid; place-items:center; overflow:hidden; border-radius:12px; }
  .stage .object > img, .stage .object > video { width:100%; height:100%; max-width:none; max-height:none; object-fit:cover; border-radius:0; }
  .specs { grid-template-columns:1fr 1fr; }
  .specs div:nth-child(2) { border-right:0; }
  .specs div:nth-child(-n+2) { border-bottom:1px solid var(--line); }
  .lede { font-size:20px; line-height:26px; padding-top:40px; }
  .block { grid-template-columns:1fr; padding:40px 0; }
  .block h2 { position:static; }
  .card { padding:16px; }
  .nums, .grid, .life { grid-template-columns:1fr; }
  .tile .row { flex-direction:column; align-items:flex-start; gap:4px; }
  .tile .row span { text-align:left; }
  .breakdown { grid-template-columns:1fr; }
  .breakdown > div { border-right:0; border-bottom:1px solid var(--line); }
  .breakdown > div:last-child { border-bottom:0; }
  .pager.three button { padding:12px 14px; }
  .intro.top { padding-top:40px; }
  /* Phones: the statement sits right under the header so the kick-off below it is visible from the start. */
  .statement { font-size:25px; line-height:30px; top:76px; }
  .intro.top.statement { padding-top:clamp(20px, 7vh, 64px); }
  .reveal-wrap { height:130vh; }
  .nums div + div { border-left:0; border-top:1px solid var(--line); }
  .xp div { grid-template-columns:1fr auto; }
  .xp div b { grid-column:1 / -1; }
  footer { flex-direction:column; }
}
/* Liquid-glass panels: the cards let the sand and dust behind them through. Blur and saturation for the
   material, a soft top sheen, a lit top rim and a hairline edge for the bevel. Pager sits a step more opaque. */
.stage, .breakdown, .specs, .hero, .card, .nums, .tile .shot, .life figure, .xp {
  background:linear-gradient(180deg, var(--panel-sheen), transparent 38%), var(--panel);
  -webkit-backdrop-filter:blur(22px) saturate(1.7); backdrop-filter:blur(22px) saturate(1.7);
  box-shadow:inset 0 1px 0 var(--panel-rim), inset 0 0 0 1px var(--panel-edge), 0 24px 48px -28px var(--panel-shadow); }
.pager { background:linear-gradient(180deg, var(--panel-sheen), transparent 60%), var(--panel-strong); border-color:transparent;
  -webkit-backdrop-filter:blur(18px) saturate(1.8); backdrop-filter:blur(18px) saturate(1.8);
  box-shadow:inset 0 1px 0 var(--panel-rim), inset 0 0 0 1px var(--panel-edge), 0 12px 28px -16px var(--panel-shadow); }
@supports not ((backdrop-filter:blur(1px)) or (-webkit-backdrop-filter:blur(1px))) {
  .stage, .breakdown, .specs, .hero, .card, .nums, .tile .shot, .life figure, .xp, .pager { background:var(--card); } }
@media (prefers-reduced-transparency: reduce) {
  .stage, .breakdown, .specs, .hero, .card, .nums, .tile .shot, .life figure, .xp, .pager { background:var(--card); -webkit-backdrop-filter:none; backdrop-filter:none; } }
@media (prefers-reduced-motion: reduce) { * { transition:none !important; } }
