/* ============================================================
   BASE — element styles and global chrome (paper grain,
   reveal animation states, ampersand class, duotone).
   ============================================================ */

*,
*::before,
*::after { box-sizing: border-box; }

* { margin: 0; }

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  text-rendering: optimizeLegibility;
}

/* Honor prefers-reduced-motion globally */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}

body {
  min-height: 100vh;
  font-family: var(--font-body);
  font-size: var(--fs-body);
  font-weight: 400;
  line-height: var(--lh-body);
  color: var(--color-ink);
  background-color: var(--color-paper);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  font-feature-settings: 'kern', 'liga', 'calt';
  text-rendering: optimizeLegibility;
}

img,
picture,
video,
canvas { display: block; max-width: 100%; height: auto; }

svg { display: block; }

input,
button,
textarea,
select { font: inherit; color: inherit; }

button {
  cursor: pointer;
  background: none;
  border: 0;
  padding: 0;
  font-family: inherit;
}

a {
  color: var(--color-ink);
  text-decoration: none;
  transition: color var(--duration-fast) ease;
}

a:hover { color: var(--color-navy); }

a:focus-visible,
button:focus-visible,
input:focus-visible,
textarea:focus-visible,
select:focus-visible {
  outline: 1px solid var(--color-navy);
  outline-offset: 4px;
  border-radius: 2px;
}

p, ul, ol { overflow-wrap: break-word; }
p { text-wrap: pretty; line-height: var(--lh-body); }
li + li { margin-top: var(--space-2); }
ul, ol { padding-left: 1.25rem; }

strong, b { color: var(--color-ink); font-weight: 600; }
em, i { font-style: italic; }

hr {
  border: 0;
  height: 1px;
  background-color: var(--color-hairline);
  margin: var(--space-6) 0;
}

::selection {
  background-color: var(--color-navy);
  color: var(--color-ink-on-dark);
}

/* ============================================================
   HEADINGS — locked to the type scale, Fraunces 500
   ============================================================ */

h1, h2, h3 {
  font-family: var(--font-display);
  color: var(--color-ink);
  font-weight: 500;
  letter-spacing: var(--ls-tight);
  line-height: var(--lh-headline);
  text-wrap: balance;
}

h1 {
  font-size: var(--fs-h1);
}

h2 {
  font-size: var(--fs-h2);
}

h3 {
  font-size: 1.5rem;
}

h4, h5, h6 {
  font-family: var(--font-body);
  color: var(--color-ink);
  font-weight: 500;
  letter-spacing: -0.005em;
  line-height: 1.2;
}

h4 { font-size: 1.125rem; }
h5 { font-size: 1rem; }
h6 { font-size: var(--fs-meta); letter-spacing: var(--ls-meta); text-transform: uppercase; }

/* ============================================================
   THE AMPERSAND CLASS — applied automatically by main.js
   Every literal & in headings, paragraphs, lists, etc.
   becomes <span class="amp">&</span> on DOMContentLoaded.
   ============================================================ */
.amp {
  font-family: var(--font-body);
  color: var(--color-green);
  letter-spacing: 0;
}

/* On dark surfaces the amp keeps its green — green works on navy too */
.section--dark .amp,
.site-footer .amp {
  color: var(--color-green);
}

/* ============================================================
   THE PAPER GRAIN — global surface texture
   Fixed 4% opacity SVG noise overlay on the entire viewport.
   Multiply blend mode tints the page beneath without
   altering color identity. Almost invisible. Removes the
   "screen flatness" that gives away a generic website.
   ============================================================ */
.paper-grain {
  position: fixed;
  inset: 0;
  pointer-events: none;
  z-index: var(--z-grain);
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='280' height='280'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='0.78' numOctaves='2' stitchTiles='stitch' seed='3'/><feColorMatrix values='0 0 0 0 0.10 0 0 0 0 0.12 0 0 0 0 0.18 0 0 0 0.55 0'/></filter><rect width='100%25' height='100%25' filter='url(%23n)' opacity='0.6'/></svg>");
  background-size: 280px 280px;
  opacity: 0.04;
  mix-blend-mode: multiply;
}

@media (prefers-reduced-motion: reduce) {
  .paper-grain { animation: none; }
}

/* ============================================================
   THE DUOTONE FILTER — applied to images for the journal look
   Shadows → navy. Highlights → paper-deep.
   Add the class .duotone to any <img> that should be treated.
   The SVG filter is injected via wp_body_open in inc/helpers.php.
   ============================================================ */
img.duotone,
.duotone img {
  filter: url(#msacct-duotone);
}

/* Container with grain shimmer overlay on duotone images */
.duotone-frame {
  position: relative;
  overflow: hidden;
}

.duotone-frame::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='240' height='240'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='2' stitchTiles='stitch' seed='5'/><feColorMatrix values='0 0 0 0 0.08 0 0 0 0 0.10 0 0 0 0 0.16 0 0 0 0.6 0'/></filter><rect width='100%25' height='100%25' filter='url(%23n)' opacity='0.6'/></svg>");
  background-size: 240px 240px;
  opacity: 0.06;
  mix-blend-mode: multiply;
  animation: grain-shimmer 12s steps(4, end) infinite;
  will-change: transform;
}

/* 2px drift in a 12s loop — almost imperceptible, adds life */
@keyframes grain-shimmer {
  0%   { transform: translate(0, 0); }
  25%  { transform: translate(-1px, 1px); }
  50%  { transform: translate(1px, -1px); }
  75%  { transform: translate(-1px, -1px); }
  100% { transform: translate(0, 0); }
}

@media (prefers-reduced-motion: reduce) {
  .duotone-frame::after { animation: none; }
}

/* Hide the SVG filter container itself */
.msacct-svg-filters {
  position: absolute;
  width: 0;
  height: 0;
  overflow: hidden;
  pointer-events: none;
}

/* ============================================================
   REVEAL ANIMATION STATES
   Driven by IntersectionObserver in main.js. CSS handles the
   transition; JS just toggles .is-revealed.
   Spec: fade-up 24px, 900ms, ease-out-cubic, once.
   ============================================================ */

[data-reveal],
[data-reveal-stagger] > .reveal-target {
  opacity: 0;
  transform: translateY(24px);
  transition:
    opacity var(--duration-reveal) var(--ease-out-cubic),
    transform var(--duration-reveal) var(--ease-out-cubic);
  will-change: opacity, transform;
}

[data-reveal].is-revealed,
[data-reveal-stagger] > .reveal-target.is-revealed {
  opacity: 1;
  transform: translateY(0);
}

/* When prefers-reduced-motion is on, elements appear instantly with full opacity */
@media (prefers-reduced-motion: reduce) {
  [data-reveal],
  [data-reveal-stagger] > .reveal-target {
    opacity: 1;
    transform: none;
    transition: none;
  }
}
