/* Page shell: canvas, loader, the focus targets' buttons and the no-WebGL page. Hero: hero.css. */
*, *::before, *::after { box-sizing: border-box; }

html, body { margin: 0; height: 100%; }

body {
  background: var(--paper);
  color: var(--ink);
  font: 400 var(--text-body) / 1.6 var(--font-body);
  overflow: hidden;
  -webkit-font-smoothing: antialiased;
}

a { color: var(--terracotta-ink); }
:focus-visible { outline: none; box-shadow: var(--focus-ring); }
/* Buttons with their own shadow would hide the ring above, so it is restored for them. */
:is(.desk-back, .resume-back, .resume-print, .resume-switch button, .harbour-back, .hero-tab):focus-visible { box-shadow: var(--focus-ring); }

.sr-only, .profile {
  position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

.kicker {
  margin: 0 0 var(--space-3);
  font: 600 var(--text-kicker) / 1.4 var(--font-body);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-soft);
  text-wrap: balance; /* no orphaned word on the second line */
}

#stage { position: fixed; inset: 0; width: 100%; height: 100%; display: block; touch-action: manipulation; }
#stage.crossfade { animation: crossfade 200ms linear; }
@keyframes crossfade { from { opacity: 0.2; } to { opacity: 1; } }

/* The room recedes while the visitor is on the laptop screen. The grade pass draws the vignette
   on the high and mid tiers; this CSS one stands in on the low tier, which has no post. */
.vignette { display: none; }
html[data-quality="low"] .vignette {
  display: block;
  position: fixed; inset: 0; pointer-events: none; opacity: 0;
  background: radial-gradient(ellipse at center, transparent 50%, rgba(74, 42, 24, 0.4));
  transition: opacity var(--dur-zoom) var(--ease-in-out);
}
html:not([data-view="desk"]) .vignette { opacity: 1; }


/* Keyboard path to each object: an invisible button over it. Keyboard focus shows the object's own
   hover (rim light on the laptop and pen, the resume's lift) and a small labelled pill on it;
   never a box round the button's area. */
.hotspot {
  position: fixed; padding: 0; border: 0; background: transparent; pointer-events: none;
}
.hotspot:focus, .hotspot:focus-visible { outline: none; box-shadow: none; }
.hotspot:focus-visible::after {
  content: attr(aria-label); position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%); /* on the object */
  padding: 3px 12px; border-radius: var(--radius-pill); background: var(--paper-raised); color: var(--ink);
  font: 500 var(--text-small) / 1.4 var(--font-body); white-space: nowrap; box-shadow: var(--focus-ring), var(--shadow-1);
}
#pen-hotspot:focus-visible::after { content: none; } /* the pen shows its own hover caption instead */
.hotspot[hidden] { display: none; }

.loader {
  position: fixed; inset: 0; display: grid; place-content: center; justify-items: center;
  gap: var(--space-4); background: var(--paper);
  transition: opacity var(--dur-slow) var(--ease-out), visibility 0s linear var(--dur-slow);
}
/* The gold line draws back and forth: DESIGN.md asks for no percentage bar. */
.loader-line { width: 120px; height: 1px; background: var(--gold); transform-origin: left; animation: draw 1.2s var(--ease-in-out) infinite alternate; }
@keyframes draw { from { transform: scaleX(0.1); } to { transform: scaleX(1); } }
html.is-loaded .loader { opacity: 0; visibility: hidden; }

/* Phones and portrait screens (DESIGN.md section 8): the scene on top, captions below it. */
@media (max-width: 767px), (orientation: portrait) {
  body { overflow-y: auto; }
  #stage { position: absolute; height: 60svh; }
}
/* A landscape screen too small for the hero zone stacks at the desk only (ui/hero.js): the laptop
   and the resume still zoom in on the full window. */
html.hero-stacked[data-view="desk"] body { overflow-y: auto; }
html.hero-stacked[data-view="desk"] #stage { position: absolute; height: 60svh; }

/* Without WebGL the profile is the page. */
html.no-webgl body { overflow: auto; }
html.no-webgl :is(#stage, .vignette, .hero-shade, .hero, .hero-tab, .hotspot, .desktop, .pen-chooser) { display: none; }
/* With 3D the profile is a screen-reader twin; a keyboard user tabbing into its links sees it. */
html:not(.no-webgl) .profile:focus-within {
  position: fixed; z-index: 20; inset: auto var(--gutter) var(--gutter); width: auto; height: auto;
  max-height: 70vh; margin: 0; padding: var(--space-6); overflow: auto; clip: auto; white-space: normal;
  background: var(--paper-raised); border-radius: var(--radius-card); box-shadow: var(--shadow-3);
}

html.no-webgl .profile {
  position: static; width: auto; height: auto; margin: 0 auto; overflow: visible; clip: auto; white-space: normal;
  max-width: calc(62ch + 2 * var(--gutter)); padding: var(--space-8) var(--gutter);
}
.profile h1 { margin: 0; font: 400 var(--text-h1) / 1.08 var(--font-display); letter-spacing: -0.015em; }
.profile h2 { margin: 0 0 var(--space-4); font: 400 var(--text-h2) / 1.15 var(--font-display); }
.profile h3 { margin: var(--space-5) 0 var(--space-2); font: 600 var(--text-h3) / 1.3 var(--font-body); }
.profile-section { padding: var(--space-7) 0; border-top: 1px solid var(--line); }
.profile header.profile-section { border-top: 0; padding-top: 0; }
.no-webgl-note { padding: var(--space-3) var(--space-4); background: var(--paper-deep); border-radius: var(--radius-control); font-size: var(--text-small); }
.profile .tags { display: flex; flex-wrap: wrap; gap: var(--space-2); padding: 0; list-style: none; }
.profile .tags li { padding: var(--space-1) var(--space-3); border: 1px solid var(--line); border-radius: var(--radius-pill); font-size: var(--text-small); }
.profile .proof { color: var(--ink-soft); font-size: var(--text-small); }

.reduced-motion *, .reduced-motion *::before, .reduced-motion *::after {
  animation-duration: 0s !important; animation-iteration-count: 1 !important;
  transition-duration: 0s !important; transition-delay: 0s !important;
}
.reduced-motion #stage.crossfade { animation-duration: 200ms !important; }
