/**
 * fill'd Accessibility — site adjustments
 *
 * Everything here is keyed off a data attribute on <html> whose value is the
 * tile's current step index, set by widget.js (and restored before first paint
 * by the inline preboot script). So data-a11y-contrast="1" is High contrast,
 * "2" is Dark mode, and so on.
 *
 * Every selector excludes the toolbar's own subtree with
 *   :not(.filld-a11y):not(.filld-a11y *)
 * The control that turns an adjustment off must never be made unreadable by
 * that adjustment being on.
 *
 * DIVISION OF LABOUR — worth knowing before editing:
 *   CSS owns  : colour (high contrast), letter/word spacing, alignment,
 *               cursors, motion, focus rings, image hiding.
 *   JS owns   : font-size, line-height, and dark mode's per-element colours.
 * Font-size and line-height cannot live here because the theme sizes type in
 * px throughout, and dark mode cannot live here because a blanket colour
 * override flattens every surface into one shade and destroys the page's
 * visual hierarchy. See widget.js.
 *
 * !important is used deliberately: these rules exist to override author styles
 * on the visitor's explicit request, which is the case it was designed for.
 */

/* ==========================================================================
   Contrast 1 — High contrast
   Black canvas, white type, cyan headings, yellow links. Deliberately blunt:
   the goal is maximum luminance separation, not a pleasant palette.
   ========================================================================== */

html[data-a11y-contrast="1"] body,
html[data-a11y-contrast="1"] body *:not(.filld-a11y):not(.filld-a11y *) {
    background-color: #000 !important;
    background-image: none !important;
    color: #fff !important;
    border-color: #fff !important;
    box-shadow: none !important;
    text-shadow: none !important;
}

/* EVERY RULE BELOW CARRIES THE FULL :not(.filld-a11y):not(.filld-a11y *) PAIR,
   and that is load-bearing rather than copy-paste.

   These are exceptions to the wildcard above, and they have to outrank it.
   Specificity is (id, class, type) compared left to right, and the wildcard
   computes to (0,3,3): three class-level tokens — the attribute selector plus
   the two :not() arguments — against html, body and the type inside the second
   :not(). Written the obvious way, with a single :not(.filld-a11y *), an
   exception lands at (0,2,4). Four types beats three, but class is compared
   first, so 2 < 3 and the exception silently loses.

   It did lose, for as long as this file has existed. Measured before the fix:
   h1 rendered rgb(255,255,255) instead of the cyan, links rendered white
   instead of yellow, and transparent images sat in black boxes — High contrast
   was a flat white-on-black wall with no hierarchy and no way to tell a link
   from a sentence. That is what "contrast makes things worse" was.

   Adding the second :not() brings each exception to (0,3,4): equal on class,
   ahead on type. If you add another exception here, match this pattern or it
   will not apply. */

html[data-a11y-contrast="1"] body a:not(.filld-a11y):not(.filld-a11y *),
html[data-a11y-contrast="1"] body a *:not(.filld-a11y):not(.filld-a11y *) {
    color: #ffe600 !important;
    text-decoration: underline !important;
}

html[data-a11y-contrast="1"] body :is(button, .btn, input, select, textarea):not(.filld-a11y):not(.filld-a11y *) {
    border: 2px solid #fff !important;
}

html[data-a11y-contrast="1"] body :is(h1, h2, h3, h4, h5, h6):not(.filld-a11y):not(.filld-a11y *) {
    color: #7cf3ff !important;
}

/* Keep media perceivable rather than blacked out by the wildcard above. */
html[data-a11y-contrast="1"] body :is(img, video, picture, svg):not(.filld-a11y):not(.filld-a11y *) {
    background-color: transparent !important;
}

/* ==========================================================================
   Contrast 2 — Dark mode
   Only the canvas baseline lives here, so a returning visitor does not get a
   white flash before scripts run. widget.js then walks the page and darkens
   each surface individually, preserving the contrast *between* surfaces that
   a blanket override would flatten.
   ========================================================================== */

html[data-a11y-contrast="2"] {
    background-color: #0f1319 !important;
    /* Makes form controls, scrollbars and the caret follow suit for free. */
    color-scheme: dark;
}

html[data-a11y-contrast="2"] body {
    background-color: #0f1319 !important;
    color: #e6eaf0 !important;
}

/* Full-brightness photography is harsh against a dark canvas. A filter here is
   safe: it is on the image itself, so it creates no containing block that
   could break the sticky header or this toolbar's fixed positioning. */
html[data-a11y-contrast="2"] body :is(img, picture, video):not(.filld-a11y *) {
    filter: brightness(0.86) contrast(1.02);
}

/* ==========================================================================
   Colour-pass transition freeze
   Set by applyColourPass() in widget.js for the duration of one pass, and
   removed immediately after. Not a visitor-facing adjustment.

   The pass reads each element's computed colours and writes darkened ones
   back. The theme animates background-color on .btn and friends, so switching
   contrast level starts a transition — and the pass, running in the same tick,
   sampled the value mid-flight. Going High contrast -> Dark mode it read a
   button's background while it was still travelling from black to pastel, saw
   something too dark to need darkening, skipped it, and left a pastel button
   carrying the light text it had already written: 1.14:1, unreadable.

   Freezing transitions for the pass makes getComputedStyle return the settled
   target value instead of a frame of an animation.
   ========================================================================== */

html[data-fa11y-notransition] body,
html[data-fa11y-notransition] body *:not(.filld-a11y):not(.filld-a11y *) {
    transition: none !important;
}

/* ==========================================================================
   Saturation
   Only the *media* half lives here. Text, backgrounds, borders and gradients
   are rewritten colour by colour in widget.js.

   The reason is the same one that ruled out greyscale and invert as page-wide
   effects: `filter` on an ancestor makes it a containing block for every fixed
   and absolutely positioned descendant, which breaks the theme's sticky header
   and this toolbar. A filter on an image or an <svg> is safe, because nothing
   positioned lives inside one — so media is filtered and everything else is
   recomputed.

   The two filters are composed through custom properties so that dark mode's
   dimming survives when saturation is also on. An unset var() with an empty
   fallback contributes nothing, which is what keeps the combinations honest
   without writing out every pairing.
   ========================================================================== */

html[data-a11y-saturation="1"] { --fa11y-saturate: saturate(0.4); }
html[data-a11y-saturation="2"] { --fa11y-saturate: grayscale(1); }
html[data-a11y-contrast="2"]   { --fa11y-img-dark: brightness(0.86) contrast(1.02); }

/* Later in the file than the dark-mode image rule on purpose: at equal
   specificity this wins, and it folds that rule's dimming back in via the
   custom property rather than dropping it. */
html[data-a11y-saturation] body :is(img, picture, video, svg, canvas):not(.filld-a11y *):not(.filld-a11y) {
    filter: var(--fa11y-img-dark, ) var(--fa11y-saturate, );
}

/* ==========================================================================
   Read aloud — the currently spoken block
   Marked with an outline rather than a background, deliberately: the colour
   pass in widget.js writes background and text colour as inline !important
   styles for dark mode and saturation, and an inline !important beats anything
   a stylesheet can say. Outline is untouched by that pass, so it is the one
   signal that stays visible in every combination of tiles.
   ========================================================================== */

body [data-fa11y-reading]:not(.filld-a11y *) {
    outline: 3px solid #b8330b !important;
    outline-offset: 3px !important;
    border-radius: 3px !important;
    box-shadow: 0 0 0 6px rgba(184, 51, 11, 0.18) !important;
    scroll-margin: 25vh !important;
}

/* ==========================================================================
   Text spacing (letter + word only)
   Line height is a separate tile and is applied in JS — see the note above.
   Level 2 matches the WCAG 2.1 SC 1.4.12 test values, so switching it on is
   also a quick way to eyeball whether the layout survives them.
   ========================================================================== */

html[data-a11y-spacing="1"] body :is(p, li, dd, dt, td, th, blockquote, figcaption, label, a, h1, h2, h3, h4, h5, h6):not(.filld-a11y *) {
    letter-spacing: 0.05em !important;
    word-spacing: 0.1em !important;
}

html[data-a11y-spacing="1"] body p:not(.filld-a11y *) {
    margin-bottom: 1.5em !important;
}

html[data-a11y-spacing="2"] body :is(p, li, dd, dt, td, th, blockquote, figcaption, label, a, h1, h2, h3, h4, h5, h6):not(.filld-a11y *) {
    letter-spacing: 0.12em !important;
    word-spacing: 0.16em !important;
}

html[data-a11y-spacing="2"] body p:not(.filld-a11y *) {
    margin-bottom: 2em !important;
}

/* The Align left tile was removed in 0.9.12 at the client's request. The
   Dyslexia friendly bundle still forces ragged-right left alignment on running
   text, so that behaviour is not gone — it is just no longer separately
   switchable. */

/* ==========================================================================
   Readable font
   Aimed at low vision: a large x-height, open apertures and unambiguous
   letterforms. Atkinson Hyperlegible was designed by the Braille Institute for
   exactly this and is picked up automatically if installed; Verdana and Tahoma
   are the universally available equivalents, DejaVu Sans covers Linux.

   Kept distinct from the Dyslexia friendly tile below, which leads with
   OpenDyslexic and brings a whole spacing bundle with it. A system stack only —
   nothing is bundled, so there is no extra request and no 404. To bundle a
   face, drop the woff2 into assets/fonts/ and add a @font-face above this rule.
   ========================================================================== */

html[data-a11y-readablefont="1"] body,
html[data-a11y-readablefont="1"] body *:not(.filld-a11y):not(.filld-a11y *) {
    font-family: "Atkinson Hyperlegible", Verdana, Tahoma, "DejaVu Sans", sans-serif !important;
}

/* ==========================================================================
   Dyslexia friendly
   A bundle, not just a typeface — the spacing and alignment changes carry at
   least as much of the benefit as the font does, and they work even when no
   dyslexia-specific font is available on the device.

   Font stack, in order of preference:
     OpenDyslexic          — weighted letter bottoms. Self-hosted from
                             assets/fonts/ when the .woff2 files are present:
                             filld_a11y_font_faces() emits the @font-face rules
                             only when they exist, so there is never a request
                             for a font that is not there. With nothing in that
                             directory this resolves only for visitors who have
                             the font installed themselves, and everyone else
                             falls silently through to Trebuchet — which is
                             what "the dyslexia font is not using OpenDyslexic"
                             looks like from the outside.
                             The fallbacks below stay regardless: they are what
                             renders if the file 404s or the fetch fails.
     Trebuchet MS          — the realistic fallback, and it leads Verdana here
                             on purpose: with nothing bundled, both this tile
                             and Readable font would otherwise land on Verdana
                             on a stock machine and be visually identical.
                             Trebuchet's letterforms are more differentiated
                             (single-storey 'g', distinct 'I'/'l'), so the two
                             tiles actually do different things out of the box.
     Verdana / DejaVu Sans — wide, open, high x-height fallbacks.

   No Comic Sans. Some dyslexia style guides list it, but controlled studies
   (Wery & Diliberto 2017; Rello & Baeza-Yates 2013) found dyslexia-specific
   and comic faces gave no reading-rate benefit over a plain sans — OpenDyslexic
   measured *worse* than Arial and was the least-preferred font tested. The
   typeface is the weakest lever here; the spacing, measure and leading below
   are the parts with actual support, so the font choice stays neutral and the
   site keeps looking like itself.

   Line height is applied in JS, not here, so it composes correctly with the
   Bigger text and Line height tiles.

   Composition with the other tiles:
     - vs Readable font — this block sits later in the file at equal
       specificity, so with both on, the dyslexia stack wins. That is the
       intended precedence: it is the more specific need.
     - vs Text spacing  — the spacing declarations below are gated on the
       Text spacing tile being OFF. They cannot simply rely on source order,
       because the :not() exclusions inflate this block's specificity to
       (0,3,2) against the spacing tile's (0,1,3) — it would win no matter
       where it sat. Gating makes the precedence explicit instead of
       accidental, so choosing a spacing level still increases spacing.
   ========================================================================== */

/* font-size-adjust keeps the apparent size steady across the swap.

   Measured on this theme at 100px: Lato has an x-height of 0.5065 and a cap
   height of 0.7165; OpenDyslexic has 0.56 and 0.847. So at an identical
   font-size, OpenDyslexic renders 10.6% taller in the lowercase and 18% taller
   in the capitals — every heading and paragraph visibly grows the moment the
   tile goes on, which is what "fix the font size of the dyslexic font" was
   about.

   Pinning to Lato's x-height ratio scales OpenDyslexic to ~0.905 of the
   nominal size, so a 15px paragraph looks like a 15px paragraph. The property
   does this per element and against whichever face actually resolves, so if
   OpenDyslexic fails to load the fallback is matched on its own metrics
   instead of being shrunk for no reason — which a fixed multiplier in JS could
   not manage.

   The value is the *target* x-height ratio, not a scale factor: change it to
   the body font's ratio if the theme's typeface ever changes. */
html[data-a11y-dyslexiafriendly="1"] body,
html[data-a11y-dyslexiafriendly="1"] body *:not(.filld-a11y):not(.filld-a11y *) {
    font-family: "OpenDyslexic", "Trebuchet MS", Verdana, "DejaVu Sans", sans-serif !important;
    font-size-adjust: var(--fa11y-size-adjust, 0.507) !important;
}

/* Running text only — deliberately not `body *`, and not headings either.

   Widening every element on the page is what made this tile look broken: word
   spacing on a nav bar pushes the items apart until they wrap, and on a 39px
   heading it adds 6px to every space, which was enough to turn a two-line
   hero heading into three lines and double its height. The decoding benefit of
   extra tracking is in running text, where the eye is stepping through many
   words in sequence; a five-word heading set at 39px is not where anyone
   struggles. So the cost lands where the benefit isn't, and this now stays on
   the prose. */
html[data-a11y-dyslexiafriendly="1"]:not([data-a11y-spacing]) body
    :is(p, li, dd, dt, blockquote, figcaption, td, th):not(.filld-a11y *) {
    letter-spacing: 0.06em !important;
    word-spacing: 0.16em !important;
}

/* ==========================================================================
   Site navigation keeps the theme font
   Applies to both font tiles, and it is a layout fix, not a preference.

   The theme's primary nav is a flex row with a fixed horizontal budget: five
   labels sharing 613px inside a 68px-tall bar. Set in OpenDyslexic the same
   five labels need 804px — 31% more — so the items shrink, each label wraps to
   three lines, and the links render 112px tall spanning -22px to 90px against
   a 69px header. That is the clipped, overlapping menu in the bug report.

   There is no version of this that fits. Allowing the row to wrap was measured
   too: the nav goes to two rows and 132px, which on a sticky header would eat
   a permanent 15% of a laptop viewport — worse for everyone, including the
   visitor who turned the tile on.

   So the swap stops at the content. Nav labels are two or three words, found
   by position and shape as much as by reading, and the evidence for a
   dyslexia face is about sustained reading of prose — which is exactly what
   still gets it. A broken menu helps nobody.

   --fa11y-chrome-font is emitted by PHP and filterable via
   'filld_a11y_chrome_font'; the fallback here only matters if that inline
   style is missing.
   ========================================================================== */

html[data-a11y-dyslexiafriendly="1"] body :is(header, .site-header) :is(nav, .nav-bar),
html[data-a11y-dyslexiafriendly="1"] body :is(header, .site-header) :is(nav, .nav-bar) *,
html[data-a11y-readablefont="1"] body :is(header, .site-header) :is(nav, .nav-bar),
html[data-a11y-readablefont="1"] body :is(header, .site-header) :is(nav, .nav-bar) * {
    font-family: var(--fa11y-chrome-font, "Lato", "Helvetica Neue", Helvetica, Arial, sans-serif) !important;
    letter-spacing: normal !important;
    word-spacing: normal !important;
}

/* Ragged-right only. Justified text opens rivers of white space that are
   especially hard to track across, and centred blocks give the eye no
   consistent left edge to return to. */
html[data-a11y-dyslexiafriendly="1"] body :is(p, li, dd, dt, blockquote, figcaption, h1, h2, h3, h4, h5, h6):not(.filld-a11y *) {
    text-align: left !important;
    text-justify: none !important;
}

/* Long measures are the other common obstacle. Capped on running text only —
   applying this to headings or layout containers would fight the grid. */
html[data-a11y-dyslexiafriendly="1"] body :is(p, li, blockquote):not(.filld-a11y *) {
    max-width: 68ch !important;
}

/* Italic and all-caps runs are markedly harder to decode — normalise them. */
html[data-a11y-dyslexiafriendly="1"] body :is(em, i, cite):not(.filld-a11y *) {
    font-style: normal !important;
    font-weight: 700 !important;
}

html[data-a11y-dyslexiafriendly="1"] body *:not(.filld-a11y):not(.filld-a11y *) {
    text-transform: none !important;
}

/* ==========================================================================
   Highlight links
   Underline plus a boxed outline — underline alone is easy to miss in running
   copy at small sizes.
   ========================================================================== */

html[data-a11y-highlightlinks="1"] body a:not(.filld-a11y *) {
    text-decoration: underline !important;
    text-underline-offset: 2px !important;
    outline: 2px solid #1e5fbf !important;
    outline-offset: 2px !important;
    background-color: #fffbe6 !important;
    color: #10305f !important;
}

html[data-a11y-highlightlinks="1"] body a *:not(.filld-a11y *) {
    background-color: transparent !important;
    color: inherit !important;
}

/**
 * Image-only links — the site logo, icon links, image cards.
 *
 * widget.js tags these (see tagLinkTypes) because CSS cannot ask whether an
 * element has visible text. They still need to be identifiable as links, but
 * the full treatment is wrong for them: a cream fill behind a logo reads as a
 * rendering fault, and an underline under an image is meaningless. So they get
 * the outline only, pushed clear of the artwork.
 *
 * Placed after the rules above so it wins on source order at equal specificity.
 */
html[data-a11y-highlightlinks="1"] body a[data-fa11y-linktype="image"]:not(.filld-a11y *) {
    background-color: transparent !important;
    text-decoration: none !important;
    outline-offset: 4px !important;
    border-radius: 4px !important;
}

/* Under a contrast mode the highlight has to follow that palette instead. */
html[data-a11y-contrast="1"][data-a11y-highlightlinks="1"] body a:not(.filld-a11y *) {
    background-color: #000 !important;
    color: #ffe600 !important;
    outline-color: #ffe600 !important;
}

html[data-a11y-contrast="2"][data-a11y-highlightlinks="1"] body a:not(.filld-a11y *) {
    background-color: #10151c !important;
    color: #9ecbff !important;
    outline-color: #9ecbff !important;
}

/* The two rules above carry an extra attribute selector, so they outrank the
   image-link exception further up and would paint a panel back in behind the
   logo. Restate the exception at matching specificity. The outline colour is
   left to those rules — only the fill has to go. */
html[data-a11y-contrast="1"][data-a11y-highlightlinks="1"] body a[data-fa11y-linktype="image"]:not(.filld-a11y *),
html[data-a11y-contrast="2"][data-a11y-highlightlinks="1"] body a[data-fa11y-linktype="image"]:not(.filld-a11y *) {
    background-color: transparent !important;
}

/* The Focus outline tile was removed in 0.9.10 at the client's request. It
   amplified the theme's own :focus-visible ring; the theme's ring is still
   there and is what keyboard users now get. */

/* ==========================================================================
   Large cursor
   Inline SVG data URIs — no image request, scales cleanly. The hotspot is
   pinned so the click point stays at the arrow tip.
   ========================================================================== */

html[data-a11y-bigcursor="1"] body,
html[data-a11y-bigcursor="1"] body *:not(.filld-a11y):not(.filld-a11y *) {
    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="M5 2l14 10-6 1 3.5 7-3 1.5L10 14l-5 4z" fill="%23000000" stroke="%23ffffff" stroke-width="1.5"/></svg>') 0 0, auto !important;
}

html[data-a11y-bigcursor="1"] body :is(a, button, [role="button"], label, summary, select):not(.filld-a11y *) {
    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 11V4.5a1.5 1.5 0 0 1 3 0V10h.5V5.5a1.5 1.5 0 0 1 3 0V10h.5V7a1.5 1.5 0 0 1 3 0v8a6 6 0 0 1-6 6h-1a6 6 0 0 1-6-6v-3a1.5 1.5 0 0 1 3-1z" fill="%23000000" stroke="%23ffffff" stroke-width="1.2"/></svg>') 8 0, pointer !important;
}

/* ==========================================================================
   Pause animations
   Covers CSS animation, transition and smooth scrolling. Playing <video> and
   <audio> are paused from JS — CSS cannot do that.
   ========================================================================== */

html[data-a11y-pauseanimations="1"] body *:not(.filld-a11y):not(.filld-a11y *),
html[data-a11y-pauseanimations="1"] body *:not(.filld-a11y):not(.filld-a11y *)::before,
html[data-a11y-pauseanimations="1"] body *:not(.filld-a11y):not(.filld-a11y *)::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
    transition-delay: 0ms !important;
}

html[data-a11y-pauseanimations="1"] {
    scroll-behavior: auto !important;
}

/* ==========================================================================
   Hide images
   visibility rather than display, so the layout does not reflow and the page
   the visitor was reading does not jump under them.
   ========================================================================== */

html[data-a11y-hideimages="1"] body :is(img, picture, svg, video, canvas, figure > iframe):not(.filld-a11y *):not(.filld-a11y) {
    visibility: hidden !important;
}

html[data-a11y-hideimages="1"] body [style*="background-image"]:not(.filld-a11y *) {
    background-image: none !important;
}
