/* ==========================================================================
   home.css — toffspot.com homepage.

   The film runs from the first frame. The wordmark IS the preloader: its
   26 brush strokes fly in from scattered starts as the assets land, then it
   holds the centre in colour and boils quietly. Nothing waits on the
   visitor, and with JS blocked the finished mark is simply there.

   Type is Inter, Toffer's call, and the same face the report at
   /hyper-reality already uses. One variable file covers 100 to 900.
   ========================================================================== */

@font-face {
  font-family: 'Inter';
  src: url('/assets/fonts/inter.woff2') format('woff2');
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
}

:root {
  --black: #000000;
  --white: #ffffff;
  --orange: #e47839;
  --blue: #002fa7;
  --dim: #858585;
  --dimmer: #5c5c5c;      /* footer: present, not read first */

  --sans: 'Inter', system-ui, -apple-system, 'Segoe UI', sans-serif;
  --gut: clamp(1.5rem, 5vw, 4rem);

  /* the reference's two easings */
  --ease-out: cubic-bezier(0.645, 0.045, 0.355, 1);
  --ease-snap: cubic-bezier(1, 0, 0, 1);

  /* the wordmark holds the centre, at the reference's proportion */
  --mark-w: min(64vw, 760px);
}

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

html { overflow-x: clip; -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  min-height: 100dvh;
  overflow-x: clip;
  background: var(--black);
  color: var(--white);
  font-family: var(--sans);
  font-weight: 300;
  -webkit-font-smoothing: antialiased;
}

/* Nothing scrolls while the preloader is up. */
html.is-preload, html.is-preload body { overflow: hidden; height: 100%; }

img { display: block; max-width: 100%; height: auto; }

.visually-hidden {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

.skip-link {
  position: absolute;
  left: -9999px; top: 0;
  z-index: 80;
  background: var(--white);
  color: var(--black);
  padding: 0.75rem 1rem;
}
.skip-link:focus { left: 0; }

:where(a):focus-visible { outline: 2px solid var(--orange); outline-offset: 4px; }

/* ==========================================================================
   STAGE
   ========================================================================== */
.stage {
  position: relative;
  min-height: 100dvh;
  display: flex;
  flex-direction: column;
  isolation: isolate;
}

/* ---------- background film ---------- */
.stage__bg {
  position: fixed;
  inset: 0;
  z-index: -1;
  overflow: hidden;
  background: var(--black);
}

.stage__video {
  width: 100%;
  height: 100%;
  object-fit: cover;
  opacity: 0;
  transition: opacity 1400ms var(--ease-out);
}

/* The film is analog static, Toffer's own footage. Full-screen,
   high-contrast, fast luminance flicker: at full strength that is the
   pattern WCAG 2.3.1 warns about, so it is held down to a texture rather
   than a screen. Raise --film-strength if it reads too faint, but do not
   take it near 1. */
.stage__video.is-playing { opacity: var(--film-strength, 0.34); }

.stage__veil {
  position: absolute;
  inset: 0;
  background:
    radial-gradient(120% 80% at 50% 45%,
      rgba(0,0,0,0.42) 0%,
      rgba(0,0,0,0.66) 60%,
      rgba(0,0,0,0.82) 100%);
  transition: background 1200ms var(--ease-out);
}

/* Darker while the preloader is up, so the wordmark carries the screen. */
.is-preload .stage__veil {
  background:
    radial-gradient(120% 80% at 50% 45%,
      rgba(0,0,0,0.74) 0%,
      rgba(0,0,0,0.88) 60%,
      rgba(0,0,0,0.96) 100%);
}

/* ==========================================================================
   THE WORDMARK
   Rests in the corner. The preload state scales and shifts it to centre.
   ========================================================================== */
.mark {
  width: var(--mark-w);
  margin: 0 auto;
  line-height: 0;
}

@media (max-width: 800px) { :root { --mark-w: 84vw; } }

.mark__stack {
  position: relative;
  display: block;
  width: 100%;
  aspect-ratio: 3000 / 978;   /* the full painting, the layout */
}

/* ---------- the strokes ----------
   The painting cut into its brush strokes: separated by colour, then by
   connected region, so each blob of paint is its own image placed by its
   measured box (--x/--y/--w/--h, percent of the wordmark). Each flies in
   from a scattered start that home.js hands over as --sx/--sy/--sr/--ss. */
.mark__stroke {
  position: absolute;
  left: var(--x);
  top: var(--y);
  width: var(--w);
  height: var(--h);
  object-fit: fill;
  transform-origin: 50% 50%;
  will-change: transform, opacity;
}

.is-preload .mark__stroke {
  opacity: 0;
  filter: blur(14px) brightness(1.8);
  transform:
    translate3d(var(--sx, 0px), var(--sy, 0px), 0)
    rotate(var(--sr, 0deg))
    scale(var(--ss, 1));
}

/* In flight it is a hot smear; on arrival it snaps sharp. The overshoot on
   the transform is what makes it land like a thrown thing, and the blur
   clearing a beat later is what makes it read as paint settling. */
.is-preload .mark__stroke.is-in {
  opacity: 1;
  filter: blur(0) brightness(1);
  transform: translate3d(0, 0, 0) rotate(0deg) scale(1);
  transition:
    opacity 220ms linear,
    transform 950ms cubic-bezier(0.16, 1.36, 0.3, 1),
    filter 700ms cubic-bezier(0.2, 0.9, 0.2, 1) 250ms;
}

/* The beat. For one moment as the word completes, the veil lifts and the
   whole stage brightens, then settles as the film comes up. */
.is-struck .stage__veil {
  background:
    radial-gradient(120% 80% at 50% 45%,
      rgba(0,0,0,0.10) 0%,
      rgba(0,0,0,0.40) 60%,
      rgba(0,0,0,0.70) 100%);
  transition-duration: 160ms;
}

/* ---------- the boil ----------
   Measured on the reference: about a fifth of its pixels change every 80ms,
   forever, long after everything has settled. Hand-drawn artwork redrawn a
   few times a second. We have one painting rather than three, so the boil
   is made by nudging each stroke a hair on a stepped timeline. Sub-pixel
   amounts on purpose: it should read as the ink breathing, not as a wobble.
   Each stroke gets its own phase and duration from home.js. */
@keyframes boil {
  0%   { translate: 0 0;            rotate: 0deg;     scale: 1; }
  20%  { translate: 0.45% -0.7%;     rotate: 0.85deg;  scale: 1.012; }
  40%  { translate: -0.6% 0.35%;     rotate: -0.7deg;  scale: 0.991; }
  60%  { translate: 0.3% 0.6%;       rotate: 0.5deg;   scale: 1.008; }
  80%  { translate: -0.35% -0.4%;    rotate: -0.4deg;  scale: 0.995; }
  100% { translate: 0 0;             rotate: 0deg;     scale: 1; }
}

/* Only once the strokes have flown home, so the boil never fights the
   entrance. steps() is what gives it the redrawn look; a smooth tween
   would read as a slide. */
.is-settled .mark__stroke {
  animation: boil var(--boil, 900ms) steps(1, end) var(--boil-delay, 0ms) infinite;
}

/* ==========================================================================
   COPY
   ========================================================================== */
.stage__inner {
  flex: 1;
  display: grid;
  place-items: center;
  padding: clamp(3rem, 10vh, 7rem) var(--gut) clamp(2rem, 6vh, 4rem);
  text-align: center;
}

/* ---------- footer ---------- */
.foot {
  position: relative;
  z-index: 3;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 1rem 2rem;
  padding: clamp(1.25rem, 4vh, 2.25rem) var(--gut);
  padding-bottom: calc(clamp(1.25rem, 4vh, 2.25rem) + env(safe-area-inset-bottom));
  padding-left: calc(var(--gut) + env(safe-area-inset-left));
  padding-right: calc(var(--gut) + env(safe-area-inset-right));
  /* The page's own face, small and dim, sentence case. No second font,
     no tracking, no caps: a credit line, not a label. */
  font-family: var(--sans);
  font-size: 0.6875rem;
  font-weight: 400;
  letter-spacing: 0;
  text-transform: none;
  color: var(--dimmer);
  transition: opacity 800ms var(--ease-out) 1000ms;
}
.is-preload .foot { opacity: 0; transition: none; }

.foot__mark { margin: 0; }

.foot__social {
  display: flex;
  gap: clamp(1rem, 3vw, 2rem);
  list-style: none;
  margin: 0;
  padding: 0;
}
.foot__social a { color: var(--dimmer); text-decoration: none; transition: color 200ms ease; }
.foot__social a:hover,
.foot__social a:focus-visible { color: #b4b4b4; }

/* ==========================================================================
   GRAIN
   A layer of film grain over everything, jumping on a stepped timeline so
   it reads as running stock rather than a static texture. Tiled, so the
   whole thing costs one 43 KB image.
   ========================================================================== */
.grain {
  position: fixed;
  inset: -50%;                 /* oversized: the jump must never expose an edge */
  z-index: 60;
  pointer-events: none;
  background: url('/assets/img/grain.png') repeat;
  background-size: 180px 180px;
  opacity: 0.11;
  will-change: transform;
  animation: grain-jump 700ms steps(1, end) infinite;
}

@keyframes grain-jump {
  0%   { transform: translate3d(0, 0, 0); }
  10%  { transform: translate3d(-6%, -4%, 0); }
  20%  { transform: translate3d(4%, -7%, 0); }
  30%  { transform: translate3d(-3%, 5%, 0); }
  40%  { transform: translate3d(7%, 3%, 0); }
  50%  { transform: translate3d(-5%, -6%, 0); }
  60%  { transform: translate3d(3%, 7%, 0); }
  70%  { transform: translate3d(-7%, 2%, 0); }
  80%  { transform: translate3d(5%, -3%, 0); }
  90%  { transform: translate3d(-2%, 6%, 0); }
  100% { transform: translate3d(0, 0, 0); }
}

/* ==========================================================================
   CURSOR
   ========================================================================== */
.cursor { display: none; }

@media (hover: hover) and (pointer: fine) {
  .cursor {
    display: block;
    position: fixed;
    top: 0; left: 0;
    z-index: 70;
    width: 1.5rem; height: 1.5rem;
    margin: -0.75rem 0 0 -0.75rem;
    border: 1px solid var(--white);
    border-radius: 50%;
    pointer-events: none;
    opacity: 0;
    mix-blend-mode: difference;
    transition: opacity 200ms ease,
                width 200ms var(--ease-snap),
                height 200ms var(--ease-snap),
                margin 200ms var(--ease-snap),
                background-color 200ms ease;
  }
  .cursor[data-active] { opacity: 1; }
  .cursor[data-hover] {
    width: 3.5rem; height: 3.5rem;
    margin: -1.75rem 0 0 -1.75rem;
    background: var(--orange);
    border-color: var(--orange);
  }
}

/* ==========================================================================
   MOTION OFF
   No flight, no flicker. The finished page, immediately.
   ========================================================================== */
@media (prefers-reduced-motion: reduce) {
  .stage__video { display: none; }
  .stage__bg {
    background: #000 url('/assets/video/stage-poster.jpg') center / cover no-repeat;
  }

  .mark, .is-preload .mark { transform: none !important; transition: none !important; }
  .is-preload .mark__stroke,
  .mark__stroke { opacity: 1 !important; filter: none !important; transform: none !important; transition: none !important; }
  .is-struck .stage__veil { transition: none; }
  .is-preload .foot,
  .foot {
    opacity: 1 !important;
    transform: none !important;
    transition: none !important;
  }
  .cursor { display: none; }
  .grain { animation: none; }
  .is-settled .mark__stroke { animation: none; }
}
