:root {
  color-scheme: light dark;

  --bg: #ffffff;
  --fg: #16181d;
  --muted: #5c6270;
  --accent: #2f5bd7;
  --rule: #e3e5ea;
  --code-bg: #f4f5f7;
  --net-rgb: 96, 103, 116;
  --panel: rgba(255, 255, 255, 0.52);
  --panel-shadow: 0 1px 2px rgba(16, 24, 40, 0.05), 0 14px 36px rgba(16, 24, 40, 0.12);
  /* Hovering a card flips it to the opposite theme's surface. */
  --invert-surface: #16181d;
  --invert-fg: #f4f5f7;
  --invert-muted: #a7adb9;
  /* Which version of a two-tone logo shows on the page surface. */
  --logo-on-light: block;
  --logo-on-dark: none;

  /* Page headings and the home page menu card titles share this. */
  --title: 1.375rem;
  /* Gap between a page heading and the block that follows it. */
  --section-gap: 2.5rem;

  --measure: 42rem;
  --gutter: 1.5rem;
  --panel-pad: clamp(1.25rem, 5vw, 2.25rem);
  /* Footer icons shrink with the viewport so they stay beside the copyright. */
  --icon: clamp(1rem, 4.2vw, 1.375rem);
  --icon-box: clamp(1.7rem, 7vw, 2.5rem);
}

/* Dark palette. Applied when the system asks for it, unless the visitor has
   chosen light, and whenever the visitor has chosen dark. The two selectors
   share one declaration list so they cannot drift apart. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme='light']) {
    --bg: #101317;
    --fg: #e7e9ee;
    --muted: #9aa1ae;
    --accent: #89a7ff;
    --rule: #262b33;
    --code-bg: #1a1f26;
    --net-rgb: 148, 156, 170;
    --panel: rgba(16, 19, 23, 0.4);
    /* Deeper and darker: a light shadow does not read against a dark page. */
    --panel-shadow: 0 1px 2px rgba(0, 0, 0, 0.3), 0 18px 44px rgba(0, 0, 0, 0.5);
    --invert-surface: #e7e9ee;
    --invert-fg: #14171c;
    --invert-muted: #565d69;
    --logo-on-light: none;
    --logo-on-dark: block;
  }
}

:root[data-theme='dark'] {
    --bg: #101317;
    --fg: #e7e9ee;
    --muted: #9aa1ae;
    --accent: #89a7ff;
    --rule: #262b33;
    --code-bg: #1a1f26;
    --net-rgb: 148, 156, 170;
    --panel: rgba(16, 19, 23, 0.4);
    /* Deeper and darker: a light shadow does not read against a dark page. */
    --panel-shadow: 0 1px 2px rgba(0, 0, 0, 0.3), 0 18px 44px rgba(0, 0, 0, 0.5);
    --invert-surface: #e7e9ee;
    --invert-fg: #14171c;
    --invert-muted: #565d69;
    --logo-on-light: none;
    --logo-on-dark: block;
}

/* Keep form controls and scrollbars in step with an explicit choice. */
:root[data-theme='light'] {
  color-scheme: light;
}

:root[data-theme='dark'] {
  color-scheme: dark;
}

*,
*::before,
*::after {
  box-sizing: border-box;
}

#constellation {
  position: fixed;
  inset: 0;
  z-index: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
}

.sheet {
  position: relative;
  z-index: 1;
  /* Padding is added on top of --measure so the text column keeps its width. */
  max-width: calc(var(--measure) + var(--panel-pad) * 2);
  margin: clamp(1rem, 4vw, 2.5rem) auto;
  padding: 0 var(--panel-pad);
  border: 1px solid var(--rule);
  border-radius: 16px;
  background: var(--panel);
  box-shadow: var(--panel-shadow);
  /* Progressive enhancement: the panel is readable on its alpha alone. The
     blur goes up with the transparency so text keeps its contrast against
     whatever the background happens to be drawing underneath. */
  backdrop-filter: blur(18px) saturate(130%);
  -webkit-backdrop-filter: blur(18px) saturate(130%);
}

body {
  margin: 0;
  padding: 0 var(--gutter);
  background: var(--bg);
  color: var(--fg);
  font: 1rem/1.65 ui-sans-serif, system-ui, -apple-system, "Segoe UI", Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
}

a {
  color: var(--accent);
}

.site-header {
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem 1.5rem;
  /* Centred, not baseline: the brand link is as tall as the 40px avatar. */
  align-items: center;
  padding: 2rem 0 1.25rem;
  border-bottom: 1px solid var(--rule);
}

/* Pushes everything after it to the right, so the nav and the theme toggle
   sit together at the end rather than being spread across the row. */
.site-brand {
  margin-right: auto;
  display: inline-flex;
  align-items: center;
  gap: 0.7rem;
  font-size: 1.125rem;
  font-weight: 650;
  letter-spacing: -0.01em;
  color: inherit;
  text-decoration: none;
}

/* Decorative: the brand link already carries the name as text. */
.avatar {
  flex: none;
  width: 48px;
  height: 48px;
  border-radius: 50%;
  border: 1px solid var(--rule);
  object-fit: cover;
}

.site-nav {
  display: flex;
  gap: 1.25rem;
}

.site-nav a {
  color: var(--muted);
  text-decoration: none;
}

.site-nav a:hover,
.site-nav a[aria-current="page"] {
  color: var(--fg);
}

/* Hidden until the theme script runs, since without it the button cannot do
   anything. Sits last in the header, so it is the rightmost control. */
.theme-toggle {
  display: none;
  align-items: center;
  justify-content: center;
  order: 1;
  width: 2.5rem;
  height: 2.5rem;
  margin: -0.75rem -0.5rem -0.75rem 0;
  padding: 0;
  border: 0;
  border-radius: 8px;
  background: none;
  color: var(--muted);
  cursor: pointer;
}

.js .theme-toggle {
  display: inline-flex;
}

.theme-toggle:hover,
.theme-toggle:focus-visible {
  color: var(--fg);
}

/* Show the theme being offered, not the one in use. */
.theme-toggle .icon-sun {
  display: none;
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme='light']) .theme-toggle .icon-sun {
    display: block;
  }

  :root:not([data-theme='light']) .theme-toggle .icon-moon {
    display: none;
  }
}

:root[data-theme='dark'] .theme-toggle .icon-sun {
  display: block;
}

:root[data-theme='dark'] .theme-toggle .icon-moon {
  display: none;
}

:root[data-theme='light'] .theme-toggle .icon-sun {
  display: none;
}

:root[data-theme='light'] .theme-toggle .icon-moon {
  display: block;
}

/* Shown only once the nav script has taken over, so a no-script visitor keeps
   the plain row of links rather than a button that does nothing. */
.nav-toggle {
  display: none;
  align-items: center;
  justify-content: center;
  width: 2.5rem;
  height: 2.5rem;
  margin: -0.75rem -0.5rem -0.75rem 0;
  padding: 0;
  border: 0;
  border-radius: 8px;
  background: none;
  color: var(--muted);
  cursor: pointer;
}

.nav-toggle:hover,
.nav-toggle[aria-expanded="true"] {
  color: var(--fg);
}

@media (max-width: 40rem) {
  .js .nav-toggle {
    display: inline-flex;
  }

  .js .site-nav {
    display: none;
    flex-basis: 100%;
    flex-direction: column;
    gap: 0.25rem;
    padding-top: 0.5rem;
  }

  .js .site-nav.is-open {
    display: flex;
  }

  .js .site-nav a {
    padding: 0.5rem 0;
  }
}

.site-main {
  padding: 2.5rem 0 4rem;
}

h1 {
  margin: 0 0 0.5rem;
  font-size: var(--title);
  line-height: 1.2;
  letter-spacing: -0.01em;
}

.lede {
  margin-top: 0;
  color: var(--muted);
  font-size: 1.125rem;
}

/* Kept in the document for structure, taken out of the visual flow. */
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

.page-menu {
  display: grid;
  gap: 0.75rem;
  margin: 0;
}

.page-menu-below-heading {
  margin-top: var(--section-gap);
}

.page-card,
.contact-card {
  transition: background-color 0.15s ease, border-color 0.15s ease, color 0.15s ease,
    transform 0.15s ease;
}

.page-card {
  display: grid;
  /* Text in the first column, the arrow pinned to the right of both rows. */
  grid-template-columns: 1fr auto;
  align-items: center;
  column-gap: 1rem;
  row-gap: 0.2rem;
  padding: 1.25rem 1.5rem;
  border: 1px solid var(--rule);
  border-radius: 14px;
  color: inherit;
  text-decoration: none;
}

.page-card:hover,
.page-card:focus-visible {
  background: var(--invert-surface);
  border-color: var(--invert-surface);
  color: var(--invert-fg);
  transform: translateY(-2px);
}

.page-card:hover .page-card-note,
.page-card:focus-visible .page-card-note {
  color: var(--invert-muted);
}

/* The logo sits beside the title, and the card drops the arrow. */
.page-card-with-logo {
  grid-template-columns: 1fr;
}

.page-card-with-logo::after {
  content: none;
}

/* Logo and title share one line and never wrap apart. */
.page-card-with-logo .page-card-title {
  display: flex;
  flex-wrap: nowrap;
  align-items: center;
  gap: 0.75rem;
}

.page-card-label {
  min-width: 0;
}

/* The description starts under the title text, past the logo, with a rule
   down its left like the roles on a professional page card. */
.page-card-with-logo .page-card-note {
  margin: 0.4rem 0 0 calc(36px + 0.75rem);
  padding-left: 1rem;
  border-left: 1px solid var(--rule);
}

.page-card-with-logo:hover .page-card-note,
.page-card-with-logo:focus-visible .page-card-note {
  border-left-color: var(--invert-muted);
}

.page-card-logo {
  display: block;
  flex: 0 0 auto;
  width: 36px;
  height: 36px;
  object-fit: contain;
}

/* A logo with a version for dark backgrounds shows the one that suits the
   card: the theme's surface at rest, the opposite one when the card inverts
   on hover. */
.page-card-logo-on-light {
  display: var(--logo-on-light);
}

.page-card-logo-on-dark {
  display: var(--logo-on-dark);
}

.page-card:hover .page-card-logo-on-light,
.page-card:focus-visible .page-card-logo-on-light {
  display: var(--logo-on-dark);
}

.page-card:hover .page-card-logo-on-dark,
.page-card:focus-visible .page-card-logo-on-dark {
  display: var(--logo-on-light);
}

.page-card-title {
  grid-column: 1;
  font-size: var(--title);
  font-weight: 650;
  letter-spacing: -0.01em;
}

/* Arrow nudges on hover to signal the whole card is the link. */
.page-card::after {
  content: "\2192";
  grid-column: 2;
  grid-row: 1 / span 2;
  color: var(--muted);
  font-size: 1.25rem;
  line-height: 1;
  transition: transform 0.15s ease;
}

.page-card:hover::after,
.page-card:focus-visible::after {
  color: var(--invert-fg);
}

.page-card:hover::after,
.page-card:focus-visible::after {
  transform: translateX(0.25rem);
}

.contact-card {
  display: grid;
  /* QR, details, download glyph. The QR column drops away on a phone. */
  grid-template-columns: auto 1fr auto;
  align-items: center;
  gap: 1.25rem;
  margin-top: 0.75rem;
  padding: 1.25rem 1.5rem;
  border: 1px solid var(--rule);
  border-radius: 14px;
  color: inherit;
  text-decoration: none;
}

.contact-card:hover,
.contact-card:focus-visible {
  background: var(--invert-surface);
  border-color: var(--invert-surface);
  color: var(--invert-fg);
  transform: translateY(-2px);
}

.contact-card:hover .contact-note,
.contact-card:focus-visible .contact-note {
  color: var(--invert-muted);
}

/* Transparent, with the modules inheriting the card's colour, so the code
   follows the theme and flips again when the card inverts on hover. */
.contact-qr {
  display: block;
  width: 150px;
  height: 150px;
}

.contact-qr svg {
  display: block;
  width: 100%;
  height: 100%;
}

.contact-body {
  display: grid;
  gap: 0.1rem;
}

/* Inherits the card's colour, so it flips with the theme and on hover. */
.contact-download-icon {
  display: block;
  color: var(--muted);
  transition: transform 0.15s ease, color 0.15s ease;
}

.contact-download-icon svg {
  display: block;
}

.contact-card:hover .contact-download-icon,
.contact-card:focus-visible .contact-download-icon {
  color: var(--invert-fg);
  transform: translateY(2px);
}

.contact-title {
  font-size: 1.125rem;
  font-weight: 650;
  letter-spacing: -0.01em;
}

.contact-detail {
  font-size: 0.9375rem;
  font-variant-numeric: tabular-nums;
}

.contact-detail:first-of-type {
  margin-top: 0.45rem;
}

.contact-note {
  margin-top: 0.45rem;
  color: var(--muted);
  font-size: 0.875rem;
}

.contact-note-tap {
  display: none;
}

/* No QR on a phone: the card itself is the download. Everything else shrinks
   against the small viewport height (svh, so it is the height at first paint)
   so the whole home page lands on one screen without scrolling. */
@media (max-width: 40rem) {
  .contact-card {
    grid-template-columns: 1fr auto;
  }

  .contact-qr,
  .contact-note-scan {
    display: none;
  }

  .contact-note-tap {
    display: block;
  }

  .sheet {
    margin: clamp(0.5rem, 2svh, 2.5rem) auto;
  }

  .site-header {
    padding: clamp(0.85rem, 2.4svh, 2rem) 0 clamp(0.6rem, 1.6svh, 1.25rem);
  }

  .site-main {
    padding: clamp(0.9rem, 2.8svh, 2.5rem) 0 clamp(0.9rem, 2.8svh, 4rem);
  }

  .site-footer {
    padding: clamp(0.6rem, 1.6svh, 1.25rem) 0 clamp(0.85rem, 2.4svh, 2.5rem);
  }

  .page-menu {
    gap: clamp(0.4rem, 1.2svh, 0.75rem);
  }

  .page-card,
  .contact-card,
  .employer {
    padding: clamp(0.6rem, 1.9svh, 1.25rem) 1.1rem;
  }

  .contact-card {
    margin-top: clamp(0.4rem, 1.2svh, 0.75rem);
  }

  :root {
    --title: clamp(1rem, 2.5svh, 1.375rem);
  }

  .contact-title {
    font-size: clamp(1rem, 2.4svh, 1.125rem);
  }

  .page-card-note,
  .contact-detail {
    font-size: clamp(0.78rem, 1.7svh, 0.9375rem);
  }

  .contact-detail:first-of-type,
  .contact-note {
    margin-top: clamp(0.15rem, 0.7svh, 0.45rem);
  }

  .contact-note {
    font-size: clamp(0.72rem, 1.5svh, 0.875rem);
  }
}

@media (prefers-reduced-motion: reduce) {
  .page-card,
  .contact-card,
  .page-card::after,
  .contact-download-icon {
    transition: none;
  }

  .page-card:hover,
  .page-card:focus-visible,
  .contact-card:hover,
  .contact-card:focus-visible,
  .contact-card:hover .contact-download-icon {
    transform: none;
  }
}

.page-card-note {
  grid-column: 1;
  color: var(--muted);
  font-size: 0.9375rem;
}

/* Small caps eyebrow, so a section reads as a label above its content
   without competing with the page heading. */
.section-title {
  margin: var(--section-gap) 0 0.75rem;
  color: var(--muted);
  font-size: 0.8125rem;
  font-weight: 650;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

/* The heading sits above this, outside the border. Padding on .review-list
   would sit inside the height the rotator sets and clip the quote, so the box
   is drawn on a wrapper instead. */
.review-box {
  padding: 1.25rem 1.5rem;
  border: 1px solid var(--rule);
  border-radius: 14px;
}

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

/* Added by the rotator. Stacking every quote in one grid cell holds the
   section at the height of the longest one, so a cross-fade does not make the
   page jump, and leaves the others in place to fade between. */
.review-list.is-rotating {
  display: grid;
  /* Items keep their natural height; the box is sized to the current one by
     the rotator and eased between quotes so it does not snap. */
  align-items: start;
  overflow: hidden;
  transition: height 0.45s ease;
}

.review-list.is-rotating .review {
  grid-area: 1 / 1;
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.45s ease, visibility 0.45s;
}

.review-list.is-rotating .review.is-current {
  opacity: 1;
  visibility: visible;
}

@media (prefers-reduced-motion: reduce) {
  .review-list.is-rotating,
  .review-list.is-rotating .review {
    transition: none;
  }
}

.review-quote {
  margin: 0;
  padding-left: 1rem;
  border-left: 2px solid var(--rule);
  font-size: 1.0625rem;
  font-style: italic;
  line-height: 1.6;
}

.review-quote p {
  margin: 0 0 0.75rem;
}

.review-quote p:last-child {
  margin-bottom: 0;
}

.review-by {
  margin: 0.75rem 0 0;
  padding-left: 1rem;
  color: var(--muted);
  font-size: 0.875rem;
}

.review-name {
  font-weight: 600;
}

.review-title::before {
  content: " · ";
}

.review-dots {
  display: flex;
  gap: 0.4rem;
  margin-top: 1rem;
}

.review-dot {
  width: 0.5rem;
  height: 0.5rem;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: var(--rule);
  cursor: pointer;
  transition: background-color 0.15s ease;
}

.review-dot[aria-selected="true"] {
  background: var(--muted);
}

/* The dot itself is tiny; give the pointer a fair target around it. */
.review-dot::after {
  content: "";
  display: block;
  margin: -0.6rem;
  padding: 0.6rem;
}

@media (prefers-reduced-motion: reduce) {
  .review-dot {
    transition: none;
  }
}

/* Arrows either side of the quote. Shown only once the rotator is running,
   since they need it to do anything; without it the box keeps its plain
   single-column layout. */
.review-arrow {
  display: none;
}

.js .review-box.has-arrows {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  column-gap: 0.75rem;
  padding-inline: 0.75rem;
}

.js .review-box.has-arrows .review-list,
.js .review-box.has-arrows .review-dots {
  grid-column: 2;
}

.js .review-arrow {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  align-self: center;
  grid-row: 1;
  width: 2.25rem;
  height: 2.25rem;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: none;
  color: var(--muted);
  cursor: pointer;
}

.js .review-arrow-prev {
  grid-column: 1;
}

.js .review-arrow-next {
  grid-column: 3;
}

.review-arrow:hover,
.review-arrow:focus-visible {
  color: var(--fg);
}

.experience,
.roles {
  list-style: none;
  margin: 0;
  padding: 0;
}

.experience {
  display: grid;
  gap: 0.75rem;
}

/* Each employer sits on its own card, the same chrome as the page menu cards
   and the recommendations box so the three read as one family. */
.employer {
  display: grid;
  grid-template-columns: 48px 1fr;
  gap: 1rem;
  align-items: start;
  padding: 1.25rem 1.5rem;
  border: 1px solid var(--rule);
  border-radius: 14px;
}

.employer-logo {
  width: 48px;
  height: 48px;
  padding: 2px;
  border: 1px solid var(--rule);
  border-radius: 10px;
  /* The logo files are opaque and mostly light, so the chip stays white in
     both themes rather than showing a white square on a dark tile. */
  background: #ffffff;
  object-fit: contain;
}

.employer-logo-monogram {
  display: grid;
  place-items: center;
  color: var(--muted);
  font-size: 1.25rem;
  font-weight: 650;
}

.employer-name {
  margin: 0 0 0.5rem;
  font-size: 1.125rem;
  line-height: 1.3;
}

.employer-name a {
  color: inherit;
  text-decoration: none;
}

.employer-name a:hover {
  text-decoration: underline;
}

.roles {
  display: grid;
  gap: 1rem;
}

/* A rule down the left groups several roles at one employer. */
.employer .roles {
  padding-left: 1rem;
  border-left: 1px solid var(--rule);
}

.role-title {
  margin: 0;
  font-weight: 600;
}

.role-meta {
  margin: 0.1rem 0 0;
  color: var(--muted);
  font-size: 0.875rem;
}

/* Separator only while the location shares the dates line. */
.role-location::before {
  content: " · ";
}

@media (max-width: 30rem) {
  .role-location {
    display: block;
  }

  .role-location::before {
    content: none;
  }
}

.role-summary {
  margin: 0.4rem 0 0;
}

ul.role-summary {
  padding-left: 1.1rem;
}

ul.role-summary li + li {
  margin-top: 0.35rem;
}

/* A card with role descriptions folds them behind a chevron. Folded only
   once script is running, since opening it needs script too. */
.employer-expand {
  display: none;
}

.js .employer[data-expandable] {
  grid-template-columns: 48px 1fr auto;
  cursor: pointer;
}

.js .employer[data-expandable]:hover {
  border-color: var(--muted);
}

.js .employer[data-expandable]:not([data-expanded]) .role-summary {
  display: none;
}

.js .employer-expand {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2.25rem;
  height: 2.25rem;
  margin: -0.25rem -0.5rem 0 0;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: none;
  color: var(--muted);
  cursor: pointer;
}

.employer-expand:hover,
.employer-expand:focus-visible {
  color: var(--fg);
}

.employer-expand svg {
  transition: transform 0.2s ease;
}

.employer[data-expanded] .employer-expand svg {
  transform: rotate(180deg);
}

@media (prefers-reduced-motion: reduce) {
  .employer-expand svg {
    transition: none;
  }
}

/* On a phone the logo column would leave the role details, and the
   description most of all, a narrow strip. The logo, name and chevron share
   the top row instead, and the roles run the full width of the card below. */
@media (max-width: 40rem) {
  .employer {
    row-gap: 0.75rem;
  }

  .employer-body {
    display: contents;
  }

  .employer-logo {
    grid-row: 1;
  }

  .employer-name {
    grid-row: 1;
    grid-column: 2;
    align-self: center;
    margin: 0;
  }

  /* The rule down the left costs width a phone cannot spare. */
  .employer .roles {
    grid-row: 2;
    grid-column: 1 / -1;
    padding-left: 0;
    border-left: 0;
  }

  .js .employer-expand {
    grid-row: 1;
    grid-column: 3;
    align-self: center;
    margin-top: 0;
  }
}

/* Employment history past the first few cards. Collapsed only once script is
   running, since the button that opens it needs script too. */
.js .experience[data-collapsed] > [data-more] {
  display: none;
}

.show-more {
  display: none;
  margin-top: 0.75rem;
  padding: 0.5rem 1rem;
  border: 1px solid var(--rule);
  border-radius: 999px;
  background: none;
  color: var(--muted);
  font: inherit;
  font-size: 0.875rem;
  cursor: pointer;
}

.js .show-more {
  display: inline-block;
}

.show-more:hover,
.show-more:focus-visible {
  color: var(--fg);
  border-color: var(--muted);
}

.error-stack {
  overflow-x: auto;
  padding: 1rem;
  border-radius: 0.5rem;
  background: var(--code-bg);
  font-size: 0.8125rem;
  line-height: 1.5;
}

.site-footer {
  display: flex;
  /* One row at every width: the icons never drop below the copyright. */
  flex-wrap: nowrap;
  align-items: center;
  gap: 1rem;
  justify-content: space-between;
  padding: 1.25rem 0 2.5rem;
  border-top: 1px solid var(--rule);
  color: var(--muted);
  font-size: clamp(0.75rem, 3vw, 0.875rem);
}

/* Lets the copyright give way rather than pushing the icons onto a new line. */
.site-footer p {
  min-width: 0;
}

.site-social {
  display: flex;
  flex: 0 0 auto;
  align-items: center;
  gap: clamp(0, 0.5vw, 0.25rem);
}

/* The icon is the whole link, so give it a comfortable tap target. */
.social-link {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--icon-box);
  height: var(--icon-box);
  margin: -0.75rem 0;
  border-radius: 50%;
  color: var(--muted);
}

.social-link svg {
  display: block;
  width: var(--icon);
  height: var(--icon);
}

.social-link:hover,
.social-link:focus-visible {
  color: var(--fg);
}

.site-footer p {
  margin: 0;
}

:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

/* Professional page heading, with the Download Resume menu to its right. */
.page-head {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem 1rem;
  margin-bottom: 0.5rem;
}

.page-head h1 {
  margin: 0;
}

.resume-menu {
  position: relative;
}

.resume-button {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.5rem 1rem;
  border: 1px solid var(--rule);
  border-radius: 999px;
  color: var(--fg);
  font-size: 0.875rem;
  list-style: none;
  cursor: pointer;
  user-select: none;
}

.resume-button::-webkit-details-marker {
  display: none;
}

.resume-button:hover,
.resume-button:focus-visible,
.resume-menu[open] > .resume-button {
  border-color: var(--muted);
}

.resume-options {
  position: absolute;
  z-index: 10;
  top: calc(100% + 0.375rem);
  right: 0;
  min-width: 100%;
  margin: 0;
  padding: 0.375rem;
  border: 1px solid var(--rule);
  border-radius: 0.75rem;
  background: var(--bg);
  box-shadow: var(--panel-shadow);
  list-style: none;
}

.resume-options a {
  display: flex;
  justify-content: space-between;
  gap: 1.5rem;
  padding: 0.5rem 0.75rem;
  border-radius: 0.5rem;
  color: var(--fg);
  font-size: 0.875rem;
  text-decoration: none;
}

.resume-options a:hover,
.resume-options a:focus-visible {
  background: var(--code-bg);
}

.resume-ext {
  color: var(--muted);
}
