/**
 * Base Styles
 * 
 * Minimal reset and base typography styles.
 * Preserves browser defaults where possible, only resetting what's necessary.
 */

/* Minimal Reset */
* {
  box-sizing: border-box;
}

html {
  font-size: 16px;
}

body {
  margin: 0;
  padding: 0;
  font-family: var(--font-body);
  font-size: var(--font-size-body);
  font-weight: var(--font-weight-body);
  line-height: var(--line-height-body);
  letter-spacing: var(--letter-spacing-body);
  color: var(--color-text);
  background-color: var(--color-bg-page);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

/* Typography */
h1, h2, h3, h4, h5, h6 {
  margin: 0;
  font-weight: normal;
}

p {
  margin: 0 0 var(--spacing-md) 0;
}

p:last-child {
  margin-bottom: 0;
}

/* Links: hover comes in fast and leaves slowly */
a {
  color: var(--color-link);
  text-decoration: none;
  transition: color var(--dur-3) var(--ease-out);
}

a:hover {
  color: var(--color-link-hover);
  transition-duration: var(--dur-1);
}

/* Page links (not article prose) get a soft sage fill on hover or keyboard
   focus, like a highlighter pass over the text and its icon. The fill is a
   pseudo-element that bleeds past the link box, so nothing reflows. It
   moves like the project rows' hover fill: in fast, out slowly, ease-out
   both ways. It's a colour change, not movement, so it stays under reduced
   motion. */
.column a,
.intro-text a,
.lower-text a,
.history-close {
  position: relative;
  isolation: isolate;
}

.column a::after,
.intro-text a::after,
.lower-text a::after,
.history-close::after {
  content: '';
  position: absolute;
  /* Figma: 25px tall, centred on the 20px line, 2px past the text each
     side. Inline links box only the 17px glyph run, so they reach 4px. */
  inset: -4px -2px;
  z-index: -1;
  background: var(--color-link-highlight);
  opacity: 0;
  pointer-events: none;
  transition: opacity var(--dur-3) var(--ease-out);
}

/* Icon links are inline-flex and already fill the 20px line */
a.archive-link::after,
a.history-link::after {
  inset: -2px -2px -3px;
}

/* The fill carries the hover, so the text holds its colour */
.column a:hover,
.intro-text a:hover,
.lower-text a:hover,
.history-close:hover {
  color: var(--color-link);
}

.column a:focus-visible::after,
.intro-text a:focus-visible::after,
.lower-text a:focus-visible::after,
.history-close:focus-visible::after {
  opacity: 1;
  transition-duration: var(--dur-1);
}

@media (hover: hover) {
  .column a:hover::after,
  .intro-text a:hover::after,
  .lower-text a:hover::after,
  .history-close:hover::after {
    opacity: 1;
    transition-duration: var(--dur-1);
  }
}

/* Images */
img {
  max-width: 100%;
  height: auto;
  display: block;
}

/* Custom scrollbar (Figma: 4px wide, 8px radius, #f5e9e0, 8px from edge) */
* {
  scrollbar-width: thin;
  scrollbar-color: #f5e9e0 transparent;
}

*::-webkit-scrollbar {
  width: 4px;
  height: 4px;
}

*::-webkit-scrollbar-track {
  background: transparent;
}

*::-webkit-scrollbar-thumb {
  background: #f5e9e0;
  border-radius: 8px;
}

*::-webkit-scrollbar-thumb:hover {
  background: #f5e9e0;
}

/* ---------------------------------------------------------------------------
   Reveal-on-load
   Elements fade (and rise) into place once, when they enter the viewport.
   Above-the-fold elements reveal immediately on load (the observer fires for
   already-visible elements). Scoped to .js so content is fully visible if
   JavaScript is unavailable. One-shot — does not reverse on scroll.

   The fade is long enough to be seen and finishes before the rise settles,
   so nothing looks ghosted. Each element's start time is --d (the desktop
   intro timeline below) or, failing that, a stagger on --i (mobile).
--------------------------------------------------------------------------- */

/* Fade + rise variant. Rise distance scales with mass (--rise). */
.js .rv {
  opacity: 0;
  transform: translateY(var(--rise, var(--rise-1)));
  transition: opacity var(--dur-4) var(--ease-out), transform var(--dur-6) var(--ease-out-expo);
  transition-delay: var(--d, calc(160ms + var(--i, 0) * 100ms));
}

/* Fade-only variant (for elements whose layout position is measured by JS, so
   they must not be transformed — e.g. the desktop illustration) */
.js .rv-fade {
  opacity: 0;
  transition: opacity var(--dur-7) var(--ease-out);
  transition-delay: var(--d, calc(160ms + var(--i, 0) * 100ms));
}

.js .rv.in,
.js .rv-fade.in {
  opacity: 1;
  transform: none;
}

/* Gridlines rule in top to bottom, left to right, like pen strokes: the
   sheet is ruled before anything is set on it */
.js .gridline {
  transform: scaleY(0);
  transition: transform var(--dur-7) var(--ease-draw);
}

.js .gridline--2 {
  transition-delay: 100ms;
}

.js .gridline--3 {
  transition-delay: 200ms;
}

.js .gridlines.in .gridline {
  transform: none;
}

/* The peek square stamps in from its top-right corner */
.js .peek {
  transform: scale(0);
}

.js .peek.in {
  transform: none;
}

/* Desktop intro timeline ("ruled, set, placed, stamped"). Each beat starts
   once the one before it is visibly under way, so the order reads:
     0      gridlines rule down (to ~1.4s)
     280    the name sets, line by line, then the e's one by one
     620    the illustration is placed
     800    the columns, left to right
     1350   the blue square stamps in, after a breath (to ~1.8s)
   Readable by ~1.5s. */
.display-name {
  --rise: var(--rise-2);
}

.display-name .line:nth-child(1) { --d: 280ms; }
.display-name .line:nth-child(2) { --d: 400ms; }
.display-name .e:nth-child(1) { --d: 480ms; }
.display-name .e:nth-child(2) { --d: 540ms; }
.display-name .e:nth-child(3) { --d: 600ms; }
.display-name .e:nth-child(4) { --d: 660ms; }
.illustration { --d: 620ms; }
.column-bio { --d: 800ms; }
.column-writing { --d: 900ms; }
.column-soon { --d: 1000ms; }
.peek { --d: 1350ms; }

/* Mobile: the logo is the name */
.logo {
  --rise: var(--rise-2);
}

/* A tab that has already played the intro gets the short version: no ruling,
   a small rise, a tight stagger (~450ms in all) */
.js.intro-quick .gridline {
  transform: none;
}

.js.intro-quick .rv {
  --rise: 4px;
  transition-duration: var(--dur-2), var(--dur-4);
  transition-delay: calc(var(--i, 0) * 30ms);
}

.js.intro-quick .rv-fade {
  transition-duration: var(--dur-3);
  transition-delay: 0s;
}

.js.intro-quick .peek {
  transition-delay: 0s;
}

@media (prefers-reduced-motion: reduce) {
  .js .rv,
  .js .rv-fade {
    transition: opacity 0.2s !important;
    transition-delay: 0s !important;
    transform: none !important;
  }

  .js .gridline {
    transform: none;
    transition: none;
  }

  /* The peek fades instead of stamping (it keeps its collapsed clip; with
     no clip transition it opens in one step) */
  .js .peek {
    transform: none;
    opacity: 0;
    transition: opacity 0.2s !important;
  }

  .js .peek.in {
    opacity: 1;
  }
}

/* Off-screen text for screen readers and crawlers. Absolutely positioned, so
   inside an inline-flex link it is not a flex item and adds no gap. */
.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;
}
