/* joshyuledesign.com — shared chassis. Edit here, not per page. */
/* ---------- Fonts ---------- */
@font-face{font-family:Sansome;src:url(../fonts/Sansome-Regular.otf) format("opentype");font-weight:400;font-style:normal;font-display:swap}
@font-face{font-family:Sansome;src:url(../fonts/Sansome-SemiBold.otf) format("opentype");font-weight:600;font-style:normal;font-display:swap}
@font-face{font-family:Sansome;src:url(../fonts/Sansome-Bold.otf) format("opentype");font-weight:700;font-style:normal;font-display:swap}
@font-face{font-family:Sansome;src:url(../fonts/Sansome-LightItalic.otf) format("opentype");font-weight:300;font-style:italic;font-display:swap}
@font-face{font-family:Sansome;src:url(../fonts/Sansome-MediumItalic.otf) format("opentype");font-weight:500;font-style:italic;font-display:swap}
@font-face{font-family:Sansome;src:url(../fonts/Sansome-BoldItalic.otf) format("opentype");font-weight:700;font-style:italic;font-display:swap}
@font-face{font-family:Nacelle;src:url(../fonts/Nacelle-Regular.woff2) format("woff2");font-weight:400;font-style:normal;font-display:swap}
@font-face{font-family:Nacelle;src:url(../fonts/Nacelle-Bold.woff2) format("woff2");font-weight:700;font-style:normal;font-display:swap}
@font-face{font-family:Mattone;src:url(../fonts/Mattone-150.woff2) format("woff2");font-weight:400;font-style:normal;font-display:swap}

/* ---------- Tokens ---------- */
:root{
  /* Webflow brand variables, recovered from the original stylesheet */
  --blue:#007bff;   /* was --light-text */
  --orange:#ff3f05; /* was --black-900 */
  --lime:#bdfc00;   /* was --line     */
  --paper:#FFFFFF;
  --ink:#0C1738;
  --muted:#5A6B99;
  --line:rgba(26,94,255,.22);
  --gutter:clamp(18px,3.4vw,48px);
  --maxw:1560px;
  --display:Mattone,"Helvetica Neue",Helvetica,Arial,sans-serif;
  --ui:Sansome,"Helvetica Neue",Helvetica,Arial,sans-serif;
  --body:Nacelle,"Helvetica Neue",Helvetica,Arial,sans-serif;
  --radius:18px;
  /* the rail's not-yet-reached grey, neutral so it reads as off */
  --rail-off:#9AA3B4;
  --rail-ring:#C9CEDA;
}

*,*::before,*::after{box-sizing:border-box}
html{-webkit-text-size-adjust:100%;scroll-behavior:smooth}
@media (prefers-reduced-motion:reduce){html{scroll-behavior:auto}
  *,*::before,*::after{animation-duration:.001ms!important;animation-iteration-count:1!important;transition-duration:.001ms!important}}
body{
  margin:0;background:var(--paper);color:var(--ink);
  font-family:var(--body);font-size:17px;line-height:1.55;
  -webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;
  overflow-x:clip;
}
img{max-width:100%;display:block;height:auto}
a{color:var(--blue)}
:focus-visible{outline:3px solid var(--orange);outline-offset:3px;border-radius:4px}

.wrap{max-width:var(--maxw);margin:0 auto;padding-inline:var(--gutter)}
.skip{position:absolute;left:-9999px;top:0;background:var(--blue);color:#fff;padding:12px 18px;z-index:10001;border-radius:0 0 8px 0;font-weight:700}
.skip:focus{left:0}

/* ---------- Home dot + radiating hamburger ---------- */
/* The Lottie paints the dot at about half its box and pulses, so the box
   is sized for the resting dot to come out at 40px, the same as the blue
   button. top and left are set from that: the visible dot then starts on
   the page gutter and its centre lines up with the button's. */
.homelink{position:fixed;top:2px;left:calc(clamp(18px,5vw,48px) - 20px);z-index:10000;
  width:80px;height:80px;display:grid;place-items:center;border-radius:50%}
.homedot{display:block;width:80px;height:80px;border-radius:50%;background:none}
.homedot:not(.has-lottie){width:40px;height:40px;background:var(--orange)}
.homedot.has-lottie{background:none}
.homedot svg{display:block;width:100%;height:100%}

.jym-btn{position:fixed;top:22px;right:clamp(18px,5vw,48px);width:40px;height:40px;
  border:none;border-radius:50%;background:var(--blue);cursor:pointer;display:grid;
  place-items:center;padding:0;z-index:10000;transition:background .3s ease;
  -webkit-tap-highlight-color:transparent}
.jym-btn::before,.jym-btn::after{content:"";position:absolute;inset:0;border-radius:50%;
  border:2.5px solid var(--blue);pointer-events:none;
  animation:jymPing 4.4s cubic-bezier(0,0,.2,1) infinite}
.jym-btn::after{animation-delay:2.2s}
@keyframes jymPing{0%{transform:scale(1);opacity:.5}80%,100%{transform:scale(2.1);opacity:0}}
.jym-box{position:relative;width:18px;height:12px}
/* No bars in the blue dot: it is a plain mark, and More names it on
   hover. They come back as the close X once the menu is open, where the
   button is white on the blue sheet. */
.jym-line{position:absolute;left:0;width:100%;height:2.5px;background:#fff;border-radius:3px;
  opacity:0;
  transition:transform .35s cubic-bezier(.6,0,.2,1),opacity .2s,background .3s}
.jym-line:nth-child(1){top:0}
.jym-line:nth-child(2){top:50%;transform:translateY(-50%)}
.jym-line:nth-child(3){bottom:0}

.jym-menu{position:fixed;inset:0;z-index:9990;background:var(--blue);display:flex;
  flex-direction:column;justify-content:center;padding:0 clamp(24px,8vw,110px);
  opacity:0;visibility:hidden;transition:opacity .45s ease,visibility .45s}
.jym-menu ul{list-style:none;margin:0;padding:0}
.jym-menu li{overflow:hidden}
.jym-menu a{display:inline-block;color:#fff;text-decoration:none;
  font-family:var(--display);font-size:clamp(34px,8vw,78px);font-weight:700;line-height:1.04;
  letter-spacing:0;text-transform:uppercase;padding:4px 0;transform:translateY(110%);
  transition:transform .55s cubic-bezier(.2,1,.3,1),color .25s ease}
.jym-menu a:hover{color:var(--lime)}
html.jym-open{overflow:hidden}
html.jym-open .jym-menu{opacity:1;visibility:visible}
html.jym-open .jym-menu a{transform:translateY(0)}
html.jym-open .jym-menu li:nth-child(1) a{transition-delay:.10s}
html.jym-open .jym-menu li:nth-child(2) a{transition-delay:.16s}
html.jym-open .jym-menu li:nth-child(3) a{transition-delay:.22s}
html.jym-open .jym-menu li:nth-child(4) a{transition-delay:.28s}
html.jym-open .jym-menu li:nth-child(5) a{transition-delay:.34s}
html.jym-open .jym-menu li:nth-child(6) a{transition-delay:.40s}
html.jym-open .jym-btn{background:#fff}
html.jym-open .jym-line{background:var(--blue);opacity:1}
html.jym-open .jym-btn::before,html.jym-open .jym-btn::after{animation:none;opacity:0}
html.jym-open .jym-line:nth-child(1){top:50%;transform:translateY(-50%) rotate(45deg)}
html.jym-open .jym-line:nth-child(2){opacity:0}
html.jym-open .jym-line:nth-child(3){bottom:auto;top:50%;transform:translateY(-50%) rotate(-45deg)}
@media (prefers-reduced-motion:reduce){
  .jym-btn::before,.jym-btn::after{animation:none}
  .jym-menu,.jym-menu a{transition:none}}

/* ---------- Hero ---------- */
/* safe centering: when the hero is taller than the viewport it falls back to
   start-aligned instead of clipping the top of the name */
.hero{min-height:100svh;display:grid;place-content:safe center;justify-items:center;
  text-align:center;padding:64px var(--gutter) 18px}
/* Mattone 150 ships as a single 400 weight; the original faux-bolded it at 700. */
.hero h1{font-family:var(--display);font-weight:700;
  color:var(--blue);margin:0;
  font-size:min(clamp(62px,14vw,196px),14.5vh);line-height:.8;letter-spacing:0;text-transform:uppercase}
.blob{width:clamp(130px,24vw,250px);margin:clamp(22px,4vw,46px) auto}
.orb-fallback ellipse{fill:var(--orange)}

/* ---------- Animated orb (Lottie) ---------- */
/* as large as the width allows, but capped by viewport height so the rotating
   titles and the scroll cue stay above the fold on a standard laptop */
/* A negative top margin tucks JYNX up under the name. The blob carries
   its own transparent margin inside the box, so the two never touch:
   checked against the blob at the top of its pulse, not at rest. */
.orb{width:min(clamp(330px,66vw,900px),60vh);margin:-10px auto 2px;aspect-ratio:1}
.orb svg{width:100%;height:100%;display:block}
.orb.has-lottie .orb-fallback{display:none}


/* ---------- Blue dot cursor ---------- */
.cursor-wrapper{position:fixed;inset:0;z-index:10000;pointer-events:none;display:none}
.cursor{position:absolute;top:0;left:0;width:20px;height:20px;border-radius:50%;
  background:var(--blue);will-change:transform;transform:translate3d(-100px,-100px,0);
  transition:background .18s ease}
/* a blue dot disappears on the blue surfaces, so flip it to the brand red */
.cursor.on-blue{background:var(--orange)}
html.has-dot-cursor .cursor-wrapper{display:block}
html.has-dot-cursor,html.has-dot-cursor a,html.has-dot-cursor button{cursor:none}
.cursor.is-over{transform-origin:50% 50%}

/* ---------- Rotating hero titles ---------- */
.title-rotator-wrap{width:100%;display:flex;justify-content:center;align-items:center;
  text-align:center;perspective:1200px}
.title-rotator{position:relative;display:inline-block;height:70px;overflow:hidden;
  font-family:var(--display);font-weight:500;font-style:normal;
  font-size:clamp(30px,3.2vw,46px);line-height:70px;color:var(--blue);text-align:center;transform-style:preserve-3d}
.title-sizer{display:block;visibility:hidden;white-space:nowrap;height:70px;line-height:70px;padding:0;margin:0}
.title-word{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;
  white-space:nowrap;box-sizing:border-box;padding:0 6px;opacity:0;backface-visibility:hidden;
  transform-origin:50% 100%;transform:translateY(42px) rotateX(-80deg);
  animation:flipReveal 12s cubic-bezier(.22,.61,.36,1) infinite;will-change:transform,opacity}
.title-word:nth-child(2){animation-delay:0s}
.title-word:nth-child(3){animation-delay:3s}
.title-word:nth-child(4){animation-delay:6s}
.title-word:nth-child(5){animation-delay:9s}
@keyframes flipReveal{
  0%{opacity:0;transform:translateY(42px) rotateX(-80deg)}
  8%{opacity:1;transform:translateY(0) rotateX(0)}
  22%{opacity:1;transform:translateY(0) rotateX(0)}
  30%{opacity:0;transform:translateY(-42px) rotateX(80deg)}
  100%{opacity:0;transform:translateY(-42px) rotateX(80deg)}}
@media (max-width:767px){
  .title-rotator{height:56px;line-height:56px;font-size:clamp(17px,5.4vw,24px);max-width:92vw}
  .title-sizer{height:56px;line-height:56px}
  .title-word{height:56px;line-height:56px;transform:translateY(26px) rotateX(-80deg);padding:0 8px}
  @keyframes flipReveal{
    0%{opacity:0;transform:translateY(26px) rotateX(-80deg)}
    8%{opacity:1;transform:translateY(0) rotateX(0)}
    22%{opacity:1;transform:translateY(0) rotateX(0)}
    30%{opacity:0;transform:translateY(-26px) rotateX(80deg)}
    100%{opacity:0;transform:translateY(-26px) rotateX(80deg)}}}
@media (prefers-reduced-motion:reduce){
  .title-word{animation:none;opacity:0;transform:none}
  .title-word:nth-child(2){opacity:1}}
.hero .role{font-family:var(--display);font-weight:700;color:var(--blue);text-transform:uppercase;
  letter-spacing:.14em;font-size:clamp(13px,1.9vw,22px);margin:0}

/* ---------- Section furniture ---------- */
section{scroll-margin-top:80px}
.sechead{padding:clamp(60px,9vw,120px) 0 clamp(26px,4vw,48px)}
.sechead h2{font-family:var(--display);font-weight:700;color:var(--blue);margin:0;
  font-size:clamp(42px,9vw,120px);line-height:.88;letter-spacing:0;text-transform:uppercase}
.eyebrow{display:inline-flex;align-items:center;gap:9px;font-family:var(--ui);font-weight:700;
  font-size:11.5px;letter-spacing:.14em;text-transform:uppercase;color:var(--orange);margin:0 0 14px}
.eyebrow::before{content:"";width:11px;height:11px;border-radius:50%;background:var(--lime);flex:0 0 auto}

/* ---------- Case studies ---------- */
.cases{display:grid;gap:clamp(18px,2.6vw,30px);padding-bottom:clamp(40px,7vw,90px)}
/* Cards stack and overlap as you scroll; each sticks a little lower than the last.
   Needs vertical room, so it is off on short viewports (landscape phones). */
/* Each card sticks a little lower than the one before it, so the stack
   keeps a band of every card showing instead of burying them. --i is set
   per card in the markup. */
@media (min-height:640px){
  .case{position:sticky;top:calc(84px + var(--i,0) * 18px);
    box-shadow:0 -10px 30px rgba(12,23,56,.07)}
  .cases::after{content:"";display:block;height:34vh}
}
@media (min-width:901px){
  .cases{gap:clamp(26px,4vw,52px)}
  .case{top:calc(96px + var(--i,0) * 26px)}
}
/* Equal heights, or a shorter card lets the one beneath show at its bottom edge.
   The floor has to clear the tallest card, which is BART at narrow widths where
   its blurb runs to four lines. */
@media (min-height:640px){
  .case{min-height:470px;align-content:center}
}
@media (min-width:901px){
  .case{min-height:clamp(470px,34vw,560px)}
}
.case{display:grid;grid-template-columns:1.05fr .95fr;gap:clamp(20px,3.5vw,54px);align-items:center;
  border:2.5px solid var(--blue);border-radius:var(--radius);padding:clamp(22px,3.4vw,46px);
  text-decoration:none;color:inherit;background:var(--paper);
  transition:transform .4s cubic-bezier(.2,1,.3,1),box-shadow .4s,background .3s}
.case:hover,.case:focus-visible{box-shadow:0 -10px 30px rgba(12,23,56,.07),0 18px 50px rgba(0,123,255,.20)}
.case h3{font-family:var(--display);font-weight:700;color:var(--blue);margin:0 0 10px;
  font-size:clamp(30px,5.2vw,62px);line-height:.92;letter-spacing:0;text-transform:uppercase}
/* reserve three lines so a shorter blurb cannot make its card shorter than
   the one it has to cover */
.case p{margin:0;color:var(--muted);font-size:clamp(14px,1.5vw,17px);max-width:34ch;min-height:4.7em}
.case .shot{border-radius:12px;overflow:hidden;background:#F2F5FF;aspect-ratio:4/3}
.case .shot img{width:100%;height:100%;object-fit:cover;transition:transform .55s cubic-bezier(.2,1,.3,1)}
.case:hover .shot img{transform:scale(1.045)}
/* Blue fill slides in from the left on hover, as the old .button-bg did
   (that one grew from width:0% and was lime). */
.cta{position:relative;overflow:hidden;display:inline-flex;align-items:center;gap:12px;
  margin-top:22px;padding:13px 24px;border:2px solid var(--blue);border-radius:999px;
  font-family:var(--ui);font-weight:700;font-size:12px;letter-spacing:.1em;
  text-transform:uppercase;color:var(--blue);transition:color .35s ease}
.cta::before{content:"";position:absolute;inset:0;background:var(--blue);
  transform:scaleX(0);transform-origin:left center;
  transition:transform .5s cubic-bezier(.22,.61,.36,1)}
.cta-label{position:relative;z-index:1}
.cta::after{content:"";position:relative;z-index:1;width:10px;height:10px;border-radius:50%;
  background:var(--orange);transition:background .35s}
.case:hover .cta::before,.case:focus-visible .cta::before,
a.typecard:hover .cta::before,a.typecard:focus-visible .cta::before,
a.tile:hover .cta::before,a.tile:focus-visible .cta::before{transform:scaleX(1)}
.case:hover .cta,.case:focus-visible .cta,
a.typecard:hover .cta,a.typecard:focus-visible .cta,
a.tile:hover .cta,a.tile:focus-visible .cta{color:#fff}
.case:hover .cta::after,.case:focus-visible .cta::after,
a.typecard:hover .cta::after,a.typecard:focus-visible .cta::after,
a.tile:hover .cta::after,a.tile:focus-visible .cta::after{background:var(--lime)}
/* Wide enough for the shot to fill the card instead of floating as a 4:3
   box with air above and below it. */
/* Give the shot more of the card once there is room. The fixed 4:3 stays:
   it is what keeps every card the same height, whatever shape the source
   picture is, and a card that is shorter than the one beneath shows its
   edge as they stack. */
@media (min-width:1200px){
  /* minmax(0,…) or a long heading's min-content width widens the text
     column, shrinking that card's shot and with it the whole card */
  .case{grid-template-columns:minmax(0,.86fr) minmax(0,1.14fr)}
}
@media (max-width:820px){.case{grid-template-columns:1fr}.case .shot{order:-1;aspect-ratio:16/10}}

/* ---------- Work grid ----------
   Six cards, two up, built to the same spec as the type design cards:
   same border, same 16:10 frame, same meta padding and type sizes. The
   card is a flex column and the blurb takes the slack, so two cards in a
   row are the same height with their buttons on the same line. */
.grid{display:grid;grid-template-columns:1fr;gap:clamp(20px,3vw,40px);
  padding-bottom:clamp(50px,8vw,100px)}
/* 1fr rows so all six cards are one size, not one size per row */
@media (min-width:860px){.grid{grid-template-columns:repeat(2,1fr);grid-auto-rows:1fr}}
a.tile{text-decoration:none;color:inherit}
/* The picture sits inset inside the border, the way the case study cards
   do, so every card on the site reads as the same component and a stacked
   one shows a white edge rather than a slice of photo. */
.tile{border:2.5px solid var(--blue);border-radius:var(--radius);overflow:hidden;
  background:var(--paper);display:flex;flex-direction:column;
  padding:clamp(10px,1vw,14px);
  transition:transform .4s cubic-bezier(.2,1,.3,1),box-shadow .4s}
a.tile:hover{transform:translateY(-5px);box-shadow:0 16px 40px rgba(26,94,255,.16)}
.tile figure{margin:0;aspect-ratio:16/10;overflow:hidden;background:#F2F5FF;
  border-radius:11px}
.tile img,.tile video{width:100%;height:100%;display:block;object-fit:cover;
  transition:transform .55s cubic-bezier(.2,1,.3,1)}
a.tile:hover img,a.tile:hover video{transform:scale(1.05)}
.tile .meta{padding:clamp(14px,1.8vw,20px) clamp(8px,1.2vw,18px) clamp(10px,1.4vw,16px);
  display:flex;flex-direction:column;flex:1}
.tile h3{font-family:var(--display);font-weight:700;color:var(--blue);margin:0 0 9px;
  font-size:clamp(26px,3.4vw,40px);letter-spacing:0;line-height:1;text-transform:uppercase}
.tile p{margin:0;font-size:14.5px;color:var(--muted);line-height:1.5;flex:1;max-width:54ch}
.tile .cta{align-self:flex-start}
.tag{display:inline-block;font-family:var(--ui);font-weight:700;font-size:10px;letter-spacing:.13em;
  text-transform:uppercase;color:var(--orange);margin-bottom:9px}

/* ---------- Type design ---------- */
.type-row{display:grid;grid-template-columns:repeat(auto-fit,minmax(300px,1fr));gap:clamp(20px,3vw,40px);
  padding-bottom:clamp(50px,8vw,100px)}
.typecard{border:2.5px solid var(--blue);border-radius:var(--radius);overflow:hidden;
  background:var(--paper);display:flex;flex-direction:column;
  padding:clamp(10px,1vw,14px);
  text-decoration:none;color:inherit;transition:transform .4s cubic-bezier(.2,1,.3,1),box-shadow .4s}
a.typecard:hover{transform:translateY(-5px);box-shadow:0 16px 40px rgba(26,94,255,.16)}
.typecard figure{margin:0;aspect-ratio:16/10;overflow:hidden;background:#F2F5FF;
  border-radius:11px}
/* the Sansome card runs its Type West specimen loop instead of a still */
.typecard figure.motion{background:#0c1020}
.typecard img,.typecard video{width:100%;height:100%;display:block;object-fit:cover}
/* same as the work cards: the blurb takes the slack, so a shorter one does
   not leave its View specimen button sitting higher than its neighbour's */
.typecard .meta{padding:clamp(14px,1.8vw,20px) clamp(8px,1.2vw,18px) clamp(10px,1.4vw,16px);
  display:flex;flex-direction:column;flex:1}
.typecard .cta{align-self:flex-start}
.typecard h3{font-family:var(--display);font-weight:700;color:var(--blue);margin:0 0 9px;
  font-size:clamp(26px,3.4vw,40px);letter-spacing:0;text-transform:uppercase;line-height:1}
.typecard p{margin:0;color:var(--muted);font-size:14.5px;max-width:54ch;flex:1}

/* ---------- About ---------- */
.about{display:grid;grid-template-columns:.85fr 1.15fr;gap:clamp(28px,5vw,80px);align-items:start;
  padding-bottom:clamp(60px,9vw,110px)}
.about .portrait{border-radius:var(--radius);overflow:hidden;border:2.5px solid var(--blue);position:sticky;top:96px}
.lede{font-family:var(--display);font-weight:700;color:var(--blue);font-size:clamp(24px,3.6vw,46px);
  line-height:1.08;letter-spacing:0;margin:0 0 clamp(26px,4vw,46px)}
.facts{display:grid;gap:clamp(18px,2.6vw,30px);margin:0;padding:0;list-style:none;counter-reset:f}
.facts li{display:grid;grid-template-columns:52px 1fr;gap:16px;padding-top:18px;border-top:1.5px solid var(--line)}
.facts li::before{counter-increment:f;content:"0" counter(f);font-family:var(--ui);font-weight:700;
  color:var(--orange);font-size:13px;letter-spacing:.06em;padding-top:3px}
.facts strong{display:block;font-family:var(--display);font-weight:700;color:var(--blue);
  font-size:clamp(16px,1.9vw,21px);margin-bottom:5px;letter-spacing:-.01em}
.facts span{color:var(--muted);font-size:15.5px}
@media (max-width:820px){.about{grid-template-columns:1fr}.about .portrait{position:static;max-width:340px}}

/* ---------- Footer ---------- */
footer{background:var(--blue);color:#fff;padding:clamp(56px,8vw,100px) 0 clamp(28px,4vw,44px);margin-top:clamp(30px,5vw,60px)}
footer h2{font-family:var(--display);font-weight:700;margin:0 0 10px;text-transform:uppercase;
  font-size:clamp(38px,8.5vw,112px);line-height:.88;letter-spacing:0}
.mailto{display:inline-block;font-family:var(--display);font-weight:700;color:var(--lime);text-decoration:none;
  font-size:clamp(17px,2.6vw,32px);letter-spacing:-.01em;border-bottom:3px solid transparent;transition:border-color .25s}
.mailto:hover{border-bottom-color:var(--lime)}
/* Four pills. They scale with the width rather than holding one size and
   dropping Resume / CV onto a second line, which is what a fixed 12px and
   22px of padding did below about 550px. Full size from 1100px up. */
.social{display:flex;flex-wrap:wrap;gap:clamp(7px,0.8vw,10px);
  margin:clamp(28px,4vw,46px) 0 0;padding:0;list-style:none}
.social a{display:inline-block;
  padding:clamp(9px,1vw,11px) clamp(13px,2vw,22px);
  border:2px solid rgba(255,255,255,.55);border-radius:999px;
  color:#fff;text-decoration:none;font-family:var(--ui);font-weight:700;
  font-size:clamp(9.5px,1.15vw,12px);
  letter-spacing:.09em;text-transform:uppercase;white-space:nowrap;
  transition:background .25s,color .25s,border-color .25s}
.social a:hover{background:#fff;color:var(--blue);border-color:#fff}
/* On a phone the four pills share the row evenly and run the full width of
   the footer, so they read as one band instead of three and a straggler. */
@media (max-width:700px){
  .social{flex-wrap:nowrap;gap:clamp(5px,1.5vw,8px)}
  .social li{flex:1 1 0;min-width:0}
  .social a{display:block;text-align:center;padding-left:4px;padding-right:4px;
    font-size:clamp(8.5px,2.35vw,11px);letter-spacing:.055em}
}
/* the address and the Email pill both copy on click, and say so */
.mailto.is-copied{border-bottom-color:var(--lime)}
/* The tag sits outside the flow so it never widens the address or its
   underline while it is hidden. */
.mailto{position:relative}
.mailto::after{content:"copied";position:absolute;left:100%;top:50%;white-space:nowrap;
  font-family:var(--ui);font-weight:700;font-size:11px;letter-spacing:.09em;text-transform:uppercase;
  color:var(--blue);background:var(--lime);border-radius:999px;padding:4px 9px;
  opacity:0;transform:translate(8px,-62%);transition:opacity .2s,transform .2s;pointer-events:none}
.mailto.is-copied::after{opacity:1;transform:translate(12px,-55%)}
.social a.is-copied{color:var(--lime);border-color:var(--lime)}
.colophon{display:flex;flex-wrap:wrap;gap:10px 26px;justify-content:space-between;align-items:center;
  margin-top:clamp(40px,6vw,76px);padding-top:22px;border-top:1px solid rgba(255,255,255,.3);
  font-size:12px;letter-spacing:.05em;text-transform:uppercase;color:rgba(255,255,255,.8);font-weight:700}
footer :focus-visible{outline-color:var(--lime)}

/* ---------- Reveal ---------- */
.rev{opacity:0;transform:translateY(26px);transition:opacity .7s ease,transform .7s cubic-bezier(.2,1,.3,1)}
.rev.in{opacity:1;transform:none}
@media (prefers-reduced-motion:reduce){.rev{opacity:1;transform:none}}

/* ---------- Section rail ----------
   The sidebar from the case study sites: one dot per section with its name
   beside it. Sections you have not reached sit in grey, the one you are in
   is blue with a filled dot. It sits on the same right margin as the
   hamburger, dot centers aligned to it. Hidden below 1200px: the names need
   about 140px of margin, which a narrow laptop cannot spare, and the
   hamburger menu already does this job. */
.rail{display:none}
@media (min-width:1200px){
  .rail{display:flex;position:fixed;top:50%;right:calc(clamp(16px,2.6vw,30px) + 17px);
    transform:translateY(-50%);z-index:9000;
    flex-direction:column;align-items:flex-end;gap:15px}
  .rail a{display:flex;align-items:center;gap:13px;text-decoration:none;border-radius:4px}
  .rail .rlbl{font-family:var(--ui);font-weight:700;font-size:10px;letter-spacing:.12em;
    text-transform:uppercase;color:var(--rail-off);max-width:80px;text-align:right;
    line-height:1.25;transition:color .28s ease,font-size .3s cubic-bezier(.2,1,.3,1)}
  .rail .rdot{width:10px;height:10px;border-radius:50%;flex:0 0 auto;
    border:1.5px solid var(--rail-ring);background:var(--paper);
    transition:background .28s ease,border-color .28s ease,transform .28s ease}
  .rail a:hover .rlbl,.rail a:focus-visible .rlbl{color:var(--ink)}
  .rail a:hover .rdot,.rail a:focus-visible .rdot{border-color:var(--blue)}
  .rail a.active .rlbl{color:var(--blue);font-size:12px}
  .rail a.active .rdot{background:var(--orange);border-color:var(--orange);transform:scale(1.4)}
  .rail a:focus-visible{outline:2px solid var(--blue);outline-offset:6px}

  /* over the blue footer the whole rail flips to white */
  .rail.inverted .rlbl{color:rgba(255,255,255,.55)}
  .rail.inverted .rdot{border-color:rgba(255,255,255,.45);background:transparent}
  .rail.inverted a:hover .rdot{border-color:#fff}
  .rail.inverted a.active .rlbl{color:#fff}
  .rail.inverted a.active .rdot{background:var(--lime);border-color:var(--lime)}
  .rail.inverted a:focus-visible{outline-color:var(--lime)}

  /* the menu sheet covers the page, so the rail steps out of its way */
  html.jym-open .rail{opacity:0;pointer-events:none;transition:opacity .2s ease}
}
@media (prefers-reduced-motion:reduce){
  .rail .rlbl,.rail .rdot{transition:none}
}

/* Hold the content clear of the rail. Padding only the side the rail is on
   costs the page about a quarter of what capping the shell would, since a
   cap takes the same room off the left as well. Past 1860px the shell has
   stopped growing and its own margin is already wide enough. */
@media (min-width:1200px){
  .has-rail .wrap{padding-right:calc(clamp(16px,2.6vw,30px) + 146px)}
}
@media (min-width:1860px){
  .has-rail .wrap{padding-right:var(--gutter)}
}

/* ---------- JYNX ----------
   The orb is the assistant. Hover and it introduces itself; click and the
   same bubble grows a field. Answers land in a strip under the hero. */
.orb.is-jynx{position:relative;cursor:pointer}
.orb.is-jynx:focus-visible{outline:2px solid var(--blue);outline-offset:14px;border-radius:50%}

.jynx-bubble{position:absolute;z-index:20;text-align:left;
  background:var(--blue);color:#fff;border-radius:20px;
  padding:17px 20px;width:max-content;max-width:248px;
  box-shadow:0 18px 44px rgba(0,123,255,.28);
  opacity:0;pointer-events:none;visibility:hidden;
  transition:opacity .26s ease,transform .36s cubic-bezier(.2,1,.3,1),
    visibility 0s linear .3s}
.jynx-say{margin:0;font-family:var(--body);font-size:16.5px;line-height:1.4}

/* the field and the suggestions only exist once it has been clicked */
.jynx-ask,.jynx-bubble .jynx-chips{display:none}
.orb.is-asking .jynx-ask{display:flex;gap:8px;margin-top:13px}
.orb.is-asking .jynx-bubble .jynx-chips{display:flex;flex-wrap:wrap;gap:7px;margin-top:11px}

.jynx-ask input{flex:1 1 auto;min-width:0;font-family:var(--body);font-size:16px;
  color:var(--ink);background:#fff;border:none;border-radius:999px;
  padding:10px 15px}
.jynx-ask input:focus{outline:2px solid var(--lime);outline-offset:1px}
.jynx-go{flex:0 0 auto;width:38px;height:38px;display:grid;place-items:center;
  cursor:pointer;border:none;border-radius:50%;background:var(--orange);
  transition:background .2s ease,opacity .2s ease}
.jynx-go:hover:not(:disabled){background:var(--lime)}
.jynx-go:disabled{opacity:.5;cursor:default}
.jynx-go svg{width:17px;height:17px;stroke:#fff;stroke-width:2.6;
  stroke-linecap:round;stroke-linejoin:round;fill:none}
.jynx-go:hover:not(:disabled) svg{stroke:var(--ink)}
.jynx-ask.is-busy input{opacity:.6}

.jynx-chip{cursor:pointer;font-family:var(--ui);font-weight:700;font-size:9.5px;
  letter-spacing:.1em;text-transform:uppercase;color:#fff;background:none;
  border:1.5px solid rgba(255,255,255,.45);border-radius:999px;padding:7px 11px;
  transition:border-color .2s ease,background .2s ease,color .2s ease}
.jynx-chip:hover{border-color:var(--lime);background:var(--lime);color:var(--ink)}

/* Beside the orb on a wide screen, with a tail. It goes on the left: the
   rail's section names run down the right edge and collide from ~1200px. */
@media (min-width:1100px){
  .jynx-bubble{right:calc(100% + 26px);top:32%;
    transform:translateX(10px) scale(.97);transform-origin:right center}
  .jynx-bubble::before{content:"";position:absolute;right:-7px;top:28px;
    width:16px;height:16px;background:var(--blue);transform:rotate(45deg);
    border-radius:3px}
  .orb.is-jynx:hover .jynx-bubble,
  .orb.is-jynx:focus-visible .jynx-bubble,
  .orb.is-asking .jynx-bubble{opacity:1;visibility:visible;
    transform:translateX(0) scale(1);transition-delay:0s}
  .orb.is-asking .jynx-bubble{pointer-events:auto;max-width:300px}
}
/* a little wider once the screen can spare it, so the introduction is not
   seven short lines */
@media (min-width:1300px){
  .jynx-bubble{max-width:286px}
  .orb.is-asking .jynx-bubble{max-width:320px}
}

/* No room beside the orb below that, and no hover to speak of, so the
   bubble becomes a card at the foot of the screen when tapped. */
@media (max-width:1099px){
  .jynx-bubble{position:fixed;left:50%;bottom:16px;top:auto;
    width:calc(100vw - 32px);max-width:420px;
    transform:translate(-50%,14px) scale(.98);transform-origin:bottom center}
  .orb.is-asking .jynx-bubble{opacity:1;visibility:visible;pointer-events:auto;
    transform:translate(-50%,0) scale(1);transition-delay:0s}
}

/* ---------- JYNX answers ---------- */
.jynx-results{padding:clamp(30px,5vw,60px) 0 0}
.jynx-results-inner{border:2.5px solid var(--blue);border-radius:var(--radius);
  padding:clamp(20px,2.6vw,32px)}
.jynx-results-head{display:flex;align-items:baseline;justify-content:space-between;
  gap:16px;margin-bottom:clamp(16px,2vw,22px)}
.jynx-results-say{margin:0;color:var(--ink);font-size:clamp(15px,1.6vw,17px);
  line-height:1.5;max-width:70ch}
.jynx-clear{flex:0 0 auto;cursor:pointer;background:none;border:none;padding:0;
  font-family:var(--ui);font-weight:700;font-size:10.5px;letter-spacing:.12em;
  text-transform:uppercase;color:var(--rail-off);transition:color .2s ease}
.jynx-clear:hover{color:var(--orange)}

.jynx-picks{display:grid;gap:clamp(12px,1.6vw,20px)}
/* The columns follow the number of answers: three fill the row, two fill
   the row, and a lone card is capped and centred rather than stretched the
   whole width or left hugging one edge. */
@media (min-width:560px){
  .jynx-picks[data-n="2"],.jynx-picks[data-n="3"]{grid-template-columns:repeat(2,minmax(0,1fr))}
}
@media (min-width:860px){
  .jynx-picks[data-n="3"]{grid-template-columns:repeat(3,minmax(0,1fr))}
  .jynx-picks[data-n="2"]{grid-template-columns:repeat(2,minmax(0,1fr))}
}
.jynx-picks[data-n="1"]{grid-template-columns:minmax(0,1fr);
  max-width:440px;margin-inline:auto}
.jynx-pick{display:flex;flex-direction:column;text-decoration:none;color:inherit;
  border:2px solid var(--line);border-radius:14px;overflow:hidden;background:var(--paper);
  transition:border-color .3s ease,transform .4s cubic-bezier(.2,1,.3,1)}
.jynx-pick:hover{border-color:var(--blue);transform:translateY(-3px)}
.jynx-pick figure{margin:0;aspect-ratio:16/10;overflow:hidden;background:#F2F5FF}
.jynx-pick img{width:100%;height:100%;display:block;object-fit:cover}
.jynx-pick .meta{padding:14px 16px 18px;display:flex;flex-direction:column;flex:1}
.jynx-pick h3{font-family:var(--display);font-weight:700;color:var(--blue);margin:0 0 6px;
  font-size:clamp(17px,1.8vw,21px);line-height:1.05;letter-spacing:0;text-transform:uppercase}
.jynx-pick p{margin:0;font-size:13px;line-height:1.5;color:var(--muted)}

@media (prefers-reduced-motion:reduce){
  .jynx-bubble,.jynx-pick{transition:none}
}


/* ---------- JYNX nudge ----------
   The orb looks like a picture until you touch it, so a blue arrow-shaped
   sign points at it until it has been used once. One element: the arrow is
   a clip-path on the sign itself, with the words inside it. */
.jynx-nudge{font-family:var(--ui);font-weight:700;font-size:10.5px;
  letter-spacing:.12em;text-transform:uppercase;color:#fff;background:var(--blue);
  white-space:nowrap;transition:opacity .3s ease}
.jynx-nudge.is-gone{opacity:0}

/* Pointing right, against the orb. The hero is exactly one screen on a
   laptop, so this is positioned rather than added to the flow. */
.jynx-nudge.is-beside{position:absolute;right:calc(100% + 16px);top:40%;
  margin:0;padding:11px 30px 11px 18px;
  clip-path:polygon(0 0,calc(100% - 17px) 0,100% 50%,calc(100% - 17px) 100%,0 100%);
  animation:jynxNudgeSide 2.1s ease-in-out infinite}
@keyframes jynxNudgeSide{
  0%,100%{transform:translateX(0)}
  50%{transform:translateX(10px)}}

/* Pointing up, under the rotating titles, where a phone has the room. */
/* width:max-content or it stretches the full hero and the point reads
   as a shallow roof instead of an arrow */
.jynx-nudge.is-below{width:max-content;max-width:100%;
  margin:clamp(16px,3.4vw,28px) auto 0;
  padding:21px 20px 12px;
  /* a pointer the width of a thumb, not a roof across the whole sign */
  clip-path:polygon(0 16px,calc(50% - 15px) 16px,50% 0,calc(50% + 15px) 16px,
    100% 16px,100% 100%,0 100%);
  animation:jynxNudgeUp 2.1s ease-in-out infinite}
@keyframes jynxNudgeUp{
  0%,100%{transform:translateY(0)}
  50%{transform:translateY(-10px)}}

@media (prefers-reduced-motion:reduce){
  .jynx-nudge{animation:none}}

/* ---------- Stacking on a phone ----------
   The case study deck reads well at that width, so the work grid and the
   type cards do the same thing once they are a single column: each card
   sticks a little lower than the last and the section builds into a deck.
   --i comes from nth-child so the markup stays clean. Off on a short
   viewport, where there is no room for it. */
@media (max-width:859px) and (min-height:640px){
  #work .grid > .tile,
  #type .type-row > .typecard{position:sticky;top:calc(80px + var(--i,0) * 16px);
    box-shadow:0 -10px 26px rgba(12,23,56,.12)}
  #work .grid > .tile:nth-child(1){--i:0}
  #work .grid > .tile:nth-child(2){--i:1}
  #work .grid > .tile:nth-child(3){--i:2}
  #work .grid > .tile:nth-child(4){--i:3}
  #work .grid > .tile:nth-child(5){--i:4}
  #work .grid > .tile:nth-child(6){--i:5}
  #type .type-row > .typecard:nth-child(1){--i:0}
  #type .type-row > .typecard:nth-child(2){--i:1}
  /* Every card in a stack has to be the same height, or a shorter one on
     top leaves the taller card behind it sticking out below. 1fr rows
     size them all to the tallest. The room under the stack is padding
     rather than an ::after, which would become a row and inherit 1fr. */
  #work .grid{grid-template-rows:repeat(6,1fr) 62vh}
  #type .type-row{grid-template-rows:repeat(2,1fr) 62vh}
  #work .grid::after,
  #type .type-row::after{content:"";display:block}
  /* a lifted card would slide out of its own stack */
  a.tile:hover,a.typecard:hover{transform:none}

}

/* Chrome paints its own Picture-in-Picture and media buttons over any video
   on hover. These loops are decoration, not a player, so hide the overlay.
   Scoped to videos with no controls attribute, since the motion and Balzac
   pages do add real controls when a browser refuses to autoplay. */
video:not([controls])::-webkit-media-controls,
video:not([controls])::-webkit-media-controls-overlay-enclosure,
video:not([controls])::-webkit-media-controls-enclosure,
video:not([controls])::-webkit-media-controls-panel{display:none !important;
  -webkit-appearance:none}

/* ---------- JYNX's eye ----------
   The white dot in the orb's lime circle. At rest it sits exactly where
   the Lottie draws its own, so the swap is invisible. While JYNX is
   working it grows and looks up and to the left, toward the bubble, and
   stays inside the lime. Positions are percentages of the orb, measured
   off the animation: lime centre 50.5% / 64.8%, dot 3.4% wide. */
.orb.has-eye .jynx-lottie-dot{fill:transparent}
.jynx-eye{position:absolute;left:50.5%;top:64.8%;width:3.4%;aspect-ratio:1;
  border-radius:50%;background:#fff;pointer-events:none;
  /* the anchor is the lime circle's centre; --eye-x and --eye-y are the
     look, set from the pointer in jynx.js */
  transform:translate(-50%,-50%) translate(var(--eye-x,0px),var(--eye-y,0px));
  transition:width .5s cubic-bezier(.2,1,.3,1),top .5s cubic-bezier(.2,1,.3,1),
    transform .22s cubic-bezier(.2,.8,.3,1)}
.orb.is-asking .jynx-eye{width:7.5%}
/* below 1100px the bubble is a card at the foot of the screen, so the eye
   looks down at it instead of up and to the left */
@media (max-width:1099px){
  .orb.is-asking .jynx-eye{top:67.4%}
}
@media (prefers-reduced-motion:reduce){
  .jynx-eye{transition:none}
}

/* ---------- Home and More signifiers ----------
   The two fixed controls are marks with no words. On hover, and on
   keyboard focus, each slides its name out away from the edge it sits on.
   Orange for both: it reads on the white pages and on the blue footer,
   where a blue label would disappear. */
.nav-sig{position:fixed;top:42px;z-index:10000;pointer-events:none;
  font-family:var(--ui);font-weight:700;font-size:11px;letter-spacing:.14em;
  text-transform:uppercase;color:var(--orange);white-space:nowrap;
  opacity:0;transition:opacity .26s ease,transform .36s cubic-bezier(.2,1,.3,1)}
.nav-sig-home{left:calc(clamp(18px,5vw,48px) + 52px);
  transform:translate(-10px,-50%)}
.nav-sig-more{right:calc(clamp(18px,5vw,48px) + 52px);color:var(--blue);
  transform:translate(10px,-50%)}
.homelink:hover + .nav-sig-home,
.homelink:focus-visible + .nav-sig-home,
.jym-btn:hover + .nav-sig-more,
.jym-btn:focus-visible + .nav-sig-more{opacity:1;transform:translate(0,-50%)}
/* the menu sheet covers the page, so the button's name steps aside with it */
html.jym-open .nav-sig{opacity:0}
@media (prefers-reduced-motion:reduce){
  .nav-sig{transition:opacity .2s ease}
}
