/* ==========================================================================
   Greta Volungevičiūtė — home page hero (3D ring carousel)
   Design canvas: 1440 × 900. Requires css/base.css.
   ========================================================================== */

:root {
  --hero-h: 900px;

  /* ring geometry — radius is derived in js/main.js from these two values */
  --card-w: 184px;
  --card-h: 254px;
  --ring-r: 573px;
  --perspective: 1400px;
}

html, body {
  height: 100%;
  /* the hero is the whole page — nothing ever scrolls */
  overflow: hidden;
  overscroll-behavior: none;
}

/* --------------------------------------------------------------------------
   Fixed 1440 × 900 canvas, scaled to fit the screen (see fit() in main.js)
   -------------------------------------------------------------------------- */

#viewport {
  position: fixed;
  inset: 0;
  overflow: hidden;
  background: var(--bg);
}

#site {
  position: absolute;
  top: 0;
  left: 0;
  width: var(--design-w);
  height: var(--hero-h);
  transform-origin: 0 0;
  /* deliberately no will-change here: promoting this to its own layer makes
     the compositor keep a stale raster mid-zoom, which shows up as the canvas
     sliding sideways before the new transform lands */
}

/* The hero page never scrolls, so its footer is pinned to the foot of the
   canvas rather than following content. No rule above it either — the vignette
   already darkens that edge, and a hairline across it would cut the fade. */
.site-foot {
  position: absolute;
  z-index: 40;
  left: 0;
  bottom: 0;
  width: var(--design-w);
  padding: 0 48px 30px;        /* 48 matches the header's gutter */
  border-top: 0;
}

/* --------------------------------------------------------------------------
   Hero
   -------------------------------------------------------------------------- */

.hero {
  position: relative;
  width: var(--design-w);
  height: var(--hero-h);
  overflow: hidden;
  background: var(--bg);
}

/* soft light pooling behind the ring.
   Layer 1 is the oval that lifts the cards off the background: wide and flat so
   it hugs the row rather than haloing it, and stepped down in three stops so the
   falloff reads as an ombre instead of a hard-edged blob. Layer 2 is a much
   larger, cooler wash that keeps the oval from looking like a pasted-on shape. */
.hero__glow {
  position: absolute;
  inset: 0;
  pointer-events: none;
  background:
    radial-gradient(ellipse 470px 185px at 50% 47%,
      rgba(255, 255, 255, 0.10) 0%,
      rgba(255, 255, 255, 0.065) 34%,
      rgba(255, 255, 255, 0.025) 62%,
      transparent 80%),
    radial-gradient(1100px 700px at 50% 40%, rgba(120, 130, 160, 0.05), transparent 72%);
}

/* film grain — kills the banding the big soft radial gradients would show */
.hero::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 25;
  pointer-events: none;
  opacity: 0.16;
  mix-blend-mode: overlay;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='3'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23n)'/%3E%3C/svg%3E");
}

/* vignette so cards dissolve into the dark at the edges */
.hero::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 30;
  pointer-events: none;
  background:
    linear-gradient(90deg, var(--bg) 0%, rgba(10, 10, 10, 0) 16%, rgba(10, 10, 10, 0) 84%, var(--bg) 100%),
    linear-gradient(180deg, rgba(10, 10, 10, 0.85) 0%, rgba(10, 10, 10, 0) 18%, rgba(10, 10, 10, 0) 74%, var(--bg) 100%);
}

/* --------------------------------------------------------------------------
   3D stage + ring
   -------------------------------------------------------------------------- */

.stage {
  position: absolute;
  inset: 0;
  z-index: 20;
  display: flex;
  align-items: center;
  justify-content: center;
  perspective: var(--perspective);
  perspective-origin: 50% 40%;
  cursor: grab;
  touch-action: none;
  -webkit-user-select: none;
  user-select: none;
}

.stage.is-dragging { cursor: grabbing; }

.ring {
  position: relative;
  width: var(--card-w);
  height: var(--card-h);
  margin-top: 0; /* set from JS — cancels the drift caused by CFG.tiltX */
  transform-style: preserve-3d;
  will-change: transform;
}

/* --------------------------------------------------------------------------
   Cards
   -------------------------------------------------------------------------- */

.card {
  position: absolute;
  top: 0;
  left: 0;
  width: var(--card-w);
  height: var(--card-h);
  transform-style: preserve-3d;
  backface-visibility: hidden;
}

.card__inner {
  position: relative;
  display: block;
  width: 100%;
  height: 100%;
  overflow: hidden;
  background: var(--bg-lift);
  box-shadow: 0 30px 60px -30px rgba(0, 0, 0, 0.9);
  transition: transform 0.6s cubic-bezier(0.16, 1, 0.3, 1);
}

.card__media {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  transform: scale(1.001);
  transition: transform 0.9s cubic-bezier(0.16, 1, 0.3, 1);
}

/* Scrim so the label stays readable on any artwork. It starts higher and
   finishes darker than it used to because the label is now the project's whole
   discipline line rather than one word — three lines of it on the longest, and
   the top of that block sits about a fifth of the way up the card, over sky in
   the Katrick render and over bare newsprint in the Rangers spread. */
.card__scrim {
  position: absolute;
  inset: 0;
  background: linear-gradient(180deg, rgba(0, 0, 0, 0) 38%, rgba(0, 0, 0, 0.72) 100%);
  opacity: 0.9;
  pointer-events: none;
}

/* depth shading — opacity is driven per-frame from JS via --shade */
.card__shade {
  position: absolute;
  inset: 0;
  background: var(--bg);
  opacity: var(--shade, 0);
  pointer-events: none;
}

/* The label carries the project's discipline line from work.html, which for
   Crabshakk and Katrick is four disciplines long. Anchored at the bottom, so
   it grows upward and one-line and three-line cards still share a floor.

   9px rather than 10, and 0.12em of tracking rather than 0.16: measured in
   Archivo at the label's real 156px width, that takes the longest line from
   four wrapped lines (52px) to three (35px). Loosening the tracking further
   buys nothing — below 0.12em the breaks stop moving, because what forces them
   is the length of "Illustration" and "Photography", not the space between
   letters. Going under 9px saves 2px and costs legibility at a distance. */
.card__label {
  position: absolute;
  left: 14px;
  bottom: 13px;
  right: 14px;
  z-index: 2;
  font-size: 9px;
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  line-height: 1.3;
  color: var(--ink);
  text-shadow: 0 1px 8px rgba(0, 0, 0, 0.7);
  pointer-events: none;
}

/* The project name stays out of the way until the card is hovered — it is the
   reward for stopping on a card, so it is set a half-pixel larger than the
   disciplines above it and nearly at full brightness rather than the whisper it
   used to be. The longest name, KATRICK TECHNOLOGIES, measures 140px here
   against the label's 156px box: enough clearance that it cannot fall to a
   second line on a machine whose font metrics round the other way. */
.card__meta {
  display: block;
  overflow: hidden;
  max-height: 0;
  opacity: 0;
  font-size: 9.5px;
  font-style: normal;
  font-weight: 600;
  letter-spacing: 0.1em;
  color: rgba(244, 243, 241, 0.92);
  transition: max-height 0.5s cubic-bezier(0.16, 1, 0.3, 1), opacity 0.4s ease;
}

/* hover — only meaningful on the cards facing the viewer */
.card.is-front .card__inner:hover { transform: translateY(-6px); }
.card.is-front .card__inner:hover .card__media { transform: scale(1.06); }
.card.is-front .card__inner:hover .card__scrim { opacity: 1; }

/* 26px, not the name's own 13: a cap below the content height clips it, and
   these names are set by hand — the ceiling should survive a longer one. */
.card.is-front .card__inner:hover .card__meta {
  max-height: 26px;
  opacity: 1;
  margin-top: 5px;
}

.card:not(.is-front) .card__inner { pointer-events: none; }

/* --------------------------------------------------------------------------
   Phones

   fit() switches from contain to cover below this width, so the canvas is now
   wider than the screen and its left and right edges are cut off. The ring is
   centred so it survives that, but the header is not — it lives at the canvas
   margins and would be cropped away with them.

   So the header undoes the canvas transform: scaled by 1/S it renders at true
   pixel size, and offset by the canvas position it lands against the real
   viewport. --fit-scale / --fit-x / --fit-y come from fit() in js/main.js.
   -------------------------------------------------------------------------- */

@media (max-width: 760px) {
  .header {
    top: calc(var(--fit-y, 0) * -1px / var(--fit-scale, 1));
    left: calc(var(--fit-x, 0) * -1px / var(--fit-scale, 1));
    width: 100vw;
    height: 58px;
    padding: 0 14px;
    gap: 10px;
    transform: scale(calc(1 / var(--fit-scale, 1)));
    transform-origin: 0 0;
  }

  /* Measured, not guessed: the wordmark is 195.7px at 12px and the three nav
     words are 112.6px at 10px. With 28px of padding and a 12px gap that is
     348px, so the header still holds together on a 360px phone. */
  .brand { font-size: 12px; }

  .nav { gap: 12px; }
  .nav a { font-size: 10px; }

  /* The footer has the header's problem and takes the header's answer, with
     one extra term. Left and scale are the same undo. The bottom is not: the
     canvas bottom sits at fitY + 900·S in real pixels, the screen bottom at
     100vh, and the difference between them — divided by S to get back into
     canvas units — is how far up from the canvas edge the footer has to sit to
     land on the screen edge. transform-origin pins the bottom-left corner so
     the unscaling grows the box upward instead of pushing it off. */
  .site-foot {
    left: calc(var(--fit-x, 0) * -1px / var(--fit-scale, 1));
    bottom: calc(
      (var(--fit-y, 0) * 1px + var(--hero-h) * var(--fit-scale, 1) - 100vh)
      / var(--fit-scale, 1)
    );
    width: 100vw;
    padding: 0 14px 14px;
    transform: scale(calc(1 / var(--fit-scale, 1)));
    transform-origin: 0 100%;
  }

  /* the vignette is tuned to the 1440 frame; at cover scale its side bands
     would eat most of a phone screen */
  .hero::after {
    background:
      linear-gradient(180deg, rgba(10, 10, 10, 0.8) 0%, rgba(10, 10, 10, 0) 16%, rgba(10, 10, 10, 0) 78%, var(--bg) 100%);
  }
}

/* --------------------------------------------------------------------------
   Reduced motion
   -------------------------------------------------------------------------- */

@media (prefers-reduced-motion: reduce) {
  .card__inner,
  .card__media,
  .nav a,
  .nav a::after { transition: none; }
}
