/* =========================================================================
   Palettes + the theme switch.
   Loaded AFTER each page's own <style> so these tokens win over the
   hardcoded values still sitting in the case-study stylesheets.
   ========================================================================= */

/* ---- the four palettes · corners of the mark, in click order ---- */
:root,
[data-theme="plum"] { --paper:#3a1326; --hi:#b2d2ff; --base:#a8bbd6; --slot:#462d39; }
[data-theme="green"]{ --paper:#514d31; --hi:#dcd3ba; --base:#ded0a4; --slot:#46442d; }
/* cream is the one light theme: hi/base darkened to clear AA on a pale ground,
   and --muted mixes toward the INK rather than the paper (see below). */
[data-theme="cream"]{ --paper:#e9e3d9; --hi:#ac4708; --base:#97552c; --slot:#46302d;
                      --muted-mix:#3a2a1c; }
[data-theme="teal"] { --paper:#122f36; --hi:#dcc6d7; --base:#d2aaca; --slot:#2d4246; }

/* ---- roles the case studies use, derived so every palette gets them free ---- */
:root{
  --ink:    var(--hi);
  --body:   var(--base);
  --accent: var(--hi);
  /* On dark themes "muted" means mixing toward the ground. On a light ground
     that washes text out instead, so --muted-mix lets cream pull the other way. */
  --muted:  color-mix(in srgb, var(--base) 68%, var(--muted-mix, var(--paper)));
  --line:   color-mix(in srgb, var(--base) 22%, transparent);
  --card:   color-mix(in srgb, var(--base)  6%, transparent);
}

body{ background:var(--paper); }

/* ---- text under an asset, one rule for every case study -----------------
   A LABEL names the asset: label font, all caps, base color.
   A DESCRIPTION says something about it: label font, sentence case, italic.
   Scoped to .shell article so the home page and prototypes are untouched. */
.shell article figcaption{
  font-family:var(--mono); font-style:italic; text-transform:none; color:var(--body); }
/* the Role / Timeline / Team labels and the contents label: base color, not the dimmest */
.shell article .meta .k, .shell .toc .lbl{ color:var(--body); }
/* every other label in a case study: section labels, result and stat labels, quote credits */
.shell article .band, .shell article .act,
.shell article .result .k, .shell article .result .l, .shell article .statgrid .l,
.shell article blockquote cite{ color:var(--body); }
.shell article .t,
.shell article figcaption.label{
  font-family:var(--mono); font-size:11px; font-weight:500; letter-spacing:.1em;
  text-transform:uppercase; font-style:normal; color:var(--body); text-align:center; }
.ph{ background:var(--slot); border:1px dashed var(--line); color:var(--muted); }

/* ---- the switch · pinned bottom-left, outside the page margins ----
   Four outlined cells; one filled square translates between the corners,
   so the fill genuinely moves rather than cross-fading. */
.mark{
  position:fixed; left:24px; bottom:24px; z-index:60;
  width:16px; height:16px; padding:0; border:0; background:none;
  cursor:pointer; -webkit-tap-highlight-color:transparent;
}
.mark i{
  position:absolute; width:7px; height:7px;
  border:1px solid var(--base); transition:border-color .45s ease;
}
.mark i:nth-child(1){ left:0;  top:0 }
.mark i:nth-child(2){ right:0; top:0 }
.mark i:nth-child(3){ left:0;  bottom:0 }
.mark i:nth-child(4){ right:0; bottom:0 }
.mark .fill{
  position:absolute; left:0; top:0; width:7px; height:7px;
  background:var(--hi);
  transition:transform .38s cubic-bezier(.65,.05,.25,1), background-color .45s ease;
}
[data-theme="green"] .mark .fill{ transform:translate(9px,0) }
[data-theme="cream"] .mark .fill{ transform:translate(0,9px) }
[data-theme="teal"]  .mark .fill{ transform:translate(9px,9px) }
.mark:hover i{ border-color:var(--hi) }
/* invisible margin around the icon: getting close is enough to open the
   label (and it makes a 44px touch target out of a 16px mark) */
.mark::before{ content:""; position:absolute; inset:-14px }
/* on hover the switch names itself, in the palette it would change: the box
   wipes open from the left (theme.js adds .tip-on), and closes the same way. */
.mark .mark-tip{
  position:absolute; left:26px; top:50%; transform:translateY(-50%);
  padding:6px 10px; border-radius:0; white-space:nowrap; pointer-events:none;
  font-family:var(--mono, "JetBrains Mono", ui-monospace, monospace);
  font-size:11px; font-weight:500; letter-spacing:.12em; line-height:1; text-align:left;
  color:var(--paper); background:var(--hi);
  clip-path:inset(0 100% 0 0);
  transition:clip-path .16s ease, background-color .45s ease, color .45s ease;
}
.mark.tip-on .mark-tip{ clip-path:inset(0 0 0 0);
  transition:clip-path .16s cubic-bezier(.2,.7,.2,1), background-color .45s ease, color .45s ease }
@media(hover:none){ .mark .mark-tip{ display:none } }
@media(prefers-reduced-motion:reduce){ .mark .mark-tip, .mark.tip-on .mark-tip{ transition:none } }
.mark:focus-visible{ outline:2px solid var(--hi); outline-offset:6px }

/* ---- cross-fade the ground and type on change ---- */
body, nav, .site-nav, .slot, .ph, .etym hr, .rule-v,
.word, h1, h2, h3, p, .lede, .eyebrow, .byline, .role,
.toc a, .toc .lbl, figcaption, .contact a, .cta, .copy,
table.metrics td, table.metrics .m, .callout{
  transition:background-color .45s ease, color .45s ease, border-color .45s ease;
}

@media(prefers-reduced-motion:reduce){
  .mark .fill{ transition:none }
}

/* =========================================================================
   Site nav — one component, shared by every page.
   Each page supplies its own container width (nav .shell / .site-nav .nav-in);
   everything below is common.
   ========================================================================= */
.site-nav{ position:sticky; top:0; z-index:40; background:var(--paper); }
.site-nav ul{
  list-style:none; width:100%; display:flex;
  justify-content:space-between; align-items:center; line-height:1.3;
}
.site-nav li{ display:flex }

.site-nav a{
  position:relative; font-family:var(--mono); font-weight:500;
  font-size:var(--fs-16); line-height:1.3; color:var(--base);
  text-decoration:none; transition:color .2s;
}
/* absolute rule, so hovering can't change the nav's height. It draws in from
   the left in step with the typing: theme.js adds .typed as the first letter
   goes and sets --ul-dur to the word's typing time, so line and letters land
   together. Leaving retracts it quickly. */
.site-nav a::after{
  content:""; position:absolute; left:0; right:0; bottom:-4px; height:1px;
  background:currentColor; transform:scaleX(0); transform-origin:left center;
  transition:transform .18s cubic-bezier(.4,0,.2,1);
}
.site-nav a:hover, .site-nav a:focus-visible{ color:var(--hi) }
.site-nav a.typed::after{ transform:scaleX(1);
  transition:transform var(--ul-dur, .26s) linear }
/* the typed copy sits over a hidden copy, so the link never changes width */
.site-nav a .nsz{ visibility:hidden }
.site-nav a .ntype{ position:absolute; left:0; top:0; white-space:nowrap }
@media(prefers-reduced-motion:reduce){
  .site-nav a::after{ transition:none }
}

/* the wordmark is the fifth member of the same space-between row, so its
   slot is always reserved and revealing it never moves the links */
.site-nav a.brand{
  position:relative; display:inline-block;
  font-family:var(--display); font-weight:700; font-size:var(--fs-20);
  line-height:1.3; color:var(--hi);
}
.site-nav a.brand::after{ content:none }        /* no hover rule on the wordmark */
.site-nav a.brand:hover, .site-nav a.brand:focus-visible{ color:var(--hi) }
.site-nav a.brand .sizer{ visibility:hidden }   /* holds the link at "pannisy" width */
/* typed text is absolute, so " zhao" spills into the gap instead of
   widening the link and redistributing every other nav item */
.site-nav a.brand .type{ position:absolute; left:0; top:0; white-space:nowrap }

/* home only: the wordmark fades in once the headword scrolls under the nav */
.site-nav.reveal a.brand{
  opacity:0; transform:translateY(-4px); pointer-events:none;
  transition:opacity .28s ease, transform .28s ease, color .45s ease;
}
.site-nav.reveal.show-brand a.brand{ opacity:1; transform:none; pointer-events:auto }

@media(prefers-reduced-motion:reduce){
  .site-nav a.brand{ transform:none }
}

/* Compact navigation is shared by home, about, and every case study. */
.mobile-menu-toggle,.mobile-menu-close{display:none}
.mobile-site-menu{display:none}
@media(max-width:760px), (max-aspect-ratio:4/5){
  .site-nav > .shell,.site-nav > .nav-in{display:flex;align-items:center;justify-content:space-between;gap:20px}
  .site-nav ul{width:auto;flex:1;flex-wrap:nowrap;justify-content:flex-start}
  .site-nav li:not(:first-child){display:none}
  .site-nav.reveal a.brand{opacity:1;transform:none;pointer-events:auto}
  .site-nav a.brand,.mobile-menu-brand{font-size:20px}
  .mobile-menu-toggle,.mobile-menu-close{
    position:relative;display:inline-block;flex:none;min-width:4ch;min-height:44px;padding:11px 0;
    border:0;background:none;color:var(--base);cursor:pointer;
    font:500 16px/1.3 var(--mono);text-align:right;text-decoration:none;
    -webkit-tap-highlight-color:transparent;
  }
  .mobile-menu-toggle::after,.mobile-menu-close::after{
    content:"";position:absolute;left:0;right:0;bottom:5px;height:1px;background:currentColor;
    transform:scaleX(0);transform-origin:left center;transition:transform .18s cubic-bezier(.4,0,.2,1);
  }
  .mobile-menu-toggle:hover,.mobile-menu-toggle:focus-visible,
  .mobile-menu-close:hover,.mobile-menu-close:focus-visible{color:var(--hi)}
  .mobile-menu-toggle.typed::after,.mobile-menu-close.typed::after{
    transform:scaleX(1);transition:transform var(--ul-dur,.26s) linear;
  }
  .mobile-menu-toggle .nsz,.mobile-menu-close .nsz{visibility:hidden}
  .mobile-menu-toggle .ntype,.mobile-menu-close .ntype{position:absolute;right:0;top:11px;white-space:nowrap}
  .mobile-site-menu{
    position:fixed;inset:0;z-index:80;display:flex;flex-direction:column;
    min-height:100vh;min-height:100dvh;padding:20px var(--pad,24px) max(24px,env(safe-area-inset-bottom));
    overflow-y:auto;background:var(--paper);color:var(--hi);
    clip-path:inset(0 0 100% 0);visibility:hidden;pointer-events:none;
    transition:clip-path .48s cubic-bezier(.76,0,.24,1),visibility 0s linear .48s;
  }
  .mobile-site-menu.is-open{clip-path:inset(0 0 0 0);visibility:visible;pointer-events:auto;transition:clip-path .48s cubic-bezier(.76,0,.24,1),visibility 0s}
  .mobile-menu-top{display:flex;align-items:center;justify-content:space-between;gap:20px;min-height:36px}
  .mobile-menu-brand{font:700 20px/1.3 var(--display);color:var(--hi);text-decoration:none}
  .mobile-menu-links{display:flex;flex-direction:column;align-items:flex-start;gap:8px;margin:clamp(62px,12vh,120px) 0 40px}
  .mobile-menu-links a{max-width:100%;font:700 clamp(42px,11vw,80px)/1.08 var(--display);letter-spacing:-.05em;color:var(--hi);text-decoration:none;overflow-wrap:anywhere;transition:color .2s,transform .2s}
  .mobile-menu-links a:hover,.mobile-menu-links a:focus-visible{color:var(--base);transform:translateX(6px)}
  .mobile-menu-contact{display:flex;flex-direction:column;margin-top:auto;padding-top:28px}
  .mobile-menu-contact a{display:flex;align-items:center;justify-content:space-between;gap:16px;min-height:64px;border-top:1px solid var(--line);color:var(--base);text-decoration:none;font:500 clamp(13px,3.6vw,16px)/1.4 var(--mono);transition:color .2s}
  .mobile-menu-contact a:last-child{border-bottom:1px solid var(--line)}
  .mobile-menu-contact a:hover,.mobile-menu-contact a:focus-visible{color:var(--hi)}
  .mobile-menu-contact .label{position:relative;overflow-wrap:anywhere}
  .mobile-menu-contact .label::after{content:"";position:absolute;left:0;right:0;bottom:-3px;height:1px;background:currentColor;transform:scaleX(0);transform-origin:left center;transition:transform .2s cubic-bezier(.4,0,.2,1)}
  .mobile-menu-contact a:hover .label::after,.mobile-menu-contact a:focus-visible .label::after{transform:scaleX(1)}
  .mobile-menu-contact .out-icon{flex:none;width:20px;height:20px;background:currentColor;-webkit-mask:url("icons/arrow-up-right.svg") center/contain no-repeat;mask:url("icons/arrow-up-right.svg") center/contain no-repeat}
  body.mobile-menu-open{overflow:hidden}
}
@media(max-width:380px){.mobile-site-menu{padding-inline:20px}.mobile-menu-links a{font-size:40px}}
@media(max-width:760px) and (max-height:650px){
  .mobile-menu-links{margin:36px 0 24px;gap:4px}
  .mobile-menu-links a{font-size:clamp(36px,8vh,52px)}
  .mobile-menu-contact a{min-height:50px}
}
@media(max-width:700px){
  .shell article .foot a{max-width:100%}
  .shell article figure img,.shell article figure video{max-width:100%}
  .shell article table.metrics td{overflow-wrap:anywhere}
}
@media(prefers-reduced-motion:reduce){
  .mobile-site-menu,.mobile-site-menu.is-open,.mobile-menu-toggle::after,.mobile-menu-close::after,.mobile-menu-links a,.mobile-menu-contact .label::after{transition:none}
}

/* =========================================================================
   Ordinary links — one hover for all of them: the colour shifts and an
   underline draws in from the left, the same gesture as the nav tabs.
   Typing stays a nav-only signature, and the outline rail keeps its own
   hover (colour + rails), so nothing competes with it.
   ========================================================================= */
.foot a, .contact a, .etym .ext, .shell article .ext, .shell article .jump{
  position:relative; text-decoration:none; transition:color .18s ease;
}
.foot a::after, .contact a::after, .etym .ext::after, .shell article .ext::after,
.shell article .jump::before{
  content:""; position:absolute; left:0; right:0; bottom:-3px; height:1px;
  background:currentColor; opacity:1;
  transform:scaleX(0); transform-origin:left center;
  transition:transform .2s cubic-bezier(.4,0,.2,1);
}
.foot a:hover::after, .foot a:focus-visible::after,
.contact a:hover::after, .contact a:focus-visible::after,
.etym .ext:hover::after, .etym .ext:focus-visible::after,
.shell article .ext:hover::after, .shell article .ext:focus-visible::after,
.shell article .jump:hover::before, .shell article .jump:focus-visible::before{
  transform:scaleX(1);
}
.foot a:hover, .foot a:focus-visible,
.etym .ext:hover, .etym .ext:focus-visible,
.shell article .ext:hover, .shell article .ext:focus-visible,
.shell article .jump:hover, .shell article .jump:focus-visible{ color:var(--accent) }
/* The case-study pager is [key cap][title], so the shared underline would run
   under the cap as well. Draw it under the title instead, and start it from the
   side the arrow points, so the wipe travels the way the reader is going. */
.foot a::after{ content:none }
.foot a .ptitle{ position:relative }
.foot a .ptitle::after{
  content:""; position:absolute; left:0; right:0; bottom:-3px; height:1px;
  background:currentColor; transform:scaleX(0); transform-origin:left center;
  transition:transform .2s cubic-bezier(.4,0,.2,1);
}
.foot a.next .ptitle::after{ transform-origin:right center }
.foot a:hover .ptitle::after, .foot a:focus-visible .ptitle::after{ transform:scaleX(1) }
@media(prefers-reduced-motion:reduce){ .foot a .ptitle::after{ transition:none } }

/* The two ends of the sequence point back to the index rather than to another
   case study, so they carry the site mark instead of a chevron and set their
   label as an eyebrow — a different kind of destination, not a fifth project. */
.foot a.index .ptitle{
  font-family:var(--mono); font-weight:500; font-size:13px;
  letter-spacing:.12em; text-transform:uppercase;
}
.foot .cap svg{ display:block; width:15px; height:15px; fill:var(--base, var(--body)) }

/* the jump link carried a permanent rule and an arrow in ::after */
.shell article .jump{ border-bottom:0 }
@media(prefers-reduced-motion:reduce){
  .foot a::after, .contact a::after, .etym .ext::after, .shell article .ext::after,
.shell article .jump::before{
    transition:none;
  }
}

/* =========================================================================
   Case-study outline — collapsed to rails, expands on hover.
   The column stays 210px and every row keeps its height, so the dash sits
   exactly where its label will appear. Nothing reflows on hover.
   ========================================================================= */
/* The "Contents" label is taken out of the flow so it can't push the rails
   down — otherwise rail 1 can't line up with the first line of the article,
   and the rails would shift when the label appears on hover. */
.toc .lbl{
  position:absolute; top:56px; left:0; margin:0;   /* flush with the dashes */
  opacity:0; transition:opacity .2s ease;
}

/* no border-left indicator: colour alone marks the active rail */
.toc #toc a{ position:relative; border-left:0 }

/* COLLAPSED — the label leaves the flow, so every rail sits on the same
   22px pitch instead of inheriting a row height that varies with how long
   the label is and whether it wraps. */
.toc #toc a{ display:block; height:22px; padding-top:0; padding-bottom:0 }
/* The row height snaps between states, so whichever element is LEAVING is
   hidden instantly — otherwise you watch the rails slide apart before the
   labels arrive. Only the arriving element fades. */
.toc #toc a span{
  position:absolute; left:0; top:0; white-space:nowrap;
  opacity:0; pointer-events:none; transform:translateY(-4px);
  transition:opacity 0s;                      /* collapsing: gone at once */
}
/* each row's reveal starts a beat after the one above it; --i is set per link
   in theme.js, and the fallback keeps it working if that never runs */
.toc #toc a{ --d:calc(60ms + var(--i, 0) * 34ms) }
/* the dash: same width and same left edge for every rail; the active one
   is --hi, the rest --base */
/* --hi and --base are nearly the same brightness (1.00–1.27 depending on
   palette), so on a 2px mark they're indistinguishable. Inactive rails are
   mixed halfway to the ground, which separates them on every palette while
   staying visible. */
.toc #toc a::before{
  content:""; position:absolute; left:12px; top:10px;
  width:22px; height:2px; border-radius:1px;   /* matches the 22px theme mark */
  background:color-mix(in srgb, var(--base) 50%, var(--paper));
  transition:opacity .22s ease .04s, background-color .2s ease;
}
.toc #toc a.sub::before{ left:12px }
.toc #toc a.active::before{ background:var(--hi) }

/* While the pointer is on the outline, the row under it takes the highlight
   and the scroll-tracked row lets go — otherwise two rows read as "current"
   at once. :not(:hover) means hovering the active row keeps it highlighted
   rather than dimming the thing you're pointing at. */
.toc #toc:hover a.active:not(:hover){ color:var(--muted) }
.toc #toc:hover a.active:not(:hover)::before{
  background:color-mix(in srgb, var(--base) 50%, var(--paper));
}

/* EXPANDED — labels return to the flow and the list reads as before */
.toc:hover #toc a,
.toc:focus-within #toc a{ height:auto; padding-top:5px; padding-bottom:5px }
.toc:hover #toc a span,
.toc:focus-within #toc a span{
  position:static; white-space:normal; opacity:1; pointer-events:auto;
  transform:none;
  transition:opacity .24s ease var(--d), transform .24s ease var(--d);
}

/* hover (or keyboard focus) reveals the outline as-is */
/* "Contents" leads the cascade; the rows follow via --d above */
.toc:hover .lbl,
.toc:focus-within .lbl{ opacity:1; transition-delay:.04s }

.toc:hover #toc a::before,
.toc:focus-within #toc a::before{ opacity:0; transition:opacity 0s }

@media(prefers-reduced-motion:reduce){
  .toc .lbl,.toc #toc a span,.toc #toc a::before{ transition:none }
  .toc #toc a span{ transform:none }
}

/* Rail 1 lines up with the first line of the article.
   `top` matches the nav height so the column doesn't start 18px low before
   you've even scrolled (sticky engages immediately when the threshold sits
   below the element's natural position). */
.toc{ position:sticky; top:66px; align-self:start; height:max-content; padding-top:46px }

/* EXPANDED alignment — "Contents" is the first thing you read when the panel
   opens, so it lines up with the first line of the article; the section list
   drops below it. Collapsed, the label is hidden and rail 1 takes that line
   instead. The rails are already gone by the time this shift happens, so the
   move is never visible. */
.toc:hover, .toc:focus-within{ padding-top:86px }

/* Captured media in case studies — same framing as figure images, so light
   screenshots and recordings don't read as holes in a dark ground. */
figure video.shot{
  display:block; width:100%; height:auto;
}

/* Flow diagram — inline SVG so it recolours with the palette */
figure svg.flow{ display:block; width:100%; height:auto; overflow:visible }

/* Mascot strip — one gorilla per unlockable feature */
.mascots{
  display:grid; grid-template-columns:repeat(4, 1fr); gap:20px 12px; margin:0;
}
.mascots figure{ margin:0; display:flex; flex-direction:column; align-items:center; gap:6px }
.mascots img{ display:block; width:100%; height:auto; max-width:110px }
.mascots figcaption{
  margin:0; font-family:var(--mono); font-size:10.5px; letter-spacing:.06em;
  text-transform:uppercase; color:var(--muted); text-align:center;
}
@media(max-width:560px){ .mascots{ grid-template-columns:repeat(3, 1fr) } }
