body { margin: 0; font: 17px/1.6 var(--sans); }
a { color: var(--acc); text-underline-offset: .2em; }
main, footer { max-width: 60rem; margin: 0 auto; padding: 0 1.5rem; }
section { margin: 5rem 0; }
h2 { margin: 0 0 1.5rem; font-size: 1.6rem; letter-spacing: -.01em; }
h3 { margin: 0 0 .4rem; font-size: 1.05rem; }
p { margin: 0; }

.icon { width: 1.2em; height: 1.2em; flex: none; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
.icon.fill { fill: currentColor; stroke: none; }

/* Hero */
.hero { margin-top: 3rem; text-align: center; }
.avatar { position: relative; display: block; width: 10rem; height: 10rem; margin: 0 auto 1.5rem; border-radius: 50%; border: 4px solid var(--card); box-shadow: 0 12px 40px var(--shadow); }
.avatar img { display: block; width: 100%; height: 100%; border-radius: 50%; }
h1 { margin: 0 0 .75rem; font-size: clamp(1.9rem, 5vw, 2.5rem); line-height: 1.1; letter-spacing: -.02em; }
.lead { max-width: 32rem; margin: 0 auto; font-size: 1.25rem; }
.where { margin-top: .5rem; color: var(--dim); font-size: .95rem; }
.buttons { display: flex; flex-wrap: wrap; justify-content: center; gap: .75rem; margin: 2rem 0 0; padding: 0; list-style: none; }
.button {
  display: inline-flex; align-items: center; gap: .5rem; padding: .6rem 1.2rem;
  color: var(--fg); text-decoration: none; font-weight: 600;
  position: relative; background: var(--card); border: 1px solid var(--card-edge); border-radius: 999px;
}
.button.primary { color: var(--on-acc); background: var(--acc); border-color: transparent; }

/* Cards and tiles share one look: translucent over the glows. No
   backdrop-filter: the glows are blurred already. */
.card, .tile {
  position: relative;
  padding: 1.4rem 1.5rem; background: var(--card); border: 1px solid var(--card-edge); border-radius: 16px;
  box-shadow: 0 8px 30px var(--shadow);
}

.card p, .tile p { color: var(--dim); }
.cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr)); gap: 1rem; }

/* Timeline: a line with four stops, left to right; top to bottom on a phone. */
/* --dot is the dot's full size; every line and ring is placed from it. */
.timeline { --dot: 1.35rem; position: relative; display: grid; grid-template-columns: repeat(4, 1fr); gap: 1.25rem; margin: 0; padding: 0; list-style: none; }
/* On desktop each entry is centred under its dot; on a phone the timeline
   runs down the left edge instead (see the media query below). */
.timeline li { position: relative; padding-top: 2rem; text-align: center; }
.timeline li::before {
  content: ""; position: absolute; top: 0; left: calc(50% - var(--dot) / 2); width: var(--dot); height: var(--dot); border-radius: 50%;
  background: var(--desk); border: 2px solid var(--rule);
}
.timeline li.now::before { background: var(--acc); border-color: var(--acc); box-shadow: 0 0 0 5px color-mix(in srgb, var(--acc) 25%, transparent); }
/* The line: each entry but the last owns the segment from its dot to the
   next one, so the line starts and ends at a dot. The green fill inside a
   segment is scaled to 0 until hovered. */
.timeline li::before, .timeline li::after { z-index: 1; }
.timeline .seg {
  position: absolute; top: calc(var(--dot) / 2 - 1px); left: 50%; width: calc(100% + 1.25rem); height: 2px;
  background: var(--rule);
}
.timeline .seg::before { content: ""; position: absolute; inset: 0; background: var(--acc); transform: scaleX(0); transform-origin: left; }
/* A green ring over a dot, hidden until hovered or drawn on scroll. */
.timeline li::after {
  content: ""; position: absolute; top: 0; left: calc(50% - var(--dot) / 2); width: var(--dot); height: var(--dot);
  border-radius: 50%; pointer-events: none;
  border: 2px solid var(--acc); box-shadow: 0 0 0 5px color-mix(in srgb, var(--acc) 25%, transparent);
  opacity: 0;
}
.timeline .when { display: block; color: var(--dim); font-size: .9rem; font-variant-numeric: tabular-nums; }
.timeline .stop { display: block; font-weight: 700; font-size: 1.1rem; }
.timeline .role { display: block; }
.timeline .note { display: block; margin-top: .25rem; color: var(--dim); }
@media (max-width: 40rem) {
  .timeline { grid-template-columns: 1fr; gap: 1.5rem; }
  .timeline li { padding: 0 0 0 2.25rem; text-align: left; }
  .timeline li::before, .timeline li::after { left: 0; }
  .timeline .seg { top: calc(var(--dot) / 2); left: calc(var(--dot) / 2 - 1px); width: 2px; height: calc(100% + 1.5rem); }
  .timeline .seg::before { transform: scaleY(0); transform-origin: top; }
}

.tiles { display: grid; grid-template-columns: repeat(auto-fill, minmax(16rem, 1fr)); gap: 1rem; margin: 0; padding: 0; list-style: none; }
/* Every card and tile starts with its icon in a round badge: tinted at rest,
   filled with the accent on hover. The icon itself stays a line drawing. */
.badge {
  position: relative; display: grid; place-items: center; width: 2.75rem; height: 2.75rem; margin-bottom: .9rem;
  border-radius: 50%; color: var(--acc); background: color-mix(in srgb, var(--acc) 14%, transparent);
}
.badge .icon { position: relative; width: 1.4rem; height: 1.4rem; stroke-width: 1.8; }

/* The end: a closing card with one offer, the mail address as a button.
   GitHub and LinkedIn stay at the top. */
footer { margin-bottom: 2.5rem; text-align: center; }
.closing {
  padding: 2.5rem 1.5rem; background: var(--card); border: 1px solid var(--card-edge); border-radius: 20px;
  box-shadow: 0 8px 30px var(--shadow);
}
.closing h2 { margin-bottom: .5rem; }
.closing .lead { color: var(--dim); font-size: 1.1rem; }
.closing .cta { margin-top: 1.75rem; }
footer .small { margin-top: 1.5rem; color: var(--dim); font-size: .85rem; }

/* Buttons are what you click, so they fill instead of lifting: a green tint
   behind the label. Mail is solid green already and shifts a shade, so it
   stays the main button. The label keeps its colour: a colour switch reads
   as a flicker. The fill shows on hover (below), on keyboard focus, and
   while a finger presses it on a touchscreen. */
.button { isolation: isolate; -webkit-tap-highlight-color: transparent; }
.button::before {
  content: ""; position: absolute; inset: -1px; z-index: -1; border-radius: inherit;
  background: color-mix(in srgb, var(--acc) 22%, transparent); opacity: 0;
}
.button.primary::before { background: color-mix(in srgb, var(--acc) 78%, var(--fg)); }
.button:focus-visible::before, .button:active::before { opacity: 1; }

/* Keyboard focus: one green ring for everything you can tab to. */
a:focus-visible { outline: 2px solid var(--acc); outline-offset: 3px; border-radius: 4px; }
.button:focus-visible { border-radius: 999px; }

/* Touchscreens have no hover, so there the page moves as you scroll
   instead. Cards and tiles fade up once as they come into view. The
   timeline draws itself entry by entry: when an entry reaches the upper part
   of the screen, the segment leading to it fills, then its dot lights up;
   scrolling back up undoes it. Cards stay once they have appeared.
   theme.js adds .reveal to <html> before the first paint, and reveal.js
   adds .in to each card, tile and timeline entry as it appears. */
@media (hover: none) and (prefers-reduced-motion: no-preference) {
  .reveal .card, .reveal .tile { opacity: 0; transform: translateY(28px); }
  .reveal .card.in, .reveal .tile.in { opacity: 1; transform: none; transition: opacity .6s ease, transform .6s ease; }
  /* Forward: the segment fills, then the dot lights. Backward (scrolling up
     past an entry): the dot goes out, then the segment pulls back. */
  .reveal .timeline .seg::before { transition: transform .4s ease .15s; }
  .reveal .timeline li:has(+ li.in) .seg::before { transform: none; transition-delay: 0s; }
  .reveal .timeline li::after { transition: opacity .3s ease; }
  .reveal .timeline li.in::after { opacity: 1; transition-delay: .35s; }
  .reveal .timeline li.in:first-child::after { transition-delay: 0s; }
}

/* Hover effects. Only with a real pointer, so a tap on a phone never leaves
   anything stuck, and only for visitors who allow motion.

   One language everywhere: boxes lift 2px and get a green edge, timeline dots
   get a green ring, badges and buttons fill, links get a solid underline,
   and the neighbours of whatever is hovered step back.

   Only transform and opacity are ever animated. Animating colours, borders
   or shadows repaints the element every frame, and over the glows behind
   the page those repaints flicker in Chromium. So every coloured hover state
   is its own layer (a pseudo-element) that fades in, and colours that have
   to change switch in one step, halfway through the fade. */
@media (hover: hover) and (prefers-reduced-motion: no-preference) {
  .card, .tile { will-change: transform; transition: transform .25s ease, opacity .25s ease; }
  .card:hover, .tile:hover { transform: translateY(-2px); }

  /* The green edge and the deeper shadow, faded in. */
  .card::before, .tile::before {
    content: ""; position: absolute; inset: -1px; border-radius: inherit; pointer-events: none;
    border: 1px solid color-mix(in srgb, var(--acc) 45%, var(--card-edge));
    box-shadow: 0 12px 36px var(--shadow);
    opacity: 0; transition: opacity .25s ease;
  }
  .card:hover::before, .tile:hover::before { opacity: 1; }

  /* An invisible strip below each box keeps a cursor on the bottom edge
     inside while the box lifts, so the hover can't flip on and off. */
  .card::after, .tile::after { content: ""; position: absolute; inset: 0 0 -4px; z-index: -1; }

  .button::before { transition: opacity .25s ease; }
  .button:hover::before { opacity: 1; }

  /* Badges: a solid disc fades in under the icon; the icon turns dark in
     one step, halfway. */
  .badge::before {
    content: ""; position: absolute; inset: 0; border-radius: 50%; background: var(--acc);
    opacity: 0; transition: opacity .25s ease;
  }
  .badge { transition: color 0s linear .12s; }
  .card:hover .badge, .tile:hover .badge { color: var(--on-acc); }
  .card:hover .badge::before, .tile:hover .badge::before { opacity: 1; }

  /* Timeline dots: the green ring fades in. A ring, not a fill: the filled
     dot stays the current job's. The avatar has no hover; it's just a photo. */
  .timeline li::after { transition: opacity .25s ease; }
  .timeline li:hover::after { opacity: 1; }

  /* Focus: hovering one item lets its neighbours step back. On the timeline
     only the text fades, so the dots and the green line stay bright. */
  .timeline li > span:not(.seg) { transition: opacity .25s ease; }
  .cards:has(.card:hover) .card:not(:hover),
  .tiles:has(.tile:hover) .tile:not(:hover),
  .timeline:has(li:hover) li:not(:hover) > span:not(.seg) { opacity: .7; }

  /* The progress line fills from the start up to the hovered entry, one
     segment after the other, and lights each dot it passes. */
  .timeline .seg::before { transition: transform .2s ease; }
  .timeline:has(li:nth-child(2):hover) li:nth-child(-n+1) .seg::before,
  .timeline:has(li:nth-child(3):hover) li:nth-child(-n+2) .seg::before,
  .timeline:has(li:nth-child(4):hover) li:nth-child(-n+3) .seg::before { transform: none; }
  .timeline:has(li:hover) li:nth-child(2) .seg::before { transition-delay: .15s; }
  .timeline:has(li:hover) li:nth-child(3) .seg::before { transition-delay: .3s; }

  /* The dots the line passes light up as it reaches them. */
  .timeline:has(li:nth-child(2):hover) li:nth-child(-n+1)::after,
  .timeline:has(li:nth-child(3):hover) li:nth-child(-n+2)::after,
  .timeline:has(li:nth-child(4):hover) li:nth-child(-n+3)::after { opacity: 1; }
  .timeline:has(li:hover) li:nth-child(2):not(:hover)::after { transition-delay: .2s; }
  .timeline:has(li:hover) li:nth-child(3):not(:hover)::after { transition-delay: .35s; }

  /* Text links: a faint line at rest, a solid one grows in from the left. */
  a:not(.button) { position: relative; text-decoration: none; }
  a:not(.button)::before, a:not(.button)::after {
    content: ""; position: absolute; left: 0; right: 0; bottom: -2px; height: 1px; background: currentColor;
  }
  a:not(.button)::before { opacity: .35; }
  a:not(.button)::after { transform: scaleX(0); transform-origin: left; transition: transform .25s ease; }
  a:not(.button):hover::after { transform: scaleX(1); }
}
