/* ============================================================================
   TAPN™ Entry — Accessibility base
   Source of truth: MASTER_DESIGN.md v1.0, § Elevation level 7 and § 13.

   Loads AFTER tapn-tokens.css and before page styles. Everything here reads
   tokens; no literal values.

   Three independent signals are handled, per § 13:
     prefers-reduced-motion       — gentler, non-vestibular equivalents
     prefers-reduced-transparency — translucent chrome goes solid
     prefers-contrast: more       — the one place the no-borders rule is lifted
   ========================================================================= */


/* ── Focus — Elevation level 7 ───────────────────────────────────────────────
   3px primary-focus ring at 40%, 2px offset. 15 files suppressed the UA
   outline without providing any replacement; this restores a visible focus
   state everywhere and is deliberately hard to override by accident.

   :focus-visible only, so a pointer press never paints a ring, but keyboard
   and other non-pointer input always does.                                   */

:where(a, button, input, select, textarea, summary, [tabindex], [role="button"],
       [role="tab"], [role="link"], [contenteditable="true"]):focus-visible {
  outline: none;
  box-shadow: var(--tapn-focus-ring);
  border-radius: inherit;
  position: relative;
  z-index: 1;
}

/* Where an element already animates box-shadow, the ring must still win. */
:where(a, button, input, select, textarea, summary, [tabindex]):focus-visible {
  transition-property: none;
}

/* Never leave a control with no focus affordance at all. If a page removes the
   outline in its own stylesheet, the ring above still applies because it is
   more specific than a bare `outline: none` on the same element. */


/* ── Touch targets — § Responsive Behavior ───────────────────────────────────
   >=48px on touch, with ~10px of invisible hit padding beyond the visual
   bounds. Applied only on coarse pointers so desktop density is untouched.   */

@media (pointer: coarse) {
  :where(button, [role="button"], a.btn, .btn, input[type="submit"],
         input[type="button"]) {
    min-height: var(--tapn-target-touch);
  }
  :where(input, select, textarea) {
    min-height: var(--tapn-target-touch);
  }
  /* Invisible hit expansion: does not affect layout. */
  :where(button, [role="button"], .btn)::after {
    content: "";
    position: absolute;
    inset: calc(var(--tapn-hit-padding) * -1);
    pointer-events: auto;
  }
  :where(button, [role="button"], .btn) {
    position: relative;
  }
}


/* ── Reduced motion — § 13 ───────────────────────────────────────────────────
   Not the absence of feedback: presses, colour and opacity stay. What goes is
   the vestibular part — travel, parallax, overshoot, and above all the
   ambient drift.

   The doc is explicit that a full-viewport field of moving objects is exactly
   the trigger this setting exists for. Six surfaces in this codebase ran
   infinite ambient animations with no guard; they are named below so the
   intent is legible rather than relying only on the blanket rule.           */

@media (prefers-reduced-motion: reduce) {

  /* Blanket: nothing travels, nothing loops. Opacity and colour survive. */
  *, *::before, *::after {
    animation-duration: 1ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: var(--tapn-reduced-motion-fade) !important;
    scroll-behavior: auto !important;
  }

  /* The eight ambient loops in this codebase, by their actual selectors. The
     blanket rule above already stops them; these are named so the intent is
     explicit and so `transform` is reset rather than frozen mid-travel.

       .particle              register.php, signin.php, event-login.php
                              — position:fixed, full-viewport, translateY(100vh)
                              — the exact vestibular trigger the doc calls out
       .hero-clock-hand--*    dashboard.php — the 10s hand is ~0.1 Hz, inside
                              the doc's slow-oscillation caution band
       .ev-dot / .live-dot    coordinator_events.php, dashboard_coordinator.php
                              — 1.9s pulse (~0.53 Hz)                          */
  .particle,
  .hero-clock-hand--hour,
  .hero-clock-hand--minute,
  .hero-clock-hand--second,
  .events-page .ev-dot,
  .dash .live-dot {
    animation: none !important;
    transform: none !important;
  }

  /* The particle field carries no meaning; hide it rather than freeze it. */
  .particle { opacity: 0 !important; }

  /* Press feedback stays — it is causal, not vestibular — but loses travel. */
  :where(button, .btn, [role="button"]):active {
    transform: none !important;
  }
}


/* ── Reduced transparency — § 13 ─────────────────────────────────────────────
   Translucent chrome goes solid and the blur comes off. The token layer
   already swaps material-chrome to surface-1; this removes the filters that
   tokens cannot reach.                                                       */

@media (prefers-reduced-transparency: reduce) {
  * {
    backdrop-filter: none !important;
    -webkit-backdrop-filter: none !important;
  }
  :where([class*="glass"], [class*="chrome"], [class*="sheet"],
         [class*="modal"], [class*="nav"], [class*="sidebar"],
         [class*="toast"], [class*="panel"]) {
    background-color: var(--tapn-surface-1);
  }
}


/* ── Increased contrast — § 13 ───────────────────────────────────────────────
   The one context where TAPN's no-borders rule is suspended: card shadows are
   replaced with a 1px hairline-strong border, and body text moves from
   ink-muted to full ink. The token layer already retunes the colours and
   nulls the card shadows; this adds the borders they are traded for.        */

@media (prefers-contrast: more) {
  :where([class*="card"], [class*="panel"], [class*="tile"], [class*="sheet"],
         [class*="modal"], [class*="widget"], [class*="box"]) {
    border: var(--tapn-contrast-border);
  }
  /* A focus ring at 40% alpha is not enough here. */
  :where(a, button, input, select, textarea, [tabindex]):focus-visible {
    outline: var(--tapn-focus-ring-width) solid var(--tapn-primary-pressed);
    outline-offset: var(--tapn-focus-ring-offset);
    box-shadow: none;
  }
}


/* ── Forced colours (Windows high contrast) ──────────────────────────────────
   Shadow-based depth conveys nothing here, so restore real boundaries.       */

@media (forced-colors: active) {
  :where([class*="card"], [class*="panel"], [class*="modal"]) {
    border: 1px solid CanvasText;
  }
  :where(a, button, input, select, textarea, [tabindex]):focus-visible {
    outline: 3px solid Highlight;
    outline-offset: 2px;
  }
}
