/* Jiung Moon — personal portfolio. Single stylesheet, ocean/minimal theme. */

:root {
  --ink: #173344;
  --paper: #fbfdfd;
  --sky-base: #fbfdfd;
  --sky-ink: #173344;   /* text over the sky (header on Hero, hero copy); ocean.js updates */
  --sky-muted: #536b79;
  --sky-faint: #9fb0b8;
  --panel: #fff;
  --muted: #536b79;
  --line: #d5e2e7;
  --signal: #226b88;
  --signal-dark: #174e66;
  --max: 1080px;
}

* { box-sizing: border-box; }

html { scroll-behavior: auto; height: 100%; }

body {
  margin: 0;
  height: 100%;
  display: flex;
  flex-direction: column;
  overflow: hidden; /* the site is a book now — see .book below, not a scrolling page */
  color: var(--ink);
  background: var(--paper);
  font-family: Arial, sans-serif;
  font-size: 16px;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
}

/* All five sections use the same sheet, lighting and timing. */
.site-header { flex: 0 0 auto; }
.book {
  position: relative;
  flex: 1 1 auto;
  min-height: 0;
  overflow: hidden;
  isolation: isolate;
}
.book-sheet {
  position: absolute;
  inset: var(--book-header-height, 89px) 0 0;
  overflow-y: auto;
  background: var(--paper);
  -webkit-overflow-scrolling: touch;
}
/* Keep live pages below the live header even during a turn. The moving sheet
   is a separate body-level layer containing both header and page pixels. */
.book-sheet[hidden], .book > [hidden] { display: none !important; }   /* the second form holds before book.js runs */
/* <picture> only chooses a file (WebP first); layout belongs to its <img>. */
picture { display: contents; }
/* Case studies are a separate reading mode, outside the turning book. */
body.work-detail-open .site-header { visibility: hidden; pointer-events: none; }
body.work-detail-open #work.book-sheet { top: 0; }
body.work-detail-open #work .work-exhibition { padding-top: 28px; }
.book-sheet::-webkit-scrollbar { width: 10px; }
.book-sheet::-webkit-scrollbar-track { background: var(--paper); }
.book-sheet::-webkit-scrollbar-thumb { background: #c5c9cb; border: 2px solid var(--paper); border-radius: 8px; min-height: 24px; }
.book-scroll-track { position: absolute; right: 0; background: var(--paper); }
.book-scroll-thumb { position: absolute; width: 100%; background: #c5c9cb; border: 2px solid var(--paper); border-radius: 8px; }
.book-sheet.hero { display: flex; }
.book-sheet:focus { outline: none; }
.paper-turn { position: fixed; inset: 0; z-index: 10; pointer-events: none; }
.paper-mesh { position: absolute; inset: 0; width: 100%; height: 100%; will-change: opacity; }
.paper-snapshot { position: absolute; margin: 0; overflow: visible; background: var(--paper); pointer-events: none; }
.paper-snapshot.hero { display: flex; }
footer.book-sheet, footer.paper-snapshot { background: #143748; }
.ocean-art.ocean-surface { position: absolute; inset: 0; visibility: hidden; pointer-events: none; filter: none; opacity: 1; transform: none; -webkit-mask-image: none; mask-image: none; }
/* `inherit`, not `visible`: an explicit visible would override the outgoing
   sheet's visibility:hidden during a page turn, leaving the water painted over
   the incoming page until settle() hides the sheet (a visible bottom jump). */
.ocean-scene[data-ocean-ready] .ocean-surface { visibility: inherit; }
.ocean-scene[data-ocean-ready] img.ocean-art:not(.ocean-surface) { visibility: hidden; }
.nav-links a.is-active { color: #102d3c; }

a { color: inherit; }
a:focus-visible { outline: 2px solid var(--signal); outline-offset: 5px; }

.shell {
  width: min(calc(100% - 80px), var(--max));
  margin: 0 auto;
}

section { scroll-margin-top: 24px; }

/* header */

/* Above .paper-turn (z-index 10) so the live rule always wins; the header keeps a
   transparent 3px border so page geometry is unchanged. */
.top-rule { position: fixed; inset: 0 0 auto; height: 3px; background: var(--signal); z-index: 20; pointer-events: none; }
/* Below the turning layer on purpose: the header is part of each page's image, so
   it travels with the sheet instead of standing still while the page moves under a
   frozen bar. The cost is that the menu is a texture for the length of the turn and
   becomes live text again on arrival — the two cannot both be had, since a live DOM
   header cannot be bent onto the curling mesh. Only .top-rule is exempt: it is the
   same on every page, so keeping it live loses nothing. */
.site-header {
  position: absolute;
  inset: 0 0 auto;
  z-index: 5;
  background: var(--paper);
  border: 0;
  border-top: 3px solid transparent;
}
.site-header[data-page="hero"] { background: transparent; color: var(--sky-ink); text-shadow: 0 0 10px var(--sky-halo, transparent), 0 0 2px var(--sky-halo, transparent); }
.site-header[data-page="hero"] .brand,
.site-header[data-page="hero"] .nav-links a { color: var(--sky-muted); }
.site-header[data-page="hero"] .brand.is-active,
.site-header[data-page="hero"] .brand:hover,
.site-header[data-page="hero"] .brand:focus-visible,
.site-header[data-page="hero"] .nav-links a:hover,
.site-header[data-page="hero"] .nav-links a:focus-visible,
.site-header[data-page="hero"] .nav-links a.is-active { color: var(--sky-ink); }
/* Contact is the dark footer page; the header takes the same ground. */
.site-header[data-page="contact"] { background: #143748; }
.site-header[data-page="contact"] .brand,
.site-header[data-page="contact"] .nav-links a { color: #b7ced8; }
.site-header[data-page="contact"] .brand.is-active,
.site-header[data-page="contact"] .brand:hover,
.site-header[data-page="contact"] .brand:focus-visible,
.site-header[data-page="contact"] .nav-links a:hover,
.site-header[data-page="contact"] .nav-links a:focus-visible,
.site-header[data-page="contact"] .nav-links a.is-active { color: #fff; }

.nav {
  min-height: 86px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 28px;
}

/* Home reads as one of the menu items: same size and weight, and like them it
   only darkens when its own page is showing. */
.brand {
  /* inline-block, not inline-flex: the width-reserving ::before below must stack
     above the text, and inside a flex box it would sit beside it instead. The nav
     row centres it vertically anyway. */
  display: inline-block;
  padding: 12px 0;
  font-family: Arial, sans-serif;
  font-size: 0.875rem;
  font-weight: 400;
  color: var(--muted);
  text-decoration: none;
}
.brand.is-active { color: #102d3c; }
.brand:hover, .brand:focus-visible { color: var(--ink); }

.nav-links { display: flex; align-items: center; gap: 28px; font-size: 0.875rem; }
.nav-links a {
  position: relative;
  color: var(--muted);
  font-weight: 400;
  text-decoration: none;
  padding: 12px 0;
  transition: none;
}
/* Selection must never move the row. Each item carries an invisible bold copy of
   its own label, laid out at zero height, so the item is always as wide as its
   bold state and switching weight changes nothing but the ink. The visible text is
   centred in that width, so it does not drift either. */
.nav-links a, .brand { text-align: center; }
.nav-links a::before, .brand::before {
  content: attr(data-label);
  display: block;
  height: 0;
  overflow: hidden;
  visibility: hidden;
  font-weight: 700;
}
.nav-links a.is-active, .brand.is-active { font-weight: 700; }
.nav-links a::after { content: none; }
@media (prefers-reduced-motion: reduce) {
  .nav-links a, .nav-links a::after { transition: none; }
}
.nav-links a:hover, .nav-links a:focus-visible { color: var(--ink); }
.nav-links { margin-left: auto; }
/* Music: a circular control in the sky's ink, sized to the 28px line box of the
   city line above it so it reads as part of that column. Play shows a triangle;
   while playing three bars breathe and a click stops. It opens the card below. */
.player { display: flex; align-items: center; margin-top: 12px; }
.player[hidden] { display: none; }
.player-button { position: relative; width: 28px; height: 28px; padding: 0; border: 1px solid currentColor; border-radius: 50%; background: none; color: var(--sky-ink); cursor: pointer; display: grid; place-items: center; opacity: .72; transition: opacity .25s, transform .25s; }
/* The circle is smaller than a comfortable touch target, so the hit area is
   extended invisibly to 44px rather than drawing a bigger button. */
.player-button::after { content: ''; position: absolute; inset: -8px; border-radius: 50%; }
.player-button:hover, .player-button:focus-visible { opacity: 1; transform: scale(1.05); }
.player-button:focus-visible { outline: 2px solid var(--signal); outline-offset: 4px; }
.player-button:disabled { cursor: default; opacity: .5; }
.player-button:disabled:hover { transform: none; opacity: .5; }
.player-button svg, .player-bars { grid-area: 1 / 1; width: 12px; height: 12px; fill: currentColor; }
.player-button .player-stop, .player-button .player-bars { opacity: 0; transition: opacity .25s; }
.player-button.is-playing .player-play { opacity: 0; }
.player-button.is-playing .player-bars { opacity: 1; }
.player-button.is-playing:hover .player-bars, .player-button.is-playing:focus-visible .player-bars { opacity: 0; }
.player-button.is-playing:hover .player-stop, .player-button.is-playing:focus-visible .player-stop { opacity: 1; }
.player-bars { display: flex; align-items: flex-end; justify-content: center; gap: 2px; }
.player-bars i { display: block; width: 2px; height: 5px; background: currentColor; border-radius: 1px; animation: player-bar 1.1s ease-in-out infinite; }
.player-bars i:nth-child(2) { animation-delay: .18s; height: 9px; }
.player-bars i:nth-child(3) { animation-delay: .36s; height: 7px; }
@keyframes player-bar { 0%, 100% { transform: scaleY(.45); } 50% { transform: scaleY(1); } }

/* The Spotify card is a disclosure: collapsed until the control opens it. Spotify
   renders its own chrome, so this only sets the width and the open/closed height. */
/* 280x80 is the smallest the embed renders without its own scrollbars: measured
   274px wide and 72px tall both overflow. */
.hero-music { max-width: 280px; max-height: 0; margin: 0; opacity: 0; overflow: hidden; pointer-events: none; border-radius: 12px; transition: max-height .45s ease, opacity .3s ease, margin-top .45s ease; }
.hero-music.is-open { max-height: 88px; margin-top: 12px; opacity: 1; pointer-events: auto; }
.hero-music[hidden] { display: none; }
.hero-music iframe { display: block; border: 0; }
/* A turning sheet is rasterised and an iframe cannot be drawn into that raster,
   so the card is hidden for the turn rather than flashing an empty box. */
.book.is-page-turning .hero-music { opacity: 0; }
.paper-snapshot .hero-music { display: none; }
@media (prefers-reduced-motion: reduce) { .player-bars i { animation: none; } .player-button, .hero-music { transition: none; } }

/* shared small-label typography */

.section-kicker, .signal-label, .reasoning-map small,
.principle .number {
  font-family: Arial, sans-serif;
  font-size: 0.75rem;
  font-weight: 400;
  letter-spacing: 0.03em;
}

.section-kicker {
  margin: 0 0 20px;
  color: var(--signal);
  text-transform: uppercase;
}

/* hero */

/* pg1 + pg2: one ordinary-looking screen. The sky/water surface covers the
   whole sheet (including the strip under the transparent menu bar) and the
   intro text sits over it, so the sky is never split into a flat CSS band
   above a rendered band below. The sheet starts at the top of the book so
   the surface runs under the menu; the padding keeps the intro clear of it. */
.hero {
  background: var(--sky-base);
  width: 100%;
  padding: calc(var(--book-header-height, 89px) + 56px) 0 0;
  display: flex;
  flex-direction: column;
}
.book-sheet.hero { top: 0; }

.hero-introduction {
  position: relative;
  z-index: 1;
  width: min(calc(100% - 80px), var(--max));
  margin: 0 auto;
}

.hero { color: var(--sky-ink); }
/* Only visible while the sky passes through mid-tone at dusk and dawn; ocean.js
   sets --sky-halo transparent the rest of the time. */
.hero-introduction { text-shadow: 0 0 10px var(--sky-halo, transparent), 0 0 2px var(--sky-halo, transparent); }
.hero h1 {
  margin: 0;
  font-family: Arial, sans-serif;
  font-size: clamp(2rem, 3.2vw, 2.75rem);
  font-weight: 700;
  line-height: 1.2;
  letter-spacing: -0.035em;
}

.hero-copy {
  margin: 18px 0 0;
  max-width: 460px;
  color: var(--sky-muted);
  font-size: 1rem;
  line-height: 1.75;
}
/* The two cities: same line height and size as the copy above. Hover or focus
   swaps a name for that city's local time in the same cell (no reflow) and
   reveals a one-line detail below; the chosen city is in ink, the other faint. */
.hero-place { position: relative; margin: 0; display: flex; align-items: center; gap: 0.45em; font-size: 1rem; line-height: 1.75; color: var(--sky-faint); }
.place-dot { color: var(--sky-faint); }
.place { display: inline-grid; padding: 0; margin: 0; border: 0; background: none; font: inherit; color: var(--sky-faint); cursor: pointer; transition: color .25s; }
.place > span { grid-area: 1 / 1; white-space: nowrap; transition: opacity .18s ease; }
.place-time { opacity: 0; font-variant-numeric: tabular-nums; letter-spacing: .05em; }
/* Hover only where there is a real hover (a mouse); a tap on a touch screen
   left the time showing until the next tap elsewhere. Keyboard focus keeps it. */
.place:focus-visible .place-name { opacity: 0; }
.place:focus-visible .place-time { opacity: 1; }
.place[aria-pressed="true"], .place:focus-visible { color: var(--sky-ink); }
@media (hover: hover) and (pointer: fine) {
  .place:hover .place-name { opacity: 0; }
  .place:hover .place-time { opacity: 1; }
  .place:hover { color: var(--sky-ink); }
}
.place:focus-visible { outline: 2px solid var(--signal); outline-offset: 4px; }
.place-detail { position: absolute; left: 0; top: 100%; white-space: nowrap; font-size: 0.8125rem; line-height: 1.4; color: var(--sky-muted); opacity: 0; transform: translateY(-2px); transition: opacity .18s cubic-bezier(0.23, 1, 0.32, 1), transform .18s cubic-bezier(0.23, 1, 0.32, 1); pointer-events: none; }
.place-detail.is-on { opacity: 1; transform: none; }
@media (prefers-reduced-motion: reduce) { .place, .place > span, .place-detail { transition: none; } }


.ocean-scene {
  position: absolute;
  inset: 0;
  overflow: hidden;
}

.ocean-art {
  -webkit-mask-image: linear-gradient(to bottom, transparent, #000 100px);
  mask-image: linear-gradient(to bottom, transparent, #000 100px);
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center 46%;
  opacity: 0.8;
  filter: saturate(1.15) brightness(1.05);
  transform: scale(1.1);
}

/* sections */

.work, .method, .about { padding: 78px 0; }

.section-heading { display: grid; grid-template-columns: 1.2fr 1fr; gap: 80px; align-items: end; margin-bottom: 42px; }

h2 {
  margin: 0;
  max-width: 700px;
  font-family: Arial, sans-serif;
  font-size: clamp(2rem, 3.6vw, 3.1rem);
  font-weight: 400;
  line-height: 1.15;
  letter-spacing: -0.045em;
}

.section-intro { margin: 0; max-width: 420px; color: var(--muted); font-size: 1rem; line-height: 1.75; }

/* Work: an index followed by seven complete, quiet case studies. */
.work-index { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 48px; margin: 0 0 84px; scroll-margin-top: 28px; }
.work-index:focus-visible { outline: 1px solid var(--signal); outline-offset: 6px; }
@media (max-width: 760px) {
  .work-index { grid-template-columns: minmax(0, 1fr); gap: 32px; margin-bottom: 56px; }
}

/* method */

.reasoning-map {
  max-width: 640px;
  margin: 0 auto 56px;
  padding: 26px 32px;
  border: 1px solid var(--line);
  background: #f0f6f8;
}

.signal-label { display: flex; align-items: center; justify-content: space-between; margin-bottom: 30px; gap: 12px; line-height: 1.5; }
.signal-label span:last-child { color: var(--muted); }

.question-node { text-align: left; }
.question-node strong { display: block; margin-top: 10px; font-family: Arial, sans-serif; font-size: 1.65rem; font-weight: 400; line-height: 1.25; letter-spacing: -0.03em; }

.branch-line { position: relative; width: 67%; height: 24px; margin: 23px auto 0; border: 1px solid #89b2c4; border-bottom: 0; border-radius: 0; }
.branch-line::before { content: ""; position: absolute; left: 50%; top: -12px; height: 35px; border-left: 1px solid #89b2c4; }

.question-branches { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 12px; }
.question-branches div { padding: 11px 0 0; border-top: 2px solid var(--signal); text-align: left; }
.question-branches span { display: block; font-size: 0.875rem; font-weight: 500; }
.question-branches small { display: block; margin-top: 5px; font-size: 0.75rem; line-height: 1.35; }

.map-caption { margin: 18px 0; text-align: left; font-size: 0.75rem; line-height: 1.5; color: var(--muted); }

.framework-node, .analysis-node { padding: 16px 0; border-top: 1px solid var(--line); }
.framework-node p, .analysis-node p { margin: 6px 0 0; font-size: 0.9375rem; line-height: 1.5; font-weight: 400; }
.analysis-node span { display: block; margin-top: 7px; color: var(--muted); font-size: 0.875rem; line-height: 1.5; }

.signal-output { padding: 18px 0 0; border-top: 1px solid var(--ink); }
.signal-output span { color: var(--signal); font-size: 0.75rem; font-weight: 400; letter-spacing: 0.03em; }
.signal-output strong { display: block; margin-top: 10px; font-family: Arial, sans-serif; font-size: 1.125rem; font-weight: 400; line-height: 1.5; }

.method-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 30px; }
.principle { padding: 20px 0 0; border-top: 1px solid var(--line); }
.principle h3 { margin: 28px 0 12px; font-family: Arial, sans-serif; font-size: 1.25rem; font-weight: 400; line-height: 1.3; }
.principle p { margin: 0; color: var(--muted); font-size: 1rem; line-height: 1.5; }

/* about */

.portrait-frame {
  display: inline-block;
  margin: 0 0 36px;
}

.about-portrait {
  display: block;
  width: min(100%, 420px);
  height: auto;
  aspect-ratio: 4 / 5;
  object-fit: cover;
  object-position: center;
  margin: 0;
  /* Museum mat + frame: a thin inner border, a paper-colored mat, then a
     thin outer border — like a photograph mounted and framed on a gallery wall. */
  box-shadow:
    0 0 0 1px var(--line),
    0 0 0 10px var(--panel),
    0 0 0 11px var(--line);
}

.portrait-caption {
  margin: 18px 2px 0;
  max-width: 420px;
  color: var(--muted);
  font-size: 0.75rem;
  font-weight: 400;
  letter-spacing: 0.03em;
  text-transform: uppercase;
}

.about-layout { display: grid; grid-template-columns: minmax(0, 1.15fr) minmax(320px, 0.85fr); gap: 80px; }
.about-copy { margin: 0; color: var(--ink); font-family: Arial, sans-serif; font-size: 2rem; font-weight: 400; line-height: 1.4; letter-spacing: -0.03em; }
.about-detail { margin: 0; color: var(--muted); }
.about-detail.coastal-background { margin-top: 20px; }

.education { display: grid; grid-template-columns: 1fr auto; gap: 16px; margin-top: 32px; padding-top: 22px; border-top: 1px solid var(--line); }
.education strong { display: block; }
.education span { color: var(--muted); font-size: 0.85rem; }
.education .education-degree-line { display: block; line-height: 1.65; }

/* A single gallery wall: source order and aspect ratios are preserved. */
.art-collection { padding: clamp(20px, 4vh, 48px) 0; }
.collection-room { height: 100%; display: flex; flex-direction: column; min-height: 0; }
.collection-heading { flex: 0 0 auto; }
.collection-heading h2 { margin: 0; font-size: clamp(1.5rem, 3vw, 2.5rem); }
/* The wall's own scrollbar is replaced by .collection-bar (collection.js). A
   native scrollbar cannot be carried into the page-turn snapshot — the copy
   always showed its thumb at the start, so it jumped at every turn from a
   scrolled wall. The bar is an ordinary element, so the snapshot copies it. */
.collection-wall { --art-height: min(42vh, 420px); flex: 1; min-height: 0; overflow-x: auto; overflow-y: hidden; padding: 24px 0; overscroll-behavior-x: contain; scrollbar-width: none; }
.collection-wall::-webkit-scrollbar { display: none; }
.collection-bar { flex: 0 0 auto; position: relative; height: 11px; margin-top: 2px; cursor: pointer; touch-action: none; }
.collection-bar::before { content: ""; position: absolute; left: 0; right: 0; top: 5px; height: 1px; background: var(--line); }
.collection-thumb { position: absolute; left: 0; top: 3px; height: 5px; border-radius: 3px; background: #c5c9cb; cursor: grab; }
.collection-thumb:active { cursor: grabbing; }
.collection-bar[hidden] { display: none; }
.collection-track { display: flex; align-items: flex-start; gap: clamp(160px, 22vw, 360px); width: max-content; min-height: 100%; padding: 16px clamp(48px, 8vw, 120px); }
.collection-wall figure { flex: 0 0 auto; width: calc(var(--art-height) * .806); margin: 0; }
.collection-wall figure:nth-child(2) { width: calc(var(--art-height) * .5625); }
.collection-wall figure:nth-child(3) { width: calc(var(--art-height) * 1.515); }
.collection-wall img { display: block; width: 100%; height: var(--art-height); object-fit: contain; }
.collection-wall figcaption { margin-top: 18px; line-height: 1.5; }
.collection-wall strong, .collection-wall span, .collection-wall small { display: block; }
.collection-wall strong { font-weight: 400; font-size: .875rem; }
.collection-wall span, .collection-wall small { color: var(--muted); font-size: .8125rem; }
.collection-wall small { margin-top: 4px; }
@media(max-width: 600px) {
  .collection-wall { --art-height: min(35vh, 300px); }
  .collection-track { gap: 120px; padding-inline: 24px; }
  .collection-wall figcaption { margin-top: 12px; }
  .collection-wall strong, .collection-wall span, .collection-wall small { font-size: .75rem; overflow-wrap: anywhere; }
  .nav-links { gap: 10px; }
}

/* footer */

footer { display: flex; align-items: center; min-height: 100%; padding: 56px 0 30px; background: #143748; color: white; }
/* The book already reserves the header above each sheet. A 100% minimum
   makes the live footer one header taller than its captured page viewport. */
footer.book-sheet, footer.paper-snapshot { min-height: 0; }
.footer-grid { display: grid; grid-template-columns: 1.4fr 0.6fr; gap: 60px; align-items: end; }
footer h2 { max-width: 760px; color: #fff; }

.contact-links { display: grid; gap: 12px; }
.contact-links a { display: flex; justify-content: space-between; padding: 12px 0; border-bottom: 1px solid #416371; color: #dfe4e3; text-decoration: none; }
.contact-links a:hover { color: #b8e1ee; border-color: var(--signal); }
/* Drawn, not typed: "↗" is not in Arial, so each device borrowed it from a
   different fallback font (and some drew it as an emoji). The path copies the
   desktop fallback's shape — a thin shaft with an open head — at 0.75em. */
.link-arrow { flex: 0 0 auto; width: .75em; height: .75em; fill: none; stroke: currentColor; stroke-width: 1; stroke-linecap: round; stroke-linejoin: round; }

.footer-note { display: flex; justify-content: space-between; gap: 20px; margin-top: 44px; padding-top: 20px; border-top: 1px solid #416371; color: #b7ced8; font-size: 0.76rem; }

/* Today's sunrise and sunset, where "Let's talk." used to be. Four numerals at
   rest; the one pointed at comes forward and the city and event that name it fade
   in. The label cells keep their space while transparent, so revealing the words
   moves nothing — the same reason the nav carries an invisible bold copy of each
   menu item. Timing matches .place-detail in the hero. */
.sun { font-family: Arial, sans-serif; display: inline-block; }
.sun:focus-visible { outline: 2px solid var(--signal); outline-offset: 10px; }
.sun-grid {
  display: grid;
  grid-template-columns: max-content repeat(2, max-content);
  column-gap: clamp(30px, 4.5vw, 68px);
  row-gap: 16px;
  align-items: baseline;
  justify-items: center;          /* a time sits under the middle of its city */
}
.sun-cell { white-space: nowrap; }
.sun time { font-variant-numeric: tabular-nums; font-size: 0.8125rem; letter-spacing: .1em; line-height: 1; color: #8fabb9; transition: color .18s ease; }
.sun-key { font-size: 0.6875rem; letter-spacing: .2em; line-height: 1; color: #55788a; white-space: nowrap; opacity: 0; transition: opacity .18s cubic-bezier(0.23, 1, 0.32, 1); }
.sun-key[data-row] { justify-self: end; }   /* the two words hug the numerals */
.sun[data-col="0"] .sun-key[data-col="0"],
.sun[data-col="1"] .sun-key[data-col="1"],
.sun[data-row="0"] .sun-key[data-row="0"],
.sun[data-row="1"] .sun-key[data-row="1"] { opacity: 1; }
.sun[data-col="0"][data-row="0"] .sun-cell[data-col="0"][data-row="0"] time,
.sun[data-col="1"][data-row="0"] .sun-cell[data-col="1"][data-row="0"] time,
.sun[data-col="0"][data-row="1"] .sun-cell[data-col="0"][data-row="1"] time,
.sun[data-col="1"][data-row="1"] .sun-cell[data-col="1"][data-row="1"] time { color: #fff; }
/* A page turn rasterises the sheet; a half-faded label would be baked into the
   snapshot, so the captured copy is always shown at rest. */
.paper-snapshot .sun-key { opacity: 0; }
.paper-snapshot .sun time { color: #8fabb9; }
@media (prefers-reduced-motion: reduce) { .sun-key, .sun time { transition: none; } }

/* responsive */

@media (max-width: 950px) {
  .hero-introduction { display: block; }
  .section-heading { grid-template-columns: 1fr; gap: 20px; }
  .method-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .about-layout { grid-template-columns: 1fr; gap: 44px; }
}

@media (max-width: 760px) {
  .shell, .hero-introduction { width: calc(100% - 40px); }
  /* Touch screens have no hover: a tap on a numeral lights it and its two words
     instead (contact-sun.js), and a tap anywhere else puts them out again. */
  .sun-grid { column-gap: 22px; }
  .sun-cell { cursor: pointer; -webkit-tap-highlight-color: transparent; }
  .nav { flex-wrap: wrap; gap: 4px 20px; padding: 18px 0 10px; min-height: 0; }
  .nav-links { width: 100%; justify-content: space-between; gap: 8px; }
  .nav-links a { min-height: 44px; display: block; font-size: 0.875rem; }
  .hero h1 { font-size: clamp(2rem, 8vw, 2.75rem); }
  .hero-copy { max-width: 100%; }
  .work, .method, .about { padding: 54px 0; }
  .section-heading { margin-bottom: 30px; }
  .method-grid { grid-template-columns: 1fr; }
  .reasoning-map { width: 100%; padding: 22px 16px; }
  .question-branches { gap: 8px; }
  .about-copy { font-size: 1.65rem; }
  .education { grid-template-columns: minmax(0, 1fr); gap: 10px; }
  .footer-grid { gap: 28px; }
  .contact-links a { min-height: 48px; align-items: center; }
  .footer-note { flex-direction: column; gap: 8px; }
}

@media (max-width: 380px) {
  .shell, .hero-introduction { width: calc(100% - 32px); }
  .question-branches { grid-template-columns: minmax(0, 1fr); gap: 12px; }
  .question-branches div { text-align: left; padding: 10px 0 0 12px; border-top: 0; border-left: 2px solid var(--signal); }
  .branch-line { display: none; }
}

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
}

/* Page arrows (book.js). At rest a hairline chevron at 30% sits at each side
   edge, and only while the pointer is within reach of that edge. Hover or focus
   lights it: full ink, the neighbouring page's name, and a soft light behind it
   like a lamp over a label. Pressing swells the light once; the arrow keys
   flash the matching side. On Hero the colours follow the sky (ocean.js). */
.page-arrows { --arrow-ink: var(--ink); --arrow-light: rgba(34, 107, 136, .12); }
.page-arrows[data-page="hero"] { --arrow-ink: var(--sky-ink); --arrow-light: var(--sky-glow, rgba(255, 250, 240, .55)); }
.page-arrow { position: fixed; top: 50%; z-index: 11; display: flex; align-items: center; gap: 10px; height: 96px; padding: 0 14px; border: 0; background: none; color: var(--arrow-ink); font: inherit; cursor: pointer; opacity: 0; pointer-events: none; transform: translateY(-50%); transition: opacity .35s ease; }
/* Invisible arrows must not catch clicks meant for the page or its scrollbar. */
.page-arrows.near-prev .page-arrow--prev, .page-arrows.near-next .page-arrow--next, .page-arrow:focus-visible { pointer-events: auto; }
.page-arrow[hidden] { display: none; }
.page-arrow--prev { left: 6px; }
.page-arrow--next { right: 16px; flex-direction: row-reverse; }  /* clears the sheet's 10px scrollbar */
.page-arrows.near-prev .page-arrow--prev, .page-arrows.near-next .page-arrow--next { opacity: .3; }
.page-arrows .page-arrow:hover, .page-arrows .page-arrow:focus-visible, .page-arrows .page-arrow.is-flash { opacity: 1; }  /* outranks the .near-* rest state */
.page-arrow:focus-visible { outline: 1px solid var(--arrow-ink); outline-offset: -6px; }
.page-arrow-glyph { position: relative; width: 12px; height: 24px; fill: none; stroke: currentColor; stroke-width: 1; stroke-linecap: round; stroke-linejoin: round; transition: stroke-width .35s ease; }
.page-arrow:hover .page-arrow-glyph, .page-arrow:focus-visible .page-arrow-glyph, .page-arrow.is-flash .page-arrow-glyph { stroke-width: 1.4; }
.page-arrow-label { position: relative; font-size: .8125rem; letter-spacing: .02em; white-space: nowrap; opacity: 0; transform: translateX(var(--label-shift, 4px)); transition: opacity .35s ease, transform .35s ease; }
.page-arrow--prev .page-arrow-label { --label-shift: -4px; }
.page-arrow:hover .page-arrow-label, .page-arrow:focus-visible .page-arrow-label, .page-arrow.is-flash .page-arrow-label { opacity: 1; transform: none; }
.page-arrow-light { position: absolute; left: 50%; top: 50%; width: 140px; height: 140px; margin: -70px 0 0 -70px; border-radius: 50%; background: radial-gradient(circle, var(--arrow-light) 0%, transparent 68%); opacity: 0; transform: scale(.8); transition: opacity .45s ease, transform .45s ease; pointer-events: none; }
.page-arrow--prev .page-arrow-light { left: 20px; }
.page-arrow--next .page-arrow-light { left: auto; right: -50px; }
.page-arrow:hover .page-arrow-light, .page-arrow:focus-visible .page-arrow-light, .page-arrow.is-flash .page-arrow-light { opacity: 1; transform: scale(1); }
.page-arrow.is-pressed .page-arrow-light { opacity: 1; transform: scale(1.3); transition-duration: .2s; }
body.work-detail-open .page-arrows { display: none; }
@media (hover: none), (max-width: 760px) { .page-arrows { display: none; } }
@media (prefers-reduced-motion: reduce) { .page-arrow, .page-arrow-label, .page-arrow-light, .page-arrow-glyph { transition: none; } }
