/* ==========================================================================
   Beacon Surveyors LLP — shared stylesheet
   Loaded on every page. Everything that used to live in five divergent
   inline <style> blocks now lives here, once.

   0. Brand palette
   1. Icon font
   2. Typography standard (H1 / H2 / H3 / eyebrow)
   3. Surfaces & backgrounds
   4. Hover effects
   5. Cards
   6. Motion: tickers, scroll reveal, text animation
   7. Reduced-motion fallback
   8. Brand lockup & beacon animation
   9. The beacon motif (beam / lamp / seal)
   ========================================================================== */

/* --- 0. Brand palette ----------------------------------------------------
   The single source of truth for the brand orange on the CSS side. The
   Tailwind token `on-tertiary-container` in assets/site-config.js carries the
   same value for the utility classes in the markup — change both together and
   every orange on the site moves at once.

   --brand-orange-rgb is the same colour as space-separated channels, so it can
   be used at partial alpha via rgb(var(--brand-orange-rgb) / .3) without
   hardcoding the hex a second time.                                         */
:root {
  --brand-orange: #df4d18;
  --brand-orange-rgb: 223 77 24;
  --brand-orange-soft: #ffb499; /* legible on black */
  --brand-orange-deep: #802200; /* legible on white at small sizes */
}

/* Brand orange carries through the two browser-drawn states as well, so
   selection and keyboard focus read as part of the identity. */
::selection {
  background: var(--brand-orange);
  color: #ffffff;
}

:focus-visible {
  outline: 2px solid var(--brand-orange);
  outline-offset: 2px;
}

/* --- 1. Icon font -------------------------------------------------------- */
.material-symbols-outlined {
  font-variation-settings:
    "FILL" 0,
    "wght" 400,
    "GRAD" 0,
    "opsz" 24;
}

/* --- 2. Typography standard ---------------------------------------------
   One treatment per heading level, used on all five pages. Sizes are fluid,
   so no md:/lg: size overrides are needed in the markup.                   */
.display-title {
  font-family: "Source Serif 4", Georgia, serif;
  font-size: clamp(2rem, 1.35rem + 2.9vw, 3rem); /* 32px → 48px */
  line-height: 1.14;
  letter-spacing: -0.02em;
  font-weight: 700;
}

.section-title {
  font-family: "Source Serif 4", Georgia, serif;
  font-size: clamp(1.75rem, 1.3rem + 2vw, 2.5rem); /* 28px → 40px */
  line-height: 1.2;
  letter-spacing: -0.015em;
  font-weight: 600;
}

.sub-title {
  font-family: "Source Serif 4", Georgia, serif;
  font-size: 1.5rem; /* 24px */
  line-height: 2rem;
  letter-spacing: -0.01em;
  font-weight: 600;
}

/* Small all-caps kicker that sits above a heading. Semantically a <p>, never
   an <h2> — headings are reserved for the outline. */
.eyebrow {
  font-family: Inter, system-ui, sans-serif;
  font-size: 0.75rem;
  line-height: 1rem;
  letter-spacing: 0.05em;
  font-weight: 700;
  text-transform: uppercase;
}

/* Primary navigation. This used to borrow the 12px label-caps token, which is
   the eyebrow size — too small and too heavy for the main menu, and misleading
   besides, since the nav is sentence case and never rendered as caps. Nav sits
   one step under body copy: large enough to read as navigation, quiet enough
   not to compete with the wordmark beside it. */
.nav-link {
  font-family: Inter, system-ui, sans-serif;
  font-size: 0.9375rem; /* 15px */
  line-height: 1.25rem;
  letter-spacing: 0.005em;
  font-weight: 500;
}
.nav-link-active {
  font-weight: 600;
}

/* Label inside a button or pill. Between nav and eyebrow in size: it needs to
   read as an action, not as a caption. */
.btn-label {
  font-family: Inter, system-ui, sans-serif;
  font-size: 0.875rem; /* 14px */
  line-height: 1.25rem;
  letter-spacing: 0.01em;
  font-weight: 600;
}

/* --- 3. Surfaces & backgrounds ------------------------------------------ */
.glass-effect {
  background: rgba(255, 255, 255, 0.94);
  backdrop-filter: blur(12px);
}

.grid-pattern {
  background-image:
    linear-gradient(to right, #e2e8f0 1px, transparent 1px),
    linear-gradient(to bottom, #e2e8f0 1px, transparent 1px);
  background-size: 60px 60px;
}

.grid-mesh {
  background-image:
    linear-gradient(#e9ecef 1px, transparent 1px),
    linear-gradient(90deg, #e9ecef 1px, transparent 1px);
  background-size: 40px 40px;
}

.map-callout::before {
  content: "";
  position: absolute;
  width: 8px;
  height: 8px;
  background: var(--brand-orange);
  border-radius: 50%;
  box-shadow: 0 0 0 4px rgb(var(--brand-orange-rgb) / 0.3);
}

/* Initials placeholder shown where a portrait is unavailable. Readable on
   black, and clearly intentional rather than a broken image. */
.initials-plate {
  background:
    radial-gradient(
      circle at 30% 20%,
      rgb(var(--brand-orange-rgb) / 0.22),
      transparent 60%
    ),
    #000000;
  color: var(--brand-orange-soft);
  font-family: "Source Serif 4", Georgia, serif;
  font-weight: 700;
  letter-spacing: 0.06em;
}

/* --- 4. Hover effects ---------------------------------------------------- */
.hover-lift {
  transition: transform 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);
}
.hover-lift:hover {
  transform: translateY(-4px);
}

/* Animated underline that grows from the left. Use on inline text links. */
.hover-underline {
  position: relative;
}
.hover-underline::after {
  content: "";
  position: absolute;
  left: 0;
  bottom: -2px;
  width: 100%;
  height: 1px;
  background: currentColor;
  transform: scaleX(0);
  transform-origin: left;
  transition: transform 0.35s cubic-bezier(0.4, 0, 0.2, 1);
}
.hover-underline:hover::after {
  transform: scaleX(1);
}

/* Arrow / chevron that slides forward with its link. */
.hover-arrow .arrow-icon {
  transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}
.hover-arrow:hover .arrow-icon {
  transform: translateX(4px);
}

/* Slow image zoom inside an overflow-hidden frame. */
.img-zoom {
  transition: transform 1s cubic-bezier(0.4, 0, 0.2, 1);
}
.img-frame:hover .img-zoom,
.svc-block:hover .svc-img {
  transform: scale(1.04);
}
.svc-img {
  transition: transform 1s cubic-bezier(0.4, 0, 0.2, 1);
}

/* --- 5. Cards ------------------------------------------------------------ */
.card {
  transition:
    transform 0.35s cubic-bezier(0.34, 1.56, 0.64, 1),
    box-shadow 0.35s ease,
    border-color 0.35s ease,
    background-color 0.35s ease;
}
.card:hover {
  transform: translateY(-6px);
  box-shadow: 0 18px 40px -18px rgba(21, 28, 34, 0.35);
  border-color: var(--brand-orange);
}

/* Orange rule that wipes in across the top edge of a card on hover. */
.card-accent {
  position: relative;
}
.card-accent::before {
  content: "";
  position: absolute;
  inset: 0 0 auto 0;
  height: 2px;
  background: var(--brand-orange);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform 0.4s cubic-bezier(0.4, 0, 0.2, 1);
}
.card-accent:hover::before {
  transform: scaleX(1);
}

/* --- 6. Motion ----------------------------------------------------------- */
.service-ticker-animation {
  animation: ticker 40s linear infinite;
}
.accred-ticker {
  animation: accredRun 36s linear infinite;
}
@keyframes ticker {
  0% {
    transform: translateX(0);
  }
  100% {
    transform: translateX(-50%);
  }
}
@keyframes accredRun {
  0% {
    transform: translateX(0);
  }
  100% {
    transform: translateX(-50%);
  }
}

/* Scroll reveal. Elements start hidden and are un-hidden by the
   IntersectionObserver in site.js. The .js guard comes from site-config.js,
   so without scripting nothing is ever hidden in the first place. */
.js .reveal {
  opacity: 0;
  transform: translateY(18px);
  transition:
    opacity 0.7s cubic-bezier(0.22, 1, 0.36, 1),
    transform 0.7s cubic-bezier(0.22, 1, 0.36, 1);
  will-change: opacity, transform;
}
.js .reveal.is-visible {
  opacity: 1;
  transform: none;
}
.reveal-d1 {
  transition-delay: 0.08s;
}
.reveal-d2 {
  transition-delay: 0.16s;
}
.reveal-d3 {
  transition-delay: 0.24s;
}
.reveal-d4 {
  transition-delay: 0.32s;
}

/* Heading entrance used on hero copy — runs on load, no observer needed. */
.text-rise {
  animation: textRise 0.9s cubic-bezier(0.22, 1, 0.36, 1) both;
}
.text-rise-d1 {
  animation-delay: 0.12s;
}
.text-rise-d2 {
  animation-delay: 0.24s;
}
.text-rise-d3 {
  animation-delay: 0.36s;
}
@keyframes textRise {
  from {
    opacity: 0;
    transform: translateY(22px);
  }
  to {
    opacity: 1;
    transform: none;
  }
}

/* Underline that draws itself under a section heading once revealed. */
.js .rule-draw {
  transform-origin: left;
  transform: scaleX(0);
  transition: transform 0.8s cubic-bezier(0.22, 1, 0.36, 1) 0.15s;
}
.js .reveal.is-visible .rule-draw,
.js .rule-draw.is-visible {
  transform: scaleX(1);
}

/* --- 7. Reduced motion ---------------------------------------------------
   Honour the OS setting: nothing moves, but nothing disappears either.     */
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
    scroll-behavior: auto !important;
  }
  .reveal,
  .js .reveal,
  .text-rise {
    opacity: 1 !important;
    transform: none !important;
  }
  .rule-draw,
  .js .rule-draw {
    transform: scaleX(1) !important;
  }
  .service-ticker-animation,
  .accred-ticker {
    animation: none !important;
  }
}

/* --- 8. Brand lockup & beacon animation ----------------------------------
   The header and footer lockups share one component so the wordmark is set
   identically in both places (it used to be serif at the top and sans at the
   bottom). The <img> is optional: if images/brand/beacon-mark.png is absent
   its onerror handler removes the node and the wordmark stands alone, exactly
   as it did before. Drop the real artwork in and the mark appears.          */
.brand-lockup {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 10px;
  line-height: 1;
  /* overflow:hidden is what keeps the hover sweep inside the lockup. The
     padding gives the beacon glow around the dot room to bloom before that
     clip reaches it; the matching negative margin cancels the padding, so the
     lockup still occupies exactly the space it did without it. */
  overflow: hidden;
  padding: 10px 14px 10px 0;
  margin: -10px -14px -10px 0;
}

.brand-mark {
  /* The mark carries fine detail — the rope ring, the lighthouse and the
     script B — which stops resolving much below this size. */
  height: 42px;
  width: auto;
  display: block;
  flex: none;
  transition: transform 0.5s cubic-bezier(0.34, 1.56, 0.64, 1);
}
.brand-lockup:hover .brand-mark {
  transform: rotate(-6deg) scale(1.06);
}

/* One wordmark treatment, used in the header (on white) and the footer (on
   black). Colour comes from the surrounding context, not from here. */
.brand-word {
  font-family: "Source Serif 4", Georgia, serif;
  font-size: 1.5rem;
  line-height: 2rem;
  letter-spacing: -0.025em;
  font-weight: 700;
  white-space: nowrap;
}

/* The full stop after the wordmark is the beacon's lamp. It holds its solid
   orange at all times; the halo behind it is what sweeps, on a slow 5s cycle
   that is dark for most of its length — a light turning, not a blinking dot. */
.brand-dot {
  position: relative;
  display: inline-block;
  color: var(--brand-orange);
}
.js .brand-dot::after {
  content: "";
  position: absolute;
  left: 50%;
  top: 55%;
  width: 26px;
  height: 26px;
  margin: -13px 0 0 -13px;
  border-radius: 50%;
  pointer-events: none;
  background: radial-gradient(
    circle,
    rgb(var(--brand-orange-rgb) / 0.6) 0%,
    rgb(var(--brand-orange-rgb) / 0) 70%
  );
  opacity: 0;
  animation: beaconPulse 5s ease-in-out infinite;
}
@keyframes beaconPulse {
  0%,
  62%,
  100% {
    opacity: 0;
    transform: scale(0.55);
  }
  10% {
    opacity: 1;
    transform: scale(1);
  }
  38% {
    opacity: 0;
    transform: scale(1.55);
  }
}

/* Light sweeping across the lockup on hover — the beam passing the observer. */
.js .brand-lockup::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  transform: translateX(-130%);
  background: linear-gradient(
    105deg,
    transparent 42%,
    rgb(var(--brand-orange-rgb) / 0.3) 50%,
    transparent 58%
  );
}
.js .brand-lockup:hover::after {
  animation: beaconSweep 1.1s cubic-bezier(0.22, 1, 0.36, 1);
}
@keyframes beaconSweep {
  from {
    transform: translateX(-130%);
  }
  to {
    transform: translateX(130%);
  }
}

/* --- The single-image lockup ----------------------------------------------
   The header uses the supplied horizontal signature as one picture — mark and
   wordmark together — so there is no typeset text beside it to keep in sync.
   Everything below is driven off --logo-h: change that one number and the
   width, the hover sweep and the lamp glow all follow, because the artwork's
   aspect ratio (661 × 224 = 2.9509) is baked into the calculations.

   46px is chosen against the 80px header row: it leaves 17px of air above and
   below, which is enough for the lockup to sit as a mark rather than as a
   banner, and the whole header still clears main's pt-20 offset.             */
.brand-lockup--logo {
  --logo-h: 46px;
  /* No right-hand padding here, unlike the mark-plus-text lockup: the glow is
     well inside the artwork's own bounds, so only vertical room is needed. */
  padding: 8px 0;
  margin: -8px 0;
  gap: 0;
}

.brand-logo {
  height: var(--logo-h);
  width: auto;
  display: block;
  flex: none;
  transition: transform 0.5s cubic-bezier(0.34, 1.56, 0.64, 1);
}
/* A whole-lockup lift, not the mark's tilt — rotating typeset words reads as a
   mistake rather than as motion. */
.brand-lockup--logo:hover .brand-logo {
  transform: scale(1.03);
}

/* The lamp glow, pinned to the lighthouse inside the artwork. The lighthouse
   lamp measures at 17.1% across and 29% down the lockup; those two fractions
   are the only magic numbers here, and they were read off the PNG itself.
   Centring is done with negative margins rather than a translate so the pulse
   keyframes can own `transform` outright. */
.js .brand-lockup--logo::before {
  content: "";
  position: absolute;
  left: calc(var(--logo-h) * 2.9509 * 0.171);
  top: calc(8px + var(--logo-h) * 0.29);
  width: calc(var(--logo-h) * 0.55);
  height: calc(var(--logo-h) * 0.55);
  margin: calc(var(--logo-h) * -0.275) 0 0 calc(var(--logo-h) * -0.275);
  border-radius: 50%;
  pointer-events: none;
  background: radial-gradient(
    circle,
    rgb(var(--brand-orange-rgb) / 0.65) 0%,
    rgb(var(--brand-orange-rgb) / 0) 70%
  );
  opacity: 0;
  animation: beaconPulse 5s ease-in-out infinite;
}

/* The footer has a whole column to itself and no navigation competing with
   it, so the signature can sit a step larger than it does in the header. */
.brand-lockup--footer {
  --logo-h: 54px;
}

@media (max-width: 480px) {
  .brand-lockup--logo {
    --logo-h: 38px;
  }
  .brand-lockup--footer {
    --logo-h: 46px;
  }
}

@media (prefers-reduced-motion: reduce) {
  .js .brand-dot::after,
  .js .brand-lockup::after,
  .js .brand-lockup:hover::after {
    animation: none !important;
    opacity: 0 !important;
  }
  .brand-lockup:hover .brand-mark {
    transform: none !important;
  }
  .brand-lockup--logo:hover .brand-logo {
    transform: none !important;
  }
  .js .brand-lockup--logo::before {
    animation: none !important;
    opacity: 0 !important;
  }
}

/* --- 9. The beacon motif -------------------------------------------------
   Everything in this section exists to answer one problem: the pages were
   well-built but anonymous — swap the logo out and they could belong to any
   consultancy. The fix is not more decoration, it is one idea repeated.

   That idea is the company's own mark: a lighthouse inside a rope ring. It
   gives three devices, and every rule below is one of the three.

     the beam   — warm light sweeping slowly across dark surfaces
     the lamp   — a pulse on the same 5s cycle as the header lockup
     the seal   — the mark itself, ghosted in as a watermark

   The 5s period is shared deliberately. The header lockup, the dividers and
   the back-to-top button all breathe together, so the page reads as one
   lighthouse rather than several unrelated blinking things.

   Paths in url() resolve against this stylesheet, hence ../images/.        */

/* 9.1 The beam — hero light sweep ---------------------------------------
   A band of warm light crossing the hero: the beam passing the observer.

   The first attempt at this rotated a conic wedge about a point in the top
   right corner, which is geometrically the honest way to draw a lighthouse.
   It was invisible. A screen-blended warm wedge has nothing to lighten
   against the bright sky that fills the top of all five hero photographs,
   and a 22° wedge spends most of a full turn pointing off the canvas.

   A travelling band has defined edges, so it reads across both the dark
   scrim on the left and the sky on the right. It also rhymes with the sweep
   already on the header lockup's hover, which makes the two read as one
   idea. The timing is the part that matters: 3.8s of travel and 7.2s of
   darkness. A beam that is always on stops being a beam.

   z-index 1 puts it above the photograph and its gradient scrim (auto/0)
   and below the copy (z-10), so nothing is ever laid over the text. screen
   blending can only lighten; at its brightest the band lifts the dark scrim
   to about #4a4038, which still leaves the white hero type near 9:1.       */
.hero-beacon {
  position: relative;
}
.js .hero-beacon::before {
  content: "";
  position: absolute;
  z-index: 1;
  pointer-events: none;
  /* Overhanging top and bottom so the skew never exposes a corner. */
  top: -25%;
  bottom: -25%;
  left: 0;
  width: 46%;
  mix-blend-mode: screen;
  background: linear-gradient(
    100deg,
    rgb(255 205 170 / 0) 0%,
    rgb(255 205 170 / 0.1) 38%,
    rgb(255 214 185 / 0.26) 50%,
    rgb(255 205 170 / 0.1) 62%,
    rgb(255 205 170 / 0) 100%
  );
  animation: heroBeam 11s cubic-bezier(0.45, 0, 0.55, 1) infinite;
}
@keyframes heroBeam {
  0% {
    transform: translateX(-150%) skewX(-14deg);
  }
  35%,
  100% {
    transform: translateX(330%) skewX(-14deg);
  }
}

/* 9.2 The seal — the mark as a watermark --------------------------------
   Sized in percentages of its container so it scales with the section. It is
   deliberately cropped by the section's overflow: a whole circle floating in
   a corner reads as a sticker, a cropped one reads as a watermark.         */
.seal-watermark {
  position: absolute;
  pointer-events: none;
  user-select: none;
  z-index: 0;
  width: clamp(240px, 38%, 520px);
  height: auto;
  opacity: 0.05;
}
.seal-watermark--dark {
  /* On a dark surface the white mark needs more presence to register at all. */
  opacity: 0.07;
}
/* A very slow drift, an order of magnitude slower than anything else on the
   page. It should never be caught moving — only noticed as different on a
   second visit to the same scroll position. */
.js .seal-watermark {
  animation: sealDrift 90s ease-in-out infinite alternate;
}
@keyframes sealDrift {
  from {
    transform: rotate(-4deg) scale(1);
  }
  to {
    transform: rotate(4deg) scale(1.06);
  }
}

/* 9.3 The lamp — section divider ----------------------------------------
   Replaces a plain 1px border between sections. The halo behind the mark
   runs the same 5s beacon cycle as the header lockup.                     */
.beacon-rule {
  display: flex;
  align-items: center;
  gap: 20px;
  width: 100%;
}
.beacon-rule::before,
.beacon-rule::after {
  content: "";
  flex: 1;
  height: 1px;
  background: linear-gradient(
    to right,
    transparent,
    rgb(var(--brand-orange-rgb) / 0.35)
  );
}
.beacon-rule::after {
  background: linear-gradient(
    to left,
    transparent,
    rgb(var(--brand-orange-rgb) / 0.35)
  );
}
.beacon-rule--dark::before {
  background: linear-gradient(to right, transparent, rgb(255 255 255 / 0.25));
}
.beacon-rule--dark::after {
  background: linear-gradient(to left, transparent, rgb(255 255 255 / 0.25));
}

/* The halo sits on a wrapper, not on the image, because an <img> cannot
   carry a pseudo-element. */
.beacon-rule-lamp {
  position: relative;
  flex: none;
  display: block;
  line-height: 0;
}
.beacon-rule-mark {
  display: block;
  height: 30px;
  width: auto;
}
.js .beacon-rule-lamp::after {
  content: "";
  position: absolute;
  left: 50%;
  top: 50%;
  width: 46px;
  height: 46px;
  margin: -23px 0 0 -23px;
  border-radius: 50%;
  pointer-events: none;
  background: radial-gradient(
    circle,
    rgb(var(--brand-orange-rgb) / 0.45) 0%,
    rgb(var(--brand-orange-rgb) / 0) 70%
  );
  opacity: 0;
  animation: beaconPulse 5s ease-in-out infinite;
}

/* 9.4 The seal on card hover --------------------------------------------
   Every card on the site already carries .card-accent, which is the only
   reason this needs no markup at all. The mark ghosts up out of the bottom
   right corner as the card lifts — a stamp being pressed.

   It is kept inside the card's own bounds rather than bleeding off the
   corner, because .card-accent is not overflow:hidden and making it so would
   risk clipping card content that has nothing to do with this.            */
.card-accent::after {
  content: "";
  position: absolute;
  right: 14px;
  bottom: 14px;
  width: 52px;
  height: 52px;
  pointer-events: none;
  background: url("../images/brand/beacon-mark.png") no-repeat center / contain;
  opacity: 0;
  transform: translateY(8px) scale(0.9);
  transition:
    opacity 0.45s ease,
    transform 0.45s cubic-bezier(0.34, 1.56, 0.64, 1);
}
.card-accent:hover::after {
  opacity: 0.13;
  transform: none;
}
/* On a dark card the orange mark sinks into the ground, so the white one is
   swapped in and given a little more weight. */
.card-accent--dark::after {
  background-image: url("../images/brand/beacon-mark-light.png");
}
.card-accent--dark:hover::after {
  opacity: 0.16;
}

/* 9.5 The beam — scroll progress ----------------------------------------
   Built by site.js, so it never exists without scripting. Width is driven
   from JS; the lamp on its leading edge is what ties a piece of browser-like
   chrome back to the identity instead of leaving it a generic orange bar.  */
.beacon-progress {
  position: fixed;
  top: 0;
  left: 0;
  height: 2px;
  width: 0;
  z-index: 60;
  pointer-events: none;
  background: linear-gradient(
    to right,
    rgb(var(--brand-orange-rgb) / 0.35),
    var(--brand-orange)
  );
}
.beacon-progress::after {
  content: "";
  position: absolute;
  right: -3px;
  top: -3px;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--brand-orange);
  box-shadow: 0 0 10px 3px rgb(var(--brand-orange-rgb) / 0.55);
}

/* 9.6 The lamp — back to top --------------------------------------------
   Also built by site.js. The mark is the button: no icon, no chevron, no
   label — a lighthouse is already the thing you steer back towards.       */
.beacon-top {
  position: fixed;
  right: 24px;
  bottom: 24px;
  z-index: 50;
  width: 52px;
  height: 52px;
  display: grid;
  place-items: center;
  border: 1px solid rgb(var(--brand-orange-rgb) / 0.35);
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.92);
  backdrop-filter: blur(8px);
  box-shadow: 0 8px 24px -10px rgba(21, 28, 34, 0.45);
  cursor: pointer;
  padding: 0;
  opacity: 0;
  visibility: hidden;
  transform: translateY(12px);
  transition:
    opacity 0.35s ease,
    transform 0.35s cubic-bezier(0.34, 1.56, 0.64, 1),
    visibility 0.35s;
}
.beacon-top.is-visible {
  opacity: 1;
  visibility: visible;
  transform: none;
}
.beacon-top img {
  width: 28px;
  height: 28px;
  display: block;
}
.beacon-top:hover {
  border-color: var(--brand-orange);
  background: #ffffff;
}
.js .beacon-top::after {
  content: "";
  position: absolute;
  inset: -6px;
  border-radius: 50%;
  pointer-events: none;
  background: radial-gradient(
    circle,
    rgb(var(--brand-orange-rgb) / 0.35) 0%,
    rgb(var(--brand-orange-rgb) / 0) 70%
  );
  opacity: 0;
  animation: beaconPulse 5s ease-in-out infinite;
}
@media (max-width: 640px) {
  .beacon-top {
    right: 16px;
    bottom: 16px;
    width: 46px;
    height: 46px;
  }
  .beacon-top img {
    width: 24px;
    height: 24px;
  }
}

/* 9.7 Reduced motion ------------------------------------------------------
   The devices stay — the beam, the seal and the lamp are the identity, not an
   embellishment. Only their movement stops. The one exception is the hero
   sweep: a wedge of light frozen mid-arc looks like a rendering fault, so
   that one is removed outright.                                           */
@media (prefers-reduced-motion: reduce) {
  .js .hero-beacon::before {
    display: none;
  }
  .js .seal-watermark {
    animation: none !important;
    transform: none !important;
  }
  .js .beacon-rule-lamp::after,
  .js .beacon-top::after {
    animation: none !important;
    opacity: 0 !important;
  }
  .card-accent::after {
    transform: none !important;
  }
}
