/* ==========================================================================
   Tripo-Pulse accessibility utilities
   P1.F2.T5 · ©2026 Vexstorm Studios – All Rights Reserved

   Three things live here and nothing else:

     1. THE CLOSED UTILITY SET. project_namespaces.md §5.4 fixes it at exactly
        six members: visually-hidden, truncate, no-wrap, text-numeric, stack,
        cluster. Adding a seventh requires orchestrator approval. The set is
        closed on purpose — utility CSS grows without limit otherwise, and a
        product with forty utilities has no design system, only a shorthand for
        inline styles.

     2. THE FOCUS RING, defined once. Every component inherits it; a component
        only redeclares :focus-visible when the ring would be clipped, and then
        it adjusts the offset rather than the colour.

     3. THE REDUCED-MOTION BLANKET (ACC-005). One of exactly two places in the
        product where !important is permitted; print.css is the other.
   ========================================================================== */

/* ==========================================================================
   1. THE CLOSED UTILITY SET
   ========================================================================== */

/* Hide from sight, keep for assistive technology. The clip-path/1px technique
   rather than display:none or visibility:hidden, both of which remove the
   content from the accessibility tree entirely.

   Used for: the visible label of an icon-only control, a table caption that
   would be redundant on screen, the live-region status text a toast announces,
   and the "skip to content" text before it is focused. */
.tp-u-visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* The one exception: a visually-hidden element that becomes visible when
   focused, which is what a skip link is. Anything using this pattern must
   still be reachable and legible once revealed. */
.tp-u-visually-hidden:focus-visible,
.tp-u-visually-hidden:focus-within {
  position: static;
  width: auto;
  height: auto;
  margin: 0;
  overflow: visible;
  clip-path: none;
  white-space: normal;
}

/* Single-line ellipsis. Only ever applied to text that is also available in
   full somewhere the user can reach — a title attribute, a detail page, or the
   exact-value table beside a chart. Truncation that loses information is a
   defect, not a style. */
.tp-u-truncate {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Prevent a wrap that would change meaning: an observation identifier, a
   date-time, a units-carrying figure. */
.tp-u-no-wrap {
  white-space: nowrap;
}

/* Tabular figures for anything that will be compared down a column. Without
   this, proportional digits make two counts of the same magnitude look
   different lengths, which is actively misleading in a moderation report. */
.tp-u-text-numeric {
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum" 1;
}

/* Vertical rhythm. A flow container whose children are separated by one
   standard gap — the single most repeated layout in the product. */
.tp-u-stack {
  display: flex;
  flex-direction: column;
  gap: var(--tp-space-4);
  min-width: 0;
}

/* Horizontal grouping that wraps rather than overflowing. This is what keeps a
   row of buttons or badges from producing horizontal body scroll at 320px
   (UX-004). */
.tp-u-cluster {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--tp-space-2);
  min-width: 0;
}

/* ==========================================================================
   2. THE FOCUS RING (ACC-002)

   One definition, applied to everything focusable. `outline` rather than a
   box-shadow because an outline follows border-radius, is never clipped by
   overflow on the element itself, and survives forced-colors mode.

   `outline-offset` is what makes the ring readable on a filled control: the
   offset gap exposes the surface behind the control, so the focus colour is
   always adjacent to a surface token rather than to a fill. That is exactly
   the pairing tests/accessibility/test_token_contrast.py asserts at 3:1 on
   every surface in both themes.

   :focus-visible rather than :focus, so a mouse click on a button does not
   leave a ring behind, while every keyboard interaction does.
   ========================================================================== */

:focus-visible {
  outline: var(--tp-border-width-thick) solid var(--tp-color-border-focus);
  outline-offset: var(--tp-space-1);
}

/* Windows High Contrast / forced-colors: the system supplies the palette and
   our tokens are ignored. Keep the ring, let the system colour it. */
@media (forced-colors: active) {
  :focus-visible {
    outline-color: Highlight;
  }
}

/* ==========================================================================
   3. REDUCED MOTION (ACC-005)

   Everything decorative stops. Nothing in this product depends on animation to
   convey state — a toast still appears, a modal still opens, a skeleton still
   marks a loading region — so a blanket disable loses no information.

   !important is required and is permitted here: the blanket has to beat
   component declarations it knows nothing about, and a reduced-motion setting
   that a component can accidentally override is not a setting.
   ========================================================================== */

@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;
  }
}

/* ==========================================================================
   4. THEME SWITCHING — transitions off for the duration of the flip

   theme.js sets .tp-is-theme-switching on <html>, changes data-theme, forces a
   reflow, and clears the class on the next frame.

   Two reasons, one cosmetic and one a real defect:

     * Cosmetic — without it, every transitioned property on the page
       cross-fades at once when the user toggles. A whole interface dissolving
       between colours is exactly the non-essential motion ACC-005 exists to
       remove, and it makes the toggle feel slow.

     * Correctness — a property being transitioned when its `var()` source
       changes can retain the OLD resolved colour in Chromium. Measured on this
       component set: after toggling to dark, .tp-pagination__link[aria-current]
       kept the light accent (#2563c4) behind dark-theme text, giving 3.37:1
       where the token pair is 6.6:1. The stale value is not a paint artefact —
       getComputedStyle reports it — so it survives until something else forces
       a recalculation. Suppressing the transition across the flip removes the
       window in which it can happen.

   !important is required for the same reason the reduced-motion blanket needs
   it: this has to beat component declarations it knows nothing about.
   ========================================================================== */

.tp-is-theme-switching,
.tp-is-theme-switching *,
.tp-is-theme-switching *::before,
.tp-is-theme-switching *::after {
  transition: none !important;
}
