/* ============================================================
   Design tokens
   ============================================================ */
:root {
  --bg:            #fcfcfc;
  --gray-200:      #f6f6f6;
  --gray-300:      #f0f0f0;
  --gray-400:      #e8e8e8;
  --gray-600:      #d0d0d0;
  --gray-900:      #8d8d8d;
  --gray-1000:     #747474;
  --gray-1100:     #646464;
  --gray-1200:     #202020;

  --preview-bg:     #ffffff;
  --preview-border: #e8e8e8;

  --blue-100: #e7f0ff;
  --blue-500: #3080ff;
  --blue-700: #1a5fd6;

  --shadow: 0 0 0 1px #0000000f, 0 1px 2px -1px #0000000f, 0 2px 4px 0 #0000000a;
  --shadow-lift: 0 0 0 1px #0000000f, 0 6px 16px -4px #00000018, 0 3px 6px -2px #0000000f;

  --font: "Inter", ui-sans-serif, system-ui, -apple-system, "Segoe UI", Arial, sans-serif;

  /* heatmap palette — swapped at runtime by script.js */
  --contrib-empty: #ebedf0;

  --tag-merged-bg: #f3eefc;
  --tag-merged-fg: #6639ba;
  --tag-open-bg: #dafbe1;
  --tag-open-fg: #1a7f37;

  color-scheme: light;
}

/* ------------------------------------------------------------
   Dark theme. The inline script in <head> stamps data-theme, so
   that selector does the work in practice; the media query is the
   no-JS fallback, and it defers to an explicit "light" choice.
   ------------------------------------------------------------ */
:root[data-theme="dark"] {
  --bg:            #111111;
  --gray-200:      #1c1c1c;
  --gray-300:      #242424;
  --gray-400:      #2e2e2e;
  --gray-600:      #3f3f3f;
  --gray-900:      #6e6e6e;
  --gray-1000:     #8b8b8b;
  --gray-1100:     #a1a1a1;
  --gray-1200:     #ededed;

  --preview-bg:     #171717;
  --preview-border: #292929;

  --blue-100: #10243e;
  --blue-500: #4c8dff;
  --blue-700: #8cb6ff;

  /* light's layered drop shadows read as smudges on a dark ground —
     a hairline ring plus a deeper shadow does the same job */
  --shadow: 0 0 0 1px #ffffff14, 0 1px 2px -1px #00000099, 0 2px 6px 0 #0000006b;
  --shadow-lift: 0 0 0 1px #ffffff1f, 0 8px 20px -4px #000000b3, 0 3px 8px -2px #00000080;

  --contrib-empty: #1b1b1b;

  --tag-merged-bg: #241a38;
  --tag-merged-fg: #b392f0;
  --tag-open-bg: #10261a;
  --tag-open-fg: #4ac26b;

  color-scheme: dark;
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --bg:            #111111;
    --gray-200:      #1c1c1c;
    --gray-300:      #242424;
    --gray-400:      #2e2e2e;
    --gray-600:      #3f3f3f;
    --gray-900:      #6e6e6e;
    --gray-1000:     #8b8b8b;
    --gray-1100:     #a1a1a1;
    --gray-1200:     #ededed;

    --preview-bg:     #171717;
    --preview-border: #292929;

    --blue-100: #10243e;
    --blue-500: #4c8dff;
    --blue-700: #8cb6ff;

    --shadow: 0 0 0 1px #ffffff14, 0 1px 2px -1px #00000099, 0 2px 6px 0 #0000006b;
    --shadow-lift: 0 0 0 1px #ffffff1f, 0 8px 20px -4px #000000b3, 0 3px 8px -2px #00000080;

    --contrib-empty: #1b1b1b;

    --tag-merged-bg: #241a38;
    --tag-merged-fg: #b392f0;
    --tag-open-bg: #10261a;
    --tag-open-fg: #4ac26b;

    color-scheme: dark;
  }
}

* { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--gray-1200);
  font-family: var(--font);
  font-size: 16px;
  line-height: 1.625;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

img { max-width: 100%; display: block; }

button {
  font: inherit;
  color: inherit;
  background: none;
  border: 0;
  padding: 0;
}

:focus-visible {
  outline: 2px solid var(--gray-900);
  outline-offset: 2px;
  border-radius: 2px;
}

/* ============================================================
   Page shell
   ============================================================ */
.page {
  max-width: 692px;
  margin-inline: auto;
  padding: 32px 24px 48px;
}

@media (min-width: 640px) {
  .page { padding: 56px 24px 96px; }
}

/* ============================================================
   Navbar
   ============================================================ */
/* Pinned to the top. The opaque background matters — the bar is only as wide
   as the content column, but so is everything that scrolls beneath it, so a
   solid fill is enough to keep text from showing through. */
.navbar {
  position: sticky;
  top: 0;
  z-index: 50;
  max-width: 692px;
  margin-inline: auto;
  padding: 16px 24px 12px;
  background: var(--bg);
}

.nav-pill {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: flex-end;
  gap: 14px;
  padding: 10px 20px;
  border: 1px solid var(--gray-400);
  border-radius: 16px;
}

.nav-link {
  color: var(--gray-1100);
  font-size: 13px;
  font-weight: 500;
  text-decoration: none;
  transition: color 0.15s ease-out;
}

.nav-link:hover { color: var(--gray-1200); }

.nav-link.is-active { color: var(--gray-1200); }

.theme-toggle {
  display: inline-flex;
  width: 26px;
  height: 26px;
  align-items: center;
  justify-content: center;
  border: 1px solid var(--gray-400);
  border-radius: 8px;
  color: var(--gray-1100);
  cursor: pointer;
  transition: color 0.15s ease-out, border-color 0.15s ease-out;
}

.theme-toggle:hover {
  color: var(--gray-1200);
  border-color: var(--gray-600);
}

.theme-toggle svg { width: 14px; height: 14px; }

/* show the icon for the theme you'd switch *to* */
.icon-moon { display: none; }
:root[data-theme="dark"] .icon-sun { display: none; }
:root[data-theme="dark"] .icon-moon { display: block; }

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

/* four links plus the toggle only just fit a phone — tighten the spacing so
   nothing wraps onto a second row */
@media (max-width: 440px) {
  .nav-pill {
    gap: 12px;
    padding: 10px 14px;
  }
}

/* jump targets have to clear the pinned navbar, not just the top edge */
.section-title[id],
.page[id] { scroll-margin-top: 88px; }

.section { margin-top: 64px; }

@media (min-width: 640px) {
  .section { margin-top: 96px; }
}

/* ---- secondary page header (projects.html) ---- */
.back-link {
  display: inline-block;
  margin-bottom: 24px;
  color: var(--gray-1100);
  font-size: 14px;
  text-decoration: none;
  transition: color 0.15s ease-out;
}

.back-link:hover { color: var(--gray-1200); }

.page-title {
  margin: 0 0 8px;
  font-size: 24px;
  font-weight: 600;
  line-height: 1.25;
  letter-spacing: -0.01em;
}

.page-lede {
  margin: 0 0 32px;
  color: var(--gray-1100);
}

.section-title {
  margin: 0 0 12px;
  font-size: 16px;
  font-weight: 500;
  line-height: 1.375;
  color: var(--gray-1200);
}

/* ============================================================
   Entrance animation
   ============================================================ */
.enter {
  opacity: 0;
  transform: translateY(8px);
  animation: enter 0.6s cubic-bezier(0.16, 1, 0.3, 1) forwards;
  animation-delay: calc(var(--stagger, 0) * 90ms);
}

@keyframes enter {
  to { opacity: 1; transform: none; }
}

/* ============================================================
   Hero
   ============================================================ */
.hero {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-bottom: 24px;
}

.hero-avatar {
  width: 44px;
  height: 44px;
  flex-shrink: 0;
  border-radius: 999px;
  object-fit: cover;
}

.hero-text { display: flex; flex-direction: column; }

.hero-name {
  display: flex;
  align-items: center;
  gap: 4px;
  margin: 0;
  font-size: 16px;
  font-weight: 500;
  line-height: 1.375;
}

.badge {
  width: 16px;
  height: 16px;
  flex-shrink: 0;
  color: var(--blue-500);
}

.hero-role {
  color: var(--gray-1100);
  line-height: 1.375;
}

/* ============================================================
   Bio + links
   ============================================================ */
.bio {
  margin: 0 0 16px;
  color: var(--gray-1100);
}

.bio.last { margin-bottom: 0; }

.link {
  color: var(--gray-1200);
  font-weight: 500;
  text-decoration: underline;
  text-decoration-color: var(--gray-600);
  text-underline-offset: 3px;
  transition: color 0.2s ease-out, text-decoration-color 0.2s ease-out;
}

.link:hover { text-decoration-color: var(--gray-1100); }

/* ============================================================
   Card (used by the heatmap)
   ============================================================ */
.card {
  padding: 4px;
  border-radius: 16px;
  box-shadow: var(--shadow);
  background: var(--bg);
}

.card-inner {
  display: flex;
  flex-direction: column;
  gap: 12px;
  padding: 16px;
  border: 1px solid var(--preview-border);
  border-radius: 12px;
  background: var(--preview-bg);
}

.card-foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  color: var(--gray-1000);
  font-size: 14px;
  font-variant-numeric: tabular-nums;
}

.card-foot > span:first-child {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* the caption and the legend can't share a line on phones */
@media (max-width: 639px) {
  .card-foot {
    flex-direction: column;
    align-items: flex-start;
    gap: 10px;
  }

  .card-foot > span:first-child {
    overflow: visible;
    white-space: normal;
    text-overflow: clip;
  }
}

/* ============================================================
   Contributions — scroll-swapped heatmap
   ============================================================ */
/* The card is pinned near the top of the viewport rather than centred in a
   full-height box — centring left ~290px of dead air above and below it, which
   read as broken spacing against the neighbouring sections. Anchoring to the
   top means the gap above is just the normal section margin, and the extra
   wrapper height below is consumed while the card is pinned, so Projects
   follows immediately once it releases. */
/* script.js sets the exact height (card + a 100px pin) and the `top` that
   centres the card. Left alone — as with no JS — the wrapper is just as tall
   as the card, so the sticky never engages and the page scrolls straight
   through. */
.contrib-scroll { height: auto; }

.contrib-sticky {
  position: sticky;
  top: 96px;
}

/* The card is a solid block rather than a run of text, so it sits tighter to
   its neighbours than the other sections do. The negative bottom margin eats
   into the following section's 96px top margin. */
.contrib-section {
  margin-top: 40px;
  margin-bottom: -56px;
}

@media (max-width: 639px) {
  .contrib-section {
    margin-top: 28px;
    margin-bottom: -28px;
  }
}

.contrib-title {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
}

.contrib-switch {
  display: inline-flex;
  align-items: center;
  gap: 10px;
}

.contrib-source {
  font-size: 13px;
  font-weight: 500;
  color: var(--gray-1100);
  text-decoration: none;
  transition: opacity 0.18s ease-out, filter 0.18s ease-out, color 0.18s ease-out;
}

.contrib-source:hover { color: var(--gray-1200); }

.contrib-source.is-swapping { opacity: 0; filter: blur(2px); }

/* contest rating + active badge, shown beside the source name */
.contrib-stat {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  font-size: 13px;
  font-weight: 500;
  color: var(--gray-1100);
  font-variant-numeric: tabular-nums;
  transition: opacity 0.18s ease-out, filter 0.18s ease-out;
}

.contrib-stat[hidden] { display: none; }

.contrib-stat.is-swapping { opacity: 0; filter: blur(2px); }

.badge-icon {
  width: 16px;
  height: 16px;
  flex-shrink: 0;
  object-fit: contain;
}

.contrib-dots { display: inline-flex; gap: 6px; }

.contrib-dot {
  width: 6px;
  height: 6px;
  border-radius: 999px;
  background: var(--gray-600);
  cursor: pointer;
  transition: background-color 0.2s ease-out, transform 0.2s ease-out;
}

.contrib-dot.is-active {
  background: var(--gray-1000);
  transform: scale(1.25);
}

.grid-scroller {
  overflow-x: auto;
  padding-bottom: 4px;
  scrollbar-width: thin;
}

.contrib-grid {
  display: grid;
  grid-auto-flow: column;
  grid-template-rows: repeat(7, 9px);
  gap: 2px;
  width: max-content;
  margin-inline: auto;
}

.contrib-cell {
  width: 9px;
  height: 9px;
  border-radius: 2px;
  background: var(--contrib-empty);
  transition: background-color 0.35s ease-out, transform 0.35s ease-out;
}

/* days that haven't happened yet stay blank, like GitHub's own graph */
.contrib-cell.is-future { background: transparent; }

.legend {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  white-space: nowrap;
}

.legend-swatches { display: inline-flex; gap: 2px; }

.legend-swatch {
  width: 9px;
  height: 9px;
  border-radius: 2px;
  transition: background-color 0.35s ease-out;
}

/* ============================================================
   List rows (writing / projects / events)
   ============================================================ */
.rows { display: flex; flex-direction: column; gap: 8px; }

.row {
  display: flex;
  align-items: center;
  gap: 16px;
  margin-inline: -8px;
  padding: 8px;
  border-radius: 17px;
  color: inherit;
  text-decoration: none;
  transition: background-color 0.18s ease-out;
}

a.row:hover,
button.row:hover { background: var(--gray-200); }

/* rows that open a detail card are real buttons, not divs with a click */
button.row {
  width: 100%;
  cursor: pointer;
  text-align: left;
}

.row.is-static { cursor: default; }


.thumb-frame {
  display: flex;
  flex-shrink: 0;
  align-items: center;
  justify-content: center;
  padding: 3px;
  border-radius: 9px;
  background: var(--bg);
  box-shadow: var(--shadow);
}

.thumb {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  flex-shrink: 0;
  overflow: hidden;
  border: 1px solid var(--preview-border);
  border-radius: 6px;
  background: var(--preview-bg);
  user-select: none;
}

.thumb-lines {
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 3px;
  width: 100%;
  height: 100%;
  padding: 4px;
}

.thumb-lines i {
  height: 2px;
  flex-shrink: 0;
  border-radius: 999px;
  background: var(--gray-400);
}

.thumb-date { justify-content: flex-start; }

.date-month {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  height: 10px;
  flex-shrink: 0;
  background: var(--gray-300);
  color: var(--gray-1000);
  font-size: 6px;
  line-height: 1;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}

.date-day {
  display: flex;
  flex: 1;
  align-items: center;
  justify-content: center;
  font-size: 13px;
  font-weight: 500;
  line-height: 1;
  color: var(--gray-1200);
  font-variant-numeric: tabular-nums;
}

/* project logo filling the thumbnail — the thumb already clips and rounds */
.repo-logo {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.thumb-glyph {
  font-size: 10px;
  font-weight: 500;
  letter-spacing: 0.02em;
  color: var(--gray-1100);
}

.row-main {
  display: flex;
  min-width: 0;
  flex: 1;
  align-items: center;
  gap: 10px;
}

.row-title {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-weight: 500;
  color: var(--gray-1200);
}

.pill {
  display: flex;
  height: 20px;
  flex-shrink: 0;
  align-items: center;
  padding-inline: 8px;
  border-radius: 999px;
  background: var(--blue-100);
  color: var(--blue-700);
  font-size: 12px;
  font-weight: 500;
}

/* state tags — GitHub's own colours, so they read at a glance */
.pill-merged { background: var(--tag-merged-bg); color: var(--tag-merged-fg); }
.pill-open   { background: var(--tag-open-bg); color: var(--tag-open-fg); }
.pill-muted  { background: var(--gray-200); color: var(--gray-1100); }

/* ============================================================
   Open Source — contributions grouped under the repo they went to
   ============================================================ */
/* Tight, like the other row lists — the collapsed heads are single lines, so
   they read better stacked closely than spread out. */
.repo-groups {
  display: flex;
  flex-direction: column;
  gap: 6px;
}

/* repo name on the left, star count on the right */
.repo-head {
  display: flex;
  align-items: center;
  gap: 16px;
  padding: 7px 8px;
  margin-inline: -8px;
  border-radius: 12px;
  cursor: pointer;
  transition: background-color 0.18s ease-out;
}

.repo-group:hover > .repo-head,
.repo-group.is-open > .repo-head { background: var(--gray-200); }

.repo-chevron {
  width: 14px;
  height: 14px;
  flex-shrink: 0;
  color: var(--gray-1000);
  transition: transform 0.3s cubic-bezier(0.16, 1, 0.3, 1);
}

.repo-group:hover .repo-chevron,
.repo-group:focus-within .repo-chevron,
.repo-group.is-open .repo-chevron { transform: rotate(180deg); }

.repo-name {
  min-width: 0;
  flex: 1;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-weight: 500;
  color: var(--gray-1200);
}

.repo-stars {
  flex-shrink: 0;
  color: var(--gray-1000);
  font-size: 13px;
  font-variant-numeric: tabular-nums;
}

/* The repo's pull requests, collapsed until you hover the group. Animating
   `grid-template-rows` from 0fr to 1fr is the one way to transition to an
   auto height without hard-coding one; the inner element does the clipping. */
.repo-items {
  display: grid;
  grid-template-rows: 0fr;
  margin-left: 52px;
  opacity: 0;
  transition: grid-template-rows 0.32s cubic-bezier(0.16, 1, 0.3, 1),
              opacity 0.22s ease-out;
}

.repo-items-inner {
  display: flex;
  min-height: 0;
  flex-direction: column;
  gap: 2px;
  overflow: hidden;
}

/* :focus-within means tabbing into a hidden link opens its group, so the
   list stays reachable by keyboard. .is-open is what touch taps toggle. */
.repo-group:hover .repo-items,
.repo-group:focus-within .repo-items,
.repo-group.is-open .repo-items {
  grid-template-rows: 1fr;
  opacity: 1;
}

.pr-row {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-inline: -8px;
  padding: 7px 8px;
  border-radius: 12px;
  color: inherit;
  text-decoration: none;
  transition: background-color 0.18s ease-out;
}

.pr-row:hover { background: var(--gray-200); }

.pr-num {
  flex-shrink: 0;
  color: var(--gray-1000);
  font-size: 13px;
  font-variant-numeric: tabular-nums;
}

.pr-title {
  min-width: 0;
  flex: 1;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: 14px;
  color: var(--gray-1200);
}

/* On phones the number and tag leave too little room for a one-line title, so
   drop the indent and let it wrap rather than ellipse most of it away. */
/* ---- the dedicated Open Source page: everything expanded, nothing to click ---- */
.repo-groups-static { gap: 28px; }

.repo-groups-static .repo-items {
  grid-template-rows: 1fr;
  opacity: 1;
}

.repo-groups-static .repo-head { cursor: default; }
.repo-groups-static .repo-group:hover > .repo-head { background: transparent; }
.repo-groups-static .repo-items-inner { gap: 6px; }

/* two-line contribution row, with a note on what the change actually did */
.pr-row-detail { align-items: flex-start; }

.pr-row-detail .pr-num { margin-top: 2px; }

.pr-main {
  display: flex;
  min-width: 0;
  flex: 1;
  flex-direction: column;
  gap: 3px;
}

.pr-line {
  display: flex;
  min-width: 0;
  align-items: center;
  gap: 10px;
}

.pr-note {
  color: var(--gray-1000);
  font-size: 13px;
  line-height: 1.55;
}

.pr-note code {
  font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
  font-size: 0.92em;
}

@media (max-width: 520px) {
  .repo-items { margin-left: 0; }

  .pr-row { align-items: flex-start; }

  .pr-num,
  .pr-row .pill { margin-top: 1px; }

  .pr-title {
    overflow: visible;
    white-space: normal;
    text-overflow: clip;
  }
}

/* ---- two-line rows, for contributions that need a word of context ---- */
.row-main-stack {
  flex-direction: column;
  align-items: flex-start;
  gap: 3px;
}

.row-line {
  display: flex;
  min-width: 0;
  max-width: 100%;
  align-items: center;
  gap: 10px;
}

.row-sub {
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  color: var(--gray-1000);
  font-size: 13px;
}

.row-sub code {
  font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
  font-size: 0.92em;
}

/* On phones the detail is the whole point of these rows, so let it wrap to a
   second line instead of truncating to nothing useful. */
@media (max-width: 639px) {
  .row-sub {
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    white-space: normal;
  }
}

.thumb-icon svg {
  width: 16px;
  height: 16px;
  color: var(--gray-1100);
}

.row-meta {
  max-width: 50%;
  flex-shrink: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  color: var(--gray-1100);
}

@media (max-width: 520px) {
  .row-meta { display: none; }
}

/* ============================================================
   Project detail card
   ============================================================ */
.modal-backdrop {
  position: fixed;
  z-index: 100;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
  inset: 0;
  background: rgba(0, 0, 0, 0.45);
  animation: modal-fade 0.18s ease-out;
  -webkit-backdrop-filter: blur(3px);
  backdrop-filter: blur(3px);
}

.modal-backdrop[hidden] { display: none; }

/* the page behind stays put instead of scrolling under the card */
body.is-modal-open { overflow: hidden; }

.modal {
  width: 100%;
  max-width: 560px;
  max-height: 85vh;
  padding: 24px;
  overflow-y: auto;
  border: 1px solid var(--gray-400);
  border-radius: 16px;
  background: var(--preview-bg);
  box-shadow: var(--shadow-lift);
  animation: modal-rise 0.22s cubic-bezier(0.16, 1, 0.3, 1);
}

@keyframes modal-fade { from { opacity: 0; } }
@keyframes modal-rise { from { opacity: 0; transform: translateY(10px) scale(0.985); } }

.modal-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 16px;
}

.modal-eyebrow {
  padding-top: 5px;
  color: var(--gray-1000);
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.08em;
}

.modal-close {
  display: grid;
  width: 28px;
  height: 28px;
  flex-shrink: 0;
  place-items: center;
  border: 1px solid var(--gray-400);
  border-radius: 8px;
  color: var(--gray-1100);
  cursor: pointer;
  transition: color 0.15s ease-out, border-color 0.15s ease-out;
}

.modal-close:hover {
  color: var(--gray-1200);
  border-color: var(--gray-600);
}

.modal-close svg { width: 14px; height: 14px; }

.modal-title {
  margin: 12px 0 6px;
  font-size: 22px;
  font-weight: 600;
  line-height: 1.25;
  letter-spacing: -0.01em;
}

.modal-lede {
  margin: 0 0 20px;
  color: var(--gray-1100);
}

.modal-figure {
  margin: 0 0 24px;
}

.modal-figure[hidden] { display: none; }

.modal-image {
  width: 100%;
  border: 1px solid var(--gray-400);
  border-radius: 10px;
  background: var(--gray-200);
}

.modal-caption {
  margin-top: 8px;
  color: var(--gray-1000);
  font-size: 13px;
}

.modal-caption[hidden] { display: none; }

/* the class sets display, so it has to concede to [hidden] itself */
.modal-meta[hidden] { display: none; }

.modal-meta {
  display: grid;
  margin: 0 0 24px;
  border-top: 1px solid var(--gray-400);
  border-bottom: 1px solid var(--gray-400);
  padding: 14px 0;
  grid-template-columns: repeat(2, 1fr);
  gap: 16px 20px;
}

.modal-meta dt {
  color: var(--gray-1000);
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

.modal-meta dd {
  margin: 3px 0 0;
  color: var(--gray-1200);
  font-size: 14px;
  font-weight: 500;
}

.modal-h {
  margin: 0 0 8px;
  font-size: 15px;
  font-weight: 600;
}

.modal-body {
  margin: 0 0 24px;
  color: var(--gray-1100);
  font-size: 15px;
}

.modal-list {
  margin: 0 0 24px;
  padding-left: 18px;
  color: var(--gray-1100);
  font-size: 15px;
}

.modal-list li { margin-bottom: 8px; }
.modal-list li:last-child { margin-bottom: 0; }

.modal-tech {
  margin: 0 0 24px;
  color: var(--gray-1100);
  font-size: 14px;
}

.modal-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 20px;
  border-top: 1px solid var(--gray-400);
  padding-top: 20px;
}

.modal-link[hidden] { display: none; }

.modal-link {
  color: var(--gray-1200);
  font-size: 14px;
  font-weight: 500;
  text-decoration: underline;
  text-decoration-color: var(--gray-600);
  text-underline-offset: 3px;
  transition: text-decoration-color 0.15s ease-out;
}

.modal-link:hover { text-decoration-color: var(--gray-1100); }

@media (max-width: 520px) {
  .modal-backdrop { padding: 12px; }
  .modal { padding: 20px; max-height: 90vh; }
  .modal-meta { grid-template-columns: 1fr; }
}

/* ============================================================
   Contact
   ============================================================ */
.more-link {
  display: inline-block;
  margin-top: 20px;
  color: var(--gray-1100);
  font-size: 14px;
  text-decoration: none;
  transition: color 0.18s ease-out;
}

.more-link:hover { color: var(--gray-1200); }

.contact-card { gap: 0; padding: 24px; }

.contact-copy {
  max-width: 420px;
  margin: 0;
  color: var(--gray-1100);
}

.contact-links {
  display: flex;
  gap: 10px;
  margin-top: 20px;
}

.contact-icon {
  display: grid;
  width: 40px;
  height: 40px;
  flex-shrink: 0;
  place-items: center;
  border: 1px solid var(--gray-400);
  border-radius: 10px;
  color: var(--gray-1200);
  transition: border-color 0.18s ease-out, background-color 0.18s ease-out, transform 0.18s ease-out;
}

.contact-icon svg { width: 18px; height: 18px; }

.contact-icon:hover {
  border-color: var(--gray-600);
  background: var(--gray-200);
  transform: translateY(-2px);
}

/* ============================================================
   Footer
   ============================================================ */
.footer {
  display: flex;
  align-items: center;
  margin-top: 64px;
  padding-top: 24px;
  border-top: 1px solid var(--gray-400);
}

@media (min-width: 640px) {
  .footer { margin-top: 128px; }
}

.clock {
  display: flex;
  align-items: baseline;
  gap: 8px;
  margin: 0;
  color: var(--gray-1000);
  font-size: 14px;
  font-variant-numeric: tabular-nums;
}

/* ============================================================
   Reduced motion
   ============================================================ */
@media (prefers-reduced-motion: reduce) {
  .enter {
    opacity: 1;
    transform: none;
    animation: none;
  }
  .contrib-cell,
  .legend-swatch,
  .contrib-source { transition-duration: 0.01ms; }

  .modal-backdrop,
  .modal { animation: none; }
  .contrib-scroll { height: auto; }
  .contrib-sticky { position: static; height: auto; }
}
