/* ==========================================================================
   Riverview Vets - base layer

   Filled at BLD-03 on 2026-09-17.

   Two sources, in this order:

     1. _ds/tokens/base.css from the design export, which is the design
        system's own element-level reset.
     2. The page-level <style> block the export repeated in the <helmet> of
        all three .dc.html files. Where the two disagree, the page block wins,
        because it is what the design actually rendered with. The only real
        disagreement is the page background: the design system says
        --surface-page (cream-100), every page said cream-50.

   Everything here selects elements, never classes. Layout is layout.css,
   components are components.css, sections are page.css.

   Cascade order, set in the <link> tags and deliberate:
     tokens -> fonts -> base -> layout -> components -> page
   tokens first because every later file reads its custom properties;
   components before page so a page rule can override a component one.
   ========================================================================== */


/* --- From _ds/tokens/base.css ---------------------------------------------
   Transcribed, not reformatted. The one addition is `text-wrap: pretty` on
   body, which the export had and which the design system did not.
   -------------------------------------------------------------------------- */

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

body {
  margin: 0;
  /* cream-50, from the page block. The design system says --surface-page,
     which is cream-100; every page in the export overrode it. */
  background: var(--cream-50);
  color: var(--text-body);
  font: var(--type-body);
  -webkit-font-smoothing: antialiased;
  text-wrap: pretty;
}

h1, h2, h3, h4 { color: var(--text-heading); margin: 0; }
h1 { font: var(--type-h1); letter-spacing: var(--ls-display); }
h2 { font: var(--type-h2); letter-spacing: var(--ls-heading); }
h3 { font: var(--type-h3); }

p { margin: 0; }

a {
  color: var(--text-link);
  text-decoration-thickness: 1px;
  text-underline-offset: 3px;
  transition: color var(--dur-fast) var(--ease-standard);
}
a:hover { color: var(--text-link-hover); }

::selection { background: var(--peach); color: var(--forest-900); }

:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 2px; }

.eyebrow {
  font: var(--type-eyebrow);
  letter-spacing: var(--ls-eyebrow);
  text-transform: uppercase;
  color: var(--text-muted);
}


/* --- From the export's page <style> block ---------------------------------
   The parts of it that are element-level. Its four @keyframes are below, and
   its blanket reduced-motion rule is deliberately NOT here; see the bottom of
   this file.
   -------------------------------------------------------------------------- */

input, select, textarea, button { font-family: inherit; }
input::placeholder { color: var(--ink-300); }


/* --- Additions BLD-02's markup needs and the export had no equivalent for --

   The export is a React tree of divs. BLD-02 rewrote it as a document, which
   introduced elements the export never rendered: <figure>, <blockquote>,
   <address>, <dl>, <ul> as real lists, and <picture> around every image.
   These four rules are what stop the browser's defaults showing through.
   -------------------------------------------------------------------------- */

figure, blockquote, dl, dd { margin: 0; }

address { font-style: normal; }

ul, ol { margin: 0; }

/* <picture> is a wrapper with no box of its own, and display:contents is what
   makes that true in CSS as well as in the spec. Without it every image rule
   on the site would have to style the picture and the img, because the img's
   flex or grid parent would be the picture, not the container it is written
   into. picture has no ARIA role, so removing its box removes nothing. */
picture { display: contents; }

/* The hidden attribute has to beat a display value set by a class, or an
   element that ships hidden and is revealed by JavaScript cannot be styled at
   all. Three elements depend on this: #registered-modal, the cookie banner,
   and anything CNS-02 adds later. */
[hidden] { display: none !important; }

/* Google's <noscript> GTM iframe, hidden by us rather than by itself.
   Added at CNS-04, and it exists because that row MEASURED something it had
   been asserting: the snippet carries style="display:none;visibility:hidden"
   as well as height="0" width="0", and CNS-04's style-src 'self' blocks the
   style attribute deliberately. The note on that row said the iframe "renders
   as a 0x0 box either way", which is wrong. Chrome's UA stylesheet gives every
   iframe `border: 2px inset`, so with the style attribute blocked the box is
   4x4 with a visible border, at the top-left of every page. Measured with
   DOM.getBoxModel, JavaScript off: 4 x 4, border 2px on all four sides.

   Only a no-JS visitor ever sees it. With JavaScript on, <noscript> content is
   parsed as raw text and no iframe element exists, so this selector matches
   nothing and costs nothing.

   This is the cheap fix of the three. The alternatives were to accept a
   visible artefact on the one visitor the whole architecture is built to serve
   well, or to buy 'unsafe-hashes' in style-src to allow Google's attribute
   back - which re-opens style attributes site-wide for anything matching that
   hash, to hide a box we can hide in one line. The attribute stays blocked and
   still reports; what goes away is the only cost that was ever visible. */
noscript iframe { display: none; }


/* --- Anchor scrolling, which is CSS and not JavaScript --------------------

   The export implements this in about 50 lines: a cubic-bezier sampled by
   Newton iteration, a duration scaled by distance, a matchMedia check and a
   history.replaceState at the end. These three rules are the whole of it, and
   unlike the export's version they keep working with JavaScript off.

   scroll-margin-top is what replaces the manual navH offset. It is on [id]
   rather than on :target so it applies to a link clicked from the same page,
   not only to a page loaded at a fragment. See PLAN-v3, "Smooth scrolling is
   50 lines of JavaScript and should be three lines of CSS".
   -------------------------------------------------------------------------- */

html { scroll-behavior: smooth; }

[id] { scroll-margin-top: calc(var(--nav-h) + 12px); }


/* --- Keyframes ------------------------------------------------------------
   All four transcribed from the export's page <style>. rvQuote drives the
   four-quote strip under the hero; the two wobbles drive the chalk paw and
   heart in the founders section. Nothing in JavaScript touches any of them.
   -------------------------------------------------------------------------- */

@keyframes rvQuote {
  0%   { opacity: 0; }
  3%   { opacity: 1; }
  22%  { opacity: 1; }
  25%  { opacity: 0; }
  100% { opacity: 0; }
}

@keyframes rvWobblePaw {
  0%, 24%   { transform: rotate(-4deg) translateY(0); }
  25%, 49%  { transform: rotate(1deg)  translateY(-1px); }
  50%, 74%  { transform: rotate(5deg)  translateY(0); }
  75%, 100% { transform: rotate(0deg)  translateY(1px); }
}

@keyframes rvWobbleHeart {
  0%, 24%   { transform: rotate(3deg)  translateY(0); }
  25%, 49%  { transform: rotate(-2deg) translateY(1px); }
  50%, 74%  { transform: rotate(-6deg) translateY(0); }
  75%, 100% { transform: rotate(-1deg) translateY(-1px); }
}


/* --- Reduced motion -------------------------------------------------------

   THE EXPORT'S RULE IS NOT COPIED, AND THAT IS THE POINT OF THIS BLOCK.

   The export shipped this on all three pages:

     @media (prefers-reduced-motion: reduce) {
       * { animation-duration:0.001ms !important;
           transition-duration:0.001ms !important; }
     }

   Measured in headless Chrome 153, not reasoned about: it renders the quote
   strip completely empty. An `infinite` animation crushed to 0.001ms does not
   settle on its `from` state, it parks on its 100% keyframe, and rvQuote's
   100% is opacity:0. All four quotes go invisible and the visitor gets a
   168px band of grape-100 with no text in it. The rule is harmless for the
   two wobbles, whose 100% is a resting transform, and it silently deletes the
   testimonials.

   So motion is switched off by name instead. tokens/motion.css already zeroes
   every --dur-* and --press-scale under the same media query, which covers
   every transition on the site; what is left is the four things with a
   literal duration written on them.
   -------------------------------------------------------------------------- */

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
}


/* --- Utilities ------------------------------------------------------------ */

/* The honeypot. Visually hidden, NEVER display:none and never
   visibility:hidden: a bot that reads styles skips a field that is hidden by
   either, and a bot that does not read them fills this one in, which is the
   whole mechanism. Screen readers do not reach it either, because the
   wrapper carries aria-hidden and the input carries tabindex="-1". */
.honeypot {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}


/* Visually hidden, for text that only a screen reader needs: the two deck
   live regions and the consent status line. BLD-09.

   Deliberately NOT shared with `.honeypot` above, which uses the same six
   declarations for an entirely different reason. That rule's comment explains
   why it must never become `display:none`, and merging the two would put an
   anti-bot constraint on a class that three live regions use - the next person
   to "tidy" this into `display:none` would break the honeypot and never know.
   Two rules, two reasons, no shared fate.

   Never `display:none` here either, and for a reason of its own: a live region
   that is not rendered is not monitored by every screen reader, so a message
   written into a hidden region can be announced by nothing at all. These stay
   in the box tree at 1px and are read. */
.vh {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}
