/* ═══════════════════════════════════════════════════════════════════════════
   COMPANION SHEET — only what Mosaic has no mechanism for.

   The rule on this site is Mosaic first: settings, tokens, utility classes and
   element styles, in that order, and a stylesheet only for what none of them
   can express. Everything below is here because it is an AT-RULE or a
   descendant selector, and Mosaic's style model has no place to put either —
   it writes declarations onto one element's own generated class.

   Every hook is a data attribute rather than a class. Mosaic owns the class
   attribute and rewrites it on every save, so a class is not a stable contract;
   an attribute passes through untouched and reads the same in the markup, in
   this file, and in companion.js.
   ═══════════════════════════════════════════════════════════════════════════ */

/* ── Base typography, handed back to the brand ──────────────────────────────
   Mosaic's theme reset (plugins/mosaic/themes/mosaic/public/style.css) sets
   base type ON THE ELEMENT:

       p:not(:where(body .is-layout-constrained p)) { margin:24px 0 0; font-size:16px; line-height:24px }
       h2:not(:where(...)) { margin:48px 0 24px }   … and so on for h1–h6

   The brand puts base type on `body` and lets everything inherit, and a value
   set directly on an element always beats an inherited one — no matter which
   sheet loads first. So every paragraph the design leaves unstyled rendered at
   16/24 instead of the brand's 17/28, and every heading carried margins the
   design never asked for. Measured on the homepage: seven paragraphs wrong.

   This is not a Mosaic setting — the rule is compiled into a plugin asset with
   nothing behind it to turn off — which is why it is here rather than in the
   builder. The selector shape is copied exactly so specificity ties and source
   order decides; this sheet is enqueued after both.

   `inherit`, not a literal: the brand's scale stays the single source of truth,
   and anything Mosaic sets per element still wins over it as normal.
   ────────────────────────────────────────────────────────────────────────── */
p:not(:where(body .is-layout-constrained p)) {
  margin: 0;
  font-size: inherit;
  line-height: inherit;
}

h1:not(:where(body .is-layout-constrained h1)),
h2:not(:where(body .is-layout-constrained h2)),
h3:not(:where(body .is-layout-constrained h3)),
h4:not(:where(body .is-layout-constrained h4)),
h5:not(:where(body .is-layout-constrained h5)),
h6:not(:where(body .is-layout-constrained h6)) { margin: 0; }

/* ── Where an in-page anchor lands ──────────────────────────────────────────
   The masthead is sticky, so a jump to an anchor has to clear it or the bar
   sits on top of what the reader just asked for. The brand sets
   scroll-padding-top: var(--fk-space-8) — 56px — which was written before this
   masthead existed: the bar measures 65px below 640 and 73px above it, so
   every anchor landed 9 to 17px UNDER it. Measured on both the mockup and the
   build, at eight widths, before and after.

   6rem clears the taller bar with 23px of air, so the target reads as
   deliberately placed rather than narrowly missed. It is here rather than in
   Mosaic because the property has to sit on the SCROLLING ELEMENT, and <html>
   is not an element Mosaic owns.
   ────────────────────────────────────────────────────────────────────────── */
html { scroll-padding-top: 6rem; }

/* ── The service register's disclosure ───────────────────────────────────────
   A <details> panel opens by snapping: measured on the homepage, the document
   went 6686 to 6717px inside 60ms with nothing in between. It is the page's
   central interaction — three services a buyer opens to compare — and the one
   place on a site where everything else animates that did not.

   `auto` is not an animatable length, which is why this has historically taken
   JavaScript. `interpolate-size: allow-keywords` makes it one, and
   `::details-content` is the box to put it on. Both are recent, so this is a
   PROGRESSIVE ENHANCEMENT: where they are unsupported the panel snaps exactly
   as it does today, and nothing is broken.

   300ms, at the site's one easing curve. Faster reads as a flicker on a panel
   this tall; slower makes comparing three services feel like waiting.

   `content-visibility` needs `allow-discrete` or the content pops in at the
   start of the open instead of arriving with the box.
   ────────────────────────────────────────────────────────────────────────── */
:root { interpolate-size: allow-keywords; }

details::details-content {
  block-size: 0;
  overflow: clip;
  transition: block-size 300ms cubic-bezier(0.23, 1, 0.32, 1),
              content-visibility 300ms cubic-bezier(0.23, 1, 0.32, 1) allow-discrete;
}
details[open]::details-content { block-size: auto; }

@media (prefers-reduced-motion: reduce) {
  details::details-content { transition: none !important; }
}

/* ── The client marquee ─────────────────────────────────────────────────────
   A keyframe animation is an at-rule: there is no element to declare it on.
   The pause on hover is a DESCENDANT selector — the hover is on the frame and
   the effect is on the track inside it — which an element style cannot reach.
   Linear easing, because constant motion is the one case that wants it. */
@keyframes mg-slide { to { transform: translateX(-50%); } }

[data-marquee-host]:hover [data-marquee] { animation-play-state: paused; }

/* ── Reduced motion ─────────────────────────────────────────────────────────
   Fewer and gentler, not zero: the colour and opacity resolutions that aid
   comprehension stay; everything that MOVES stops, and the marquee holds still
   rather than looping. The universal selector is the point of the block, and a
   universal selector is exactly what an element style cannot be. */
@media (prefers-reduced-motion: reduce) {
  /* !important throughout, and not for convenience: Mosaic prints its element
     styles as inline <style> blocks AFTER every enqueued sheet, so this file
     can never win a tie on source order. A reader who has asked the operating
     system for less motion is not a tie to lose. */
  [data-marquee] { animation: none !important; transform: none !important; }
  [data-js] [data-enter],
  [data-js] [data-rise] { transform: none !important; }
  [data-rotator] [data-active] { transform: none !important; }
  *, *::before, *::after { transition-duration: 0.01ms !important; }
  .fk-consent__btn:active { transform: none !important; }
  [data-ready="true"] [data-enter] { transition: opacity 200ms linear !important; }
}

/* ── The focus ring, taken back ─────────────────────────────────────────────
   The brand states one focus treatment: `:focus-visible { outline: 2px solid
   var(--fk-focus-ring); outline-offset: 2px }`, an ink ring, in core.css.

   Mosaic's theme reset does `:focus-visible { outline: none }` and then puts a
   1px ring back for buttons and form controls only:

     button:focus-visible, [type=button]:focus-visible, ...
       { outline: 1px solid var(--mosaic-frontend-neutral-900); outline-offset: 2px }

   Both of core.css's rules and Mosaic's blanket reset are one pseudo-class
   each, and core.css is enqueued after Mosaic, so LINKS kept the 2px ink ring.
   The button rule adds an element to the selector, which beats a bare
   pseudo-class on specificity no matter what order the sheets load in. So every
   <button> on the site -- the theme toggle included -- has been focusing with a
   1px ring in #0D0D0D instead of a 2px ring in #0A0A0A since the port.

   Visible either way, which is why nothing failed. But two focus treatments is
   two, and the brand only describes one. companion.css is the last sheet in, so
   matching Mosaic's specificity here is enough to win. */
:where(button, [type="button"], [type="reset"], [type="submit"]):focus-visible,
button:focus-visible,
[type="button"]:focus-visible,
[type="reset"]:focus-visible,
[type="submit"]:focus-visible {
  outline: 2px solid var(--fk-focus-ring);
  outline-offset: 2px;
}

/* ── Analytics consent ──────────────────────────────────────────────────────
   Every colour is a brand token, so the banner inverts with [data-theme="dark"]
   for free rather than needing a second set of rules.

   It sits at the bottom, where the persistent enquiry bar also lives. Rather
   than stack them, the script adds `fk-no-cta` to <body> while this is open --
   the state that already hides that bar on /contact/ and /thank-you/ -- so
   there is only ever one thing anchored to the bottom edge. */
.fk-consent {
  position: fixed;
  inset-inline: 0;
  bottom: 0;
  z-index: 60;                       /* over the enquiry bar (45) and masthead (40) */
  background: var(--fk-surface);
  border-block-start: 1px solid var(--fk-border-strong);
  padding: var(--fk-space-4) var(--mg-gutter);
}

.fk-consent__inner {
  max-inline-size: var(--mg-shell);
  margin-inline: auto;
  display: grid;
  gap: var(--fk-space-4);
  align-items: center;
}

@media (min-width: 44rem) {
  .fk-consent__inner {
    grid-template-columns: minmax(0, 1fr) auto;
    gap: var(--fk-space-6);
  }
}

.fk-consent__text {
  margin: 0;
  font-family: var(--fk-font-text);
  font-size: var(--fk-body-s-size);
  line-height: 1.5;
  color: var(--fk-text-secondary);
  text-wrap: pretty;
}

.fk-consent__text a { color: var(--fk-text-primary); }

.fk-consent__actions {
  display: flex;
  gap: var(--fk-space-3);
  flex-wrap: wrap;
}

/* 44px min, matching every other control on the site rather than the 24px
   floor. A consent choice is the worst place to make someone aim. */
.fk-consent__btn {
  min-block-size: 44px;
  padding-inline: 1.6rem;
  border: 1px solid var(--fk-border-strong);
  /* 999px, matching .mg-btn. Every other control on this site is a pill; a
     rounded rectangle here reads as a browser dialog rather than as part of
     the page. */
  border-radius: 999px;
  background: transparent;
  color: var(--fk-text-primary);
  font-family: var(--fk-font-text);
  font-size: var(--fk-body-s-size);
  cursor: pointer;
  transition: transform var(--mg-fast) var(--mg-ease);
}

.fk-consent__btn--yes {
  background: var(--fk-text-primary);
  color: var(--fk-surface);
  border-color: var(--fk-text-primary);
}

.fk-consent__btn:active { transform: scale(0.97); }

/* ── Print ──────────────────────────────────────────────────────────────────
   The manifesto's ink ground is a screen device. On paper it becomes a
   document like every other section, and nothing may still be waiting on an
   observer that will never run. */
@media print {
  [data-marquee-host] { display: none; }
  [data-ground] {
    background: none !important;
    color: #000 !important;
    padding-block: 2rem !important;
  }
  [data-enter], [data-rise], [data-reveal] {
    opacity: 1 !important;
    transform: none !important;
    color: inherit !important;
  }
  header[role="banner"] { position: static !important; }
  .fk-consent { display: none !important; }
}
