/* ==================================================================
   John Fordham — portfolio
   One stylesheet for every page.
   ================================================================== */

:root{
  --ground:#F3F2EE;
  --surface:#FFFFFF;
  --dark:#15222B;
  --dark-2:#20303B;
  --ink:#191C1A;
  --muted:#5E6661;
  --faint:#646B65;
  --on-dark:#F5F3EF;
  --on-dark-muted:#879A9E;
  --rule:#D8D9D1;
  --rule-soft:#E5E5DE;
  --rule-dark:#34414A;
  --accent:#17605C;
  /* Pressed/hover green. A link that is already --accent has nowhere to go on
     hover, so it needs a darker step rather than a change of hue. 8.70:1 on
     the ground. */
  --accent-strong:#124C49;
  /* The accent measures 2.34:1 on --dark, below even the 3:1 floor for UI, so
     dark grounds get a tinted version of the same green. 7.96:1 on --dark. */
  --accent-on-dark:#7FC2B8;
  --accent-soft:#E2EDEA;
  /* Primary-button hover fills: the button's own colour mixed 20% toward the
     ground behind it, so the light button darkens and the dark one lightens. */
  --btn-solid-hover:#CAC7C7;
  --btn-dark-solid-hover:#454744;

  --display:"Newsreader",Georgia,"Times New Roman",serif;
  --body:"Source Sans 3",-apple-system,BlinkMacSystemFont,"Segoe UI",Helvetica,Arial,sans-serif;
  --mono:"IBM Plex Mono",ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;

  --measure:62ch;
  --max:1120px;
  --gutter:28px;
  --header-h:60px;
  --radius:12px;
}
@media (max-width:640px){ :root{ --gutter:18px; } }

*{box-sizing:border-box}
[hidden]{display:none!important}
html{scroll-behavior:smooth;scroll-padding-top:calc(var(--header-h) + 14px)}
html:has(.proj-nav){scroll-padding-top:calc(var(--header-h) + 62px)}
body{
  margin:0;background:var(--ground);color:var(--ink);
  font-family:var(--body);font-size:17px;line-height:1.62;
  -webkit-font-smoothing:antialiased;
}
img{max-width:100%;display:block}
a{color:var(--accent)}

.wrap{max-width:var(--max);margin:0 auto;padding-inline:var(--gutter)}

/* ---------------- header ---------------- */
.site-header{
  position:sticky;top:env(safe-area-inset-top,0px);z-index:50;
  background:var(--ground);border-bottom:1px solid transparent;
  transition:border-color .2s ease;
}
.site-header.is-stuck{border-bottom-color:var(--rule-soft)}
.site-header .wrap{
  display:flex;align-items:center;justify-content:space-between;gap:16px;
  height:var(--header-h);
}
.wordmark{font-family:var(--display);font-size:1.32rem;font-weight:600;letter-spacing:.01em;color:var(--ink);text-decoration:none}
.wordmark:hover{color:var(--accent)}

.site-nav{display:flex;gap:26px}
.site-nav a{
  font-family:var(--mono);font-size:.72rem;letter-spacing:.09em;text-transform:uppercase;
  color:var(--accent);text-decoration:none;padding-bottom:3px;border-bottom:1px solid transparent;
}
.site-nav a:hover{color:var(--accent-strong);border-bottom-color:var(--accent-strong)}


.nav-toggle{
  display:none;background:none;border:1px solid var(--rule);border-radius:3px;
  font-family:var(--mono);font-size:.68rem;letter-spacing:.09em;text-transform:uppercase;
  color:var(--muted);padding:7px 11px;cursor:pointer;
}
@media (max-width:760px){
  .nav-toggle{display:block}
  .site-nav{
    position:absolute;top:100%;left:0;right:0;flex-direction:column;gap:0;
    background:var(--ground);border-bottom:1px solid var(--rule);padding:6px var(--gutter) 14px;
  }
  .site-nav[hidden]{display:none}
  .site-nav a{padding:10px 0;border-bottom:1px solid var(--rule-soft)}
}

/* ---------------- hero ---------------- */
.hero{background:var(--dark);color:var(--on-dark)}
.hero .wrap{padding-block:46px 56px}
.hero-top{
  display:flex;justify-content:space-between;align-items:baseline;gap:18px;flex-wrap:wrap;
  font-family:var(--mono);font-size:.76rem;letter-spacing:.14em;text-transform:uppercase;
  color:var(--on-dark);
}
.hero-top .hero-role{color:var(--on-dark-muted);text-align:right}
.hero hr{border:0;border-top:1px solid var(--rule-dark);margin:40px 0 38px}
.hero h1{
  font-family:var(--display);font-weight:400;
  font-size:clamp(2.3rem,6vw,4rem);line-height:1.08;letter-spacing:-.02em;
  margin:0 0 26px;max-width:none;text-wrap:balance;
}
.hero p{max-width:none;color:var(--on-dark-muted);font-size:1.12rem;line-height:1.55;margin:0 0 32px}
.hero-actions{display:flex;gap:12px;flex-wrap:wrap}
.btn{
  display:inline-block;font-family:var(--mono);font-size:.72rem;letter-spacing:.1em;text-transform:uppercase;
  padding:13px 20px;text-decoration:none;border:1px solid var(--on-dark);color:var(--on-dark);
  background:transparent;
  transition:background .18s ease,color .18s ease,border-color .18s ease,transform .18s ease;
}
/* Hover raises a button's intensity; it never swaps it between filled and
   outlined. Inverting made the primary read as secondary the moment the cursor
   reached it, and made the two indistinguishable on either hover. */
.btn:hover{background:rgba(245,243,239,.14);border-color:#FFFFFF;color:#FFFFFF;transform:translateY(-1px)}
.btn:active{transform:translateY(0)}
.btn--solid{background:var(--on-dark);color:var(--dark)}
.btn--solid:hover{background:var(--btn-solid-hover);border-color:var(--btn-solid-hover);color:var(--dark)}

/* ---------------- section furniture ---------------- */
.section{padding-block:52px}
.section + .section{border-top:1px solid var(--rule-soft)}
.section-label{
  font-family:var(--mono);font-size:.72rem;letter-spacing:.14em;text-transform:uppercase;
  color:var(--accent);font-weight:400;margin:0 0 30px;
}
h2.section-title{
  font-family:var(--display);font-weight:400;
  font-size:clamp(1.8rem,4vw,2.5rem);line-height:1.16;letter-spacing:-.015em;
  margin:0 0 26px;max-width:20ch;text-wrap:balance;
}

/* ---------------- projects ---------------- */
.project{
  display:grid;grid-template-columns:1fr 1fr;gap:56px;align-items:center;
  padding-block:52px;
}
.project + .project{border-top:1px solid var(--rule-soft)}
.projects .project:nth-child(even) .project-media{order:2}
.project-frame{
  background:var(--surface);border:1px solid var(--rule);
  padding:0;overflow:hidden;
}
.project-frame img{
  aspect-ratio:3/2;object-fit:cover;object-position:center;width:100%;height:auto;background:var(--surface);
  transform:rotate(-2deg) scale(1.12);
}
.projects .project:nth-child(even) .project-frame img{transform:rotate(2deg) scale(1.12)}
.project-client{
  font-family:var(--mono);font-size:.72rem;letter-spacing:.12em;text-transform:uppercase;
  color:var(--accent);margin:0 0 12px;
}
.project h3{
  font-family:var(--display);font-weight:400;
  font-size:clamp(1.5rem,3vw,2rem);line-height:1.18;letter-spacing:-.012em;
  margin:0 0 14px;text-wrap:balance;
}
.project h3 a{color:inherit;text-decoration:none;border-bottom:1px solid transparent}
.project h3 a:hover{border-bottom-color:var(--accent);color:var(--accent)}
.project p{margin:0 0 18px;color:var(--muted);max-width:46ch}
.tags{display:flex;flex-wrap:wrap;gap:8px;list-style:none;margin:0 0 20px;padding:0}
.tags li{
  font-family:var(--mono);font-size:.64rem;letter-spacing:.1em;text-transform:uppercase;
  color:var(--muted);border:1px solid var(--rule);padding:5px 9px;
}
.project-link{
  font-family:var(--mono);font-size:.72rem;letter-spacing:.09em;text-transform:uppercase;
  color:var(--accent);text-decoration:none;border-bottom:1px solid var(--rule);padding-bottom:3px;
}
.project-link:hover{border-bottom-color:var(--accent)}
@media (max-width:860px){
  /* One column, resequenced: client, title, picture, description, tags, link.
     .project-body becomes display:contents so its children are grid items of
     .project and can be ordered against .project-media. */
  .project{grid-template-columns:1fr;gap:20px;padding-block:40px;align-items:start}
  .project-body{display:contents}
  .project-client{order:1;margin:0}
  .project h3{order:2;margin:0}
  /* :nth-child(n) matches every card and carries the same specificity as the
     nth-child(even) rule above, so this wins on source order rather than luck. */
  .projects .project:nth-child(n) .project-media{order:3}
  /* the description is the only unclassed <p> in the body — .project-client is
     also a <p> and must keep its own order */
  .project-body > p:not([class]){order:4;margin:0}
  .project .tags{order:5;margin:0}
  .project-link{order:6;justify-self:start;margin:0}
  .project-frame{padding:16px}
}

/* ---------------- about ---------------- */
.split{display:grid;grid-template-columns:1fr 1fr;gap:56px}
@media (max-width:860px){ .split{grid-template-columns:1fr;gap:34px} }
.skills-lead{max-width:none;margin:0 0 26px;font-size:1.04rem}
.skills{
  list-style:none;margin:0;padding:0;counter-reset:stage;
  border-top:1px solid var(--rule-soft);
}
.skills li{
  padding:16px 0 16px 38px;border-bottom:1px solid var(--rule-soft);
  position:relative;counter-increment:stage;
}
.skills li::before{
  content:counter(stage,decimal-leading-zero);
  position:absolute;left:0;top:19px;
  font-family:var(--mono);font-size:.68rem;letter-spacing:.08em;color:var(--accent);
}
.skills b{display:block;font-weight:600;font-size:1rem;margin-bottom:3px}
.skills span{display:block;color:var(--muted);font-size:.94rem;line-height:1.5}
.intro--takes{font-size:1rem;color:var(--on-dark-muted);margin-top:-16px}
.about-body p{max-width:var(--measure);margin:0 0 16px}
.about-body p:first-child{font-weight:600;font-size:1.16rem;color:var(--ink)}

/* ---------------- experience ---------------- */
.role{
  display:grid;grid-template-columns:minmax(210px,290px) 1fr;gap:44px;
  padding:26px 0 26px 38px;border-top:1px solid var(--rule-soft);
}
.role:last-child{border-bottom:1px solid var(--rule-soft)}
.role .title{
  font-family:var(--display);font-weight:500;font-size:1.22rem;line-height:1.25;
  display:block;margin-bottom:4px;
}
.role .org{
  font-family:var(--mono);font-size:.72rem;letter-spacing:.1em;text-transform:uppercase;
  color:var(--accent);display:block;
}
.role p{margin:0 0 12px;color:var(--muted);font-size:1rem;max-width:none}
.role p:last-child{margin-bottom:0}
@media (max-width:760px){ .role{grid-template-columns:1fr;gap:12px;padding:22px 0 22px 38px} }

/* ---------------- clients ---------------- */
.clients{
  display:grid;grid-template-columns:repeat(auto-fill,minmax(160px,1fr));
  gap:1px;background:transparent;list-style:none;margin:0;padding:0;
}
.clients li{
  background:var(--ground);padding:22px 14px;text-align:center;
  font-family:var(--mono);font-size:.76rem;letter-spacing:.1em;text-transform:uppercase;color:var(--muted);
  display:flex;align-items:center;justify-content:center;min-height:78px;
  box-shadow:0 0 0 1px var(--rule-soft);
}
/* cap both dimensions: the marks vary from square to very wide, and only
   capping height would let a wide wordmark overflow its tile */
.clients img{
  max-height:32px;max-width:130px;width:auto;height:auto;margin:0 auto;
  filter:grayscale(1);opacity:.55;transition:filter .25s ease,opacity .25s ease;
}
.clients li:hover img{filter:grayscale(0);opacity:1}
@media (prefers-reduced-motion:reduce){ .clients img{transition:none} }

/* ---------------- contact / footer ---------------- */
.site-footer{background:var(--dark);color:var(--on-dark)}
.site-footer .wrap{padding-block:60px 56px}
.site-footer .section-label{color:var(--on-dark-muted)}
.site-footer h2{
  font-family:var(--display);font-weight:400;
  font-size:clamp(1.9rem,4.4vw,2.8rem);line-height:1.14;letter-spacing:-.018em;
  margin:0 0 20px;max-width:none;text-wrap:balance;
}
.site-footer .intro{color:var(--on-dark-muted);max-width:none;font-size:1.08rem;line-height:1.55;margin:0 0 34px}
.contact-grid{
  display:grid;grid-template-columns:repeat(auto-fit,minmax(190px,1fr));gap:26px;
  border-top:1px solid var(--rule-dark);padding-top:26px;
}
.contact-grid dt{
  font-family:var(--mono);font-size:.66rem;letter-spacing:.12em;text-transform:uppercase;
  color:var(--on-dark-muted);margin-bottom:6px;
}
/* The dd is dimmed, not the links inside it — they set their own colour — so
   the one value that is not a link (Location) steps back from the three that
   are. 9.9:1 on the footer: a visible difference, still comfortably readable. */
.contact-grid dd{margin:0;font-size:.98rem;color:#C8C4BE}
/* Footer links carry no line at rest — the resting footer is clean text — and
   draw one on hover. It is a real underline rather than a bottom border, set
   5px clear of the baseline and told to break around descenders, so it passes
   under the j and g of the address instead of through them. */
.contact-grid a{color:var(--on-dark);text-decoration:none;border-bottom:0}
.contact-grid a:hover{
  text-decoration:underline;text-decoration-thickness:1px;
  text-decoration-color:var(--on-dark);text-underline-offset:5px;
  text-decoration-skip-ink:auto;
}
.colophon{
  margin-top:40px;padding-top:20px;border-top:1px solid var(--rule-dark);
  font-family:var(--mono);font-size:.7rem;letter-spacing:.06em;color:var(--on-dark-muted);
  display:flex;justify-content:space-between;gap:14px;flex-wrap:wrap;
}

@media (prefers-reduced-motion:reduce){
  *{animation:none!important;transition:none!important;scroll-behavior:auto!important}
}

/* ---------------- project pages ---------------- */
.proj-overview{background:var(--dark);color:var(--on-dark)}
.proj-overview .wrap{padding-block:48px 52px}
.proj-logo{height:52px;width:auto;margin:0 0 24px;display:block}
.proj-client{
  font-family:var(--mono);font-size:.74rem;letter-spacing:.14em;text-transform:uppercase;
  color:var(--on-dark-muted);margin:0 0 18px;
}
.proj-overview h1{
  font-family:var(--display);font-weight:400;
  font-size:clamp(2.1rem,5.4vw,3.4rem);line-height:1.1;letter-spacing:-.02em;
  margin:0 0 22px;text-wrap:balance;
}
.proj-overview .tagline{
  font-family:var(--display);font-style:italic;font-size:1.24rem;line-height:1.5;
  color:var(--on-dark-muted);margin:0 0 46px;
}
.ov-body{
  display:grid;grid-template-columns:2fr 1fr;gap:56px;
  border-top:1px solid var(--rule-dark);padding-top:36px;
}
@media (max-width:860px){ .ov-body{grid-template-columns:1fr;gap:34px} }
.ov-challenge h2{
  font-family:var(--mono);font-size:1rem;font-weight:500;letter-spacing:.16em;
  text-transform:uppercase;color:var(--on-dark-muted);margin:0 0 20px;line-height:1.3;
}
.ov-challenge p{margin:0 0 16px;color:var(--on-dark);font-size:1.06rem;line-height:1.65}
.ov-challenge p:last-child{margin-bottom:0}
.ov-facts{margin:0;display:grid;gap:22px;align-content:start;padding-top:22px}
.ov-facts dt{
  font-family:var(--mono);font-size:.64rem;letter-spacing:.12em;text-transform:uppercase;
  color:var(--on-dark-muted);margin:0 0 6px;
}
.ov-facts dd{margin:0;font-size:.92rem;line-height:1.55;color:var(--on-dark)}
.ov-facts dd p{margin:0}
.tags--dark{margin:30px 0 0;gap:0;display:block}
.tags--dark li{
  display:inline;border:0;padding:0;color:var(--on-dark-muted);
  font-family:var(--mono);font-size:.7rem;letter-spacing:.12em;text-transform:uppercase;
}
.tags--dark li + li::before{content:" · ";color:var(--rule-dark)}

.proj-nav{
  position:sticky;top:calc(var(--header-h) + env(safe-area-inset-top,0px));z-index:40;
  background:var(--ground);border-bottom:1px solid var(--rule-soft);
}
.proj-nav .wrap{display:flex;gap:26px;padding-block:13px;overflow-x:auto}
.proj-nav a{
  font-family:var(--mono);font-size:.7rem;letter-spacing:.1em;text-transform:uppercase;
  color:var(--accent);text-decoration:none;white-space:nowrap;
  padding-bottom:3px;border-bottom:1px solid transparent;
}
.proj-nav a:hover{color:var(--accent-strong);border-bottom-color:var(--accent-strong)}

/* full-width prose on project pages */
/* h2.section-title sets max-width:20ch — match its specificity or it wins */
h2.section-title--wide{max-width:50vw;text-wrap:pretty}
@media (max-width:860px){ h2.section-title--wide{max-width:none;text-wrap:balance} }
.prose--wide{max-width:none}
.prose--wide p{max-width:none}
.section--wide .stage p{max-width:none}

/* The row paints the hairlines: it has the rule colour as its background and
   the tiles sit on top with 1px gaps between them. That trick only works while
   every grid cell is covered by a tile — an empty cell shows the background as
   a solid grey block.

   auto-fit used to give a 2-column layout between roughly 440 and 620px, which
   left the third of three tiles alone on row 2 and the fourth cell grey. So we
   skip the 2-up step entirely: stacked below 640px, all-in-one-row above it.

   grid-auto-flow:column rather than repeat(3,1fr) because the column count then
   follows the number of tiles — ZEISS Electronics has two, and a fixed three
   would leave it with the same orphan cell this fix is removing. */
.outcome-row{
  display:grid;grid-template-columns:1fr;
  gap:1px;background:var(--rule-soft);border-block:1px solid var(--rule);margin:44px 0 0;
}
@media (min-width:640px){
  .outcome-row{grid-auto-flow:column;grid-auto-columns:1fr}
}
.outcome-row .tile{background:var(--ground);padding:24px 18px 22px 0}
@media (min-width:640px){ .outcome-row .tile{padding-left:24px} .outcome-row .tile:first-child{padding-left:0} }
.outcome-row .fig{
  font-family:var(--display);font-weight:500;font-size:clamp(2rem,4.6vw,2.7rem);
  line-height:1;letter-spacing:-.02em;font-variant-numeric:tabular-nums;display:block;margin-bottom:10px;
}
.outcome-row .fig .unit{font-size:.55em}
.outcome-row .what{display:block;font-weight:600;font-size:.97rem;margin-bottom:2px}
.outcome-row .ctx{display:block;font-size:.9rem;color:var(--muted)}

.prose p{margin:0 0 16px}
.prose p:last-child{margin-bottom:0}

.stage{padding:26px 0;border-top:1px solid var(--rule-soft)}
.stage h3{
  font-family:var(--display);font-weight:500;font-size:1.3rem;line-height:1.25;
  margin:0 0 10px;letter-spacing:-.008em;
}
.stage p{margin:0 0 12px;color:var(--muted)}
.stage p:last-child{margin-bottom:0}

/* gate */
.gate{
  display:grid;grid-template-columns:minmax(0,1fr) minmax(0,36%);gap:40px;align-items:center;
  border:1px solid var(--rule);border-radius:var(--radius);background:var(--surface);
  padding:36px;text-align:center;margin-block:8px;
}
/* A boxed crop of the project's hero, sitting beside the message rather than
   above it. Fixed ratio so the panel height is set by the text, not the image. */
.gate-hero{
  margin:0;border:1px solid var(--rule);border-radius:calc(var(--radius) - 4px);
  overflow:hidden;background:var(--ground);aspect-ratio:4/3;
}
.gate-hero img{display:block;width:100%;height:100%;object-fit:cover;object-position:center}

.gate h2{
  font-family:var(--display);font-weight:400;font-size:clamp(1.5rem,3.2vw,2rem);
  line-height:1.2;margin:0 0 12px;letter-spacing:-.012em;
}
.gate p{color:var(--muted);max-width:52ch;margin:0 auto 24px}
.gate-actions{display:flex;gap:12px;justify-content:center;flex-wrap:wrap}
/* Hover raises intensity without changing a button's category. The primary
   shifts tone toward the ground behind it; the secondary picks up a faint
   tint of the foreground. Neither swaps between filled and outlined. */
.btn--dark{border-color:var(--ink);color:var(--ink)}
.btn--dark:hover{background:rgba(25,28,26,.09);border-color:var(--ink);color:var(--ink)}
.btn--dark-solid{background:var(--ink);color:var(--ground);border-color:var(--ink)}
.btn--dark-solid:hover{background:var(--btn-dark-solid-hover);border-color:var(--btn-dark-solid-hover);color:var(--ground)}
.gate form{display:flex;gap:10px;justify-content:center;flex-wrap:wrap;margin-top:4px}

/* Phones: picture first, message under it. Centring is inherited. */
@media (max-width:760px){
  .gate{grid-template-columns:1fr;gap:24px;padding:26px}
  .gate-hero{order:-1;aspect-ratio:16/9}
}
.gate input{
  font-family:var(--mono);letter-spacing:.06em;
  padding:12px 14px;border:1px solid var(--rule);background:var(--ground);color:var(--ink);min-width:220px;
  /* 16px is a floor, not a style choice: iOS Safari zooms the page when a
     focused input computes below 16px, and the user is then left pinching
     back out to read the unlocked page. Do not shrink this to match the
     other mono fields. The viewport meta deliberately has no maximum-scale,
     because suppressing zoom to fix this would break pinch-zoom site-wide. */
  font-size:16px;
}

.gate .err{color:#9B2C2C;font-size:.88rem;margin:14px 0 0}
@media (prefers-color-scheme:dark){ .gate .err{color:#E88} }

.backlink{
  display:inline-block;margin-top:44px;font-family:var(--mono);font-size:.72rem;
  letter-spacing:.09em;text-transform:uppercase;color:var(--accent);
  text-decoration:none;border-bottom:1px solid var(--rule);padding-bottom:3px;
}
.backlink:hover{border-bottom-color:var(--accent)}

.visually-hidden{
  position:absolute;width:1px;height:1px;margin:-1px;padding:0;
  overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;border:0;
}

/* ---------------- figures inside an approach stage ---------------- */
.stage-figs{display:grid;gap:22px;margin:22px 0 4px}
.stage-figs:not(.stage-figs--pair){max-width:860px}
.stage-figs--pair{align-items:start}
.stage-figs--pair{grid-template-columns:1fr 1fr}
@media (max-width:760px){ .stage-figs--pair{grid-template-columns:1fr} }
.stage-figs figure{margin:0}
.stage-figs img{
  width:100%;height:auto;display:block;
  background:var(--surface);border:1px solid var(--rule);
}
.stage-figs figcaption{
  font-family:var(--mono);font-size:.66rem;letter-spacing:.09em;text-transform:uppercase;
  color:var(--faint);margin-top:10px;line-height:1.45;
}

/* ---------------- context: prose with an aside ---------------- */
.context-body{display:grid;grid-template-columns:2fr 1fr;gap:56px;align-items:start}
@media (max-width:860px){ .context-body{grid-template-columns:1fr;gap:32px} }
.context-body .prose{max-width:none}
.context-body .prose p{max-width:none}
.ctx-aside blockquote{
  margin:0;border-left:2px solid var(--accent);padding:2px 0 2px 20px;
}
.ctx-aside blockquote p{
  font-family:var(--display);font-style:italic;font-size:1.22rem;line-height:1.45;
  color:var(--ink);margin:0 0 14px;
}
.ctx-aside blockquote footer{
  font-family:var(--mono);font-size:.68rem;letter-spacing:.1em;text-transform:uppercase;
  color:var(--faint);
}
.ctx-aside--pull p{
  font-family:var(--display);font-size:1.34rem;line-height:1.4;color:var(--ink);margin:0;
}
.ctx-aside--pull{border-left:2px solid var(--accent);padding:2px 0 2px 20px}

/* ---------------- quote lead-in ---------------- */
.ctx-lead{
  font-size:.96rem;line-height:1.5;color:var(--faint);margin:0 0 16px;
}

/* ---------------- more work ---------------- */
.more-work{background:var(--ground);border-top:1px solid var(--rule-soft);padding:44px 0 56px}
.more-grid{display:grid;grid-template-columns:1fr 1fr;gap:44px}
@media (max-width:760px){ .more-grid{grid-template-columns:1fr;gap:30px} }
.more-link{
  display:block;text-decoration:none;border-bottom:0;
  border-top:1px solid var(--rule-soft);padding-top:20px;
}
.more-dir{
  display:block;margin-bottom:12px;font-family:var(--mono);font-size:.66rem;
  letter-spacing:.12em;text-transform:uppercase;color:var(--accent);
}
.more-title{
  display:block;font-family:var(--display);font-weight:400;
  font-size:clamp(1.35rem,2.4vw,1.85rem);line-height:1.2;color:var(--ink);
  letter-spacing:-.015em;
}
.more-desc{
  display:block;margin-top:8px;font-family:var(--mono);font-size:.7rem;
  letter-spacing:.1em;text-transform:uppercase;color:var(--accent);
}
.more-link:hover .more-title{color:var(--accent)}

/* ---------------- tools ---------------- */
/* Reads as the last row of the .skills stack: same 38px indent, no rule of its
   own (the final li already draws one), label in the gutter style of the
   numbers above it. */
.tools{
  margin:0;padding:16px 0 0 38px;
  font-size:.94rem;color:var(--muted);line-height:1.5;
}
.tools span{
  display:block;margin-bottom:5px;font-family:var(--mono);font-size:.68rem;
  letter-spacing:.08em;text-transform:uppercase;color:var(--accent);
}

/* Card image is a link through to the case study. tabindex="-1" in the markup
   keeps it out of the tab order — the heading link already goes to the same
   place, so keyboard users get one stop per card, not two. */
.project-media-link{display:block;text-decoration:none;color:inherit}
.project-media-link .project-frame{transition:border-color .25s ease}
.project-media-link:hover .project-frame{border-color:var(--accent)}
.project-media-link .project-frame img{transition:transform .5s ease}
.project-media-link:hover .project-frame img{transform:rotate(-2deg) scale(1.16)}
.projects .project:nth-child(even) .project-media-link:hover .project-frame img{transform:rotate(2deg) scale(1.16)}

@media (prefers-reduced-motion:reduce){
  .project-media-link .project-frame img{transition:none}
  .project-media-link:hover .project-frame img{transform:rotate(-2deg) scale(1.12)}
  .projects .project:nth-child(even) .project-media-link:hover .project-frame img{transform:rotate(2deg) scale(1.12)}
}


/* The 1px lift is decoration; drop it for anyone who has asked for less motion. */
@media (prefers-reduced-motion:reduce){
  .btn{transition:background .18s ease,color .18s ease,border-color .18s ease}
  .btn:hover,.btn:active{transform:none}
}

/* Portrait at the head of the About column. Square rather than a circle to sit
   with the rest of the page's framing, and small enough not to compete with
   the section heading underneath it. */
.about-photo{
  width:190px;height:190px;object-fit:cover;object-position:center 28%;
  display:block;border:1px solid var(--rule);flex:0 0 auto;
}
/* Portrait and heading side by side at the head of the column. */
.about-head{display:flex;align-items:flex-start;gap:26px;margin:0 0 26px}
.about-head .section-title{margin:0}
@media (max-width:560px){
  .about-head{flex-direction:column;gap:18px}
  .about-head .about-photo{width:150px;height:150px}
}

/* ---------------- methodology ---------------- */
/* Full-width intro: two columns of prose rather than one narrow one. */
.ov-body--full{grid-template-columns:1fr 1fr;gap:56px}
@media (max-width:860px){ .ov-body--full{grid-template-columns:1fr;gap:28px} }

/* Phase navigator: nine stages, clickable, scrolls itself on narrow screens. */
.phase-nav{
  list-style:none;display:flex;gap:4px;margin:0 0 20px;padding:0;
  overflow-x:auto;scrollbar-width:none;
}
.phase-nav::-webkit-scrollbar{display:none}
.phase-nav li{flex:1 1 0;min-width:104px}
.phase-nav button{
  display:block;width:100%;text-align:left;background:none;border:0;cursor:pointer;
  padding:9px 12px 10px;border-radius:8px;
  font-family:var(--body);color:var(--muted);
  transition:color .2s ease,background-color .2s ease;
}
.phase-nav .pn-num{
  display:block;font-family:var(--mono);font-size:.64rem;letter-spacing:.14em;
  color:var(--faint);margin-bottom:4px;
}
/* Two lines at most, and a fixed box, so the bar never changes height as the
   active phase moves along it. */
.phase-nav .pn-name{
  display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:2;overflow:hidden;
  font-size:.88rem;line-height:1.3;
}
.phase-nav button{min-height:70px}
.phase-nav button:hover{color:var(--ink);background:rgba(25,28,26,.04)}
.phase-nav button[aria-current="true"]{color:var(--ink);background:var(--accent-soft)}
.phase-nav button[aria-current="true"] .pn-num{color:var(--accent)}

/* Rail: native horizontal scrolling with snap, so a swipe works on touch and a
   trackpad without any script. The buttons are an affordance for mouse users. */
.phase-shell{position:relative}
.phase-rail{
  list-style:none;margin:0;padding:0 0 6px;display:flex;gap:18px;
  overflow-x:auto;scroll-snap-type:x mandatory;scroll-behavior:smooth;
  overscroll-behavior-x:contain;scrollbar-width:none;-webkit-overflow-scrolling:touch;
  padding:10px 2px 26px;margin:-10px -2px -10px;
}
.phase-rail::-webkit-scrollbar{display:none}
.phase-rail:focus-visible{outline:2px solid var(--focus);outline-offset:6px}
.pcard{
  flex:0 0 93%;scroll-snap-align:start;scroll-snap-stop:always;
  background:var(--surface);border:1px solid var(--rule-soft);border-radius:var(--radius);
  box-shadow:0 1px 2px rgba(25,28,26,.04), 0 18px 40px -22px rgba(25,28,26,.30);
  transition:opacity .45s ease, transform .45s ease, box-shadow .45s ease;
  opacity:.38;transform:scale(.985);
  padding:30px 30px 28px;
  /* One grid for the whole card, so the two columns share rows: the headline
     sits level with EXAMPLE OUTPUT, and the summary with the outputs text —
     putting the OUTPUTS label on the row just above — whatever the length of
     the activity list. */
  display:grid;grid-template-columns:minmax(0,1fr) minmax(0,38%);
  grid-template-rows:auto auto auto 1fr auto auto;
  column-gap:34px;align-items:start;
}
.pcard-head{
  grid-column:1;grid-row:1;min-width:0;
  /* Number and title share a line so the card loses a row on a phone. */
  display:flex;align-items:baseline;column-gap:10px;margin-bottom:10px;
}
.pcard-lead{grid-column:1;grid-row:2}
.pcard-label--act{grid-column:1;grid-row:3}
.pcard-acts{grid-column:1;grid-row:4}
.pcard-note{grid-column:1;grid-row:6}
.pcard-eg{margin:0;grid-column:2;grid-row:2 / span 3;align-self:stretch;min-height:0;display:flex;flex-direction:column}
.pcard-label--out{grid-column:2;grid-row:5;align-self:end}
.pcard-out{grid-column:2;grid-row:6}
.pcard.is-active{opacity:1;transform:none;box-shadow:0 1px 2px rgba(25,28,26,.05), 0 26px 56px -26px rgba(25,28,26,.38)}
@media (prefers-reduced-motion:reduce){ .pcard{transition:none} }

.rail-btn{
  position:absolute;top:50%;transform:translateY(-50%);z-index:2;
  width:40px;height:40px;border:1px solid var(--rule);border-radius:50%;background:var(--surface);
  color:var(--ink);font-size:1rem;line-height:1;cursor:pointer;
  display:grid;place-items:center;transition:border-color .18s ease,color .18s ease;
}
.rail-btn:hover{border-color:var(--accent);color:var(--accent)}
.rail-btn[disabled]{opacity:.3;cursor:default;border-color:var(--rule);color:var(--muted)}
.rail-btn--prev{left:-20px}
.rail-btn--next{right:-20px}
@media (max-width:1180px){ .rail-btn{display:none} }

.pcard-num{
  /* Title's face and size, kept accent-green so it still reads as an index
     rather than the first word of the heading. */
  margin:0;flex:none;font-family:var(--display);font-weight:500;font-size:1.24rem;
  line-height:1.18;letter-spacing:-.01em;color:var(--accent);
}
.pcard h3{
  font-family:var(--display);font-weight:500;font-size:1.24rem;line-height:1.18;
  margin:0;letter-spacing:-.01em;
}
.pcard-lead{margin:0 0 18px;font-size:1rem;line-height:1.5;color:var(--ink)}
.pcard-label{
  margin:0 0 8px;font-family:var(--mono);font-size:.62rem;letter-spacing:.14em;
  text-transform:uppercase;color:var(--accent);
}
.pcard-label--out{padding-top:16px}
.pcard-acts{margin:0 0 14px;padding:0 0 0 16px;list-style:none}
.pcard-acts li{position:relative;margin:0 0 7px;font-size:.92rem;line-height:1.52;color:var(--muted)}
.pcard-acts li::before{
  content:"";position:absolute;left:-16px;top:.62em;width:5px;height:1px;background:var(--accent);
}
.pcard-acts li:last-child{margin-bottom:0}
.pcard-acts b{font-weight:600;color:var(--ink)}
.pcard-note{margin:0 0 16px;font-size:.88rem;line-height:1.55;color:var(--muted);font-style:italic}

/* Example artefact, between the note and the outputs. */
.pcard-eg img{
  display:block;width:100%;flex:1 1 0;min-height:0;object-fit:cover;object-position:top center;
  border:1px solid var(--rule-soft);border-radius:calc(var(--radius) - 4px);background:var(--ground);
}
.pcard-eg figcaption{
  margin-top:8px;font-family:var(--mono);font-size:.6rem;letter-spacing:.09em;
  text-transform:uppercase;color:var(--faint);line-height:1.5;
}
.pcard-out{margin:0;font-size:.92rem;line-height:1.6;color:var(--muted)}

/* Constants keep the card shell but sit in a static grid. */
.thread-grid{
  list-style:none;margin:0;padding:0;
  display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:18px;
}
.tcard{
  background:var(--surface);border:1px solid var(--rule-soft);border-radius:var(--radius);
  box-shadow:0 1px 2px rgba(25,28,26,.04), 0 14px 30px -20px rgba(25,28,26,.26);
  padding:24px;
}
@supports (grid-template-rows:subgrid){
  .tcard{display:grid;grid-row:span 2;grid-template-rows:subgrid;row-gap:0}
}
.tcard h3{
  font-family:var(--display);font-weight:500;font-size:1.18rem;line-height:1.2;
  margin:0 0 8px;letter-spacing:-.01em;align-self:start;
}
.tcard p{margin:0;font-size:.94rem;line-height:1.58;color:var(--muted);align-self:start}

@media (max-width:1000px){
  .thread-grid{grid-template-columns:repeat(2,minmax(0,1fr))}
}
/* Phones: back to a single stack, image between the note and the outputs.
   .pcard-main becomes display:contents so its children can be ordered against
   the figure, which sits after it in the markup. */
@media (max-width:860px){
  .pcard{display:block;flex:0 0 88%;padding:18px}
  .pcard-head,.pcard-lead,.pcard-label,.pcard-acts,.pcard-note,.pcard-eg,.pcard-out{grid-column:auto;grid-row:auto}
  /* DOM order is already head → activities → note → example → outputs, so
     nothing needs reordering once the wrappers dissolve. */
  .pcard-note{margin-bottom:12px;font-size:.84rem;line-height:1.45}
  .pcard-eg{margin-bottom:12px}
  .pcard-label--out{padding-top:10px}
  .pcard-out{font-size:.88rem;line-height:1.5}
  /* Tighter type so a whole phase fits a portrait screen without cutting the
     specifics that make the card worth reading. */
  .pcard-head{margin-bottom:6px;column-gap:8px}
  .pcard-num{font-size:1.14rem}
  .pcard h3{font-size:1.14rem}
  .pcard-lead{font-size:.95rem;line-height:1.42;margin-bottom:12px}
  .pcard-label{margin-bottom:6px}
  .pcard-acts{margin-bottom:12px}
  .pcard-acts li{font-size:.86rem;line-height:1.42;margin-bottom:5px}
  /* height:auto is required or the img's height attribute beats aspect-ratio */
  .pcard-eg img{flex:0 0 auto;height:auto;aspect-ratio:3/1;min-height:0}
  .pcard-eg figcaption{margin-top:6px;font-size:.56rem}
  /* Navigator collapses to numbers with the active stage named beside them. */
  .phase-nav li{flex:0 0 auto;min-width:0}
  .phase-nav button{padding:8px 14px 9px;min-height:0}
  .phase-nav .pn-name{display:none}
  /* one line always, or the bar changes height as you swipe */
  .phase-nav button[aria-current="true"] .pn-name{display:inline;font-size:.8rem;white-space:nowrap}
  .thread-grid{grid-template-columns:1fr}
}

/* Guiding principles: numbered, two columns. */
.principles{
  list-style:none;margin:0;padding:0;counter-reset:tenet;
  display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:0 48px;
  border-top:1px solid var(--rule-soft);
}
.principles li{
  position:relative;padding:22px 0 22px 38px;border-bottom:1px solid var(--rule-soft);
  counter-increment:tenet;
}
.principles li::before{
  content:counter(tenet,decimal-leading-zero);
  position:absolute;left:0;top:25px;
  font-family:var(--mono);font-size:.68rem;letter-spacing:.08em;color:var(--accent);
}
.principles b{display:block;font-weight:600;font-size:1.02rem;margin-bottom:4px}
.principles span{display:block;color:var(--muted);font-size:.96rem;line-height:1.58}

/* Link from a case study through to the method. */
/* A single case-study link at the foot of the method page, built from the
   prev/next component but constrained so the headline keeps a sane measure. */
.method-case-lead{max-width:none;margin:0 0 30px;font-size:1.12rem;line-height:1.6;color:var(--muted)}
.method-case{max-width:620px}

.about-body 
@media (max-width:1000px){
  .phase-grid,.thread-grid{grid-template-columns:repeat(2,minmax(0,1fr))}
}
@media (max-width:680px){
  .phase-grid,.thread-grid,.principles{grid-template-columns:1fr}
}

/* ---------------- "How I work" callout ---------------- */
/* One component, three homes: the About column on the homepage, and the top of
   the More work nav on every project page. It sits OUTSIDE [data-protected] on
   those pages deliberately, so it reads the same whether or not the visitor has
   the password. The whole card is the link — a methodology page is the sort of
   thing people click the headline of, not a trailing arrow. */
.method-cta{
  display:block;text-decoration:none;border:1px solid var(--rule-soft);
  border-radius:var(--radius);background:var(--surface);
  padding:26px 30px 24px;
  transition:border-color .2s ease, box-shadow .2s ease;
}
.method-cta:hover{
  border-color:var(--accent);
  box-shadow:0 1px 2px rgba(25,28,26,.04), 0 18px 40px -26px rgba(25,28,26,.34);
}

.method-cta-kicker{
  display:block;margin-bottom:11px;font-family:var(--mono);font-size:.68rem;
  letter-spacing:.12em;text-transform:uppercase;color:var(--accent);
}
.method-cta-title{
  display:block;font-family:var(--display);font-weight:400;
  font-size:clamp(1.28rem,2.1vw,1.62rem);line-height:1.22;letter-spacing:-.015em;
  color:var(--ink);transition:color .2s ease;
}
.method-cta:hover .method-cta-title{color:var(--accent)}
.method-cta-body{
  /* Full container width on purpose: stretching the line makes the block shorter,
     which is what keeps a wide banner from dominating the section it sits in. */
  display:block;margin-top:11px;
  color:var(--muted);font-size:.97rem;line-height:1.55;
}
.method-cta-go{
  display:block;margin-top:17px;font-family:var(--mono);font-size:.73rem;
  letter-spacing:.1em;text-transform:uppercase;color:var(--accent);
}
/* On the project pages it heads the More work nav, so it needs air beneath it. */
.more-work .method-cta{margin:0 0 40px}
/* The All projects link sits between the More work label and the pair of
   project cards, outside the password gate it used to live behind. */
.more-work .backlink{margin:2px 0 26px}
/* On the homepage it is a grid item of .split spanning both columns, so it runs
   the full page width under them. On a phone .split collapses to one column and
   order puts it between the disciplines and the biography, where John wants it. */
.about-cta{grid-column:1 / -1;margin:14px 0 0}
@media (max-width:860px){
  .about-cta{order:2;margin:0}
  .about-body{order:3}
}
@media (max-width:760px){
  .method-cta{padding:22px 20px 20px}
  .more-work .method-cta{margin-bottom:30px}
}

/* ==================================================================
   Interaction states
   One focus ring, two link behaviours, one arrow, one press.
   Everything interactive on the site should be covered by this block;
   if something needs its own rule, that is a sign it should not exist.
   ================================================================== */

/* --- focus -------------------------------------------------------- */
/* The ring colour is a variable, not a constant, because the accent green is
   2.34:1 on the dark ground — a ring nobody can see. Any container sitting on
   dark redefines --focus for everything inside it. */
:root{ --focus:var(--accent); }
.hero, .site-footer{ --focus:var(--accent-on-dark); }

a:focus-visible,
button:focus-visible,
input:focus-visible,
[tabindex]:focus-visible{
  outline:2px solid var(--focus);
  outline-offset:3px;
}

/* --- skip link ---------------------------------------------------- */
.skip-link{
  position:absolute;left:var(--gutter);top:-72px;z-index:200;
  padding:11px 18px;border:1px solid var(--accent);border-radius:8px;
  background:var(--surface);color:var(--accent);text-decoration:none;
  font-family:var(--mono);font-size:.72rem;letter-spacing:.1em;text-transform:uppercase;
  transition:top .16s ease;
}
.skip-link:focus{ top:12px; }

/* --- links -------------------------------------------------------- */
/* Two behaviours, and which one applies is decided by where the link starts.
   A. Starts as ink or muted: hover brings the green in and draws the underline.
   B. Starts green already: hover darkens it. Hue change is not available to a
      link that is the accent colour, which is why these used to feel inert. */
.project-link, .backlink, .method-cta-go, .more-title{
  transition:color .18s ease, border-bottom-color .18s ease;
}
.project-link:hover, .backlink:hover,
.method-cta:hover .method-cta-go{ color:var(--accent-strong) }
.project-link:hover, .backlink:hover{ border-bottom-color:var(--accent-strong) }
.project-link:active, .backlink:active,
.method-cta:active .method-cta-go{ color:var(--accent-strong) }

/* On dark, green is unavailable, so the underline brightens to the foreground
   instead. Same grammar, different ground. */
.site-footer a{ --focus:var(--accent-on-dark); }

/* --- arrows ------------------------------------------------------- */
/* An arrow is the site's mark for "this goes somewhere", so it moves. Three
   mechanisms exist in the markup — a wrapped character, a generated ::before
   and a generated ::after — and all three nudge by the same 3px. */
.arw{ display:inline-block;transition:transform .2s ease }
a:hover .arw, .method-cta:hover .arw{ transform:translateX(3px) }
a:hover .arw--back{ transform:translateX(-3px) }

/* --- press -------------------------------------------------------- */
/* Hover does not exist on a touchscreen, so :active is the only feedback a tap
   ever gets. Each variant presses one step further along the path its own
   hover already took. */
.btn:active{ transform:translateY(0);background:rgba(245,243,239,.24);border-color:#FFFFFF }
.btn--solid:active{ background:#BDB9B9;border-color:#BDB9B9 }
.btn--dark:active{ transform:translateY(0);background:rgba(25,28,26,.16) }
.btn--dark-solid:active{ background:#5A5C58;border-color:#5A5C58 }
.rail-btn:active{ background:var(--accent-soft) }
.phase-nav button:active{ background:rgba(25,28,26,.08) }

/* --- motion ------------------------------------------------------- */
@media (prefers-reduced-motion:reduce){
  html{ scroll-behavior:auto }
  .arw,
  .method-cta,
  .skip-link{ transition:none }
  a:hover .arw, a:hover .arw--back{ transform:none }
}

/* Option B: small uppercase mono is the green layer, so the top menu and the
   project sub-nav join it. That makes them rule-B links — already green, so
   hover darkens rather than introducing colour — and leaves the arrow as the
   only thing that says something goes somewhere. */
.site-nav a, .proj-nav a{ transition:color .18s ease, border-bottom-color .18s ease }

/* ==================================================================
   Cards
   A card is one hover unit. Inside it the display title changes colour
   but never underlines — three underlines lighting up at once reads as
   noise — while the small mono action link underlines and its arrow
   moves, exactly as a standalone link does. Images highlight.
   ================================================================== */

/* :has() lets the tile react to any of its three links being hovered, with no
   script and no wrapper element. The body copy and tags are not triggers,
   because they are not clickable and should not promise that they are. */
.projects .project:has(.project-media-link:hover),
.projects .project:has(h3 a:hover),
.projects .project:has(.project-link:hover){ --card-hover:1 }

.projects .project:has(.project-media-link:hover) .project-frame,
.projects .project:has(h3 a:hover) .project-frame,
.projects .project:has(.project-link:hover) .project-frame{ border-color:var(--accent) }

.projects .project:has(.project-media-link:hover) .project-frame img,
.projects .project:has(h3 a:hover) .project-frame img,
.projects .project:has(.project-link:hover) .project-frame img{ transform:rotate(-2deg) scale(1.16) }
.projects .project:nth-child(even):has(.project-media-link:hover) .project-frame img,
.projects .project:nth-child(even):has(h3 a:hover) .project-frame img,
.projects .project:nth-child(even):has(.project-link:hover) .project-frame img{ transform:rotate(2deg) scale(1.16) }

/* Title: colour only. The underline it used to draw is now the action link's job. */
.project h3 a:hover{ border-bottom-color:transparent }
.projects .project:has(.project-media-link:hover) h3 a,
.projects .project:has(h3 a:hover) h3 a,
.projects .project:has(.project-link:hover) h3 a{ color:var(--accent);border-bottom-color:transparent }

.projects .project:has(.project-media-link:hover) .project-link,
.projects .project:has(h3 a:hover) .project-link,
.projects .project:has(.project-link:hover) .project-link{
  color:var(--accent-strong);border-bottom-color:var(--accent-strong);
}
.projects .project:has(.project-media-link:hover) .project-link .arw,
.projects .project:has(h3 a:hover) .project-link .arw,
.projects .project:has(.project-link:hover) .project-link .arw{ transform:translateX(3px) }

.project h3 a{ transition:color .18s ease, border-bottom-color .18s ease }

/* The "How I work" card had an action link with no underline, which made it the
   one green link on the site that did not look like the others. */
.method-cta-go{ display:inline-block;border-bottom:1px solid var(--rule);padding-bottom:3px }
.method-cta:hover .method-cta-go{ border-bottom-color:var(--accent-strong) }

@media (prefers-reduced-motion:reduce){
  .projects .project:has(.project-media-link:hover) .project-frame img,
  .projects .project:has(h3 a:hover) .project-frame img,
  .projects .project:has(.project-link:hover) .project-frame img{ transform:rotate(-2deg) scale(1.12) }
  .projects .project:nth-child(even):has(.project-media-link:hover) .project-frame img,
  .projects .project:nth-child(even):has(h3 a:hover) .project-frame img,
  .projects .project:nth-child(even):has(.project-link:hover) .project-frame img{ transform:rotate(2deg) scale(1.12) }
}

/* The previous/next card now follows the card rule like the project tiles:
   the title changes colour only, and a mono action link carries the underline
   and the arrow. Direction lives in the kicker above ("Previous project"), so
   the arrow always points forward — the same arrow as "View project" on the
   home page, for the same action. */
.more-go{
  display:inline-block;margin-top:14px;
  font-family:var(--mono);font-size:.72rem;letter-spacing:.09em;text-transform:uppercase;
  color:var(--accent);border-bottom:1px solid var(--rule);padding-bottom:3px;
  transition:color .18s ease, border-bottom-color .18s ease;
}
.more-link:hover .more-go{ color:var(--accent-strong);border-bottom-color:var(--accent-strong) }

/* The whole project tile is the link. The action link is stretched across it,
   so body copy and tags are clickable too; the picture and the heading keep
   their own hit areas above it so middle-click and right-click still address
   the thing under the cursor. */
/* The overlay is positioned against the padding box, so inset:0 would hand the
   visitor 52px of empty gutter above and below the tile as a hit area. Inset it
   by the row padding instead, and the clickable area stops exactly at the
   topmost and bottommost visible thing. --row-pad tracks the padding, so the
   two cannot drift apart. */
.projects .project{ position:relative;--row-pad:52px }
.project-link::after{ content:"";position:absolute;inset:var(--row-pad) 0;z-index:1 }
.project-media-link, .project h3 a{ position:relative;z-index:2 }
@media (max-width:860px){ .projects .project{ --row-pad:40px } }

/* A neighbour card is clickable, so it takes a pointer and, on hover, moves
   part of the way toward the active state — brighter, a shade larger, a deeper
   shadow. Only part of the way: shadow depth is what tells you which card you
   are reading, so a hovered neighbour must never arrive at the active card's
   weight or the two become indistinguishable. The active card is not clickable
   and keeps the text cursor, because reading it is the point. */
.pcard:not(.is-active){ cursor:pointer }
.pcard:not(.is-active):hover{
  opacity:.62;transform:scale(.995);
  box-shadow:0 1px 2px rgba(25,28,26,.05), 0 22px 48px -24px rgba(25,28,26,.34);
}
@media (hover:none){ .pcard:not(.is-active):hover{ opacity:.38;transform:scale(.985) } }
