/* ==========================================================================
   Base: reset, fonts, typography scale, container, buttons.
   No inline styles anywhere in this project, so every rule lives in a file
   that can be wp_enqueue_style()'d unchanged.
   ========================================================================== */

@import url("../fonts/fonts.css");

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

html {
  -webkit-text-size-adjust: 100%;
  overflow-y: scroll; /* always reserve the scrollbar so layout width is stable */
}

body {
  margin: 0;
  /* Purple gradient tile repeating down the page over black, exactly as the
     reference does it: the glow lives on the body, not on the sections, which
     is why individual sections are transparent. */
  background-color: var(--c-body-bg);
  background-image: url("../img/bg-gradient.webp");
  background-size: 100% auto;
  background-position: 100% 900px;
  background-repeat: repeat-y;
  background-attachment: scroll;
  color: var(--c-text);
  font-family: var(--ff-body);
  font-size: var(--fs-body);
  line-height: var(--lh-body);
  font-weight: 400;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;

  /* clip, NOT hidden. overflow-x:hidden makes the block a scroll container,
     which silently breaks position:sticky for every descendant. clip contains
     the same horizontal overflow without creating one. */
  overflow-x: clip;
}

h1, h2, h3, h4, h5, h6, p, figure, blockquote { margin: 0; }
ul, ol { margin: 0; padding: 0; list-style: none; }
img, svg, video { display: block; max-width: 100%; }
a { color: inherit; text-decoration: none; }
button { font: inherit; color: inherit; background: none; border: 0; cursor: pointer; }

:focus-visible { outline: 2px solid var(--c-accent); outline-offset: 3px; }

/* --- Accessibility ------------------------------------------------------ */
.skip-link {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: 999;
  padding: 12px 20px;
  background: var(--c-accent);
  color: var(--c-white);
  font-weight: 700;
}
.skip-link:focus { left: 0; }

.screen-reader-text {
  position: absolute !important;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0, 0, 0, 0);
  white-space: nowrap; border: 0;
}

/* --- Layout ------------------------------------------------------------- */
.container {
  width: 100%;
  max-width: var(--container);
  margin-inline: auto;
}

.section { position: relative; }

/* --- Typography --------------------------------------------------------- */
.h-primary {
  font-size: var(--fs-h-primary);
  font-weight: var(--fw-h-primary);
  line-height: var(--lh-h-primary);
  color: var(--c-primary);
}

.h-secondary {
  font-size: var(--fs-h-secondary);
  font-weight: var(--fw-h-secondary);
  line-height: var(--lh-h-secondary);
  color: var(--c-primary);
}

/* The theme's `.section-title`: a small, widely tracked label preceded by an
   eight-point asterisk in the accent colour. Seven sections use it, so it is
   defined once here. The asterisk is a mask rather than an <img> so it takes
   its colour from the token and recolours for free at the rebrand. */
.eyebrow {
  position: relative;
  padding-left: var(--eyebrow-indent);
  font-size: var(--fs-eyebrow);
  font-weight: var(--fw-eyebrow);
  line-height: var(--lh-eyebrow);
  letter-spacing: var(--ls-eyebrow);
  color: var(--c-primary);
}

.eyebrow::before {
  content: "";
  position: absolute;
  left: 0;
  top: 50%;
  width: var(--eyebrow-mark-w);
  height: var(--eyebrow-mark-h);
  transform: translateY(-50%);
  background-color: var(--c-accent);
  -webkit-mask-image: url("../img/asterisk-icon.svg");
          mask-image: url("../img/asterisk-icon.svg");
  /* `100% 100%`, not `cover`. The source is 24x25 and the box is 15x24, so
     `cover` would crop the arms off; the reference squashes it instead. */
  -webkit-mask-size: 100% 100%;
          mask-size: 100% 100%;
}

/* --- Buttons ------------------------------------------------------------ */
/* Pill button with a detached circular icon that slides in on hover.        */
.btn {
  position: relative;
  display: inline-block;
  padding: var(--btn-pad);
  border-radius: var(--btn-radius);
  background: var(--c-accent);
  color: var(--c-white);
  font-size: var(--fs-accent);
  font-weight: var(--fw-accent);
  line-height: 1.4;
  text-align: center;
  transition: background var(--dur) var(--ease);
}

/* Every button on the reference carries a flat white wash at 20%. It is what
   lifts the solid purple fill, and on the ghost variant it IS the pill. The
   reference lets it sit above the label rather than behind it, so this does
   too: at 20% over white text the wash is imperceptible either way. */
.btn::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: var(--btn-radius);
  background: var(--c-white);
  opacity: .2;
}

/* Secondary variant. No fill of its own, so the 20% wash is the whole pill.
   Used for the softer calls to action that sit next to body copy. */
.btn--ghost { background: transparent; }

/* Reserves the width the detached icon circle overhangs by, so the button
   does not collide with anything to its right. Mirrors Elementor's own
   .elementor-button-wrapper, which applies the same 50px. */
.btn-wrap { padding-right: var(--btn-icon-size); }

.btn__content {
  display: flex;
  flex-direction: row-reverse;
  justify-content: center;
}

.btn__icon {
  position: absolute;
  top: 0;
  right: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  width: var(--btn-icon-size);
  height: var(--btn-icon-size);
  border-radius: 50%;
  background: var(--c-accent);
  /* The arrow is black at rest and stays black on hover; only the circle
     behind it changes colour. */
  color: var(--c-black);
  transform: translateX(var(--btn-icon-size));
  transition: background var(--dur) var(--ease-in-out),
              color var(--dur) var(--ease-in-out),
              transform var(--dur) var(--ease-in-out);
}

.btn__icon svg { width: 24px; height: 24px; }

/* On hover the detached circle flips to white and turns 45deg. Because the
   arrow points north-east by default, that quarter-turn swings it round to
   point straight down-right.

   The pill fill deliberately does NOT change: the reference leaves it be and
   lets the circle carry the whole interaction. */
.btn:hover .btn__icon,
.btn:focus-visible .btn__icon {
  background: var(--c-white);
  transform: translateX(var(--btn-icon-size)) rotate(45deg);
}

/* --- Image hover sweep --------------------------------------------------- */
/* A 30% white band, laid at -45deg and pinched to zero height, that grows
   through the frame on hover and fades out as it goes. The reference uses it
   on every photographic image, so it lives here rather than in a section. */
.image-anime {
  position: relative;
  overflow: hidden;
}

.image-anime::after {
  content: "";
  position: absolute;
  left: 50%;
  top: 50%;
  z-index: 1;
  width: 200%;
  height: 0;
  background-color: rgba(255, 255, 255, .3);
  transform: translate(-50%, -50%) rotate(-45deg);
  pointer-events: none;
}

/* Only the hover state carries a transition, so the band sweeps out and then
   snaps back to nothing rather than sweeping in reverse. */
.image-anime:hover::after {
  height: 250%;
  background-color: rgba(255, 255, 255, 0);
  transition: 600ms linear;
}

/* --- Motion preferences ------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
    scroll-behavior: auto !important;
  }
}

/* Off-screen but still announced. Used for form labels that the design does
   not show. */
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}


/* ==========================================================================
   Preloader

   A black full-viewport cover holding the logo mark inside a ring whose two
   opposite arcs rotate. Shown from first paint, faded out on `window.load` by
   `src/scripts/preloader.ts`.

   Every value here is measured off the reference's live preloader, not
   eyeballed. Their authored rules, for the record:

     .preloader        position:fixed; inset 0; z-index:10000; bg black; flex centred
     .loading-container, .loading   100x100, position:relative, border-radius:100%
     .loading          border:1px solid transparent, border-color transparent
                       ACCENT transparent ACCENT, rotate-loading 1.5s linear infinite
     #loading-icon     position:absolute; top/left 50%; max-width:66px;
                       translate(-50%,-50%)

   THE TWO ARCS ARE BORDERS, not two elements and not an SVG. `.loading` is an
   empty 100px box with a 100% radius whose top and bottom borders are
   transparent and whose left and right are the accent, so rotating the box
   sweeps two facing arcs around the mark. Change `border-color` to a single
   value and it becomes a plain ring with nothing to animate.

   `#loading-icon` RESOLVES TO 50px WIDE, NOT 66. It is absolutely positioned
   at `left: 50%` inside a 100px container, so the width available to it is the
   remaining 50px, and shrink-to-fit takes that before `max-width: 66px` ever
   binds. The reference measures 50 x 36.4 for a 268x195 mark and so do we.
   The 66px is dead in both builds; it is kept because it is theirs and because
   a wider container would make it live again.
   ========================================================================== */

.preloader {
  position: fixed;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  z-index: 10000;
  display: flex;
  align-items: center;
  justify-content: center;
  background-color: var(--c-black);
}

/* The end state, set by the script once the fade completes.

   IT MUST BE `display`, and not opacity or visibility. `tools/probe.py`'s
   UNREVEAL pass forces `opacity: 1; visibility: visible` onto every element
   carrying an inline style that mentions either, to undo the reference's
   ScrollTrigger transforms before measuring. A preloader parked at opacity 0
   would be restored to fully opaque for every probe run -- a black overlay at
   z-index 10000 over every measurement and every screenshot, while the page
   looks perfect in a browser. UNREVEAL does not touch `display`. */
.preloader.is-done {
  display: none;
}

.loading-container,
.loading {
  position: relative;
  width: 100px;
  height: 100px;
  border-radius: 100%;
}

.loading-container {
  margin: 40px auto;
}

.loading {
  border: 1px solid transparent;
  border-color: transparent var(--c-accent) transparent var(--c-accent);
  transform-origin: 50% 50%;
  animation: rotate-loading 1.5s linear infinite;
}

#loading-icon {
  position: absolute;
  top: 50%;
  left: 50%;
  max-width: 66px;
  transform: translate(-50%, -50%);
}

/* `height: auto` is NOT redundant here, and leaving it out renders the mark
   50 x 195 instead of 50 x 36.4 -- a stretched column filling the ring.

   The reset at the top of this file is `img, svg, video { display: block;
   max-width: 100% }`, with no `height`. The reference's preloader img carries
   no width/height attributes at all, so its height is already auto and it
   scales. Ours carries `width="268" height="195"`, because CLAUDE.md landmine
   3 says to keep explicit dimensions on every <img> during the port. Those
   two facts collide: `max-width` pins the width to the available 50px while
   the height attribute holds 195, and the mark distorts. This restores the
   ratio without dropping the attributes, so the intrinsic size is still
   declared and the box is still reserved before the file arrives. */
#loading-icon img {
  height: auto;
}

@keyframes rotate-loading {
  from { transform: rotate(0deg); }
  to   { transform: rotate(360deg); }
}
