/* ============================================================
   Listing gallery — page styles
   Loader morph + revealed page shell + WebGL background canvas.
   Tokens come from ds-tokens.css (load fonts.css, ds-tokens.css,
   then this file).
   ============================================================ */

/* ---- Reset / page shell ---- */
* { margin: 0; padding: 0; box-sizing: border-box; }
html, body { height: 100%; }
body {
  background: var(--color-bg);
  color: var(--color-fg);
  font-family: var(--font-primary);
  overflow: hidden;
}

/* ---- WebGL background (water lines) ---- */
#bg {
  position: fixed; inset: 0;
  z-index: 0;
  display: block;
  pointer-events: none;
}

/* ---- Revealed page content ---- */
/* page is a non-interactive overlay so wheel/drag pass through to #gallery;
   interactive children (nav) re-enable pointer events. */
#page { position: fixed; inset: 0; z-index: 10; pointer-events: none; }

.nav {
  position: fixed; top: 0; left: 0; right: 0;
  display: flex; justify-content: space-between; align-items: center;
  padding: var(--space-4) var(--margin-page);
  z-index: 20;
  pointer-events: auto;
}
.nav .wordmark {
  font-size: var(--text-nav); font-weight: var(--weight-regular);
  letter-spacing: var(--tracking-nav);
}
.nav .links { display: flex; gap: var(--space-5); }
.nav .links a {
  font-size: var(--text-nav); font-weight: var(--weight-regular);
  letter-spacing: var(--tracking-caps); text-transform: uppercase;
  color: var(--color-fg); text-decoration: none;
}

/* Brand corner → home (mirrors base.html; standalone page needs its own rule) */
.brand-mark {
  position: fixed; top: var(--space-4); left: var(--margin-page);
  z-index: 40; pointer-events: auto;
  font-size: var(--text-nav); font-weight: var(--weight-regular);
  text-transform: uppercase; letter-spacing: var(--tracking-caps);
  color: var(--color-fg); text-decoration: none;
}

.stage-center {
  position: absolute; inset: 0;
  display: grid; place-items: center;
  text-align: center;
}
.stage-center .ds-hero {
  font-size: clamp(40px, 7vw, 96px);
  font-weight: var(--weight-light);
  letter-spacing: var(--tracking-display);
  line-height: var(--lh-display);
}
.stage-center .doodle {
  display: block; margin-top: var(--space-3);
  font-family: var(--font-doodle); font-weight: 700;
  font-size: 22px; color: var(--color-muted);
  transform: rotate(-2deg);
}

.meta {
  position: fixed; left: var(--margin-page); bottom: var(--space-5);
  z-index: 20;
}
.meta .label {
  font-size: var(--text-caption); text-transform: uppercase;
  letter-spacing: var(--tracking-caps); color: var(--color-muted);
}
.meta .title { font-size: var(--text-heading); font-weight: var(--weight-light); margin-top: var(--space-2); }

/* ---- Cinematic preloader (mirrors the "/" landing intro) ---- */
#preloader {
  position: fixed; inset: 0; z-index: 1000;
  background: var(--color-fg);
  color: var(--color-bg);
  display: flex; flex-direction: column;
  align-items: center; justify-content: center;
  will-change: transform;
}
html.no-js #preloader { display: none; }   /* no JS → no intro, show page */

.pl-block { display: flex; flex-direction: column; align-items: center; gap: var(--space-2); }
.pl-name-wrap, .pl-tag-wrap { display: block; overflow: hidden; }
.pl-name {
  display: block;
  font-size: clamp(28px, 5vw, 64px);
  font-weight: var(--weight-light);
  letter-spacing: var(--tracking-display);
  line-height: 1;
}
.pl-tag {
  display: block;
  font-size: clamp(13px, 1.4vw, 18px);
  font-weight: var(--weight-light);
  text-transform: uppercase;
  letter-spacing: var(--tracking-caps);
  color: var(--dark-muted);
}
.pl-count {
  position: absolute;
  right: var(--margin-page); bottom: var(--margin-page);
  font-size: var(--text-nav);
  letter-spacing: var(--tracking-nav);
  font-variant-numeric: tabular-nums;
  color: var(--dark-muted);
}

/* ============================================================
   3D frame gallery
   ============================================================ */
#gallery {
  position: fixed; inset: 0; z-index: 5;
  perspective: 1500px;
  overflow: hidden;
  cursor: grab;
  user-select: none;
  -webkit-user-select: none;
}
#gallery.grabbing { cursor: grabbing; }

#stage {
  position: absolute; inset: 0;
  transform-style: preserve-3d;
}

.frame {
  position: absolute; top: 50%; left: 50%;
  width: min(60vw, 880px);
  aspect-ratio: 16 / 10;
  transform-style: preserve-3d;
  backface-visibility: hidden;
  will-change: transform, opacity;
  pointer-events: none;            /* .frame-hit re-enables for the front frame */
}

/* windowed frame: titlebar + live iframe + transparent hit overlay */
.frame-inner {
  position: absolute; inset: 0;
  background: var(--color-fg);
  color: var(--color-bg);
  border: 1px solid var(--color-fg);   /* subtle black edge against the light bg */
  border-radius: 12px;
  overflow: hidden;
  display: flex; flex-direction: column;
}
.frame-bar {
  flex: 0 0 auto;
  display: flex; align-items: center; justify-content: space-between; gap: var(--space-3);
  padding: var(--space-2) var(--space-4);
}
.frame-name {
  font-size: var(--text-caption);
  text-transform: uppercase; letter-spacing: var(--tracking-caps);
  color: var(--dark-muted);
}
.frame-cta {
  display: flex; align-items: center; gap: var(--space-2);
  font-size: var(--text-nav);
  text-transform: uppercase; letter-spacing: var(--tracking-caps);
}
.frame-embed {
  flex: 1 1 auto;
  width: 100%; border: 0;
  background: var(--color-bg);
  pointer-events: none;            /* clicks/drags go to .frame-hit, not the app */
}
.frame-hit {
  position: absolute; inset: 0;
  pointer-events: none;            /* JS sets 'auto' only on the front frame */
}

/* empty state — no hosted apps */
.empty-state {
  position: absolute; inset: 0;
  display: grid; place-items: center;
  text-align: center;
  font-size: clamp(28px, 4vw, 56px);
  font-weight: var(--weight-light);
  letter-spacing: var(--tracking-display);
  color: var(--color-fg);
}

/* Dock navbar styles now live in dock.css (shared with base.html). */

/* ============================================================
   Custom dot cursor
   Outer #cursor is positioned by GSAP (x/y + xPercent/yPercent).
   Inner .cursor__disc owns size/colour so its transform never
   fights GSAP. mix-blend-mode makes it contrast whatever's beneath.
   ============================================================ */
body.has-dot { cursor: none; }
body.has-dot #gallery,
body.has-dot #gallery.grabbing { cursor: none; }

#cursor {
  position: fixed; top: 0; left: 0;
  z-index: 60; pointer-events: none;
}
.cursor__disc {
  display: grid; place-items: center;
  width: 12px; height: 12px; border-radius: 50%;
  background: #fff; color: #fff;          /* idle: difference blend -> auto contrast over anything */
  mix-blend-mode: difference;
  transition: width var(--duration-fast) var(--ease-out-expo),
              height var(--duration-fast) var(--ease-out-expo),
              background var(--duration-fast) var(--ease-out-expo);
}
.cursor__label {
  font-size: var(--text-caption);
  text-transform: uppercase; letter-spacing: var(--tracking-caps);
  opacity: 0; transition: opacity var(--duration-fast) var(--ease-out-expo);
}
/* visit: solid blue disc (drop the blend so the blue reads true) */
#cursor.cursor--visit .cursor__disc {
  width: 76px; height: 76px;
  background: #2d6cdf; color: #fff;
  mix-blend-mode: normal;
}
#cursor.cursor--visit .cursor__label { opacity: 1; }
