/*
 * brains-a11y — axis behaviour (brand-agnostic)
 * ---------------------------------------------------------------------------
 * The eight viewing-preference axes, expressed only as behaviour. This file
 * contains NO brand colours or typefaces — it consumes tokens that a brand
 * file (tokens/shard.css or tokens/brains.css) defines.
 *
 * Every axis is driven by a data-* attribute on <html>, and every axis has a
 * default that renders as "no preference applied". An absent attribute must
 * always read as the stock appearance, so a page renders correctly when
 * storage is blocked, JavaScript fails, or the script has not run yet.
 *
 * Two axes — contrast and reading-font — write tokens the brand files also own.
 * The brand files sit in the `brains-a11y-brand` cascade layer and this file is
 * unlayered, so these rules win without a specificity fight. Do not layer this
 * file, and do not unlayer the brand files: contrast and reading-font silently
 * stop working if either happens.
 *
 * Axis rules are bound to the attribute, not to :root. :root still matches, so
 * a normal page is unaffected — but any container carrying the attributes scopes
 * them to its own subtree, which is what makes a settings preview pane, or two
 * brands side by side, possible. A container that also needs text-size and
 * line-spacing to apply must carry `data-a11y-scope`, because those two work by
 * setting font-size and line-height rather than by inheriting a variable alone.
 *
 *   data-theme         midnight | bone
 *   data-text-spacing  standard | wide
 *   data-measure       standard | narrow
 *   data-decoration    shown | hidden
 *   data-reading-guide off | ruler | focus     (beta)
 *   data-tint          none | warm | cool      (beta)
 *   data-density       comfortable | compact
 *   data-motion        full | reduced
 *   data-contrast      default | high
 *   data-text-size     s | m | l | xl | xxl   (87.5% .. 200%)
 *   data-line-spacing  tight | standard | roomy
 *   data-reading-font  standard | hyperlegible
 *   data-accent        gold | teal | blue
 */

:root {
  /* Axis outputs. Brand files must not redefine these. */
  --a11y-text-scale: 1;
  --a11y-line-height: 1.6;
  --a11y-density: 1;
  --a11y-motion-duration: 200ms;

}

/* Spacing rhythm. Components use these rather than fixed padding, which is what
 * makes density work without enumerating utility classes.
 *
 * Declared on every element, not on :root, and the reason is load-bearing: a
 * custom property is substituted at the element where it is DECLARED, so
 * `--space-4: calc(1rem * var(--a11y-density))` on :root resolves --a11y-density
 * once, at :root, and inherits an already-fixed length. A density change deeper
 * in the tree then moves nothing. Re-declaring per element makes each one
 * resolve its own inherited density. */
*, *::before, *::after {
  --space-1: calc(0.25rem * var(--a11y-density));
  --space-2: calc(0.5rem  * var(--a11y-density));
  --space-3: calc(0.75rem * var(--a11y-density));
  --space-4: calc(1rem    * var(--a11y-density));
  --space-6: calc(1.5rem  * var(--a11y-density));
  --space-8: calc(2rem    * var(--a11y-density));
  --space-12: calc(3rem   * var(--a11y-density));
  --space-16: calc(4rem   * var(--a11y-density));
}

/* ── Text size ──────────────────────────────────────────────────────────────
 * Scales the root font size, so every rem-based measurement follows.
 *
 * The top of the scale is 200%, which is the benchmark WCAG 2.2 SC 1.4.4
 * (Resize Text) sets. It is not a decorative maximum: a person with low vision
 * may genuinely work at it all day. Browser zoom stacks on top of this, so a
 * viewer who needs 400% can reach it by combining the two.
 *
 * A layout that survives 200% here also satisfies SC 1.4.10 (Reflow) — no
 * horizontal scrolling at a 320px viewport — so this axis is the cheapest way
 * to find out whether a surface is genuinely usable or merely passes an audit. */
[data-text-size='s']   { --a11y-text-scale: 0.875; }  /*  87.5% */
[data-text-size='m']   { --a11y-text-scale: 1; }      /* 100%   */
[data-text-size='l']   { --a11y-text-scale: 1.25; }   /* 125%   */
[data-text-size='xl']  { --a11y-text-scale: 1.6; }    /* 160%   */
[data-text-size='xxl'] { --a11y-text-scale: 2; }      /* 200% — SC 1.4.4 */

html,
[data-a11y-scope] { font-size: calc(100% * var(--a11y-text-scale)); }

/* ── Line spacing ────────────────────────────────────────────────────────── */
[data-line-spacing='tight']    { --a11y-line-height: 1.4; }
[data-line-spacing='standard'] { --a11y-line-height: 1.6; }
[data-line-spacing='roomy']    { --a11y-line-height: 1.9; }

body,
[data-a11y-scope] { line-height: var(--a11y-line-height); }

/* ── Density ────────────────────────────────────────────────────────────────
 * Compact reduces the spacing rhythm to ~72%. Deliberately implemented as a
 * single scalar rather than a list of utility-class overrides: the earlier
 * shard-audit implementation enumerated exact Tailwind classes (.mb-28, .mb-24,
 * …) and its own comment recommended this refactor, because any new spacing
 * class silently escaped the compact treatment. */
[data-density='comfortable'] { --a11y-density: 1; }
[data-density='compact']     { --a11y-density: 0.72; }

/* ── Motion ─────────────────────────────────────────────────────────────────
 * Honour the OS setting first; the explicit control can only ever reduce
 * motion further, never re-enable it against a system preference. */
@media (prefers-reduced-motion: reduce) {
  :root { --a11y-motion-duration: 0ms; }
  *, *::before, *::after {
    animation-duration: 0ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0ms !important;
    scroll-behavior: auto !important;
  }
}

[data-motion='reduced'] { --a11y-motion-duration: 0ms; }
[data-motion='reduced'] *,
[data-motion='reduced'] *::before,
[data-motion='reduced'] *::after {
  animation-duration: 0ms !important;
  animation-iteration-count: 1 !important;
  transition-duration: 0ms !important;
  scroll-behavior: auto !important;
}

/* ── Reading font ───────────────────────────────────────────────────────────
 * Atkinson Hyperlegible, chosen for letter-form differentiation. Italics are
 * dropped because sustained italic text defeats the purpose; emphasis falls
 * back to weight. */
[data-reading-font='hyperlegible'] {
  --font-body: 'Atkinson Hyperlegible', system-ui, -apple-system, sans-serif;
  --font-display: 'Atkinson Hyperlegible', system-ui, -apple-system, sans-serif;
}
/* OpenDyslexic. In the beta channel and documented in docs/EVIDENCE.md: it is
 * offered because readers ask for it, not because the evidence supports a
 * reading gain. The consuming surface must load the face; this only names it. */
[data-reading-font='dyslexic'] {
  --font-body: 'OpenDyslexic', 'Comic Sans MS', system-ui, sans-serif;
  --font-display: 'OpenDyslexic', 'Comic Sans MS', system-ui, sans-serif;
}
[data-reading-font='dyslexic'] em,
[data-reading-font='dyslexic'] i,
[data-reading-font='dyslexic'] cite {
  font-style: normal;
  font-weight: 700;
}

[data-reading-font='hyperlegible'] em,
[data-reading-font='hyperlegible'] i,
[data-reading-font='hyperlegible'] cite {
  font-style: normal;
  font-weight: 600;
}
[data-reading-font='hyperlegible'] p { margin-block: 1em; }

/* ── Contrast ───────────────────────────────────────────────────────────────
 * Three settings, not two, because contrast is not a single dial that everyone
 * wants turned up. High strengthens borders and lifts muted text to full ink.
 * Soft does the opposite: it lowers luminance and takes the edge off pure
 * white and pure black, which is what many autistic, migraine-prone and
 * photophobic readers actually need. Offering only "more" quietly assumes
 * everyone's need points the same way. */
[data-contrast='high'] {
  --ink-muted: var(--ink-muted-high, var(--ink));
  --line: var(--line-high, currentColor);
}
[data-contrast='high'] :focus-visible {
  outline-width: 3px;
}

[data-contrast='soft'] {
  --bg: var(--bg-soft, var(--bg));
  --surface: var(--surface-soft, var(--surface));
  --ink: var(--ink-soft, var(--ink));
  --ink-muted: var(--ink-muted-soft, var(--ink-muted));
}

/* ── Letter and word spacing ────────────────────────────────────────────────
 * The values are those a page must survive under WCAG 2.2 SC 1.4.12 (Text
 * Spacing), offered as a control rather than only tested against. Loosening
 * tracking is one of the better-supported adjustments for dyslexic readers. */
[data-text-spacing='wide'] {
  --a11y-letter-spacing: 0.12em;
  --a11y-word-spacing: 0.16em;
  --a11y-paragraph-spacing: 2em;
}
[data-text-spacing='wide'] p,
[data-text-spacing='wide'] li,
[data-text-spacing='wide'] dd,
[data-text-spacing='wide'] blockquote {
  letter-spacing: var(--a11y-letter-spacing);
  word-spacing: var(--a11y-word-spacing);
}
[data-text-spacing='wide'] p + p { margin-block-start: var(--a11y-paragraph-spacing); }

/* ── Line length ────────────────────────────────────────────────────────────
 * Long lines lose the reader on the return sweep. Standard caps running text
 * at a conventional measure; narrow tightens it further, which helps readers
 * who track line-to-line with difficulty. Applies to running text only —
 * never to tables, code or layout containers. */
[data-measure] :is(p, li, dd, blockquote, h1, h2, h3, h4) { max-width: var(--a11y-measure, 72ch); }
[data-measure='standard'] { --a11y-measure: 72ch; }
[data-measure='narrow']   { --a11y-measure: 52ch; }

/* ── Decorative imagery ─────────────────────────────────────────────────────
 * Hides imagery that carries no information, for readers who find it noise.
 * Deliberately opt-in per element: a surface marks what is decorative with
 * `data-decorative`, because only the surface knows. Nothing that conveys
 * meaning should ever carry that attribute. */
[data-decoration='hidden'] [data-decorative] { display: none !important; }
[data-decoration='hidden'] [data-decorative-bg] { background-image: none !important; }

/* ── Reading guide (beta) ───────────────────────────────────────────────────
 * Ruler draws a band across the line under the pointer or caret. Focus dims
 * everything except the section being read. Both need the small module in
 * core/reading-guide.mjs to track position; the CSS alone does nothing. */
[data-reading-guide='ruler'] .a11y-ruler {
  position: fixed;
  left: 0;
  right: 0;
  height: var(--a11y-ruler-height, 2.4em);
  background: var(--accent);
  opacity: 0.14;
  pointer-events: none;
  z-index: 2147483000;
  transform: translateY(var(--a11y-ruler-y, 0));
}
[data-reading-guide='focus'] [data-a11y-section] {
  opacity: 0.35;
  transition: opacity var(--a11y-motion-duration);
}
[data-reading-guide='focus'] [data-a11y-section][data-a11y-current] { opacity: 1; }

/* ── Paper tint (beta) ──────────────────────────────────────────────────────
 * A coloured ground, which some readers report helps and others do not. The
 * research on coloured overlays is contested; this is offered as a preference,
 * never as a treatment, and docs/EVIDENCE.md says so plainly. Light grounds
 * only — tinting a dark ground does not do the same thing. */
[data-theme='bone'][data-tint='warm'] { --bg: var(--bg-tint-warm, #F7EFE0); }
[data-theme='bone'][data-tint='cool'] { --bg: var(--bg-tint-cool, #EAF0F4); }

/* ── Operating-system signals ───────────────────────────────────────────────
 * A person who has already told their OS what they need should not have to
 * tell every website as well. These apply automatically; the explicit controls
 * remain available to go further. */

/* Windows High Contrast Mode and similar forced palettes. The browser replaces
 * our colours wholesale, which flattens any state we signalled with background
 * alone — so the selected option is re-stated in system colours that survive. */
@media (forced-colors: active) {
  .a11y-panel__option[data-active] {
    background: Highlight;
    color: HighlightText;
    border-color: Highlight;
  }
  .a11y-panel__option { border-color: ButtonBorder; }
  :focus-visible { outline-color: Highlight; }
  .a11y-skip-link { background: Highlight; color: HighlightText; }
}

/* The OS asked for more contrast. Same treatment as data-contrast="high". */
@media (prefers-contrast: more) {
  :root {
    --ink-muted: var(--ink-muted-high, var(--ink));
    --line: var(--line-high, currentColor);
  }
}

/* The OS asked for less transparency — brand files may opt in by defining
 * --line-opaque and --overlay-bg-opaque. */
@media (prefers-reduced-transparency: reduce) {
  :root {
    --line: var(--line-opaque, var(--line));
    --overlay-bg: var(--overlay-bg-opaque, var(--overlay-bg));
  }
}

/* ── Focus, always ──────────────────────────────────────────────────────────
 * Never removed by any axis. */
:focus-visible {
  outline: 2px solid var(--focus-ring, currentColor);
  outline-offset: 2px;
}

/* ── Skip link ──────────────────────────────────────────────────────────── */
.a11y-skip-link {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: 999;
  padding: var(--space-2) var(--space-4);
  background: var(--accent);
  color: var(--accent-ink);
  font-family: var(--font-body);
  text-decoration: none;
  border-radius: 3px;
}
.a11y-skip-link:focus {
  left: var(--space-2);
  top: var(--space-2);
}

/* ── Screen-reader-only utility ─────────────────────────────────────────── */
.a11y-sr-only {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}
