/* Shared by every page: the background layer (gradient glows + grain).
   The glows' strength follows the visitor's local time of day: an inline
   script in each page's <head> sets --tint-warm, --tint-cool and --tint-night
   before first paint and every ten minutes after (DESIGN_BRIEF 5.2). */

@property --tint-warm { syntax: "<number>"; inherits: true; initial-value: 0.08; }
@property --tint-cool { syntax: "<number>"; inherits: true; initial-value: 0.15; }
@property --tint-night { syntax: "<number>"; inherits: true; initial-value: 0; }
@property --gx1 { syntax: "<percentage>"; inherits: false; initial-value: 16%; }
@property --gy1 { syntax: "<percentage>"; inherits: false; initial-value: 18%; }
@property --gx2 { syntax: "<percentage>"; inherits: false; initial-value: 84%; }
@property --gy2 { syntax: "<percentage>"; inherits: false; initial-value: 34%; }
@property --gx3 { syntax: "<percentage>"; inherits: false; initial-value: 42%; }
@property --gy3 { syntax: "<percentage>"; inherits: false; initial-value: 92%; }
@property --bg-shift { syntax: "<percentage>"; inherits: false; initial-value: 0%; }

:root {
  --tint-scale: 1;
  --night-depth: 4%;
  transition: --tint-warm 90s linear, --tint-cool 90s linear, --tint-night 90s linear;
}

:root[data-theme="light"] {
  --tint-scale: 0.5;
  --night-depth: 2%;
}

@media (prefers-reduced-motion: reduce) {
  :root { transition: none; }
}

.page-bg {
  position: fixed;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  background:
    radial-gradient(circle at var(--gx1) calc(var(--gy1) + var(--bg-shift)),
      color-mix(in srgb, var(--c-amber) calc(var(--tint-warm) * var(--tint-scale) * 100%), transparent),
      transparent 58vmax),
    radial-gradient(circle at var(--gx2) calc(var(--gy2) + var(--bg-shift)),
      color-mix(in srgb, var(--c-teal-1) calc(var(--tint-cool) * var(--tint-scale) * 100%), transparent),
      transparent 62vmax),
    radial-gradient(circle at var(--gx3) calc(var(--gy3) + var(--bg-shift)),
      color-mix(in srgb, var(--c-teal-1) calc(var(--tint-cool) * var(--tint-scale) * 60%), transparent),
      transparent 46vmax),
    color-mix(in srgb, var(--bg), var(--c-black) calc(var(--tint-night) * var(--night-depth)));
}

/* The background is the slowest layer: its glows drift on a 28 s loop and
   rise a little over the whole scroll of the page (DESIGN_BRIEF 4, 5.2).
   The loop's phase comes from the clock (--drift-delay, set in each page's
   head), so the drift carries on across page changes instead of restarting. */
@media (prefers-reduced-motion: no-preference) {
  .page-bg {
    animation: bg-drift var(--drift-period) var(--ease-in-out) var(--drift-delay, 0s) infinite;
  }

  @supports (animation-timeline: scroll()) {
    .page-bg {
      animation: bg-drift var(--drift-period) var(--ease-in-out) var(--drift-delay, 0s) infinite, bg-rise linear both;
      animation-timeline: auto, scroll(root);
    }
  }
}

/* On the landing page the drift waits until the 3D intro is done: while the
   scene starts up, the GPU should not be repainting the background too. */
html.scene-starting .page-bg { animation-play-state: paused; }

@keyframes bg-drift {
  0%, 100% { --gx1: 16%; --gy1: 18%; --gx2: 84%; --gy2: 34%; --gx3: 42%; --gy3: 92%; }
  33% { --gx1: 24%; --gy1: 27%; --gx2: 75%; --gy2: 24%; --gx3: 53%; --gy3: 84%; }
  66% { --gx1: 11%; --gy1: 30%; --gx2: 88%; --gy2: 45%; --gx3: 33%; --gy3: 97%; }
}

@keyframes bg-rise {
  to { --bg-shift: -12%; }
}

.page-bg::after {
  content: "";
  position: fixed;
  inset: 0;
  pointer-events: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='180' height='180'%3E%3Cfilter id='g'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23g)'/%3E%3C/svg%3E");
  background-size: 180px 180px;
  opacity: 0.035;
  mix-blend-mode: overlay;
}

/* ---------- The strip as a logo, and the theme toggle ----------
   Both sit in every page's nav. The logo is the hero's knot seen from the
   front, three of its wireframe lines in the strip's own colours. */
.knot-logo {
  width: var(--logo-size);
  height: var(--logo-size);
  flex: none;
  fill: none;
  stroke-width: 1.6;
  stroke-linejoin: round;
}

:root[data-theme="light"] .knot-logo { stroke: var(--wire); }

.theme-toggle {
  display: inline-grid;
  place-items: center;
  flex: none;
  width: var(--tap-min);
  height: var(--tap-min);
  padding: 0;
  color: var(--control);
  background: transparent;
  border: var(--hairline) solid color-mix(in srgb, var(--control) 50%, transparent);
  border-radius: var(--radius-pill);
  cursor: pointer;
  transition: background var(--dur-fast) var(--ease-out), color var(--dur-fast) var(--ease-out);
}

.theme-toggle:hover {
  background: var(--control);
  color: var(--control-ink);
}

.theme-toggle svg {
  width: var(--icon-size);
  height: var(--icon-size);
  fill: none;
  stroke: currentColor;
  stroke-width: 1.6;
  transition: rotate var(--dur-slow) var(--ease-in-out);
}

.theme-toggle path { fill: currentColor; stroke: none; }

:root[data-theme="light"] .theme-toggle svg { rotate: 180deg; }

@media (prefers-reduced-motion: reduce) {
  .theme-toggle svg { transition: none; }
}

/* ---------- Hover previews (DESIGN_BRIEF 7.3) ----------
   Cards for the paper, OEIS, Scholar, ORCID, LinkedIn and CV links. Each is
   a popover placed under its link by anchor positioning, flipping above (or
   to the other side) when there is no room; assets/previews.js opens and
   closes them. The same lifted, blurred surface as the bento cards. */
.preview {
  position: fixed;
  position-anchor: --preview;
  position-area: block-end span-inline-end;
  position-try-fallbacks: flip-block, flip-inline, flip-block flip-inline;
  inset: auto;
  margin: var(--space-2xs) 0;
  width: min(var(--preview-width), calc(100vw - 2 * var(--space-s)));
  padding: var(--space-s) var(--space-m) var(--space-m);
  border: 0;
  border-radius: var(--radius-l);
  background: var(--surface);
  backdrop-filter: blur(var(--blur-m));
  box-shadow: inset 0 0 0 var(--hairline) var(--line), var(--shadow-2);
  color: var(--text);
  font-family: var(--font-body);
  font-size: var(--text-sm);
  line-height: var(--leading-snug);
  text-align: left;
  overflow: visible;
}

@media (prefers-reduced-motion: no-preference) {
  .preview {
    transition: opacity var(--dur-preview) var(--ease-out), translate var(--dur-preview) var(--ease-out),
      display var(--dur-preview) allow-discrete, overlay var(--dur-preview) allow-discrete;
  }

  .preview:not(:popover-open) {
    opacity: 0;
    translate: 0 var(--preview-shift);
  }

  @starting-style {
    .preview:popover-open {
      opacity: 0;
      translate: 0 var(--preview-shift);
    }
  }
}

.preview p { margin: 0; max-width: none; }
.preview > * + * { margin-top: var(--space-3xs); }

.preview-label {
  font-size: var(--text-xs);
  letter-spacing: var(--tracking-wide);
  color: var(--text-muted);
}

.preview .preview-title {
  margin-top: var(--space-2xs);
  font-size: var(--text-md);
  font-weight: var(--weight-strong);
  color: var(--text-strong);
  text-wrap: balance;
}

.preview-meta { color: var(--text-muted); }
.preview-stat { color: var(--accent); font-weight: var(--weight-strong); }

.preview-id,
.preview-terms,
.preview-when {
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  font-variant-numeric: tabular-nums;
}

.preview-terms { overflow-wrap: anywhere; }

.preview .preview-math {
  margin-top: var(--space-2xs);
  overflow-x: auto;
}

.preview-math .katex-display { margin: 0; text-align: left; }
.preview-math .katex-display > .katex { text-align: left; }

.preview-timeline {
  display: grid;
  gap: var(--space-3xs);
  margin: var(--space-2xs) 0 0;
  padding: 0;
  list-style: none;
}

.preview-timeline li {
  display: grid;
  grid-template-columns: 5.5em 1fr;
  gap: var(--space-2xs);
}

.preview-when { color: var(--text-muted); }

/* The arXiv author card draws the papers as a small tree: the first paper,
   then a branch to each one that came out of it. Work in preparation hangs
   on a dashed branch with a hollow node. */
.preview:has(.preview-tree) { --preview-width: 28rem; }

.preview-head,
.preview-tree .preview-id {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: var(--space-xs);
}

.preview .preview-tree {
  --node: 0.5rem;
  --node-y: 0.5rem;
  --branch: 1.25rem;
  --trunk: calc(var(--node) / 2 - var(--hairline) / 2);
  display: grid;
  gap: var(--space-xs);
  margin: var(--space-xs) 0 0;
  padding: 0;
  list-style: none;
}

.preview-tree li { position: relative; padding-left: var(--branch); }
.preview-tree li + li { padding-left: calc(2 * var(--branch)); }
.preview-tree li > * + * { margin-top: var(--space-3xs); }

.preview-tree .preview-id { color: var(--text-muted); }

.preview-tree .preview-id::before {
  content: "";
  position: absolute;
  top: calc(var(--node-y) - var(--node) / 2);
  left: 0;
  box-sizing: border-box;
  width: var(--node);
  height: var(--node);
  border: 1.5px solid var(--accent);
  border-radius: 50%;
  background: var(--accent);
}

.preview-tree li + li .preview-id::before { left: var(--branch); }
.preview-tree .pending .preview-id::before { background: none; }

.preview-tree li::before,
.preview-tree li + li:not(:last-child)::after {
  content: "";
  position: absolute;
  left: var(--trunk);
  border-left: var(--hairline) solid var(--line-strong);
}

.preview-tree li:first-child::before {
  top: calc(var(--node-y) + var(--node));
  bottom: calc(-1 * var(--space-xs));
}

.preview-tree li + li::before {
  top: calc(-1 * var(--space-xs));
  width: calc(var(--branch) - var(--node));
  height: calc(var(--space-xs) + var(--node-y));
  border-bottom: var(--hairline) solid var(--line-strong);
}

.preview-tree li + li:not(:last-child)::after {
  top: var(--node-y);
  bottom: calc(-1 * var(--space-xs));
}

.preview-tree li:last-child::before { border-bottom-left-radius: var(--radius-m); }
.preview-tree .pending::before { border-style: dashed; border-top: 0; border-right: 0; }

.preview-rel { font-family: var(--font-body); white-space: nowrap; }
.preview-paper { font-weight: var(--weight-strong); color: var(--text-strong); }
.preview-tree .katex { font-size: 1.1em; }

/* the tree grows from its root as the card opens */
@media (prefers-reduced-motion: no-preference) {
  .preview-tree li {
    transition: opacity var(--dur-preview) var(--ease-out), translate var(--dur-preview) var(--ease-out);
  }

  .preview-tree li:nth-child(2) { transition-delay: 90ms; }
  .preview-tree li:nth-child(3) { transition-delay: 180ms; }

  @starting-style {
    .preview-tree li {
      opacity: 0;
      translate: 0 var(--preview-shift);
    }
  }
}

/* ---------- Page transitions (DESIGN_BRIEF 5.5) ----------
   Cross-document view transitions. The background, nav and theme toggle are
   named so they hold still while only the content moves. The strip on the
   landing page and the knot logo in every other page's nav share one name:
   leaving the landing page shrinks the strip into the logo, coming home
   grows it back. Browsers without cross-document transitions simply load
   the next page. */
@view-transition { navigation: auto; }

.page-bg { view-transition-name: bg; }
.site-nav, #site-header, html.m-on .m-bar { view-transition-name: nav; }
.theme-toggle { view-transition-name: theme; }
.knot-logo, #strip-frame { view-transition-name: mobius; }
.nav-indicator { view-transition-name: nav-indicator; }
.globe { view-transition-name: globe; }
#page-title { view-transition-name: page-title; }
.container { view-transition-name: page-body; }

/* The page being left renames its content, so the old title and body fade
   out where they are instead of morphing into the new page's. A dive into
   the strip hands the strip over under a name of its own (see below). */
html.vt-leaving #page-title { view-transition-name: page-title-out; }
html.vt-leaving .container { view-transition-name: page-body-out; }
html.vt-dive #strip-frame { view-transition-name: strip-dive; }

::view-transition-group(*) {
  animation-duration: var(--vt-morph);
  animation-timing-function: var(--ease-in-out);
}

/* Content moves 12px up and out, and in from 12px below; between pages that
   sit side by side in the nav it slides 16px sideways, in the direction of
   travel (the forward and back types are set in each page's head). */
:root {
  --vt-dx: 0px;
  --vt-dy: var(--vt-rise);
}

:root:active-view-transition-type(forward) { --vt-dx: var(--vt-slide); --vt-dy: 0px; }
:root:active-view-transition-type(back) { --vt-dx: calc(-1 * var(--vt-slide)); --vt-dy: 0px; }

::view-transition-old(page-title-out),
::view-transition-old(page-body-out) {
  animation: vt-leave var(--vt-out) var(--ease-in-out) both;
}

::view-transition-new(page-title) {
  animation: vt-enter var(--vt-in) var(--ease-out) calc(var(--vt-in-delay) - var(--vt-title-lead)) both;
}

::view-transition-new(page-body) {
  animation: vt-enter var(--vt-in) var(--ease-out) var(--vt-in-delay) both;
}

::view-transition-old(root) { animation: vt-fade-out var(--vt-out) var(--ease-in-out) both; }
::view-transition-new(root) { animation: vt-fade-in var(--vt-in) var(--ease-out) var(--vt-in-delay) both; }

/* The strip and the logo swap pictures while small: shrinking into the
   nav (forward, from the landing page) the knot holds on and the logo takes
   over late; growing home (back) the logo lets go early. */
:root:active-view-transition-type(forward)::view-transition-old(mobius) { animation-name: vt-hold-out; mix-blend-mode: plus-lighter; }
:root:active-view-transition-type(forward)::view-transition-new(mobius) { animation-name: vt-late-in; mix-blend-mode: plus-lighter; }
:root:active-view-transition-type(back)::view-transition-old(mobius) { animation-name: vt-early-out; mix-blend-mode: plus-lighter; }
:root:active-view-transition-type(back)::view-transition-new(mobius) { animation-name: vt-early-in; mix-blend-mode: plus-lighter; }

@keyframes vt-hold-out { 0%, 55% { opacity: 1; } 100% { opacity: 0; } }
@keyframes vt-late-in { 0%, 45% { opacity: 0; } 100% { opacity: 1; } }
@keyframes vt-early-out { 0% { opacity: 1; } 45%, 100% { opacity: 0; } }
@keyframes vt-early-in { 0% { opacity: 0; } 55%, 100% { opacity: 1; } }

/* Diving into a label: the strip keeps rushing towards the camera as it
   fades, and the page unfolds out of it. */
::view-transition-old(strip-dive) {
  animation: vt-dive var(--vt-dive) var(--ease-out) both;
}

:root:active-view-transition-type(dive)::view-transition-new(page-title),
:root:active-view-transition-type(dive)::view-transition-new(page-body) {
  animation-name: vt-unfold;
}

@keyframes vt-leave { to { opacity: 0; translate: calc(-1 * var(--vt-dx)) calc(-1 * var(--vt-dy)); } }
@keyframes vt-enter { from { opacity: 0; translate: var(--vt-dx) var(--vt-dy); } }
@keyframes vt-unfold { from { opacity: 0; scale: var(--vt-unfold-scale); } }
@keyframes vt-dive { to { opacity: 0; scale: var(--vt-dive-scale); } }
@keyframes vt-fade-out { to { opacity: 0; } }
@keyframes vt-fade-in { from { opacity: 0; } }

/* Theme toggle (DESIGN_BRIEF 7.1): the new theme sweeps out of the toggle
   in a growing circle. Names are dropped for the sweep so the whole page
   changes as one picture. */
html.theme-sweep * { view-transition-name: none !important; }

html.theme-sweep::view-transition-old(root) {
  animation: none;
  mix-blend-mode: normal;
}

html.theme-sweep::view-transition-new(root) {
  animation: vt-sweep var(--vt-sweep) var(--ease-in-out) both;
  mix-blend-mode: normal;
}

@keyframes vt-sweep {
  from { clip-path: circle(0 at var(--sweep-x) var(--sweep-y)); }
  to { clip-path: circle(150% at var(--sweep-x) var(--sweep-y)); }
}

/* Reduced motion: every page change is a plain 150 ms crossfade. */
@media (prefers-reduced-motion: reduce) {
  ::view-transition-group(*) { animation: none !important; }
  ::view-transition-old(*) { animation: vt-fade-out var(--vt-fade) linear both !important; }
  ::view-transition-new(*) { animation: vt-fade-in var(--vt-fade) linear both !important; }
}
