/* polygonals.com — bespoke gallery, hero animation, lightbox and player.
 * Layered on top of stylesheet.css (the framework copy). Everything here is the
 * "art" of the site: the letter-by-letter wordmark reveal, the angled field of
 * sliding video tiles, and the lightbox that flies a tile into the foreground
 * to play it. Tokens come from stylesheet.css (--accent, --bg, …). */

/* ---------- 1. HERO ---------------------------------------------------- */
.hero {
  position: relative;
  min-height: 92vh;
  /* break out of main's centred column to the full viewport width */
  width: 100vw;
  margin-left: calc(50% - 50vw);
  margin-right: calc(50% - 50vw);
  margin-top: -44px;                       /* pull up under the sticky header */
  padding: 0;
  overflow: hidden;
  box-shadow: none; clip-path: none;       /* cancel the section-band background */
  display: flex; align-items: center; justify-content: center;
  isolation: isolate;
  background:
    radial-gradient(70% 60% at 50% 42%, rgba(17,250,156,0.07), transparent 70%),
    var(--bg-2);
}
.hero > * { max-width: none; width: auto; }   /* override the section centring for the hero */

/* edge fade as a cheap overlay vignette (sits above the tiles, below the logo)
   instead of a mask-image on the field — a plain gradient is far cheaper to
   paint than masking a large, constantly-moving layer */
.hero::after {
  content: ""; position: absolute; inset: 0; z-index: 1; pointer-events: none;
  background: radial-gradient(120% 110% at 50% 46%, transparent 42%, var(--bg-2) 86%);
}

/* the angled, oversized field of sliding tiles sits behind everything */
.tilefield {
  position: absolute; inset: -22% -12%;
  z-index: 0;
  display: flex; flex-direction: column; justify-content: center; gap: 18px;
  transform: rotate(-9deg) scale(1.08);
  transform-origin: center;
  /* dimming lives on each .tile (not here) so a hovered tile can reach full
     opacity — group opacity on the field would otherwise cap every child */
}

/* A row = a WRAPPER that slides in once from the edge (ease-out, so it settles
   smoothly to the loop's constant speed) wrapping an INNER that runs the
   seamless infinite loop (two copies → translateX 0→-50%). All motion is CSS on
   the compositor: smooth at any frame rate, and the -50% uses the exact rendered
   width so there's no wrap drift / forward skip. */
/* Three nested layers so both directions share ONE animation and are a TRUE
   mirror of each other:
     .tilerow        — for .rev, a static scaleX(-1) mirror (never animated)
     .tilerow-slide  — the one-time ease-out slide-in from the edge
     .tilerow-inner  — the seamless infinite loop (two copies, translateX 0→-50%)
   Left-anchored, the slide comes in from the RIGHT and the loop cruises LEFT;
   the leading (left) edge decelerates and settles at the left screen edge, so
   only the ease-out's slow tail is on screen — a smooth entry. A .rev row is
   that exact animation flipped horizontally, so it reads as "enter from the
   left, cruise right". For the mirror to be TRUE in the viewport the anchor
   must flip too: .rev is RIGHT-anchored (align-self: flex-end) so its leading
   (right) edge settles at the right screen edge — the mirror of the left case.
   (Left-anchoring a .rev row puts that settle point far off the right side, so
   only the ease-out's fast opening is seen: it whips in and scrolls off.)
   Thumbnails are un-mirrored so images read normally.
   The slide moves a FULL row-width (112%) so the row starts entirely off-screen
   (a row wider than the viewport only clears by shifting its whole width); a
   strong ease-out spends that off-screen distance almost instantly, so only the
   last sliver is seen — a quick entry decelerating to the slow cruise speed. */
.tilerow { width: max-content; }
.tilerow.rev { transform: scaleX(-1); transform-origin: center; align-self: flex-end; }
.tilerow.rev .tile img { transform: scaleX(-1); }   /* keep images the right way round */
.tilerow-slide {
  width: max-content;
  animation: tile-in-right 3.4s cubic-bezier(0.13, 0.86, 0.2, 1) var(--in-delay, 0s) both;
}
.tilerow-inner {
  display: flex; width: max-content;
  will-change: transform;
  animation: tile-loop var(--dur, 240s) linear infinite;
}
/* Spacing is a per-tile margin, NOT flex `gap`. `gap` sits only BETWEEN items,
   so the last tile has no trailing gap — that makes the row 89 gaps wide while a
   clean repeat needs 90, so translateX(-50%) lands half a gap (9px) short of the
   loop point and every wrap jumps by 9px. A trailing margin on every tile makes
   both copies exactly periodic, so -50% is a perfect seam. */
/* Hovering a row freezes just that row (pure CSS, so it works even when the
   window is unfocused); the tile you touch stops sliding immediately, which is
   what makes the hover rock-solid. Only active once the intro slide-in has
   finished (.hover-on). The play button freezes the whole field. */
.tilefield.hover-on .tilerow:hover .tilerow-slide,
.tilefield.hover-on .tilerow:hover .tilerow-inner,
.field-paused .tilerow-slide, .field-paused .tilerow-inner { animation-play-state: paused; }

@keyframes tile-loop     { from { transform: translateX(0); }    to { transform: translateX(-50%); } }
@keyframes tile-in-right { from { transform: translateX(112%); } to { transform: translateX(0); } }

.tile {
  position: relative; flex: 0 0 auto;
  width: clamp(120px, 15vw, 188px); aspect-ratio: 1;
  margin-right: 18px;                            /* the row gap — as a margin so every tile (incl. the last) carries it → seamless -50% loop */
  border-radius: 14px; overflow: hidden;
  background: var(--surface);
  border: 1px solid rgba(167,121,255,0.22);
  box-shadow: 0 8px 22px -12px #000;
  cursor: pointer;
  opacity: 0.55;                                 /* faded at rest; full on hover */
  transition: transform 0.22s cubic-bezier(.2,.8,.2,1), box-shadow 0.22s, border-color 0.22s, opacity 0.22s;
}
.tile img { width: 100%; height: 100%; object-fit: cover; display: block; }
.tile::after {   /* a soft violet veil so the field reads as a texture, lifted on hover */
  content: ""; position: absolute; inset: 0;
  background: linear-gradient(180deg, rgba(19,0,51,0.12), rgba(19,0,51,0.42));
  transition: opacity 0.25s; opacity: 1;
}
/* highlight is native :hover — it re-evaluates on the compositor, clears the
   moment the pointer leaves (even off the window edge), and works unfocused */
.tilefield.hover-on .tile:hover {
  transform: scale(1.1); z-index: 5; opacity: 1;
  border-color: var(--accent);
  box-shadow: 0 10px 26px -12px #000, 0 0 14px rgba(17,250,156,0.4);   /* lighter shadow, no filter → cheap paint */
}
.tilefield.hover-on .tile:hover::after { opacity: 0; }

/* the wordmark stage, centred over the field — a dark card so the logo, tagline
   and buttons stay legible above the moving tiles */
.logo-stage {
  position: relative; z-index: 2;
  display: flex; flex-direction: column; align-items: center; gap: 22px;
  text-align: center; padding: 40px 52px;
  background: rgba(10, 0, 30, 0.68);            /* deep purple, near-black */
  border: 1px solid rgba(167, 121, 255, 0.24);  /* faint violet edge */
  border-radius: 24px;
  box-shadow: 0 24px 60px -24px #000, inset 0 0 0 1px rgba(255,255,255,0.02);
  backdrop-filter: blur(7px); -webkit-backdrop-filter: blur(7px);
}

/* the wordmark — the actual angular logo letterforms, vectorised from the brand
   PNG into an inline SVG; each letter is its own <g class="gly"> so it can still
   animate in independently */
/* nav brand: the cropped logo PNG sitting next to the octagon icon */
.brand-word { height: 44px; width: auto; display: block; filter: drop-shadow(0 0 5px rgba(17,250,156,0.45)); }

.wordmark-h { margin: 0; line-height: 0; }
.wordmark {
  display: block; width: clamp(240px, 46vw, 480px); height: auto; overflow: visible;
  color: var(--accent);
  filter: drop-shadow(0 0 26px rgba(17,250,156,0.45));
  transform-origin: 50% 60%;
  animation: wm-tumble 1.2s ease 1.5s both;
}
.gly {
  transform-box: fill-box; transform-origin: 50% 55%;   /* per-letter, around its own centre */
  opacity: 0;
  animation: var(--anim, gly-grow) 0.7s cubic-bezier(.2,.9,.2,1.1) both;
  animation-delay: calc(var(--i) * 0.12s);
}

/* entrance flavours, cycled across the letters (translates use % so they scale
   with the letterforms now that they're vectors) */
@keyframes gly-grow   { 0%{opacity:0; transform:scale(0) rotate(-18deg);} 100%{opacity:1; transform:none;} }
@keyframes gly-shrink { 0%{opacity:0; transform:scale(2.6); filter:blur(6px);} 100%{opacity:1; transform:none; filter:blur(0);} }
@keyframes gly-spin   { 0%{opacity:0; transform:rotate(-220deg) scale(.2);} 100%{opacity:1; transform:none;} }
@keyframes gly-drop   { 0%{opacity:0; transform:translateY(-80%) rotate(12deg);} 70%{opacity:1;} 100%{opacity:1; transform:none;} }
@keyframes gly-rise   { 0%{opacity:0; transform:translateY(80%) scale(.7);} 100%{opacity:1; transform:none;} }

/* once the letters are in, the whole word gives a slow grow + rotate */
@keyframes wm-tumble {
  0%   { transform: rotate(0deg) scale(1); }
  55%  { transform: rotate(-5deg) scale(1.12); }
  100% { transform: rotate(0deg) scale(1.04); }
}
.hero-tag {
  font-size: clamp(0.95rem, 2.2vw, 1.2rem); color: var(--fg);
  letter-spacing: 0.36em; text-transform: uppercase; font-weight: 500;
  opacity: 0; animation: fade-up 0.8s ease 2.0s both;     /* after the letters */
}
.hero-cta { opacity: 0; animation: fade-up 0.8s ease 2.4s both; }   /* then the buttons */
@keyframes fade-up { from { opacity: 0; transform: translateY(12px); } to { opacity: 1; transform: none; } }

/* a scroll hint pinned to the bottom of the hero */
.hero-hint {
  position: absolute; bottom: 22px; left: 50%; transform: translateX(-50%);
  z-index: 2; color: var(--muted); font-size: 0.8rem; letter-spacing: 0.2em; text-transform: uppercase;
  display: flex; flex-direction: column; align-items: center; gap: 6px;
  opacity: 0; animation: fade-up 0.9s ease 3.0s both;
}
.hero-hint::after { content: "▾"; font-size: 1.1rem; color: var(--accent); animation: bob 1.6s ease-in-out infinite; }
@keyframes bob { 0%,100%{ transform: translateY(0);} 50%{ transform: translateY(5px);} }

/* pause / play toggle for the tile field, bottom-right of the hero */
.field-toggle, .field-toggle:hover, .field-toggle:focus-visible {
  position: absolute; bottom: 20px; right: 20px; z-index: 6;
  width: 44px; height: 44px; padding: 0; border: 0; border-radius: 50%;
  background: transparent; box-shadow: none; cursor: pointer;
  filter: drop-shadow(0 2px 7px rgba(0,0,0,0.6));
}
.field-toggle { opacity: 0; transform: none; transition: opacity 0.5s ease, transform 0.15s ease; }
.hero.revealed .field-toggle { opacity: 0.8; }
.field-toggle:hover { opacity: 1; transform: scale(1.09); }
.field-toggle svg { width: 100%; height: 100%; display: block; }
.field-toggle .icon-play { display: none; }
.field-toggle.paused .icon-pause { display: none; }
.field-toggle.paused .icon-play { display: block; }

/* a skip-intro control, top-right of the hero */
.skip-intro {
  position: absolute; top: 16px; right: 18px; z-index: 6;
  font-size: 0.78rem; letter-spacing: 0.12em; text-transform: uppercase;
  color: var(--muted); background: rgba(13,0,34,0.5); border: 1px solid var(--border);
  padding: 5px 12px; border-radius: var(--radius-pill); cursor: pointer; box-shadow: none;
}
.skip-intro:hover { color: var(--accent-hover); border-color: var(--accent); background: var(--accent-tint); transform: none; box-shadow: none; }
.hero.revealed .skip-intro { display: none; }

/* Skip: fast-forward the one-time intro animations to their end state (the loop
   on .tilerow-inner keeps running). */
.hero.skipped .gly,
.hero.skipped .hero-tag,
.hero.skipped .hero-cta,
.hero.skipped .hero-hint { animation: none; opacity: 1; transform: none; }
.hero.skipped .wordmark { animation: none; }
.hero.skipped .tilerow-slide { animation: none; transform: none; }

/* ---------- 2. VIDEO GRID (the #videos section) ----------------------- */
.video-grid { display: grid; gap: 20px; grid-template-columns: repeat(auto-fill, minmax(230px, 1fr)); width: 100%; }
.vcard {
  display: flex; flex-direction: column; gap: 0; padding: 0; overflow: hidden;
  background: var(--surface); border: 1px solid var(--border-strong); border-radius: var(--radius);
  box-shadow: var(--shadow-sm); cursor: pointer;
  transition: transform 0.16s, box-shadow 0.2s, border-color 0.2s;
}
.vcard:hover { transform: translateY(-3px); border-color: var(--accent); box-shadow: var(--shadow-md), var(--glow-soft); }
.vcard-thumb { position: relative; aspect-ratio: 1; overflow: hidden; background: var(--bg-2); }
.vcard-thumb img { width: 100%; height: 100%; object-fit: cover; transition: transform 0.3s; }
.vcard:hover .vcard-thumb img { transform: scale(1.06); }   /* composite-only, no filter repaint */
.vcard-body { padding: 13px 15px 16px; display: flex; flex-direction: column; gap: 4px; }
.vcard-title { font-weight: 650; color: var(--heading); font-size: 0.98rem; line-height: 1.3;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
.vcard-date { font-size: 0.78rem; color: var(--muted); }

/* the Specials strip: a couple of wide (16:9) behind-the-scenes cards */
.specials-grid { display: grid; gap: 20px; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); width: 100%; }
.specials-grid .vcard-thumb { aspect-ratio: 16 / 9; }
@media (min-width: 760px) { .specials-grid { grid-template-columns: repeat(2, 1fr); } }

/* the round green play badge, reused on tiles, cards and the lightbox media */
.play-badge {
  position: absolute; inset: 0; margin: auto; width: 56px; height: 56px;
  display: grid; place-items: center; border-radius: 50%;
  background: rgba(17,250,156,0.92); color: var(--accent-fg);
  box-shadow: var(--glow); border: 0;
  opacity: 0; transform: scale(0.8); transition: opacity 0.2s, transform 0.2s, background 0.15s;
  cursor: pointer; padding: 0;
}
.vcard:hover .play-badge, .lb-media:hover .play-badge { opacity: 1; transform: scale(1); }
.play-badge:hover { background: var(--accent-hover); transform: scale(1.08); box-shadow: var(--glow); }
.play-badge svg { width: 22px; height: 22px; margin-left: 3px; }
.lb-media .play-badge { width: 76px; height: 76px; opacity: 1; transform: scale(1); }
.lb-media .play-badge svg { width: 30px; height: 30px; }

/* ---------- 3. LIGHTBOX + PLAYER -------------------------------------- */
.lightbox {
  position: fixed; inset: 0; z-index: 100;
  display: grid; place-items: center; padding: 24px;
  background: rgba(7,0,20,0); -webkit-backdrop-filter: blur(0px); backdrop-filter: blur(0px);
  opacity: 0; visibility: hidden;
  transition: opacity 0.3s ease, background 0.35s ease, backdrop-filter 0.35s ease, visibility 0.35s;
}
.lightbox.open {
  opacity: 1; visibility: visible;
  background: rgba(7,0,20,0.78); -webkit-backdrop-filter: blur(8px); backdrop-filter: blur(8px);
}
/* the frame wraps the panel + the close button; the close can sit OUTSIDE the
   panel because the frame doesn't clip. Width tracks viewport height so the
   square video + body always fit. */
.lb-frame {
  position: relative; width: min(640px, 94vw, 62vh);
  transform: scale(0.86) translateY(14px); opacity: 0;
  transition: transform 0.4s cubic-bezier(.2,.8,.2,1), opacity 0.3s ease;
}
.lightbox.open .lb-frame { transform: none; opacity: 1; }
.lb-panel {
  position: relative; width: 100%; max-height: calc(100vh - 48px); overflow-y: auto;
  background: var(--surface); border: 1px solid var(--border-strong); border-radius: var(--radius);
  box-shadow: var(--shadow-lg), 0 0 60px -20px rgba(17,250,156,0.4);
  padding: 0;
}

/* close button: always present, hovering just outside the popup's top-right corner */
.lb-close {
  position: absolute; top: -16px; right: -16px; z-index: 6;
  width: 40px; height: 40px; border-radius: 50%; padding: 0;
  background: var(--accent); color: var(--accent-fg);
  border: 2px solid var(--bg-2);
  font-size: 1rem; line-height: 1; display: grid; place-items: center;
  box-shadow: var(--glow-soft), var(--shadow-md);
  transition: background 0.15s, transform 0.12s, box-shadow 0.2s;
}
.lb-close:hover { background: var(--accent-hover); transform: scale(1.08); box-shadow: var(--glow); }

/* the media stage: a square viewer flush to the top — the videos are square and
   the player fills it edge to edge, pure picture, no chrome */
.lb-media {
  position: relative; width: 100%; aspect-ratio: 1;
  background: #000; overflow: hidden; border-radius: var(--radius) var(--radius) 0 0;
}
.lb-media img { width: 100%; height: 100%; object-fit: cover; }
/* Overscale the player vertically and clip: YouTube draws its title (top) and
   "watch / more videos" branding (bottom) at the iframe's extreme edges, so a
   taller iframe letterboxes the video and the chrome falls into the clipped
   margins — the video itself stays whole and fills the box exactly. */
.lb-media iframe, .lb-media #yt-player {
  position: absolute; left: 0; top: -56px; width: 100%; height: calc(100% + 112px); border: 0;
  /* the pointer never reaches the player, so YouTube shows no hover chrome or
     centre play/pause button — playback is driven entirely through the API */
  pointer-events: none;
}
/* the wide (rectangular 16:9) variant — used by the Specials videos */
.lightbox.wide .lb-frame { width: min(1000px, 94vw, 150vh); }
.lightbox.wide .lb-media { aspect-ratio: 16 / 9; }

.lb-body { padding: 20px 24px 24px; display: flex; flex-direction: column; gap: 12px; }
.lb-title { font-size: 1.3rem; color: var(--heading); font-weight: 700; line-height: 1.25; }
.lb-meta { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; color: var(--muted); font-size: 0.85rem; }
.lb-desc { color: var(--fg); white-space: pre-wrap; line-height: 1.6; font-size: 0.95rem;
  max-height: 8.5em; overflow-y: auto; padding-right: 6px; }
.lb-desc a { word-break: break-word; }
.lb-nav { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; margin-top: 4px; }
.lb-nav .stretch { flex: 1 1 0; }

/* a subtle keyed swap of the text when navigating prev/next/random (the video
   itself swaps smoothly via the player, so it isn't animated) */
.lb-panel.swapping .lb-body { animation: lb-swap 0.32s ease; }
@keyframes lb-swap { 0% { opacity: 0; transform: translateX(var(--from, 16px)); } 100% { opacity: 1; transform: none; } }

/* ===== small / phone screens: the lightbox fills the screen ==========
   app.js (layoutLightbox) picks the mode by measuring whether the details
   panel actually fits next to / below the video:
     .lb-full       — fill the whole screen (no floating frame)
     .lb-col        — portrait: video on top, details below
     .lb-row        — landscape: video on the left, details on the right
     .lb-only       — no room for details: just the centred video           */
.lightbox.lb-full { padding: 0; }
.lightbox.lb-full .lb-frame { width: 100vw; height: 100vh; height: 100dvh; max-width: none; }
.lightbox.lb-full .lb-panel {
  width: 100%; height: 100%; max-height: none; overflow: hidden;
  border-radius: 0; border-width: 0; box-shadow: none; background: #000; display: flex;
}
.lightbox.lb-full .lb-media { border-radius: 0; }
.lightbox.lb-full .lb-body { background: var(--surface); overflow-y: auto; min-width: 0; min-height: 0; }
.lightbox.lb-full .lb-desc { max-height: none; }
.lightbox.lb-full .lb-close { top: 12px; right: 12px; }   /* inside, top-right of the screen */

/* portrait — video on top, details below */
.lightbox.lb-full.lb-col .lb-panel { flex-direction: column; }
.lightbox.lb-full.lb-col .lb-media { flex: 0 0 auto; width: 100%; }
.lightbox.lb-full.lb-col .lb-body { flex: 1 1 auto; }

/* landscape — video on the left, details on the right */
.lightbox.lb-full.lb-row .lb-panel { flex-direction: row; }
.lightbox.lb-full.lb-row .lb-media { flex: 0 0 auto; height: 100%; width: auto; }
.lightbox.lb-full.lb-row .lb-body { flex: 1 1 auto; }
.lightbox.lb-full.lb-row .lb-title { padding-right: 46px; }   /* keep the title clear of the close button */

/* no room for the details — centre the video, fill the screen, details hidden */
.lightbox.lb-full.lb-only .lb-panel { align-items: center; justify-content: center; }
.lightbox.lb-full.lb-only .lb-body { display: none; }
.lightbox.lb-full.lb-only .lb-media { width: min(100vw, 100dvh); height: auto; }
.lightbox.lb-full.lb-only.wide .lb-media { width: min(100vw, calc(100dvh * 16 / 9)); height: auto; }

@media (max-width: 560px) {
  .lb-body { padding: 16px; }
  .lb-title { font-size: 1.1rem; }
  .hero { min-height: 88vh; }
}
