/*
  SHARED COMPONENTS — used on both index.html and project.html.
  Depends on tokens.css being loaded first.
*/

* { margin: 0; padding: 0; box-sizing: border-box; }

html, body {
  background: var(--color-surface-hero);
  /* `clip` not `hidden`: setting overflow-x alone to `hidden` forces the
     browser to compute overflow-y as `auto` too (a CSS 2.1 rule — only one
     axis may be `visible`, so the other axis gets promoted), which quietly
     turns html/body into their OWN scroll containers. That breaks every
     position:sticky element on the page (a sticky element's nearest
     scrolling ancestor becomes ambiguous between html and body instead of
     the viewport). `overflow-x: clip` prevents horizontal scroll without
     that side effect, since clip never implies a scroll container. */
  overflow-x: clip;
}

body {
  font-family: var(--font-mono);
  /* NOT text-transform:uppercase here — this is just the page-wide
     DEFAULT font, not every element using it. Since text-transform
     inherits, putting it here would force uppercase onto every piece of
     text on the page (including ones that override to a different font,
     like the hero title) unless THEY also explicitly set it back to
     none. Every element that should actually be uppercase mono text
     (nav, labels, captions, etc.) sets text-transform: uppercase itself,
     right alongside its own font-family: var(--font-mono) — see
     components.css/main.css/project.css. */
  color: var(--color-text-on-dark);
  -webkit-font-smoothing: antialiased;
}

a { color: inherit; text-decoration: none; }
img { display: block; max-width: 100%; }

/* RULE: every button on this site inherits text-transform from its
   context, full stop. `font: inherit` below only carries family / size /
   weight / style / line-height — text-transform isn't part of the font
   shorthand, and buttons (unlike plain text) don't inherit it from an
   ancestor by default, so a button sitting inside an uppercase Syne Mono
   context (nav, footer, etc.) would otherwise render in whatever case it
   was typed in HTML. This is what silently broke the nav's "Contact"
   button. Fixing it once here means no button anywhere on the site can
   slip through un-uppercased, instead of re-declaring text-transform on
   every individual button. */
button { font: inherit; color: inherit; background: none; border: none; cursor: inherit; text-transform: inherit; }

/* ---------- type utility classes (map 1:1 to the tokens) ---------- */

.text-title {
  font-family: var(--font-display);
  font-weight: var(--text-title-weight);
  /* 98pt (--text-title-size) is the mockup's desktop value, not a fixed
     size for every screen — clamp() uses it as the ceiling and scales
     down on narrower viewports so it can never overflow/overlap the nav
     the way an un-clamped 98pt does below ~1200px wide. */
  font-size: clamp(42px, 9vw, var(--text-title-size));
  line-height: var(--text-title-line-height);
  /* NOT using text-transform:capitalize here on purpose: the hero title's
     letters are each split into their own `display: inline-block` span
     for the letter-by-letter animation (see js/main.js), and that breaks
     the word-boundary detection capitalize relies on — every isolated
     letter-box reads as "start of a word," capitalizing ALL of them
     instead of just the first letter per word (this is what caused the
     accidental all-caps look). Simplest fix: just type titles in the
     case you want in the HTML — it's a title, not user input, so there's
     nothing to sanitize here anyway. */
}

.text-h1 {
  font-family: var(--font-display);
  font-weight: var(--text-h1-weight);
  font-size: var(--text-h1-size);
  line-height: var(--text-h1-line-height);
}

.text-h2 {
  font-family: var(--font-mono);
  text-transform: uppercase; /* all Syne Mono text is uppercase, per spec */
  font-weight: var(--text-h2-weight);
  font-size: var(--text-h2-size);
}

.text-subheader {
  font-family: var(--font-mono);
  text-transform: uppercase; /* all Syne Mono text is uppercase, per spec */
  font-weight: var(--text-subheader-weight);
  font-size: var(--text-subheader-size);
}

.text-subheader-section {
  font-family: var(--font-display);
  font-weight: var(--text-subheader-section-weight);
  font-size: var(--text-subheader-section-size);
  line-height: var(--text-subheader-section-line-height);
  letter-spacing: var(--text-subheader-section-tracking);
}

.text-body1 {
  font-family: var(--font-mono);
  text-transform: uppercase; /* all Syne Mono text is uppercase, per spec */
  font-weight: var(--text-body1-weight);
  font-size: var(--text-body1-size);
}

.text-project-body {
  font-family: var(--font-display);
  font-weight: var(--text-project-body-weight);
  font-size: var(--text-project-body-size);
  line-height: var(--text-project-body-line-height);
}

.on-light { color: var(--color-text-on-light); }
.on-dark  { color: var(--color-text-on-dark); }

/* ---------- nav (identical on every page) ---------- */

.site-nav {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  z-index: var(--z-nav);
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 12px var(--grid-margin);
  /* border-top sits flush with the viewport edge (top:0 + no outer
     margin); border-bottom closes the nav off as its own bordered strip,
     per the mockup. currentColor so it adapts automatically to whichever
     page's text color is active (white on the hero, blue on light pages) —
     same trick as .site-footer's divider below. */
  border-top: 1px solid currentColor;
  border-bottom: 1px solid currentColor;
  font-family: var(--font-mono);
  text-transform: uppercase; /* all Syne Mono text is uppercase, per spec */
  font-size: var(--text-nav1-size);
  pointer-events: none; /* re-enabled on the interactive children below */
}

.site-nav * { pointer-events: auto; }

/* Homepage-only load-in (see js/main.js) — a modifier, not the base
   .site-nav rule, so project.html's nav (no load sequence) is unaffected
   and just renders normally at full opacity from the start. */
.site-nav--fade-in {
  opacity: 0;
  transition: opacity 500ms ease;
}

.site-nav--fade-in.is-in {
  opacity: 0.9;
}

.nav-left {
  display: flex;
  align-items: center;
  gap: 16px;
}

/* Logo placeholder: a round-cornered mark standing in for a real logo
   later. A real <a href="index.html"> on every page (including the
   homepage itself) — clicking it always does a genuine page reload, so
   it also resets the homepage's intro animations, not just a scroll. */
.logo-button {
  width: 48px;
  height: 48px;
  border-radius: 16px;
  background: rgba(252, 252, 252, 0.15);
  border: 1px solid rgba(252, 252, 252, 0.4);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 10pt;
  letter-spacing: 0.05em;
  cursor: pointer;
  transition: background var(--duration-fast) var(--ease-smooth);
}

.logo-button:hover {
  background: rgba(252, 252, 252, 0.28);
}

.nav-right {
  display: flex;
  align-items: center;
  gap: 28px;
  font-size: 14pt;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

.nav-right a,
.nav-right button {
  cursor: pointer;
  opacity: 0.85;
  transition: opacity var(--duration-fast) ease, color var(--duration-fast) ease;
}

.nav-right a:hover,
.nav-right button:hover {
  opacity: 1;
  color: var(--color-ink);
}

@media (max-width: 760px) {
  .site-nav { font-size: 16pt; }
}

/* ---------- custom cursor ---------- */
/*
  The real cursor hides (desktop/pointer:fine only) and this label follows
  it. At rest it's just a small dot; hovering a [data-cursor-label] element
  grows it into a filled rectangle showing that element's label text, with
  a fill/text pairing read from --cursor-fill / --cursor-text (set per
  element via inline style or a modifier class) so any paper can have its
  own on-brand contrast pair.
*/

body.custom-cursor-active,
body.custom-cursor-active * {
  cursor: none !important;
}

.cursor-label {
  position: fixed;
  left: 0;
  top: 0;
  z-index: var(--z-cursor-label);
  pointer-events: none;
  transform: translate(-50%, -50%);
  display: flex;
  align-items: center;
  justify-content: center;
  white-space: nowrap;
  overflow: hidden; /* BUG FIX guard: resting dot can never show leftover label text (see collapse() in js/cursor.js) */
  font-family: var(--font-mono);
  text-transform: uppercase; /* all Syne Mono text is uppercase, per spec */
  font-size: 10pt;
  letter-spacing: 0.08em;
  border-radius: 999px;
  background: #FFFFFF;
  color: var(--color-ink);
  /* `difference` inverts against whatever's underneath, so the resting
     dot stays visible on ANY background color automatically — no need to
     hand-pick a contrasting fill per section. Turned off below once the
     label expands with an explicit --cursor-fill/--cursor-text pair,
     since blending would distort those deliberately-chosen colors. */
  mix-blend-mode: difference;
  opacity: 0;
  width: 10px;
  height: 10px;
  padding: 0;
  transition:
    opacity var(--duration-fast) ease,
    width var(--duration-base) var(--ease-smooth),
    height var(--duration-base) var(--ease-smooth),
    padding var(--duration-base) var(--ease-smooth),
    background-color var(--duration-fast) ease,
    color var(--duration-fast) ease;
}

.cursor-label.is-visible {
  opacity: 0.9;
}

/* Over nav-right's "Mind Cloud"/"Contact" (see data-cursor-solid="white"
   below), the text itself turns charcoal (var(--color-ink)) on hover —
   the dot's usual mix-blend-mode:difference trick would react to THAT
   text color change in a way that reads as muddy here, so this state
   forces it to plain, un-blended white instead for a clean, deliberate
   contrast against the now-dark text. */
.cursor-label.is-solid-white {
  background: #FFFFFF;
  mix-blend-mode: normal;
}

.cursor-label.is-expanded {
  width: auto;
  height: auto;
  padding: 10px 20px;
  border-radius: 24px;
  mix-blend-mode: normal;
}

@media (pointer: coarse) {
  .cursor-label { display: none; }
}

/* ---------- paper component (shared base; homepage/project page extend it) ---------- */
/*
  A "paper" is any draggable/clickable card styled like a sheet of paper.
  Hover = drift a few px away from the cursor (set via --drift-x/--drift-y,
  JS-driven). Click = .is-active, which a page-specific rule promotes to
  --z-paper-active. This base class owns the drift + shadow + tape visuals;
  pages own position/size/z-index for their own specific papers.
*/

/*
  Two-layer structure, deliberately: .paper owns WHERE the sheet sits
  (left/top/rotate — set per-paper, either inline or via a modifier
  class) and never gets a hover/drift transform itself. .paper__inner
  owns HOW it reacts (the hover-drift translate, and the click-to-front
  scale bump) via its own independent `transform`. Keeping these on two
  separate elements means the drift animation and the base placement
  never fight over one `transform` property.
*/

.paper {
  position: absolute;
  cursor: pointer;
}

.paper__inner {
  --drift-x: 0px;
  --drift-y: 0px;
  --drift-rot: 0deg;
  position: relative;
  width: 100%;
  height: 100%;
  background: var(--color-paper-white);
  box-shadow: 0 20px 48px rgba(29, 29, 30, 0.22);
  transform: translate(var(--drift-x), var(--drift-y)) rotate(var(--drift-rot));
  transition:
    transform var(--duration-base) var(--ease-smooth),
    box-shadow var(--duration-base) ease;
  will-change: transform;
}

.paper:hover .paper__inner {
  box-shadow: 0 28px 64px rgba(29, 29, 30, 0.3);
}

.paper.is-active {
  z-index: var(--z-paper-active);
}

/* Actively being dragged (js/drag.js) — above the nav, above everything.
   No transition while dragging: it needs to track the pointer 1:1, not
   ease toward it. */
.paper.is-dragging {
  z-index: var(--z-dragging);
  cursor: grabbing;
}

.paper.is-dragging .paper__inner {
  transition: none;
}

/* Persists after the drag ends (see js/drag.js) — once moved, a box stays
   above normal page content permanently, not just mid-drag, since it may
   now sit on top of text/sections at its new, arbitrary position. */
.paper.was-dragged {
  z-index: var(--z-dragging);
}

.paper.is-active .paper__inner {
  box-shadow: 0 36px 80px rgba(29, 29, 30, 0.38);
}

/* Homepage papers only (js/paper.js's hover-to-front rule) — a smooth
   "lift" cue to sell the z-index jump, which has no transition of its
   own. translate() still comes from --drift-x/--drift-y (the hover
   repulsion) — scale is ALL this adds, deliberately no rotate(), ever,
   per spec. */
.paper.is-hover-front .paper__inner {
  transform: translate(var(--drift-x), var(--drift-y)) scale(1.03);
  box-shadow: 0 32px 72px rgba(29, 29, 30, 0.34);
}

.paper__tape {
  position: absolute;
  width: 34px;
  height: 18px;
  background: var(--color-tape);
  opacity: 0.85;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.15);
}

/* ---------- contact modal ---------- */
/*
  Full-viewport paper graphic with a 12px buffer on every side (the outer
  wrapper is transparent — "0% opacity fill" — and sits inset:0; the
  visible paper surface is the inner element, inset by the 12px buffer).
*/

.contact-modal {
  position: fixed;
  inset: 0;
  z-index: var(--z-modal);
  background: rgba(0, 0, 0, 0); /* 0% opacity fill, per spec — purely a click-catcher */
  display: flex;
  opacity: 0;
  pointer-events: none;
  transition: opacity var(--duration-base) var(--ease-smooth);
}

.contact-modal.is-open {
  opacity: 1;
  pointer-events: auto;
}

.contact-modal__paper {
  position: absolute;
  inset: 12px;
  background: var(--color-paper-white);
  display: flex;
  align-items: center;
  justify-content: center;
  transform: scale(0.96);
  transition: transform var(--duration-base) var(--ease-bounce);
}

.contact-modal.is-open .contact-modal__paper {
  transform: scale(1);
}

.contact-modal__email {
  font-family: var(--font-display);
  font-weight: 300;
  font-size: clamp(28px, 6vw, 64px);
  color: var(--color-blue);
  letter-spacing: 0.02em;
  border-bottom: 2px solid transparent;
  transition: border-color var(--duration-fast) ease;
}

.contact-modal__email:hover {
  border-color: var(--color-blue);
}

.contact-modal__close {
  position: absolute;
  top: 24px;
  right: 28px;
  font-family: var(--font-mono);
  text-transform: uppercase; /* all Syne Mono text is uppercase, per spec */
  font-size: 10pt;
  letter-spacing: 0.08em;
  color: var(--color-blue);
  cursor: pointer;
}

/* ---------- site footer (identical on every page) ---------- */

.site-footer {
  padding: 24px var(--grid-margin) 32px;
  border-top: 1px solid currentColor;
}

.site-footer p {
  opacity: 0.6;
  letter-spacing: 0.06em;
}

@media (prefers-reduced-motion: reduce) {
  .paper, .cursor-label, .contact-modal, .contact-modal__paper {
    transition: none !important;
  }
}
