/* ==========================================================
   Maryam — homepage redesign
   Design language: light "design-tool canvas" (ref: adasilv2)
   DM Sans, #FBFBFB paper, #161515 ink, #4A77FF cursor-blue.
   All tokens are --hm-* so the case-study styles (styles.css
   :root vars) stay byte-identical.
   ========================================================== */

:root {
  /* ===== KRAFT TEST =====
     Two sheets rather than one: the hero is the warmer stock underneath, and
     everything that scrolls over it is the lighter sheet on top. That tonal
     step is what makes the torn edge read as paper on paper. To go back,
     restore #FBFAF7 here, drop --hm-hero-bg, and delete the aura rule below. */
  --hm-bg: #FEFDFB;            /* the front sheet: white with a breath of warmth */
  /* The SHEETS stay near-white — the colour belongs to the clouds, not to the
     paper. What separates the hero is how much of the tint pools on it. */
  --hm-hero-bg: #FBF8F2;
  --hm-ink: #161515;
  --hm-dark: #313131;
  --hm-gray: #858585;
  --hm-gray-2: #8b8b8b;
  --hm-gray-3: #767676;
  --hm-line: #d0d0d0;
  --hm-line-soft: #e8e8e8;
  --hm-blue: #4a77ff;
  --hm-accent: #C8901A;
  /* the same gold, deepened until small text on white clears AA (4.55:1);
     the accent itself sits at 2.81:1 and is for marks, not for reading */
  --hm-accent-ink: #9A6E12;        /* the avatar's mustard, deepened so strokes read */
  --hm-gold:   #F3C02A;        /* the avatar's mustard exactly, for fills */
  /* the lens's second reading — your #91B0DA hue, pushed up in saturation and
     down in lightness so it actually carries at display size */
  --hm-lens: #9A6B12;          /* the revealed reading, warm to match */
  --hm-card: #f7f7f7;
  /* the three project tints: the card's open state AND the case-study wipe
     both read these, so a colour only ever changes in one place */
  --tint-noty:   #DBE5F1;
  --tint-vinnie: #F0E1DA;
  --tint-linko:  #DCE8D2;
  --hm-foot: #f5f5f5;
  --hm-font: "DM Sans", "Inter", sans-serif;
  --hm-spring: cubic-bezier(.34, 1.56, .64, 1);
  --hm-ease: cubic-bezier(.22, 1, .36, 1);
}

/* page ground — override the old cream theme without touching :root vars */
body {
  background: var(--hm-bg);
  color: var(--hm-ink);
  font-family: var(--hm-font);
  cursor: auto;
}
/* the old film-grain overlay belongs to the previous design; the reference
   look is clean flat paper. Case studies carry their own backgrounds. */
body::after { display: none; }

.hm-page {
  position: relative;
  z-index: 1;
  /* the paper is painted HERE, not only on body: the avatar's multiply blend
     needs something inside this stacking context to blend into, or its white
     artboard stays a white box */
  background: var(--hm-bg);
  min-height: 100vh;
  overflow-x: clip;
}

/* ==========================================================
   The aura — three very pale blue clouds behind the paper.
   Same #2A6ECB family as the lens, at low alpha and heavily blurred, so
   it reads as light on the page rather than as shapes. They wander on
   four-step paths of different lengths, so no two ever sync up and the
   drift never retraces the same line. Kept clear of the right-hand
   column, so the portrait always sits on plain paper.
   ========================================================== */
.hm-aura {
  position: fixed; inset: 0; z-index: 0;
  pointer-events: none; overflow: hidden;
}
/* One tint, two strengths. --aura is the kraft tan the paper is made of, so
   the clouds read as the sheet's own colour pooling rather than a yellow
   light thrown at it. Each layer sets its own --aura-a: the front sheet keeps
   its wash faint so it stays bright, the sheet behind carries the colour. */
.hm-aura {
  --aura: 197, 162, 112;
  --aura-a: .13;
}
.hm-aura span {
  position: absolute; display: block; border-radius: 50%;
  background: radial-gradient(circle,
      rgba(var(--aura), var(--aura-a)) 0%,
      rgba(var(--aura), calc(var(--aura-a) * .42)) 42%,
      rgba(var(--aura), 0) 70%);
  filter: blur(46px);
  will-change: transform;
}
/* the front sheet: in step with the All Projects and About pages (.13), a
   shade under them, and well below the hero's .26 */
.hm-scroller > .hm-aura { --aura-a: .12; }
/* the sheet behind: where the colour is allowed to gather */
.hm-hero__paper > .hm-aura { --aura-a: .26; }
.hm-aura span:nth-child(1) {
  width: 46vw; height: 46vw; left: -12vw; top: -10vh;
  animation: auraA 13s ease-in-out infinite;
}
.hm-aura span:nth-child(2) {
  width: 38vw; height: 38vw; left: 14vw; top: 34vh;
  animation: auraB 17s ease-in-out infinite;
}
.hm-aura span:nth-child(3) {
  width: 32vw; height: 32vw; left: 30vw; bottom: -14vh;
  animation: auraC 15s ease-in-out infinite;
}
/* four steps rather than two: a wander, not a shuttle. The x range on each is
   bounded to the left of the portrait's column so the sketch never picks up a
   tint, however far along the cycle they are. */
@keyframes auraA {
  0%   { transform: translate3d(0, 0, 0)         scale(1); }
  25%  { transform: translate3d(14vw, 11vh, 0)   scale(1.14); }
  50%  { transform: translate3d(6vw, 22vh, 0)    scale(1.02); }
  75%  { transform: translate3d(17vw, 6vh, 0)    scale(1.18); }
  100% { transform: translate3d(0, 0, 0)         scale(1); }
}
@keyframes auraB {
  0%   { transform: translate3d(0, 0, 0)         scale(1.05); }
  25%  { transform: translate3d(-17vw, -13vh, 0) scale(1); }
  50%  { transform: translate3d(-7vw, 15vh, 0)   scale(1.16); }
  75%  { transform: translate3d(-21vw, 5vh, 0)   scale(1.03); }
  100% { transform: translate3d(0, 0, 0)         scale(1.05); }
}
@keyframes auraC {
  0%   { transform: translate3d(0, 0, 0)         scale(1); }
  25%  { transform: translate3d(-13vw, -16vh, 0) scale(1.14); }
  50%  { transform: translate3d(3vw, -8vh, 0)    scale(1.04); }
  75%  { transform: translate3d(-6vw, -22vh, 0)  scale(1.2); }
  100% { transform: translate3d(0, 0, 0)         scale(1); }
}
/* The archive and the about page are opaque fixed overlays, so the body's aura
   cannot show through them — each carries its own copy instead. Their content is
   lifted above the clouds. NB the headers only get a z-index: they must keep
   position: sticky, or they stop pinning to the top as the page scrolls. */
.hm-aura--overlay { z-index: 0; }
.works > .wk__top,
.hm-about > .ab__top { z-index: 2; }
.works > .wk__inner,
.hm-about > .hm-about__inner { position: relative; z-index: 1; }
@media (prefers-reduced-motion: reduce) {
  .hm-aura span { animation: none; }
}

.hm-page ::selection { background: rgba(74, 119, 255, .28); }

/* styles.css still styles bare h2/h3 for the case studies (condensed display face,
   uppercase, red). Nothing in the new pages should inherit that. */
.hm-page h1, .hm-page h2, .hm-page h3, .hm-page h4,
.hm-about h1, .hm-about h2, .hm-about h3, .hm-about h4,
.works h1, .works h2, .works h3, .works h4 {
  font-family: var(--hm-font);
  text-transform: none;
  color: var(--hm-ink);
}

/* ---------- shared bits ---------- */

.hm-pill {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-family: var(--hm-font);
  font-size: 18px;
  font-weight: 500;
  letter-spacing: -.02em;
  line-height: 1;
  padding: 14px 22px;
  border-radius: 100px;
  white-space: nowrap;
  transition: transform .45s var(--hm-spring), box-shadow .45s var(--hm-spring), background .25s ease;
}
.hm-pill:hover { transform: translateY(-2px) scale(1.03); }
.hm-pill:active { transform: translateY(0) scale(.98); }

.hm-pill--dark {
  background: var(--hm-ink);
  color: #fff;
}
.hm-pill--dark:hover { box-shadow: 0 10px 24px rgba(22, 21, 21, .22); }

.hm-pill--ghost {
  background: #fff;
  color: var(--hm-ink);
  border: 1.4px solid var(--hm-line);
}
.hm-pill--ghost:hover { box-shadow: 0 10px 24px rgba(22, 21, 21, .08); }

/* section headers: doodle · title+sub · action pill (ref layout) */
.hm-sechead {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  gap: 18px;
  max-width: 1160px;
  margin: 0 auto;
  padding: 0 28px clamp(48px, 7vh, 76px);
}
.hm-sechead__doodle { justify-self: start; }
.hm-sechead__action { justify-self: end; }
.hm-sechead__mid { text-align: center; }
.hm-sechead__title {
  font-family: var(--hm-font);
  font-size: 31px;
  font-weight: 500;
  letter-spacing: -.04em;
  line-height: 1.02;
  text-transform: none;
  color: var(--hm-ink);
  margin: 0 0 8px;
}
.hm-sechead__sub {
  font-size: 18px;
  font-weight: 500;
  letter-spacing: -.04em;
  color: var(--hm-gray-2);
  margin: 0;
}
.hm-sechead__doodle svg { display: block; }

/* appear-on-scroll (JS adds .in; springs handled by GSAP) */
.hm-appear { will-change: transform, opacity; }

/* ==========================================================
   Top bar
   ========================================================== */

.hm-top {
  position: sticky;
  top: 0;
  z-index: 40;
  /* absent on arrival — it fades in and pins once the reader starts scrolling.
     It keeps its space in flow, so nothing jumps when it appears. */
  opacity: 0;
  transform: translateY(-10px);
  pointer-events: none;
  transition: opacity .4s var(--hm-ease), transform .5s var(--hm-ease);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 22px clamp(20px, 4vw, 56px);
  background: linear-gradient(to bottom, var(--hm-bg) 68%, rgba(251, 251, 251, 0));
}

/* wordmark: the leaf mark, then the name set plainly — no frames, no cursor */
.hm-logo {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  padding: 4px 2px;
  background: none;
  border: 0;
  cursor: pointer;
  text-decoration: none;
  color: var(--hm-ink);
  font-family: var(--hm-font);
}
.hm-logo__mark {
  width: 30px;
  height: 30px;
  flex: none;
  object-fit: contain;
  transition: transform .5s var(--hm-ease);
}
.hm-logo__name {
  font-size: 22px;
  font-weight: 600;
  letter-spacing: -.035em;
  line-height: 1;
  white-space: nowrap;
}
.hm-logo:hover .hm-logo__mark { transform: rotate(-8deg) scale(1.06); }

/* the About overlay wears the same wordmark, in its own header bar */
.ab__home { border: 0; }

.hm-top__actions {
  display: flex;
  align-items: center;
  gap: 12px;
}

/* Resume pill grows a tiny doc-stack peek on hover */
.hm-resume { position: relative; overflow: visible; }
.hm-resume__peek {
  position: absolute;
  left: 50%;
  bottom: 78%;
  width: 132px;
  height: 171px;
  transform: translateX(-50%) translateY(14px) rotate(-6deg) scale(.6);
  opacity: 0;
  z-index: -1;
  border-radius: 7px;
  border: 2.5px solid #fff;
  object-fit: cover;
  box-shadow: 0 8px 20px rgba(0, 0, 0, .18);
  transition: transform .5s var(--hm-spring), opacity .3s ease;
  pointer-events: none;
}
.hm-resume:hover .hm-resume__peek {
  opacity: 1;
  transform: translateX(-50%) translateY(0) rotate(-6deg) scale(1);
}

/* ==========================================================
   Hero — the design-tool canvas
   ========================================================== */

.hm-hero {
  position: relative;
  max-width: 1240px;
  margin: 0 auto;
  /* the portrait owns the right-hand side, so the reading column centres in
     what is left of the hero rather than in the hero itself */
  padding: clamp(24px, 4vh, 56px) 24px clamp(18px, 2.6vh, 34px);
  /* the portrait's width, in one place: it tracks the window's width AND its
     height, and is capped by the hero's own height so it can never grow tall
     enough to slide under the sticky header */
  --por-w: clamp(240px, min(38vw, calc((100vh - 258px) / 1.34)), 640px);
  --por-w: clamp(240px, min(38vw, calc((100svh - 258px) / 1.34)), 640px);
  /* the avatar's box: wider than it is tall, so it is capped on height too */
  --av-w: clamp(280px, min(40vw, calc((100vh - 300px) * 1.02)), 560px);
  --av-w: clamp(280px, min(40vw, calc((100svh - 300px) * 1.02)), 560px);
  padding-right: calc(24px + var(--av-w));
  /* the hero takes the first screen minus the header, the marks strip and the
     dock, so the logos land just above the bar instead of floating mid-page */
  min-height: max(430px, calc(100vh - 300px));
  min-height: max(430px, calc(100svh - 300px));
  display: grid;
  place-items: center;
}

/* the sheet the whole hero is drawn on: viewport-sized (the hero is pinned
   and fills it), painted below everything else in the hero's own context */
.hm-hero__paper {
  position: fixed;
  inset: 0;
  z-index: 0;
  background: var(--hm-hero-bg);
  pointer-events: none;
}
.hm-hero__paper > .hm-aura { z-index: 0; }

.hm-hero__canvas {
  position: relative;
  z-index: 1;
  /* the text moves with the avatar and the folder: they are one composition,
     and shifting only the absolutely-placed pieces pulls it apart */
  transform: translateY(var(--stage-drop, 0px));
  width: min(760px, 100%);
  text-align: center;
}

/* the pill and its hello share one relative box, so the note can be placed
   against the pill itself rather than against the whole centre column */
.hm-hero__tagwrap { position: relative; display: inline-block; }
.hm-hero__tag {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 18px;
  font-weight: 500;
  letter-spacing: -.02em;
  color: #6e6e6e;
  background: #fff;
  border: 1.4px solid var(--hm-line-soft);
  border-radius: 100px;
  padding: 10px 20px;
  margin-bottom: 30px;
}
.hm-hero__tag svg { flex: none; }

.hm-hero__title span { display: inline-block; }

.hm-hero__title {
  font-family: var(--hm-font);
  font-size: clamp(34px, 5.4vw, 49px);
  font-weight: 400;
  letter-spacing: -.04em;
  line-height: 1.04;
  text-transform: none;
  color: var(--hm-ink);
  margin: 0;
}

/* the closing phrase keeps its own face — Instrument Serif italic — with no
   marker swipe behind it */
.hm-hero__em {
  font-family: "Instrument Serif", Georgia, serif;
  font-style: italic;
  font-weight: 400;
  font-size: 1.2em;
  color: var(--hm-ink);
  margin-top: 6px;
}

/* the highlighted phrase — its own face (Instrument Serif italic) swiped with
   an orange highlighter. No frame, no border: just the marker mark. The
   section paper shows through around it. */
.hm-sel {
  position: relative;
  display: inline-block;
  font-family: "Instrument Serif", Georgia, serif;
  font-style: italic;
  font-weight: 400;
  font-size: 1.2em;
  color: var(--hm-ink);
  padding: 0 10px 2px;
  margin-top: 8px;
  /* the marker swipe: translucent orange, sitting low, soft (rounded) ends */
  background-image: linear-gradient(100deg,
      rgba(255, 74, 0, 0) 1%,
      rgba(255, 74, 0, .55) 4%,
      rgba(255, 106, 24, .6) 50%,
      rgba(255, 74, 0, .55) 96%,
      rgba(255, 74, 0, 0) 99%);
  background-repeat: no-repeat;
  background-size: 100% 58%;
  background-position: 0 74%;
  transform: rotate(-1deg);
}

/* blue mouse cursor + name chip hanging off the selection */
.hm-me-cursor {
  position: absolute;
  right: -6%;
  bottom: -58px;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 2px;
  pointer-events: none;
}
.hm-me-cursor svg { display: block; margin-left: -4px; }
.hm-me-cursor__chip {
  font-family: var(--hm-font);
  font-style: normal;
  font-size: 19px;
  font-weight: 600;
  letter-spacing: -.02em;
  color: #fff;
  background: var(--hm-blue);
  border-radius: 100px;
  padding: 8px 18px;
  margin-left: 20px;
  box-shadow: 0 6px 18px rgba(74, 119, 255, .35);
}

/* floating stickers that remain (the two little stars) */
.hm-stick { position: absolute; pointer-events: none; }
.hm-stick--star1 { right: 10%; top: 2%; }
.hm-stick--star2 { right: 4%; bottom: 8%; }

/* ---------- left portrait: sketch + liquid photo reveal ---------- */
.hm-portrait {
  position: absolute;
  right: clamp(-40px, -1vw, 0px);
  bottom: clamp(0px, 3vh, 34px);
  width: var(--por-w, clamp(240px, 30vw, 430px));
  aspect-ratio: 909 / 1000;
  z-index: 1;
  /* the magnifier is the pointer here — no arrow behind it */
  cursor: none;
}
.hm-portrait__sketch,
.hm-portrait__reveal {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
}
.hm-portrait__sketch {
  object-fit: contain;
  object-position: bottom center;
  user-select: none;
  -webkit-user-drag: none;
}
.hm-portrait__reveal { pointer-events: none; overflow: visible; }
/* the revealed photo lifts a touch as it blooms in */
.hm-portrait__blob { transition: none; }

/* ==========================================================
   Worked with — one quiet row of client and school marks.
   Greyed back and set at a single height so a detailed crest and
   a plain wordmark carry the same weight; colour on hover.
   ========================================================== */
.hm-clients {
  max-width: 1180px;
  margin: 0 auto;
  padding: clamp(10px, 1.6vh, 20px) 28px 0;
  text-align: center;
}
.hm-clients__label {
  margin: 0 0 clamp(20px, 3vh, 30px);
  font-size: 11px; font-weight: 500; letter-spacing: .16em;
  text-transform: uppercase; color: var(--hm-gray-2);
}
.hm-clients__row {
  list-style: none; margin: 0; padding: 0;
  display: flex; flex-wrap: wrap; align-items: center; justify-content: center;
  gap: clamp(20px, 3.2vw, 46px);
}
/* every mark gets the same box and is fitted inside it, so a wide wordmark
   (NASCAR) and a compact square (Microsoft) carry equal optical weight —
   matching on height alone would let the wordmarks dominate the row */
.hm-clients__row img {
  display: block;
  /* concrete caps, not percentages: each mark is fitted into the same bounding
     box, so wide wordmarks come out width-limited and compact squares
     height-limited, and every logo ends up about the same optical size */
  max-width: clamp(78px, 8.6vw, 116px);
  max-height: clamp(28px, 3vw, 40px);
  width: auto; height: auto;
  filter: grayscale(1); opacity: .55;
  transition: filter .35s var(--hm-ease), opacity .35s var(--hm-ease);
}
.hm-clients__row li:hover img { filter: none; opacity: 1; }

/* ==========================================================
   The scroller — everything below the hero.

   The hero is PINNED behind this block, so scrolling drags the page
   up over the avatar instead of carrying it away.

   The tear must not be on screen at load: the hero is sized so its
   foot lands exactly on the fold, which puts the torn edge at the
   very bottom of the window whatever that window's height is. It
   then rises into view as you scroll.

   The tear is DRAWN at pixel width, not masked and not stretched:
   transparent above the rip, paper below it, with a warm fibre line
   along the edge. Nothing sits behind it and nothing casts a shadow
   — both sheets are the same paper, and the fibre line is the only
   thing that says one is on top.
   ========================================================== */
.hm-hero {
  position: sticky;
  /* pinned at the BAR's height, not at 0: the top bar is sticky too and sits
     directly above the hero in flow, so pinning at 0 would slide the whole
     hero up underneath it the moment scrolling starts and the avatar would
     visibly jump. Held here, nothing moves — the paper comes up over it. */
  /* pinned at 0 now that it starts at 0: any other value would make the whole
     hero DROP by that much the moment scrolling began */
  top: 0;
  z-index: 1;
  /* Pinning made this a stacking context, which cuts the avatar's
     mix-blend-mode: multiply off from the paper painted on .hm-page — with
     nothing to blend into, its white artboard shows as a white box. The paper
     is painted by .hm-hero__paper INSIDE this context instead of on the box
     itself: the hero is only 1240px wide, so a background here would both
     stop short of the avatar's right edge and cut a rectangle out of the
     aura behind it. */
  /* pulled up under the top bar, which is invisible until you scroll: that
     92px of reserved space was dead ground at the head of the page */
  margin-top: calc(-1 * var(--hm-bar, 92px));
  padding-top: calc(var(--hm-bar, 92px) * .5);
  /* the foot of the hero IS the fold, so the rip starts exactly off-screen.
     svh, not vh: on iOS the URL bar collapsing changes vh mid-scroll, which
     re-ran every calc below it and slid the whole composition while you were
     reading it. svh is the SMALL viewport — the toolbar-out size — so it
     never changes. vh first, for Safari before 15.4. */
  min-height: 100vh;
  min-height: 100svh;
  /* the avatar's own height, derived from its artboard, so the composition
     can be centred against it instead of dropped on the hero's floor */
  --av-h: calc(var(--av-w) * 465 / 528);
  /* above dead centre: the composition reads better high in the frame than
     floating in the middle of a full-viewport hero */
  --stage-drop: clamp(-80px, -6vh, -30px);
  --stage-drop: clamp(-80px, -6svh, -30px);
}
.hm-scroller {
  --tear-h: clamp(52px, 5vw, 78px);
  position: relative;
  z-index: 2;
  margin-top: 0;
  /* the flat paper starts BELOW the torn strip; the strip itself is painted
     by ::before, so the block never shows a straight cut edge */
  background: linear-gradient(var(--hm-bg), var(--hm-bg)) no-repeat;
  background-position: 0 calc(var(--tear-h) - 1px);
  background-size: 100% calc(100% - var(--tear-h) + 1px);
}
/* the rip itself. Generated in JS at the page's real pixel width: a slow
   wander with a couple of deeper bites, and a fine fibrous fringe walked
   along it. Stretching one SVG across the width would smear that fringe into
   a sawtooth, which is exactly what a torn edge is not. */
.hm-tear {
  position: absolute;
  left: 0; right: 0; top: 0;
  height: var(--tear-h);
  pointer-events: none;
  /* BELOW the aura. The rip fills opaque paper up to its own edge, so painted
     above the aura it wiped the wash out in a flat band right under the tear
     — which read as a white edge along the bottom of the torn sheet. Sat
     under it, the aura carries straight across the rip. */
  z-index: 0;
}
.hm-tear svg { display: block; width: 100%; height: 100%; }

/* paper and rip at the bottom, the aura washing over both, content above */
.hm-scroller > .hm-aura { z-index: 1; }
.hm-scroller > :not(.hm-aura):not(.hm-tear) { position: relative; z-index: 2; }

@media (prefers-reduced-motion: reduce) {
  /* pinning is motion: without it the hero simply scrolls away as usual */
  .hm-hero { position: relative; }
}

/* ==========================================================
   The hello — small, faint and handwritten, sitting just above the
   UX/UI Designer line with a curl running down into it. Caveat is
   already on the page for the case studies, so no new font loads.
   ========================================================== */
.hm-hello {
  position: absolute;
  /* up and to the LEFT of the line rather than sitting on top of it: its
     right edge lands just before the title's first letter */
  left: auto;
  right: calc(100% + clamp(2px, .5vw, 9px));
  transform: rotate(-7deg);
  transform-origin: 100% 100%;
  max-width: max-content;
  bottom: calc(100% - clamp(2px, .6vh, 8px));
  display: flex; align-items: flex-end; gap: 2px;
  color: var(--hm-gray);
  opacity: .55;
  pointer-events: none;
}
.hm-hello__text {
  font-family: "Caveat", cursive;
  font-size: clamp(17px, 1.5vw, 22px);
  font-weight: 600;
  letter-spacing: .01em;
  line-height: 1;
  white-space: nowrap;
}
.hm-hello__flourish {
  width: clamp(38px, 3.4vw, 50px); height: auto;
  align-self: flex-start;
  margin-right: -6px;
}
.hm-hello__curl { width: clamp(16px, 1.4vw, 21px); height: auto; margin-bottom: -8px; }
/* both marks draw themselves in once, in the order a hand would write them */
.hm-hello__flourish path { stroke-dasharray: 100; animation: hm-hello-draw 1.4s var(--hm-ease) .5s backwards; }
.hm-hello__curl path    { stroke-dasharray: 100; animation: hm-hello-draw 1s var(--hm-ease) 1.5s backwards; }
@keyframes hm-hello-draw { from { stroke-dashoffset: 100; } to { stroke-dashoffset: 0; } }
@media (prefers-reduced-motion: reduce) {
  .hm-hello__flourish path, .hm-hello__curl path { animation: none; }
}

/* ==========================================================
   The pen — resting off the end of the last line of the headline.
   Anchored to the h1 rather than to the marks layer so it tracks the
   type at every clamp step, and inked like every other mark.
   ========================================================== */
.hm-scrawl {
  position: absolute;
  left: 50%;
  top: calc(100% - clamp(10px, 1.4vw, 18px));
  transform: translateX(-50%);
  width: clamp(230px, 44%, 320px);
  height: 0;
  color: var(--hm-ink);
  pointer-events: none;
}
.hm-scrawl__pen {
  position: absolute;
  /* clear of the text's right edge rather than tucked against it */
  left: 104%;
  top: 0;
  width: clamp(28px, 2.6vw, 38px);
  height: auto;
  opacity: .78;
  /* no entrance: it is one of the marks, and they are all simply there from
     the first frame. The same slow drift, on its own clock. */
  --rot: 14deg;
  transform: rotate(var(--rot));
  animation: hm-mark-float 6.2s ease-in-out -2.4s infinite alternate;
}
@media (prefers-reduced-motion: reduce) {
  .hm-scrawl__pen { animation: none; }
}

/* ==========================================================
   The marks of the trade — thrown across the hero's empty ground
   rather than lined up. Each carries its own --x/--y/--s/--rot, so
   nothing sits on a grid and nothing is anchored to the folder.
   ========================================================== */
.hm-marks {
  position: absolute;
  /* the whole hero, not just the left of it — the marks are placed one by one
     into the ground the avatar, the headline and the folder leave free */
  left: 0; top: 0;
  width: 100%; height: 100%;
  margin: 0; padding: 0; list-style: none;
  z-index: 2;
  pointer-events: none;
  /* ink, not grey: at .55 of --hm-gray these washed out to almost nothing
     against the paper */
  color: var(--hm-ink);
}
.hm-mark {
  position: absolute;
  left: var(--x); top: var(--y);
  width: calc(clamp(34px, 3.4vw, 50px) * var(--s, 1));
  height: calc(clamp(34px, 3.4vw, 50px) * var(--s, 1));
  opacity: .78;
  /* the lean is baked into the drift so the two never fight over transform */
  animation: hm-mark-float 6.2s ease-in-out infinite alternate;
  animation-delay: calc(var(--i) * -1.05s);
}
.hm-mark svg { display: block; width: 100%; height: 100%; }
/* Placed here rather than inline, so each breakpoint can put them somewhere
   sensible instead of the marks being dropped when the ground moves. */
.hm-mark--figma  { --x: 1%;  --y: 23%; }
.hm-mark--cursor { --x: 5%;  --y: 40%; }
.hm-mark--art    { --x: 51%; --y: 34%; }
@keyframes hm-mark-float {
  from { transform: rotate(var(--rot)) translateY(0); }
  to   { transform: rotate(calc(var(--rot) + 6deg)) translateY(-9px); }
}
/* The marks used to be dropped below 1180. They are kept and re-placed
   instead — and from here down they stop being scattered: they pair up, two
   flanking the type on the left and two on the right, at matched heights.
   Ordered widest to narrowest, because these all carry the same specificity
   and the last matching rule is the one that wins. */
@media (max-width: 1180px) {
  /* the square joins the pair instead of parking in the top-right corner:
     the strip between the last word and the avatar is free from here down */
  .hm-mark--figma  { --x: 1%;  --y: 33%; }
  .hm-mark--cursor { --x: 3%;  --y: 45%; }
  .hm-mark--art    { --x: 50%; --y: 33%; }
  /* the two loose stars go too: five marks on one screen is the scatter this
     arrangement exists to fix */
  .hm-stick--star1, .hm-stick--star2 { display: none; }
}
@media (max-height: 520px), (max-width: 300px) { .hm-marks { display: none; } }
@media (prefers-reduced-motion: reduce) {
  .hm-mark { animation: none; transform: rotate(var(--rot)); }
}

/* ==========================================================
   The desk — the tools of the trade and the folder of clients,
   filling the hero's empty left column.

   The logos live INSIDE the folder: the back panel, then the
   documents, then the front panel over them, so only the top edge
   of each shows until the folder is reached for. Then they ride up
   in sequence, the way papers do when you pull at them.
   ========================================================== */
.hm-desk {
  position: absolute;
  z-index: 3;
  /* the hero's headline is centred in the column left of the avatar, so the
     genuinely free ground is the bottom-left corner UNDER it. The width is
     capped so the desk always clears the last line of the headline. */
  left: clamp(4px, 1vw, 20px);
  /* just below the ground line the avatar stands on, so the marks it throws
     clear the headline instead of landing in it */
  /* --desk-lift is how far above the avatar's ground line it sits; narrower
     windows push it below that line to clear the headline as the column
     closes in */
  bottom: calc(50% - var(--stage-drop) - var(--av-h) / 2 + var(--desk-lift, clamp(6px, 1.2vh, 20px)));
  z-index: 3;
  display: flex; flex-direction: column; align-items: flex-start;
  gap: clamp(8px, 1.4vh, 14px);
  width: clamp(126px, 11vw, 158px);
}

/* ---- the folder ---- */
.hm-folder {
  position: relative;
  width: 100%;
  aspect-ratio: 240 / 200;
  cursor: default;
  transform: rotate(-3.5deg);
}
.hm-folder__back,
.hm-folder__front {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  display: block;
  overflow: visible;
}
.hm-folder__back  { z-index: 1; }
.hm-folder__front { z-index: 3; }
.hm-folder__back path,
.hm-folder__front path {
  fill: #F1EFEA;
  stroke: var(--hm-ink);
  stroke-width: 2.6;
}
/* the front sits a shade lighter than the back so the two panels read apart */
.hm-folder__front path { fill: #FAF8F4; }
.hm-folder__front .hm-folder__grip { fill: none; stroke: var(--hm-line); }
.hm-folder:hover .hm-folder__front,
.hm-folder.is-open .hm-folder__front { transform: translateY(2px) rotate(-.4deg); }
/* no pointer to reach with, so the folder answers to a tap instead */
@media (hover: none) {
  .hm-folder { cursor: pointer; }
}
.hm-folder__front { transition: transform .45s var(--hm-ease); }

.hm-folder__docs {
  position: absolute; inset: 0; z-index: 2;
  margin: 0; padding: 0; list-style: none;
}
/* no card behind them: the mark itself is what pokes out of the folder, so a
   white tile would just look like paper stuck to the outside */
.hm-folder__doc {
  position: absolute;
  left: calc(3% + var(--i) * 14.2%);
  /* at rest the marks sit ON the folder's mouth: below the back panel's top
     edge, but well clear of the front panel, so each one pokes out */
  top: 26%;
  width: 24%;
  height: 30%;
  display: grid; place-items: center;
  transform: rotate(var(--r));
  transition: transform .62s var(--hm-spring);
  transition-delay: calc(var(--i) * 26ms);
}
/* and on hover they spill: every one gets its OWN throw and spin, written on
   the element, so the scatter is genuinely uneven rather than a fanned deck */
.hm-folder:hover .hm-folder__doc,
.hm-folder.is-open .hm-folder__doc,
.hm-desk:focus-within .hm-folder__doc {
  transform: translate(var(--sx), var(--sy)) rotate(var(--sr)) scale(1.08);
}
/* Two of the six lead: they stand a little further out of the folder and
   read stronger, so the file has a front rank instead of six equal edges.
   Still greyscale — the colour is what hovering buys you. */
.hm-folder__doc[data-lead] { transform: translateY(-26%) rotate(var(--r)) scale(1.08); }
.hm-folder__doc[data-lead] img { opacity: 1; }

.hm-folder__doc img {
  display: block;
  max-width: 100%; max-height: 100%;
  width: auto; height: auto;
  object-fit: contain;
  /* greyed back until you reach for the folder */
  filter: grayscale(1);
  opacity: .55;
  transition: filter .45s var(--hm-ease), opacity .45s var(--hm-ease);
  transition-delay: calc(var(--i) * 26ms);
}
.hm-folder:hover .hm-folder__doc img,
.hm-folder.is-open .hm-folder__doc img,
.hm-desk:focus-within .hm-folder__doc img { filter: none; opacity: 1; }

/* written ON the folder, in the same hand as the hello — a label someone
   scrawled on a wallet, not a caption set above it */
.hm-folder__label {
  position: absolute;
  left: 12%; top: 56%;
  z-index: 4;
  font-family: "Caveat", cursive;
  font-size: clamp(15px, 1.35vw, 20px);
  font-weight: 600;
  letter-spacing: .01em;
  color: var(--hm-gray-3);
  transform: rotate(-4deg);
  pointer-events: none;
}

/* The desk is not decoration — it carries the client marks — so it is kept
   rather than dropped as the window narrows. What changes is where it stands:
   beside the headline while there is room next to it, then down on the hero's
   floor underneath it, and finally in the flow as the last row of the stack.
   It only goes away when the viewport is too short to hold anything at all. */
/* Between the phone stack and the full desktop layout the desk comes OUT of
   absolute positioning entirely. Anchored to the hero's floor it drifted to
   the bottom-left corner of a tall window — correct by the numbers, and far
   too low to belong to the composition. As a flow row under the headline it
   travels with the type at every height. */
@media (max-width: 1180px) {
  /* still on the avatar's ground line — that is what keeps it part of the
     composition. Only the size comes down as the column narrows around it. */
  .hm-desk { width: clamp(112px, 13vw, 140px); }
}
/* Under about a thousand pixels the headline widens into the desk's corner,
   so it steps DOWN off the ground line — far enough to clear the last line,
   nowhere near the floor it used to fall to. */
@media (max-width: 1000px) {
  .hm-desk { --desk-lift: calc(-1 * clamp(26px, 5vh, 62px)); }
}
/* Once the reading column is this narrow the marks have to leave the middle
   entirely: the type fills it, the avatar holds the right of it and the desk
   has stepped down into the bottom-left. What is left is the four corners. */
/* ---- two a side, level with the type ----
   LEFT: the Figma glyph above the cursor, both down the free margin beside
   the headline. RIGHT: the artboard, with the pen (which lives in the
   headline itself) below it as the fourth. The loose star is retired here —
   a fifth mark floating on its own is exactly the scatter this fixes. */
@media (max-width: 1000px) {
  /* Sides of the TYPE, not of the window: the avatar owns the right third
     here, and the reading column ends around the halfway mark, so the free
     strip either side of the words is where these belong. The desk holds the
     bottom-left, which is why the left pair sits above it. */
  /* both of these floated well above the type here — dropped to sit level
     with the pill and the first line */
  .hm-mark--figma  { --x: 1.5%; --y: 35%; --s: .9; }
  .hm-mark--cursor { --x: 1.5%; --y: 48%; --s: .84; }
  .hm-mark--art    { --x: 51%;  --y: 35%; --s: .84; }
}
/* Below 860 the hero is barely half a screen tall, so the same percentage
   travels much further: the pair only needs a nudge down to reach the type. */
@media (max-width: 860px) {
  .hm-mark--figma { --y: 27%; }
  .hm-mark--art   { --x: 52%; --y: 27%; }
}
@media (max-width: 640px) {
  /* the phone column is full-width type, so the pairs flank the pill and the
     last line rather than the headline's middle */
  .hm-mark--figma  { --x: 3%;  --y: 31%; --s: .82; }
  .hm-mark--cursor { --x: 3%;  --y: 59%; --s: .78; }
  .hm-mark--art    { --x: 84%; --y: 31%; --s: .8; }
  .hm-mark { opacity: .66; }
}
/* the note sits off the pill's LEFT edge by default; in a narrow column that
   hangs it off the side of the screen, so it moves above the pill instead */
@media (max-width: 900px) {
  .hm-hello {
    right: auto; left: 0;
    bottom: calc(100% + clamp(2px, .8vh, 7px));
    transform: rotate(-6deg);
    transform-origin: 0 100%;
  }
}
@media (max-width: 1180px) and (max-height: 720px) {
  .hm-desk { width: clamp(104px, 11vw, 128px); }
}
/* nothing fits under anything at this height — the hero is barely taller than
   its own type */
@media (max-height: 520px) {
  .hm-desk { display: none; }
}
@media (prefers-reduced-motion: reduce) {
  .hm-folder__doc { transition: none; }
}

/* ==========================================================
   The lede — one first-person sentence between the marks and the
   work, set at the footer intro's size so the two bookends match.
   ========================================================== */
.hm-lede {
  max-width: 1240px;
  margin: 0 auto;
  padding: clamp(90px, 14vh, 170px) 28px 0;
  text-align: center;
}
.hm-lede__text {
  margin: 0 auto;
  max-width: 900px;
  font-size: clamp(26px, 3.2vw, 36px);
  font-weight: 400;
  letter-spacing: -.04em;
  line-height: 1.32;
  color: var(--hm-ink);
  text-wrap: balance;
}
.hm-lede__text .hm-inl {
  display: inline-grid; place-items: center;
  width: 1.35em; height: 1.35em;
  border-radius: .38em;
  vertical-align: -.32em;
  margin: 0 .12em;
  background: var(--hm-dark);
  transform: rotate(6deg);
}
.hm-lede__text .hm-inl svg { width: .72em; height: .72em; }

/* ==========================================================
   Featured projects — a column of statements.

   Each row is a claim in the first person with its credit under it
   on one side, and the artwork on the other; the sides swap on
   every other row so the eye keeps crossing the page.

   At rest a row is bare: artwork with its own soft shadow, nothing
   else. The hand-drawn frame, the loose tick marks and the sparkle
   belong to hover, and hovering ANY part of the row — the writing
   included — brings them in together.
   ========================================================== */
.hm-projects { padding: clamp(110px, 16vh, 200px) 0 clamp(40px, 7vh, 90px); }

/* without the doodle and the pill the header is just the middle column */
/* the arrow is absolutely positioned, so it takes no room of its own — the
   header has to hold the space it hangs into */
.hm-sechead--plain { grid-template-columns: minmax(0, 1fr); padding-bottom: clamp(104px, 14vh, 146px); }
.hm-sechead--plain .hm-sechead__mid { grid-column: 1; }
.hm-sechead--plain .hm-sechead__title {
  font-size: clamp(36px, 3.6vw, 50px);
  letter-spacing: -.045em;
  margin-bottom: clamp(14px, 2vh, 22px);
}

/* the arrow down into the work — one looping stroke, drawn and redrawn.
   pathLength="100" on the path means the dash pattern is in percent, so the
   same two rules work at any size without ever measuring the geometry.

   It hangs off the LEFT of the phrase, not the centre of the block: the pen
   finishes the words and then carries straight on down. */
.hm-sechead__hold { position: relative; display: inline-block; }
.hm-down {
  position: absolute;
  /* mirrored so the loop opens away from the words, and leaned over so the
     stroke reads as carrying on out of the phrase rather than dropping off
     the end of it */
  transform: scaleX(-1) rotate(13deg);
  transform-origin: 50% 0;
  /* it sets off from BESIDE the phrase, level with the middle of the H,
     rather than from underneath it */
  right: calc(100% + 2px);
  /* the em pull-up lands the stroke's first point on the title's own midline
     at any size — the path does not begin at the very top of its box */
  top: calc(50% - .25em);
  width: clamp(34px, 3.4vw, 46px);
  height: auto;
  color: var(--hm-ink);
  opacity: .75;
}
.hm-down__line {
  stroke-dasharray: 100;
  animation: hm-down-draw 2.5s var(--hm-ease) infinite;
}
.hm-down__tip {
  stroke-dasharray: 100;
  animation: hm-down-tip 2.5s var(--hm-ease) infinite;
}
@keyframes hm-down-draw {
  0%        { stroke-dashoffset: 100; opacity: 0; }
  6%        { opacity: 1; }
  52%, 84%  { stroke-dashoffset: 0; opacity: 1; }
  100%      { stroke-dashoffset: 0; opacity: 0; }
}
@keyframes hm-down-tip {
  0%, 48%   { stroke-dashoffset: 100; opacity: 0; }
  52%       { opacity: 1; }
  62%, 84%  { stroke-dashoffset: 0; opacity: 1; }
  100%      { stroke-dashoffset: 0; opacity: 0; }
}
@media (prefers-reduced-motion: reduce) {
  .hm-down__line, .hm-down__tip { animation: none; stroke-dashoffset: 0; }
}

.pj {
  list-style: none;
  margin: 0; padding: 0 28px;
  max-width: 1240px; margin-inline: auto;
  display: flex; flex-direction: column;
  gap: clamp(70px, 11vh, 150px);
}

.pj-item {
  position: relative;
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.06fr);
  align-items: center;
  gap: clamp(28px, 5vw, 84px);
  /* the pointer badge does the talking, so the arrow is hidden across the row */
  cursor: none;
  outline: none;
}
.pj-item * { cursor: none; }
/* every other row flips: artwork first, writing second — done with `order`
   rather than a second markup order so the reading order stays claim-then-art */
.pj-row:nth-child(even) .pj-item__copy  { order: 2; }
.pj-row:nth-child(even) .pj-item__media { order: 1; }
.pj-item:focus-visible { outline: 2.5px solid var(--hm-ink); outline-offset: 14px; border-radius: 18px; }

/* ---------- the writing ---------- */
.pj-item__copy { min-width: 0; }
.hm-page .pj-item__claim,
.pj-item__claim {
  margin: 0;
  /* the page's own voice is 400 at -.04em — anything heavier reads as a
     different typeface even though it is the same DM Sans */
  font-size: clamp(21px, 2.1vw, 31px);
  font-weight: 400;
  letter-spacing: -.04em;
  line-height: 1.2;
  color: var(--hm-ink);
  text-wrap: balance;
}
.hm-page .pj-item__meta,
.pj-item__meta {
  margin: clamp(14px, 1.7vw, 22px) 0 0;
  font-size: clamp(13.5px, 1.1vw, 16px);
  font-weight: 400;
  letter-spacing: -.02em;
  line-height: 1.3;
  color: var(--hm-gray-2);
  transition: color .35s var(--hm-ease);
}
.pj-item__year { font-variant-numeric: tabular-nums; }
.pj-item__bar { padding: 0 .12em; opacity: .55; }
.pj-item:hover .pj-item__meta,
.pj-item:focus-visible .pj-item__meta { color: var(--hm-gray-3); }

/* ---------- recognition ----------
   The marks sit under the credit line, at the foot of the copy: they are the
   last thing the entry says, not a badge stuck on the artwork. Composited
   with multiply so whatever white the award art carries drops into the paper
   rather than sitting on a plate of its own. */
.pj-item__awards {
  display: flex; align-items: flex-end;
  gap: clamp(10px, 1.1vw, 16px);
  margin: clamp(16px, 1.9vw, 24px) 0 0;
  padding: 0; list-style: none;
}
/* All three award marks are 560px tall at source, so a fixed HEIGHT gives them
   one optical scale whether the art is tall (Indigo) or nearly square (The
   Rookies). They carry small type, so they cannot be shrunk to a footnote. */
.pj-award {
  display: block;
  height: clamp(64px, 5.6vw, 86px);
  transition: transform .4s var(--hm-ease);
}
.pj-award img {
  display: block;
  width: auto; height: 100%;
  object-fit: contain;
  mix-blend-mode: multiply;
  /* black and white at rest, colour when the row is under the pointer — the
     same reveal the client marks in the folder use */
  filter: grayscale(1);
  opacity: .78;
  transition: filter .4s var(--hm-ease), opacity .4s var(--hm-ease);
}
.pj-item:hover .pj-award img,
.pj-item:focus-visible .pj-award img { filter: none; opacity: 1; }
.pj-item:hover .pj-award,
.pj-item:focus-visible .pj-award { transform: translateY(-2px); }

/* ---------- the artwork ----------
   At rest each row sits a degree or two off square, the way a photo left on a
   desk does; hovering squares it up. The tilt lives on the MEDIA wrapper, not
   the stage, so the drawn frame turns with the picture instead of sliding off
   it. Rows lean opposite ways so the column never looks like a systematic
   error. */
.pj-item__media {
  position: relative; min-width: 0;
  --tilt: -1.5deg;
  transform: rotate(var(--tilt));
  transition: transform .55s var(--hm-ease);
}
.pj-row:nth-child(even) .pj-item__media { --tilt: 1.7deg; }
.pj-row:nth-child(3n) .pj-item__media { --tilt: -1.1deg; }
.pj-item:hover .pj-item__media,
.pj-item:focus-visible .pj-item__media { transform: rotate(0deg); }
.pj-item__stage {
  position: relative;
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  grid-template-rows: minmax(0, 1fr);
  height: clamp(210px, 24vw, 340px);
  padding: clamp(10px, 1.2vw, 18px);
  border-radius: clamp(16px, 1.8vw, 24px);
  background: #fff;
  /* the resting state is this and nothing else */
  box-shadow: 0 10.43px 58px rgba(94, 94, 94, .17);
  transition: box-shadow .45s var(--hm-ease), transform .5s var(--hm-ease);
}
.pj-item:hover .pj-item__stage,
.pj-item:focus-visible .pj-item__stage {
  transform: translateY(-5px);
  box-shadow: 0 18px 64px rgba(94, 94, 94, .24);
}
/* artwork that carries its own ground fills the card instead of sitting in a
   padded well: no mount, no letterbox, corners clipped to the card.

   Only the STILL is covered. Where a row also has an animated layer, that
   layer is a mockup floating on transparency — cropping it to fill would cut
   the phones off, so it keeps object-fit: contain and floats over the top. */
.pj-item[data-fill] .pj-item__stage { padding: 0; overflow: hidden; }
.pj-item[data-fill] .pj-item__img--still {
  object-fit: cover;
  /* The well is wider than this artwork, so covering it trims top and bottom.
     WHICH part it keeps depends on the picture: the phone mockups carry their
     interest low and want a downward bias, a title card carries it at the top
     and would lose the wordmark. Each row can set --focus for itself. */
  object-position: 50% var(--focus, 62%);
}
.pj-item[data-fill] .pj-item__img--anim { object-fit: contain; }
.pj-item__img {
  grid-area: 1 / 1; display: block;
  width: 100%; height: 100%; min-width: 0; min-height: 0;
  object-fit: contain;
  transition: opacity .45s var(--hm-ease);
}
/* the swap waits on .is-anim, which JS sets only once the animation has
   actually decoded. Crossfading on :hover alone means the still can leave
   before the WebP has arrived, and the well flashes white in between. */
.pj-item__img--anim { opacity: 0; }
.pj-item.is-anim:hover .pj-item__img--anim,
.pj-item.is-anim:focus-visible .pj-item__img--anim { opacity: 1; }
.pj-item.is-anim:hover .pj-item__img--still,
.pj-item.is-anim:focus-visible .pj-item__img--still { opacity: 0; }

/* Artwork that carries its own transparency needs no card behind it — the
   white well and its shadow were what showed through the transparent parts.
   The paper is its ground instead. */
.pj-item[data-bare] .pj-item__stage,
.pj-item[data-bare]:hover .pj-item__stage,
.pj-item[data-bare]:focus-visible .pj-item__stage {
  background: none;
  box-shadow: none;
}

/* ---------- the hand-drawn frame ----------
   Four strokes, one per side, each carrying its two corners round with it.
   At rest they overlap at the corners; the animation walks each one out
   along its own axis, so the corners open into gaps and close again — the
   frame is drawn and redrawn rather than sitting still.

   The paths are GENERATED in JS at the box's real pixel size. A single path
   scaled non-uniformly smears both the nib weight and the corner radius,
   which is exactly what gives a fake hand-drawn line away.

   The frame sits at inset 0: flush to the artwork, no gap. The corner marks
   overhang on their own, which is what `overflow: visible` on the svg is for.
   ========================================================== */
.pj-frame {
  position: absolute;
  inset: 0;
  /* ABOVE the artwork. The stage is a positioned later sibling, so without
     this it paints over the frame — the inner half of every stroke vanished
     under the white card and the top stroke went completely, wiped out by the
     stage's own 58px shadow. */
  z-index: 3;
  pointer-events: none;
  color: var(--hm-ink);
  opacity: 0;
  transition: opacity .18s var(--hm-ease);
}
.pj-item:hover .pj-frame,
.pj-item:focus-visible .pj-frame { opacity: 1; }
.pj-frame svg { display: block; width: 100%; height: 100%; overflow: visible; }

/* one side of the box, corners included. pathLength="100" on every side means
   the draw-on is in percent, so four paths of four different lengths all
   arrive together without anyone measuring anything. */
.pjf__side {
  fill: none; stroke: currentColor;
  stroke-width: 2.6; stroke-linecap: round; stroke-linejoin: round;
  stroke-dasharray: 100;
  stroke-dashoffset: 100;
  transform-box: fill-box; transform-origin: center;
}
/* the animations live on the HOVER selector, not the element, so leaving the
   row tears them down and the next hover draws the frame on from nothing
   again — a restart you cannot get from a paused animation */
.pj-item:hover .pjf__side,
.pj-item:focus-visible .pjf__side {
  animation: pjf-draw .34s var(--hm-ease) forwards,
             pjf-part 3.2s ease-in-out infinite alternate;
}
@keyframes pjf-draw {
  from { stroke-dashoffset: 100; }
  to   { stroke-dashoffset: 0; }
}
/* --dx/--dy point outward along each side's own axis, so the top rides up,
   the right rides out, and the corners they share come apart between them */
@keyframes pjf-part {
  from { transform: translate(0, 0) rotate(0deg); }
  to   { transform: translate(var(--dx, 0px), var(--dy, 0px)) rotate(var(--dr, 0deg)); }
}

/* ---- the marks ----
   Loose ticks and scattered dots off the TOP-LEFT, four-pointed stars off the
   TOP-RIGHT: one kind of mark per corner, so neither corner is busy. All of
   them pulse — scaling up and down and flashing with it — each on its own
   clock, written per-element by JS so no two ever beat together. */
.pjf__tick,
.pjf__star {
  transform-box: fill-box; transform-origin: center;
}
.pjf__tick {
  fill: none; stroke: currentColor;
  stroke-width: 2.4; stroke-linecap: round;
}
.pjf__star { fill: currentColor; stroke: none; }

.pj-item:hover .pjf__tick,
.pj-item:focus-visible .pjf__tick {
  animation: pjf-mark-in .3s var(--hm-spring) backwards,
             pjf-pulse var(--beat, 1.5s) ease-in-out .3s infinite;
}
.pj-item:hover .pjf__star,
.pj-item:focus-visible .pjf__star {
  animation: pjf-star-in .38s var(--hm-spring) backwards,
             pjf-flash var(--beat, 1.5s) ease-in-out .38s infinite;
}

@keyframes pjf-mark-in {
  from { opacity: 0; transform: scale(.35); }
  to   { opacity: 1; transform: scale(1); }
}
@keyframes pjf-star-in {
  from { opacity: 0; transform: scale(.15) rotate(-50deg); }
  to   { opacity: 1; transform: scale(1) rotate(0deg); }
}
/* the pulse: up and down in size, and the light going with it */
@keyframes pjf-pulse {
  0%, 100% { transform: scale(.72); opacity: .5; }
  50%      { transform: scale(1.18); opacity: 1; }
}
@keyframes pjf-flash {
  0%, 100% { transform: scale(.7) rotate(-10deg);  opacity: .45; }
  50%      { transform: scale(1.22) rotate(6deg);  opacity: 1; }
}

@media (prefers-reduced-motion: reduce) {
  .pj-item:hover .pjf__side,
  .pj-item:hover .pjf__tick,
  .pj-item:hover .pjf__star { animation: none; stroke-dashoffset: 0; opacity: .9; }
}

/* ---------- the same frame, on the All Projects cards ----------
   Border only: no ticks, no stars. The grid there is dense enough that the
   corner marks of one card would land inside its neighbour. The stroke
   styles themselves are shared with the homepage rows — only the trigger and
   the marks differ. */
.wk-card { position: relative; }
.wk-frame {
  position: absolute;
  inset: clamp(-14px, -1.2vw, -8px);
  z-index: 4;
  pointer-events: none;
  color: var(--hm-ink);
  opacity: 0;
  transition: opacity .18s var(--hm-ease);
}
.wk-card:hover .wk-frame,
.wk-card:focus-within .wk-frame { opacity: 1; }
.wk-frame svg { display: block; width: 100%; height: 100%; overflow: visible; }
.wk-card:hover .pjf__side,
.wk-card:focus-within .pjf__side {
  animation: pjf-draw .34s var(--hm-ease) forwards,
             pjf-part 3.2s ease-in-out infinite alternate;
}
@media (hover: none) { .wk-frame { display: none; } }
@media (prefers-reduced-motion: reduce) {
  .wk-card:hover .pjf__side { animation: none; stroke-dashoffset: 0; }
}

/* ---------- view all projects ---------- */
.pj-all__wrap {
  display: flex; justify-content: center;
  padding: clamp(70px, 11vh, 140px) 28px 0;
}
.pj-all {
  position: relative;
  display: inline-flex; align-items: center; gap: 14px;
  margin: 0; padding: 10px 6px;
  border: 0; background: none; cursor: pointer;
  color: var(--hm-ink);
  font-family: var(--hm-font);
  transition: color .3s var(--hm-ease), transform .45s var(--hm-spring);
}
.pj-all:hover { transform: translateX(4px); }
.pj-all:focus-visible { outline: 2.5px solid var(--hm-ink); outline-offset: 8px; border-radius: 12px; }
.pj-all__arrow { width: clamp(30px, 3vw, 40px); height: auto; flex: none; }
.pj-all__label {
  position: relative;
  font-size: clamp(20px, 2vw, 28px);
  font-weight: 500;
  letter-spacing: .01em;
}
/* the underline is drawn on, not faded in: dashoffset from 100 to 0 over a
   pathLength-normalised path, so it reads as a pen going under the words.
   non-scaling-stroke keeps the nib at 2.2px however wide the label gets. */
.pj-all__rule {
  position: absolute;
  left: -2px; right: -4px; top: calc(100% + 1px);
  width: auto; height: clamp(9px, .9vw, 12px);
  overflow: visible;
  color: var(--hm-ink);
}
.pj-all__rule path {
  stroke-dasharray: 100;
  stroke-dashoffset: 100;
  transition: stroke-dashoffset .5s var(--hm-ease);
}
.pj-all:hover .pj-all__rule path,
.pj-all:focus-visible .pj-all__rule path { stroke-dashoffset: 0; }
@media (prefers-reduced-motion: reduce) {
  .pj-all__rule path { transition: none; }
}
/* the sparks ride above the tail of the phrase and keep a slow idle wobble —
   the same restless energy as the doodles elsewhere on the page */
.pj-all__spark {
  position: absolute;
  right: -26px; top: clamp(-20px, -1.3vw, -14px);
  width: clamp(24px, 2.4vw, 34px); height: auto;
  color: currentColor;
  pointer-events: none;
}
.pj-all__tick,
.hm-spark__tick {
  transform-box: fill-box; transform-origin: 50% 100%;
  animation: pj-spark 2.6s ease-in-out infinite;
}
.pj-all__tick--b, .hm-spark__tick--b { animation-delay: .25s; animation-duration: 3.1s; }
.pj-all__tick--c, .hm-spark__tick--c { animation-delay: .5s;  animation-duration: 2.9s; }
@keyframes pj-spark {
  0%, 100% { transform: scale(.82) rotate(-5deg); opacity: .55; }
  50%      { transform: scale(1.1) rotate(5deg);  opacity: 1; }
}
.pj-all:hover .pj-all__tick,
.hm-licard:hover .hm-spark__tick { animation-duration: 1.1s; }
@media (prefers-reduced-motion: reduce) {
  .pj-all__tick, .hm-spark__tick { animation: none; opacity: .8; }
}

/* ---------- the pointer badge ----------
   An arrow and the length of the read, or a padlock and the reason there
   isn't one. It grows out of the same round dark chip the arrow used to be,
   so a row that can't be opened still feels answered rather than dead. */
.cs-cursor {
  position: fixed; top: 0; left: 0; z-index: 130;
  display: flex; align-items: center; gap: 8px;
  height: 42px; padding: 0 16px 0 13px;
  border-radius: 999px;
  background: var(--hm-ink); color: #fff;
  white-space: nowrap;
  box-shadow: 0 12px 28px -12px rgba(22, 21, 21, .5);
  translate: calc(var(--cx, -300px) + 16px) calc(var(--cy, -300px) + 14px);
  opacity: 0; transform: scale(.7); transform-origin: 0 0;
  transition: opacity .22s var(--hm-ease), transform .32s var(--hm-spring),
              background .3s var(--hm-ease);
  pointer-events: none;
}
.cs-cursor.is-on { opacity: 1; transform: scale(1); }
.cs-cursor__ico { width: 19px; height: 19px; display: none; flex: none; }
.cs-cursor__label {
  font-family: var(--hm-font);
  font-size: 14px; font-weight: 600; letter-spacing: -.015em;
}
.cs-cursor:not(.is-locked) .cs-cursor__ico--go { display: block; }
.cs-cursor.is-locked .cs-cursor__ico--lock { display: block; }
.cs-cursor.is-locked { background: var(--hm-dark); }
.cs-cursor.is-empty { padding: 0; width: 42px; justify-content: center; }
@media (hover: none) { .cs-cursor { display: none; } }

@media (max-width: 900px) {
  .pj { gap: clamp(56px, 8vh, 90px); }
  .pj-item {
    grid-template-columns: minmax(0, 1fr);
    gap: clamp(20px, 4vw, 30px);
    cursor: pointer;
  }
  .pj-item * { cursor: inherit; }
  /* stacked, the flip has nothing to flip — writing always leads */
  .pj-row:nth-child(even) .pj-item__copy  { order: 0; }
  .pj-row:nth-child(even) .pj-item__media { order: 0; }
  .pj-item__stage { height: clamp(190px, 44vw, 280px); }
  /* touch has no hover, so the frame simply never comes */
  .pj-frame { display: none; }
}
@media (max-width: 640px) {
  .pj { padding-inline: 20px; }
  .hm-lede { padding-inline: 20px; }
}

/* ==========================================================
   What others say — the turn from the work to the people who
   were in the room for it. The lede carries a speech bubble on
   the phrase that matters; the track below slides by itself,
   clipped so the quotes seem to come out of a drawer.
   ========================================================== */
.hm-say__head {
  max-width: 1160px;
  margin: 0 auto;
  padding: 0 28px clamp(44px, 6vh, 70px);
  text-align: center;
}
/* the same voice as the lede at the top and the intro at the foot: 400 at
   -.04em, clamp(26 → 36). Anything heavier or larger reads as a different
   typeface even though it is the same DM Sans. */
.hm-page .hm-say__lede,
.hm-say__lede {
  margin: 0 auto;
  max-width: 22ch;
  font-size: clamp(26px, 3.2vw, 36px);
  font-weight: 400;
  letter-spacing: -.04em;
  line-height: 1.32;
  text-transform: none;
  color: var(--hm-ink);
  text-wrap: balance;
}
/* the bubble sits on the baseline like a word, not like a box dropped on top:
   the padding is in em so it tracks the type at every clamp step */
.hm-bubble {
  position: relative;
  display: inline-block;
  margin: 0 .06em;
  padding: .1em .42em .16em;
  border-radius: .62em;
  background: var(--hm-gold);
  color: var(--hm-ink);
  font-weight: 400;
  /* the quoted phrase is an aside, so it steps back a size rather than
     shouting louder than the sentence carrying it */
  font-size: .8em;
  letter-spacing: -.03em;
  vertical-align: .1em;
}
.hm-bubble__tail {
  position: absolute;
  left: .5em; bottom: -.24em;
  width: .42em; height: .32em;
  background: inherit;
  clip-path: polygon(0 100%, 100% 0, 12% 0);
}

/* ==========================================================
   How I work — the hinge. The underline and the pencil belong to
   the phrase, so they are drawn INSIDE it and scale with the type;
   the scribble is loose and rides the corner of the block.
   ========================================================== */
/* close to the quotes it is answering, and well clear of what follows */
.hm-how {
  max-width: 1160px;
  margin: 0 auto;
  padding: clamp(18px, 2.4vh, 34px) 28px 0;
  text-align: center;
}
.hm-page .hm-how__text,
.hm-how__text {
  position: relative;
  margin: 0 auto;
  max-width: 22ch;
  font-size: clamp(26px, 3.2vw, 36px);
  font-weight: 400;
  letter-spacing: -.04em;
  line-height: 1.32;
  color: var(--hm-ink);
  text-wrap: balance;
}
.hm-how__mark { position: relative; display: inline-block; white-space: nowrap; }
/* the same drawn rule the "View all projects" link carries: ink, not grey,
   at the same nib weight and the same curve */
.hm-how__rule {
  position: absolute;
  left: -.04em; right: -.1em; top: calc(100% + .02em);
  width: auto; height: .2em;
  overflow: visible;
  color: var(--hm-ink);
}
@media (max-width: 640px) {
  .hm-say__head, .hm-how { padding-inline: 20px; }
}

/* ==========================================================
   The drawer itself
   ========================================================== */
.hm-say { padding: clamp(80px, 12vh, 160px) 0 0; }

.hm-drawer {
  position: relative; overflow: hidden;
  padding: 6px 0 10px;
  /* the paper closes over both ends of the track */
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 9%, #000 91%, transparent);
          mask-image: linear-gradient(90deg, transparent, #000 9%, #000 91%, transparent);
}
.hm-drawer__track {
  display: flex; gap: clamp(16px, 2vw, 26px);
  width: max-content; padding-inline: clamp(20px, 4vw, 60px);
  animation: hmSlide 74s linear infinite;
}
.hm-drawer:hover .hm-drawer__track { animation-play-state: paused; }
@keyframes hmSlide {
  from { transform: translate3d(0, 0, 0); }
  to   { transform: translate3d(-50%, 0, 0); }   /* the set is duplicated by JS */
}

.hm-said {
  flex: none; width: clamp(258px, 26vw, 340px); margin: 0;
  display: flex; flex-direction: column; justify-content: space-between; gap: 20px;
  padding: clamp(22px, 2.2vw, 30px);
  background: #fff; border: 1.4px solid var(--hm-line-soft);
  border-radius: clamp(16px, 1.8vw, 22px);
  transition: transform .45s var(--hm-spring), box-shadow .45s var(--hm-ease),
              border-color .3s var(--hm-ease);
}
.hm-said:hover {
  transform: translateY(-6px);
  border-color: var(--hm-line);
  box-shadow: 0 20px 44px -26px rgba(22, 21, 21, .26);
}
.hm-page .hm-said p {
  margin: 0; font-size: clamp(15px, 1.2vw, 16.5px); line-height: 1.6;
  letter-spacing: -.015em; color: var(--hm-ink);
}
.hm-said figcaption { display: grid; gap: 1px; }
.hm-said figcaption b {
  font-size: 14px; font-weight: 500; letter-spacing: -.02em; color: var(--hm-ink);
}
.hm-said figcaption span { font-size: 12.5px; letter-spacing: -.01em; color: var(--hm-gray-2); }

@media (prefers-reduced-motion: reduce) {
  .hm-drawer__track { animation: none; }
  .hm-drawer { -webkit-mask-image: none; mask-image: none; }
  .hm-drawer__track { overflow-x: auto; width: auto; }
  .pj-item__stage { transition: none; }
}

.hm-foot {
  max-width: 1160px;
  margin: 0 auto;
  padding: clamp(150px, 22vh, 270px) 28px clamp(30px, 5vh, 60px);
}
/* the copyright line closes the page after the testimonials */
.hm-legal { max-width: 1160px; margin: 0 auto; padding: 0 28px clamp(40px, 6vh, 70px); }

.hm-foot__main {
  display: grid;
  grid-template-columns: 1.5fr 1fr;
  align-items: center;
  gap: clamp(30px, 5vw, 70px);
  margin-bottom: clamp(40px, 7vh, 80px);
}

.hm-foot__intro { position: relative; padding-top: 34px; }
.hm-foot__cursor {
  position: absolute;
  top: 0;
  left: -6px;
  transform: rotate(-6deg);
}
.hm-foot__text {
  font-size: clamp(26px, 3.2vw, 36px);
  font-weight: 400;
  letter-spacing: -.04em;
  line-height: 1.32;
  color: var(--hm-ink);
  margin: 0 0 30px;
}
.hm-foot__text .hm-inl {
  display: inline-grid;
  place-items: center;
  width: 1.35em;
  height: 1.35em;
  border-radius: .38em;
  vertical-align: -0.32em;
  margin: 0 .12em;
  background: var(--hm-dark);
  transform: rotate(6deg);
}
.hm-foot__text .hm-inl svg { width: .72em; height: .72em; }
.hm-foot__text .hm-inl--light {
  background: #fff;
  border: 1.4px solid var(--hm-line-soft);
  transform: rotate(-5deg);
}
.hm-foot__actions { display: flex; gap: 12px; flex-wrap: wrap; }

/* LinkedIn card */
/* the marks ride off the card's top-right corner, outside its rounded edge,
   and turn with it — the card is rotated, so they have to be its children
   rather than the column's */
.hm-licard__spark {
  position: absolute;
  right: -14px; top: -16px;
  width: clamp(26px, 2.6vw, 36px); height: auto;
  color: var(--hm-ink);
  pointer-events: none;
}
.hm-licard {
  position: relative;
  justify-self: end;
  width: min(330px, 100%);
  background: #fff;
  border-radius: 32px;
  box-shadow: 0 10.43px 58px rgba(94, 94, 94, .17);
  padding: 18px 18px 16px;
  transform: rotate(-6deg);
  transition: transform .6s var(--hm-spring);
}
.hm-licard:hover { transform: rotate(-2deg) translateY(-6px); }
.hm-licard__photo {
  width: 100%;
  height: 240px;
  object-fit: contain;
  object-position: center bottom;
  display: block;
  background:
    radial-gradient(120% 90% at 50% 100%, rgba(74, 119, 255, .16), rgba(74, 119, 255, 0) 70%);
  border-radius: 20px;
}
.hm-licard__row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  margin-top: 14px;
}
.hm-licard__id {
  display: flex;
  align-items: center;
  gap: 10px;
  min-width: 0;
}
.hm-licard__id img {
  width: 40px;
  height: 40px;
  border-radius: 50%;
  object-fit: cover;
  object-position: center 20%;
  background: #eef1ff;
}
.hm-licard__id b {
  display: block;
  font-size: 14px;
  font-weight: 500;
  letter-spacing: -.02em;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.hm-licard__id span {
  display: block;
  font-size: 13px;
  color: #787878;
}
.hm-licard__btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  flex: none;
  font-size: 14px;
  color: #fff;
  background: #000;
  border-radius: 16px;
  padding: 10px 16px;
  box-shadow: 0 3px 10px rgba(0, 0, 0, .25);
  text-decoration: none;
  transition: transform .45s var(--hm-spring);
}
.hm-licard__btn:hover { transform: scale(1.06); }

/* contact rows */
.hm-foot__contact {
  background: none;
  border: 0;
  border-radius: 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  justify-content: center;
}
/* the same row, lifted out of the footer to sit just above the copyright */
.hm-contact-end {
  max-width: 1160px;
  margin: 0 auto clamp(26px, 4vh, 44px);
  width: calc(100% - 56px);
}
.hm-foot__group {
  background: none;
  border: 0;
  border-radius: 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}
.hm-chip {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-size: 14px;
  font-weight: 500;
  letter-spacing: -.04em;
  color: #525252;
  background: #fff;
  border: .65px solid var(--hm-line-soft);
  border-radius: 10px;
  padding: 10px 16px;
  text-decoration: none;
  transition: transform .45s var(--hm-spring), box-shadow .45s var(--hm-spring);
}
.hm-chip:hover { transform: translateY(-2px); box-shadow: 0 8px 18px rgba(0, 0, 0, .08); }
.hm-chip svg { width: 15px; height: 15px; flex: none; }

.hm-foot__legal {
  text-align: center;
  font-size: 13px;
  color: #a8a8a8;
  padding-top: 34px;
  /* clear the fixed dock so the last line is never sitting under it */
  padding-bottom: 92px;
}

/* ==========================================================
   Fixed bottom dock — the site's persistent menu
   ========================================================== */

.hm-dock {
  position: fixed;
  left: 50%;
  bottom: 22px;
  transform: translateX(-50%);
  z-index: 110;   /* over .works (95) and .hm-about (92), under .study (140) */
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 9px;
  background: rgba(255, 255, 255, .82);
  backdrop-filter: blur(22px) saturate(1.6);
  -webkit-backdrop-filter: blur(22px) saturate(1.6);
  border: 1.4px solid rgba(22, 21, 21, .07);
  border-radius: 22px;
  box-shadow: 0 14px 40px rgba(22, 21, 21, .14);
}

.hm-dock__item {
  position: relative;
  display: grid;
  place-items: center;
  width: 50px;
  height: 50px;
  flex: none;
  border-radius: 14px;
  background: var(--hm-card);
  color: var(--hm-dark);
  text-decoration: none;
  transition: transform .45s var(--hm-spring), background .25s ease, color .25s ease;
}
.hm-dock__item svg { width: 23px; height: 23px; }
.hm-dock__item:hover { transform: translateY(-8px) scale(1.06); background: #e9e9e9; }
.hm-dock__item.is-on { background: var(--hm-ink); color: #fff; }

.hm-dock__item--go { background: var(--hm-ink); color: #fff; }
.hm-dock__item--go:hover { background: var(--hm-blue); }

.hm-dock__sep {
  width: 1.4px;
  height: 26px;
  background: var(--hm-line-soft);
  margin: 0 2px;
}

/* hover label */
.hm-dock__item::after {
  content: attr(data-label);
  position: absolute;
  bottom: calc(100% + 12px);
  left: 50%;
  transform: translateX(-50%) translateY(6px);
  padding: 7px 12px;
  border-radius: 9px;
  background: var(--hm-ink);
  color: #fff;
  font-family: var(--hm-font);
  font-size: 13px;
  font-weight: 500;
  letter-spacing: -.02em;
  white-space: nowrap;
  opacity: 0;
  pointer-events: none;
  transition: opacity .25s ease, transform .35s var(--hm-spring);
}
.hm-dock__item:hover::after { opacity: 1; transform: translateX(-50%) translateY(0); }

/* The dock stays put across home, the archive and about — it is the site's
   navigation, not the homepage's. Only a case study sends it away, because it
   carries its own floating section bar down there and the two would collide. */
body.hm-study .hm-dock { opacity: 0; pointer-events: none; transform: translateX(-50%) translateY(20px); }
.hm-dock { transition: opacity .3s ease, transform .45s var(--hm-spring); }

/* ==========================================================
   About me — full-page overlay in the homepage language
   ========================================================== */

.hm-about {
  position: fixed;
  inset: 0;
  z-index: 92;
  overflow-y: auto;
  /* the tilted portrait and its badges sit outside the card box — clip them
     rather than letting them widen the page on small screens */
  overflow-x: hidden;
  -webkit-overflow-scrolling: touch;
  overscroll-behavior: contain;
  background: var(--hm-bg);
  font-family: var(--hm-font);
  color: var(--hm-ink);
  --ab-gut: clamp(20px, 5vw, 56px);
}
.hm-about[hidden] { display: none; }

/* same close as the archive's: white with a hairline, inverting to ink on hover */
.hm-about__close {
  flex: none;
  width: 46px;
  height: 46px;
  display: grid;
  place-items: center;
  border-radius: 50%;
  border: 1.4px solid var(--hm-line);
  background: #fff;
  color: var(--hm-ink);
  font-size: 15px;
  padding: 0;
  cursor: pointer;
  transition: background .25s var(--hm-ease), color .25s var(--hm-ease),
              transform .35s var(--hm-spring);
}
.hm-about__close:hover { background: var(--hm-ink); color: #fff; transform: rotate(90deg); }

/* the About header, wearing the same fade as the homepage's and the archive's,
   so the paper scrolls up under it the same way. (.hm-about__inner is sized in
   about.css, which loads after this file.) */
.ab__top {
  background: linear-gradient(to bottom, var(--hm-bg) 68%, rgba(251, 251, 251, 0));
}

/* hero */
.hm-about__hero {
  display: grid;
  grid-template-columns: 340px 1fr;
  align-items: center;
  gap: clamp(30px, 5vw, 68px);
  margin-bottom: clamp(50px, 8vh, 90px);
}

.hm-about__portrait {
  position: relative;
  border-radius: 34px;
  background:
    radial-gradient(120% 88% at 50% 100%, rgba(74, 119, 255, .18), rgba(74, 119, 255, 0) 70%),
    #fff;
  box-shadow: 0 10.43px 58px rgba(94, 94, 94, .17);
  padding: 16px 16px 0;
  transform: rotate(-4deg);
  transition: transform .6s var(--hm-spring);
}
.hm-about__portrait:hover { transform: rotate(-1deg) translateY(-6px); }
.hm-about__portrait img {
  display: block;
  width: 100%;
  height: 300px;
  object-fit: contain;
  object-position: center bottom;
}
.hm-about__badge {
  position: absolute;
  font-size: 13px;
  font-weight: 500;
  letter-spacing: -.02em;
  white-space: nowrap;
  background: #fff;
  border: 1.4px solid var(--hm-line-soft);
  border-radius: 100px;
  padding: 8px 14px;
  box-shadow: 0 10px 22px rgba(0, 0, 0, .09);
}
.hm-about__badge--a { top: 16%; left: -18%; transform: rotate(-5deg); }
.hm-about__badge--b {
  bottom: 14%;
  right: -14%;
  transform: rotate(4deg);
  background: var(--hm-blue);
  color: #fff;
  border-color: transparent;
  box-shadow: 0 10px 22px rgba(74, 119, 255, .3);
}

.hm-about__title {
  font-family: var(--hm-font);
  font-size: clamp(30px, 4.2vw, 46px);
  font-weight: 400;
  letter-spacing: -.04em;
  line-height: 1.12;
  margin: 0 0 22px;
  text-transform: none;
}
.hm-about__title .hm-sel { font-size: inherit; }
.hm-about__title--sm { font-size: clamp(24px, 3.2vw, 36px); max-width: 22ch; }

.hm-about__body {
  font-size: clamp(15px, 1.3vw, 17px);
  line-height: 1.62;
  letter-spacing: -.01em;
  color: var(--hm-gray-3);
  margin: 0 0 16px;
  max-width: 52ch;
}
.hm-about__lead .hm-foot__actions { margin-top: 26px; }

/* stats */
.hm-about__stats {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 16px;
  margin-bottom: clamp(50px, 8vh, 90px);
}
.hm-astat {
  background: var(--hm-card);
  border-radius: 24px;
  padding: 26px;
  text-align: center;
  transition: transform .5s var(--hm-spring);
}
.hm-astat:hover { transform: translateY(-6px); }
.hm-astat b {
  display: block;
  font-size: clamp(34px, 4.4vw, 50px);
  font-weight: 500;
  letter-spacing: -.04em;
  line-height: 1;
  color: var(--hm-blue);
  margin-bottom: 8px;
}
.hm-astat span {
  font-size: 14px;
  letter-spacing: -.02em;
  color: var(--hm-gray-2);
}

/* blocks */
.hm-about__block { margin-bottom: clamp(50px, 8vh, 90px); }
.hm-about__h2 {
  font-family: var(--hm-font);
  font-size: 31px;
  font-weight: 500;
  letter-spacing: -.04em;
  line-height: 1.05;
  text-transform: none;
  color: var(--hm-ink);
  margin: 0 0 26px;
}
.hm-about__cards {
  display: grid;
  grid-template-columns: 1.3fr 1fr 1fr;
  gap: 16px;
}

/* toolkit chips */
.hm-tools-list {
  list-style: none;
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  padding: 0;
  margin: 0;
}
.hm-tools-list li {
  font-size: 15px;
  font-weight: 500;
  letter-spacing: -.02em;
  color: var(--hm-ink);
  background: #fff;
  border: 1.4px solid var(--hm-line-soft);
  border-radius: 100px;
  padding: 11px 20px;
  transition: transform .45s var(--hm-spring), border-color .25s ease, color .25s ease;
}
.hm-tools-list li:hover {
  transform: translateY(-3px);
  border-color: var(--hm-blue);
  color: var(--hm-blue);
}

/* the path — a real sequence, so it is numbered by time not decoration */
.hm-path {
  list-style: none;
  padding: 0;
  margin: 0;
  display: grid;
  gap: 14px;
}
.hm-path li {
  display: grid;
  grid-template-columns: 130px 1fr;
  column-gap: 22px;
  row-gap: 6px;
  align-items: start;
  background: var(--hm-card);
  border-radius: 22px;
  padding: 24px 26px;
  transition: transform .5s var(--hm-spring);
}
.hm-path li:hover { transform: translateX(6px); }
.hm-path__when {
  grid-column: 1;
  grid-row: 1 / span 2;
  font-size: 14px;
  font-weight: 500;
  letter-spacing: -.02em;
  color: #fff;
  background: var(--hm-ink);
  border-radius: 100px;
  padding: 7px 14px;
  justify-self: start;
}
/* title and description share the second column, stacked */
.hm-path h3 { grid-column: 2; grid-row: 1; }
.hm-path p { grid-column: 2; grid-row: 2; }

.hm-path h3 {
  font-family: "Inter", var(--hm-font);
  font-size: 16px;
  font-weight: 700;
  letter-spacing: -.01em;
  color: var(--hm-ink);
  margin: 0 0 6px;
  text-transform: none;
}
.hm-path p {
  font-size: 14.5px;
  line-height: 1.55;
  letter-spacing: -.01em;
  color: var(--hm-gray-3);
  margin: 0;
}

.hm-about__cta {
  text-align: center;
  padding-top: clamp(20px, 4vh, 40px);
}
.hm-about__cta .hm-about__title { margin-inline: auto; }
.hm-about__cta .hm-foot__actions { justify-content: center; }

/* The work archive owns its own paint now — see css/works.css. */

/* ==========================================================
   Case studies — typography only.
   The layout, colour themes and animations of the studies are
   deliberately untouched; this just brings their TYPE onto the
   homepage's DM Sans so the whole site reads as one voice.
   ========================================================== */

.study,
.study__doc,
.study__doc p,
.study .ncs,
.study .ncs p,
.study .ncs li,
.study .ncs cite,
.study .ncs blockquote,
.study .ncs dt,
.study .ncs dd,
.study .ncs h1,
.study .ncs h2,
.study .ncs h3,
.study .ncs h4,
.study .ncs .ncs__nav a,
.study .ncs .ncs__title,
.study .ncs .ncs__h2,
.study .ncs .ncs__h3,
.study .ncs .ncs__lead,
.study .ncs .ncs__kicker,
.study .ncs .ncs__eyebrow,
.study .ncs .ncs__num,
.study .ncs .ncs__divider span,
.study .ncs .ncs__gsearch-text,
.study .ncs .ncs__gsearch-logo {
  font-family: var(--hm-font);
}
/* headings keep their weight/size but pick up the homepage's tight tracking */
.study .ncs h1,
.study .ncs h2,
.study .ncs h3,
.study .ncs .ncs__title,
.study .ncs .ncs__h2,
.study .ncs .ncs__h3,
.study .ncs .ncs__divider span { letter-spacing: -.03em; }

/* ==========================================================
   Focus + reduced motion + responsive
   ========================================================== */

.hm-page a:focus-visible,
.hm-page button:focus-visible {
  outline: 2px solid var(--hm-blue);
  outline-offset: 3px;
  border-radius: 6px;
}

@media (prefers-reduced-motion: reduce) {
  .hm-page *, .hm-page *::before, .hm-page *::after {
    transition-duration: .01ms !important;
    animation-duration: .01ms !important;
  }
}

@media (max-width: 1080px) {
}

/* the portrait needs real estate; below this it'd fight the type, and the
   reveal needs a pointer anyway, so retire it on small/touch */
@media (max-width: 860px) {
  .hm-portrait { display: none; }
  .hm-hero { min-height: 56vh; }
  .hm-clients { padding-inline: 20px; }
  .hm-clients__row { gap: 18px 24px; }
  .hm-clients__row img { max-width: 80px; max-height: 28px; opacity: .62; }
}

@media (max-width: 900px) {
  .hm-foot__main { grid-template-columns: 1fr; }
  .hm-licard { justify-self: center; }
  .hm-sechead { grid-template-columns: 1fr; justify-items: center; text-align: center; row-gap: 14px; }
  .hm-sechead__doodle, .hm-sechead__action { justify-self: center; }

  .hm-about__hero { grid-template-columns: 1fr; justify-items: center; text-align: center; }
  .hm-about__portrait { width: min(300px, 100%); }
  .hm-about__badge--a { left: -6%; }
  .hm-about__badge--b { right: -4%; }
  .hm-about__body { margin-inline: auto; }
  .hm-about__lead .hm-foot__actions { justify-content: center; }
  .hm-about__cards { grid-template-columns: 1fr; }
}

@media (max-width: 640px) {
  .hm-about__stats { grid-template-columns: 1fr; }
  .hm-about__portrait { width: min(260px, 82%); }
  .hm-about__badge { font-size: 12px; padding: 7px 11px; }
  .hm-about__badge--a { left: -4%; top: 10%; }
  .hm-about__badge--b { right: -4%; bottom: 10%; }
  .hm-path li { grid-template-columns: 1fr; row-gap: 10px; }
  .hm-path__when { grid-column: 1; grid-row: 1; }
  .hm-path h3 { grid-column: 1; grid-row: 2; }
  .hm-path p { grid-column: 1; grid-row: 3; }
  .works { padding-bottom: 120px; }
}

@media (max-width: 640px) {
  .hm-top { padding: 14px 16px; }
  .hm-logo__name { font-size: 18px; }
  .hm-logo__mark { width: 25px; height: 25px; }
  .hm-top__actions .hm-pill { font-size: 15px; padding: 11px 16px; }
  /* on small screens keep only the Resume button in the top bar */
  .hm-top .hm-pill--dark { display: none; }
  .hm-hero { min-height: 0; }
  /* phones keep only the selection cursor + one star — the rest crowds the type */
  .hm-stick--star2 { display: none; }
  .hm-me-cursor { right: 0; bottom: -54px; }
  .hm-dock { bottom: 14px; gap: 5px; padding: 7px; border-radius: 19px; }
  .hm-dock__item { width: 44px; height: 44px; border-radius: 12px; }
  .hm-dock__item svg { width: 20px; height: 20px; }
  .hm-dock__item::after { display: none; }
  .hm-foot__contact, .hm-foot__group { width: 100%; }
  .hm-chip { width: 100%; justify-content: center; }
}


/* ==========================================================
   Small and short viewports.

   The hero is pulled up by a full bar height and given back only half of it,
   which is right when there is a screenful to centre in. On a phone — and on
   any short window — there is not: the pill ended up at y = 0 or above it,
   sitting under the (revealed) top bar. So on those screens the hero stops
   borrowing the bar's space, and the composition stops being lifted.
   ========================================================== */
/* Nothing to lift into on a short screen: centring a box taller than its
   container overflows BOTH ways, and the top half is the half that gets
   clipped. This one is about height, so it applies at any width. */
@media (max-width: 640px), (max-height: 720px) {
  .hm-hero { --stage-drop: 0px; }
}
/* Giving the bar's space back is a PHONE change, not a short-window one. On a
   short desktop the pull-up is what keeps the hero inside the fold — dropped
   there, the hero started below the bar and ran 92px past the bottom, taking
   anything anchored to its floor (the desk) off-screen with it. */
@media (max-width: 640px) {
  .hm-hero {
    margin-top: 0;
    /* the bar already occupies its height in flow here, so this is breathing
       room only — a bar height on top of it would double-count */
    padding-top: clamp(10px, 2.4vh, 30px);
  }
}

/* ==========================================================
   Beyond the surface — the lens
   The tagline IS the concept: a frosted circle rides with the
   pointer across the headline and, inside it, each line reads
   differently. Two copies of every line share one circle — the
   visible one has it masked OUT, the hidden one is clipped TO it,
   so the two readings can never sit on top of each other.
   ========================================================== */
[data-lens] { position: relative; }
.lens { position: relative; display: inline-block; }
.lens__base {
  position: relative; z-index: 1;
  /* The hole is cut with STOPS, not by sizing the gradient.
     `radial-gradient(circle var(--lr) ...)` with --lr at its 0px default is a
     zero-radius gradient — undefined territory that WebKit rasterises as a
     fully transparent mask, which takes the headline with it. It only ever
     hit Safari, and hardest on touch, where wireLens() bails out early and
     --lr is never set at all. Sized farthest-corner and punched with stops,
     the mask is opaque everywhere at --lr: 0 in every engine. */
  -webkit-mask-image: radial-gradient(circle at var(--lx, -999px) var(--ly, -999px),
                                      transparent 0, transparent var(--lr, 0px), #000 calc(var(--lr, 0px) + 1px));
          mask-image: radial-gradient(circle at var(--lx, -999px) var(--ly, -999px),
                                      transparent 0, transparent var(--lr, 0px), #000 calc(var(--lr, 0px) + 1px));
}
/* belt and braces: no pointer, no lens, no mask at all */
@media (hover: none) {
  .lens__base { -webkit-mask-image: none; mask-image: none; }
}
.lens__under {
  position: absolute; inset: 0; z-index: 2;
  display: block; white-space: nowrap;
  color: var(--hm-lens);
  clip-path: circle(var(--lr, 0px) at var(--lx, -999px) var(--ly, -999px));
  pointer-events: none;
}
/* the glass: a frosted disc with a hairline rim and a soft highlight */
.lens__glass {
  position: absolute; top: 0; left: 0; z-index: 3;
  width: calc(var(--lr, 0px) * 2); height: calc(var(--lr, 0px) * 2);
  translate: calc(var(--gx, -999px) - var(--lr, 0px)) calc(var(--gy, -999px) - var(--lr, 0px));
  border-radius: 50%;
  box-shadow: 0 10px 30px -10px rgba(22, 21, 21, .28),
              inset 0 1px 0 rgba(255, 255, 255, .7);
  backdrop-filter: blur(.6px) saturate(1.25);
  -webkit-backdrop-filter: blur(.6px) saturate(1.25);
  background: radial-gradient(circle at 32% 28%, rgba(255, 255, 255, .42), rgba(255, 255, 255, 0) 62%);
  opacity: 0; pointer-events: none;
  transition: opacity .28s var(--hm-ease);
}
[data-lens].is-lensing .lens__glass { opacity: 1; }
@media (hover: none) { .lens__under, .lens__glass { display: none; } }

/* ---- the same second reading, on a phone ----
   No pointer to drag a lens with, so the line swaps whole: the first reading
   fades out in place (opacity, not display — the box has to keep its size or
   the headline would jump) and the second fades in centred over it. */
.hm-touch .hm-hero__title .lens__base {
  transition: opacity .3s var(--hm-ease);
}
.hm-touch .hm-hero__title .lens__under {
  display: block;
  clip-path: none;
  /* centred on the line it replaces, and free to be a different length */
  left: 50%; right: auto; top: 0;
  transform: translateX(-50%);
  width: max-content; max-width: 96vw;
  text-align: center;
  opacity: 0;
  transition: opacity .3s var(--hm-ease);
}
.hm-touch .hm-hero__title.is-swapped .lens__base { opacity: 0; }
.hm-touch .hm-hero__title.is-swapped .lens__under { opacity: 1; }
/* it is a control now, so it should feel like one */
.hm-touch .hm-hero__title { cursor: pointer; -webkit-tap-highlight-color: transparent; }

/* ---------- client marks: one slow belt, no label ---------- */
.hm-clients__belt { position: relative; }
/* one static row — no belt, no repeats */
.hm-clients .hm-clients__row {
  width: auto; flex-wrap: wrap; justify-content: center;
}


/* the header, once scrolling has begun */
.hm-top.is-in { opacity: 1; transform: none; pointer-events: auto; }
/* the overlay pages never hide their bar: it is pinned from the first frame,
   with no reveal to wait on */
.works > .wk__top,
.hm-about > .ab__top {
  opacity: 1;
  transform: none;
  pointer-events: auto;
  transition: none;
}
@media (prefers-reduced-motion: reduce) {
  .hm-top { transition: none; }
}

/* ==========================================================
   The avatar — a Rive illustration that watches the pointer.
   It sits in its own box on the right and takes NO pointer events,
   so the copy beside it stays hoverable and selectable. The hero's
   pointer position is replayed into the canvas as a `mousemove` —
   see wireAvatar() — which is what lets her eye follow the cursor
   anywhere on the page without the canvas covering anything.
   ========================================================== */
.hm-avatar {
  position: absolute;
  right: clamp(-30px, -1vw, 0px);
  /* Centred, not floor-anchored: pinning the hero to the full viewport made
     it much taller, and a bottom offset simply pushed her down and left the
     whole top of the page empty.

     Done with `top`, NOT a transform. A transform creates a stacking context,
     and that re-isolates the canvas's mix-blend-mode: multiply from the
     hero's paper — which puts the artboard's white box straight back. */
  top: calc(50% - var(--av-h) / 2 + var(--stage-drop));
  bottom: auto;
  /* NO z-index and NO transform here. Either one makes this a stacking
     context, and the canvas's mix-blend-mode: multiply would then blend
     against THIS box's empty backdrop instead of the hero's paper — which
     puts the artboard's white rectangle straight back. It paints above the
     paper on document order alone, which is all it needs. */
  width: var(--av-w);
  aspect-ratio: 528 / 465;          /* the artboard's own proportion */
  pointer-events: none;             /* never intercepts the copy or the lens */
}
.hm-avatar__canvas {
  position: relative; z-index: 5;
  display: block; width: 100%; height: 100%;
  /* Takes NO pointer events, and does not need to: the runtime listens for
     `mousemove` on this element, and a synthetic one carries just as well as a
     real one (there is no isTrusted check — measured). So home.js forwards the
     window's pointer in here, and the canvas can stay exactly the size of the
     drawing instead of being blown up into an overlay that swallows hovers.
     The .riv's own look area (2312 x 1246 around a 528 x 465 artboard) is what
     gives the reach — it maps out past every edge of the viewport. */
  pointer-events: none;
  /* the .riv has an opaque white artboard — multiply drops that into the paper */
  mix-blend-mode: multiply;
}

/* On a phone the composition stacks. It cannot stay side by side: the hero
   reserves a whole --av-w column on the right for the avatar, which at 320px
   is 304 of the 320 available and left the headline reading down a 137px
   gutter — six lines deep, with the avatar laid over the top of it. */
@media (max-width: 640px) {
  .hm-hero {
    padding-right: 24px;                       /* the reserved column, released */
    padding-bottom: clamp(74px, 13vh, 104px);  /* clears the floating dock */
    row-gap: clamp(4px, 1.6vh, 16px);
  }
  .hm-avatar {
    /* into the flow, above the copy — she is a grid row now, not an overlay.
       Still NO z-index and NO transform: either would make this a stacking
       context and cut the canvas's multiply off from the paper. */
    position: static;
    width: min(58vw, 230px);
    margin: 0 auto;
  }
  .hm-hero__canvas { width: 100%; }
  /* the room the note needs above the pill, between her and the type */
  .hm-hero__canvas { margin-top: clamp(20px, 3.4vh, 34px); }
  /* the desk joins the stack instead of floating over it. It sits BEFORE the
     copy in the markup, so the three rows are ordered explicitly here. */
  .hm-desk {
    position: static;
    width: clamp(120px, 38vw, 150px);
    /* headroom for the spill: the marks are thrown UP out of the folder, and
       without this the tallest of them lands on the last line of the headline */
    margin: clamp(18px, 3.8vh, 42px) auto 0;
    align-items: center;
  }
  .hm-avatar { order: 1; }
  .hm-hero__canvas { order: 2; }
  .hm-desk { order: 3; }
}

/* A short phone has to fit four things between the bar and the dock — her,
   the pill, three lines of headline and the desk. Everything gives a little
   so the desk still lands above the dock rather than behind it. */
@media (max-width: 640px) and (max-height: 700px) {
  .hm-hero { padding-bottom: clamp(58px, 11vh, 80px); row-gap: 2px; }
  .hm-avatar { width: min(44vw, 165px); }
  .hm-desk { width: clamp(96px, 31vw, 120px); margin-top: clamp(8px, 1.6vh, 18px); }
  .hm-hero__title { font-size: clamp(26px, 8.4vw, 32px); }
  /* no room to buy headroom on a short phone, so the throw itself comes in */
  .hm-folder.is-open .hm-folder__docs { transform: scale(.66); transform-origin: 50% 88%; }
}
