/* INSTALLED COPY — generated by tools/sites/install-sitekit.mjs from sitekit/css/safeguards.css.
   Do not edit here. Change the workspace source and reinstall. */
/*
 * SiteKit safeguards — technical only, and deliberately design-agnostic.
 *
 * There is no palette here, no type scale, no spacing scale and no component. Those
 * belong to the supplied template, which is the visual source of truth for its site.
 * This file closes the class of defect `tools/qa/render-probe.mjs` calls an
 * INVARIANT, and it does so without expressing any opinion about how a site looks.
 *
 * It declares no colour of its own: `outline` and `currentColor` inherit whatever
 * the template sets, so loading this file can never change a design.
 *
 * Each block names the invariant it prevents. Do not remove one to tidy up.
 */

*, *::before, *::after { box-sizing: border-box; }

/*
 * INVARIANT: `body is a scroll container (overflow-x: ...)`.
 * overflow-x on html or body forces the overflow measurement to read 0 whether or
 * not the page overflows, and breaks every sticky child. Containment belongs on the
 * offending component. A template that needs to clip something clips that thing.
 */
html, body { overflow-x: visible; }

/*
 * INVARIANT: `Npx horizontal overflow with the clip lifted`.
 * The three habitual causes, closed once: replaced elements wider than their
 * container, long unbroken strings, and wide tables.
 */
img, picture, video, canvas, svg, iframe { max-width: 100%; }
img, video, canvas { height: auto; }
h1, h2, h3, h4, h5, h6, p, li, a, dt, dd, figcaption, th, td, blockquote { overflow-wrap: anywhere; }

/* A wide child scrolls inside its own box, so the document never scrolls sideways. */
.sk-scroll-x { overflow-x: auto; -webkit-overflow-scrolling: touch; }

/*
 * INVARIANT: `interactive control has no accessible name` — the markup contract for
 * an icon-only control. Positioning only; the template owns how it looks.
 */
.sk-visually-hidden {
  position: absolute; width: 1px; height: 1px; padding: 0;
  margin: -1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap;
}

/*
 * Keyboard visibility. currentColor, so this inherits the template's ink and
 * introduces no colour decision of its own.
 */
:focus-visible { outline: 3px solid; outline-offset: 2px; }

/*
 * Accessibility floor as a UTILITY, never applied to a component here. A template
 * opts a control in; SiteKit does not decide which elements are controls.
 */
.sk-tap { min-height: 44px; min-width: 44px; }

/*
 * INVARIANT: `a service card is not reachable`. The carousel is a native horizontal
 * scroll container, so every card stays reachable by finger, trackpad and keyboard with
 * no script at all. Positioning and overflow only — no colour, no spacing scale, no card
 * geometry and no opinion about how many cards are visible. Those are the template's.
 */
[data-carousel-track] {
  display: flex;
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  -webkit-overflow-scrolling: touch;
}
[data-carousel-track] > * { flex: 0 0 auto; scroll-snap-align: start; }

/* Scroll snapping and continuously moving content are mutually exclusive: snap drags the
   position back to the nearest card on every frame, so a drifting track judders in place
   instead of moving.

   THIS USED TO BE SCOPED TO [data-carousel-drifting] — on for exactly as long as the track was
   animating, off the moment it was still, so that a swipe would still land on a card. That
   scoping was the bug. Every pause flipped snap back ON with the track parked mid-card, and the
   browser immediately dragged it to the nearest edge: pause and the carousel visibly jumped.
   It read as the component resetting itself, which is precisely what an endless loop must never
   appear to do, and it fired on focus and after every swipe as well as on hover.

   An autoplaying carousel is therefore exempt for its whole life, not for the duration of a
   frame loop. Snap and continuous motion cannot share a track: a swipe now ends where the
   reader let go and the drift picks up from there, which is what continuous means. A carousel
   with no autoplay is a discrete pager and keeps its snap. */
[data-carousel][data-carousel-autoplay] [data-carousel-track] { scroll-snap-type: none; }

/* A control that cannot act is hidden outright, so it leaves the tab ring and the
   accessibility tree together rather than being focusable and inert. */
[data-carousel-prev][hidden], [data-carousel-next][hidden] { display: none; }

/* Loop clones are decoration standing in for content that is really elsewhere in the
   track. They are built at runtime, so nothing without JavaScript ever sees them; the
   behaviour makes each one inert, aria-hidden and id-free. This is the CSS half of the
   same statement: a clone can never be the thing a keyboard lands on. */
[data-carousel-clone] { user-select: none; -webkit-user-select: none; }


/*
 * INVARIANT: `laid-out element(s) are still fully transparent with reduced motion
 * emulated`. A reveal that starts transparent leaves the page blank under the
 * preference, and a screenshot taken there is not evidence of anything.
 */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: .001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .001ms !important;
  }
  [data-reveal] { opacity: 1 !important; transform: none !important; }
  /* INVARIANT: automatic carousel progression under the preference. The behaviour
     refuses to start under reduce; this closes the CSS half so a track that animates
     itself cannot keep moving either, and states the rule in the SERVED STYLESHEET,
     where the validator can read it rather than having to parse a script. */
  [data-carousel-autoplay] [data-carousel-track] { scroll-behavior: auto !important; }
  [data-carousel-autoplay] { animation: none !important; }
}
