/* Design tokens (fonts, colors, type scale, spacing, radii, motion) live in
   styles/tokens.css, a separate page-agnostic file included by base.njk
   before this one — see CLAUDE.md. This file is component/layout CSS only. */

/* ==========================================================================
   Base layout — container + vertical rhythm.
   Section-specific internal layouts (project grid, hero, etc.) are further
   down, grouped with each section's own styling.
   ========================================================================== */

header,
main > section,
footer {
  max-width: var(--content-max);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
  padding-block: var(--space-4);
}

main > section {
  padding-block-start: var(--space-10);
}

main > section:first-of-type {
  padding-block-start: var(--space-9);
}

main > section:last-of-type {
  padding-block-end: var(--space-8);
}

footer {
  padding-block: var(--space-6);
}

main p {
  max-width: var(--measure);
}

/* ==========================================================================
   Global element styling
   ========================================================================== */

html {
  color-scheme: dark light;
}

body {
  background-color: var(--color-canvas);
  color: var(--color-ink);
  font-family: var(--font-body);
  font-size: var(--step-0);
  line-height: var(--leading-normal);
  transition: background-color var(--transition-base), color var(--transition-base);
  position: relative;
}

/* The page's mathematical ground: a full-viewport canvas, fixed behind
   all scrolling content, drawn entirely by scripts/system.js. Sized in
   both CSS (here) and JS (backing-store resolution + devicePixelRatio) —
   this half just has to make it cover the viewport and stay out of the
   way of everything painted above it. */
#system-canvas {
  position: fixed;
  inset: 0;
  width: 100%;
  height: 100%;
  z-index: -1;
  pointer-events: none;
}

h1,
h2,
h3 {
  font-family: var(--font-display);
  color: var(--color-ink);
  line-height: var(--leading-tight);
  letter-spacing: -0.01em;
  font-weight: 600;
}

h1 {
  font-size: var(--step-4);
}

h2 {
  font-size: var(--step-2);
  font-weight: 500;
  margin-block-end: var(--space-4);
}

h3 {
  font-size: var(--step-1);
  font-weight: 500;
}

a {
  color: var(--color-signal-ink);
  text-decoration-line: underline;
  text-decoration-color: color-mix(in srgb, var(--color-signal-ink) 45%, transparent);
  text-underline-offset: 0.15em;
  transition: text-decoration-color var(--transition-fast), color var(--transition-fast);
}

a:hover {
  text-decoration-color: currentColor;
}

button {
  font-family: var(--font-mono);
  font-size: var(--step--1);
  letter-spacing: 0.02em;
  background: none;
  color: var(--color-mute);
  border: 1px solid var(--color-rule);
  border-radius: var(--radius-sm);
  padding: var(--space-2) var(--space-3);
  cursor: pointer;
  transition: color var(--transition-fast), border-color var(--transition-fast),
    background-color var(--transition-fast);
}

button:hover {
  color: var(--color-ink);
  border-color: color-mix(in srgb, var(--color-ink) 40%, transparent);
}

button[aria-pressed="true"] {
  color: var(--color-signal-ink);
  border-color: var(--color-signal-ink);
}

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

/* ==========================================================================
   Header
   ========================================================================== */

header {
  border-block-end: 1px solid var(--color-rule);
  flex-wrap: wrap;
  row-gap: var(--space-2);
}

.brand {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: var(--step-1);
  color: var(--color-ink);
}

.controls {
  display: flex;
  align-items: center;
  gap: var(--space-3);
}

.lang-toggle {
  display: flex;
  gap: var(--space-1);
}

/* Real <nav> landmark with jump links to this page's sections plus a
   cross-page link to /lab/ — the proof that the hub pattern (shared
   header/footer/nav partials, see CLAUDE.md) actually works, not just a
   claim. Sits between the brand and the toggles; header's existing
   flex-wrap + space-between handles the extra flex child. */
.site-nav ul {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-4);
}

.site-nav a {
  font-family: var(--font-mono);
  font-size: var(--step--1);
  letter-spacing: 0.02em;
  color: var(--color-mute);
  text-decoration: none;
}

.site-nav a:hover,
.site-nav a[aria-current="page"] {
  color: var(--color-signal-ink);
}

/* Shared decorative arrow used on project/contact links. Kept out of the
   translated text node so Phase 4's language swap only ever touches copy. */
.link-arrow {
  display: inline-block;
  transition: transform var(--transition-fast);
}

a:hover .link-arrow {
  transform: translateX(0.2em);
}

/* ==========================================================================
   Hero — the signature moment.
   The figure itself is no longer markup here: it's the de Jong attractor
   drawn by #system-canvas, fixed behind the whole page and at its most
   visible while this section is on screen. This block only has to give
   the text room to sit in front of it and hold the viewport for a while,
   so the attractor actually gets a moment before the page scrolls past.
   ========================================================================== */

#hero {
  display: flex;
  flex-direction: column;
  justify-content: center;
  min-height: min(80vh, 42rem);
}

.hero-content {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
}

.hero-eyebrow {
  font-family: var(--font-mono);
  font-size: var(--step--1);
  font-weight: 500;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--color-mute);
}

.hero-identity {
  font-size: var(--step-1);
  font-weight: 400;
  color: var(--color-mute);
  max-width: 42ch;
}

/* ==========================================================================
   Section labels (About / Projects / Contact) — quiet mono labels rather
   than a second display-font statement; the hero already spent that weight.
   ========================================================================== */

.section-heading {
  font-family: var(--font-mono);
  font-size: var(--step--1);
  font-weight: 500;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--color-mute);
  margin-block-end: var(--space-5);
}

/* Section divider — a single generated curve (main.js computes a damped
   sine wave from a hash of the section's own id, see initGenerativeDividers)
   rather than four identical hand-drawn axes: same principle, a related
   but non-repeating line per section. The endpoint stays a fixed accent
   point, the one constant landmark the varying curve resolves toward. */
.section-divider {
  width: clamp(3.5rem, 6vw, 5.5rem);
  margin-block-end: var(--space-3);
  color: var(--color-mute);
}

.section-divider svg {
  width: 100%;
  height: auto;
  display: block;
  overflow: visible;
}

.divider-line {
  stroke: currentColor;
  stroke-width: 0.4;
  fill: none;
  vector-effect: non-scaling-stroke;
  opacity: 0.6;
}

.divider-point {
  fill: var(--color-signal-ink);
}

/* ==========================================================================
   About — a single, self-contained bio block (see MIGRATION.md: replaces
   the old three-fragment #chapter-1/2/3 narrative dissolved between the
   project cards). Just a .section-heading + a plain <p>; the global
   `main p { max-width: var(--measure) }` rule already caps its line length.
   ========================================================================== */

/* ==========================================================================
   Works — a compact three-tile grid (see MIGRATION.md, "Werke-Grid mit
   Hover-Expand"). Each tile is small/catchy at rest (title, preview,
   tags); the full description opens on hover, keyboard focus, or a
   click/tap toggle — three independent routes to the exact same visible
   state, since touch has no real hover and keyboard only ever gets
   :focus-within.
   ========================================================================== */

.works-grid {
  display: grid;
  /* auto-fit rather than a fixed breakpoint: naturally 3-up on desktop,
     folding down to fewer columns and finally a single stacked column on
     narrow viewports with no separate media query needed. */
  grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
  align-items: start;
  gap: var(--space-6) var(--space-5);
}

/* Staggered reveal (scripts/main.js's initWorksReveal): all three entries
   get .is-visible at the same moment, once #works scrolls into view — the
   cascade comes entirely from each entry's own transition-delay, reusing
   the existing .reveal-init/.is-visible mechanism below rather than any
   new animation logic. */
.works-grid .project-section:nth-of-type(1) {
  transition-delay: 0ms;
}

.works-grid .project-section:nth-of-type(2) {
  transition-delay: 140ms;
}

.works-grid .project-section:nth-of-type(3) {
  transition-delay: 280ms;
}

.project-card {
  container: project-card / inline-size;
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  padding: var(--space-5);
  background-color: var(--color-surface);
  border: 1px solid var(--color-rule);
  border-radius: var(--radius-md);
  transition: border-color var(--transition-fast), background-color var(--transition-base);
}

/* Expanded state has three independent triggers -- real mouse hover,
   keyboard focus landing anywhere inside the tile, and a persisted
   click/tap toggle (scripts/main.js's initProjectTiles, for touch devices
   that have no hover at all) -- all three must produce this exact same
   look, so they share one selector list everywhere in this section. */
.project-card:hover,
.project-card:focus-within,
.project-card.is-expanded {
  border-color: var(--color-signal-ink);
  background-color: var(--color-surface-2);
}

.project-card p {
  color: var(--color-mute);
}

/* The title doubles as the click/tap toggle -- reset out of the page's
   generic `button` chrome (mono font, outlined pill, muted color) back to
   plain heading styling, since visually this must still read as
   "Epiphyte", not as a form control. */
.project-toggle {
  all: unset;
  display: flex;
  align-items: center;
  gap: var(--space-2);
  width: 100%;
  font: inherit;
  color: inherit;
  letter-spacing: inherit;
  cursor: pointer;
}

.toggle-icon {
  margin-inline-start: auto;
  font-family: var(--font-mono);
  font-size: var(--step-0);
  font-weight: 400;
  color: var(--color-mute);
  transition: transform var(--transition-fast), color var(--transition-fast);
}

.project-card:hover .toggle-icon,
.project-card:focus-within .toggle-icon,
.project-card.is-expanded .toggle-icon {
  transform: rotate(45deg);
  color: var(--color-signal-ink);
}

/* Live miniature: each card's own small, independent simulation (see
   scripts/system.js's card-previews section) -- not the background
   #system-canvas, just styled consistently with it. */
.project-preview {
  border-radius: var(--radius-sm);
  overflow: hidden;
  background-color: color-mix(in srgb, var(--color-ink) 5%, transparent);
}

.project-preview canvas {
  display: block;
  width: 100%;
  height: clamp(5.5rem, 20vw, 7rem);
}

.tech-tags {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
}

.tech-tags li {
  font-family: var(--font-mono);
  font-size: var(--step--1);
  color: var(--color-mute);
  background-color: color-mix(in srgb, var(--color-ink) 8%, transparent);
  border: 1px solid var(--color-rule);
  border-radius: var(--radius-full);
  padding: 0.15em 0.7em;
  line-height: 1.5;
}

/* Status badge (e.g. Aura's "Work in progress"): same pill shape as a
   tech tag, but a dashed accent-colored border marks it as unfinished
   rather than a settled fact about the project. */
.status-badge {
  display: inline-flex;
  align-self: flex-start;
  font-family: var(--font-mono);
  font-size: var(--step--1);
  color: var(--color-signal-ink);
  border: 1px dashed color-mix(in srgb, var(--color-signal-ink) 55%, transparent);
  border-radius: var(--radius-full);
  padding: 0.15em 0.7em;
  line-height: 1.5;
}

/* Detail region: the full description, collapsed to zero height at rest
   -- not display:none, so it stays in the DOM and in the accessibility
   tree and screen reader users can still reach it regardless of the
   visual expand state (see MIGRATION.md). The 0fr/1fr grid-row trick
   animates height without ever measuring scrollHeight in JS; the inner
   wrapper's own opacity fade is what actually keeps text from being
   readable mid-transition. prefers-reduced-motion needs no special
   handling here -- reset.css already collapses every transition-duration
   globally, so the state still changes, just instantly. */
.project-detail {
  display: grid;
  grid-template-rows: 0fr;
  transition: grid-template-rows var(--transition-base);
}

.project-detail-inner {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  overflow: hidden;
  min-height: 0;
  opacity: 0;
  transition: opacity var(--transition-fast);
}

.project-card:hover .project-detail,
.project-card:focus-within .project-detail,
.project-card.is-expanded .project-detail {
  grid-template-rows: 1fr;
}

.project-card:hover .project-detail-inner,
.project-card:focus-within .project-detail-inner,
.project-card.is-expanded .project-detail-inner {
  opacity: 1;
  transition-delay: 80ms;
}

.project-link {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  align-self: flex-start;
  font-family: var(--font-mono);
  font-size: var(--step--1);
  text-decoration: none;
}

.project-link:hover,
.contact-link:hover {
  text-decoration-line: underline;
  text-decoration-color: currentColor;
  text-underline-offset: 0.15em;
}

/* Wider cards (e.g. a two-column desktop grid with room to spare) get a
   little more room to breathe — a genuine container-driven adjustment,
   independent of the viewport's own breakpoints. */
@container project-card (min-width: 24rem) {
  .project-card {
    padding: var(--space-6);
  }
}

/* ==========================================================================
   Contact
   ========================================================================== */

.contact-list {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}

.contact-list li {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  column-gap: var(--space-3);
}

.contact-label {
  font-family: var(--font-mono);
  font-size: var(--step--1);
  font-weight: 500;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--color-mute);
  min-width: 6ch;
}

.contact-link {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  text-decoration: none;
}

/* ==========================================================================
   Footer — the Fourier-epicycle signature's anchor (see #system-canvas):
   given enough height for that closing visual to actually have a moment
   on screen, unlike its previous single-line-of-padding size. Not as
   tall as the hero, since this is a quiet closing beat, not the primary
   moment, but still real presence rather than a sliver.
   ========================================================================== */

footer {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  min-height: min(45vh, 24rem);
  border-block-start: 1px solid var(--color-rule);
  color: var(--color-mute);
  font-size: var(--step--1);
}

/* ==========================================================================
   Motion (Phase 4). These classes are only ever added by scripts/main.js,
   never present in the markup itself — without JS, nothing here applies
   and content stays fully visible. prefers-reduced-motion is honored by
   main.js not even adding these classes in the first place, and as a
   second line of defense reset.css collapses any transition duration
   that does slip through to near-zero.
   ========================================================================== */

.reveal-init {
  opacity: 0;
  transform: translateY(1rem);
  transition: opacity var(--transition-slow), transform var(--transition-slow);
}

.reveal-init.is-visible {
  opacity: 1;
  transform: none;
}

/* Section-divider draw-on: the generated curve extends outward as its
   section reveals. pathLength="1" (set in the markup) normalizes
   dasharray/dashoffset to 0..1 regardless of the actual path geometry,
   so this rule stays the same no matter what main.js computed for d. */
.reveal-init .divider-line {
  stroke-dasharray: 1;
  stroke-dashoffset: 1;
  transition: stroke-dashoffset 550ms ease;
}

.reveal-init .divider-point {
  opacity: 0;
  transition: opacity 300ms ease;
}

.is-visible .divider-line {
  stroke-dashoffset: 0;
}

.is-visible .divider-point {
  opacity: 1;
  transition-delay: 380ms;
}
