/* q4u_accessibility — widget + preference effects.
   Hand-written CSS (no build step), same approach as q4u_help_dialog.
   Theme-specific reactions to data-bs-contrast / data-bs-transparency (header glass)
   stay in q4u_base's theme SCSS — this file only carries generic effects. */

/* ─────────────────────────── Fonts ─────────────────────────── */
@font-face { font-family: 'OpenDyslexic'; font-weight: 400; font-style: normal; font-display: swap;
  src: url('../Fonts/OpenDyslexic/OpenDyslexic-Regular.woff2') format('woff2'); }
@font-face { font-family: 'OpenDyslexic'; font-weight: 700; font-style: normal; font-display: swap;
  src: url('../Fonts/OpenDyslexic/OpenDyslexic-Bold.woff2') format('woff2'); }

/* ─────────────────────────── Launcher ───────────────────────────
   Geometry, colours, label chip and dot come from q4u_base's floating-controls
   family (.q4u-fab, components/_floating-controls.scss). The rules below are the
   STANDALONE fallback for installs without q4u_base, scoped to
   html:not([data-q4u-floating]) — with q4u_base present they never apply. */
html:not([data-q4u-floating]) .q4u-a11y-launcher {
  position: fixed; bottom: 16px; left: 16px; width: 56px; height: 56px;
  display: inline-flex; align-items: center; justify-content: center;
  border: 0; border-radius: 50%; padding: 0; cursor: pointer;
  background-color: var(--bs-primary, #3d3a61); color: #fff;
  box-shadow: 0 2px 10px rgba(0, 0, 0, .18);
  z-index: 990;
}
html:not([data-q4u-floating]) .q4u-a11y-launcher .q4u-fab__icon { width: 28px; height: 28px; }
html:not([data-q4u-floating]) .q4u-a11y-launcher .q4u-fab__label {
  position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap;
}
html:not([data-q4u-floating]) .q4u-a11y-launcher__dot {
  position: absolute; top: 4px; right: 4px; width: 12px; height: 12px; border-radius: 50%;
  background: var(--bs-warning, #ffc107); border: 2px solid var(--bs-body-bg, #fff); display: none;
}
html:not([data-q4u-floating]) .q4u-a11y-launcher.is-active .q4u-a11y-launcher__dot { display: block; }
html:not([data-q4u-floating]) .q4u-a11y-launcher:focus-visible { outline: 3px solid var(--bs-emphasis-color, #000); outline-offset: 3px; }
.q4u-a11y-panel :is(button, input, a):focus-visible {
  outline: 3px solid var(--bs-primary);
  outline-offset: 2px;
}

/* Dismissed for this browser session. q4u_base's family rule says "render nothing,
   rather than render and hide", because the empty-anchor compensation keys on
   :has(), which tests presence in the DOM rather than visibility. We cannot obey
   the letter of it here: the state is known in <head>, where the launcher markup
   is not parsed yet, and removing it later in deferred JS would let the button
   the visitor just dismissed flash up first. So we keep its effect instead and
   set the base offset ourselves — the cookie disc moves down exactly as it would
   if nothing had been rendered. Documented as an exception in
   docs/superpowers/specs/2026-09-17-floating-controls-design.md. */
html[data-a11y-launcher-hidden] .q4u-a11y-launcher { display: none; }
html[data-a11y-launcher-hidden] { --q4u-fab-base-start: 1; }

/* ─────────────────────────── Panel ─────────────────────────── */
/* Position, z-index and height clamp: q4u_base .q4u-fab-panel--start (anchored above
   the whole left stack, bottom sheet below 576px). No own max-height — the family's
   clamp keeps the panel below the sticky header; an own one written for the old 84px
   anchor would push the first rows above the viewport.
   The panel must NOT be a scroll container itself, though: the family gives every
   .q4u-fab-panel `overflow-y: auto`, and with the whole flex column scrollable the
   header (title + close) and the reset footer scrolled out of view — measured at
   scrollTop 300 the header sat 299px above the panel's top edge. `overflow: hidden`
   here keeps the chrome put and leaves __body as the only scroller.
   Standalone fallback below. */
.q4u-a11y-panel {
  width: 24rem;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  background-color: var(--bs-body-bg);
  color: var(--bs-body-color);
  border: 1px solid rgba(var(--bs-emphasis-color-rgb), .08);
  border-radius: var(--bs-border-radius);
  box-shadow: 0 8px 30px rgba(var(--bs-emphasis-color-rgb), .18);
  /* .cf-card carries margin-bottom: 1.5rem for stacked document-flow cards
     (q4u_base components/_solr-results.scss); on this position:fixed panel that
     margin pushes the border-box 24px above where q4u-fab-panel--start's `bottom`
     calc places it, breaking the family's flush-above-the-stack anchor. Cancel it here. */
  margin-bottom: 0;
}
html:not([data-q4u-floating]) .q4u-a11y-panel {
  position: fixed; bottom: 84px; left: 16px; max-width: calc(100vw - 32px);
  max-height: calc(100vh - 100px); z-index: 995;
}
.q4u-a11y-panel[hidden] { display: none; }
.q4u-a11y-panel__header {
  display: flex; align-items: center; justify-content: space-between; gap: .5rem;
  padding: .75rem 1rem;
  background-color: rgba(var(--bs-emphasis-color-rgb), .03);
  border-bottom: 1px solid rgba(var(--bs-emphasis-color-rgb), .08);
}
.q4u-a11y-panel__actions { display: flex; gap: .5rem; }
/* The only scroller in the panel. min-height: 0 lets a flex item shrink below its
   content size — without it the body would push the footer out instead of scrolling.
   overscroll-behavior stops a wheel/touch gesture that reaches the end from chaining
   into the page behind the panel. */
.q4u-a11y-panel__body {
  padding: 1rem;
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
}
.q4u-a11y-panel__footer {
  display: flex; align-items: center; justify-content: space-between; gap: 1rem;
  padding: .5rem 1rem; border-top: 1px solid rgba(var(--bs-emphasis-color-rgb), .06); font-size: .875rem;
}
.q4u-a11y-panel__reset,
.q4u-a11y-panel__hide { color: var(--bs-body-color); text-decoration: underline; }
.q4u-a11y-panel__reset:hover,
.q4u-a11y-panel__hide:hover { color: var(--bs-primary); }
.q4u-a11y-group legend { font-weight: 600; }
/* Divider between fieldsets — Bootstrap's mb-4 alone left no visible section boundary. */
.q4u-a11y-group + .q4u-a11y-group {
  border-top: 1px solid rgba(var(--bs-emphasis-color-rgb), .08);
  padding-top: 1rem;
}
/* The same partial renders in two very different places: the floating panel, 24rem wide,
   and the settings page, a full content column. Four full-width cards there read as a long
   list running down half the screen. A container query picks the layout from the space the
   list actually has, so neither surface needs to know where it is — and a browser without
   container query support keeps the single column the markup ships with. Two columns, not
   auto-fit: on a wide page auto-fit would put all four in a row and lose the pairing. */
.q4u-a11y-profiles {
  container-type: inline-size;
}

@container (min-width: 34rem) {
  .q4u-a11y-profiles__list {
    grid-template-columns: 1fr 1fr;
  }
}

/* The profile cards borrow .btn for its focus, hover and pressed states, but a button's
   typography is written for one centred word. The theme centres button content and sets
   font-weight 600, so the icon-plus-text block floated in the middle of a 448px card with
   a wide empty gutter beside it, and the description came out as bold as its own title.
   `text-start` could not fix that: it sets text-align, while the block's position comes
   from justify-content. These three declarations give the card back its card behaviour;
   everything else still comes from .btn. */
.q4u-a11y-profile {
  justify-content: flex-start;
  padding: .75rem 1rem;
  font-weight: 400;
}
.q4u-a11y-profile__icon { color: var(--bs-primary); flex-shrink: 0; margin-top: .1rem; }
.q4u-a11y-profile__icon svg { display: block; }
.q4u-a11y-profile__label { margin-bottom: .15rem; }
.q4u-a11y-profile__desc { font-weight: 400; line-height: 1.4; }
.q4u-a11y-profile[aria-pressed="true"] {
  background-color: var(--bs-primary); color: #fff; border-color: var(--bs-primary);
}
/* .q4u-a11y-profile__desc carries Bootstrap's .text-body-secondary utility class, whose own
   rule is `color: var(--bs-secondary-color) !important` — a plain `color: inherit` here loses
   to that !important unconditionally, in every theme and every contrast mode. Verified live on
   two different site themes: pressed/hovered cards kept the muted secondary color (near-black
   in one theme) instead of turning light, becoming close to unreadable against a dark fill. */
.q4u-a11y-profile[aria-pressed="true"] .q4u-a11y-profile__desc { color: inherit !important; opacity: .85; }
.q4u-a11y-profile[aria-pressed="true"] .q4u-a11y-profile__icon { color: inherit; }
/* :hover fills the background red (theme's own --bs-btn-hover-bg) but, in this build,
   does not reliably resolve --bs-btn-hover-color for text/icons — verified live: hovering
   an unchecked/unpressed btn-outline-primary here left red text on the now-red background,
   invisible. Force it explicitly rather than depend on the theme's hover custom property. */
.q4u-a11y-profile.btn-outline-primary:hover,
.q4u-a11y-choice .btn-outline-primary:hover {
  color: #fff;
}
.q4u-a11y-profile:hover .q4u-a11y-profile__desc { color: inherit !important; }
.q4u-a11y-profile:hover .q4u-a11y-profile__icon { color: inherit; }

@media (max-width: 575.98px) {
  .q4u-a11y-panel {
    width: auto;
    border-radius: var(--bs-border-radius) var(--bs-border-radius) 0 0;
  }
  html:not([data-q4u-floating]) .q4u-a11y-panel {
    /* max-width belongs here, not unscoped: the fallback's own calc(100vw - 32px)
       out-specifies an unscoped `max-width: none` and left a 32px gap at the edge. */
    left: 0; right: 0; bottom: 0; max-width: none; max-height: 80dvh;
  }
  html:not([data-q4u-floating]) .q4u-a11y-launcher { bottom: 12px; left: 12px; }
}

/* ─────────────────────────── Text ─────────────────────────── */
/* moved from q4u_base _tactile-enhancements.scss 437–443, extended by 200 */
html[data-fontsize="125"] { font-size: 125%; }
html[data-fontsize="150"] { font-size: 150%; }
html[data-fontsize="200"] { font-size: 200%; }

html[data-a11y-lineheight="large"]  body { line-height: 1.8; }
html[data-a11y-lineheight="xlarge"] body { line-height: 2.1; }
html[data-a11y-lineheight] :is(h1,h2,h3,h4,h5,h6,.h1,.h2,.h3,.h4,.h5,.h6) { line-height: 1.4; }

html[data-a11y-letterspacing="large"]  body { letter-spacing: .08em; word-spacing: .16em; }
html[data-a11y-letterspacing="xlarge"] body { letter-spacing: .14em; word-spacing: .28em; }

/* Bolder text. Body AND headings move together on purpose: the theme sets
   headings to font-weight 500 and body text to 400, so lifting only the body
   to 600 would make running text heavier than the headings above it and flip
   the visual hierarchy. strong/b go a step further again so emphasis stays
   readable as emphasis. */
html[data-a11y-fontweight="bold"] body { font-weight: 600; }
html[data-a11y-fontweight="bold"] :is(h1,h2,h3,h4,h5,h6,.h1,.h2,.h3,.h4,.h5,.h6) { font-weight: 700; }
html[data-a11y-fontweight="bold"] :is(strong, b) { font-weight: 800; }

/* moved from q4u_base _tactile-enhancements.scss 471–481 */
html[data-font="opendyslexic"] {
  --bs-font-sans-serif: 'OpenDyslexic', sans-serif;
  --bs-body-font-family: 'OpenDyslexic', sans-serif;
  font-family: 'OpenDyslexic', sans-serif !important;
}
html[data-font="system"] {
  --bs-font-sans-serif: system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;
  --bs-body-font-family: system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;
  font-family: system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif !important;
}

/* ─────────────────────────── Colour ─────────────────────────── */
/* filter on the ROOT element keeps position:fixed intact (verified 2026-09-11) */
html[data-a11y-saturation="reduced"]   { filter: saturate(.5); }
html[data-a11y-saturation="grayscale"] { filter: grayscale(1); }

/* ─────────────────────────── Reading & orientation ─────────────────────────── */
html[data-a11y-highlight-links] main a:not(.btn):not(.q4u-a11y-panel a) {
  text-decoration: underline;
  text-decoration-thickness: 2px;
  outline: 2px solid currentColor;
  outline-offset: 2px;
  background-color: rgba(var(--bs-emphasis-color-rgb), .06);
}
html[data-a11y-highlight-links] main a:not(.btn):not(.q4u-a11y-panel a):focus-visible {
  outline: 3px solid var(--bs-primary);
  outline-offset: 4px;
}
html[data-a11y-highlight-headings] main :is(h1,h2,h3,h4,h5,h6) {
  outline: 2px dashed currentColor;
  outline-offset: 4px;
  padding: .1em .2em;
}

/* Outline whatever the pointer is on. :not(:has(:hover)) narrows this to the
   INNERMOST hovered element — plain `:hover` matches the whole ancestor chain, so
   over a paragraph the paragraph, its section and every wrapping div would each
   draw their own frame. (That is exactly what the OneTap plugin this was modelled
   on does: body.onetap-highlight-all :hover { outline: … }.) Colour from the design
   system, the same token the focus ring was lifted to in 13.6.0 when it failed
   contrast at 1.3:1. Scoped to main like highlight-links, so the page chrome and
   the widget's own panel stay calm. */
html[data-a11y-highlight-hover] main :hover:not(:has(:hover)) {
  outline: 2px solid var(--bs-primary);
  outline-offset: 2px;
}

/* No hovering pointer, no hover outline — and the option has no control there,
   the same rule the magnifier follows. */
@media (hover: none) {
  .q4u-a11y-pref--highlightHover { display: none; }
}

html[data-a11y-big-cursor],
html[data-a11y-big-cursor] body { cursor: url('../Icons/cursor-arrow.svg') 4 4, auto; }
html[data-a11y-big-cursor] :is(a, button, input, label, select, textarea, [role="button"], summary) {
  cursor: url('../Icons/cursor-pointer.svg') 14 4, pointer;
}

html[data-a11y-hide-images] :is(img, picture, video):not(.q4u-a11y-keep) { visibility: hidden; }
html[data-a11y-hide-images] [style*="background-image"]:not(.q4u-a11y-keep) { background-image: none !important; }
html[data-a11y-hide-images] .q4u-a11y-img-alt {
  display: inline-block; max-width: 100%; padding: .5rem .75rem;
  border: 1px dashed rgba(var(--bs-emphasis-color-rgb), .3);
  border-radius: var(--bs-border-radius); font-size: .875rem; color: var(--bs-body-color);
}
html[data-a11y-hide-images] .q4u-a11y-img-alt::before { content: attr(data-a11y-alt); }

.q4u-a11y-guide {
  position: fixed; left: 0; right: 0; height: 2.2em; pointer-events: none;
  border-top: 3px solid rgba(255, 237, 0, .95); border-bottom: 3px solid rgba(255, 237, 0, .95);
  background: rgba(0, 0, 0, .06); z-index: 2147483000; transform: translateY(-50%);
}
.q4u-a11y-mask { position: fixed; left: 0; right: 0; pointer-events: none; background: rgba(0, 0, 0, .45); z-index: 2147483000; }
html[data-bs-theme="dark"] .q4u-a11y-mask { background: rgba(0, 0, 0, .65); }
.q4u-a11y-mask--top { top: 0; }
.q4u-a11y-mask--bottom { bottom: 0; }

/* Text magnifier. Sized in rem so it grows with the fontSize preference instead
   of fighting it. pointer-events: none keeps the page underneath clickable —
   the box follows the pointer and must never become the hover target itself. */
.q4u-a11y-magnifier {
  position: fixed; z-index: 2147483000; pointer-events: none;
  max-width: min(42rem, calc(100vw - 1rem));
  padding: .5rem .75rem;
  font-size: 1.75rem; line-height: 1.35;
  background: var(--bs-body-bg); color: var(--bs-body-color);
  border: 2px solid var(--bs-primary); border-radius: var(--bs-border-radius);
  box-shadow: 0 .5rem 1.5rem rgba(0, 0, 0, .25);
}

/* A magnifier that follows the finger sits under the finger — it would cover
   exactly what it is meant to show. Hidden where there is no hovering pointer;
   the preference itself stays valid, it just has no control on this device. */
@media (hover: none) {
  .q4u-a11y-pref--textMagnifier { display: none; }
}

/* ─────────────────────────── Motion ─────────────────────────── */
/* moved from q4u_base _tactile-enhancements.scss 418–434. NOT symmetric, and
   deliberately so: motion is a Toggle (default/reduce), so an explicit "no,
   I want animation" can never outrank the OS's prefers-reduced-motion — there
   is no cookie state for it, unlike theme/contrast (Choice with an 'auto'
   value, see resolve-system-preferences.js). Reduce wins whenever either the
   user or the OS asks for it; a user cannot override the OS toward MORE
   motion. That's a deliberate vestibular-safety default, not the same bug
   contrast had (which was purely a visual preference with no safety stake). */
html[data-bs-motion="reduce"] *,
html[data-bs-motion="reduce"] *::before,
html[data-bs-motion="reduce"] *::after {
  animation-duration: .01ms !important;
  animation-iteration-count: 1 !important;
  transition-duration: .01ms !important;
  scroll-behavior: auto !important;
}
html[data-bs-motion="reduce"] { view-transition-name: none; scroll-behavior: auto !important; }
@media (prefers-reduced-motion: reduce) {
  html:not([data-bs-motion]) *,
  html:not([data-bs-motion]) *::before,
  html:not([data-bs-motion]) *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
    scroll-behavior: auto !important;
  }
}
