/* ═══════════════════════════════════════════════════════════════════════════
   Fonts — self-hosted (latin subset). No external requests.
   ═══════════════════════════════════════════════════════════════════════════ */

@font-face {
  font-family: 'Playfair Display';
  src: url('../fonts/playfair-latin-var.woff2') format('woff2');
  font-weight: 400 700;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: 'Playfair Display';
  src: url('../fonts/playfair-latin-italic-var.woff2') format('woff2');
  font-weight: 400 500;
  font-style: italic;
  font-display: swap;
}
@font-face {
  font-family: 'Inter';
  src: url('../fonts/inter-latin-var.woff2') format('woff2');
  font-weight: 200 500;
  font-style: normal;
  font-display: swap;
}


/* ═══════════════════════════════════════════════════════════════════════════
   Tokens
   ═══════════════════════════════════════════════════════════════════════════ */

:root {
  --bg:               #0d0d0f;   /* page                                      */
  --text:             #ece9e4;   /* 16.03:1 on --bg                           */
  --muted:            #a3a09b;   /*  7.45:1 on --bg                           */
  --accent:           #7fb0ad;   /*  8.06:1 on --bg                           */
  --focus:            #f5f2ec;   /* 17.38:1 on --bg                           */

  --region:           #212128;   /* unvisited fill, just above the page       */
  --region-line:      #35353c;   /* hairline between unvisited regions        */
  --region-line-vis:  #b9d6d4;   /* heavier hairline on visited regions       */
  --hatch:            #a6cbc9;   /* diagonal rule inside visited regions      */

  --serif: 'Playfair Display', Georgia, 'Times New Roman', serif;
  --sans:  'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;

  --gutter: clamp(1.5rem, 5vw, 5.5rem);
}


/* ═══════════════════════════════════════════════════════════════════════════
   Reset
   ═══════════════════════════════════════════════════════════════════════════ */

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

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

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font-family: var(--sans);
  font-weight: 300;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

h1, h2, p, ul { margin: 0; }
ul { padding: 0; list-style: none; }
img { display: block; max-width: 100%; }

.visually-hidden {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

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


/* ═══════════════════════════════════════════════════════════════════════════
   Section 1 — Hero
   ═══════════════════════════════════════════════════════════════════════════ */

.hero {
  position: relative;
  min-height: 100vh;
  min-height: 100svh;
  display: grid;
  grid-template-columns: 47fr 53fr;
  align-items: center;
}

/* Portrait ---------------------------------------------------------------- */

.hero__portrait {
  position: relative;
  height: 100%;
  overflow: hidden;
  /* Vertical dissolve. The horizontal dissolve lives on the <img> so that the
     two fades stack without needing mask-composite. */
  -webkit-mask-image: linear-gradient(to bottom, transparent 0%, #000 9%, #000 84%, transparent 100%);
          mask-image: linear-gradient(to bottom, transparent 0%, #000 9%, #000 84%, transparent 100%);
}

.hero__portrait img {
  /* Absolute so the photo's intrinsic 2:3 ratio cannot stretch the hero past
     one viewport on wide, short screens. */
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  /* The subject sits in the right-hand third of the frame, so when the column
     is narrow enough to crop horizontally we keep the right of the photo and
     throw away the empty black on the left. */
  object-position: 72% center;
  -webkit-mask-image: linear-gradient(to right, transparent 0%, #000 19%, #000 97%, transparent 100%);
          mask-image: linear-gradient(to right, transparent 0%, #000 19%, #000 97%, transparent 100%);
}

/* Masthead ---------------------------------------------------------------- */

.hero__text {
  padding-inline: clamp(1.5rem, 4vw, 4rem) var(--gutter);
}

.masthead {
  font-family: var(--serif);
  font-weight: 400;
  line-height: 1;
}

.masthead__given {
  display: block;
  font-style: italic;
  font-size: clamp(1.35rem, 2.7vw, 2.45rem);
  letter-spacing: 0.005em;
  color: var(--muted);
  margin-bottom: 0.15em;
}

.masthead__family {
  display: block;
  font-weight: 500;
  font-size: clamp(4rem, 12vw, 9rem);
  line-height: 0.85;
  letter-spacing: -0.017em;
  text-transform: uppercase;
  color: var(--text);
}

.tagline {
  margin-top: clamp(1.4rem, 3vh, 2.2rem);
  font-family: var(--sans);
  font-weight: 300;
  font-size: clamp(0.65rem, 1.05vw, 0.78rem);
  letter-spacing: 0.28em;
  text-transform: uppercase;
  color: var(--accent);
}

/* Scroll cue -------------------------------------------------------------- */

.scroll-cue {
  position: absolute;
  bottom: clamp(1.5rem, 4vh, 2.75rem);
  left: 50%;
  transform: translateX(-50%);
  display: block;
  width: 26px;
  color: var(--muted);
  opacity: 0.5;
  transition: opacity 0.3s ease;
  animation: cue 3.4s ease-in-out infinite;
}
.scroll-cue:hover,
.scroll-cue:focus-visible { opacity: 1; }
.scroll-cue svg { width: 100%; height: auto; display: block; }

@keyframes cue {
  0%, 100% { transform: translate(-50%, 0); }
  50%      { transform: translate(-50%, 5px); }
}


/* ═══════════════════════════════════════════════════════════════════════════
   Section 2 — Travel map
   ═══════════════════════════════════════════════════════════════════════════ */

.travels {
  max-width: 1180px;
  margin-inline: auto;
  padding: clamp(5rem, 14vh, 9.5rem) var(--gutter) clamp(5rem, 12vh, 8rem);
}

.travels__head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: 1.5rem 2rem;
  padding-bottom: 1.4rem;
  border-bottom: 1px solid #24242a;
}

.travels__title {
  font-family: var(--serif);
  font-weight: 400;
  font-size: clamp(2rem, 4.4vw, 3.1rem);
  line-height: 1;
  letter-spacing: -0.01em;
  color: var(--text);
}

/* Tabs -------------------------------------------------------------------- */

.tabs { display: flex; gap: clamp(1.25rem, 3vw, 2.5rem); }

.tab {
  appearance: none;
  background: none;
  border: 0;
  padding: 0.35rem 0;
  font-family: var(--sans);
  font-weight: 300;
  font-size: 0.7rem;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--muted);
  cursor: pointer;
  border-bottom: 1px solid transparent;
  transition: color 0.2s ease, border-color 0.2s ease;
}
.tab:hover { color: var(--text); }
.tab[aria-selected='true'] {
  color: var(--text);
  border-bottom-color: var(--accent);
}

/* Tally ------------------------------------------------------------------- */

.tally {
  font-size: 0.7rem;
  font-weight: 300;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--muted);
}

/* Toolbar: count on the left, zoom controls on the right ------------------- */

.map-toolbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 0.9rem 1.5rem;
  margin-top: 1.75rem;
}

.zoom {
  display: flex;
  align-items: center;
  gap: 0.5rem;
}

.zoom__btn {
  appearance: none;
  min-width: 32px;
  height: 32px;
  padding: 0 0.5rem;
  background: none;
  border: 1px solid var(--region-line);
  color: var(--muted);
  font-family: var(--sans);
  font-size: 0.85rem;
  font-weight: 300;
  line-height: 1;
  cursor: pointer;
  transition: color 0.2s ease, border-color 0.2s ease;
}

.zoom__btn--reset {
  font-size: 0.62rem;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  padding: 0 0.75rem;
}

.zoom__btn:hover:not(:disabled) {
  color: var(--text);
  border-color: var(--muted);
}

.zoom__btn:disabled {
  opacity: 0.35;
  cursor: default;
}

/* Map --------------------------------------------------------------------- */

.map-holder {
  margin-top: clamp(1.5rem, 4vh, 2.5rem);
  min-height: 180px;
}

.map-holder svg {
  width: 100%;
  height: auto;
  display: block;
  overflow: hidden;         /* keep zoomed geography inside the map's box */
  touch-action: pan-y;      /* at full view, a one-finger drag scrolls the page */
}

/* Once zoomed in, the map takes over touch so one finger can pan it. Reset
   returns it to the state above, so the page can never trap your scroll. */
.map-holder svg.is-zoomed {
  touch-action: none;
  cursor: grab;
}
.map-holder svg.is-zoomed:active { cursor: grabbing; }

.zoom-layer.is-animating {
  transition: transform 0.28s cubic-bezier(0.4, 0, 0.2, 1);
}

.region {
  fill: var(--region);
  stroke: var(--region-line);
  /* non-scaling-stroke keeps borders a true hairline at every zoom level and
     at every screen width, instead of thickening 8x when zoomed in. */
  vector-effect: non-scaling-stroke;
  stroke-width: 0.6;
  stroke-linejoin: round;
  cursor: pointer;
  outline: none;
  transition: fill 0.2s ease, filter 0.2s ease;
}
.region[data-visited='true'] {
  stroke: var(--region-line-vis);
  stroke-width: 1.1;
}
.region:hover { filter: brightness(1.45); }

/* Visited regions are filled with this hatch pattern, so "visited" is legible
   as texture as well as colour. */
.hatch-bg   { fill: var(--accent); }
.hatch-line { stroke: var(--hatch); stroke-width: 2; }

/* Focus and selection are drawn as overlay paths so that a highlighted region
   is never overdrawn by its neighbours. */
.ring {
  fill: none;
  pointer-events: none;
  stroke-linejoin: round;
  vector-effect: non-scaling-stroke;
}
.ring--focus-under { stroke: var(--bg);    stroke-width: 4;   }
.ring--focus       { stroke: var(--focus); stroke-width: 2;   }
.ring--select      { stroke: var(--text);  stroke-width: 1.4; }

/* Region label ------------------------------------------------------------ */

.region-label {
  min-height: 3.4em;
  margin-top: clamp(1.25rem, 3vh, 2rem);
  font-family: var(--serif);
  font-style: italic;
  font-size: clamp(1.15rem, 2.6vw, 1.8rem);
  line-height: 1.3;
  color: var(--text);
}

/* Legend ------------------------------------------------------------------ */

.legend {
  display: flex;
  gap: 1.75rem;
  margin-top: 0.5rem;
  font-size: 0.66rem;
  font-weight: 300;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--muted);
}
.legend li { display: flex; align-items: center; gap: 0.6rem; }

.legend__swatch {
  width: 13px;
  height: 13px;
  flex: none;
  border: 1px solid var(--region-line);
}
.legend__swatch--visited {
  background-color: var(--accent);
  background-image: repeating-linear-gradient(
    45deg, transparent 0 2px, var(--hatch) 2px 3px
  );
  border-color: var(--region-line-vis);
}
.legend__swatch--unvisited { background: var(--region); }

/* Error notice ------------------------------------------------------------ */

.map-error {
  margin-top: 1.5rem;
  padding: 1rem 1.25rem;
  border-left: 2px solid var(--accent);
  font-size: 0.85rem;
  line-height: 1.6;
  color: var(--muted);
}
.map-error code {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  color: var(--text);
}


/* ═══════════════════════════════════════════════════════════════════════════
   Responsive — below 768px the hero stacks
   ═══════════════════════════════════════════════════════════════════════════ */

@media (max-width: 767px) {
  .hero {
    grid-template-columns: 1fr;
    grid-template-rows: auto 1fr;
    align-items: start;
    padding-top: clamp(1rem, 3vh, 2rem);
  }

  /* A narrow column pinned to the left edge. Because it is taller than the
     photo's own 2:3 shape, `cover` now crops horizontally — and pinning
     object-position to the right of the frame throws away the empty black
     margin on the left of the photo, which is what carries the subject in
     from the right-hand edge to the left third of the screen. */
  .hero__portrait {
    width: 54%;
    justify-self: start;
    height: 54svh;
    min-height: 260px;
    -webkit-mask-image: linear-gradient(to bottom, transparent 0%, #000 9%, #000 78%, transparent 99%);
            mask-image: linear-gradient(to bottom, transparent 0%, #000 9%, #000 78%, transparent 99%);
  }

  .hero__portrait img {
    object-position: 100% center;
    -webkit-mask-image: linear-gradient(to right, transparent 0%, #000 9%, #000 84%, transparent 100%);
            mask-image: linear-gradient(to right, transparent 0%, #000 9%, #000 84%, transparent 100%);
  }

  .hero__text {
    padding-inline: var(--gutter);
    /* Sit in the middle of what the photo leaves, rather than piling up under
       it with a dead gap at the bottom of the screen. */
    align-self: center;
    text-align: right;
  }

  .travels__head { flex-direction: column; align-items: flex-start; }
}

/* Tablet portrait: the photo column is tall and narrow here, which crops the
   photo hard. Give it more width so the face is never pushed against the edge. */
@media (min-width: 768px) and (max-width: 1023px) {
  .hero { grid-template-columns: 55fr 45fr; }
  .hero__portrait img { object-position: 68% center; }
}

/* Phones held sideways: too short to stack. Put the photo in a left-hand column
   and the type beside it, keeping the same photo-left / name-right reading. */
@media (max-width: 767px) and (max-height: 620px) {
  .hero {
    grid-template-columns: 42fr 58fr;
    grid-template-rows: 1fr;
    align-items: center;
    padding-top: 0;
  }

  .hero__portrait {
    width: 100%;
    height: 100%;
    min-height: 0;
    -webkit-mask-image: linear-gradient(to bottom, transparent 0%, #000 10%, #000 84%, transparent 100%);
            mask-image: linear-gradient(to bottom, transparent 0%, #000 10%, #000 84%, transparent 100%);
  }
}


/* ═══════════════════════════════════════════════════════════════════════════
   Reduced motion
   ═══════════════════════════════════════════════════════════════════════════ */

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
  }
  .scroll-cue { animation: none; transform: translateX(-50%); }
  .zoom-layer.is-animating { transition: none; }
}

/* Windows High Contrast / forced colours */
@media (forced-colors: active) {
  .region { forced-color-adjust: none; }
  .ring--focus { stroke: Highlight; }
}
