/*
  Accessibility layer: skip link, the accessibility toolbar, focus styles,
  reduced motion, and the color-vision-mode / high-contrast hookups.
  This file is deliberately separate from styles.css so the accessibility
  contract is easy to audit on its own.
*/

/* ---------------------------------------------------------------------
   Visually hidden (but still announced by screen readers) utility
--------------------------------------------------------------------- */
.visually-hidden {
  position: absolute !important;
  width: 1px !important;
  height: 1px !important;
  padding: 0 !important;
  margin: -1px !important;
  overflow: hidden !important;
  clip: rect(0, 0, 0, 0) !important;
  white-space: nowrap !important;
  border: 0 !important;
}

.visually-hidden-focusable:focus,
.visually-hidden-focusable:focus-within {
  position: static !important;
  width: auto !important;
  height: auto !important;
  margin: 0 !important;
  overflow: visible !important;
  clip: auto !important;
  white-space: normal !important;
}

/* ---------------------------------------------------------------------
   Skip link — first focusable element on every page (WCAG 2.4.1)
--------------------------------------------------------------------- */
.skip-link {
  position: absolute;
  top: -3rem;
  left: var(--space-4);
  z-index: 2000;
  background: var(--color-bg-inverse);
  color: var(--color-text-inverse);
  padding: var(--space-3) var(--space-5);
  border-radius: 0 0 var(--radius-md) var(--radius-md);
  font-weight: 700;
  text-decoration: underline;
}

.skip-link:focus {
  top: 0;
}

/* ---------------------------------------------------------------------
   Focus visibility (WCAG 2.4.7 / 2.4.11) — a double ring so it reads
   against both light and dark surfaces, never removed, never color-only.
--------------------------------------------------------------------- */
:focus {
  outline: none;
}

:focus-visible {
  outline: 3px solid var(--color-focus-ring);
  outline-offset: 3px;
  box-shadow: 0 0 0 3px var(--color-focus-ring-offset), 0 0 0 6px var(--color-focus-ring);
  border-radius: var(--radius-sm);
}

/* ---------------------------------------------------------------------
   Accessibility toolbar — .pinned-stack (the bar + the real header row,
   see .site-header below) is the sticky element, not the bar alone, so
   both stay pinned together as one unit regardless of either one's
   height (which changes with font-size scaling and content wrapping).
   The category nav is deliberately outside .pinned-stack — it scrolls
   away; only the toolbar and the logo/search/account/cart row stay put.
--------------------------------------------------------------------- */
.pinned-stack {
  position: sticky;
  top: 0;
  z-index: 1000;
}

.a11y-bar-wrap {
  background: var(--color-bg-inverse);
}

/* The bar itself collapses via the grid-rows trick (see .hero__collapsible
   for the same pattern) — .a11y-bar__clip is the overflow-hidden grid
   item, .a11y-bar is the animated grid container. */
.a11y-bar {
  display: grid;
  grid-template-rows: 1fr;
  transition: grid-template-rows var(--motion-duration) var(--motion-easing);
  color: var(--color-text-inverse);
  font-size: var(--step--1);
}

.a11y-bar.is-collapsed {
  grid-template-rows: 0fr;
}

.a11y-bar__clip {
  min-height: 0;
  overflow: hidden;
}

/* The clip wrapper's overflow:hidden is what makes the collapse animation
   squish to nothing — but it also clips the vision-mode popover, which is
   position:absolute and needs to visually escape this box. Only relax it
   while that popover is actually open. */
.a11y-bar__clip:has(.vision-picker.is-open) {
  overflow: visible;
}

/* Same escape-the-clip problem as the vision-mode popover above, but for
   every plain button tooltip in the bar: .tooltip-bubble is
   position:absolute and needs to visually clear this box on hover/focus,
   not just while a panel happens to be open. :focus-visible (not
   :focus-within/:focus) so a mouse click doesn't wedge this open — :has()
   can't nest :has(), so this targets the button directly rather than
   :has(.tooltip-wrap:has(:focus-visible)). */
.a11y-bar__clip:has(.tooltip-wrap:hover),
.a11y-bar__clip:has(button:focus-visible) {
  overflow: visible;
}

.a11y-bar__inner {
  max-width: var(--container-max);
  margin-inline: auto;
  padding: var(--space-2) var(--space-4);
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-4);
}

.a11y-bar__group {
  display: flex;
  align-items: center;
  gap: var(--space-2);
}

.a11y-bar__label {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  font-weight: 700;
  white-space: nowrap;
}

.a11y-bar__label svg {
  width: 1.1em;
  height: 1.1em;
  flex-shrink: 0;
}

.a11y-btn {
  background: transparent;
  color: var(--color-text-inverse);
  border: 1px solid rgba(255, 255, 255, 0.55);
  border-radius: var(--radius-sm);
  padding: var(--space-1) var(--space-3);
  font-family: inherit;
  font-size: inherit;
  font-weight: 600;
  line-height: 1.4;
  cursor: pointer;
  min-height: 2.25rem;
}

.a11y-btn:hover {
  background: rgba(255, 255, 255, 0.12);
}

.a11y-btn[aria-pressed="true"],
.a11y-btn[aria-current="true"] {
  background: var(--color-accent);
  color: var(--color-accent-contrast);
  border-color: var(--color-accent);
}

.a11y-select {
  background: var(--color-bg);
  color: var(--color-text);
  border: 1px solid var(--color-border-strong);
  border-radius: var(--radius-sm);
  padding: var(--space-1) var(--space-2);
  font-family: inherit;
  font-size: inherit;
  min-height: 2.25rem;
}

/* Toolbar wraps into multiple rows on narrow screens — it never scrolls
   horizontally or clips a control. */
@media (max-width: 40rem) {
  .a11y-bar__inner {
    justify-content: center;
  }

  .a11y-bar__group {
    flex-wrap: wrap;
    justify-content: center;
  }
}

/* ---------------------------------------------------------------------
   Collapse tab — a thin strip that's always visible (never collapses
   itself), sitting right below the toolbar. When the toolbar collapses
   to nothing, this strip is what's left sitting directly against the
   header row below it — the "tab" the toolbar drops into the search
   area when minimized.
--------------------------------------------------------------------- */
.a11y-bar__handle-row {
  display: flex;
  justify-content: center;
  border-top: 1px solid rgba(255, 255, 255, 0.15);
  border-bottom: 1px solid var(--color-brand-navy-dark);
}

.a11y-bar__tab {
  background: transparent;
  color: var(--color-text-inverse);
  border: none;
  border-radius: 0;
  padding: var(--space-2) var(--space-4);
  font-family: inherit;
  font-size: var(--step--1);
  font-weight: 700;
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  cursor: pointer;
}

.a11y-bar__tab:hover {
  background: rgba(255, 255, 255, 0.12);
}

/* Fixed rem size (not em) so it doesn't shrink with the tab's own small
   font-size — matches .a11y-bar__tab-symbol below so the pair reads as
   one unit. */
.a11y-bar__tab-icon {
  width: 1.1rem;
  height: 1.1rem;
  flex-shrink: 0;
  transition: transform var(--motion-duration) var(--motion-easing);
}

.a11y-bar.is-collapsed ~ .a11y-bar__handle-row .a11y-bar__tab-icon {
  transform: rotate(180deg);
}

/* The universal-access symbol next to the label - a fixed brand mark for
   the control, so it does not rotate with the collapse chevron. Sized in
   rem, not em, so it stays legible regardless of the tab's own small
   font-size. Tuned twice: 1.05em (~15px) was too small to read, 2.25rem
   (36px) overpowered the row - 1.5rem lands as a clear, proportionate
   icon next to the label text. */
.a11y-bar__tab-symbol {
  width: 1.5rem;
  height: 1.5rem;
  flex-shrink: 0;
}

/* ---------------------------------------------------------------------
   Tooltips — every toolbar control explains itself on hover *and*
   keyboard focus, not hover alone. role="tooltip" + aria-describedby
   means assistive tech gets the same text. Keyed off :focus-visible,
   not :focus-within/:focus — a plain mouse click still focuses a
   <button> and holds that focus, so :focus-within would leave the
   tooltip stuck open until focus moves elsewhere; :focus-visible only
   matches real keyboard navigation in practice, so a click-then-look-away
   dismisses it the way hover already does.
--------------------------------------------------------------------- */
.tooltip-wrap {
  position: relative;
  display: inline-flex;
}

.tooltip-bubble {
  position: absolute;
  top: calc(100% + 0.6rem);
  left: 50%;
  transform: translateX(-50%) translateY(-4px);
  background: var(--color-brand-navy-dark);
  color: #ffffff;
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-sm);
  font-size: var(--step--1);
  font-weight: 600;
  line-height: 1.3;
  width: max-content;
  max-width: 14rem;
  box-shadow: var(--shadow-md);
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transition: opacity var(--motion-duration) var(--motion-easing), transform var(--motion-duration) var(--motion-easing);
  z-index: 1300;
}

.tooltip-bubble::before {
  content: "";
  position: absolute;
  bottom: 100%;
  left: 50%;
  transform: translateX(-50%);
  border: 6px solid transparent;
  border-bottom-color: var(--color-brand-navy-dark);
}

.tooltip-wrap:hover .tooltip-bubble,
.tooltip-wrap:has(:focus-visible) .tooltip-bubble,
.tooltip-wrap.is-hint-visible .tooltip-bubble {
  opacity: 1;
  visibility: visible;
  transform: translateX(-50%) translateY(0);
}

/* A trigger that opens its own panel suppresses its tooltip once that
   panel is open — a tooltip and a panel both anchored to the same
   button, both appearing below it, would otherwise overlap. The panel
   itself (visible text + controls) already explains more than the
   tooltip did, so nothing is lost. */
.vision-picker.is-open .tooltip-bubble {
  opacity: 0 !important;
  visibility: hidden !important;
}

/* ---------------------------------------------------------------------
   Color-vision picker — sunglasses button whose lenses tint to match
   the active mode. Opens its panel on click only (not hover) — hover
   alone shows just the tooltip. Hover-opened panels are also a known
   accessibility trap for touch/motor-impaired users (WCAG 1.4.13), so
   click/keyboard activation is the more correct trigger here, not just
   a fix for the tooltip collision.
--------------------------------------------------------------------- */
.vision-picker {
  position: relative;
}

.vision-picker__trigger {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
}

.vision-picker__icon .vision-picker__lens {
  fill: #9fb4bd;
  fill-opacity: 0.6;
  transition: fill var(--motion-duration) var(--motion-easing);
}

.vision-picker__icon[data-mode="deuteranopia"] .vision-picker__lens {
  fill: #8a8f3f;
  fill-opacity: 0.9;
}

.vision-picker__icon[data-mode="protanopia"] .vision-picker__lens {
  fill: #a68a3d;
  fill-opacity: 0.9;
}

.vision-picker__icon[data-mode="tritanopia"] .vision-picker__lens {
  fill: #4f7cac;
  fill-opacity: 0.9;
}

.vision-picker__icon[data-mode="grayscale"] .vision-picker__lens {
  fill: #8a8f94;
  fill-opacity: 0.9;
}

.vision-panel {
  /* Reset fieldset's default border/min-width quirks — the panel styles
     below (border, padding, background) fully replace them. */
  min-width: 0;
  position: absolute;
  top: calc(100% + 0.6rem);
  left: 0;
  background: var(--color-bg);
  color: var(--color-text);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-md);
  padding: var(--space-4);
  width: 19rem;
  max-width: 85vw;
  z-index: 1200;
  opacity: 0;
  visibility: hidden;
  transform: translateY(-4px);
  pointer-events: none;
  transition: opacity var(--motion-duration) var(--motion-easing), transform var(--motion-duration) var(--motion-easing);
}

.vision-picker.is-open .vision-panel {
  opacity: 1;
  visibility: visible;
  transform: translateY(0);
  pointer-events: auto;
}

.vision-panel__intro {
  padding: 0;
  margin: 0 0 var(--space-3);
  font-size: var(--step--1);
  font-weight: 700;
  color: var(--color-text);
}

.vision-option {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-2);
  border-radius: var(--radius-sm);
  cursor: pointer;
  min-height: 2.75rem;
}

.vision-option:hover,
.vision-option:focus-within {
  background: var(--color-bg-alt);
}

.vision-option input {
  flex-shrink: 0;
}

.vision-option__swatch {
  display: inline-flex;
  gap: 0.2rem;
  flex-shrink: 0;
}

.vision-option__text {
  display: flex;
  flex-direction: column;
  line-height: 1.3;
  font-size: var(--step--1);
  color: var(--color-text);
}

.vision-option__text small {
  color: var(--color-text-muted);
  font-weight: 400;
}

.dot {
  width: 0.85rem;
  height: 0.85rem;
  border-radius: 999px;
  display: inline-block;
}

.dot--magenta {
  background: var(--color-brand-magenta);
}

.dot--gold {
  background: var(--color-brand-gold);
}

.dot--teal {
  background: var(--color-success);
}

/* ---------------------------------------------------------------------
   Reduced motion — honors OS setting AND the manual toolbar toggle
--------------------------------------------------------------------- */
html[data-motion="reduced"] *,
html[data-motion="reduced"] *::before,
html[data-motion="reduced"] *::after {
  animation-duration: 0.001ms !important;
  animation-iteration-count: 1 !important;
  transition-duration: 0.001ms !important;
  scroll-behavior: auto !important;
}

@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
    scroll-behavior: auto !important;
  }
}

/* ---------------------------------------------------------------------
   Dyslexia-friendly font — OpenDyslexic, toggled site-wide by
   #a11y-dyslexic-font. Loaded from jsdelivr's npm CDN (the real,
   verified "open-dyslexic" package) rather than bundled, same hosting
   pattern Atkinson Hyperlegible already uses via Google Fonts. Only two
   weights are shipped (regular/bold) — the whole point of this font is
   its exaggerated, weighted letter bottoms, so an italic fallback to the
   base font (browsers do this automatically when a style isn't defined)
   is preferable to a synthetic-oblique OpenDyslexic, which would distort
   that exact weighting.
--------------------------------------------------------------------- */
@font-face {
  font-family: "OpenDyslexic";
  src: url("https://cdn.jsdelivr.net/npm/open-dyslexic@1.0.3/woff/OpenDyslexic-Regular.woff") format("woff");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: "OpenDyslexic";
  src: url("https://cdn.jsdelivr.net/npm/open-dyslexic@1.0.3/woff/OpenDyslexic-Bold.woff") format("woff");
  font-weight: 700;
  font-style: normal;
  font-display: swap;
}

/* Applied to body (every element inherits font-family by default) rather
   than every selector that currently sets --font-family-base explicitly —
   this is the one override that needs to reach the entire site regardless
   of how many components later pin their own font-family. OpenDyslexic's
   glyphs also run wider than Atkinson Hyperlegible at the same point
   size, so line-height gets a touch more room here too, rather than long
   paragraphs feeling cramped.

   h1-h6 need their own line here too, not just body: styles.css gives
   headings their own explicit font-family (for the heading/body font
   pairing when dyslexic mode is off), which breaks inheritance from body -
   so without this, every heading, AND every link nested inside a heading
   (e.g. every product card's <h3><a>title</a></h3>), silently stayed in
   the base font while the rest of the page switched. */
html[data-dyslexic-font="on"] body {
  font-family: var(--font-family-dyslexic);
  line-height: 1.7;
}

html[data-dyslexic-font="on"] h1,
html[data-dyslexic-font="on"] h2,
html[data-dyslexic-font="on"] h3,
html[data-dyslexic-font="on"] h4,
html[data-dyslexic-font="on"] h5,
html[data-dyslexic-font="on"] h6 {
  font-family: var(--font-family-dyslexic);
}

/* The toolbar button's own "D" is set in OpenDyslexic unconditionally
   (the @font-face above isn't gated by [data-dyslexic-font] - only the
   site-wide body override is) so the label previews the font it switches
   to, regardless of whether the toggle is currently on. A fixed width
   keeps it visually aligned with its neighbors (A−/A+/Reset) rather than
   shrink-wrapping to one glyph. */
.a11y-btn--dyslexic {
  font-family: "OpenDyslexic", var(--font-family-base);
  font-size: 1.05em;
  min-width: 2.5rem;
  justify-content: center;
}

/* ---------------------------------------------------------------------
   Color-vision modes. Grayscale/high-contrast are plain CSS filters;
   the CVD-assist modes reference an SVG <filter> defined once per page
   (see the inline <svg class="visually-hidden"> in every template) that
   shifts hue separation toward the ranges the chosen deficiency can
   still distinguish. These are a best-effort aid, not a substitute for
   never using color as the only signal — every status/badge in this
   site also carries an icon and a text label.

   The filter is applied per content region (header, nav, breadcrumbs,
   main, footer) rather than on <html> — CSS filter has no descendant
   opt-out, so filtering the whole document would also re-filter the
   color-vision picker's own reference swatches every time you changed
   the selection, making them drift instead of staying a stable preview.
   Scoping it to content regions keeps the toolbar itself (and its
   swatches) unaffected, regardless of which mode is active.
--------------------------------------------------------------------- */
/* Belle's chat panel (`.belle-chat-panel`) is deliberately appended straight to `document.body`
   (`belle-chat.ts` - a `position: fixed` panel needs no particular DOM parent), which is exactly why
   it fell outside every rule below before it was added here - none of `.pinned-stack > header`/
   `.primary-nav`/`.breadcrumbs`/`main`/`.site-footer` is an ancestor of it. Reported directly: the
   colorblind-mode filter wasn't reaching the open chat window. */
html[data-vision="protanopia"] .pinned-stack > header,
html[data-vision="protanopia"] .primary-nav,
html[data-vision="protanopia"] .breadcrumbs,
html[data-vision="protanopia"] main,
html[data-vision="protanopia"] .site-footer,
html[data-vision="protanopia"] .belle-chat-panel {
  filter: url(#cvd-protanopia) saturate(1.15);
}

html[data-vision="deuteranopia"] .pinned-stack > header,
html[data-vision="deuteranopia"] .primary-nav,
html[data-vision="deuteranopia"] .breadcrumbs,
html[data-vision="deuteranopia"] main,
html[data-vision="deuteranopia"] .site-footer,
html[data-vision="deuteranopia"] .belle-chat-panel {
  filter: url(#cvd-deuteranopia) saturate(1.15);
}

html[data-vision="tritanopia"] .pinned-stack > header,
html[data-vision="tritanopia"] .primary-nav,
html[data-vision="tritanopia"] .breadcrumbs,
html[data-vision="tritanopia"] main,
html[data-vision="tritanopia"] .site-footer,
html[data-vision="tritanopia"] .belle-chat-panel {
  filter: url(#cvd-tritanopia) saturate(1.15);
}

html[data-vision="grayscale"] .pinned-stack > header,
html[data-vision="grayscale"] .primary-nav,
html[data-vision="grayscale"] .breadcrumbs,
html[data-vision="grayscale"] main,
html[data-vision="grayscale"] .site-footer,
html[data-vision="grayscale"] .belle-chat-panel {
  filter: grayscale(100%) contrast(1.05);
}

/* ---------------------------------------------------------------------
   Underline links by default — never rely on color alone (WCAG 1.4.1)
--------------------------------------------------------------------- */
.prose a,
.body-copy a {
  text-decoration: underline;
  text-underline-offset: 0.15em;
}

/* ---------------------------------------------------------------------
   Minimum touch target size (WCAG 2.5.8) — 44x44 for anything tappable
--------------------------------------------------------------------- */
button,
.btn,
a.btn,
select,
.icon-btn {
  min-height: 2.75rem;
}

/* Checkboxes/radios stay visually compact, but their clickable *label*
   (see .filter-option in styles.css) is sized to the same 44x44 minimum —
   distorting the glyph itself would look broken, not accessible. */
input[type="checkbox"],
input[type="radio"] {
  width: 1.25rem;
  height: 1.25rem;
  flex-shrink: 0;
}

.icon-btn {
  min-width: 2.75rem;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
