/* ============================================================================
   base.css — reset, body texture, base typography, global a11y/motion rules.

   Depends on: css/tokens.css (all --tokens below), css/fonts.css (--font-*).
   Load order: fonts.css, tokens.css, base.css, components.css, views.css,
   decor.css, (print.css only inside @media print).

   Assumes from index.html (the orchestrator's markup):
     - <body> is the direct scroll container (no extra fixed-height wrapper
       fighting it); real content sits in normal flow above the fixed
       body::before texture layer (z-index: var(--z-bg)).
     - #particleCanvas — a full-viewport <canvas>, fixed position, that
       js/fx.js draws ambient particles into. This file only gives it a
       neutral fixed/inset-0 placement + pointer-events:none + the shared
       --z-bg layer so it sits behind content but above the plain surface
       color; js/fx.js owns everything drawn inside it.
     - No other element is required to exist for this file to be correct —
       base.css never queries an #id that might not be there yet.
   ============================================================================ */

/* ---------------------------------------------------------------------- */
/* 1. RESET                                                                 */
/* ---------------------------------------------------------------------- */

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

html, body, h1, h2, h3, h4, h5, h6, p, figure, blockquote, dl, dd, ul, ol {
  margin: 0;
}

ul[class], ol[class] { list-style: none; padding: 0; margin: 0; }

html {
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
  scroll-behavior: smooth;
  color-scheme: dark;
}

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

img { height: auto; }

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

button {
  background: none;
  border: none;
  cursor: pointer;
  text-align: inherit;
}

button:disabled { cursor: not-allowed; }

a { color: inherit; text-decoration: none; }

/* explicit — some downstream utility class (flex/grid on the same node) can
   otherwise out-specificity the UA default and make [hidden] visibly render */
[hidden] { display: none !important; }

/* ---------------------------------------------------------------------- */
/* 2. BODY GROUND + REAL TEXTURE (grain + vignette, 0 network requests)     */
/* ---------------------------------------------------------------------- */

html, body {
  min-height: 100%;
}

body {
  position: relative;
  background: var(--surface-1);
  color: var(--text-body);
  font-family: var(--font-sans);
  font-size: var(--fs-base);
  line-height: var(--lh-normal);
  min-height: 100vh;
  min-height: 100dvh;
  overflow-x: hidden;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

/* the actual grain+vignette: a fixed full-viewport layer sitting under all
   content (z-index: var(--z-bg)) but over the flat --surface-1 color.
   Two background-image layers + a matching background-blend-mode list:
   layer 1 (grain, on top) blends via 'overlay' against layer 2 (vignette)
   beneath it — that is what lets one pseudo-element carry both a raster-free
   grain AND a true dark vignette without fighting each other's blend mode. */
body::before {
  content: "";
  position: fixed;
  inset: 0;
  z-index: var(--z-bg);
  pointer-events: none;
  background-image:
    var(--noise-grain),
    radial-gradient(ellipse 90% 80% at 50% 38%, transparent 40%, rgba(0,0,0,0.62) 100%);
  background-repeat: repeat, no-repeat;
  background-size: 180px 180px, cover;
  background-blend-mode: overlay, normal;
  opacity: 0.85;
}

/* faint warm gradient wash beneath the grain so the vellum isn't a flat
   black-brown — reads as candlelight falling from upper-left, scriptorium-style */
body::after {
  content: "";
  position: fixed;
  inset: 0;
  z-index: var(--z-bg);
  pointer-events: none;
  background: radial-gradient(120% 90% at 15% 0%, color-mix(in oklab, var(--gold-dim) 16%, transparent) 0%, transparent 55%);
}

#particleCanvas {
  position: fixed;
  inset: 0;
  z-index: var(--z-bg);
  pointer-events: none;
  width: 100%;
  height: 100%;
  touch-action: none;
}

/* everything else authored (header, nav, main, modals...) rides above the
   background stack by simply being later in DOM flow + z-index: var(--z-app)
   applied by views.css/components.css on the actual layout containers. */

/* ---------------------------------------------------------------------- */
/* 3. TYPOGRAPHY BASE                                                       */
/* ---------------------------------------------------------------------- */

h1, h2, h3, h4, h5, h6 {
  font-family: var(--font-cinzel);
  color: var(--text-heading);
  font-weight: 700;
  line-height: var(--lh-tight);
  letter-spacing: var(--ls-wide);
}

p { color: var(--text-body); }

small, .text-muted { color: var(--text-muted); }

strong, b { color: var(--text-heading); font-weight: 700; }

em, i { font-style: italic; }

a {
  color: var(--text-accent);
  text-underline-offset: 3px;
}
a:hover, a:focus-visible { color: var(--gold-hi); text-decoration: underline; }

code, kbd {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  background: var(--surface-sunken);
  border: var(--border-hairline) solid var(--gold-dim);
  border-radius: var(--radius-sm);
  padding: 0.1em 0.4em;
  font-size: 0.9em;
}

blockquote {
  margin: var(--space-4) 0;
  padding: var(--space-3) var(--space-4);
  border-left: var(--border-thick) solid var(--gold-leaf);
  color: var(--text-muted);
  font-style: italic;
}

/* ---------------------------------------------------------------------- */
/* 3b. ICONS + NATIVE FORM CONTROLS                                         */
/*    A global fallback so any <svg class="icon"> sizes sanely wherever it  */
/*    lands (AW.Icons.html() output is dropped into many different          */
/*    contexts — toasts, modal headers, plain prose); a more specific       */
/*    selector in components.css/views.css (.btn .icon, .nav-tab .icon…)    */
/*    still wins by specificity when one exists. Native checkbox/radio/     */
/*    range inputs (settings.js uses plain <input type="checkbox"/"range">, */
/*    not a custom .switch) get the same gold accent so they're never left  */
/*    rendering in the browser's default blue.                              */
/* ---------------------------------------------------------------------- */

.icon {
  display: inline-block;
  width: 1.2em;
  height: 1.2em;
  vertical-align: -0.2em;
  flex-shrink: 0;
  color: currentColor;
}

input[type="checkbox"],
input[type="radio"] {
  accent-color: var(--gold-leaf);
  width: 1.15em;
  height: 1.15em;
}
input[type="range"] {
  accent-color: var(--gold-leaf);
}
input[type="text"],
input[type="search"],
input[type="email"],
input[type="password"],
input[type="number"],
select,
textarea {
  font-family: var(--font-sans);
  color: var(--text-body);
  background: var(--surface-sunken);
  border: var(--border-thin) solid color-mix(in oklab, var(--gold-leaf) 40%, transparent);
  border-radius: var(--radius-md);
  padding: 0.65em 0.9em;
}
input:focus-visible, select:focus-visible, textarea:focus-visible {
  outline: none;
  border-color: var(--gold-hi);
  box-shadow: var(--shadow-focus);
}

/* ---------------------------------------------------------------------- */
/* 4. SELECTION + SCROLLBAR                                                 */
/* ---------------------------------------------------------------------- */

::selection {
  background: var(--gold-hi);
  color: var(--ink); /* 15.7:1 — see tokens.css contrast table */
}

* { scrollbar-width: thin; scrollbar-color: var(--gold-dim) var(--surface-0); }

::-webkit-scrollbar { width: 8px; height: 8px; }
::-webkit-scrollbar-track { background: var(--surface-0); }
::-webkit-scrollbar-thumb {
  background: var(--gold-dim);
  border-radius: var(--radius-pill);
  border: 2px solid var(--surface-0);
}
::-webkit-scrollbar-thumb:hover { background: var(--gold-leaf); }

/* ---------------------------------------------------------------------- */
/* 5. FOCUS VISIBILITY (no Tailwind reset means no free focus ring either)  */
/* ---------------------------------------------------------------------- */

:focus { outline: none; }

:focus-visible {
  outline: none;
  box-shadow: var(--shadow-focus);
  border-radius: var(--radius-sm);
}

/* ---------------------------------------------------------------------- */
/* 6. GLOBAL prefers-reduced-motion SWITCH                                  */
/*    This is the app-wide kill switch for "all non-essential animation".  */
/*    ONE deliberate, documented exemption below: the primary-button gold  */
/*    shimmer is explicitly spec'd as "subtle and continuous" and stays on.*/
/*    Everything else (float, pulse, egg glow, screen-shake, dice spin,    */
/*    modal/toast transitions, hover lifts) collapses to ~instant.         */
/*    JS-driven motion (canvas particles, translate3d screen-shake, the    */
/*    dice cube's rotation math) is NOT reachable from CSS — js/fx.js      */
/*    checks matchMedia('(prefers-reduced-motion: reduce)') itself and     */
/*    provides the static/no-motion fallback described in its own header.  */
/* ---------------------------------------------------------------------- */

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }

  *, *::before, *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
    transition-delay: 0ms !important;
    scroll-behavior: auto !important;
  }

  /* EXEMPTION — see components.css for the .btn-primary::after rule this
     targets. Keep this selector in sync if that rule is ever renamed. */
  .btn-primary::after {
    animation-duration: var(--dur-shimmer) !important;
    animation-iteration-count: infinite !important;
  }
}

/* ---------------------------------------------------------------------- */
/* 7. js/fx.js HOOKS — screen-shake + full-screen flash                    */
/*    fx.js injects/toggles these two classes itself; it never writes      */
/*    inline transform/opacity, so both effects stay overridable here and  */
/*    both fully disappear under reduced motion (fx.js also skips calling  */
/*    them at all in that case — this is the CSS-side backstop).           */
/* ---------------------------------------------------------------------- */

body.fx-shake {
  animation: fx-shake 420ms var(--ease-standard);
}
@keyframes fx-shake {
  0%   { transform: translate3d(0,0,0); }
  15%  { transform: translate3d(-6px, 2px, 0); }
  30%  { transform: translate3d(5px, -3px, 0); }
  45%  { transform: translate3d(-4px, 3px, 0); }
  60%  { transform: translate3d(3px, -2px, 0); }
  75%  { transform: translate3d(-2px, 1px, 0); }
  100% { transform: translate3d(0,0,0); }
}

/* injected once by AW.FX.init() if not already present */
.fx-flash-overlay {
  position: fixed;
  inset: 0;
  z-index: var(--z-fx-flash);
  background: radial-gradient(circle at 50% 40%, var(--gold-hi) 0%, var(--spirit-glow) 45%, transparent 75%);
  opacity: 0;
  pointer-events: none;
  transition: opacity 480ms var(--ease-standard);
}
.fx-flash-overlay.is-active { opacity: 0.75; transition-duration: 90ms; }

@media (prefers-reduced-motion: reduce) {
  body.fx-shake { animation: none; }
  /* fx.js does not add .is-active at all when reduced motion is on, but if
     a stray call ever did, this keeps it inert rather than flashing. */
  .fx-flash-overlay.is-active { opacity: 0; transition: none; }
}
