/* Glidance training site.
 *
 * Rules this stylesheet is written to hold to, all of them WCAG 2.2 Level AA
 * obligations rather than preferences:
 *   - Never set a root font size in px; the user's browser setting wins.
 *   - Never remove a focus indicator without replacing it with a stronger one.
 *   - Body text and UI text meet 4.5:1 contrast; large text meets 3:1.
 *   - Layout reflows to 320px with no horizontal scrolling (1.4.10).
 *   - Nothing conveys meaning by color alone (1.4.1).
 *   - Standalone links are at least 24px tall, and 44px on touch (2.5.8, and the
 *     scope document's stricter mobile figure).
 *   - Nothing is sticky, fixed, or overlaid, so a focused control can never be
 *     covered (2.4.11 Focus Not Obscured, new in 2.2). Keep it that way.
 *
 * The site has to work on a phone with VoiceOver on iOS Safari and TalkBack on
 * Android Chrome, so several rules below exist for touch rather than for mouse.
 */

/* Atkinson Hyperlegible Next, the typeface glidance.io uses.
 *
 * Adopted on its merits, not only for brand consistency: it was designed by the
 * Braille Institute specifically for low-vision readers, with letterforms drawn
 * to be distinguishable from one another rather than to be uniform. That is the
 * right choice for this audience regardless of what the marketing site does.
 *
 * Self-hosted rather than loaded from Google Fonts. A third-party font request
 * on every page is a privacy leak and a dependency, and it would need the CSP to
 * be relaxed. One 34 KB variable file covers 400 through 700.
 *
 * SIL Open Font License 1.1, Braille Institute of America — see
 * assets/fonts/OFL.txt, which must ship with the font.
 *
 * font-display: swap so text is readable in the fallback face immediately. For
 * a screen reader user the font never matters; for a low-vision user, waiting on
 * a web font to read anything is the worse failure. */
@font-face {
  font-family: "Atkinson Hyperlegible Next";
  src: url("/assets/fonts/atkinson-hyperlegible-next-latin.woff2") format("woff2");
  font-weight: 400 700;
  font-style: normal;
  font-display: swap;
}

/* Palette derived from glidance.io, adjusted where the brand values do not meet
 * AA. Every pair is checked by tools/check-contrast.mjs in both schemes.
 *
 * What was taken as-is:
 *   --text        #282d2e is the site's own heading colour, 13.95:1 on white.
 *   --surface     #edfeff is its pale cyan, used here for code and table headers.
 *   --brand       #de1c6d is the exact logo magenta, kept for non-text accents.
 *
 * What had to change, and why:
 *   Their link colour is #de1c6d with no underline, on a body colour so close to
 *   it that the two are hard to tell apart — that fails 1.4.1. Here links are
 *   always underlined, and the magenta is darkened to #b01453 (6.84:1 rather
 *   than the brand value's 4.67:1). 4.67 does pass AA, but it sits close enough
 *   to the 4.5 line that any future tweak breaks it, and this audience is the
 *   last one to spend that margin on.
 *
 *   The brand magenta is 3.85:1 on the dark background and FAILS outright, so
 *   dark mode uses a lightened #ee5c93 at 5.64:1.
 *
 *   Their paragraph grey #757575 is 4.61:1. Ours stays at 9.08:1.
 */
:root {
  --text: #282d2e;
  --text-muted: #44494f;
  --background: #ffffff;
  --surface: #edfeff;
  --link: #b01453;
  --link-visited: #6b2d8f;
  --border: #767b82;
  --focus: #a1230b;
  --brand: #de1c6d;
  --measure: 68ch;
}

@media (prefers-color-scheme: dark) {
  :root {
    --text: #e9ecef;
    --text-muted: #b9c0c7;
    --background: #14171a;
    --surface: #22272b;
    --link: #ee5c93;
    --link-visited: #d0a6ec;
    --border: #8b9199;
    --focus: #ffb27a;
    --brand: #ee5c93;
  }
}

*,
*::before,
*::after {
  box-sizing: border-box;
}

html {
  /* No px value here on purpose: this inherits the user's own font size. */
  font-family: "Atkinson Hyperlegible Next", "Atkinson Hyperlegible", system-ui, sans-serif;
  color: var(--text);
  background-color: var(--background);

  /* iOS Safari inflates text on rotation unless this is pinned. 100% stops the
     automatic inflation; it does NOT stop pinch zoom. Never set this to "none",
     which would block zoom and fail 1.4.4. */
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
}

body {
  margin: 0;
  font-size: 1.0625rem;
  line-height: 1.6;
}

/* Focus is the single most important visual affordance for keyboard users.
   A thick outline plus an offset stays visible against any background here. */
:focus-visible {
  outline: 3px solid var(--focus);
  outline-offset: 2px;
}

.skip-link {
  position: absolute;
  left: -100vw;
  top: 0;
  padding: 0.75rem 1rem;
  background-color: var(--background);
  color: var(--link);
  z-index: 10;
}

/* Off-screen until focused, then placed in the normal reading position.
   display:none or visibility:hidden would remove it from the tab order. */
.skip-link:focus {
  left: 0;
}

a {
  color: var(--link);
}

a:visited {
  color: var(--link-visited);
}

/* Underlines stay on body links. Color alone is not enough (1.4.1). */
main a {
  text-decoration: underline;
  text-underline-offset: 0.15em;
}

/* Touch target size, and cursor-landing safety.
 *
 * A bare link at this font size is about 22px tall, which fails WCAG 2.5.8.
 * Links inside a sentence are exempt from the criterion and are left alone; this
 * targets the standalone ones a thumb aims at.
 *
 * `block`, not `inline-block`, and that distinction is the important part. An
 * inline-block link leaves room beside itself in the line box for anything else
 * inside the <li> — a whitespace text node, a list marker — and NVDA's browse
 * cursor stops there when the user arrows to the item or jumps by list item
 * ("i"). Enter on that stop does nothing, while jumping by link ("k") still
 * works, so the defect hides from casual testing and from pa11y and axe alike.
 * `block` makes the anchor the item's entire content box: nowhere else to land.
 *
 * It also makes the whole row a touch target rather than just the text. */
.contents-list a,
.contents-sublist a,
.section-pager a,
.section-index a {
  display: block;
  min-height: 24px;
  padding-block: 0.25rem;
}

/* The site name is a standalone link too, but inline-block: it is not a list
   item and should not span the header. Sizing lives here, above the
   pointer: coarse block below, so that block's 44px actually wins — these
   selectors have equal specificity, so source order decides. */
.site-name a {
  display: inline-block;
  min-height: 24px;
  padding-block: 0.25rem;
}

/* The scope document requires "at least 44x44px on mobile", which is stricter
   than the 24px WCAG floor above. `pointer: coarse` is the precise test for
   "being operated by a finger rather than a mouse", so the larger target applies
   exactly where the requirement asks and does not stretch a 13-item contents
   list down a desktop screen. */
@media (pointer: coarse) {
  .contents-list a,
  .contents-sublist a,
  .section-pager a,
  .section-index a,
  .site-name a {
    min-height: 44px;
    padding-block: 0.6rem;
  }

  .contents-disclosure > summary,
  .contents-sub > summary,
  .contents-expand {
    min-height: 44px;
    padding-block: 0.6rem;
  }
}

.site-header,
.site-footer,
main {
  max-width: var(--measure);
  margin-inline: auto;
  padding-inline: 1rem;
}

.site-header {
  padding-block: 1rem;
  /* The brand magenta as a rule under the header. Decorative, so the 3:1 UI
     threshold applies rather than 4.5:1, and the exact logo colour clears it. */
  border-bottom: 3px solid var(--brand);
}

.site-name {
  margin: 0 0 0.5rem;
  font-size: 1.125rem;
  font-weight: 700;
}

/* The logo carries the word "Glidance" as its alt text and the visible text
   beside it says "Training", so the link's accessible name reads "Glidance
   Training" without the word appearing twice on screen. */
.site-logo {
  width: 175px;
  height: 30px;
  max-width: 60vw;
  vertical-align: middle;
  margin-inline-end: 0.4rem;
}

/* Sizing for this link is set above, deliberately before the pointer: coarse
   block. Do not add min-height or padding here — it would win on source order
   and silently undo the 44px touch target. */
.site-name a {
  text-decoration: none;
}

.site-name a:hover {
  text-decoration: underline;
}

/* Table of contents, used as the site menu. <details> supplies the disclosure
   behaviour the scope document asks for, with no JavaScript. */
.contents-disclosure > summary {
  display: block; /* keeps the default triangle out of the accessible name */
  padding-block: 0.5rem;
  font-weight: 700;
  cursor: pointer;
}

.contents-disclosure > summary::before {
  content: "\25B8\00A0"; /* right-pointing triangle */
  display: inline-block;
}

.contents-disclosure[open] > summary::before {
  content: "\25BE\00A0"; /* down-pointing triangle */
}

/* The "Contents" summary and the Expand All button share a line.
 *
 * Flex, not absolute positioning: the button is a sibling of the <details>, and
 * align-items: flex-start holds it level with the summary while the opened list
 * grows downward beneath it. Nothing overlaps anything, so a focused control can
 * never end up covered (2.4.11). */
.contents-bar {
  display: flex;
  align-items: flex-start;
  flex-wrap: wrap;
  gap: 0.25rem 1rem;
}

.contents-bar > .contents-disclosure {
  flex: 1 1 auto;
}

.contents-expand {
  flex: 0 0 auto;
  margin-block: 0.5rem;
  padding: 0.35rem 0.75rem;
  color: var(--link);
  background-color: var(--background);
  border: 2px solid var(--border);
  border-radius: 0.25rem;
  font: inherit;
  font-size: 0.9375rem;
  cursor: pointer;
}

.contents-expand:hover {
  border-color: var(--link);
}

/* Nested disclosures inside the menu. Indented with padding, never with
   whitespace in the markup. */
.contents-sub > summary {
  display: block;
  padding-block: 0.35rem;
  cursor: pointer;
}

.contents-sub > summary::before {
  content: "\25B8\00A0";
  display: inline-block;
}

.contents-sub[open] > summary::before {
  content: "\25BE\00A0";
}

.contents-sublist {
  list-style: none;
  margin: 0 0 0.25rem;
  padding-inline-start: 1.5rem;
  border-inline-start: 2px solid var(--border);
}

.contents-branch {
  margin-block-end: 0.15rem;
}

.section-children {
  margin-block-start: 2.5rem;
}

/* Link lists: the contents menu, the home page section index, and the pager.
 *
 * list-style: none removes the marker. That is not cosmetic here. A marker — a
 * bullet or a number — is drawn inside the <li> and outside the <a>, so it is
 * another place the browse cursor can stop where Enter does nothing. The
 * matching role="list" in the markup is what keeps Safari announcing these as
 * lists, since Safari drops list semantics when the marker is removed.
 *
 * Indentation is done here with padding, never with whitespace in the HTML. */
.contents-list,
.contents-sublist,
.section-index,
.pager-list {
  list-style: none;
}

.contents-list,
.section-index,
.pager-list {
  margin: 0.25rem 0 0.5rem;
  padding-inline-start: 0;
}

.contents-list li,
.contents-sublist li,
.section-index li,
.pager-list li {
  margin-block-end: 0;
}

/* The current page is marked by aria-current for screen readers; this is the
   matching visual cue, and it uses weight and a rule rather than color alone. */
.contents-list a[aria-current="page"] {
  font-weight: 700;
  text-decoration-thickness: 0.2em;
  text-underline-offset: 0.2em;
}

.section-progress {
  margin-block: 0 1.5rem;
  color: var(--text-muted);
  font-size: 0.9375rem;
}

main {
  padding-block: 2rem 3rem;
}

main:focus {
  /* Focus lands here from the skip link. Do not draw a box around the whole
     page; the heading announcement is the feedback that matters. */
  outline: none;
}

h1,
h2,
h3,
h4 {
  line-height: 1.25;
  margin-block: 2rem 0.5rem;
  text-wrap: balance;
}

h1 {
  margin-block-start: 0;
  font-size: 1.875rem;
}

h2 {
  font-size: 1.5rem;
}

h3 {
  font-size: 1.25rem;
}

p,
ul,
ol,
dl,
table,
pre,
blockquote {
  margin-block: 0 1rem;
}

li {
  margin-block-end: 0.35rem;
}

code,
pre {
  font-family: ui-monospace, monospace;
  font-size: 0.9375em;
}

pre {
  padding: 1rem;
  background-color: var(--surface);
  border: 1px solid var(--border);
  /* Reflow rule: long code must scroll in its own box, not the page. */
  overflow-x: auto;
}

blockquote {
  padding-inline-start: 1rem;
  border-inline-start: 4px solid var(--border);
  color: var(--text-muted);
}

table {
  border-collapse: collapse;
  width: 100%;
}

th,
td {
  padding: 0.5rem 0.75rem;
  border: 1px solid var(--border);
  text-align: start;
}

th {
  background-color: var(--surface);
}

img {
  max-width: 100%;
  height: auto;
}

/* Form controls, for when they arrive. Two mobile rules baked in now so the
   first form does not reintroduce them:
   - font: inherit keeps controls at 17px. iOS Safari zooms the whole page in
     when a focused control's text is under 16px, which shifts the layout under
     someone who is already navigating by touch and audio.
   - 2.75rem is Apple's and Google's recommended touch target, comfortably above
     the 24px WCAG 2.5.8 floor. Controls are the targets that most need it. */
input,
select,
textarea,
button {
  font: inherit;
  min-height: 2.75rem;
}

.section-pager {
  margin-block-start: 3rem;
  padding-block-start: 1rem;
  border-top: 1px solid var(--border);
}


/* Visible label, deliberately not hidden. It carries the same information to a
   sighted reader that it carries into the link's accessible name. */
.pager-direction {
  font-weight: 700;
}

.site-footer {
  padding-block: 1.5rem 2rem;
  border-top: 1px solid var(--border);
  color: var(--text-muted);
}

/* Honour a user who has asked the OS to reduce motion. Nothing here animates
   today; this is the guard for whatever gets added later. */
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}
