/* Effects layer for the reconstructed aael24.com.
   Everything here sits on top of the site runtime, which owns the rest of the DOM. */

/* ---------- preloader ---------- */
#fx-preloader {
  position: fixed;
  inset: 0;
  z-index: 100000;
  display: flex;
  align-items: center;
  justify-content: center;
  background: #fafafa;            /* the page's own field */
  will-change: opacity;
}

/* Fades rather than slides away: the mark it leaves behind is the same mark, at
   the same size in the same place, so nothing should appear to move. */
#fx-preloader.fx-leaving {
  transition: opacity 0.6s ease;
  opacity: 0;
  pointer-events: none;
}

/* The mark draws itself here, so it needs no entrance of its own — the first
   stroke appearing is the entrance. The filled mark waits underneath at zero
   and takes over once the drawing is complete. */
#fx-logo {
  position: relative;
  width: 116px;
  height: 116px;
}

#fx-logo svg,
#fx-logo img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
}

#fx-logo img {
  opacity: 0;
  transition: opacity 0.26s ease;
}

#fx-logo.fx-logo-set svg { opacity: 0; transition: opacity 0.26s ease; }
#fx-logo.fx-logo-set img { opacity: 1; }

/* ---------- the metal mark (mark.js) ---------- */
/* One fixed box the size of the big mark, moved and scaled by a transform from
   mark.js: the preloader's mark, the opening screen's, the docked link to
   About and About's own. Above the curtain, so the drawing is on top of it. */
#fx-mark {
  position: fixed;
  top: 0;
  left: 0;
  z-index: 100002;
  display: block;
  transform-origin: 50% 50%;
  will-change: transform;
  -webkit-tap-highlight-color: transparent;
  outline: none;
}
#fx-mark .fx-mark-gl,
#fx-mark .fx-mark-gl > img,
#fx-mark .fx-mark-gl > canvas {
  display: block;
  width: 100%;
  height: 100%;
}
/* the flat SVG only shows if the metal never comes */
#fx-mark.is-pending img { opacity: 0; }
#fx-mark.is-inert { pointer-events: none; }
/* Big, it is there to be spun. Once the page scrolls it rests (mark.js):
   not dragged any more, the way back to the start. */
#fx-mark.is-still { cursor: pointer; }
#fx-mark.is-still canvas { pointer-events: none; }

/* The opening screen's copy (mark.js): About's sentence in About's type
   (about-layout.css, .about-lead), behind the big mark. Centred on the
   visible window like the mark; mark.js moves it up with the first screen.
   It rises into place with the captions as the curtain lifts. */
#fx-lead {
  position: fixed;
  left: 0;
  right: 0;
  top: 0;
  height: var(--fx-vh, 100vh);
  z-index: 9999;
  display: flex;
  align-items: center;
  justify-content: center;
  pointer-events: none;
  will-change: transform;
}
#fx-lead p {
  /* margins from site.js (mark.js): pushed away from each side */
  position: relative;
  left: var(--fx-lead-dx-d, 0px);
  top: var(--fx-lead-dy-d, 0px);
  width: var(--fx-lead-measure-d, 694px);
  max-width: calc(100vw - 240px);
  margin: 0;
  font-family: "Ose", Georgia, serif;
  font-weight: 400;
  font-size: var(--fx-lead-size-d, 64px);
  line-height: var(--fx-lead-leading, 1);
  letter-spacing: var(--fx-lead-tracking, 0);
  text-transform: uppercase;
  text-align: center;
  color: #000;
  word-break: break-word;
  opacity: 0;
  transform: translateY(48px);
  transition: opacity 0.9s ease, transform var(--fx-lead-rise-ms, 1100ms) cubic-bezier(0.22, 1, 0.36, 1);
}
#fx-lead.is-in p {
  opacity: 1;
  transform: none;
}
/* Drawn by the gallery's canvas (so the glass band bends it): this copy only
   holds the layout and is what screen readers read. */
#fx-lead.is-raster p {
  color: transparent;
  -webkit-text-fill-color: transparent;
}
/* only the gallery's first screen has it */
:root:not(.fx-gallery) #fx-lead { visibility: hidden; }
@media (min-width: 761px) and (max-width: 1180px) {
  #fx-lead p {
    left: var(--fx-lead-dx-t, var(--fx-lead-dx-d, 0px));
    top: var(--fx-lead-dy-t, var(--fx-lead-dy-d, 0px));
    width: var(--fx-lead-measure-t, var(--fx-lead-measure-d, 694px));
    font-size: var(--fx-lead-size-t, var(--fx-lead-size-d, 64px));
  }
}
@media (max-width: 760px) {
  #fx-lead p {
    left: var(--fx-lead-dx-p, 0px);
    top: var(--fx-lead-dy-p, 0px);
    width: var(--fx-lead-measure-p, 350px);
    max-width: calc(100vw - 40px);
    font-size: var(--fx-lead-size-p, 48px);
  }
}
/* the ring is drawn inside the scaled box, so it is sized up by the scale
   mark.js writes to --fx-mark-scale, to land at 2px on screen */
#fx-mark:focus-visible .fx-mark-gl {
  outline: calc(2px / var(--fx-mark-scale, 1)) solid #111;
  outline-offset: calc(6px / var(--fx-mark-scale, 1));
  border-radius: 50%;
}

/* The mark takes ABOUT's place on Home: the link stays in the layout, so
   mark.js can measure where to dock, but it is not seen or reached. HOME on
   About is untouched. */
:root.fx-gallery #container [data-fx-nav],
:root.fx-gallery #fx-nav {
  visibility: hidden !important;
  pointer-events: none !important;
}

/* Hold the page still while the preloader is up. */
html.fx-locked,
html.fx-locked body { overflow: hidden; }

/* ---------- word reveal ---------- */
/* No overflow:hidden here on purpose. The runtime gives these captions a
   line-height far smaller than their font-size (an 18px box at 40px type), so a
   clipping mask decapitates the glyphs — AAEL renders as "A A |- I". The words
   still rise into place, they just are not clipped on the way. */
.fx-mask {
  display: inline-block;
  vertical-align: top;
  line-height: inherit;
}

.fx-word {
  display: inline-block;
  transform: translateY(0.5em);
  opacity: 0;
  will-change: transform, opacity;
  transition: transform 1.1s cubic-bezier(0.22, 1, 0.36, 1),
              opacity 1.1s cubic-bezier(0.22, 1, 0.36, 1);
}

.fx-word.fx-in {
  transform: translateY(0);
  opacity: 1;
}

/* ---------- glass bands ---------- */
/* Matches gionatannese.com: pure vertical refraction, no blur, no tint and no
   specular edge — the sag profile already fades to nothing at the inner edge,
   so no mask is needed either. backdrop-filter is set from JS, which sizes the
   displacement map to the viewport. */
.fx-glass {
  position: fixed;
  left: 0;
  width: 100%;
  height: var(--fx-band-h, 8vh);
  /* under the site's fixed captions (z 9999), above the imagery */
  z-index: 9998;
  pointer-events: none;
}

.fx-glass--top { top: 0; }
.fx-glass--bottom { bottom: 0; }

/* Safari path: no SVG displacement available, so soften the band towards the
   page instead of leaving a hard-edged blurred rectangle. */
.fx-glass--fallback.fx-glass--top {
  -webkit-mask-image: linear-gradient(to bottom, #000 0%, #000 30%, transparent 100%);
  mask-image: linear-gradient(to bottom, #000 0%, #000 30%, transparent 100%);
}

.fx-glass--fallback.fx-glass--bottom {
  -webkit-mask-image: linear-gradient(to top, #000 0%, #000 30%, transparent 100%);
  mask-image: linear-gradient(to top, #000 0%, #000 30%, transparent 100%);
}

#fx-glass-defs {
  position: absolute;
  width: 0;
  height: 0;
  pointer-events: none;
}

@media (prefers-reduced-motion: reduce) {
  .fx-word { transform: none; opacity: 1; }
  #fx-preloader.fx-leaving { transition-duration: 0.2s; }
}

/* ---------- WebGL layer (effects-gallery.js) ---------- */
/* Sits above the page imagery but below the fixed captions, mirroring how the
   reference site keeps its navbar in the DOM on top of the canvas. */
#fx-canvas {
  position: fixed;
  inset: 0;
  /* Size comes from visualViewport in JS — on iOS neither vh, dvh nor lvh match
     what is actually on screen while the address bar animates, and every one of
     them left bands of background at the edges. */
  width: 100%;
  height: 100vh;
  z-index: 9998;
  pointer-events: none;
  display: block;
}

/* ---------- gallery: caption inversion ---------- */

/* Inversion via blend mode, the way the reference site does it: the captions
   are painted white and differenced against whatever is behind them, so they go
   black over a light frame and white over a dark one with no state to track.
   `isolation: auto` on the ancestors is what lets them blend against the canvas
   rather than against an empty stacking context. */
:root.fx-gallery #container p,
:root.fx-gallery #container a,
:root.fx-gallery #container sup {
  color: #fff !important;
  /* difference against the frame behind, same rule as the captions — otherwise
     the label stays white and disappears over a light render */
  mix-blend-mode: difference;
  /* the runtime paints type through -webkit-text-fill-color, which wins over
     `color` — without this the captions ignore the rule entirely */
  -webkit-text-fill-color: #fff !important;
}

/* The blend goes on the fixed wrapper that owns the stacking context, not on the
   text node itself (see markBlendRoots in effects-gallery.js). */
:root.fx-gallery [data-fx-invert] {
  mix-blend-mode: difference;
}

/* The logo is a dark SVG; inverting it first makes difference flip it with the
   frame behind, same as the type. It also needs lifting above the opaque canvas. */
/* The canvas draws the mark itself. The runtime's copy used to be merely hidden
   behind it, which was fine until the canvas started panning away — then its
   twin sat in the middle of the page waiting to be met. */
:root.fx-gallery [data-fx-logo-wrap] {
  display: none !important;
}

/* The runtime parks the copyright line in flow; pin it to the bottom edge. */
#container [data-fx-copyright] {
  position: fixed !important;
  left: 0 !important;
  right: 0 !important;
  top: auto !important;
  /* Above the home indicator, or Safari's floating bar, wherever the page
     runs under the bottom edge (viewport-fit=cover). Zero where it does not. */
  bottom: calc(24px + env(safe-area-inset-bottom, 0px)) !important;
  margin: 0 !important;
  text-align: center;
  z-index: 9999;
}

/* The nav link's wrapper (tagged in effects-gallery.js) keeps clear of the
   status bar where the page runs under it — the home-screen app, in-app
   browsers. In Safari's own portrait view the inset is zero. */
#container [data-fx-nav] {
  margin-top: env(safe-area-inset-top, 0px) !important;
}

/* Teodor is gone: About's copy is Ose, and the "Lights on" label that kept the
   Regular cut went with the click. Both files stay in fonts/ (not deployed,
   see .vercelignore) in case the label comes back with the product page. */

:root.fx-gallery #container a,
:root.fx-gallery #container a * {
  font-family: "Neue Haas Display", ui-sans-serif, system-ui, sans-serif !important;
}

/* ---------- Ose (About's lead copy) ---------- */
/* The features sit on the face rather than only on the element: text drawn
   into a canvas (About's touch ripple) picks up OpenType features from here
   and nowhere else. Per Figma: dlig, ss01 (nested capitals), ss02 (curved R
   with small capitals), ss05 (alternate J, Q, R). ss03, the straight-R twin
   of ss02, stays off. */
@font-face {
  font-family: "Ose";
  src: url("/fonts/Ose-Regular.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
  font-feature-settings: "dlig", "ss01", "ss02", "ss05";
}

/* ---------- Neue Haas Grotesk Display (Roman) ---------- */
/* woff2 of the supplied TTF: the same font, about a third of the bytes. */
@font-face {
  font-family: "Neue Haas Display";
  src: url("/fonts/NeueHaasDisplayRoman.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}

/* The captions the runtime renders (AAEL, C24, © 2026) are specced in Neue Haas
   Grotesk Display Pro 55 Roman; the dump ships no @font-face for it. */
#container p,
#container span,
#container sup {
  font-family: "Neue Haas Display", "Neue Haas Grotesk Display Pro", ui-sans-serif, system-ui, sans-serif;
}

/* On the gallery the glass comes from the WebGL shader, so the backdrop-filter
   bands are a second, conflicting copy of the effect — and effects.js rebuilds
   them on every resize (which on mobile fires constantly as the address bar
   shows and hides). Removing the nodes loses that race; hiding them does not.
   About keeps its bands. */
:root.fx-gallery .fx-glass {
  display: none !important;
}

/* The gallery owns the gesture: without this Safari still rubber-bands, pulls
   to refresh and runs its edge back-swipe over our own scrolling. */
:root.fx-gallery,
:root.fx-gallery body {
  overscroll-behavior: none;
  touch-action: none;
  overflow: hidden;
  height: 100%;
}

/* On a phone the page runs under Safari's bars — measured on the device, not
   guessed: Safari blurs the *document* beyond the viewport into its bottom bar
   and, once something has been scrolled past, into the status bar; it never
   paints a fixed layer there, and a theme-color makes the bars opaque. So on
   touch the document is taller than the viewport by more than the bars, held
   scrolled by a few dozen pixels (effects-gallery.js), and the canvas is part
   of the document rather than fixed to the viewport. Only touch-action holds
   the page, which the phone test showed is enough. */
@media (hover: none) and (pointer: coarse) {
  :root.fx-gallery,
  :root.fx-gallery body {
    overflow: visible;
    height: auto;
  }
  :root.fx-gallery body {
    min-height: calc(100vh + 220px);
    min-height: calc(100lvh + 220px);
  }
  #fx-canvas {
    position: absolute;
    top: 0;
    left: 0;
  }
}

#fx-canvas {
  touch-action: none;
}

/* With viewport-fit=cover the page reaches under the notch and home indicator;
   painting these the strip's colour keeps them from reading as bands before the
   canvas draws. */
html, body {
  /* !important: the runtime paints body white after us, and Safari takes the
     page's colour from here for its own bars — one colour, top to bottom.
     --fx-page is set from the gallery's own frame loop (effects-gallery.js),
     so the colour under the bars is the colour of the render on screen. */
  background-color: var(--fx-page, #fafafa) !important;
}

/* The copyright line is gone from both pages: the phone frame reads as three
   things — the labels above, the mark or the copy, the way to the other page
   below — and the desktop had nothing to say there either. */
#container [data-fx-copyright] {
  display: none !important;
}

/* Mobile — Figma frames 1515:4610 (Home) and 1515:4687 (About), one layout
   for both pages: AAEL and C24 in the corners at the top (36px, tracked
   10.8px, 40px in from the sides), the nav link centred at the bottom (36px,
   tracked 1.8px, 40px up), the mark or the copy alone in the middle. The
   runtime lays these out by its desktop metrics and gives the two pages
   different sizes; every rule here is pinned so the pages match. */
@media (max-width: 760px) {
  #container p,
  #container a,
  #container a * {
    font-size: 36px !important;
  }

  /* the side labels keep their monogram tracking; ABOUT and HOME are words */
  #container [data-fx-side],
  #container [data-fx-side] * {
    letter-spacing: 10.8px !important;
  }
  #container [data-fx-nav],
  #container [data-fx-nav] * {
    letter-spacing: 1.8px !important;
  }

  /* Cap height of 36px type sits at 41 in the frame. The runtime gives these
     an 18px line box at 36px type, so the glyphs overhang it by about 4px
     above: the box goes at 45 for the caps to land at 41. */
  #container [data-fx-side] {
    position: fixed !important;
    top: calc(45px + env(safe-area-inset-top, 0px)) !important;
    bottom: auto !important;
    transform: none !important;
    margin: 0 !important;
    width: auto !important;
    text-align: left !important;
  }

  #container [data-fx-side="left"] {
    left: 40px !important;
    right: auto !important;
  }

  #container [data-fx-side="right"] {
    right: 40px !important;
    left: auto !important;
    text-align: right !important;
  }

  /* On the phone the corner labels are the page's own (#fx-labels, built in
     effects.js); the runtime's are hidden so nothing re-renders up there. */
  #container [data-fx-side] {
    display: none !important;
  }

  /* The way to the other page is the page's own element too (#fx-nav), for
     the same reason as the labels — and because the runtime's link cannot be
     anchored to the bottom edge on iOS without paint and geometry parting
     company. Its own link is hidden here. */
  #container [data-fx-nav] {
    display: none !important;
  }
}

#fx-nav { display: none; }

/* Text slides in the strip (effects-gallery.js). The type is set here, one
   screen each, and drawn into the canvas from this layout; the DOM copy is
   transparent, under the canvas, for screen readers. */
.fx-slide-text {
  position: fixed;
  left: 0;
  top: 0;
  width: 100%;
  height: var(--fx-vh, 100vh);
  z-index: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  pointer-events: none;
}
.fx-slide-text p {
  margin: 0;
  position: relative;
  left: var(--dx-d, 0px);
  top: var(--dy-d, 0px);
  width: var(--measure-d, 900px);
  max-width: calc(100vw - 80px);
  font-family: var(--font);
  font-weight: 400;
  font-size: var(--size-d, 64px);
  line-height: var(--leading, 1.05);
  letter-spacing: var(--tracking, 0);
  text-align: var(--align, center);
  text-transform: var(--case, uppercase);
  color: transparent;
  -webkit-text-fill-color: transparent;
}
@media (min-width: 761px) and (max-width: 1180px) {
  .fx-slide-text p {
    left: var(--dx-t, var(--dx-d, 0px));
    top: var(--dy-t, var(--dy-d, 0px));
    width: var(--measure-t, var(--measure-d, 900px));
    font-size: var(--size-t, var(--size-d, 64px));
  }
}
@media (max-width: 760px) {
  .fx-slide-text p {
    left: var(--dx-p, 0px);
    top: var(--dy-p, 0px);
    width: var(--measure-p, 350px);
    max-width: calc(100vw - 40px);
    font-size: var(--size-p, 40px);
  }
}

/* pages.js: the strip fades through the page colour as it changes screens */
#fx-page-veil {
  position: fixed;
  inset: 0;
  z-index: 9998;
  background: #fafafa;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.22s ease;
}
#fx-page-veil.is-on { opacity: 1; }

/* Buttons on a screen of the strip (effects-gallery.js): a layer per screen
   moved with the strip, each button at the middle of it, shifted by its
   margins per device. Outlined by default, filled with .is-fill. */
.fx-slide-buttons {
  position: fixed;
  left: 0;
  top: 0;
  width: 100%;
  z-index: 3;
  pointer-events: none;
  will-change: transform;
}
.fx-button {
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%) translate(var(--dx-d, 0px), var(--dy-d, 0px));
  pointer-events: auto;
  padding: 0.95em 1.7em 0.85em;
  border: 1px solid var(--color, #161616);
  border-radius: 999px;
  color: var(--color, #161616);
  font-family: "Neue Haas Display", ui-sans-serif, system-ui, sans-serif;
  font-size: var(--size, 14px);
  line-height: 1;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  text-decoration: none;
  white-space: nowrap;
}
.fx-button.is-fill { background: var(--color, #161616); color: #fafafa; }
@media (min-width: 761px) and (max-width: 1180px) {
  .fx-button { transform: translate(-50%, -50%) translate(var(--dx-t, var(--dx-d, 0px)), var(--dy-t, var(--dy-d, 0px))); }
}
@media (max-width: 760px) {
  .fx-button { transform: translate(-50%, -50%) translate(var(--dx-p, 0px), var(--dy-p, 0px)); }
}

/* AAEL and C24, the page's own (mark.js) on every screen now. These rules
   are where they END: the sides on the centre line on a desktop (as the
   runtime set them, 40px type tracked 30%, an 18px line box at 40px), the
   top corners on a phone (36px, caps at 41). mark.js writes the transform
   that starts them as a caption under the sentence. Over a dark frame they
   invert like the runtime's captions did: white through difference. */
.fx-label {
  position: fixed;
  z-index: 9999;
  font-family: "Neue Haas Display", ui-sans-serif, system-ui, sans-serif;
  font-size: var(--fx-label-size-d, 40px);
  line-height: 0.45;
  letter-spacing: var(--fx-label-tracking, 0.3em);
  color: #161616;
  white-space: nowrap;
  pointer-events: none;
  transform-origin: 0 0;
  will-change: transform;
  top: calc(var(--fx-vh, 100vh) / 2 - 0.225em);
}
.fx-label[data-side="left"] { left: 40px; }
/* the tracking after the last glyph is not part of the design's 40px */
.fx-label[data-side="right"] { right: calc(40px - var(--fx-label-tracking, 0.3em)); }
:root.fx-gallery .fx-label {
  color: #fff;
  mix-blend-mode: difference;
}
/* they rise into place with the sentence as the curtain lifts */
.fx-label-in {
  display: inline-block;
  opacity: 0;
  transform: translateY(0.6em);
  transition: opacity 0.9s ease, transform var(--fx-lead-rise-ms, 1100ms) cubic-bezier(0.22, 1, 0.36, 1);
}
#fx-labels.is-in .fx-label-in { opacity: 1; transform: none; }
@media (min-width: 761px) and (max-width: 1180px) {
  .fx-label { font-size: var(--fx-label-size-t, var(--fx-label-size-d, 40px)); }
}
@media (max-width: 760px) {
  .fx-label {
    font-size: var(--fx-label-size-p, 36px);
    top: calc(45px + env(safe-area-inset-top, 0px));
  }
}
/* The runtime's own AAEL and C24 are replaced on every screen. */
#container [data-fx-side] { display: none !important; }
@media (max-width: 760px) {

  /* Hung from the visible bottom (--fx-vh, set in effects.js), never from
     `bottom`: 83px of box — the word and 20px of tap target around it —
     ending 20px above the edge, so the word sits 40px up as in the frame. */
  #fx-nav {
    display: block;
    position: fixed;
    left: 0;
    right: 0;
    top: calc(var(--fx-vh, 100svh) - 103px - env(safe-area-inset-bottom, 0px));
    z-index: 9999;
    padding: 20px 28px;
    font: 36px/43px "Neue Haas Display", ui-sans-serif, system-ui, sans-serif;
    letter-spacing: 1.8px;
    text-align: center;
    text-transform: uppercase;
    text-decoration: none;
    color: #161616;
    -webkit-tap-highlight-color: transparent;
  }
  :root.fx-gallery #fx-nav {
    color: #fff;
    mix-blend-mode: difference;
  }
}

/* Page switch: the two layers dip through the page's own colour rather than
   cutting between a canvas and a field of type (see router.js). */
#fx-veil {
  position: fixed;
  inset: 0;
  z-index: 100001;
  background: #fafafa;
  opacity: 0;
  pointer-events: none;
  transition: opacity 260ms ease;
}

#fx-veil.is-on { opacity: 1; }

@media (prefers-reduced-motion: reduce) {
  #fx-veil { transition-duration: 0.1s !important; }
}
