/* januariopinto.com: one screen, five sections, GIPX type and motion over a slow smoke */

/* the faces, served from here rather than from Google Fonts: the same files (Geist and Geist Mono vary by weight,
   Newsreader's italic by optical size), Latin and Latin Extended. OFL, see fonts/OFL.txt */
@font-face { font-family: 'Geist'; font-style: normal; font-weight: 100 900; font-display: swap; src: url(fonts/geist-latin.woff2) format('woff2'); unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; }
@font-face { font-family: 'Geist'; font-style: normal; font-weight: 100 900; font-display: swap; src: url(fonts/geist-latin-ext.woff2) format('woff2'); unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C4, U+2113, U+2C60-2C7F, U+A720-A7FF; }
@font-face { font-family: 'Geist Mono'; font-style: normal; font-weight: 100 900; font-display: swap; src: url(fonts/geist-mono-latin.woff2) format('woff2'); unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; }
@font-face { font-family: 'Geist Mono'; font-style: normal; font-weight: 100 900; font-display: swap; src: url(fonts/geist-mono-latin-ext.woff2) format('woff2'); unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C4, U+2113, U+2C60-2C7F, U+A720-A7FF; }
@font-face { font-family: 'Newsreader'; font-style: italic; font-weight: 400; font-display: swap; src: url(fonts/newsreader-italic-latin.woff2) format('woff2'); unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; }
@font-face { font-family: 'Newsreader'; font-style: italic; font-weight: 400; font-display: swap; src: url(fonts/newsreader-italic-latin-ext.woff2) format('woff2'); unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C4, U+2113, U+2C60-2C7F, U+A720-A7FF; }

:root {
  --f-sans: 'Geist', ui-sans-serif, system-ui, sans-serif;
  --f-mono: 'Geist Mono', ui-monospace, monospace;
  --f-serif: 'Newsreader', Georgia, serif;
  --reel: cubic-bezier(.2, .7, .2, 1);        /* GIPX line entrance: fast start, long settle */
  --press: cubic-bezier(.25, .46, .45, .94);   /* button press */
  --move: cubic-bezier(.645, .045, .355, 1);   /* things already on screen sliding */
  --fade: 1.2s ease;                           /* colour moving between looks */
  --out: .16s;                                 /* leaving: everything at once, quickly */
  --pad: 20px;
  --top: calc(env(safe-area-inset-top, 0px) + clamp(64px, 11svh, 140px)); /* titles sit high; the content gets the room */
  --bar-b: calc(env(safe-area-inset-bottom, 0px) + clamp(24px, 7svh, 72px));
  --bar-h: 44px;
  --bar-w: min(590px, calc(100vw - 2 * var(--pad))); /* the bar's width; titles end where it ends */
  color-scheme: dark;
}
@media (min-width: 640px) { :root { --pad: 40px; } }
@media (min-width: 900px) { :root { --top: calc(env(safe-area-inset-top, 0px) + clamp(56px, 9svh, 120px)); } }

/* looks: GIPX's teal, ink, violet and ember (its red), and a gold one built the same way. One accent per look;
   descriptions are always grey. Teal opens the site, as on the share image; the intro lines are grey with white
   key words, so the name stays the only coloured word there. On black (as on GIPX) the name is silver, and a
   title's italic word is gold, so the black and white page still has its one coloured word. */
:root { --a-ink: #ececea; --a-teal: #7fd1c4; --a-gold: #e6c27a; --a-violet: #c3a8ff; --a-ember: #ff6a57; --name: var(--accent); }
:root, :root[data-look="teal"] { --bg: #071615; --fg: #eaf6f3; --muted: #b1b5b3; --accent: var(--a-teal); --s1: #1f5f5b; --s2: #7fd1c4; --s3: #071615; }
:root[data-look="ink"] { --bg: #090909; --fg: #f5f5f3; --muted: #a9a8a4; --accent: var(--a-ink); --title-acc: var(--a-gold); --name: #c4c3bf; --s1: #404040; --s2: #c9c9c9; --s3: #090909; }
:root[data-look="gold"] { --bg: #120d05; --fg: #f7f0e1; --muted: #b8b4ab; --accent: var(--a-gold); --s1: #5c471a; --s2: #e6c27a; --s3: #120d05; }
:root[data-look="violet"] { --bg: #0b0716; --fg: #f3eeff; --muted: #b4b2bb; --accent: var(--a-violet); --s1: #3b2a7a; --s2: #c3a8ff; --s3: #0b0716; }
:root[data-look="ember"] { --bg: #140705; --fg: #fff1e6; --muted: #bcb4af; --accent: var(--a-ember); --s1: #7a2a12; --s2: #ffb070; --s3: #140705; }
:root { --line: color-mix(in srgb, var(--fg) 22%, transparent); --hi: var(--title-acc, var(--accent)); /* the colour of a lit item */ }

*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; text-size-adjust: 100%; -webkit-tap-highlight-color: transparent; } /* no browser flash on a tap: a lit item fades into its own colour instead */
body {
  margin: 0; min-height: 100svh; background: var(--bg); color: var(--fg);
  font-family: var(--f-sans); font-size: 17px; line-height: 1.45;
  -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
}
::selection { background: var(--accent); color: var(--bg); }
:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }
a { color: inherit; }
button { font: inherit; color: inherit; }
.sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; }

/* the smoke; a gradient in the same colours shows until the first frame is drawn */
.sky {
  position: fixed; inset: 0; z-index: 0;
  background:
    radial-gradient(65% 55% at 74% 38%, color-mix(in srgb, var(--s2) 33%, transparent), transparent 72%),
    radial-gradient(90% 80% at 30% 55%, color-mix(in srgb, var(--s1) 40%, transparent), transparent 76%),
    linear-gradient(180deg, var(--s1), var(--s3));
}
.sky canvas { display: block; width: 100%; height: 100%; image-rendering: pixelated; opacity: 0; transition: opacity .7s ease; } /* a canvas pixel per block of smoke, so its edges stay hard */
.sky canvas.on { opacity: 1; }
.veil { position: fixed; inset: 0; z-index: 1; pointer-events: none; background: linear-gradient(180deg, rgba(0, 0, 0, .25), transparent 35%, rgba(0, 0, 0, .6)); }

/* ---------- type ---------- */
.title { margin: 0; font-size: calc(min(clamp(2.6rem, 11.6vw, 6.4rem), calc(var(--bar-w) / 7)) * var(--fit, 1)); font-weight: 900; line-height: .98; letter-spacing: -.055em; }
/* a section's title runs on until the bar ends, and only then breaks between its parts. The size above keeps the
   widest line ('What I’m doing', 6.8em) inside the bar with a little room, so no part ever breaks inside */
.stage:not(.hero) .title { max-width: var(--bar-w); }
.stage:not(.hero) .title .l { white-space: nowrap; }
.title .l { display: block; }
.title .acc { font-family: var(--f-serif); font-style: italic; font-weight: 400; letter-spacing: -.025em; padding-left: .06em; color: var(--title-acc, var(--accent)); }

.hero .title { font-size: calc(clamp(4.4rem, min(20.5vw, 19svh), 12.5rem) * var(--fit, 1)); }
/* the name shines now and then, like light passing over metal; only while the intro is on screen */
.hero .name {
  background: linear-gradient(100deg, var(--name) 40%, #fff 50%, var(--name) 60%) 100% 0 / 250% 100% no-repeat;
  -webkit-background-clip: text; background-clip: text; color: var(--name); -webkit-text-fill-color: transparent;
}
.hero.in .name { animation: sheen 4.8s cubic-bezier(.4, 0, .2, 1) 1.3s infinite; }
@keyframes sheen { 0% { background-position: 100% 0; } 34%, 100% { background-position: 0% 0; } }
/* with WebGL the letters are windows onto a lighter copy of the smoke (the shine above is the fallback) */
.hero .name { position: relative; }
.letters { position: absolute; display: block; pointer-events: none; }
.namefx .hero .name, .namefx .hero.in .name { background: none; color: transparent; -webkit-text-fill-color: transparent; animation: none; }
/* three lines: the sentence balanced over two, then 'from Lisbon.' */
/* three statements, each on one line at the largest size that allows; the last word of each in white */
/* two big lines under the name: what I do (turning from one thing to the next) and where; last words in white */
.say { margin: 0; font-size: calc(clamp(1.6rem, 8.6vw, 4.6rem) * var(--fit, 1)); font-weight: 700; line-height: 1.1; letter-spacing: -.045em; color: var(--muted); white-space: nowrap; }
.say.what { margin-top: clamp(28px, 6svh, 72px); }
.say.where { margin-top: clamp(4px, 1.2svh, 14px); }
.say .k { color: var(--fg); }
.morph { position: relative; display: inline-block; }
.morph .w { display: inline-block; }
.morph .k { color: var(--fg); }
.morph .ghost { position: absolute; left: 0; top: 0; pointer-events: none; }
.morph .cut { display: none; }

.svc { display: grid; gap: clamp(22px, 3.4svh, 38px); margin-top: clamp(20px, 3svh, 36px); }
/* service names read like the portfolio names: big, light and white, with the grey description under them */
.svc h3 { margin: 0; font-size: calc(clamp(1.9rem, 9vw, 3.2rem) * var(--fit, 1)); font-weight: 500; line-height: 1; letter-spacing: -.05em; color: var(--fg); }
.svc p { margin: .4em 0 0; max-width: 34ch; font-size: max(12px, calc(clamp(1rem, 1.25vw, 1.2rem) * var(--fit, 1))); font-weight: 500; line-height: 1.38; letter-spacing: -.01em; color: var(--muted); text-wrap: pretty; }
.svc p { margin-top: .6em; }
.svc p .l { display: block; }
.svc p .l, .nm-d { text-wrap: balance; }

.doing { list-style: none; margin: clamp(20px, 3svh, 36px) 0 0; padding: 0; display: grid; gap: clamp(14px, 4svh, 44px); }
.js .doing { margin-block: auto; } /* centred in the room under the title */
.js #contact .asks { margin-top: auto; }
.js #contact .mail { margin-bottom: auto; }
.doing li { font-size: calc(clamp(1.55rem, 7vw, 2.6rem) * var(--fit, 1)); font-weight: 700; line-height: 1.1; letter-spacing: -.04em; color: var(--muted); }

.names { list-style: none; margin: clamp(20px, 3svh, 36px) 0 0; padding: 0; display: grid; gap: clamp(14px, 2.2svh, 26px); }
.nm { display: block; font-size: calc(clamp(2.7rem, 12.5vw, 4.2rem) * var(--fit, 1)); font-weight: 500; line-height: .95; letter-spacing: -.055em; }
.nm-d { display: block; margin-top: .5em; font-size: max(12px, calc(clamp(1rem, 1.25vw, 1.2rem) * var(--fit, 1))); font-weight: 500; letter-spacing: -.01em; color: var(--muted); }
/* the last line of a list, 'and more.', in the serif like a title's italic word */
.more .nm, .doing .more .l { font-family: var(--f-serif); font-style: italic; font-weight: 400; letter-spacing: -.03em; color: var(--accent); }

/* contact opens with two questions in the 'doing now' size, in white */
/* contact: five lines in one size and one grey, so the email is the bright thing */
.asks { margin: clamp(20px, 3svh, 36px) 0 0; }
.asks p { margin: 0; font-size: calc(clamp(1.55rem, 7vw, 2.6rem) * var(--fit, 1)); font-weight: 700; line-height: 1.1; letter-spacing: -.04em; color: var(--muted); }
.asks p + p { margin-top: .3em; }
.asks .dim { color: var(--muted); }
.asks .m { display: none; } /* phone-only line breaks */
.mail { display: flex; align-items: flex-end; gap: 4px; margin-top: clamp(22px, 3.4svh, 40px); }
/* the address reads like the portfolio names: big and light */
.addr { font-size: calc(clamp(2.2rem, 9.4vw, 4.2rem) * var(--fit, 1)); font-weight: 500; line-height: 1; letter-spacing: -.055em; text-decoration: none; color: var(--fg); user-select: text; -webkit-user-select: text; transition: color .25s ease; }
.addr .hi { color: var(--accent); }
.copy { position: relative; flex: none; width: 44px; height: 44px; display: grid; place-items: center; padding: 0; border: 0; border-radius: 999px; background: none; color: var(--muted); cursor: pointer; -webkit-tap-highlight-color: transparent; transition: transform .15s var(--press), color .2s ease; }
.copy svg { grid-area: 1 / 1; width: 19px; height: 19px; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; transition: opacity .2s ease, transform .3s var(--reel), filter .2s ease; }
.copy .ok { opacity: 0; transform: scale(.6); filter: blur(2px); }
.copy.done { color: var(--accent); }
.copy.done .ok { opacity: 1; transform: none; filter: none; }
.copy.done .icon { opacity: 0; transform: scale(.6); filter: blur(2px); }
.copy:active { transform: scale(.94); }
@media (hover: hover) and (pointer: fine) { .addr:hover { color: var(--accent); } .copy:hover { color: var(--accent); } }

.foot { margin-top: 48px; font: 500 11px/1.6 var(--f-mono); letter-spacing: .14em; text-transform: uppercase; color: var(--muted); }
.foot nav { display: flex; gap: 18px; margin-top: 8px; }
.foot a { text-decoration: none; }

/* smaller text keeps a soft dark halo, so it stays readable when a bright part of the smoke drifts behind it */
.say, .asks, .svc h3, .svc p, .doing li, .nm-d, .mail { text-shadow: 0 1px 14px rgba(0, 0, 0, .5), 0 1px 2px rgba(0, 0, 0, .3); }

/* ---------- without script: one page you scroll ---------- */
.stage { position: relative; z-index: 2; padding: 16svh var(--pad) 6svh; }
.look { display: none; }

/* ---------- one fixed screen (a cramped one shrinks its big text to fit: --fit, set by the script) ---------- */
.js, .js body { height: 100%; overflow: hidden; overscroll-behavior: none; }
.js .stage {
  position: fixed; inset: 0; margin: 0; display: flex; flex-direction: column;
  padding: var(--top) var(--pad) calc(var(--bar-b) + var(--bar-h) + 24px);
  overflow-y: auto; overscroll-behavior: contain; scrollbar-width: none; touch-action: pan-y; /* sideways swipes are ours */
  visibility: hidden; user-select: none; -webkit-user-select: none;
}
.js .stage::-webkit-scrollbar { display: none; }
.js .stage.cur { visibility: visible; }
.js:not(.desk) .stage:not(.cur) { content-visibility: hidden; } /* a screen out of view isn't even laid out, so a phone starts with the intro alone */
.js .stage:not(.hero) .title { min-height: 2.02em; } /* every title takes two lines, so the content below starts at the same height */
/* a short title takes one line; 'now' keeps its own */
.js .stage .title.one { min-height: auto; margin-bottom: clamp(14px, 3.4svh, 36px); } /* auto, not 0: a flex item at 0 can be squeezed under the text after it */
.title.one .l { display: inline-block; }
.js .foot { display: none; }

/* lines rise out of a soft blur, one after another; on leaving they all fade at once */
.js .l { opacity: 0; transform: translateY(.3em); filter: blur(min(.21em, 10px)); transition: opacity var(--out) var(--reel), transform 0s var(--out), filter 0s var(--out); }
.js .in .l { opacity: 1; transform: none; filter: none; transition: opacity .7s var(--reel) var(--d, 0s), transform .7s var(--reel) var(--d, 0s), filter .7s var(--reel) var(--d, 0s); }

/* ---------- section buttons: one row, and everything in it moves with transforms and opacity only.
   The bubble slides to the current button and fades into the section's colour while its label crossfades;
   on a phone (compact) the other sections are letter circles that slide aside to make room. ---------- */
.bar { position: fixed; z-index: 5; left: var(--pad); bottom: var(--bar-b); width: var(--bar-w); display: flex; gap: 6px; opacity: 0; transform: translateY(10px); pointer-events: none; transition: opacity .6s var(--reel) 1s, transform .6s var(--reel) 1s; }
.ready .bar { opacity: 1; transform: none; pointer-events: auto; }
.bar-row { position: relative; flex: 1; min-width: 0; height: var(--bar-h); touch-action: none; }
.bar-row button {
  position: absolute; top: 0; left: 0; height: var(--bar-h); display: grid; place-items: center; padding: 0; appearance: none;
  border: 1px solid rgba(255, 255, 255, .17); border-radius: 999px; background: rgba(0, 0, 0, .24); box-shadow: 0 2px 0 rgba(0, 0, 0, .35);
  color: var(--muted); font: 500 10.5px/1 var(--f-mono); letter-spacing: .08em; text-transform: uppercase; white-space: nowrap;
  cursor: pointer; -webkit-tap-highlight-color: transparent; touch-action: none;
  transition: translate .42s var(--move), scale .15s var(--press);
}
.bar-row button:active { scale: .97; }
.bar-row button:focus-visible { outline-offset: 2px; }
@media (hover: hover) and (pointer: fine) { .bar-row button:hover { color: var(--btn-c, var(--fg)); } }
.bar .ini { display: none; font-size: 11.5px; letter-spacing: 0; }
.bar.compact .ini { display: block; }
.bar.compact .full { display: none; }
.bubble {
  position: absolute; top: 0; left: 0; height: var(--bar-h); display: grid; place-items: center; border-radius: 999px; overflow: hidden;
  pointer-events: none; color: #0b0b0b; will-change: translate; isolation: isolate; /* keeps the glint clipped to the pill in WebKit */
  transition: translate .42s var(--move), scale .15s var(--press);
}
.bubble .was { position: absolute; inset: 0; opacity: 0; }
.bubble .bl { grid-area: 1 / 1; position: relative; z-index: 1; font: 500 10.5px/1 var(--f-mono); letter-spacing: .08em; text-transform: uppercase; white-space: nowrap; opacity: 0; transition: opacity .2s ease; }
.bubble .bl.on { opacity: 1; transition-delay: .14s; }
.bubble .glint { position: absolute; z-index: 2; top: 0; bottom: 0; left: 0; width: 56px; background: linear-gradient(100deg, transparent 15%, rgba(255, 255, 255, .6) 50%, transparent 85%); transform: translateX(-80px); }
.bubble.press { scale: .97; }
.bar.dragging .bar-row button, .bar.dragging .bubble { transition-duration: .22s; }
.bar.dragging .bar-row button:active { scale: none; }
.bar.still .bar-row button, .bar.still .bubble { transition: none; }

/* ---------- lit items: the one you point at, tap or reach with j and k takes the section's colour, and with
   WebGL the smoke runs through its letters (a canvas that fades in over them). The others step back, as the
   services do on the long page, and the lit one steps forward a little ---------- */
.doing .l { display: block; }
.doing .l, .nm, .svc h3 { position: relative; }
.doing li, .names li { width: fit-content; }
.js .doing li, .js .names li, .js:not(.desk) .svc article { cursor: pointer; }
.doing .lit, .names .lit, .svc .lit { color: var(--hi); }
.js .in .doing .l, .js .in .names .nm, .js .in .svc h3 { transition: opacity .7s var(--reel) var(--d, 0s), transform .7s var(--reel) var(--d, 0s), filter .7s var(--reel) var(--d, 0s), color .4s ease; }
.doing > li, .names > li, .js:not(.desk):not(.fold) .svc > article { transition: opacity .4s ease, translate .5s var(--move); }
.doing:has(.lit) > li:not(:has(.lit)), .names:has(.lit) > li:not(:has(.lit)), .js:not(.desk):not(.fold) .svc:has(.lit) > article:not(:has(.lit)) { opacity: .3; }
@media (prefers-reduced-motion: no-preference) {
  .doing > li:has(.lit), .names > li:has(.lit), .js:not(.desk):not(.fold) .svc > article:has(.lit) { translate: 10px 0; }
}
.glow { opacity: 0; transition: opacity .45s ease; }
.glow.on { opacity: 1; }

/* ---------- colour button: one emblem per look ---------- */
/* ---------- the emblem ends the row: it changes shape and colour with the section, and tapping it plays through the looks ---------- */
.js .look { flex: none; width: var(--bar-h); height: var(--bar-h); display: grid; place-items: center; padding: 0; border: 1px solid rgba(255, 255, 255, .17); border-radius: 999px; background: rgba(0, 0, 0, .24); box-shadow: 0 2px 0 rgba(0, 0, 0, .35); color: var(--accent); cursor: pointer; touch-action: manipulation; -webkit-tap-highlight-color: transparent; transition: transform .15s var(--press), box-shadow .15s var(--press), color .42s var(--move); }
.js .look:active { transform: translateY(1px) scale(.94); box-shadow: 0 1px 0 rgba(0, 0, 0, .35); }
.look svg { width: 20px; height: 20px; overflow: visible; }
.look .em { fill: none; stroke: currentColor; stroke-width: 1.6; stroke-linejoin: round; transform-box: fill-box; transform-origin: center; opacity: 0; transform: scale(.6) rotate(-40deg); transition: opacity .25s ease, transform .5s var(--reel); }
.look .em .fill { fill: currentColor; stroke: none; }
.look[data-look="teal"] .em-teal, .look[data-look="ink"] .em-ink, .look[data-look="gold"] .em-gold, .look[data-look="violet"] .em-violet, .look[data-look="ember"] .em-ember { opacity: 1; transform: none; }

/* ---------- the keys: a ? at the end of the bar (mouse screens only) and the guide it opens ---------- */
.help { display: none; }
@media (hover: hover) and (pointer: fine) {
  .js .help { flex: none; width: var(--bar-h); height: var(--bar-h); display: grid; place-items: center; padding: 0; border: 1px solid rgba(255, 255, 255, .17); border-radius: 999px; background: rgba(0, 0, 0, .24); box-shadow: 0 2px 0 rgba(0, 0, 0, .35); color: var(--muted); font: 500 13px/1 var(--f-mono); cursor: pointer; transition: color .2s ease, transform .15s var(--press); }
  .js .help:hover { color: var(--fg); }
  .js .help:active { transform: translateY(1px) scale(.94); }
}
.guide {
  width: min(400px, calc(100vw - 32px)); margin: auto; padding: 26px 26px 22px; border: 1px solid var(--line); border-radius: 14px;
  background: color-mix(in srgb, var(--bg) 86%, transparent); -webkit-backdrop-filter: blur(18px); backdrop-filter: blur(18px);
  color: var(--fg); box-shadow: 0 24px 60px rgba(0, 0, 0, .5);
}
.guide[open] { animation: guide-in .22s var(--reel); }
@keyframes guide-in { from { opacity: 0; transform: translateY(8px) scale(.98); } }
.guide::backdrop { background: rgba(0, 0, 0, .45); }
.guide h2 { margin: 0 0 16px; font: 500 10.5px/1 var(--f-mono); letter-spacing: .14em; text-transform: uppercase; color: var(--muted); }
.guide dl { margin: 0; }
.guide dl div { display: grid; grid-template-columns: 9.5em 1fr; align-items: center; gap: 12px; padding: 10px 0; border-top: 1px solid var(--line); }
.guide dt { display: flex; align-items: center; gap: 4px; }
.guide dt span { margin: 0 4px; font-size: 12px; color: var(--muted); }
.guide dd { margin: 0; font-size: 14px; color: var(--muted); }
.guide kbd { min-width: 26px; height: 26px; padding: 0 6px; display: inline-grid; place-items: center; border: 1px solid var(--line); border-bottom-width: 2px; border-radius: 6px; font: 500 12px/1 var(--f-mono); color: var(--fg); }
.guide .sw { display: flex; align-items: center; gap: 10px; margin-top: 14px; padding-top: 14px; border-top: 1px solid var(--line); font-size: 14px; color: var(--muted); cursor: pointer; }
.guide .sw input { appearance: none; position: relative; flex: none; width: 34px; height: 20px; margin: 0; border-radius: 999px; background: var(--line); cursor: pointer; transition: background-color .2s ease; }
.guide .sw input::after { content: ''; position: absolute; top: 3px; left: 3px; width: 14px; height: 14px; border-radius: 50%; background: var(--fg); transition: translate .2s var(--move); }
.guide .sw input:checked { background: var(--accent); }
.guide .sw input:checked::after { translate: 14px 0; background: #0b0b0b; }
.guide .touch { margin: 12px 0 0; font-size: 13px; color: var(--muted); }
.guide .x { position: absolute; top: 14px; right: 14px; width: 32px; height: 32px; display: grid; place-items: center; padding: 0; border: 0; border-radius: 8px; background: none; color: var(--muted); cursor: pointer; }
.guide .x:hover { color: var(--fg); }
.guide .x svg { width: 16px; height: 16px; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; }

/* ---------- wider screens ---------- */
@media (min-width: 900px) {
  /* each service is a row: the name, then its description beside it, both from the left */
  .svc { grid-template-columns: max-content minmax(0, 50ch); column-gap: clamp(28px, 3.4vw, 56px); row-gap: clamp(22px, 3.2svh, 34px); }
  .svc article { display: grid; grid-template-columns: subgrid; grid-column: 1 / -1; align-items: start; }
  .svc p { margin-top: 0; max-width: none; }
  .asks p { font-size: calc(min(2.6rem, 5svh) * var(--fit, 1)); }
  .svc h3 { font-size: calc(clamp(2.2rem, 3.4vw, 3.2rem) * var(--fit, 1)); }
  .names li { display: flex; align-items: baseline; gap: 22px; }
  .nm-d { margin-top: 0; }
}

/* a wide, short window (a phone on its side) sets each name beside its line too */
@media (min-width: 640px) and (max-width: 899px) and (min-aspect-ratio: 1/1) {
  .names li { display: flex; align-items: baseline; gap: 22px; }
  .nm-d { margin-top: 0; }
}

@media (max-width: 420px) {
  .bar { left: 12px; width: calc(100vw - 24px); gap: 4px; }
}
/* tall phone screens have room to spare: the intro and the 'doing now' list spread further down */
/* phones: the headings are the roof and the bar is the floor; the content sits centred between them */
@media (max-aspect-ratio: 3/4) {
  .names { gap: clamp(14px, 4.6svh, 48px); }
  .js .svc, .js .names { margin-block: auto; padding-top: clamp(16px, 2.4svh, 28px); }
  .js .more { margin-top: clamp(10px, 4svh, 44px); }
  /* the intro in three places: the name at the top, what I do big in the middle (it moves, so it leads),
     Porto just above the bar. What I do always breaks before its last word */
  .js .hero-in { flex: 1; display: flex; flex-direction: column; }
  .say.what { margin-block: auto; padding-block: clamp(20px, 3svh, 36px); font-size: calc(clamp(1.6rem, 14.5vw, 4.6rem) * var(--fit, 1)); line-height: 1; white-space: normal; }
  .say .morph { display: block; }
  .say .cut { display: inline; }
  .say.where { margin-top: 0; font-size: calc(clamp(1.4rem, 10vw, 4rem) * var(--fit, 1)); }
  /* bigger lists: long lines may take two */
  .doing li { font-size: calc(clamp(1.55rem, 9.5vw, 3rem) * var(--fit, 1)); line-height: 1.04; text-wrap: balance; }
  .doing { gap: clamp(14px, 3.6svh, 40px); }
  .asks p { font-size: calc(clamp(1.55rem, 8.2vw, 2.6rem) * var(--fit, 1)); line-height: 1.06; }
  .asks .m { display: inline; }
  .mail { margin-top: clamp(22px, 5svh, 56px); }
  .js #contact .asks { padding-top: clamp(16px, 2.6svh, 30px); }
  /* services and contact sit a little above the middle, nearer their heading than the bar: a spacer under them
     that gives way when the text needs the room */
  :root:not(.desk) #services::after, :root:not(.desk) #contact::after { content: ''; flex: 0 1 clamp(0px, 14svh - 60px, 120px); }
  .svc { gap: clamp(18px, 3.6svh, 48px); }
}
/* tall phones: services get a little more presence and air */
@media (max-aspect-ratio: 3/4) and (min-height: 800px) {
  .svc p { font-size: max(12px, calc(1.06rem * var(--fit, 1))); line-height: 1.42; }
  .svc { gap: clamp(22px, 5svh, 56px); }
}
@media (max-height: 720px) {
  .svc { gap: 16px; }
  .doing { gap: clamp(12px, 4svh, 28px); }
  .names { gap: 12px; }
}
@media (max-height: 720px) and (max-width: 899px) { .svc h3 { font-size: calc(min(1.75rem, 8.4vw) * var(--fit, 1)); } .nm { font-size: calc(min(2.5rem, 11vw) * var(--fit, 1)); } }

/* ---------- wide screens with a mouse: one page you scroll, each section a screen tall, the bar along the
   bottom as on a phone. As there, the title is the roof and the bar the floor, and the content sits centred
   between them; sizes follow the screen's height too, so nothing ever reaches the bar. ---------- */
.desk, .desk body { height: auto; overflow: visible; }
.desk { scroll-snap-type: y proximity; }
.desk.gliding { scroll-snap-type: none; } /* the script's glide sets the scroll every frame */
.desk .stage { position: relative; inset: auto; min-height: 100svh; overflow: visible; visibility: visible; scroll-snap-align: start; user-select: text; -webkit-user-select: text; }
.desk .title { font-size: min(6.4rem, 11.6vw, 11svh, calc(var(--bar-w) / 7)); }
.desk .hero .title { font-size: min(12.5rem, 20.5vw, 17svh); }
.desk .hero-in { flex: 1; display: flex; flex-direction: column; }
.desk .say { font-size: min(4.6rem, 8.6vw, 8svh); }
.desk .say.what { margin-top: auto; }
.desk .say.where { margin-bottom: auto; }
.desk .doing li { font-size: min(2.6rem, 5.4svh); }
.desk .names { margin-block: auto; }
.desk .nm { font-size: min(4.2rem, 7svh); }
.desk .asks p { font-size: min(2.6rem, 4.4svh); }
.desk .addr { font-size: min(4.2rem, 6svh); }

/* services hold still while you scroll through them: the names stacked, the one you're on in the accent
   with its text open under it. Phones fold them the same way, a tap opening one (.fold) */
.desk #services { height: calc(110svh * var(--n, 3)); padding-block: 0; } /* --n: how many services (set by the script) */
/* the pin only holds the services still on the long page; elsewhere its title and list are the screen's own */
.pin { display: contents; }
.desk .pin { position: sticky; top: 0; height: 100svh; display: flex; flex-direction: column; padding: var(--top) 0 calc(var(--bar-b) + var(--bar-h) + 24px); }
.desk .svc, .fold .svc { display: block; }
.desk .svc { margin-block: auto; }
.desk .svc article, .fold .svc article { display: grid; grid-template-columns: none; grid-template-rows: auto 0fr; transition: grid-template-rows .6s var(--move); }
.desk .svc article.on, .fold .svc article.on { grid-template-rows: auto 1fr; }
.fold .measure .svc article { transition: none; } /* while the script measures the longest text open */
.desk .svc article + article, .fold .svc article + article { margin-top: clamp(6px, 1.4svh, 18px); }
.desk .svc h3 { width: max-content; font-size: min(4rem, 4vw, 6.4svh); cursor: pointer; }
.fold .svc h3 { width: fit-content; }
/* once the section is in, the names dim and light at once, without the entrance's stagger */
.desk .in .svc h3, .fold .in .svc h3 { transition: opacity .5s var(--move), color .5s var(--move), transform .7s var(--reel) var(--d, 0s), filter .7s var(--reel) var(--d, 0s); }
.desk .in .svc article:not(.on) h3, .fold .in .svc article:not(.on) h3 { opacity: .3; }
.desk .in .svc article:not(.on) h3:hover, .fold .in .svc article:not(.on) h3:hover { opacity: .6; }
.desk .svc article.on h3, .fold .svc article.on h3 { opacity: 1; color: var(--title-acc, var(--accent)); }
.desk .svc p, .fold .svc p { margin-top: 0; min-height: 0; overflow: hidden; }
.desk .svc p { max-width: 60ch; font-size: min(1.4rem, 1.45vw, 2.6svh); } /* wide enough that each phrase keeps to its line */
.desk .svc p .l:first-child, .fold .svc p .l:first-child { padding-top: 14px; color: var(--fg); }
.desk .svc p .l:last-child, .fold .svc p .l:last-child { padding-bottom: 12px; }
.desk .svc article:not(.on) p .l, .fold .svc article:not(.on) p .l { opacity: 0; transform: translateY(.3em); filter: blur(min(.21em, 10px)); }

@media (prefers-reduced-motion: reduce) {
  .hero.in .name { animation: none; }
  .js .l, .js .in .l { transform: none; filter: none; }
  .js .in .l { transition: opacity .4s ease; }
  .bar-row button, .bubble { transition: none; }
  .sky canvas { transition: none; }
  .guide[open] { animation: none; }
}
