/* Accessibility display-preference widget (#1208, ported from StripyFish.Net's
   css/style.css a11y rules — StripyFish.Net#7).

   This project's pages split into two unrelated visual families: a light
   OKLCH portal theme driven by design-tokens.css custom properties
   (portal.html, portal-home.html, telephony-dashboard.html, help-guide.html,
   dpa-reseller-accept.html) and a dark-navy/gold marketing+assessment family
   that hardcodes literal colors with no custom properties at all (index.html,
   landing.html, privacy.html, terms.html, dpa*.html) — see
   check_css_tokens.sh, which fails the build on any CSS custom property
   reference anywhere under templates/ or static/ that design-tokens.css
   doesn't declare. Since the widget mounts on both families, its own chrome
   is self-contained (hardcoded literals, no custom properties) so it renders
   identically regardless of which family the host page belongs to, rather
   than adopting design-tokens.css and becoming invisible/unstyled on the
   pages that don't load that file. */

@font-face {
  font-family: 'Atkinson Hyperlegible';
  src: url('/static/fonts/atkinson-hyperlegible-regular.woff2') format('woff2');
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: 'Atkinson Hyperlegible';
  src: url('/static/fonts/atkinson-hyperlegible-bold.woff2') format('woff2');
  font-weight: 700;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: 'OpenDyslexic';
  src: url('/static/fonts/opendyslexic-regular.woff2') format('woff2');
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: 'OpenDyslexic';
  src: url('/static/fonts/opendyslexic-bold.woff2') format('woff2');
  font-weight: 700;
  font-style: normal;
  font-display: swap;
}

/* Font choice: unlike StripyFish (which reuses a single shared font custom
   property every component already reads), no page family here exposes an
   equivalent — index.html/landing.html hardcode 'Outfit'/'Cormorant
   Garamond' per-selector, and the portal family's own font token is applied
   inconsistently across components. A universal !important override is the
   only selector that reliably wins against that many pre-existing
   declarations; scoped out of the widget itself so its own "Aa" toggle
   doesn't relabel into the chosen font. */
html[data-font="atkinson"] *:not(.a11y-widget):not(.a11y-widget *) {
  font-family: 'Atkinson Hyperlegible', system-ui, -apple-system, sans-serif !important;
}
html[data-font="opendyslexic"] *:not(.a11y-widget):not(.a11y-widget *) {
  font-family: 'OpenDyslexic', system-ui, -apple-system, sans-serif !important;
}
html[data-font="monospace"] *:not(.a11y-widget):not(.a11y-widget *) {
  font-family: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, monospace !important;
}

/* Text size: every template in this project hardcodes font-size in px
   rather than rem (verified across index.html, landing.html, privacy.html,
   portal.html), so StripyFish's `html { font-size: 112% }` root-scaling
   trick — which only affects rem/em/%-sized descendants — would be a no-op
   here. `zoom` scales the whole rendered page (including px-sized layout)
   the same way native browser zoom does, and is supported by every engine
   this product targets. */
html[data-text-size="112"] { zoom: 1.12; }
html[data-text-size="125"] { zoom: 1.25; }
/* zoom composes multiplicatively down the tree rather than being an inherited
   value that can just be unset, so the widget's own chrome (unlike the
   font-family/line-spacing rules below, which exclude it via :not()) has to
   counter-scale by the reciprocal to stay a fixed, predictable size instead
   of growing/drifting alongside body copy. */
html[data-text-size="112"] .a11y-widget { zoom: calc(1 / 1.12); }
html[data-text-size="125"] .a11y-widget { zoom: calc(1 / 1.25); }

/* Line spacing: !important is deliberate — this is a user-triggered,
   opt-in accessibility override (WCAG 1.4.12 expects author styles to yield
   to it) and needs to reliably beat hardcoded line-height/letter-spacing
   values throughout the codebase without a per-page specificity fight.
   Excludes nav and the widget's own chrome, which aren't body copy. */
html[data-line-spacing="relaxed"] :where(p, li):not(nav *):not(.a11y-widget *) {
  line-height: 1.9 !important;
  letter-spacing: 0.01em !important;
  word-spacing: 0.03em !important;
}

/* --- Widget chrome (self-contained; see file header) --- */
/* Bottom-anchored positions default to a raised 5rem offset rather than
   hugging the very corner. Floating bottom-corner controls are common
   across the site — support-chat launchers (#support-btn /
   #portal-support-btn), the legal-doc pages' "back to top" jump-to-menu
   button (privacy.html etc.) — and used to need enumerating one at a time
   via a body:has(#that-specific-id) override each time a new one turned
   up (originally just the support launcher, #1230/#1232; a gap-report
   iframe collision fixed separately in #1342 exposed the same pattern
   again). A shared raised baseline eliminates that per-element
   enumeration for any *static* page element — it does NOT replace the
   GA4 consent-banner override below, which still uses the same
   body:has() approach on top of this baseline for a different reason:
   that banner is only in the DOM transiently (first visit, until
   Accept/Decline), so it needs its own conditional push rather than
   raising the baseline further for everyone. The panel's own position
   (`bottom: calc(100% + 0.75rem)` in the rule below) is relative to the
   toggle, not the viewport, so it follows this offset automatically
   without a separate override. */
.a11y-widget {
  position: fixed;
  z-index: 2147483647;
  bottom: 5rem;
  right: 1.25rem;
}
.a11y-widget[data-pos="bottom-left"] { right: auto; left: 1.25rem; }
.a11y-widget[data-pos="top-right"] { bottom: auto; top: 1.25rem; }
.a11y-widget[data-pos="top-left"] { bottom: auto; top: 1.25rem; right: auto; left: 1.25rem; }

/* The GA4 consent banner (#1237, ga-consent.css) is a full-width bar
   pinned to the very bottom of the viewport and outranks this widget's
   z-index while visible, so a bottom-anchored toggle would render inside
   or under it even at the raised 5rem baseline above. Push clear a
   little further only while the banner is actually present; it's
   removed from the DOM the moment the visitor picks Accept/Decline, so
   this is a first-visit-only accommodation, not a permanent offset. */
body:has(.ga-consent-banner) .a11y-widget[data-pos="bottom-right"],
body:has(.ga-consent-banner) .a11y-widget[data-pos="bottom-left"] {
  bottom: 5.5rem;
}

/* The gap-report iframe's evidence-chat compose bar (.chat-input-wrap,
   ~85-90px tall) is pinned to the bottom of its own document and centred —
   up to 960px in gap.html (sysc10a, pending retirement), up to 800px in
   gap-aware.html (gdpr, standardized by #1380) — on any viewport narrower
   than that it spans full width, so its send button lands in this same
   bottom-right corner, at or past the raised 5rem baseline above. This file lives in the
   parent shell with no visibility into the iframe's content, but
   #content.gap-report-active (toggled in displayPage()/​
   _openLatestGdprGapReport(), portal-home.js) reliably marks exactly when
   that iframe is on screen — matches the #portal-support-btn override for
   the same page in portal-home.html's own <style> block, kept in sync. */
body:has(#content.gap-report-active) .a11y-widget[data-pos="bottom-right"],
body:has(#content.gap-report-active) .a11y-widget[data-pos="bottom-left"] {
  bottom: 9.5rem;
}

.a11y-toggle {
  width: 46px;
  height: 46px;
  border-radius: 50%;
  background: #1c2333;
  border: 1px solid #3a4358;
  color: #f4f1ea;
  font-family: 'Outfit', system-ui, -apple-system, sans-serif;
  font-weight: 700;
  font-size: 15px;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  box-shadow: 0 4px 16px rgba(0,0,0,0.35);
  transition: border-color 0.2s, color 0.2s;
}
.a11y-toggle:hover,
.a11y-toggle[aria-expanded="true"] {
  border-color: #c8a55a;
  color: #c8a55a;
}
.a11y-toggle:focus-visible {
  outline: 2px solid #c8a55a;
  outline-offset: 2px;
}

.a11y-panel {
  position: absolute;
  width: min(300px, calc(100vw - 2.5rem));
  max-height: min(420px, calc(100vh - 6rem));
  overflow-y: auto;
  background: #1c2333;
  border: 1px solid #3a4358;
  border-radius: 12px;
  padding: 20px;
  box-shadow: 0 12px 32px rgba(0,0,0,0.45);
}
.a11y-panel[hidden] { display: none; }
.a11y-widget[data-pos="bottom-right"] .a11y-panel { bottom: calc(100% + 0.75rem); right: 0; }
.a11y-widget[data-pos="bottom-left"] .a11y-panel { bottom: calc(100% + 0.75rem); left: 0; }
.a11y-widget[data-pos="top-right"] .a11y-panel { top: calc(100% + 0.75rem); right: 0; }
.a11y-widget[data-pos="top-left"] .a11y-panel { top: calc(100% + 0.75rem); left: 0; }

.a11y-panel-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 16px;
}
.a11y-panel-header h2 {
  font-family: 'Outfit', system-ui, -apple-system, sans-serif;
  font-size: 14px;
  font-weight: 600;
  color: #f4f1ea;
  margin: 0;
}
.a11y-panel-close {
  background: none;
  border: none;
  color: #9aa3b8;
  font-size: 18px;
  line-height: 1;
  cursor: pointer;
  padding: 3px 6px;
}
.a11y-panel-close:hover { color: #f4f1ea; }
.a11y-panel-close:focus-visible { outline: 2px solid #c8a55a; outline-offset: 2px; }

.a11y-group {
  /* Browsers give fieldset a default border/padding/min-width (a light
     "groove" box) that was never reset here — it doesn't match this
     widget's borderless panel chrome, and its extra box height is what
     made each option row read as pinned to the bottom of an oversized
     section rather than sitting directly under its legend. */
  border: none;
  margin: 0 0 16px;
  padding: 0;
  min-width: 0;
}
.a11y-group:last-of-type { margin-bottom: 20px; }
.a11y-group legend {
  font-family: 'Outfit', system-ui, -apple-system, sans-serif;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: #9aa3b8;
  margin-bottom: 8px;
  padding: 0;
}
.a11y-option-row {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}

/* Native radio inputs, visually hidden but still in the tab order and
   keyboard-operable (arrow keys move within the group, Space selects) —
   avoids re-implementing roving-tabindex/ARIA radiogroup logic in JS.
   Each input is absolutely positioned inside its OWN small wrapper (inset:0)
   rather than the whole panel — with no wrapper, an all-auto-offset
   `position: absolute` collapses every option in every group to the same
   static-position point, so clicks silently land on whichever option
   happens to paint on top instead of the one under the visible label. */
.a11y-option {
  position: relative;
  display: inline-flex;
}
.a11y-option-input {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  margin: 0;
  opacity: 0;
  cursor: pointer;
}
.a11y-option-label {
  display: inline-flex;
  align-items: center;
  font-family: 'Outfit', system-ui, -apple-system, sans-serif;
  font-size: 12.5px;
  font-weight: 500;
  padding: 6px 12px;
  border-radius: 100px;
  border: 1px solid #3a4358;
  color: #9aa3b8;
  cursor: pointer;
  transition: border-color 0.15s, color 0.15s, background 0.15s;
}
.a11y-option-input:checked + .a11y-option-label {
  border-color: #c8a55a;
  color: #c8a55a;
  background: rgba(200, 165, 90, 0.12);
}
.a11y-option-input:focus-visible + .a11y-option-label {
  outline: 2px solid #c8a55a;
  outline-offset: 2px;
}

.a11y-panel-reset {
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: 'Outfit', system-ui, -apple-system, sans-serif;
  font-size: 12.5px;
  font-weight: 500;
  padding: 8px 12px;
  border-radius: 8px;
  border: 1px solid #3a4358;
  background: transparent;
  color: #f4f1ea;
  cursor: pointer;
  transition: border-color 0.15s, background 0.15s;
}
.a11y-panel-reset:hover { border-color: #c8a55a; background: rgba(200, 165, 90, 0.08); }
.a11y-panel-reset:focus-visible { outline: 2px solid #c8a55a; outline-offset: 2px; }
