/* Workshop Ledger — shared visual-elevation CSS, linked from every page
   (all 5 HTML entries plus 404.html). Hand-authored, not generated (compare
   tokens.css, which is generated from src/tokens.js).

   Every effect in this file is CSS-first, degrades gracefully in
   unsupported browsers, and is wrapped in @media (prefers-reduced-motion:
   no-preference) — see each section below. */

/* ── 1. Cross-document View Transitions ──────────────────────────────
   Progressive enhancement: browsers without support just do a normal
   navigation, nothing breaks. Named transitions on the nav monogram,
   brand wordmark, and each page's top heading (opt-in via
   view-transition-name, set inline per element) let that chrome morph
   between pages instead of hard-cutting. */
@media (prefers-reduced-motion: no-preference) {
  @view-transition {
    navigation: auto;
  }

  ::view-transition-group(*) {
    animation-duration: 320ms;
    animation-timing-function: cubic-bezier(0.22, 1, 0.36, 1);
  }

  ::view-transition-old(root),
  ::view-transition-new(root) {
    animation-duration: 240ms;
    animation-timing-function: cubic-bezier(0.22, 1, 0.36, 1);
  }

  ::view-transition-group(ap-monogram),
  ::view-transition-group(ap-brand-name),
  ::view-transition-group(ap-page-hero) {
    animation-duration: 340ms;
    animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1);
  }
}

/* ── 2. Variable-font accent words ───────────────────────────────────
   Fraunces is already loaded as a true variable font (weight 300..900,
   optical size 9..144) for the italic red accent words used throughout
   headings — this animates the axes that are already on the page, no new
   bytes. Apply className="ap-accent" to an italic accent span/em. */
@media (prefers-reduced-motion: no-preference) {
  .ap-accent {
    font-variation-settings: "opsz" 60, "wght" 400;
    animation: ap-accent-breathe 1100ms cubic-bezier(0.16, 1, 0.3, 1) both;
    transition: font-variation-settings 260ms ease;
    display: inline-block;
  }
  .ap-accent:hover {
    font-variation-settings: "opsz" 100, "wght" 480;
  }
  @keyframes ap-accent-breathe {
    from { font-variation-settings: "opsz" 9, "wght" 300; }
    to   { font-variation-settings: "opsz" 60, "wght" 400; }
  }
}

/* ── 3. Scroll-driven reveals ─────────────────────────────────────────
   Apply className="ap-reveal" (React) or class="ap-reveal" (static HTML)
   to SectionHeader, pricing cards, the Westgate case study, and the
   why-a-website.html stat blocks. Staggered children can add
   `ap-reveal-1`.."ap-reveal-4" for a small delay offset.

   Native path: `animation-timeline: view()`, supported Chrome/Edge 115+.
   Fallback path: IntersectionObserver adds `.is-visible` (see the
   useScrollReveal hook in ap-chrome.jsx for React pages, and the inline
   bootstrap script at the bottom of why-a-website.html) — gated by
   `.js-scroll-reveal` on <html>, which JS only adds when the native
   timeline API is unsupported. Unknown/no-JS browsers default to plain
   visible content, never permanently hidden. */
@media (prefers-reduced-motion: no-preference) {
  @keyframes ap-reveal-in {
    from { opacity: 0; transform: translateY(28px); }
    to   { opacity: 1; transform: translateY(0); }
  }

  @supports (animation-timeline: view()) {
    .ap-reveal {
      animation: ap-reveal-in linear both;
      animation-timeline: view();
      animation-range: entry 0% cover 30%;
    }
  }

  .js-scroll-reveal .ap-reveal {
    opacity: 0;
    transform: translateY(28px);
    transition: opacity 560ms cubic-bezier(0.16, 1, 0.3, 1), transform 560ms cubic-bezier(0.16, 1, 0.3, 1);
  }
  .js-scroll-reveal .ap-reveal.is-visible {
    opacity: 1;
    transform: none;
  }
  .js-scroll-reveal .ap-reveal-1.is-visible { transition-delay: 80ms; }
  .js-scroll-reveal .ap-reveal-2.is-visible { transition-delay: 160ms; }
  .js-scroll-reveal .ap-reveal-3.is-visible { transition-delay: 240ms; }
  .js-scroll-reveal .ap-reveal-4.is-visible { transition-delay: 320ms; }
}

/* ── 4. @property-typed brass→red sheen ──────────────────────────────
   Untyped custom properties can't be smoothly interpolated by the browser
   (they're treated as opaque strings) — @property gives --ap-sheen a real
   <percentage> type so a gradient sweep can actually animate. Degrades to a
   static gradient position in browsers without @property support (all
   current evergreen browsers have it; the one gap is older Firefox, where
   this just doesn't move — never broken, never invisible).

   .ap-sheen-line  — continuous slow drift, for thin rule/underline elements.
   .ap-sheen       — hover-triggered sweep overlay, for cards and CTA
                     buttons. Needs the element to size itself (it uses an
                     ::after overlay), works whether or not the element
                     already sets position:relative inline. */
@property --ap-sheen {
  syntax: "<percentage>";
  inherits: false;
  initial-value: 0%;
}

@media (prefers-reduced-motion: no-preference) {
  .ap-sheen-line {
    background: linear-gradient(90deg, var(--brass), var(--red), var(--brass));
    background-size: 300% 100%;
    background-position: var(--ap-sheen) 0;
    animation: ap-sheen-drift 6s ease-in-out infinite;
  }
  @keyframes ap-sheen-drift {
    0%, 100% { --ap-sheen: 0%; }
    50%      { --ap-sheen: 100%; }
  }

  .ap-sheen {
    position: relative;
    overflow: hidden;
  }
  .ap-sheen::after {
    content: "";
    position: absolute;
    inset: 0;
    background: linear-gradient(100deg, transparent 30%, var(--brass) 48%, var(--red) 55%, transparent 72%);
    background-size: 250% 100%;
    background-position: var(--ap-sheen) 0;
    opacity: 0;
    transition: opacity 220ms ease;
    pointer-events: none;
  }
  .ap-sheen:hover::after,
  .ap-sheen:focus-visible::after {
    opacity: 0.35;
    animation: ap-sheen-sweep 1200ms ease-in-out;
  }
  @keyframes ap-sheen-sweep {
    from { --ap-sheen: -40%; }
    to   { --ap-sheen: 140%; }
  }
}

/* ── 5. Micro-interactions ────────────────────────────────────────────
   Craft over spectacle: small, consistent, everywhere. Unlike the ambient
   effects above (sheen drift, accent breathe, scroll reveals — fully
   disabled under reduced motion since they're pure flourish), these are
   direct responses to a user action (hover/focus/press). The end state
   always applies with or without motion preference; only the *smoothing*
   between states is gated, so nothing ever feels broken or unresponsive
   for reduced-motion users, it just changes state instantly instead of
   easing into it. */

/* Nav links: underline draws in from the left on hover/focus. */
.ap-underline {
  position: relative;
  text-decoration: none;
}
.ap-underline::after {
  content: "";
  position: absolute;
  left: 0; right: 100%; bottom: -3px;
  height: 1px;
  background: currentColor;
}
.ap-underline:hover::after,
.ap-underline:focus-visible::after {
  right: 0;
}
@media (prefers-reduced-motion: no-preference) {
  .ap-underline::after {
    transition: right 240ms cubic-bezier(0.16, 1, 0.3, 1);
  }
}

/* Buttons (and anything else): a light spring press-down on :active. */
.ap-press {
  transform: scale(1);
}
.ap-press:active {
  transform: scale(0.96);
}
@media (prefers-reduced-motion: no-preference) {
  .ap-press {
    transition: transform 180ms cubic-bezier(0.34, 1.56, 0.64, 1);
  }
}

/* ── 6. Texture ────────────────────────────────────────────────────────
   The site is flat. Everything here is layered CSS only — no images, no
   canvas — consistent with the Workshop Ledger identity (the ruled-paper
   pattern already existed inline in App.jsx's Hero; this makes it a
   reusable class instead of a one-off, and adds a grid variant, a grain
   overlay, and a shared "elevated card" treatment). None of this is
   ambient/auto-playing, so none of it needs a reduced-motion guard except
   the hover-lift transform on .ap-elevated. */

/* Faint horizontal rules, like ruled notepaper. */
.ap-ruled {
  position: relative;
}
.ap-ruled::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  background-image: repeating-linear-gradient(to bottom,
    transparent 0, transparent 47px,
    rgba(232, 228, 219, 0.035) 47px, rgba(232, 228, 219, 0.035) 48px);
}

/* Faint crossed rules, like graph paper. */
.ap-grid-paper {
  position: relative;
}
.ap-grid-paper::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  background-image:
    repeating-linear-gradient(to right, rgba(232, 228, 219, 0.035) 0, rgba(232, 228, 219, 0.035) 1px, transparent 1px, transparent 48px),
    repeating-linear-gradient(to bottom, rgba(232, 228, 219, 0.035) 0, rgba(232, 228, 219, 0.035) 1px, transparent 1px, transparent 48px);
}

/* Subtle film-grain, via an inline SVG feTurbulence filter — no raster asset. */
.ap-noise {
  position: relative;
}
.ap-noise::before {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  opacity: 0.035;
  mix-blend-mode: overlay;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
}

/* Shared elevated-card treatment: border (bring your own) + shadow + a
   touch of hover lift. Apply alongside .ap-sheen / .ap-reveal freely. */
.ap-elevated {
  box-shadow: 0 1px 0 0 rgba(255, 255, 255, 0.04) inset, 0 24px 48px -28px rgba(0, 0, 0, 0.55);
}
.ap-elevated:hover {
  box-shadow: 0 1px 0 0 rgba(255, 255, 255, 0.06) inset, 0 32px 64px -28px rgba(0, 0, 0, 0.6);
}
@media (prefers-reduced-motion: no-preference) {
  .ap-elevated {
    transition: box-shadow 220ms ease, transform 220ms ease;
  }
  .ap-elevated:hover {
    transform: translateY(-2px);
  }
}

/* ── 7. Skip-to-content link ──────────────────────────────────────────
   First focusable element on every page. Visually hidden until it
   receives keyboard focus, then pinned top-left over everything.
   Pair with id="main" on that page's <main> (or main content wrapper). */
.ap-skip-link {
  position: absolute;
  top: -100px;
  left: 12px;
  z-index: 1000;
  background: var(--red);
  color: var(--off);
  font-family: var(--mono);
  font-size: 12px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  text-decoration: none;
  padding: 12px 18px;
  transition: top 160ms ease;
}
.ap-skip-link:focus {
  top: 12px;
}

