/* Accessibility panel — host adoption layer.
   Panel implementation © 2026 INTEGRITY Ltd. / INTEGRITY Kft., MIT OR Apache-2.0.

   The panel itself (static/js/a11y-panel.js) never touches this site's markup.
   It writes `--a11y-*` custom properties and `data-a11y-*` attributes on <html>,
   and nothing else. This file is the other half of that contract: it maps those
   tokens onto the design system in vendor-tokens.css so the controls actually
   move the page.

   Load order is the whole cascade story — this file must come last, so its
   equal-specificity rules win over vendor-tokens.css and site.css. */

/* ── 1 · Token contract defaults ────────────────────────────────────────
   What the page reads when the panel is absent or its script fails. The
   panel overwrites the scalars inline on :root, which outranks everything
   here by definition. */
:root {
  --a11y-font-scale: 1;
  --a11y-line-height: 1.55;
  --a11y-letter-spacing: 0em;
  --a11y-word-spacing: 0em;
  --a11y-paragraph-spacing: 1.15em;
  --a11y-content-width: 68ch;
  --a11y-focus-width: 2px;
  --a11y-focus-offset: 2px;

  /* Semantic colours as literal hex — the panel's colour editor reads these
     back with getComputedStyle and discards anything that is not hex, so the
     site's color-mix() divider cannot be handed over as-is. */
  --a11y-color-surface: #f2f2f3;          /* = --color-bg */
  --a11y-color-surface-raised: #e9e9ea;   /* = --color-surface */
  --a11y-color-text: #1d1f20;
  --a11y-color-link: #1e73be;
  --a11y-color-border: #d0d0d1;           /* = #1d1f20 at 16% over the bg */
  --a11y-color-focus: #1e73be;
}

:root[data-theme="dark"] {
  --a11y-color-surface: #1a1c1e;
  --a11y-color-surface-raised: #242628;
  --a11y-color-text: #eef0f1;
  --a11y-color-link: #3d8bcb;
  --a11y-color-border: #3c3e40;
  --a11y-color-focus: #3d8bcb;
}

/* ── 2 · Contrast and tint ──────────────────────────────────────────────
   Three orthogonal axes, as the panel models them: polarity, contrast
   ratio, and warmth. High contrast is not "dark mode with more punch" —
   someone who needs 7:1 on paper white is not served by being pushed into
   a dark theme, so these key off the resolved polarity, not instead of it. */
:root[data-a11y-contrast="high"]:not([data-theme="dark"]) {
  --a11y-color-surface: #ffffff;
  --a11y-color-surface-raised: #ffffff;
  --a11y-color-text: #000000;
  --a11y-color-link: #0000cc;
  --a11y-color-border: #000000;
  --a11y-color-focus: #0000cc;
  --a11y-focus-width: 3px;
}
:root[data-a11y-contrast="high"][data-theme="dark"] {
  --a11y-color-surface: #000000;
  --a11y-color-surface-raised: #000000;
  --a11y-color-text: #ffffff;
  --a11y-color-link: #ffff00;
  --a11y-color-border: #ffffff;
  --a11y-color-focus: #ffff00;
  --a11y-focus-width: 3px;
}

:root[data-a11y-tint="warm"]:not([data-theme="dark"]) {
  --a11y-color-surface: #fbf3e4;
  --a11y-color-surface-raised: #f3e7d2;
  --a11y-color-text: #2b2318;
  --a11y-color-link: #8a4a10;
  --a11y-color-border: #8a7659;
  --a11y-color-focus: #8a4a10;
}
:root[data-a11y-tint="warm"][data-theme="dark"] {
  --a11y-color-surface: #1a1510;
  --a11y-color-surface-raised: #241d16;
  --a11y-color-text: #f0e4d2;
  --a11y-color-link: #f3bd7e;
  --a11y-color-border: #756450;
  --a11y-color-focus: #f3bd7e;
}
/* High contrast keeps working under the warm tint: the paper warms, the ink
   stays pure, so the ratio survives. */
:root[data-a11y-contrast="high"][data-a11y-tint="warm"]:not([data-theme="dark"]) {
  --a11y-color-surface: #fffaf0;
  --a11y-color-surface-raised: #fffaf0;
  --a11y-color-link: #00008f;
  --a11y-color-focus: #00008f;
}
:root[data-a11y-contrast="high"][data-a11y-tint="warm"][data-theme="dark"] {
  --a11y-color-surface: #0a0700;
  --a11y-color-surface-raised: #0a0700;
  --a11y-color-text: #fff6e6;
  --a11y-color-border: #fff6e6;
}

/* Under forced colours the OS owns the palette; stop painting our own. */
@media (forced-colors: active) {
  :root, :root[data-theme="dark"] {
    --a11y-color-surface: Canvas;
    --a11y-color-surface-raised: Canvas;
    --a11y-color-text: CanvasText;
    --a11y-color-link: LinkText;
    --a11y-color-border: CanvasText;
    --a11y-color-focus: Highlight;
  }
}

/* ── 3 · Design-system tokens follow the contract ───────────────────────
   The selector list is deliberate: `[data-theme="dark"]` in vendor-tokens.css
   would otherwise outrank a bare `:root` and strand the mapping in dark mode. */
:root, :root[data-theme="dark"] {
  --color-bg: var(--a11y-color-surface);
  --color-surface: var(--a11y-color-surface-raised);
  --color-text: var(--a11y-color-text);
  --color-divider: var(--a11y-color-border);
}

/* Zola's highlighter emits `color-scheme: light dark` plus `light-dark()`
   colours inline on every code block, which makes them follow the OS setting
   and ignore the site's own switch — a white code block on a dark page. The
   inline declaration can only be beaten by an important one; this is the sole
   use of !important in this file, and it targets markup we do not author. */
:root[data-theme="light"] pre.giallo { color-scheme: light !important; }
:root[data-theme="dark"] pre.giallo { color-scheme: dark !important; }

/* ── 4 · Typography ─────────────────────────────────────────────────────
   Every font size and spacing token in this site is rem-based, so scaling
   the root scales the whole page — layout included — instead of leaving
   px-pinned type behind. */
html { font-size: calc(100% * var(--a11y-font-scale)); }

body {
  line-height: var(--a11y-line-height);
  letter-spacing: var(--a11y-letter-spacing);
  word-spacing: var(--a11y-word-spacing);
}

/* Reflow guard (SC 1.4.10). At 200–250% a long headline word is wider than a
   phone viewport, and the default `overflow-wrap: normal` would rather scroll
   the page sideways than break it. Breaking is the lesser harm — and only
   happens when the word genuinely does not fit. */
h1, h2, h3, h4, h5, h6, p, li, dd, dt, figcaption, .card-title { overflow-wrap: break-word; }

/* Paragraph spacing adds to the site's own rhythm rather than replacing it:
   at the default 1.15em this evaluates to exactly --space-3. */
p { margin-bottom: calc(var(--space-3) + var(--a11y-paragraph-spacing) - 1.15em); }

/* Measure, applied to running prose only — cards and grids own their width. */
.simple-page p, .simple-page li,
.kb-article-grid article p, .kb-article-grid article li {
  max-width: var(--a11y-content-width);
}

/* Full-width content. The measure has to be lifted along with the containers,
   or the setting would visibly do nothing on the prose pages — and it cannot
   be done by redefining --a11y-content-width, because the panel writes that
   one inline on :root where no stylesheet can reach it. */
:root[data-a11y-width="full"] main,
:root[data-a11y-width="full"] .hero,
:root[data-a11y-width="full"] .simple-page,
:root[data-a11y-width="full"] .kb-article-grid,
:root[data-a11y-width="full"] .kb-hero h1,
:root[data-a11y-width="full"] .kb-search,
:root[data-a11y-width="full"] .simple-page p,
:root[data-a11y-width="full"] .simple-page li,
:root[data-a11y-width="full"] .kb-article-grid article p,
:root[data-a11y-width="full"] .kb-article-grid article li {
  max-width: none;
}

/* Font choice. C-7 in the panel disables faces that cannot render the active
   script, so an unavailable face is never silently half-substituted here. */
:root[data-a11y-font="sans"]     { --a11y-font-family: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif; }
:root[data-a11y-font="serif"]    { --a11y-font-family: Georgia, "Times New Roman", serif; }
:root[data-a11y-font="mono"]     { --a11y-font-family: ui-monospace, "Cascadia Mono", Menlo, Consolas, monospace; }
:root[data-a11y-font="hyper"]    { --a11y-font-family: "Atkinson Hyperlegible Next", "Atkinson Hyperlegible", Verdana, system-ui, sans-serif; }
:root[data-a11y-font="humanist"] { --a11y-font-family: Verdana, Tahoma, Geneva, system-ui, sans-serif; }

:root[data-a11y-font="sans"],
:root[data-a11y-font="serif"],
:root[data-a11y-font="mono"],
:root[data-a11y-font="hyper"],
:root[data-a11y-font="humanist"] {
  --font-body: var(--a11y-font-family);
  --font-heading: var(--a11y-font-family);
  --font-heading-weight: 700;
}

/* ── 5 · Links and focus ────────────────────────────────────────────────
   Chrome links (nav, footer, TOC, link rows) set their own colour at higher
   specificity and are left alone — this is body-copy link styling. */
a { color: var(--a11y-color-link); }

:root[data-a11y-links="underline"] a { text-decoration: underline; }
:root[data-a11y-links="strong"] a {
  text-decoration: underline;
  text-decoration-thickness: 0.14em;
  text-underline-offset: 0.16em;
  font-weight: 700;
}

:focus-visible {
  outline: var(--a11y-focus-width) solid var(--a11y-color-focus);
  outline-offset: var(--a11y-focus-offset);
}
:root[data-a11y-focus="strong"] :focus-visible {
  outline-width: max(3px, var(--a11y-focus-width));
  outline-offset: 3px;
  box-shadow: 0 0 0 6px color-mix(in srgb, var(--a11y-color-focus) 35%, transparent);
}

/* ── 6 · Motion ─────────────────────────────────────────────────────────
   Nothing in this site animates today; the rule is here so anything added
   later is covered by the control rather than by a later bug report. */
:root[data-a11y-motion="reduced"] *,
:root[data-a11y-motion="reduced"] *::before,
:root[data-a11y-motion="reduced"] *::after {
  animation-duration: 1ms;
  animation-iteration-count: 1;
  transition-duration: 1ms;
  scroll-behavior: auto;
}

/* ── 7 · Images ─────────────────────────────────────────────────────────
   Presentation, not repair, and fully reversible — no markup is rewritten.
   `alt` suppresses the picture and marks the box it occupied; surfacing the
   alt string itself would need a wrapper element the panel is not permitted
   to create. The alt text stays available to assistive technology either way. */
:root[data-a11y-images="hidden"] main img { visibility: hidden; }
:root[data-a11y-images="alt"] main img {
  opacity: 0.05;
  outline: 2px dashed var(--a11y-color-border);
  outline-offset: -2px;
}

/* ── 8 · Pointer, abbreviations, reading mask ───────────────────────────── */
:root[data-a11y-cursor="large"] body {
  cursor: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='48' height='48' viewBox='0 0 24 24'><path d='M3 1l0 20 5-5 3 7 4-2-3-6 7 0z' fill='white' stroke='black' stroke-width='1.5' stroke-linejoin='round'/></svg>") 3 1, auto;
}
:root[data-a11y-cursor="large"] a,
:root[data-a11y-cursor="large"] button {
  cursor: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='48' height='48' viewBox='0 0 24 24'><path d='M9 2a1.6 1.6 0 013 0v7h.5a1.6 1.6 0 013 0V10a1.6 1.6 0 013 0v6a6 6 0 01-6 6h-2a6 6 0 01-6-6V8a1.6 1.6 0 013 0z' fill='white' stroke='black' stroke-width='1.4' stroke-linejoin='round'/></svg>") 8 2, pointer;
}

abbr[title] { text-decoration: underline dotted; text-underline-offset: 0.18em; cursor: help; }
:root[data-a11y-abbr="on"] abbr[title]::after {
  content: " (" attr(title) ")";
  font-style: italic;
  color: color-mix(in srgb, var(--color-text) 65%, transparent);
}

/* The mask is the panel's own overlay; it owns no part of the host DOM. */
#a11y-mask {
  position: fixed; inset: 0; z-index: 2147483000;
  pointer-events: none; display: none; background: transparent;
}
:root[data-a11y-mask="on"] #a11y-mask { display: block; }

/* ── 9 · Panel furniture in this site's chrome ──────────────────────────
   The trigger's own button lives in shadow DOM under `all: initial` and reads
   exactly one token from us, so the chrome behind it is ours to supply. It is
   fixed to the bottom-right corner and icon-only, floating over the page —
   which means it needs a surface of its own to stay legible over whatever it
   happens to be sitting on. The button draws its own 1px border, so the host
   deliberately does not draw a second one. */
a11y-trigger {
  display: inline-flex; align-items: center;
  background: var(--color-accent);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-lg);
  /* The button's shadow DOM runs under `all: initial` and reads exactly one
     token for both its glyph and its border, so setting that token here is
     the only way in — and it has to be the accent's counterpart, not the
     page's text colour, or the icon sits dark-on-blue. */
  --a11y-color-text: var(--color-bg);
}
/* In high contrast the accent is not the promise the mode makes, so the button
   inverts like the primary one does. The glyph token above already resolves to
   the surface colour, which is this background's opposite. */
:root[data-a11y-contrast="high"] a11y-trigger { background: var(--color-text); }

/* The footer used to dim itself with opacity, which this file had to undo so
   the control below could be legible. site.css now sets a real colour on it,
   so there is nothing left here to correct. */
.a11y-footer-hint {
  display: inline-flex; align-items: center; gap: var(--space-2);
  color: var(--color-text);
}
.a11y-footer-hint a { color: inherit; text-decoration: underline; }
.a11y-footer-hint kbd {
  font-family: ui-monospace, Menlo, Consolas, monospace;
  border: 1px solid var(--color-divider);
  border-bottom-width: 2px;
  border-radius: var(--radius-sm);
  padding: 0.1em 0.4em;
}

/* ── 10 · High contrast reaches the components too ──────────────────────
   Swapping the palette tokens is not enough on its own: several components
   dim their own text with a transparency mix, or paint their own accent, and
   both survive a palette change untouched. High contrast is a promise about
   the ratio the user actually gets, so those are brought back to the contract
   palette for as long as the setting is on — and only then. */
:root[data-a11y-contrast="high"] .card-meta,
:root[data-a11y-contrast="high"] .card-body,
:root[data-a11y-contrast="high"] .text-muted,
:root[data-a11y-contrast="high"] figcaption,
:root[data-a11y-contrast="high"] .field > label,
:root[data-a11y-contrast="high"] .table th,
:root[data-a11y-contrast="high"] .kb-breadcrumb,
:root[data-a11y-contrast="high"] .toc-list a,
:root[data-a11y-contrast="high"] .site-footer {
  color: var(--a11y-color-text);
  opacity: 1;
}

/* The brand accent is a mid-tone blue: it carries either black or white text
   at roughly 4:1, which is fine as a default and not fine as "high contrast".
   Both the primary button and the accent card invert instead, which keeps them
   visually distinct from their neighbours at 21:1 rather than flattening them
   into the page. */
:root[data-a11y-contrast="high"] .btn-primary,
:root[data-a11y-contrast="high"] .card-accent {
  background: var(--a11y-color-text);
  color: var(--a11y-color-surface);
  border-color: var(--a11y-color-text);
}
:root[data-a11y-contrast="high"] .card-accent .card-title,
:root[data-a11y-contrast="high"] .card-accent .card-kicker,
:root[data-a11y-contrast="high"] .card-accent .card-body,
:root[data-a11y-contrast="high"] .card-accent .card-meta {
  color: var(--a11y-color-surface);
  opacity: 1;
}

@media print {
  a11y-trigger, a11y-panel, #a11y-mask, .a11y-footer-hint { display: none; }
}
