/* ───────────────────────────────────────────────────────────
   Oliver — homepage
   Palette: paper white + ink, earthy green accents.
   All motion lives on transform / opacity (compositor-only);
   the one-shot choreography is driven from script.js.
   ─────────────────────────────────────────────────────────── */

:root {
  /* color */
  --bg:        #FCFCFA;
  --ink:       #181D18;
  --ink-soft:  #3F4C41;
  --moss:      #5A6E5C;
  --moss-deep: #3E4B40;
  --sage:      #AEBFAF;
  --hairline:  #E3E8E1;
  --caption:   #66786B;
  --paper:     #FFFFFF;

  /* scale */
  --name-size: clamp(56px, min(13vw, 22vh), 132px);
  --ring:      clamp(96px, 24vmin, 148px);
  --dot:       clamp(42px, 11vmin, 50px);
  --orbit-r:   min(36vmin, 252px);

  /* type */
  --font: "Inter", -apple-system, "Segoe UI", system-ui, sans-serif;
}

* { box-sizing: border-box; }

html, body { height: 100%; }

html {
  background: var(--bg);
  -webkit-text-size-adjust: 100%;
  -webkit-tap-highlight-color: transparent;   /* no grey box on tap (inherits) */
}

body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font-family: var(--font);
  overflow: hidden;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

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

/* ── stage ───────────────────────────────────────────────── */

.stage {
  position: fixed;
  inset: 0;
  user-select: none;
  -webkit-user-select: none;
}

/* ── Act I · the typed name ──────────────────────────────── */

.name {
  position: absolute;
  left: 50%; top: 50%;
  transform: translate(-50%, -50%);
  font-size: var(--name-size);
  font-weight: 900;
  letter-spacing: -0.045em;
  line-height: 1;
  white-space: nowrap;
  color: var(--ink);
}

.letter {
  display: inline-block;
  visibility: hidden;            /* revealed one keystroke at a time */
  will-change: transform, opacity;
}

/* The O is a vector from its first frame: the real glyph stays for
   text metrics but paints transparent; the path overlays it and later
   morphs — one element, glyph to perfect circle, no crossfade. */
.letter-o { position: relative; color: transparent; }

.oglyph {
  position: absolute;
  overflow: visible;
  pointer-events: none;
}

.oglyph path { fill: var(--ink); }

.caret {
  position: absolute;
  left: 0;
  top: calc(50% - 0.41em);
  width: 0.06em;
  height: 0.82em;
  border-radius: 0.02em;
  background: var(--moss);
  opacity: 0;
  will-change: transform, opacity;
}

/* ── Act II · the centerpiece ────────────────────────────── */

.mark {
  position: absolute;
  left: 50%; top: 50%;
  width: var(--ring);
  height: var(--ring);
  margin: calc(var(--ring) / -2) 0 0 calc(var(--ring) / -2);
  opacity: 0;
  will-change: transform, opacity;
}

.mark-breathe {
  width: 100%; height: 100%;
  will-change: transform;
}

.live .name { display: none; }
.live .mark { opacity: 1; }

.live .mark-breathe { animation: breathe 6.8s ease-in-out infinite; }

@keyframes breathe {
  0%, 100% { transform: scale(1); }
  50%      { transform: scale(1.016); }
}

.ring {
  display: block;
  width: 100%; height: 100%;
  overflow: visible;
}

.ring circle {
  fill: none;
  stroke: var(--ink);
  stroke-width: 26;   /* near Inter Black's O — the morph reads as refinement */
}

/* ── Act III · the orbit ─────────────────────────────────── */

.orbit-guide {
  position: absolute;
  left: 50%; top: 50%;
  width: calc(var(--orbit-r) * 2);
  height: calc(var(--orbit-r) * 2);
  margin: calc(var(--orbit-r) * -1) 0 0 calc(var(--orbit-r) * -1);
  border: 1px solid rgba(24, 29, 24, 0.06);
  border-radius: 50%;
  opacity: 0;
}

.live .orbit-guide { opacity: 1; }

.orbit {
  position: absolute;
  left: 50%; top: 50%;
  width: 0; height: 0;
  will-change: transform;
}

.dot {
  position: absolute;
  left: 0; top: 0;
  width: var(--dot);
  height: var(--dot);
  margin: calc(var(--dot) / -2) 0 0 calc(var(--dot) / -2);
  transform: rotate(var(--a)) translateX(var(--orbit-r)) rotate(calc(-1 * var(--a)));
  opacity: 0;
  outline: none;
  touch-action: manipulation;
  will-change: transform, opacity;
}

.dot.on { opacity: 1; }

/* generous hit area; also keeps hover alive while the orbit creeps */
.dot::before {
  content: "";
  position: absolute;
  inset: -10px;
  border-radius: 50%;
}

body:not(.live) .dot { pointer-events: none; }

.dot-counter {
  position: absolute;
  inset: 0;
  will-change: transform;
}

.dot-face {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  border-radius: 50%;
  background: var(--paper);
  border: 1px solid var(--hairline);
  box-shadow: 0 2px 6px rgba(24, 29, 24, 0.05);
  color: var(--ink-soft);
  transition:
    transform   0.45s cubic-bezier(0.34, 1.45, 0.5, 1),
    background  0.35s cubic-bezier(0.45, 0, 0.25, 1),
    color       0.35s cubic-bezier(0.45, 0, 0.25, 1),
    border-color 0.35s cubic-bezier(0.45, 0, 0.25, 1),
    box-shadow  0.35s cubic-bezier(0.45, 0, 0.25, 1);
  will-change: transform;
}

/* halo — blooms outward on attention */
.dot-face::after {
  content: "";
  position: absolute;
  inset: -7px;
  border-radius: 50%;
  border: 1.5px solid rgba(90, 110, 92, 0.22);
  opacity: 0;
  transform: scale(0.82);
  transition: opacity 0.4s ease, transform 0.5s cubic-bezier(0.2, 0.8, 0.25, 1);
}

.dot:hover .dot-face,
.dot:focus-visible .dot-face {
  transform: scale(1.16);
  background: var(--moss-deep);
  border-color: transparent;
  color: #EFF3EE;
  box-shadow: 0 8px 22px rgba(40, 52, 42, 0.20);
}

.dot:hover .dot-face::after,
.dot:focus-visible .dot-face::after {
  opacity: 1;
  transform: scale(1);
}

.dot:focus-visible .dot-face { outline: 2px solid var(--moss); outline-offset: 4px; }

.dot svg {
  width: 20px;
  height: 20px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.6;
  stroke-linecap: round;
  stroke-linejoin: round;
}

/* per-dot label — hidden on pointer (desktop) devices, where the
   shared caption types the label on hover; shown on touch instead.
   Lives inside .dot-counter so it stays upright as the dot orbits. */
.dot-name {
  display: none;
  position: absolute;
  top: calc(100% + 7px);
  left: 50%;
  transform: translateX(-50%);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.13em;
  white-space: nowrap;
  color: var(--caption);
  pointer-events: none;
}

/* ── the caption ─────────────────────────────────────────── */

.caption {
  position: absolute;
  left: 50%;
  top: min(calc(50% + var(--orbit-r) + var(--dot) / 2 + 38px), calc(100% - 36px));
  transform: translateX(-50%);
  margin: 0;
  display: flex;
  align-items: center;
  gap: 5px;
  font-size: 12.5px;
  font-weight: 600;
  letter-spacing: 0.18em;
  color: var(--caption);
  transition: opacity 0.3s ease;
}

.caption.idle { opacity: 0; }

.caption-text { white-space: pre; }

.caption-caret {
  width: 1.5px;
  height: 13px;
  background: var(--moss);
  animation: caption-blink 1.06s step-end infinite;
}

@keyframes caption-blink {
  0%, 55% { opacity: 1; }
  56%, 100% { opacity: 0; }
}

/* freeze hook (debug / tuning): ?freeze=<seconds> */
.frozen * { animation-play-state: paused !important; }

/* ── noscript fallback ───────────────────────────────────── */

.noscript {
  position: fixed;
  inset: 0;
  z-index: 10;
  background: var(--bg);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 22px;
  font-family: var(--font);
}

.noscript .ns-ring {
  width: 92px; height: 92px;
  border: 20px solid var(--ink);
  border-radius: 50%;
}

.noscript strong { font-size: 28px; font-weight: 900; letter-spacing: -0.03em; }

.noscript nav { display: flex; gap: 18px; flex-wrap: wrap; justify-content: center; }

.noscript a {
  color: var(--moss);
  text-decoration: none;
  font-size: 14px;
  letter-spacing: 0.12em;
}

.noscript a:hover { color: var(--moss-deep); text-decoration: underline; }

/* ───────────────────────────────────────────────────────────
   Placeholder pages
   ─────────────────────────────────────────────────────────── */

.page {
  overflow: auto;
}

.home-mark {
  position: fixed;
  top: 28px;
  left: 30px;
  width: 30px;
  height: 30px;
  color: var(--ink);
  transition: color 0.3s ease, transform 0.45s cubic-bezier(0.34, 1.45, 0.5, 1);
}

.home-mark:hover,
.home-mark:focus-visible { color: var(--moss); transform: scale(1.12); }

.home-mark svg { display: block; width: 100%; height: 100%; }

.home-mark svg circle { fill: none; stroke: currentColor; stroke-width: 26; }

.page-main {
  min-height: 100svh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 18px;
  text-align: center;
  padding: 24px;
}

.page-title {
  margin: 0;
  font-size: clamp(44px, 9vw, 92px);
  font-weight: 900;
  letter-spacing: -0.04em;
  line-height: 1;
  animation: rise 0.7s cubic-bezier(0.2, 0.8, 0.25, 1) both;
}

.page-title .tint { color: var(--moss); }

.page-note {
  margin: 0;
  font-size: 15px;
  color: var(--caption);
  letter-spacing: 0.02em;
  animation: rise 0.7s cubic-bezier(0.2, 0.8, 0.25, 1) 0.1s both;
}

@keyframes rise {
  from { opacity: 0; transform: translateY(14px); }
  to   { opacity: 1; transform: translateY(0); }
}

/* ───────────────────────────────────────────────────────────
   Phones (portrait) — larger type and touch targets.
   Desktop is untouched by construction: viewports wider than
   640px never enter this block, and at desktop sizes the base
   tokens already sit at their clamp caps.
   ─────────────────────────────────────────────────────────── */

@media (max-width: 640px) and (orientation: portrait) {
  :root {
    --name-size: min(24vw, 20vh, 120px);
    --ring:      min(31vw, 124px);
    --dot:       56px;
    --orbit-r:   min(calc(50vw - 46px), 252px);
  }

  .dot svg { width: 24px; height: 24px; }

  .caption { font-size: 14px; letter-spacing: 0.16em; }
  .caption-caret { height: 14px; }

  .home-mark { width: 36px; height: 36px; }
  .page-title { font-size: clamp(52px, 15vw, 84px); }
  .page-note { font-size: 17px; }
}

/* ───────────────────────────────────────────────────────────
   Touch devices (no hover) — reveal each dot's label, since
   there is no hover to type it into the caption. Gated on
   pointer capability, so desktop is never affected. Labels fade
   in once the intro settles, then ride the orbit, staying upright.
   ─────────────────────────────────────────────────────────── */

@media (hover: none) {
  .dot-name {
    display: block;
    opacity: 0;
    transition: opacity 0.5s ease;
  }

  .live .dot-name { opacity: 1; }
}
