  /* ---------- light sections ---------- */
  .sec{padding:clamp(56px,9vh,110px) var(--gut)}
  .sec-head{display:flex;align-items:baseline;justify-content:space-between;gap:20px;flex-wrap:wrap;margin-bottom:clamp(30px,5vh,52px)}
  .sec-head h2{font-family:var(--disp);font-weight:400;text-transform:uppercase;font-size:clamp(34px,5vw,64px);line-height:1}
  .sec-head .meta{font-family:var(--mono);font-size:10.5px;letter-spacing:.1em;text-transform:uppercase;color:var(--meta)}
  .sec-head .meta.big{font-size:clamp(14px,1.3vw,21px);letter-spacing:.07em;color:var(--meta-strong)}

  /* education - three static cards, nothing to expand: unlike a work
     history, an institution's whole story is a couple of lines and a short
     list, already short enough to read at a glance */
  /* Education specifically runs tighter than the section default - three
     short static cards read better dense than airy, unlike the longer
     work-history rows elsewhere on the page. */
  #education .sec-head{margin-bottom:clamp(20px,3vh,32px)}
  .edu{
    display:grid;grid-template-columns:repeat(3,1fr);
    gap:clamp(14px,1.6vw,22px);
  }
  @media (max-width:900px){.edu{grid-template-columns:1fr}}
  .edu-card{
    display:flex;flex-direction:column;gap:clamp(10px,1.1vw,14px);
    border:1px solid var(--line);border-radius:16px;
    padding:clamp(16px,1.7vw,22px);
    transition:translate .32s cubic-bezier(.22,.61,.36,1),scale .32s cubic-bezier(.22,.61,.36,1),
               box-shadow .32s ease-out,border-color .32s ease-out;
  }
  .edu-card:hover{
    translate:0 -6px;scale:1.035;border-color:var(--line);
    box-shadow:0 20px 40px rgba(23,21,18,.14);
    /* the whole tile grows over its grid neighbours rather than under them */
    position:relative;z-index:1;
  }
  .edu-head{display:flex;align-items:center;gap:clamp(12px,1.3vw,16px)}
  .edu-lg{
    width:clamp(100px,9vw,136px);height:clamp(100px,9vw,136px);object-fit:contain;flex:none;
    transition:rotate .4s cubic-bezier(.22,.61,.36,1),scale .4s cubic-bezier(.22,.61,.36,1);
  }
  .edu-txt{display:flex;flex-direction:column;gap:5px;min-width:0}
  /* the name draws its own underline in on hover, timed with the logo's
     motion so the card reads as activating as one thing, not a moving
     picture beside static type */
  .edu-txt h3{
    position:relative;padding-bottom:5px;
    font-size:clamp(18px,1.75vw,24px);font-weight:700;letter-spacing:-.015em;line-height:1.15;
  }
  .edu-txt h3::after{
    content:"";position:absolute;left:0;bottom:0;width:100%;height:2px;
    background:var(--orange);transform-origin:0 50%;scale:0 1;
    transition:scale .4s cubic-bezier(.22,.61,.36,1);
  }
  .edu-card:hover .edu-txt h3::after{scale:1 1}
  @media (prefers-reduced-motion:reduce){
    .edu-txt h3::after{transition:none}
  }
  .edu-when{font-family:var(--mono);font-size:12.5px;color:var(--meta);white-space:nowrap}
  .edu-role{font-size:16px;font-weight:600;color:var(--ink)}
  .edu-focus{display:block;margin-top:3px;font-size:13.5px;font-weight:400;color:var(--meta)}
  .edu-pts{list-style:none;padding:0;display:flex;flex-direction:column;gap:7px}
  .edu-pts li{
    position:relative;padding-left:18px;max-width:56ch;
    font-size:15px;line-height:1.6;color:#54514A;text-wrap:pretty;
  }
  .edu-pts li::before{
    content:"";position:absolute;left:2px;top:.6em;width:7px;height:2px;background:var(--orange);
  }
  /* one shared gesture across all three logos - leaning in, like tilting a
     screen to read it more closely */
  .edu-card:hover .edu-lg{rotate:-8deg;scale:1.08}
  @media (prefers-reduced-motion:reduce){
    .edu-card{transition:border-color .2s ease-out}
    .edu-card:hover{translate:0 0;scale:1}
    .edu-lg{transition:none}
    .edu-card:hover .edu-lg{rotate:0deg;scale:1}
  }

  /* tech stack - bare logos, grouped; security leads at a larger size */
  .tk-groups{display:flex;flex-direction:column;gap:clamp(24px,2.6vw,42px)}
  .tkg-h{
    font-family:var(--mono);font-size:10.5px;letter-spacing:.16em;text-transform:uppercase;
    color:var(--meta);font-weight:500;
    padding-bottom:9px;margin-bottom:clamp(15px,1.7vw,23px);border-bottom:1px solid var(--line);
  }
  .tkg-main .tkg-h{color:var(--ink);border-bottom-color:var(--ink);border-bottom-width:2px}
  .tks{
    --sz:clamp(46px,4vw,64px);
    list-style:none;padding:0;display:grid;
    gap:clamp(18px,2vw,30px) clamp(12px,1.4vw,22px);
    grid-template-columns:repeat(auto-fill,minmax(calc(var(--sz) * 1.35 + 22px),1fr));
  }
  .tkg-main .tks{--sz:clamp(56px,5.2vw,84px)}
  /* games - one flat size for every group, no featured row, just bigger
     across the board than the tech stack's default */
  #games .tks{--sz:clamp(64px,5.8vw,96px)}
  .tk{
    display:flex;flex-direction:column;align-items:center;gap:9px;text-align:center;
    transition:translate .18s ease-out;
  }
  .tk:hover{translate:0 -5px}
  /* one box for every mark; --k lifts the naturally small ones so the
     rendered logos carry the same optical weight */
  .tk img{
    width:var(--sz);height:var(--sz);object-fit:contain;
    scale:var(--k,1);
  }
  .tk-n{font-size:clamp(12px,.92vw,14px);font-weight:700;letter-spacing:-.01em;line-height:1.2}
  .tkg-main .tk-n{font-size:clamp(14px,1.15vw,18px)}
  /* the reverse of .chip-lg further down (the cert ledger's light card) -
     a pale or white-on-transparent mark (a game or show logo cut for a
     dark splash screen, say) needs a dark backdrop to read at all against
     this page's light theme, not a light one. Shared with the ledger
     below, not scoped to .tk, so either can reach for it. */
  .chip-dk{background:#15130f;border-radius:10px;padding:6px;box-sizing:border-box}

  /* certification ledger */
  .ledger{
    display:grid;grid-template-columns:repeat(2,1fr);
    column-gap:clamp(24px,3.4vw,60px);
    border-top:2px solid var(--ink);
  }
  @media (max-width:820px){.ledger{grid-template-columns:1fr}}
  .row{
    display:grid;grid-template-columns:auto 1fr auto auto;gap:10px clamp(16px,1.6vw,24px);
    align-items:center;
    border-bottom:1px solid var(--line);padding:clamp(18px,1.8vw,24px) 0;
    margin:0 0 0 -20px;padding-left:20px;padding-right:20px;border-radius:12px;
    transition:background .18s ease-out;
  }
  /* one hover treatment shared by every ledger - certifications and movies
     alike, plus whatever else reaches for .row later. Rows that actually
     open something (certs, via data-cert) get the pointer cursor; the
     movie rows get the same lift and glow with no implied click. */
  .row:hover{background:var(--panel)}
  html[data-theme="dark"] section[data-view="pro"] .row:hover{background:var(--sec-hi)}
  .row[data-cert]{cursor:pointer}
  .row .lg{transition:scale .18s ease-out}
  /* CTIA's row-level badge pair (AMA + TESDA, see 03-stage.css for the
     hero version and the reasoning) - same box the plain .lg logo used to
     occupy, so it drops into the grid without shifting anything else. */
  .row .logo-pair{width:clamp(76px,7vw,104px);height:clamp(60px,5.4vw,80px)}
  .row:hover .lg{scale:calc(var(--k, 1) * 1.08)}
  /* --k lifts the naturally small marks the same way the tech-stack and
     games grids already do, so a padded wordmark and a full-bleed poster
     read as the same size instead of whichever fills its box more */
  .row .lg{width:clamp(76px,7vw,104px);height:clamp(60px,5.4vw,80px);object-fit:contain;scale:var(--k,1)}
  /* the one row that isn't sharing the two-column grid with anything -
     the favorite gets the full width to itself, and a poster sized to
     match, well ahead of the other two posters below it. Scoped to the
     movies ledger only - the cert ledger has more than one earned row
     and those are meant to stay in the two-column grid. */
  #movies .row.earned{grid-column:1 / -1}
  .row .lg.lg-feat{
    width:clamp(130px,11.5vw,186px);height:clamp(186px,16.5vw,266px);
    border-radius:6px;object-fit:cover;scale:1;
  }
  @media (max-width:640px){
    .row .lg.lg-feat{width:clamp(100px,26vw,140px);height:clamp(144px,37vw,200px)}
  }
  /* ISC2 CC and CCNA carry dark text and outlines on a transparent
     background, invisible against a dark section - they get a light card
     behind them so the text stays readable in dark mode. The padding is
     applied in both modes rather than dark only, so the artwork itself is
     not sized differently (inset by the card's padding) depending on the
     theme - a visible size jump when toggling was worse than the card
     showing in light mode. The background defaults to --light, the same
     token the section's own light-mode background already is, so the card
     matches it exactly instead of an approximate literal white; dark mode
     swaps in an explicit light color, since --light is repointed dark
     there for everything else in the section. */
  .chip-lg{background:var(--light);border-radius:10px;padding:6px;box-sizing:border-box}
  html[data-theme="dark"] .chip-lg{background:#fff}
  .row h3{font-size:clamp(17px,2vw,22px);font-weight:700;letter-spacing:-.01em}
  .row .via{font-size:13px;color:var(--meta)}
  .row .yr{font-family:var(--mono);font-size:11px;color:var(--meta);white-space:nowrap}
  .row .st{
    font-family:var(--mono);font-size:9.5px;letter-spacing:.12em;text-transform:uppercase;
    border:1.5px solid var(--ink);border-radius:99px;padding:5px 13px;white-space:nowrap;
    transition:background .18s ease-out,color .18s ease-out;
  }
  .row.earned .st{background:var(--ink);color:var(--light)}
  @media (max-width:640px){
    .row{grid-template-columns:auto 1fr}
    .row .yr{display:none}
    .row .st{grid-column:2;justify-self:start}
  }

  /* projects - one-row auto-scrolling carousel */
  .proj-rail{
    --g:clamp(20px,2.4vw,32px);
    /* seconds for one full set to pass - the JS reads this to set drift speed */
    --speed:68s;
    position:relative;overflow-x:auto;overflow-y:hidden;cursor:grab;user-select:none;
    scrollbar-width:none;-ms-overflow-style:none;
    overscroll-behavior-x:contain;
    /* bleed past the section padding so the row runs edge to edge */
    margin-inline:calc(-1 * var(--gut));
    padding-inline:var(--gut);
    -webkit-mask-image:linear-gradient(90deg,transparent,#000 4%,#000 96%,transparent);
            mask-image:linear-gradient(90deg,transparent,#000 4%,#000 96%,transparent);
  }
  .proj-rail::-webkit-scrollbar{display:none}
  .proj-rail.dragging{cursor:grabbing}
  .proj-rail.dragging .proj{pointer-events:none}
  .proj-grid{display:flex;gap:var(--g);width:max-content;padding:6px 0 18px}
  /* the rail covers more ground on a big screen, so shorten the cycle to keep
     the perceived pace even */
  @media (min-width:1400px){.proj-rail{--speed:52s}}
  @media (min-width:1900px){.proj-rail{--speed:46s}}
  @media (prefers-reduced-motion:reduce){
    .proj-rail{scroll-snap-type:x mandatory}
    .proj{scroll-snap-align:center}
  }
  .proj{
    flex:none;width:clamp(360px,44vw,660px);
    border:2px solid var(--ink);border-radius:18px;padding:clamp(20px,1.6vw,26px);background:var(--light);
    display:flex;flex-direction:column;gap:18px;
    transition:background .18s ease-out,box-shadow .18s ease-out,translate .18s ease-out;
  }
  .proj-body{display:flex;flex-direction:column;gap:12px;min-width:0;flex:1}
  /* image tile - placeholder until real screenshots land; swap the div for an <img> */
  .proj-shot{
    position:relative;border:2px solid var(--ink);border-radius:12px;overflow:hidden;
    aspect-ratio:16/9;background:var(--shot,rgba(23,21,18,.06));
    display:grid;place-items:center;
  }
  .proj-shot::before{
    content:"";position:absolute;inset:0;
    background:repeating-linear-gradient(45deg,rgba(23,21,18,.09) 0 2px,transparent 2px 9px);
  }
  .proj-shot span{
    position:relative;font-family:var(--mono);font-size:11px;letter-spacing:.16em;
    text-transform:uppercase;color:rgba(23,21,18,.72);
  }
  .proj-shot img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover}
  /* taller-than-16:9 shots whose headline sits at the top */
  .proj-shot img.top{object-position:top}
  .proj.c1 .proj-shot{--shot:color-mix(in srgb,var(--cyan) 30%,var(--light))}
  .proj.c2 .proj-shot{--shot:color-mix(in srgb,var(--yellow) 30%,var(--light))}
  .proj.c3 .proj-shot{--shot:color-mix(in srgb,var(--pink) 24%,var(--light))}
  .proj.c4 .proj-shot{--shot:color-mix(in srgb,var(--green) 30%,var(--light))}
  .proj:hover{background:#fff;box-shadow:0 14px 30px rgba(23,21,18,.14);translate:0 -4px}
  .proj h3{font-size:clamp(22px,2vw,32px);font-weight:700;letter-spacing:-.015em;line-height:1.15}
  .proj p{font-size:clamp(15px,1.05vw,17px);line-height:1.55;color:#54514A}
  /* the security claim is the payoff of each card - give it real weight */
  .proj .fact{
    margin-top:auto;padding-top:15px;border-top:1.5px solid var(--ink);
    display:flex;align-items:baseline;gap:9px;
    font-family:var(--mono);font-size:clamp(15px,1.35vw,20px);font-weight:700;
    letter-spacing:-.01em;line-height:1.35;color:var(--ink);
  }
  .proj .fact::before{content:"»";color:var(--orange);font-size:1.2em;line-height:1}

  /* project detail overlay - image large on the left, copy on the right */
  .proj{cursor:pointer}
  .proj h3{transition:color .18s ease-out}
  .proj:hover h3{color:var(--orange)}
  .pm{
    position:fixed;inset:0;z-index:95;display:none;place-items:center;
    background:rgba(12,11,9,.9);padding:clamp(14px,3vw,40px);
  }
  .pm.on{display:grid}
  .pm-panel{
    /* fills the screen - the point of the blow-up is the screenshot */
    width:min(64vw,1260px);height:min(64vh,735px);overflow:hidden;
    background:var(--light);border:2px solid var(--ink);border-radius:20px;
    display:grid;grid-template-columns:1.45fr 1fr;
    animation:pm-in .3s cubic-bezier(.16,1,.3,1);
  }
  @keyframes pm-in{from{opacity:0;scale:.96}}
  .pm-shot{background:var(--card);display:grid;place-items:center;overflow:hidden}
  .pm-shot img{width:100%;height:100%;object-fit:contain;display:block}
  .pm-body{
    position:relative;padding:clamp(24px,2.6vw,44px);
    display:flex;flex-direction:column;gap:16px;justify-content:center;overflow-y:auto;
  }
  .pm-body h3{font-size:clamp(24px,2.4vw,40px);font-weight:700;letter-spacing:-.02em;line-height:1.08}
  .pm-body p{font-size:clamp(15px,1.05vw,18px);line-height:1.6;color:#54514A}
  /* certification modal (#cm) reuses this whole .pm shell - same shot/body
     split a project card gets - just with a status pill instead of the
     repo/demo links, and a placeholder graphic in the shot for whichever
     certs don't have an uploaded photo yet */
  .cm-status{
    display:inline-block;width:fit-content;color:var(--ink);
    font-family:var(--mono);font-size:9.5px;letter-spacing:.12em;text-transform:uppercase;
    border:1.5px solid var(--ink);border-radius:99px;padding:5px 13px;
  }
  .cm-status.earned{background:var(--ink);color:var(--light)}
  .cm-empty{
    display:flex;flex-direction:column;align-items:center;gap:10px;
    color:var(--meta);text-align:center;padding:0 24px;
  }
  .cm-empty-ico{width:44px;height:44px;flex:none;opacity:.6}
  .cm-empty span{font-family:var(--mono);font-size:12px;letter-spacing:.08em;text-transform:uppercase}
  .pm-body .fact{
    margin-top:6px;padding-top:16px;border-top:1.5px solid var(--ink);
    display:flex;align-items:baseline;gap:10px;
    font-family:var(--mono);font-size:clamp(16.5px,1.5vw,24px);font-weight:700;
    letter-spacing:-.01em;line-height:1.35;color:var(--ink);
  }
  .pm-body .fact::before{content:"»";color:var(--orange);font-size:1.15em;line-height:1}
  /* repo / live-demo actions - a project without one yet (most of them,
     until the real URLs are in) shows the button greyed out and inert
     rather than missing, so the row still reads as "code + demo" */
  .pm-links{display:flex;flex-wrap:wrap;gap:12px;margin-top:4px}
  .pm-link{
    display:inline-flex;align-items:center;gap:9px;
    font-family:var(--mono);font-size:13px;font-weight:700;letter-spacing:.06em;text-transform:uppercase;
    color:var(--ink);text-decoration:none;
    border:1.5px solid var(--ink);border-radius:99px;padding:11px 20px;
    transition:background .18s ease-out,color .18s ease-out,translate .18s ease-out,opacity .18s ease-out;
  }
  .pm-link svg{width:17px;height:17px;flex:none}
  .pm-link:hover{background:var(--ink);color:var(--light);translate:0 -2px}
  html[data-theme="dark"] .pm-link{color:var(--cream);border-color:var(--pill-line)}
  html[data-theme="dark"] .pm-link:hover{background:var(--orange);border-color:var(--orange);color:#fff}
  .pm-link-disabled{
    opacity:.4;cursor:default;border-style:dashed;pointer-events:none;
  }
  .pm-close{
    position:absolute;top:14px;right:14px;width:42px;height:42px;border-radius:50%;
    border:1.5px solid var(--line);background:#fff;color:var(--ink);
    font-size:20px;line-height:1;cursor:pointer;transition:background .18s ease-out;
  }
  .pm-close:hover{background:var(--ink);color:var(--light)}
  /* --light stays a fixed light tone across the theme toggle by design (the
     hero and its CV buttons rely on that) - which is exactly why this panel,
     built on --light, never went dark with the rest of the page. It gets
     its own light/dark pair here instead of borrowing that token. */
  html[data-theme="dark"] .pm-panel{background:var(--sec-hi);border-color:var(--pill-line)}
  html[data-theme="dark"] .pm-shot{background:var(--sec-bg)}
  html[data-theme="dark"] .pm-body h3{color:var(--cream)}
  html[data-theme="dark"] .pm-body p{color:var(--pill-fg)}
  html[data-theme="dark"] .pm-body .fact{color:var(--cream);border-top-color:var(--pill-line)}
  html[data-theme="dark"] .cm-status{border-color:var(--pill-line);color:var(--cream)}
  html[data-theme="dark"] .cm-status.earned{background:var(--cream);color:var(--sec-hi)}
  html[data-theme="dark"] .cm-empty{color:var(--pill-fg)}
  html[data-theme="dark"] .pm-close{background:var(--sec-hi);border-color:var(--pill-line);color:var(--cream)}
  html[data-theme="dark"] .pm-close:hover{background:var(--orange);border-color:var(--orange);color:#fff}
  @media (max-width:900px){
    .pm-panel{width:94vw;height:auto;max-height:92vh;grid-template-columns:1fr;grid-template-rows:auto 1fr}
    .pm-shot{aspect-ratio:16/9}
  }

  /* experience - company | role, expanding to the description on click */
  .xp{display:flex;flex-direction:column;gap:10px}
  /* each row is a card, so it reads as a control rather than a table line */
  .xp-row{
    position:relative;overflow:hidden;width:100%;text-align:left;cursor:pointer;
    font:inherit;color:inherit;
    /* buttons suppress selection in the UA sheet - put it back so the copy
       in here can be highlighted and pasted */
    -webkit-user-select:text;user-select:text;
    background:rgba(255,255,255,.5);border:1px solid var(--line);border-radius:16px;
    /* minmax(0,…) rather than a bare fr: a plain fr track's implicit min
       width is its content's min-content, so a long company name (or the
       long "See all" description) was quietly widening that row's column
       past its share of the grid - which is exactly why the divider .xp-right
       draws on its own left edge landed at a different x per row instead of
       lining up down the list. */
    display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1.25fr);
    gap:clamp(18px,2.6vw,52px);
    --pad:clamp(15px,1.5vw,20px);
    padding:var(--pad) clamp(78px,7vw,110px) var(--pad) clamp(16px,1.7vw,24px);
    align-items:center;
    --ex:.52s cubic-bezier(.32,.72,0,1);              /* one curve for everything that grows */
    transition:background .22s ease-out,border-color .22s ease-out,
               translate .22s ease-out,box-shadow .22s ease-out,
               padding var(--ex),grid-template-columns var(--ex);
  }
  /* gutter wipes in on hover and stays on once opened, in the orange accent */
  .xp-row::before{
    content:"";position:absolute;left:0;top:0;bottom:0;width:4px;background:var(--orange);
    scale:1 0;transform-origin:50% 50%;transition:scale .24s ease-out;
  }
  .xp-row:hover,.xp-row:focus-visible{
    background:#fff;border-color:#BFB9AC;translate:6px 0;
    box-shadow:0 10px 24px rgba(23,21,18,.08);
  }
  .xp-row:hover .xp-co h3,.xp-row:focus-visible .xp-co h3,
  .xp-row:hover .xp-role h4,.xp-row:focus-visible .xp-role h4{color:var(--orange)}
  .xp-row:hover::before,.xp-row:focus-visible::before,
  .xp-row[aria-expanded="true"]::before{scale:1 1}
  .xp-row:focus-visible{outline:2px solid var(--orange);outline-offset:2px}
  /* the ask, spelled out and sitting in the reading path */
  /* the ask, spelled out - parked on the far right of the row */
  .xp-cue{
    position:absolute;right:clamp(16px,1.8vw,28px);top:50%;translate:0 -50%;
    display:flex;flex-direction:column;align-items:center;gap:9px;
    font-family:var(--mono);font-size:clamp(10px,.85vw,12px);
    letter-spacing:.14em;text-transform:uppercase;
    color:var(--meta);transition:color .2s ease-out;
  }
  .xp-cue::after{content:"What I did"}
  .xp-row[aria-expanded="true"] .xp-cue::after{content:"Hide"}
  .xp-row:hover .xp-cue,.xp-row:focus-visible .xp-cue{color:var(--orange)}
  .xp-cue .pm{
    position:relative;width:clamp(34px,3vw,44px);height:clamp(34px,3vw,44px);flex:none;
    border:2px solid currentColor;border-radius:50%;
    transition:background .2s ease-out,border-color .2s ease-out;
  }
  .xp-cue .pm::before,.xp-cue .pm::after{
    content:"";position:absolute;left:50%;top:50%;background:currentColor;
    translate:-50% -50%;transition:rotate .34s cubic-bezier(.32,.72,0,1),opacity .34s ease-out;
  }
  .xp-cue .pm::before{width:52%;height:2.5px}
  .xp-cue .pm::after{width:2.5px;height:52%}
  .xp-row[aria-expanded="true"] .xp-cue .pm::after{rotate:90deg;opacity:0}
  /* logo sits beside the company name, centred on it. It's an absolutely
     positioned layer over .xp-co rather than a flex item, so opening the
     row can slide it clear across the name and date - covering them like
     a wipe - on its way to landing enlarged and centred in the compartment,
     instead of just resizing in place. */
  .xp-co{position:relative;display:flex;align-items:center;gap:clamp(14px,1.3vw,20px);min-height:clamp(42px,3.3vw,58px)}
  .xp-co .olg{
    position:absolute;left:0;top:50%;translate:0 -50%;z-index:2;
    width:clamp(42px,3.3vw,58px);height:clamp(42px,3.3vw,58px);flex:none;
    object-fit:cover;border-radius:12px;box-shadow:0 6px 18px rgba(23,21,18,0);
    transition:width var(--ex),height var(--ex),border-radius var(--ex),
               left var(--ex),translate var(--ex),scale .22s ease-out,box-shadow var(--ex);
  }
  .xp-row:hover .xp-co .olg,.xp-row:focus-visible .xp-co .olg{scale:1.28}
  /* landing spot: dead centre of the compartment, bigger, with a little lift
     now that it's sitting on top of (rather than beside) the name */
  .xp-row[aria-expanded="true"] .xp-co .olg{
    left:50%;translate:-50% -50%;scale:1;
    width:clamp(88px,7.6vw,130px);height:clamp(88px,7.6vw,130px);border-radius:18px;
    box-shadow:0 14px 32px rgba(23,21,18,.26);
  }
  .co-txt{
    display:flex;flex-direction:column;gap:7px;min-width:0;
    /* reserves the room the absolutely positioned logo would otherwise take
       in flow, so collapsed rows still read as logo-then-name */
    padding-left:calc(clamp(42px,3.3vw,58px) + clamp(14px,1.3vw,20px));
    transition:opacity .3s ease-out;
  }
  /* the name and date fade out as the logo sweeps over them - "erased" by
     the same thing that's about to sit in their place */
  .xp-row[aria-expanded="true"] .co-txt{opacity:0;transition-duration:.2s}
  /* the name holds one line in both states - it resizes to fit the column
     instead of reflowing onto a second row mid-transition */
  .xp-co h3{
    font-family:var(--disp);font-weight:400;text-transform:uppercase;
    font-size:clamp(17px,1.7vw,26px);letter-spacing:.03em;line-height:.98;
    color:var(--ink);transition:font-size var(--ex),scale .22s ease-out;
    transform-origin:left center;
    white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
  }
  .xp-row:hover .xp-co h3,.xp-row:focus-visible .xp-co h3{scale:1.16}
  .xp-co .span{
    font-family:var(--mono);font-size:clamp(12px,.85vw,14px);letter-spacing:0;color:var(--meta);
    white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
    transition:font-size var(--ex);
  }
  /* divider runs the full height of the row so the rule reads as one
     continuous line down the list, matching the horizontal separators */
  .xp-right{position:relative;padding-left:clamp(16px,1.8vw,28px)}
  .xp-right::before{
    content:"";position:absolute;left:0;width:1px;background:var(--line);
    top:calc(-1 * var(--pad));bottom:calc(-1 * var(--pad));
  }
  .xp-role{list-style:none;display:flex;flex-direction:column;gap:14px;padding:0}
  /* current role leads; earlier ones step down in size and weight */
  .xp-role h4{
    font-family:var(--body);font-size:clamp(15px,1.5vw,22px);font-weight:700;
    letter-spacing:-.025em;line-height:1.05;color:var(--ink);
    transition:font-size var(--ex);
  }
  .xp-role li:not(:first-child) h4{
    font-size:clamp(12.5px,1vw,15px);font-weight:600;color:#57534B;letter-spacing:-.015em;
    transition:font-size .3s ease-out;
  }
  .xp-role .r-when{font-family:var(--mono);font-size:clamp(12px,.85vw,14px);letter-spacing:0;color:var(--meta);margin-top:5px}
  .xp-role li:not(:first-child) .r-when{font-size:clamp(11.5px,.8vw,13px);margin-top:4px}
  /* the row opens into its description: roles step back, the copy unfolds */
  .xp-desc{
    display:grid;grid-template-rows:0fr;opacity:0;
    transition:grid-template-rows var(--ex),opacity .3s ease-out .06s;
  }
  .xp-desc-in{overflow:hidden}
  .xp-pts{list-style:none;padding:22px 0 6px;display:flex;flex-direction:column;gap:13px}
  /* one bullet per role, set as a paragraph - it wraps and holds a readable measure */
  .xp-pts li{
    position:relative;padding-left:26px;max-width:74ch;
    font-size:clamp(13px,1.02vw,16px);line-height:1.72;color:#403D36;
    text-wrap:pretty;
  }
  .xp-pts li::before{
    content:"";position:absolute;left:2px;top:.62em;width:9px;height:2px;background:var(--orange);
  }
  /* An earlier role gets its own note, stepped back the same way its title is
     in the list above: smaller, quieter, a shorter measure, and a grey rule
     instead of the orange one - so the lead description stays the thing you
     read first and these read as history under it. */
  .xp-pts li.past{
    font-size:clamp(12px,.92vw,14.5px);line-height:1.66;
    color:#5A564E;max-width:70ch;
  }
  .xp-pts li.past::before{background:#C4BEB1;width:7px}
  .pt-role{
    display:block;margin-bottom:5px;
    font-family:var(--mono);font-size:clamp(10px,.75vw,11.5px);
    letter-spacing:.12em;text-transform:uppercase;color:var(--meta);
  }
  /* no bullets to show - the row still opens, it just grows */
  .xp-row.no-detail .xp-cue{display:none}
  .xp-row[aria-expanded="true"]{background:#fff;border-color:#BFB9AC;grid-template-columns:minmax(0,.95fr) minmax(0,1.95fr)}
  .xp-row[aria-expanded="true"] .xp-desc{grid-template-rows:1fr;opacity:1}
  .xp-row[aria-expanded="true"]{--pad:clamp(24px,2.8vw,38px)}
  .xp-row[aria-expanded="true"] .xp-role h4{font-size:clamp(21px,2.3vw,34px)}
  .xp-row[aria-expanded="true"] .xp-role li:not(:first-child) h4{font-size:clamp(15px,1.3vw,19px)}
  @media (max-width:900px){
    .xp-row{grid-template-columns:1fr;gap:16px;align-items:start}
    .xp-row[aria-expanded="true"]{grid-template-columns:1fr}
    .xp-right{border-top:1px solid var(--line);padding:14px 0 0}
    .xp-right::before{display:none}
    /* the name may wrap when collapsed - it is the long one, and down here
       there is no expand-time column resize left to protect a single line
       against (the name fades away instead of shrinking to fit) */
    .xp-co h3{
      white-space:normal;overflow:visible;text-overflow:clip;
      line-height:1.08;text-wrap:balance;
    }
    .xp-co .span{font-size:clamp(11.5px,.85vw,14px)}
  }

  /* the section heading opens every project at once */
  .sec-head h2 .pa-open{
    background:none;border:0;padding:0;margin:0;font:inherit;color:inherit;
    letter-spacing:inherit;text-transform:inherit;cursor:pointer;
    transition:color .18s ease-out;
  }
  .sec-head h2 .pa-open:hover{color:var(--orange)}
  .sec-head h2 .pa-open:focus-visible{outline:2px solid var(--orange);outline-offset:5px}
  /* the cue that says the heading does something */
  .h-cue{
    display:inline-flex;align-items:center;vertical-align:middle;
    /* the display font's cap-height sits well above its middle, so a plain
       vertical-align:middle rides the pill up almost into the letters
       above it - nudged back down clear of them */
    translate:0 8px;
    margin-left:clamp(12px,1.5vw,26px);
    font-family:var(--mono);font-size:clamp(12.5px,1.05vw,17px);font-weight:500;
    letter-spacing:.13em;text-transform:uppercase;
    border:2px solid var(--orange);color:var(--orange);
    border-radius:99px;padding:9px 19px;white-space:nowrap;
    transition:background .18s ease-out,color .18s ease-out;
  }
  .pa-open:hover .h-cue,.pa-open:focus-visible .h-cue{background:var(--orange);color:#fff}
  /* the events gallery is long; once someone is deep inside it, offer the way
     out to the next section rather than making them scroll the whole set */
  .skipto{
    position:fixed;z-index:62;left:50%;bottom:clamp(46px,8vh,104px);
    display:flex;align-items:center;gap:11px;cursor:pointer;white-space:nowrap;
    font-family:var(--mono);font-size:clamp(10.5px,.82vw,12.5px);font-weight:500;
    letter-spacing:.13em;text-transform:uppercase;
    background:var(--card);color:var(--cream);
    border:2px solid var(--pill-line);border-radius:99px;
    padding:12px 20px;box-shadow:0 12px 30px rgba(0,0,0,.28);
    opacity:0;pointer-events:none;translate:-50% 14px;
    transition:opacity .3s ease-out,translate .3s cubic-bezier(.32,.72,0,1),
               background .18s ease-out,border-color .18s ease-out,color .18s ease-out;
  }
  .skipto.on{opacity:1;pointer-events:auto;translate:-50% 0}
  .skipto:hover,.skipto:focus-visible{background:var(--orange);border-color:var(--orange);color:#fff}
  .skipto svg{width:13px;height:15px;flex:none;overflow:visible}
  .skipto svg path{stroke:currentColor;stroke-width:2.2;fill:none;stroke-linecap:round;stroke-linejoin:round}
  .skipto.on svg{animation:skip-bob 1.7s ease-in-out infinite}
  /* pointing back the way you came */
  .skipto.up svg{rotate:180deg}
  .skipto.up.on svg{animation-name:skip-bob-up}
  @keyframes skip-bob-up{0%,100%{translate:0 2px}50%{translate:0 -3px}}
  @keyframes skip-bob{0%,100%{translate:0 -2px}50%{translate:0 3px}}
  @media (max-width:860px){.skipto{bottom:clamp(34px,6vh,64px)}}
  @media (prefers-reduced-motion:reduce){.skipto.on svg{animation:none}}
  body.pa-open{overflow:hidden}
  /* experience entries stack full width and arrive already open - clicking
     one does nothing (no toggle handler on the clone, it's already at its
     one state), but they keep the live list's hover feedback: cursor:default
     rather than :none says "not clickable" without switching hover off too */
  .xa-list{display:flex;flex-direction:column;gap:clamp(10px,1.2vw,16px)}
  .xa-list .xp-row{cursor:default}
  .xa-list .xp-cue{display:none}
  .pa{
    position:fixed;inset:0;z-index:96;display:none;
    background:var(--card);overflow-y:auto;overscroll-behavior:contain;
  }
  /* this overlay lives outside any <section data-view="pro">, so it never
     picked up that section's dark-mode --ink/--line/--meta recolor - role
     titles inside the cloned .xp-row cards were rendering in the light
     theme's dark ink, invisible against this modal's own dark background */
  html[data-theme="dark"] .pa{
    --ink:var(--cream); --light:var(--sec-bg);
    --line:var(--pill-line); --meta:var(--pill-fg); --meta-strong:var(--cream-dim);
  }
  .pa.on{display:block;animation:pa-in .28s cubic-bezier(.16,1,.3,1)}
  @keyframes pa-in{from{opacity:0}}
  .pa-in{
    max-width:1800px;margin:0 auto;
    padding:clamp(22px,4vw,64px) clamp(20px,4vw,64px) clamp(44px,6vw,96px);
  }
  .pa-head{
    display:flex;align-items:baseline;justify-content:space-between;gap:20px;flex-wrap:wrap;
    margin-bottom:clamp(22px,3vw,42px);
  }
  .pa-h{
    font-family:var(--disp);font-weight:400;text-transform:uppercase;
    font-size:clamp(30px,4.6vw,62px);line-height:1;color:var(--cream);
  }
  .pa-meta{
    font-family:var(--mono);font-size:10.5px;letter-spacing:.14em;text-transform:uppercase;
    color:var(--cream-dim);
  }
  /* the cards are fixed-width flex items in the rail; here they fill a cell */
  .pa-grid{
    display:grid;gap:clamp(18px,2vw,32px);
    grid-template-columns:repeat(auto-fill,minmax(min(100%,370px),1fr));
  }
  .pa-grid .proj{width:auto;flex:none;cursor:pointer}
  .pa-close{
    position:fixed;top:clamp(12px,2vw,26px);right:clamp(12px,2vw,26px);z-index:2;
    width:clamp(40px,3.4vw,52px);height:clamp(40px,3.4vw,52px);
    display:grid;place-items:center;cursor:pointer;
    background:var(--card);color:var(--cream);
    border:2px solid var(--pill-line);border-radius:50%;
    font-size:24px;line-height:1;font-family:var(--body);
    transition:background .18s ease-out,color .18s ease-out,border-color .18s ease-out;
  }
  .pa-close:hover{background:var(--orange);border-color:var(--orange);color:#fff}
  @media (max-width:700px){.pa-grid{grid-template-columns:1fr}}

  /* Security dark mode: the handful of colors above that were written as
     literal hex rather than a token, so they never picked up the section
     recolor further up this file - card copy, past-role bullets, and the
     hover/expanded surfaces that were hardcoded white. */
  html[data-theme="dark"] .edu-pts li,
  html[data-theme="dark"] .proj p{color:var(--pill-fg)}
  html[data-theme="dark"] .xp-role li:not(:first-child) h4{color:var(--cream-dim)}
  html[data-theme="dark"] .xp-pts li{color:var(--cream-dim)}
  html[data-theme="dark"] .xp-pts li.past{color:var(--pill-fg)}
  html[data-theme="dark"] .xp-pts li.past::before{background:var(--pill-fg)}
  /* the base (unhovered, unexpanded) card was also hardcoded - a translucent
     WHITE wash, rgba(255,255,255,.5), which is why the section going dark
     around it still read as pale: every row sat on its own little patch of
     white regardless. A translucent light overlay this faint reads as
     "barely lighter than the dark surface" instead. */
  html[data-theme="dark"] .xp-row{background:rgba(255,255,255,.05)}
  html[data-theme="dark"] .xp-row:hover,
  html[data-theme="dark"] .xp-row:focus-visible,
  html[data-theme="dark"] .xp-row[aria-expanded="true"]{
    background:var(--sec-hi);border-color:var(--pill-line);
  }
  html[data-theme="dark"] .proj:hover{background:var(--sec-hi)}
  /* GitHub and OpenAI ship their marks as solid black on a transparent
     background - correct on the light stack grid, invisible once that
     grid goes dark. Flipping just these two rather than the whole grid,
     since every other logo here already carries its own color. */
  html[data-theme="dark"] .mono-lg{filter:invert(1) brightness(1.35)}
