/* The reading preferences panel, and what a visitor's answers do.
 *
 * Its own stylesheet, drawn over whatever theme a site runs and inheriting
 * nothing from it - the same reasoning as the editing bar's. Every property is
 * set rather than assumed, and everything is prefixed `ay-a11y` so nothing a
 * theme writes can collide with it.
 *
 * This half is the preferences, as `html[data-ay-*]` rules. The attribute
 * names and values are the ones `Accessibility::PREFERENCES` declares - that
 * file is the contract between this, the panel's markup and the script that
 * applies it. The panel's own design is in accessibility_panel.css, which is
 * not in the head: this is render blocking because it decides how the page is
 * drawn, and that one is not because the panel is `hidden` until the script
 * has run.
 *
 * The preferences carry `!important` because they are the reader's answer and
 * a theme's utility class is not allowed to win against it. That is the one
 * place in this application where `!important` is the correct tool: the whole
 * point of the declaration is that it overrides the document.
 *
 * What is deliberately NOT here: anything that changes what the document
 * means. No injected alternative text, no rewritten landmarks, no synthetic
 * screen reader. See app/models/accessibility.rb.
 */

/* Fetched only if somebody asks for it. A declared face that nothing uses is
 * never downloaded, so the reader who leaves this alone pays nothing. */
@font-face {
  font-family: "Atkinson Hyperlegible Next";
  font-style: normal;
  font-weight: 200 800;
  font-display: swap;
  src: url("/assets/atkinson-hyperlegible-latin-c30cdd7a.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: "Atkinson Hyperlegible Next";
  font-style: normal;
  font-weight: 200 800;
  font-display: swap;
  src: url("/assets/atkinson-hyperlegible-latin-ext-6291a398.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* ---------------------------------------------------------------------------
   What the reader asked for
   --------------------------------------------------------------------------- */

/* Text size, on the root, because everything a well-built theme sizes is in
 * `rem` - so one declaration scales the type, the spacing and the line length
 * together instead of enlarging text inside boxes that stayed the same size. */
html[data-ay-text="large"]   { font-size: 112.5%; }
html[data-ay-text="larger"]  { font-size: 125%; }
html[data-ay-text="largest"] { font-size: 150%; }

/* Line height and the space between paragraphs. Applied to blocks of text
 * rather than to everything: `* { line-height }` is how a button's label ends
 * up outside the button. The widest step is WCAG 1.4.12's own figure - line
 * height 1.5 times the font size, paragraph spacing twice it. */
html[data-ay-lines="comfortable"] :is(p, li, dd, dt, blockquote, figcaption, td, th) {
  line-height: 1.7 !important;
}
html[data-ay-lines="comfortable"] p { margin-block-end: 1.5em !important; }
html[data-ay-lines="wide"] :is(p, li, dd, dt, blockquote, figcaption, td, th) {
  line-height: 2 !important;
}
html[data-ay-lines="wide"] p { margin-block-end: 2em !important; }

/* Letter and word spacing, likewise together, and likewise 1.4.12's figures
 * at the widest step: 0.12em between letters and 0.16em between words. */
html[data-ay-letters="comfortable"] :is(p, li, dd, dt, blockquote, figcaption, td, th, h1, h2, h3, h4, h5, h6) {
  letter-spacing: 0.06em !important;
  word-spacing: 0.09em !important;
}
html[data-ay-letters="wide"] :is(p, li, dd, dt, blockquote, figcaption, td, th, h1, h2, h3, h4, h5, h6) {
  letter-spacing: 0.12em !important;
  word-spacing: 0.16em !important;
}

/* The high-legibility face. `svg` is left out because an icon set is drawn,
 * not typeset, and the panel is left out because its own controls should not
 * move under the reader while they are being used. */
html[data-ay-font="legible"] body :not(.ay-a11y):not(.ay-a11y *) {
  font-family: "Atkinson Hyperlegible Next", Verdana, Tahoma, sans-serif !important;
}

/* Contrast, colour and brightness are all filters, and a second `filter`
 * declaration replaces the first rather than adding to it - so three answers
 * would leave only one standing. Each answer sets a custom property instead,
 * and one declaration composes them.
 *
 * The declaration is attached only when at least one of the three attributes
 * is present: `filter` on the root creates a containing block, which changes
 * where a fixed element anchors, and that is not a thing to do to every page
 * on the chance somebody might want it. */
html[data-ay-contrast],
html[data-ay-colour],
html[data-ay-brightness] {
  filter:
    invert(var(--ay-invert, 0))
    hue-rotate(calc(var(--ay-invert, 0) * 180deg))
    contrast(var(--ay-contrast, 1))
    brightness(var(--ay-brightness, 1))
    saturate(var(--ay-saturate, 1));
}

html[data-ay-contrast="high"] { --ay-contrast: 1.35; }

/* Dark, and full inversion. Both are the same operation; they differ in
 * whether photographs are turned back the right way round, which is the
 * difference between "I want a dark page" and "I want everything inverted".
 * The white background is what the inversion turns into the dark one - a
 * transparent root would invert to nothing. */
html[data-ay-contrast="dark"],
html[data-ay-contrast="invert"] {
  --ay-invert: 1;
  background-color: #fff;
}

html[data-ay-contrast="dark"] :is(img, picture, video, iframe, canvas, [data-ay-keep-colour]) {
  filter: invert(1) hue-rotate(180deg);
}

/* The panel is deliberately NOT turned back the right way round.
 *
 * An earlier version counter-inverted it, so the controls somebody was using
 * to invert the page stayed as drawn. That reads well in the abstract and
 * badly in practice: it pinned the panel to one palette, so on an already
 * dark site the page would go light and the panel would stay dark. Letting it
 * invert with everything else means it matches the resulting page in every
 * combination, with no rule per combination - and `invert` paired with
 * `hue-rotate(180deg)` flips lightness while keeping hue, so a near
 * monochrome panel with one accent comes out readable either way round.
 */

html[data-ay-colour="dim"] { --ay-saturate: 0.55; }
html[data-ay-colour="mono"] { --ay-saturate: 0; }

html[data-ay-brightness="dim"] { --ay-brightness: 0.85; }
html[data-ay-brightness="dimmer"] { --ay-brightness: 0.7; }

/* Text pulled back to the start of the line, or centred. Justified text is
 * not offered: the rivers it opens up are the thing anybody who tracks lines
 * with difficulty is trying to get away from. */
html[data-ay-align="start"] :is(p, li, dd, dt, blockquote, figcaption, h1, h2, h3, h4, h5, h6):not(.ay-a11y *) {
  text-align: start !important;
}
html[data-ay-align="centre"] :is(p, li, dd, blockquote, figcaption, h1, h2, h3, h4, h5, h6):not(.ay-a11y *) {
  text-align: center !important;
}

/* A focus ring nobody can miss, on top of whatever the theme draws. */
html[data-ay-focus="on"] :is(a, button, input, select, textarea, summary, [tabindex]):not(.ay-a11y *):focus-visible {
  outline: 4px solid #ffbf00 !important;
  outline-offset: 3px !important;
  box-shadow: 0 0 0 8px rgb(0 0 0 / 0.55) !important;
}

/* Every link underlined, everywhere. A theme that says "link" with colour
 * alone is a theme that says nothing to anybody who cannot see the colour;
 * this is the reader turning that on for themselves. */
html[data-ay-links="on"] a:not(.ay-a11y a) {
  text-decoration: underline !important;
  text-decoration-thickness: 2px !important;
  text-underline-offset: 3px !important;
}

/* Headings marked out, so a long page can be skimmed by shape. A rule down
 * the side rather than a box, because a box round a heading changes the
 * layout of everything under it. */
html[data-ay-headings="on"] :is(h1, h2, h3, h4, h5, h6):not(.ay-a11y *) {
  border-inline-start: 4px solid currentColor !important;
  padding-inline-start: 0.6em !important;
}

/* A larger pointer, drawn rather than depended on: no operating system agrees
 * on a name for a big cursor, and a data URI is one that works everywhere.
 * `auto` stays as the fallback so a browser that refuses the image still has
 * a pointer. */
html[data-ay-cursor="on"],
html[data-ay-cursor="on"] * {
  cursor: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='48' height='48' viewBox='0 0 24 24'%3E%3Cpath d='M5 2l14 8.5-6.2 1.2L16 19l-2.6 1.1-3-6.6L5 17z' fill='%23fff' stroke='%23000' stroke-width='1.4'/%3E%3C/svg%3E") 3 2, auto !important;
}
html[data-ay-cursor="on"] :is(a, button, summary, [role="button"]) {
  cursor: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='48' height='48' viewBox='0 0 24 24'%3E%3Cpath d='M9 2v9l2-2 2 5 2-1-2-5h3L9 2z' fill='%23fff' stroke='%23000' stroke-width='1.4'/%3E%3C/svg%3E") 8 2, pointer !important;
}

/* Nothing moves unless the reader asked it to. `prefers-reduced-motion` is
 * the right way to say this and the panel never overrides it - this is only
 * for somebody who has not set it in their system. */
html[data-ay-motion="on"] *,
html[data-ay-motion="on"] *::before,
html[data-ay-motion="on"] *::after {
  animation-duration: 1ms !important;
  animation-iteration-count: 1 !important;
  transition-duration: 1ms !important;
  scroll-behavior: auto !important;
}

/* Pictures out of the way. The caption stays, because a caption is text
 * somebody wrote and not an image. */
html[data-ay-images="on"] :is(img, picture, video, canvas):not(.ay-a11y *) {
  display: none !important;
}

/* The reading guide: a ruler under the pointer, or a band with the rest of
 * the page dimmed. Positioned by the script, which writes the one custom
 * property below. */
/* Both are in the document from the start and shown by the reader's own
 * answer, like every other preference here: one vocabulary, and the script
 * only has to move them rather than decide whether they exist. */
.ay-a11y-guide,
.ay-a11y-mask { display: none; }

html[data-ay-guide="on"] .ay-a11y-guide { display: block; }
html[data-ay-mask="on"] .ay-a11y-mask { display: block; }

.ay-a11y-guide {
  position: fixed;
  inset-inline: 0;
  top: var(--ay-guide-y, 50vh);
  z-index: 2147482000;
  height: 3px;
  background: #d40000;
  box-shadow: 0 0 0 1px rgb(255 255 255 / 0.9);
  pointer-events: none;
}

.ay-a11y-mask {
  position: fixed;
  inset: 0;
  z-index: 2147482000;
  pointer-events: none;
  background: rgb(0 0 0 / 0.62);
  /* A window in the mask rather than four dimmed panels: one element, and the
   * band follows the pointer by moving the gradient stops. */
  mask-image: linear-gradient(
    to bottom,
    #000 0,
    #000 calc(var(--ay-guide-y, 50vh) - 3.2rem),
    transparent calc(var(--ay-guide-y, 50vh) - 3.2rem),
    transparent calc(var(--ay-guide-y, 50vh) + 3.2rem),
    #000 calc(var(--ay-guide-y, 50vh) + 3.2rem)
  );
}
