/* ==========================================================================
   SECTIONS - experience, interlude, skills, contact.
   ========================================================================== */

/* ==========================================================================
   01 - EXPERIENCE
   ========================================================================== */

/* --------------------------------------------------------------------------
   The record is three groups, not one list.

   This section used to be a flat run of nine entries with a category filter
   bar on top. Two things were wrong with that. Nobody filters - they scroll,
   and the default view was all nine at identical weight. And the two entries
   a reader actually goes looking for (which university, which school) sat
   dead last, behind seven others.

   So the ranking is built into the layout instead of being offered as a
   control: education first and compact, three roles at full weight, and the
   remaining four as a dense roll that opens on demand. Nothing was cut -
   it is all still here, just no longer all shouting at once.
   -------------------------------------------------------------------------- */

.rec-group + .rec-group { margin-top: clamp(var(--sp-9), 7vw, var(--sp-11)); }

.rec-sub {
  display: flex;
  align-items: center;
  gap: var(--sp-4);
  margin: 0 0 clamp(var(--sp-5), 3vw, var(--sp-7));
  font-weight: 400;
}

.rec-sub__label { color: var(--ink-2); white-space: nowrap; }

.rec-sub__rule {
  flex: 1;
  min-width: var(--sp-5);
  height: 1px;
  background: linear-gradient(90deg, var(--line-2), transparent);
}

.rec-sub__meta { color: var(--ink-4); white-space: nowrap; }

@media (max-width: 560px) {
  /* The meta is the first thing to go when the line gets tight - the label
     is what carries the structure. */
  .rec-sub__meta { display: none; }
}

/* Education plates -------------------------------------------------------- */

.creds {
  display: grid;
  gap: var(--sp-4);
}

@media (min-width: 720px) {
  .creds {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    column-gap: var(--sp-5);
    row-gap: var(--sp-2);
  }

  /* Subgrid so both plates agree on where the rule, the name and the degree
     sit, however differently their text wraps - "J. Clarke Richardson
     Collegiate" takes two lines where "University of Waterloo" takes one,
     and without this that shunts everything below it out of step. */
  @supports (grid-template-rows: subgrid) {
    .creds { grid-template-rows: auto auto auto auto; }
    .cred { grid-row: span 4; grid-template-rows: subgrid; }
  }
}

.cred {
  --cred-accent: var(--line-2);
  position: relative;
  display: grid;
  gap: var(--sp-2);
  padding: clamp(var(--sp-5), 3vw, var(--sp-6));
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  background: var(--surface);
  overflow: hidden;
}

/* A hairline of accent along the top edge: enough to rank the two plates
   against each other without a second border colour. */
.cred::before {
  content: '';
  position: absolute;
  inset: 0 0 auto;
  height: 2px;
  background: linear-gradient(90deg, var(--cred-accent), transparent 78%);
}

.cred--next {
  --cred-accent: var(--copper);
  border-color: color-mix(in srgb, var(--copper) 22%, var(--line));
  background:
    linear-gradient(180deg, var(--copper-wash), transparent 42%),
    var(--surface);
}

.cred__top {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  flex-wrap: wrap;
  margin-bottom: var(--sp-3);
  padding-bottom: var(--sp-4);
  border-bottom: 1px solid var(--line);
}

.cred__mark {
  width: 30px;
  height: 30px;
  flex: none;
  border-radius: 5px;
  background: #fff;
  padding: 2px;
  object-fit: contain;
}

.cred__mark--pad { padding: 1px; }

/* Pushes the chip to the far edge and lets the dates sit next to the mark. */
.cred__when { margin: 0; margin-right: auto; color: var(--ink-3); }

/* Sans, not Fraunces. A plate is a record, not prose - and at this size the
   display face's SOFT and WONK axes turn a long institution name mushy,
   which is exactly the case .h-sans exists for. */
.cred__school {
  margin: 0;
  font-family: var(--font-sans);
  font-weight: 560;
  font-size: var(--step-1);
  line-height: var(--leading-snug);
  letter-spacing: -0.022em;
  color: var(--ink);
  text-wrap: balance;
}

.cred__degree {
  margin: 0;
  font-size: var(--step--1);
  color: var(--ink-2);
}

.cred__where { margin: var(--sp-2) 0 0; color: var(--ink-4); }

/* Timeline ---------------------------------------------------------------- */

.timeline {
  list-style: none;
  margin: 0;
  padding: 0;
  position: relative;
  display: grid;
  gap: clamp(var(--sp-6), 3.5vw, var(--sp-8));
}

.tl {
  position: relative;
  display: grid;
  gap: var(--sp-3) var(--sp-5);
  grid-template-columns: 1fr;
  padding-left: var(--sp-7);
  transition: opacity var(--dur-3) var(--ease-out);
}

.tl::before {
  content: '';
  position: absolute;
  left: 5px;
  top: 0.55rem;
  bottom: calc(-1 * clamp(var(--sp-6), 3.5vw, var(--sp-8)));
  width: 1px;
  background: var(--line-2);
}

/* No connector trailing off the bottom of the last entry. (There used to be
   an .is-tail hook here too, for when filtering could hide the real last
   entry; the filter is gone, so :last-child is now always right.) */
.tl:last-child::before { display: none; }

.tl__node {
  position: absolute;
  left: 0;
  top: 0.45rem;
  width: 11px; height: 11px;
  border-radius: 50%;
  background: var(--bg);
  border: 1.5px solid var(--ink-4);
  transition: border-color var(--dur-3) var(--ease-out),
              background-color var(--dur-3) var(--ease-out),
              box-shadow var(--dur-3) var(--ease-out);
}

.tl:hover .tl__node,
.tl:focus-within .tl__node {
  border-color: var(--copper);
  background: var(--copper);
  box-shadow: 0 0 0 4px var(--copper-wash);
}

.tl__when {
  display: flex;
  align-items: baseline;
  gap: var(--sp-3);
  font-family: var(--font-mono);
  font-size: var(--step--2);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-3);
}

.tl__when > :last-child { color: var(--ink-4); }
.tl__when > :last-child::before { content: '→ '; color: var(--ink-4); }
.tl__now { color: var(--copper) !important; }

.tl__what { display: grid; gap: var(--sp-3); min-width: 0; }

.tl__org {
  color: var(--ink-2);
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  flex-wrap: wrap;
}

/* Organisation marks sit on a light chip so black-on-transparent crests
   stay visible against the dark theme. */
.org-logo {
  width: 26px;
  height: 26px;
  flex: none;
  border-radius: 5px;
  background: #fff;
  padding: 2px;
  object-fit: contain;
}

.org-logo--pad { padding: 1px; }

.tl__role { margin: 0; color: var(--ink); }

.tl__points {
  margin: 0;
  padding: 0;
  list-style: none;
  display: grid;
  gap: var(--sp-2);
  color: var(--ink-2);
  max-width: 62ch;
}

.tl__points li { position: relative; padding-left: var(--sp-5); }

.tl__points li::before {
  content: '';
  position: absolute;
  left: 0;
  top: 0.72em;
  width: 10px;
  height: 1px;
  background: var(--copper-lo);
}

/* A figure inside a timeline entry - e.g. the board layout. */
.tl__figure { margin: var(--sp-3) 0 0; display: grid; gap: var(--sp-3); max-width: 540px; }

.tl__figure-frame {
  border: 1px solid var(--line);
  border-radius: var(--radius);
  overflow: hidden;
  background: var(--bg-sunk);
  transition: border-color var(--dur-3) var(--ease-out);
}

.tl__figure-frame img { width: 100%; height: auto; }
.tl:hover .tl__figure-frame { border-color: color-mix(in srgb, var(--copper) 35%, transparent); }

@media (min-width: 780px) {
  .tl {
    grid-template-columns: 140px minmax(0, 1fr);
    padding-left: var(--sp-8);
    align-items: start;
  }
  .tl__when { flex-direction: column; gap: 2px; padding-top: 0.2rem; }
  .tl__when > :last-child::before { content: ''; }
}

@media (min-width: 1100px) {
  .tl { grid-template-columns: 160px minmax(0, 1fr); }
}

.chip {
  display: inline-flex;
  align-items: center;
  padding: 2px 8px;
  border-radius: 99px;
  font-family: var(--font-mono);
  font-size: var(--step--2);
  letter-spacing: 0.1em;
  text-transform: uppercase;
}

.chip--live {
  color: var(--copper);
  background: var(--copper-wash);
  border: 1px solid color-mix(in srgb, var(--copper) 35%, transparent);
}

/* The roll ---------------------------------------------------------------- */
/* One line per role, in the same hover idiom as the skills index, so the two
   dense lists on the page read as the same kind of object. */

.roll { list-style: none; margin: 0; padding: 0; }

.roll__item { border-bottom: 1px solid var(--line); }
.roll__item:first-child { border-top: 1px solid var(--line); }

.roll__d {
  --roll-inset: 0px;
  position: relative;
}

/* A copper edge marks the row you opened, so an expanded row still reads as
   one item once its detail pushes the next row down the page. */
.roll__d::before {
  content: '';
  position: absolute;
  inset: 0 auto 0 0;
  width: 2px;
  background: var(--copper);
  transform: scaleY(0);
  transform-origin: 50% 0;
  transition: transform var(--dur-2) var(--ease-out);
}

.roll__d[open]::before { transform: scaleY(1); }

.roll__sum {
  display: grid;
  grid-template-columns: 12px minmax(0, 1fr);
  grid-template-areas:
    'chev when'
    '.    org'
    '.    role';
  align-items: center;
  gap: 2px var(--sp-4);
  padding: 0.9rem var(--sp-3) 0.9rem calc(var(--sp-3) + var(--roll-inset));
  cursor: pointer;
  list-style: none;
  transition: background-color var(--dur-2) var(--ease-out),
              padding-left var(--dur-2) var(--ease-out);
}

.roll__sum::-webkit-details-marker { display: none; }
.roll__sum::marker { content: ''; }

.roll__sum:hover { --roll-inset: var(--sp-3); background: color-mix(in srgb, var(--copper) 5%, transparent); }
.roll__d[open] .roll__sum { --roll-inset: var(--sp-3); }

.roll__sum:focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: -2px;
  border-radius: var(--radius-sm);
}

/* Chevron: right when closed, down when open. */
.roll__chev {
  grid-area: chev;
  width: 6px;
  height: 6px;
  align-self: center;
  border-right: 1.5px solid var(--ink-4);
  border-bottom: 1.5px solid var(--ink-4);
  transform: rotate(-45deg);
  transition: transform var(--dur-2) var(--ease-out),
              border-color var(--dur-2) var(--ease-out);
}

.roll__sum:hover .roll__chev { border-color: var(--copper); }
.roll__d[open] .roll__chev { transform: rotate(45deg); border-color: var(--copper); }

.roll__when {
  grid-area: when;
  font-size: var(--step--2);
  letter-spacing: 0.08em;
  color: var(--ink-3);
  white-space: nowrap;
}

.roll__org {
  grid-area: org;
  font-weight: 540;
  font-size: var(--step--1);
  color: var(--ink);
  line-height: 1.35;
}

.roll__role {
  grid-area: role;
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: var(--sp-2) var(--sp-3);
  font-family: var(--font-mono);
  font-size: var(--step--2);
  letter-spacing: 0.02em;
  color: var(--ink-3);
  line-height: 1.45;
}

/* Compressing a role must not hide the one fact that made it worth keeping,
   so anything that won something says so on the closed row. */
.roll__badge {
  display: inline-flex;
  align-items: baseline;
  gap: 5px;
  padding: 2px 8px;
  border: 1px solid color-mix(in srgb, var(--copper) 30%, transparent);
  border-radius: 99px;
  background: var(--copper-wash);
  color: var(--copper);
  font-size: 0.94em;
  letter-spacing: 0.06em;
  white-space: nowrap;
}

.roll__body {
  display: grid;
  gap: var(--sp-4);
  padding: 0 var(--sp-3) var(--sp-6) calc(var(--sp-6) + var(--sp-3));
}

/* Once there is room the row becomes a single scannable line: dates, then
   organisation, then role, in three fixed columns. */
@media (min-width: 780px) {
  .roll__sum {
    grid-template-columns: 12px minmax(0, 8.5rem) minmax(0, 1.15fr) minmax(0, 1fr);
    grid-template-areas: 'chev when org role';
    gap: var(--sp-5);
    padding-block: 0.85rem;
  }
  .roll__org { font-size: var(--step-0); letter-spacing: -0.005em; }
  .roll__body {
    padding-left: calc(12px + 8.5rem + var(--sp-5) * 2 + var(--sp-3));
    padding-bottom: var(--sp-7);
  }
}

/* Animate the open where the browser can interpolate to auto. Everywhere
   else it just snaps, which is the pre-2024 <details> behaviour and fine. */
@supports (interpolate-size: allow-keywords) {
  .roll { interpolate-size: allow-keywords; }

  .roll__d::details-content {
    block-size: 0;
    overflow: clip;
    transition: block-size var(--dur-3) var(--ease-out),
                content-visibility var(--dur-3) allow-discrete;
  }

  .roll__d[open]::details-content { block-size: auto; }
}

@media (prefers-reduced-motion: reduce) {
  .roll__d::details-content { transition: none; }
}

/* ==========================================================================
   INTERLUDE - a full-bleed visual break
   ========================================================================== */

.plate {
  position: relative;
  margin-left: var(--rail);
  height: clamp(300px, 46vh, 560px);
  overflow: hidden;
  isolation: isolate;
  border-block: 1px solid var(--line);
}

.plate__img { position: absolute; inset: -12% 0; z-index: -1; }

.plate__img img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  will-change: transform;
}


/* Fade the plate into the page above and below it. */
.plate::after {
  content: '';
  position: absolute;
  inset: 0;
  background: linear-gradient(180deg,
    var(--bg) 0%,
    transparent 26%,
    transparent 74%,
    var(--bg) 100%);
}

/* In light mode the band stays dark and keeps hard edges - inverting it
   turned the gold pink, and a full-bleed dark break on paper reads as a
   deliberate choice rather than an accident. */
[data-theme='light'] .plate::after { background: none; }
[data-theme='light'] .plate { border-block-color: transparent; }

/* ==========================================================================
   04 - SKILLS

   Set as an index rather than a list of claims: every row names a tool and
   the thing it was actually used to build. That is both more useful to a
   reader (it answers "can he really use this?") and denser, since each row
   now earns its height instead of holding a single word.

   The old treatment drew these as parts stubbed off a PCB bus. That was a
   leftover from when the site led with electrical work, and it read as
   decoration once the framing moved to computer science.
   ========================================================================== */

.proof {
  display: grid;
  gap: clamp(var(--sp-8), 5vw, var(--sp-10));
}

.proof__title {
  margin: 0 0 var(--sp-4);
  padding-bottom: var(--sp-3);
  border-bottom: 1px solid var(--line-2);
  color: var(--ink-2);
}

.proof__rows { list-style: none; margin: 0; padding: 0; }

.proof__row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  grid-template-areas: 'skill link' 'use link';
  align-items: baseline;
  gap: 2px var(--sp-4);
  padding: 0.72rem 0.5rem 0.72rem 0;
  border-bottom: 1px solid var(--line);
  transition: background-color var(--dur-2) var(--ease-out),
              padding-left var(--dur-2) var(--ease-out);
}

.proof__row:hover {
  background: color-mix(in srgb, var(--copper) 5%, transparent);
  padding-left: var(--sp-3);
}

.proof__skill {
  grid-area: skill;
  font-weight: 560;
  font-size: var(--step-0);
  color: var(--ink);
  letter-spacing: -0.005em;
}

.proof__use {
  grid-area: use;
  font-family: var(--font-mono);
  font-size: var(--step--2);
  letter-spacing: 0.02em;
  color: var(--ink-3);
  line-height: 1.5;
}

.proof__link {
  grid-area: link;
  align-self: center;
  min-width: 1.4em;
  text-align: right;
  font-size: var(--step--1);
  color: var(--copper);
  text-decoration: none;
  opacity: 0;
  transform: translateX(-4px);
  transition: opacity var(--dur-2) var(--ease-out),
              transform var(--dur-2) var(--ease-out);
}

/* The arrow is an affordance, not a decoration, so it appears on approach
   and is always present for keyboard users. */
.proof__row:hover a.proof__link,
a.proof__link:focus-visible { opacity: 1; transform: none; }

@media (pointer: coarse) {
  a.proof__link { opacity: 1; transform: none; padding: 6px 0 6px 12px; }
}

/* Two columns of rows once there is room; the skill and its evidence stay
   on one line each at this width, which is where the index reads best. */
@media (min-width: 760px) {
  .proof__row {
    grid-template-columns: minmax(0, 13rem) minmax(0, 1fr) auto;
    grid-template-areas: 'skill use link';
    align-items: baseline;
    gap: var(--sp-5);
    padding-block: 0.62rem;
  }
  .proof__use { font-size: var(--step--1); }
}

@media (min-width: 1180px) {
  .proof { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--sp-9) clamp(var(--sp-7), 4vw, var(--sp-9)); }
  /* Languages is the longest group and the one that matters most, so it
     takes the full width and the shorter two sit beside each other. */
  .proof__group:first-child { grid-column: 1 / -1; }
  .proof__group:first-child .proof__rows {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    column-gap: clamp(var(--sp-7), 4vw, var(--sp-9));
  }
  /* Narrower skill column here: the columns are half-width, so every
     millimetre given to the label is one the evidence loses, and evidence
     wrapping to two lines is what makes the rows ragged. */
  .proof__row { grid-template-columns: minmax(0, 7.5rem) minmax(0, 1fr) auto; gap: var(--sp-4); }
  .proof__use { font-size: var(--step--2); }
}

/* ==========================================================================
   05 - CONTACT
   ========================================================================== */

/* The closing section carries the same generated lattice as the interlude,
   but pushed right back behind a heavy scrim - it is texture here, not a
   focal point, and the type has to stay at AA. */
.contact {
  position: relative;
  isolation: isolate;
  overflow: hidden;
}

.contact__bg {
  position: absolute;
  inset: 0;
  z-index: -1;
  pointer-events: none;
}

/* This variant was generated for the job - already dark, already sparse -
   so it needs far less scrim than a repurposed one would. */
.contact__bg img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: 50% 60%;
  opacity: 0.9;
}

.contact__bg::after {
  content: '';
  position: absolute;
  inset: 0;
  background:
    radial-gradient(90% 80% at 16% 40%,
      var(--bg) 0%,
      color-mix(in srgb, var(--bg) 72%, transparent) 42%,
      transparent 80%),
    linear-gradient(180deg,
      var(--bg) 0%,
      color-mix(in srgb, var(--bg) 28%, transparent) 24%,
      color-mix(in srgb, var(--bg) 30%, transparent) 72%,
      var(--bg) 100%);
}

/* On paper the dark lattice would read as grime, so it drops to a whisper. */
[data-theme='light'] .contact__bg img { opacity: 0.12; }

.contact__grid {
  position: relative;
  display: grid;
  gap: clamp(var(--sp-8), 6vw, var(--sp-11));
  align-items: start;
}

@media (min-width: 940px) {
  .contact__grid { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); align-items: center; }
}

.contact__title { margin: 0 0 var(--sp-5); }
.contact__lede { margin: 0; }

.pins { list-style: none; margin: 0; padding: 0; display: grid; gap: 0; }

.pin {
  position: relative;
  display: grid;
  grid-template-columns: 38px minmax(0, 1fr) auto;
  align-items: center;
  gap: var(--sp-4);
  border-top: 1px solid var(--line);
  transition: background-color var(--dur-2) var(--ease-out);
}

.pin:last-child { border-bottom: 1px solid var(--line); }

.pin__no {
  display: grid;
  place-items: center;
  width: 26px; height: 26px;
  border-radius: var(--radius-sm);
  border: 1px solid var(--line-2);
  background: var(--pad);
  font-size: var(--step--2);
  color: var(--ink-3);
  transition: border-color var(--dur-2) var(--ease-out),
              color var(--dur-2) var(--ease-out),
              background-color var(--dur-2) var(--ease-out);
}

.pin__link {
  display: grid;
  gap: 3px;
  padding-block: var(--sp-4);
  text-decoration: none;
  min-width: 0;
}

.pin__k { color: var(--ink-4); }

.pin__v {
  font-size: var(--step-0);
  color: var(--ink);
  overflow-wrap: anywhere;
  transition: color var(--dur-2) var(--ease-out);
}

.pin__ext { color: var(--copper); font-size: 0.85em; }

.pin:not(.pin--static):hover { background: color-mix(in srgb, var(--copper) 5%, transparent); }
.pin:not(.pin--static):hover .pin__no { border-color: var(--copper); color: var(--copper); background: var(--copper-wash); }
.pin:not(.pin--static):hover .pin__v { color: var(--copper-hi); }

.pin__copy {
  margin-right: var(--sp-3);
  padding: 0.4rem 0.75rem;
  border: 1px solid var(--line-2);
  border-radius: 99px;
  background: transparent;
  font-family: var(--font-mono);
  font-size: var(--step--2);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-3);
  cursor: pointer;
  transition: border-color var(--dur-2) var(--ease-out), color var(--dur-2) var(--ease-out);
}

.pin__copy:hover { border-color: var(--copper); color: var(--copper); }
.pin__copy.is-done { border-color: var(--signal); color: var(--signal); }

@media (max-width: 520px) {
  .pin { grid-template-columns: 30px minmax(0, 1fr); }
  .pin__copy { grid-column: 2; justify-self: start; margin: 0 0 var(--sp-4); }
}
