/* One variable file covers 100 to 900, so every weight on the page costs a
   single 113KB download instead of one file per weight. */
@font-face{
  font-family:"Roboto Slab";
  src:url("fonts/RobotoSlab-var.woff2?v=2026-08-21-r126") format("woff2");
  font-weight:100 900;
  font-style:normal;
  font-display:swap;
}
@font-face{
  font-family:"Outfit";
  src:url("fonts/Outfit-var.woff2?v=2026-08-21-r126") format("woff2");
  font-weight:100 900;
  font-style:normal;
  font-display:swap;
}
/* ============================================================================
   Makiko Kanzaki — portfolio
   Version stamp: 2026-08-20-r1  (bump SITE_V in the script when an asset
   changes, or Safari serves the previous build over the LAN and the change
   looks like it did not happen)

   Built from the Figma frame "Portfolio Website" (367:9499) on a 40px grid.
   Token names and values are lifted verbatim from the design system so this
   page cannot claim a value the system does not have.
============================================================================ */

:root{
  /* ---- type ---- */
  /* Roboto Slab is the display face. Body copy stays on the system sans, which
     is the readable choice at 16px and costs nothing to load. Point --font-body
     at var(--font-display) to put the slab everywhere. */
  --font-body: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  /* The slab carries the reading voice: nav, the opening line, the eyebrow,
     the Tagalong fields, the chapter disciplines. */
  --font-display: "Roboto Slab", var(--font-body);
  /* Outfit carries the named things: your name, both section headings, chapter
     names, buttons, the years, the card titles. Self-hosted variable file, same
     as the slab, so both faces together cost two requests and no third party. */
  --font-heading: "Outfit", var(--font-body);

  --fs-xs:12px; --fs-sm:14px; --fs-md:16px; --fs-lg:20px; --fs-xl:24px; --fs-3xl:32px;
  --fs-heading:36px;        /* Tagalong */
  --fs-section:32px;        /* My Career Journey. Smaller because it is longer. */
  --fw-light:300; --fw-body:400; --fw-label:500; --fw-emphasis:700;
  --lh-body:1.5;

  /* ---- spacing. Named steps, not raw numbers, so "no gap here" stays a
         decision on the record rather than a bare 0 in a rule. ---- */
  --sp-none:0px; --sp-xxs:2px; --sp-xs:4px; --sp-sm:8px; --sp-md:12px; --sp-lg:16px;
  --sp-xl:20px; --sp-2xl:24px; --sp-3xl:28px; --sp-4xl:32px; --sp-5xl:40px;
  --sp-7xl:48px; --sp-8xl:56px;

  /* ---- radius ---- */
  --r-sm:8px; --r-xl:28px; --r-full:360px;

  /* ---- colour ---- */
  --surface-light-primary:#FFFFFF;
  --surface-light-secondary:#F2F3F6;
  --text-primary:#0F1117;
  --text-disabled:#0F111761;
  --border-light-quiet:#0F111714;
  --border-light-loud:#0F111724;
  --accent-edge:#27282A14;

  /* Chapter headers and years sit one step off the body ink. Their own names
     because they come from the timeline's type ramp, not the body ramp. */
  --ink-timeline:#202022;
  --ink-timeline-quiet:rgba(32,32,34,.85);

  --accent-pink:#FFCDD0;    /* nav hover fill, active pagination dot */
  /* Same hue as --accent-pink, dark enough to be text. 5.73:1 on the page
     background, 6.36:1 on a card. */
  --accent-pink-dark:#A8383F;
  --focus-ring:#285FC6;   /* Figma info/500. Red read as an error state. */    /* 6.3:1 on the page, so the ring is still a ring */
  --on-accent-pink:#8F2F35; /* pink-dark on the pink fill is 4.49; this is 5.7 */
  --accent-pink-mid:#E5776F;  /* active pagination dot */
  --accent-blue:#B4CEFF;    /* hero buttons */

  --shadow-sm:0 4px 8px rgba(0,0,0,.12);

  /* ---- layout ---- */
  --gutter:80px;
  /* The timeline keeps its own inset so it can run nearly edge to edge on a
     wide display while the nav and hero stay centred on --hero-max. */
  --track-gutter:var(--gutter);            /* page margin, 2 grid units */
  --page-top:60px;          /* clear space above the nav band */
  --hero-top:0px;           /* the nav band's own lower half is the gap */
  --intro-gap:90px;         /* intro to the Latest work block */
  --hero-bottom:50px;       /* buttons to the timeline heading */
  --intro-w:760px;         /* measure for the opening line */
  --measure-wide:492px;    /* span-3 card copy */
  --measure-modal:600px;   /* modal body copy, wider than the cards */
  /* The hero art sits in a fixed rectangle with no fill of its own. The image
     is contained inside it, so swapping the artwork for something a different
     shape cannot move the copy beside it. Both on the 40 grid. */
  --hero-art-w:480px;
  --hero-art-h:320px;
  --hero-art-max:760px;    /* the image stops growing here */
  --hero-col-min:340px;    /* the two hero buttons, 159 + 16 + 164 */
  --hero-col-max:640px;    /* the text column */
  /* 640 text + 100 gap + 760 image. Past this the nav and the hero centre in
     the page rather than letting the two halves drift further apart. */
  --hero-max:1500px;
  --btn-h:44px;             /* hero buttons */
  --arw-size:40px;          /* timeline arrows */
  --header-height:60px;     /* nav band, rule sits on its bottom edge */
  --nav-fill-inset:14px;    /* fill hugs the 24px line box like the pill does */
  --nav-fill-inset-x:2px;   /* 2px each side, so adjacent fills sit 4px apart */
  /* Flip these two together: uppercase wants .02em, sentence case wants none. */
  --nav-case:none;
  --nav-tracking:normal;
  --fs-name:20px;           /* the wordmark outranks the nav links */
  --col:320px;             /* one timeline column */
  --col-gap:40px;          /* one grid unit */
  --chapter-gap:20px;      /* chapter header down to its card */
  /* Space under a section heading, down to whatever it introduces. Tagalong
     and My Career Journey are the same kind of thing, so they get the same
     gap. */
  --section-gap:var(--sp-2xl);
  --axis-gap:36px;         /* card down to its year */
  --track-pad-b:12px;      /* exactly the reach of the card shadow, 0 4 8 */
  --year-h:36px;
  --fs-year:16px;          /* one step under the chapter name above it */
  --year-ink:25px;         /* measured glyph height of digits at --fs-year */
  --year-pad-x:12px;       /* how far the mask reaches past the digits */
  --card-h:472px;
  --logo-h:140px;          /* front-face logo box */
  --work-h:240px;          /* front-face product image */
  --header-h:80px;         /* chapter header block */

  --dur:220ms;
  /* Front fills the 320x140 slot the way the Figma component does; the turned
     card drops back to the 160x70 the copy leaves room for. */
  --logo-front:119px;
  --logo-hover:70px;
  --logo-top:10px;
  --logo-top-hover:12px;
  --title-top:26px;        /* confidential card: name sits lower than a logo would */
  --card-delay:250ms;      /* pause before a card turns, so sweeping the track
                               does not set every card off in turn */
  --ease:cubic-bezier(.2,.6,.2,1);
}

*{box-sizing:border-box}
/* contain on the track is not enough in Safari: its back-swipe is a gesture
   above the scroll chain, so a swipe past either end of the timeline left the
   site. none on the root turns the gesture off for the page. */
html{-webkit-text-size-adjust:100%;overscroll-behavior-x:none}
body{
  margin:0;
  background:var(--surface-light-secondary);
  color:var(--text-primary);
  font-family:var(--font-body);
  font-size:var(--fs-md);
  line-height:var(--lh-body);
  -webkit-font-smoothing:antialiased;
  text-rendering:optimizeLegibility;
}
img{display:block;max-width:100%}
button{font:inherit;color:inherit;background:none;border:0;margin:0;padding:0}
a{color:inherit}

/* A control's outline is its shape. Focus is drawn, never removed. */
:focus-visible{outline:2px solid var(--focus-ring);outline-offset:3px;border-radius:var(--r-sm)}
/* Focus moved by script, not by a person. Safari counts a programmatic focus
   as focus-visible, which drew a ring on the button behind a modal every time
   it closed. The class is put on for that one move and taken off at the next
   key press, so a keyboard still rings everything it lands on. */
.is-quiet-focus:focus-visible{outline:none}

/* The page margin, but never letting the content pass --hero-max. Above that
   the extra width goes to the margins and the block centres. */
.wrap{padding-inline:max(var(--gutter), calc((100% - var(--hero-max)) / 2))}

/* ---------------------------------------------------------------- nav ---- */
.nav{
  display:flex;align-items:center;justify-content:space-between;
  gap:var(--sp-5xl);
  height:var(--header-height);
}
/* Padding on the header rather than a margin on the nav. As a margin it
   collapsed out through body, so body's box began 60px down the page and a
   link to #top landed at 60 instead of 0. */
header.wrap{padding-top:var(--page-top)}
.nav__name{
  /* A link on every page but the one it points at, so it has to shed the
     underline and keep the ink colour. */
  text-decoration:none;color:inherit;
  font-family:var(--font-heading);font-size:var(--fs-name);font-weight:var(--fw-emphasis);
  text-transform:var(--nav-case);letter-spacing:var(--nav-tracking);
  height:var(--header-height);display:flex;align-items:center;
}
.nav__list{display:flex;gap:var(--sp-none);list-style:none;margin:0;padding:0}
.nav__link{
  display:flex;align-items:center;justify-content:center;gap:var(--sp-xs);
  padding-inline:var(--sp-lg);height:var(--header-height);border-radius:var(--r-sm);
  font-family:var(--font-display);font-size:var(--fs-sm);text-decoration:none;white-space:nowrap;
  text-transform:var(--nav-case);letter-spacing:var(--nav-tracking);
}
.nav__link{position:relative;isolation:isolate}
.nav__link::before{
  content:"";position:absolute;
  inset:var(--nav-fill-inset) var(--nav-fill-inset-x);
  border-radius:var(--r-sm);
  background:var(--accent-pink);
  opacity:0;transition:opacity var(--dur) var(--ease);
  z-index:-1;
}
.nav__link:hover::before,
.nav__link:focus-visible::before{opacity:1}
.nav__link .icon{width:20px;height:20px;flex:0 0 auto}

/* The copy icon has to mean copy, so the link copies the address instead of
   launching a mail client. The href stays a mailto so right-click, middle-click
   and no-JS all still do something sensible. */
.nav__link.is-copied::before{opacity:1}
/* One 20px slot, two glyphs stacked in it, so the swap cannot reflow the nav. */
.nav__icons{position:relative;width:20px;height:20px;flex:0 0 auto}
.nav__icons .icon{position:absolute;inset:0;transition:opacity var(--dur) var(--ease)}
.icon--check{opacity:0}
.is-copied .icon--copy{opacity:0}
.is-copied .icon--check{opacity:1}

/* The nav rule is out. The class stays because the timeline head draws the
   same hairline, and restoring the header's is one <hr> in the markup. */
.rule{height:1px;background:var(--border-light-loud);border:0;margin:0}
.nav-rule{margin-top:var(--sp-none)}

/* --------------------------------------------------------------- hero ---- */
.hero{padding-block:var(--hero-top) var(--hero-bottom)}
.hero__intro{
  max-width:var(--intro-w);margin:0 0 var(--intro-gap);
  font-family:var(--font-display);font-weight:var(--fw-light);
  font-size:var(--fs-md);
}
.hero__grid{
  display:grid;
  grid-template-columns:minmax(var(--hero-col-min), var(--hero-col-max)) minmax(var(--hero-art-w), 1fr);
  column-gap:var(--sp-5xl);
  row-gap:var(--section-gap);
  align-items:stretch;
}
/* Three children rather than a nested column, so the image can slot between
   the heading and the copy once the layout stacks. */
.hero__id{grid-column:1;grid-row:1;display:flex;flex-direction:column;gap:var(--sp-sm);align-items:flex-start}
.hero__body{grid-column:1;grid-row:2;display:flex;flex-direction:column;gap:var(--sp-2xl)}

.eyebrow{
  font-family:var(--font-display);font-weight:600;font-size:var(--fs-xs);
  letter-spacing:.6px;text-transform:uppercase;
  color:var(--accent-pink-dark);
}
.hero__title{
  display:flex;align-items:center;gap:var(--sp-sm);
  margin:0;font-family:var(--font-heading);font-weight:600;font-size:var(--fs-heading);
  line-height:1.15;letter-spacing:-.01em;
}

.field{
  display:flex;flex-direction:column;gap:var(--sp-sm);
  font-family:var(--font-display);font-weight:var(--fw-light);
}
.field__label{margin:0;font-weight:600}
.field p{margin:0}

.btnrow{display:flex;gap:var(--sp-lg);align-items:center;flex-wrap:wrap}
.btn{
  display:inline-flex;align-items:center;justify-content:center;gap:var(--sp-xs);
  height:var(--btn-h);padding-inline:var(--sp-xl);
  background:var(--accent-blue);
  border:1px solid var(--border-light-quiet);
  border-radius:var(--r-sm);
  font-family:var(--font-heading);font-weight:600;font-size:var(--fs-md);
  letter-spacing:-.16px;line-height:1.3;
  cursor:pointer;text-decoration:none;
  transition:filter var(--dur) var(--ease);
}
.btn:hover{filter:brightness(.96)}
.btn .icon{width:20px;height:20px;flex:0 0 auto}

.hero__frame{
  grid-column:2;grid-row:1 / span 2;
  width:100%;max-width:var(--hero-art-max);min-height:var(--hero-art-h);
  margin-inline-start:auto;
  display:flex;align-items:flex-end;justify-content:center;
}
.hero__art{max-width:100%;max-height:100%;width:auto;height:auto;object-fit:contain}

/* The panel is centred on the button that opens it, so it needs a box that is
   exactly the button's width to measure from. It floats over the copy: in the
   flow it grew the hero by 277px on opening and shoved the timeline down. */
.qrwrap{position:relative;display:inline-flex}
.qr{
  position:absolute;left:50%;bottom:100%;z-index:2;
  /* Off-scale: 24 sat too close to the button, 28 read as a gap of its own. */
  margin-bottom:26px;
  width:max-content;
  padding:var(--sp-xl);
  background:var(--surface-light-primary);
  border-radius:var(--r-xl);
  box-shadow:var(--shadow-sm);
  text-align:center;
  opacity:0;transform:translate(-50%, 6px);
  transition:opacity var(--dur) var(--ease), transform var(--dur) var(--ease);
}
.qr.is-open{opacity:1;transform:translateX(-50%)}
.qr__label{
  margin:0 0 var(--sp-md);
  font-family:var(--font-heading);font-size:var(--fs-sm);
  color:var(--ink-timeline);
}
.qr__img{display:block;width:160px;height:160px}

.foot{
  padding-block:var(--sp-2xl);
  font-family:var(--font-heading);font-weight:var(--fw-light);
  font-size:var(--fs-sm);color:var(--ink-timeline-quiet);
}
.foot p{margin:0}

/* ----------------------------------------------------------- timeline ---- */
.tl{padding-block:var(--sp-5xl) var(--sp-8xl)}
.tl__head{
  position:relative;
  display:flex;align-items:center;gap:var(--sp-xl);
  /* Left edge matches the hero block. The right side is open so the rule can
     run to the window edge without needing to know the inset. */
  padding-left:max(var(--gutter), calc((100% - var(--hero-max)) / 2));
  padding-right:var(--sp-none);
}
/* On a wide display the heading sits inboard of the timeline, so this carries
   the same hairline across the gap above the first cards. Zero width, and so
   invisible, until the block starts centring. */
.tl__head::before{
  content:"";position:absolute;top:50%;height:1px;
  left:var(--track-gutter);
  right:calc(100% - max(var(--gutter), (100% - var(--hero-max)) / 2) + var(--sp-xl));
  background:var(--border-light-loud);
}
.tl__title{
  margin:0;font-family:var(--font-heading);font-weight:600;font-size:var(--fs-section);
  line-height:1.15;letter-spacing:-.01em;
}
.tl__arrows{display:flex;gap:var(--sp-md)}
.arw{
  display:flex;align-items:center;justify-content:center;
  width:var(--arw-size);height:var(--arw-size);
  background:var(--surface-light-primary);
  border:1px solid var(--accent-edge);
  border-radius:var(--r-full);
  cursor:pointer;
  transition:opacity var(--dur) var(--ease), box-shadow var(--dur) var(--ease);
}
.arw:hover:not(:disabled){box-shadow:var(--shadow-sm)}
.arw:disabled{cursor:default}
.arw:disabled .icon{opacity:.3}
.arw .icon{width:24px;height:24px;transition:opacity var(--dur) var(--ease)}
.tl__rule{
  flex:1;height:1px;background:var(--border-light-loud);
  margin-right:var(--sp-none);
  transition:margin-right var(--dur) var(--ease);
}
/* At the end of the track the rule stops where the last card stops. */
.tl.is-at-end .tl__rule{margin-right:var(--track-gutter)}

/* The track is a real scroller. Nothing intercepts the wheel or the trackpad,
   so drag, shift-wheel, arrow keys and the buttons all work and none of them
   can desync from the layout. */
.track{
  display:grid;
  grid-auto-flow:column;
  grid-auto-columns:var(--col);
  gap:var(--chapter-gap) var(--col-gap);
  overflow-x:auto;
  overscroll-behavior-x:contain;
  /* No scroll-snap. Mandatory snapping made Safari stutter through a swipe
     and yanked the track to a stop the moment the fingers lifted. Free
     scrolling under the hand; the arrows and the dots still land a card on
     the gutter, which is where snapping actually earns its keep. */
  scroll-padding-inline-start:var(--track-gutter);
  padding-inline:var(--track-gutter);
  padding-block:var(--section-gap) var(--track-pad-b);
  scrollbar-width:none;
}
.track::-webkit-scrollbar{display:none}

.chapter{
  grid-row:1;
  height:var(--header-h);
  display:flex;flex-direction:column;align-items:center;justify-content:center;
  gap:var(--sp-xxs);text-align:center;
}
.chapter__discipline{
  /* 400, not the 300 the duration uses. The slab's light and Outfit's light do
     not land on the same weight, so matching the numbers read as a mistake
     rather than a pairing. */
  margin:0;font-family:var(--font-display);font-weight:var(--fw-body);
  font-size:var(--fs-sm);line-height:20px;color:var(--ink-timeline-quiet);
}
.chapter__name{
  margin:0;font-family:var(--font-heading);font-weight:600;
  font-size:var(--fs-lg);line-height:24px;color:var(--ink-timeline);
}
.chapter__duration{
  /* Matched to the year rather than sitting a step under it. */
  margin:0;font-family:var(--font-heading);font-weight:var(--fw-light);
  font-size:var(--fs-sm);line-height:var(--lh-body);
  color:var(--ink-timeline-quiet);
}
/* Only shown once the year axis is gone. */
.chapter__year{
  display:none;margin:0;
  /* Outfit, like the year on the axis it replaces. */
  font-family:var(--font-heading);font-weight:600;font-size:var(--fs-md);
  color:var(--ink-timeline-quiet);
}
/* Hulu is one chapter across three cards, so its header spans the columns its
   cards occupy rather than repeating three times. */
.chapter--span3{grid-column:span 3}
.card--span3{grid-column:span 3}
/* Three shots share the front face of the wide card. */
.card__work--row{display:grid;grid-auto-flow:column;grid-auto-columns:var(--col);gap:var(--col-gap)}
.card__work--row img{width:100%;height:100%;object-fit:contain;justify-self:center}
/* A 1040 card is 112 characters to a line, which is unreadable. Hold the
   copy to a normal measure and centre it; the card has the height to spare. */
.card--span3 .card__body{padding-inline:var(--sp-xl)}
.card--span3 .card__body p{max-width:var(--measure-wide);margin-inline:auto}

/* ------------------------------------------------------------- cards ---- */
.card{
  grid-row:2;
  position:relative;
  height:var(--card-h);
  background:var(--surface-light-primary);
  border-radius:var(--r-xl);
  box-shadow:var(--shadow-sm);
  overflow:hidden;
  text-align:left;
  width:100%;
}
/* Only the cards that lead somewhere are controls. The rest are content and
   must not lift or show a pointer, or people click them and get nothing. */
.card--static{cursor:default}

.card__logo{
  position:absolute;left:0;top:0;
  width:100%;height:var(--logo-h);
  display:flex;align-items:flex-start;justify-content:center;
  padding-top:var(--logo-top);
  transition:padding-top var(--dur) var(--ease);
}
.card__logo img{
  max-height:var(--logo-front);max-width:272px;width:auto;object-fit:contain;
  transition:max-height var(--dur) var(--ease);
}

/* Front face: the product. */
.card__work{
  position:absolute;left:0;top:var(--logo-h);
  width:100%;height:var(--work-h);
  display:flex;align-items:center;justify-content:center;
  opacity:1;transition:opacity var(--dur) var(--ease);
}
.card__work img{max-width:100%;max-height:100%;object-fit:contain}

/* Back face: the words. Hidden with opacity rather than display:none so it
   stays in the accessibility tree and a screen reader reads it either way. */
.card__body{
  position:absolute;left:0;right:0;top:82px;bottom:0;
  padding:0 var(--sp-xl);
  opacity:0;pointer-events:none;
  transition:opacity var(--dur) var(--ease);
}
.card__body p{margin:0;font-family:var(--font-display);font-weight:var(--fw-light)}
/* A hyphenated compound is a legal break point, so the browser was splitting
   "next-generation" and "end-to-end" across two lines and leaving a short
   ragged line behind. These two hold together. */
.nobr{white-space:nowrap}
/* Cards with a CTA end the copy box above its band, or the last lines run
   underneath it. 40 control + 40 bottom inset + 16 breathing room. */
.card:has(.card__action) .card__body,
.card:has(.card__note) .card__body{bottom:96px}

/* Hover on desktop, focus for the keyboard. Same reveal, two input methods,
   because a keyboard user never hovers and would otherwise never reach the
   CTA or read a word of the copy. */
.card:hover .card__work,
.card:focus-within .card__work,
.card:hover .card__body,
.card:focus-within .card__body,
.card:hover .card__logo,
.card:focus-within .card__logo,
.card:hover .card__logo img,
.card:focus-within .card__logo img{transition-delay:var(--card-delay)}

.card:hover .card__work,
.card:focus-within .card__work{opacity:0}
.card:hover .card__body,
.card:focus-within .card__body{opacity:1;pointer-events:auto}
.card:hover .card__logo,
.card:focus-within .card__logo{padding-top:var(--logo-top-hover)}
.card:hover .card__logo img,
.card:focus-within .card__logo img{max-height:var(--logo-hover)}

.card__action{
  position:absolute;left:50%;transform:translateX(-50%);bottom:var(--sp-5xl);
  width:220px;height:40px;
  display:inline-flex;align-items:center;justify-content:center;gap:var(--sp-xs);
  padding:var(--sp-sm) var(--sp-xl);
  background:var(--surface-light-secondary);
  border:1px solid var(--border-light-quiet);
  border-radius:var(--r-sm);
  /* Same typography as the hero buttons. Only the fill differs. */
  font-family:var(--font-heading);font-weight:600;font-size:var(--fs-md);
  letter-spacing:-.16px;line-height:1.3;
  cursor:pointer;
  transition:filter var(--dur) var(--ease);
}

.card__action:hover{filter:brightness(.97)}
/* Each CTA carries its project's own wash, left to right, in place of the flat
   secondary surface. Her stops, from the Figma gradient set. */
.card__action[data-modal="hulu"]{background:linear-gradient(90deg, #EEFFD8, #F5FED5)}
.card__action[data-modal="vessel"]{background:linear-gradient(90deg, #E9F7FF, #E2FCFF)}
.card__action[data-modal="verizon"]{background:linear-gradient(90deg, #FFF0F0, #FFF2ED)}
.card__action[data-modal="servicenow"]{background:linear-gradient(90deg, #E8FFE2, #E9FFDA)}
.card__action[data-modal="tagalong"]{background:linear-gradient(90deg, #EBF2FF, #ECF7FF)}
.card__action .icon{width:24px;height:24px}

/* Without this the card reads as a broken hover: no logo, no image, no
   reveal. The badge says the emptiness is the point. 40 note inset + 21 note
   line + 16 gap. */
.card__badge{
  position:absolute;left:0;right:0;
  bottom:calc(var(--sp-5xl) + 21px + var(--sp-lg));
  margin:0;text-align:center;
}
.card__badge span{
  display:inline-block;padding:var(--sp-xs) var(--sp-md);
  border-radius:var(--r-full);
  background:var(--accent-pink);color:var(--on-accent-pink);
  font-family:var(--font-heading);font-weight:var(--fw-label);
  font-size:var(--fs-xs);line-height:1.4;
}

.card__note{
  position:absolute;left:0;right:0;bottom:var(--sp-5xl);
  margin:0;text-align:center;font-family:var(--font-heading);font-weight:var(--fw-light);
  font-size:var(--fs-sm);color:var(--ink-timeline-quiet);
}

/* Lunar is confidential: no product image, no destination. Its front face is
   the label itself, so it never crossfades. */
.card--confidential .card__body{opacity:1;pointer-events:auto}
/* No logo to show, so the name stands in for one. It sits in the band the
   logo occupies on hover, starting at --logo-top-hover and bottoming where
   the Tagalong wordmark bottoms, so both cards break to copy on one line. */
.card--confidential .card__title{
  position:absolute;left:0;right:0;top:var(--title-top);
  margin:0;padding-inline:var(--sp-xl);text-align:center;
  font-family:var(--font-heading);font-weight:600;font-size:var(--fs-md);
  line-height:22px;
  color:var(--ink-timeline);
}

/* -------------------------------------------------------------- axis ---- */
.axis{
  position:relative;
  margin-top:calc(var(--axis-gap) - var(--track-pad-b) - (var(--year-h) - var(--year-ink)) / 2);
  padding-inline:var(--track-gutter);
  overflow:hidden;
}
.axis__line{position:absolute;left:var(--track-gutter);right:var(--track-gutter);
  top:calc(var(--year-h) / 2);height:1px;background:var(--border-light-loud)}
.axis__years{
  display:grid;grid-auto-flow:column;grid-auto-columns:var(--col);gap:var(--col-gap);
  width:max-content;
}
.year{
  height:var(--year-h);display:flex;align-items:center;justify-content:center;
  width:max-content;margin-inline:auto;padding-inline:var(--year-pad-x);
  background:var(--surface-light-secondary);
  font-family:var(--font-heading);font-weight:600;font-size:var(--fs-year);
  line-height:1;
  color:var(--ink-timeline);
  position:relative;z-index:1;
}

/* --------------------------------------------------------- pagination ---- */
.dots{display:flex;justify-content:center;gap:0;margin-top:var(--sp-lg)}
/* 18 wide holds an 8px gap between the 10px dots. Height stays 40 so the
   hit target keeps some size vertically. */
.dot{
  width:18px;height:40px;display:flex;align-items:center;justify-content:center;
  cursor:pointer;
}
.dot::before{
  content:"";width:10px;height:10px;border-radius:var(--r-full);
  background:var(--border-light-loud);
  transition:background var(--dur) var(--ease);
}
.dot[aria-current="true"]::before{background:var(--accent-pink-mid)}

/* Past --hero-max the nav and hero are centred, so the timeline is free to run
   nearly edge to edge on its own inset. */
@media (min-width:1661px){
  :root{--track-gutter:var(--sp-8xl)}
  .dots{display:none}
}

/* 80 + 340 + 40 + 480 + 80. Below this the text column cannot shrink further
   without the two hero buttons wrapping, so the hero stacks. */
@media (max-width:1020px){
  .hero__grid{grid-template-columns:minmax(0,1fr);row-gap:var(--section-gap)}
  /* Heading, then the image, then the copy. */
  .hero__id{grid-column:1;grid-row:1}
  .hero__frame{
    grid-column:1;grid-row:2;
    width:90%;max-width:640px;min-height:0;margin-inline:auto;
    padding-top:var(--sp-2xl);
  }
  .hero__body{grid-column:1;grid-row:3}
  .hero__art{max-height:none}
}

/* ========================================================== mobile ========
   Below 900 the two-face card is wrong: there is no hover to turn it with, so
   both faces are shown at once and the card grows to fit. The horizontal track
   becomes a vertical list, which is the one place the timeline can afford to
   be long.
========================================================================== */
@media (max-width:900px){
  :root{
    --gutter:24px;
    --page-top:32px;
    --intro-gap:48px;
    --hero-bottom:48px;
    --hero-art-w:100%;
    --hero-art-h:auto;
    --art-scale:1.12;
    --section-gap:var(--sp-5xl);
    --logo-pad-top:33px;     /* lands the mark 40 below the card top: 33 padding
                                minus the 28 pulled back, plus the frame the
                                PNG carries above its own ink */
    /* 44 tall link, 32 tall pill, so 6 each side rather than the 14 that suits
       the 60 tall desktop band. */
    --nav-fill-inset:6px;
  }

  /* Name over links, both centred: in one column the page's own headings are
     centred, so a flush-left nav read as a different page. */
  .nav{height:auto;flex-direction:column;align-items:center;gap:var(--sp-sm)}
  .nav__name{height:auto}
  /* Four links end to end are 353 wide, which ran off the side of any phone
     narrower than 393 and took the document with it. Tighter padding first,
     wrapping as the backstop. */
  .nav__list{justify-content:center;flex-wrap:wrap}
  .nav__link{height:44px;padding-inline:var(--sp-md)}
  .nav{margin-bottom:var(--sp-lg)}

  .hero__intro{font-size:var(--fs-md)}
  .hero__title{font-size:var(--fs-3xl)}
  .btnrow{width:100%}
  /* The rule beside the heading has nowhere to run in one column. */
  .tl__rule,.tl__head::before{display:none}
  /* The three page-level headings centre together: the nav, Latest work over
     Tagalong, and My Career Journey. The running copy stays flush left. */
  /* The desktop head is padded on the left only, so the rule can run to the
     window edge. Centred in one column it needs the matching inset back. */
  .tl__head{justify-content:center;text-align:center;padding-right:var(--gutter)}
  .hero__id{align-items:center;text-align:center}

  .tl__title{font-size:var(--fs-xl)}
  /* Nothing to page through and nothing to bleed toward. */
  .tl__arrows,.dots,.axis{display:none}
  .tl__rule{margin-right:var(--sp-none)}

  .track{
    display:block;overflow:visible;
    scroll-padding-inline-start:0;
    padding-inline:var(--gutter);
  }

  /* In one column each chapter carries the gap that separates it from the card
     above, so the track must not add a second one at the very top. */
  .track{padding-block-start:var(--sp-none)}
  .chapter{
    height:auto;align-items:center;text-align:center;
    padding-block:var(--section-gap) var(--sp-md);
  }
  .chapter__discipline,.chapter__duration{width:auto}
  /* Four lines, four faces, four line-heights, so an even gap between the
     BOXES leaves the ink unevenly spaced. These nudges even the ink out
     instead, which is what the eye is actually reading: 6 / 8 / 8. */
  .chapter__name{margin-top:-1px}
  .chapter__year{margin-top:-2.5px}
  .chapter__duration{margin-top:-3.5px}
  .chapter__year{display:block}

  /* Static flow, both faces, natural height. The desktop card stacks its two
     faces on top of each other with absolute positioning; here they simply
     follow one another. */
  .card{height:auto;padding-bottom:var(--sp-5xl)}
  .card__logo,.card__work,.card__body{position:static;opacity:1;pointer-events:auto}
  /* Full-size logo on the phone. There is no hover here, so the shrunk hover
     size just left the mark swimming in white. Padding matches the 24 the copy
     and the product shot use on the sides. --art-scale lifts the logo and the
     shot together, so the balance drawn into the canvases is preserved. */
  /* Static in one column: the same size at rest and on a tap, so nothing moves.
     --logo-front unscaled, which is the size that appeared on tap before. */
  .card__logo{height:auto;padding-block:var(--logo-pad-top) var(--sp-lg);align-items:center}
  .card__logo img{
    max-height:var(--logo-front);
    max-width:min(272px, 100%);
    /* Every logo PNG is a 960x420 canvas with the same dead frame around the
       mark: 100px above, 60px below. That margin is not design, it is export
       padding, and on the phone it was adding ~50px of nothing to each card.
       Pulled back here so the 24 above and below is the real gap. Relative
       scale between the marks is untouched, since every file has the same
       frame. */
    margin-block:
      calc(var(--logo-front) * -100 / 420)
      calc(var(--logo-front) * -60 / 420);
  }
  .card__work{
    height:auto;
    /* The scaled width, but never closer than 8px to the card edge. */
    padding-inline:max(var(--sp-sm), calc((100% - (100% - var(--sp-2xl) * 2) * var(--art-scale)) / 2));
  }
  /* Same story: 960x720 canvases with 60px of dead frame top and bottom. The
     image fills its box exactly, so a percentage of that box is exact. */
  .card__work img{margin-block:-6.25%}
  .card__work{margin-top:calc(var(--sp-lg) * -1)}
  /* One column shows the 2012 laptop only. Stacking all three made the Hulu
     card 840 tall, half again the height of any other. */
  .card__work--row{
    grid-auto-flow:row;grid-template-columns:minmax(0,1fr);
    grid-auto-columns:auto;gap:var(--sp-2xl);
  }
  .card__work--row img:not(:last-child){display:none}
  .card__work--row img{height:auto;width:100%;max-width:100%}
  .card__work img{max-width:100%;height:auto}
  .card__body{padding:var(--sp-lg) var(--sp-2xl) 0}
  .card--span3 .card__body{padding-inline:var(--sp-2xl)}
  /* display:flex, not the inline-flex it uses on desktop: an inline-level box
     ignores auto margins, so the button sat hard left instead of centring. */
  .card__action{position:static;transform:none;display:flex;width:220px;margin:var(--sp-2xl) auto 0}
  .card--span3 .card__action{transform:none;left:auto}
  .card__badge{position:static;margin-top:var(--sp-2xl)}
  .card__note{position:static;margin-top:10px}
  .foot{text-align:center}
  .card--confidential .card__body{padding-top:0}
  .card--confidential .card__title{
    position:static;padding:var(--sp-5xl) var(--sp-2xl) var(--sp-2xl);
    /* It is standing in for a logo, and the logos grew here, so it does too. */
    font-size:var(--fs-lg);line-height:26px;
  }

  /* No hover on touch, so the card must not react to one. */
  .card:hover .card__logo,.card:focus-within .card__logo{padding-top:var(--logo-pad-top)}
  .card:hover .card__logo img,.card:focus-within .card__logo img{max-height:var(--logo-front)}
  /* Nothing to animate to in one column, and a tap leaves :hover stuck on iOS,
     so the delay and the transition both come off. */
  .card__logo,.card__logo img{transition:none}
  .card:hover .card__logo,.card:focus-within .card__logo,
  .card:hover .card__logo img,.card:focus-within .card__logo img{transition-delay:0s}
  .card:hover .card__work,.card:focus-within .card__work{opacity:1}

  /* The year rides with its card instead of on a shared axis. */
  .stop-year{
    display:block;margin:0;padding-top:var(--sp-md);
    font-family:var(--font-heading);font-size:var(--fs-md);color:var(--ink-timeline-quiet);
  }
}


/* --------------------------------------------------- reduced motion ----- */
/* The rule is no large movement, not no animation. The crossfade stays so the
   two faces do not snap, the logo simply stops travelling. */
/* 159 + 16 + 164 + the 20 gutters. Above this the two buttons sit side by
   side; below it they have nowhere left to go. */
@media (max-width:380px){
  .btn{width:100%;justify-content:center}
  .qrwrap{width:100%}
}

@media (prefers-reduced-motion:reduce){
  .qr,.qr.is-open{transform:translateX(-50%);transition:opacity var(--dur) var(--ease)}
  :root{--dur:120ms}
  .card__logo,.card__logo img{transition:none}
}

/* ============================================================== about ====
   Three cards, then the What I do panel. The cards stretch to a common
   height and pin their pictures to the bottom, so the row of images sits on
   one line however long the paragraphs run.
========================================================================== */
/* No top padding: the opening line above carries --intro-gap, which is what
   puts the first card on the same line as the Latest work eyebrow next door. */
.about{padding-block:var(--sp-none) var(--sp-8xl)}
.about__grid{
  display:grid;
  /* 360 rather than 320: at 320 a 1500 wide page fits four columns and the
     row silently became four cards. */
  grid-template-columns:repeat(auto-fit,minmax(min(360px,100%),1fr));
  gap:var(--col-gap);
  align-items:stretch;
}
.about__card{
  display:flex;flex-direction:column;gap:var(--sp-2xl);
  padding:var(--sp-2xl);
  background:var(--surface-light-primary);
  border-radius:var(--r-xl);
  box-shadow:var(--shadow-sm);
}
.about__card p{
  margin:0;
  font-family:var(--font-display);font-weight:var(--fw-light);
  font-size:var(--fs-md);line-height:var(--lh-body);
}
/* Floated rather than a second column: the paragraph is the subject and the
   drawing is an aside to it. clear on the media row below stops the float
   reaching past its own card. */
/* height:auto matters: the img carries width and height attributes, and
   without it the attribute height wins over the CSS width and squashes the
   drawing. */
.about__aside{float:right;width:64px;height:auto;margin:0 0 var(--sp-md) var(--sp-lg)}
/* margin-top:auto is what lands every card's pictures on the same line. */
.about__media{clear:both;margin-top:auto;display:flex;gap:var(--sp-sm);
  align-items:flex-end;justify-content:center}
.about__media img{display:block;max-width:100%;height:auto;min-width:0}
/* Two photographs, equal halves, cropped to one shape so the pair sits level
   whatever the originals were. */
.about__media--pair{display:grid;grid-template-columns:1fr 1fr}
.about__media--pair img{width:100%;aspect-ratio:23 / 30;object-fit:cover;
  border-radius:var(--r-sm)}
/* Five screens across a 425 card is 75 each. They are a texture at that size,
   not something to read, which is why they carry their own alt text. */
.about__media--screens{gap:var(--sp-xs)}
.about__media--screens img{flex:1 1 0;min-width:0}
/* A grid, not the flex row the others use: three items each allowed
   max-width:100% of the row can add up to 300% and run off the card. */
.about__media--flow{display:grid;grid-template-columns:1fr auto 1fr;align-items:center}
.about__media--flow img{width:100%}
.about__media--flow .about__arrow{width:32px}

.about__panel{
  margin-top:var(--col-gap);
  padding:var(--sp-2xl);
  background:var(--surface-light-primary);
  border-radius:var(--r-xl);
  box-shadow:var(--shadow-sm);
}
.about__panel h2{
  margin:0 0 var(--sp-xl);
  font-family:var(--font-heading);font-weight:600;font-size:var(--fs-lg);
  line-height:1.3;
}
.about__rule{height:1px;background:var(--border-light-loud);border:0;margin:0 0 var(--sp-xl)}
.about__cols{
  display:grid;grid-template-columns:repeat(auto-fit,minmax(min(220px,100%),1fr));
  gap:var(--col-gap) var(--sp-5xl);
}
.about__cols h3{
  margin:0 0 var(--sp-md);
  font-family:var(--font-heading);font-weight:600;font-size:var(--fs-md);
  line-height:1.3;
}
.about__cols ul{margin:0;padding:0;list-style:none;
  display:flex;flex-direction:column;gap:var(--sp-sm)}
.about__cols li{
  position:relative;padding-left:var(--sp-lg);
  font-family:var(--font-display);font-weight:var(--fw-light);
  font-size:var(--fs-md);line-height:var(--lh-body);
}
/* A real bullet rather than the list marker, so the indent is on the grid
   rather than wherever the browser puts it. */
.about__cols li::before{
  content:"";position:absolute;left:0;top:calc(var(--lh-body) * 0.5em - 2px);
  width:4px;height:4px;border-radius:var(--r-full);
  background:var(--ink-timeline-quiet);
}

@media (max-width:900px){
  .about{padding-block:var(--sp-none) var(--sp-5xl)}
  .about__grid{gap:var(--sp-2xl)}
  .about__panel{margin-top:var(--sp-2xl)}
  .about__cols{gap:var(--sp-2xl)}
}

/* ============================================================== modal ====
   A native <dialog>: it brings the focus trap, Escape, inert background and
   the top layer with it, which is a lot of correct behaviour not to write.
   Content is cloned in from a <template> on open, so the pictures cost
   nothing until somebody asks for them.
========================================================================== */
.modal{
  /* Grows with the display so the pictures do. The copy does not follow: it
     stays capped at --measure-modal and centres, so a wider modal buys width
     for the artwork and nothing else. */
  width:min(1240px, calc(100vw - var(--sp-5xl)));
  /* Height rides the window rather than a fixed ceiling: 90% of it, with the
     modal free to be shorter when the piece is short. svh, not vh, so a mobile
     browser's toolbar sliding in does not crop the bottom off. */
  max-height:90svh;
  padding:var(--sp-none);
  border:0;border-radius:var(--r-xl);
  background:var(--surface-light-secondary);
  color:var(--text-primary);
  overflow:hidden;
}
/* Tagalong's frames are shot on white; the grey read as a second surface
   behind them. */
.modal[data-project="tagalong"],
.modal[data-project="servicenow"]{background:var(--surface-light-primary)}
.modal::backdrop{background:rgba(15,17,23,.55)}
.modal__inner{position:relative;display:flex;flex-direction:column;max-height:inherit}
/* The header does not scroll away: the close is the way out and it has to
   stay reachable from anywhere in a long piece. */
.modal__head{
  flex:0 0 auto;display:flex;align-items:flex-start;gap:var(--sp-xl);
  padding:var(--sp-4xl) var(--sp-5xl) var(--sp-xl);
  position:static;
  overflow:hidden;
  transition:height var(--dur) var(--ease), padding var(--dur) var(--ease);
  border-bottom:1px solid var(--border-light-quiet);
}
.modal__titles{position:relative;flex:1 1 auto;min-width:0;text-align:center}
.modal__eyebrow{
  margin:0 0 var(--sp-xs);
  font-family:var(--font-display);font-weight:600;font-size:var(--fs-xs);
  letter-spacing:.6px;text-transform:uppercase;color:var(--accent-pink-dark);
}
.modal__title{
  margin:0;font-family:var(--font-heading);font-weight:600;font-size:var(--fs-heading);
  line-height:1.15;letter-spacing:-.01em;
}
/* The mark stands in for the h2, which stays in the markup for the accessible
   name and for anyone the image does not reach. */
/* The logo PNGs are a fixed canvas with the mark inset: hulu's ink is 37% of
   its height. A 40px box drew a 15px wordmark, which is why it read small. */
/* The 90 box carries 33 of ink: 27 of air above it and 30 below. The negative
   block margins eat that air, so the numbers that read on screen are 12 to the
   eyebrow and 12 to the line under. Re-trimming the PNG would let these go. */
.modal__logo{display:block;height:90px;width:auto;max-width:280px;object-fit:contain;
  margin-inline:auto;margin-block:-19px -26px;
  transition:height var(--dur) var(--ease), margin var(--dur) var(--ease)}
/* Condensed: the mark alone at three quarters, the words out of the flow so
   the header can close over them rather than clipping them mid-line. Every
   number here is the full-size one times .75, the air in the PNG included. */
/* Nothing animates while the two heights are being read. */
.modal__head.is-measuring,
.modal__head.is-measuring *{transition:none !important}
.modal__head.is-compact{padding-block:var(--sp-lg)}
.modal__head.is-compact .modal__logo{height:67.5px;margin-block:-14.25px -19.5px}
.modal__head.is-compact .modal__eyebrow,
.modal__head.is-compact .modal__meta{
  position:absolute;left:0;right:0;margin:0;opacity:0;pointer-events:none;
}
.modal__head.is-compact .modal__eyebrow{top:0}
.modal__head.is-compact .modal__meta{bottom:0}
.modal__eyebrow,.modal__meta{transition:opacity var(--dur) var(--ease)}
@media (prefers-reduced-motion:reduce){
  .modal__head,.modal__logo,.modal__eyebrow,.modal__meta{transition:none}
}
.modal__title.is-hidden{
  position:absolute;width:1px;height:1px;overflow:hidden;
  clip-path:inset(50%);white-space:nowrap;
}
.modal__meta{
  margin:var(--sp-sm) 0 0;
  font-family:var(--font-heading);font-weight:var(--fw-light);font-size:var(--fs-sm);
  color:var(--ink-timeline-quiet);
}
/* Half the head's inset, and out of the flow: it is a control on the dialog,
   not a piece of the content, so it sits in the corner rather than in the row
   with the title. */
.modal__x{
  position:absolute;top:var(--sp-lg);right:var(--sp-lg);z-index:2;
  display:flex;align-items:center;justify-content:center;
  width:var(--arw-size);height:var(--arw-size);
  border:0;border-radius:var(--r-sm);
  background:none;cursor:pointer;
  transition:opacity var(--dur) var(--ease);
}
.modal__x:hover{opacity:.6}
.modal__x .icon{width:24px;height:24px}
.modal__body:focus,
.modal__body:focus-visible{outline:none}
.modal__body{
  flex:1 1 auto;overflow-y:auto;overscroll-behavior:contain;
  padding:var(--sp-4xl) var(--sp-5xl) 54px;
  display:flex;flex-direction:column;gap:var(--sp-2xl);
}
.modal__body p{
  margin:0 auto;max-width:var(--measure-modal);
  font-family:var(--font-display);font-weight:var(--fw-light);
  font-size:var(--fs-md);line-height:var(--lh-body);
}
.modal__lede{display:flex;flex-direction:column;gap:var(--sp-lg)}
.modal__shot{display:flex;flex-direction:column;gap:var(--sp-2xl)}
/* The stage is the export canvas, so every frame in a tray lands at the same
   scale and nothing is resized against its neighbours. 2300x950 is Hulu's; a
   project whose art is a different shape sets --shot-w and --shot-h in the markup. The
   art is transparent and the stage has no fill, so the slack around a frame is
   just the modal showing through. */
/* 24 of its own on top of the block's 24, so a tray sits clear of the words
   that run up to it. */
/* The body is a flex column with a 24 gap, which is right between paragraphs
   and too tight around a picture. The 8 on each side takes a tray to 32 top
   and bottom, evenly, rather than 48 above and 24 below. */
.shot{display:flex;flex-direction:column;gap:var(--sp-none);
  margin-block:calc(var(--sp-4xl) - var(--sp-2xl))}
/* A tray opening the piece sits straight under the head rule. The body's 32
   padding is pulled back off it, because a picture needs no run-up the way a
   first paragraph does. */
.modal__body > .modal__shot:first-child .shot{margin-block-start:calc(var(--sp-4xl) * -1)}
/* Vessel is the one piece that opens on a picture, and flush to the rule reads
   as cramped once the picture is a monitor on a light ground. 20 back, no more,
   so it still starts higher than a paragraph would. */
.modal[data-project="vessel"] .modal__body > .modal__shot:first-child .shot{
  margin-block-start:calc(var(--sp-xl) - var(--sp-4xl))
}
/* The Verizon phone frames run to the bottom of their canvas, so the dot row
   lands against them. Every other tray has transparent padding doing that job,
   or a label sitting in between. The dots clear the canvas by 24. */
.modal[data-project="verizon"] .shot__stage + .shot__dots{margin-top:9px}
/* Two trays back to back with nothing but a dot row between them: no words to
   say the first has ended, so they get 40 rather than 32. A block that ends in
   a paragraph, as Hulu's do, is not this case and keeps 32. */
.modal__shot:has(> .shot__dots:last-child) + .modal__shot .shot{
  margin-block-start:calc(var(--sp-5xl) - var(--sp-2xl))
}
.shot__stage{
  position:relative;aspect-ratio:var(--shot-w, 2300) / var(--shot-h, 950);
  overflow:hidden;
  /* Sideways belongs to the tray, up and down to the piece, and two fingers
     to the browser so a picture can be pinched open. */
  touch-action:pan-y pinch-zoom;
}
/* Zoomed in, the picture needs dragging more than the tray needs swiping. */
.is-zoomed .shot__stage{
  touch-action:auto;
}
.shot__img{
  position:absolute;inset:0;width:100%;height:100%;object-fit:contain;
  opacity:0;transition:opacity 600ms var(--ease);
}
.shot__img.is-on{opacity:1}
/* The same dot as the timeline's: 10px mark, 8px gap, 18 wide hit area. The
   40 tall hit area carries 15px of its own air on each side, so the row is
   pulled up by that plus 12 to sit 12 under the caption, and pulled down by
   15 to leave the 24 the block below asks for. */
.shot__dots{display:flex;justify-content:center;gap:var(--sp-none);margin-top:-27px;margin-bottom:-15px}
/* No caption above them, so there is nothing to pull past: the row sits under
   the picture with the same 12 a caption would have given it. */
.shot > .shot__dots{margin-top:-3px}
.shot__dot{
  width:18px;height:var(--sp-5xl);display:flex;align-items:center;justify-content:center;
  cursor:pointer;background:none;border:0;padding:0;
}
.shot__dot::before{
  content:"";width:10px;height:10px;border-radius:var(--r-full);
  background:var(--border-light-loud);
  transition:background var(--dur) var(--ease);
}
.shot__dot[aria-current="true"]::before{background:var(--accent-pink-mid)}
/* Nothing moves on its own for anyone who has asked for that. */
@media (prefers-reduced-motion:reduce){
  .shot__img{transition:none}
}
/* .modal__body p is (0,1,1) and was beating this at (0,1,0), so the label was
   drawing as slab 16 light like the body copy. */
.modal__body .modal__label{
  font-family:var(--font-heading);font-weight:600;font-size:var(--fs-sm);
  color:var(--ink-timeline);
}
.modal__label span{font-weight:var(--fw-light);color:var(--ink-timeline-quiet)}
/* A caption with no title above it: it carries the heading face at caption
   size, in the body's colour rather than the timeline's quieter ink. */
.modal__body .modal__cap{
  font-family:var(--font-heading);font-weight:var(--fw-light);font-size:var(--fs-sm);
  color:var(--text-primary);
}
/* The closing line and the button share a left edge. The body's paragraphs are
   centred blocks at a 600 measure, which put a short line 90px in while the
   button sat at 0, and the two read as a mistake. */
/* The closing line and its button share the running copy's measure and its
   left edge, rather than sitting out at the content edge on their own. */
.modal__foot{
  margin:0 auto;max-width:var(--measure-modal);
  display:flex;flex-direction:column;align-items:flex-start;gap:var(--sp-2xl);
}
/* The button is the last thing in the piece, so it centres rather than
   hanging off the left edge of a paragraph that has already ended. */
.modal__foot .modal__out{align-self:center}
/* A scrolling flex column shrinks its children once the content is taller than
   the box. That took 44 off the button and squeezed the pictures. Nothing in
   here should ever be compressed to fit: it scrolls instead. */
.modal__body > *{flex-shrink:0}
.modal__out{
  display:inline-flex;align-items:center;gap:var(--sp-xs);
  height:var(--btn-h);padding-inline:var(--sp-xl);
  background:var(--accent-blue);
  border:1px solid var(--border-light-quiet);border-radius:var(--r-sm);
  font-family:var(--font-heading);font-weight:600;font-size:var(--fs-md);
  letter-spacing:-.16px;line-height:1.3;text-decoration:none;
  transition:filter var(--dur) var(--ease);
}
.modal__out:hover{filter:brightness(.96)}
.modal__out .icon{width:20px;height:20px}

@media (max-width:900px){
  .modal{
    width:100vw;max-width:100vw;max-height:100svh;height:100svh;
    border-radius:var(--sp-none);
  }
  .modal__head{padding:var(--sp-2xl) var(--sp-2xl) var(--sp-lg)}
  .modal__x{top:var(--sp-md);right:var(--sp-md)}
  .modal__title{font-size:var(--fs-3xl)}
  .modal__logo{height:66px;margin-block:calc(var(--sp-sm) * -1)}
  .modal__head.is-compact .modal__logo{height:49.5px;margin-block:-6px}
  /* One phrase per line rather than one long line broken wherever it runs
     out: the dash is a separator the line breaks already do, so it goes. */
  /* A tray with a mobile version of its frames also has a mobile canvas, which
     is a different shape, so the stage has to follow it. Trays with no mobile
     set fall back to the desktop numbers. */
  .shot__stage{aspect-ratio:var(--shot-w-sm, var(--shot-w, 2300)) / var(--shot-h-sm, var(--shot-h, 950))}
  .modal__meta .meta__part{display:block}
  .modal__meta .meta__sep{display:none}
  /* Stacked, the button sits in the left half of the row, so a panel centred
     on it hung off the edge. The row becomes the box it measures from, which
     is the content column, which is centred in the viewport. The vertical
     anchor does not move: the row is exactly one button tall. */

  .modal__body{padding:var(--sp-4xl) var(--sp-2xl) 54px;gap:var(--sp-2xl)}
  /* The tray alone breaks the body's 24 inset by half, so the artwork reads
     larger on a phone without the caption or the copy moving with it. */
  .shot{margin-inline:calc(var(--sp-md) * -1)}
}
@media (prefers-reduced-motion:reduce){
  .modal__x{transition:none}
}
