/* ------------------------------------------------------------------ *
 *  home.css — the landing page
 *  Left: three small lower-case words, a quiet menu. The middle one on
 *  the vertical centre. Resting words faint; the active one goes to ink.
 *  Right: the active section's puzzle — the cover and the section's other
 *  frames, packed edge to edge at each one's own real shape. On a wide
 *  screen that's a real justified-row layout (home.js solves it against
 *  the box's actual width), the cover centred in its own row; on a phone
 *  it's a CSS masonry instead, the same pieces packed into columns. Either
 *  way they fade from sharp to faded outward from the cover, then fade
 *  into view on load, later the more faded ones. Hover a piece to see its
 *  exif.
 * ------------------------------------------------------------------ */

.home {
  min-height: 100vh;
  min-height: 100svh;
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  align-items: center;
  gap: clamp(1.5rem, 5vw, 4.5rem);
  padding: var(--pad-edge);
  padding-bottom: calc(var(--pad-edge) * 2.4);
  overflow: hidden;                 /* the bloom may reach past the edges */
}

/* — frosted glass —————————————————————————————————————————————— *
 * The sheet itself lives in base.css, because every page carries it now.
 * All the landing page needs is its own place in the stack:
 *   words (10) above the cover and every tile (5), which all sit above
 *   the glass (3). Nothing here is veiled by the mask hole any more —
 *   see the note on .tile below — so the exact stacking among them only
 *   matters for the hover-lift (.tile:hover, further down). */
.page-home .glass { z-index: 3; }

/* — the three words ————————————————————————————————————————— */
.three {
  align-self: center;
  position: relative;         /* z-index is inert without this */
  z-index: 10;                /* always above the glass */
  padding-left: clamp(0rem, 2vw, 2rem);
}
.three__word {
  display: block;
  width: max-content;
  max-width: 40vw;
  white-space: nowrap;
  font-size: var(--big);
  font-weight: var(--w-light);
  line-height: 1.28;
  letter-spacing: -0.01em;
  /* real ink held back by alpha rather than the warm --ink-faint: the
     words sit on cool glass here, and a warm grey vanishes against it */
  color: rgba(28, 26, 23, 0.63);
  padding: 0.14em 0;
  transition: color var(--t-mid) var(--ease), text-shadow var(--t-mid) var(--ease),
              opacity var(--t-mid) var(--ease);
}
.three__word:nth-child(1) { font-family: var(--font-word-1); }
.three__word:nth-child(2) { font-family: var(--font-word-2); }
.three__word:nth-child(3) { font-family: var(--font-word-3); }

.three__word.is-active {
  color: var(--ink);
  text-shadow: 0 0 16px rgba(28, 26, 23, 0.16);
}
.three.has-active .three__word:not(.is-active) { opacity: 0.85; }

/* letters fade + pop in on load */
.ch   { display: inline-block; white-space: pre; }
.word { display: inline-block; white-space: nowrap; }
.js .three__word .ch { opacity: 0; }
.js .three__word .ch.in { animation: letterIn 420ms var(--ease-soft) forwards; }
@keyframes letterIn {
  from { opacity: 0; transform: translateY(4px) scale(0.94); filter: blur(2px); }
  to   { opacity: 1; transform: none;                        filter: blur(0);  }
}

/* — the preview, right ————————————————————————————————————— */
.preview {
  position: relative;
  z-index: auto;
  align-self: center;
  justify-self: center;
  width: 100%;
  max-width: 50rem;
}

.field { position: relative; display: none; }
.field.is-current { display: block; }

/* a section with nothing to show yet — no cover, no tiles, just a quiet
   note centred in the same box a puzzle would otherwise fill */
.field.is-current[data-coming-soon] {
  display: flex;
  align-items: center;
  justify-content: center;
}
/* same face as "other projects" itself (.three__word:nth-child(3)) rather
   than the body font — this is that word's own preview, not a caption.
   A true grey rather than the near-white resting-word tone: with no cover
   image to anchor on, the sheet's own clear-centre (glass.js place())
   falls back to .preview's centre — exactly where this text sits — so
   the brightest, most cleared part of the glass always lands right on
   top of it, and a grey sits far enough from both that end of the sheet
   and the dark end for plain contrast to carry it without a shadow. */
.preview__soon {
  position: relative;   /* z-index is inert without this */
  z-index: 5;            /* above the glass (3), matching .preview__main/.tile */
  margin: 0;
  font-family: var(--font-word-3);
  font-weight: var(--w-light);
  font-size: 1.1rem;
  letter-spacing: -0.01em;
  color: #8a8880;
  opacity: 0;
  transition: opacity 1.15s ease;
}

/* a small status line above a section's cover ("in production") — same
   face as that section's own word in .three (home.js reads which word
   this field belongs to, not this rule; --font-word-1 is simply the
   only one in use so far), just smaller and quieter, since it's a status
   note rather than the word itself */
.preview__status {
  position: relative;   /* z-index is inert without this — see .preview__soon */
  z-index: 5;
  margin: 0;
  font-family: var(--font-word-1);
  font-weight: var(--w-light);
  font-size: 1.05rem;
  letter-spacing: -0.01em;
  text-align: center;
  color: var(--ink-faint);
  opacity: 0;
  transition: opacity 1.15s ease;
}
/* a true grey rather than the near-white resting-word tone — the label
   sits right above the cover, exactly where the sheet's clear-centre
   (glass.js place()) tends to land once that image is hovered, so it
   needs to read against the brightest part of the glass too, the same
   reasoning as .preview__soon above */
.glass-lit .preview__status { color: #8a8880; }

/* home.js (layoutDesktop) sets left/top/width/height on every piece
   directly, in real pixels solved against .preview's actual width — a
   real justified-row layout, same technique .stream--masonry uses for
   /stills, just arranged as a compact centred cluster instead of a
   full-width column. Nothing here sizes them; this only has to give the
   box they're positioned inside of. */
.preview__main,
.tile {
  position: absolute;
  overflow: hidden;                      /* keeps the exif overlay in bounds,
                                             and is a crop backstop that
                                             should never actually engage —
                                             every box is solved at its
                                             image's own exact ratio */
  background: transparent;   /* so fading a tile (below) reveals the glass
                                 sheet behind .preview, not a solid box */
  opacity: 0;
  transition: opacity 1.15s ease;
}
/* the actual link (home.js) — .preview__main/.tile are a plain box now,
   see the note in home.js for why; this just fills that box exactly the
   way the box itself used to fill its parent */
.frame__link { display: block; width: 100%; height: 100%; }
.preview__main img,
.tile img {
  width: 100%; height: 100%;
  display: block;
  /* the box is sized to the image's own ratio, so contain and cover
     agree — using contain anyway is a hard guarantee, not just a
     practical one, that nothing is ever cropped. */
  object-fit: contain;
}

/* --- how deep into the set a frame is, out from the cover ---------- *
 * --bl is set per tile by home.js: 0 at the cover, up to 1 at the
 * furthest piece — a plain sweep, nothing held flat along the way — on
 * a wide screen by real on-screen distance from the cover once the
 * justified-row layout is solved, on a phone by plain rank in gallery
 * order (the masonry's columns fill in that order, so rank already
 * reads as "how far into the cluster"). The cover is not a .tile, so it
 * never takes this.
 *
 * Blurring a photo's own pixels still leaves an opaque, sharp-edged
 * rectangle recognisably a photo, just a soft one — what actually reads
 * as "fading into the sheet" is the photo going toward transparent so
 * the frosted glass behind .preview (position:fixed, so it's there at
 * every tile's position, not only the cover's) shows through and does
 * the blending. Opacity carries that; blur and a slight scale-up are
 * only there to soften the edge instead of leaving a hard-edged cutout,
 * and to hide the soft transparent fringe blur leaves inside the
 * overflow:hidden box. */
.tile img {
  filter: blur(calc(var(--bl, 0) * var(--tile-w, 130px) * 0.045));
  opacity: calc(1 - var(--bl, 0) * 0.88);
  transform: scale(calc(1 + var(--bl, 0) * 0.08));
  transition: filter 0.45s var(--ease), transform 0.45s var(--ease),
              opacity 0.45s var(--ease);
}
/* a frame you are actually looking at comes fully back, and rises above
   its neighbours (see .tile:hover z-index below). Quick rather than the
   0.45s above: a piece faded in hard (--bl close to 1) took long enough
   clearing that a normal, unhurried mouse pass over it never actually
   finished — it never read as "fully visible", exactly the complaint.
   Fading back out on mouse-leave keeps the slower, deliberate speed;
   only coming into focus is snappy. */
.tile:hover img {
  filter: none; transform: none; opacity: 1;
  transition-duration: 0.12s;
}

/* shutter · aperture · iso — appears over the bottom of the image on hover */
.exif {
  position: absolute;
  left: 0; right: 0; bottom: 0;
  padding: 0.55rem 0.65rem 0.5rem;
  font-family: var(--font-meta);
  font-size: 0.64rem;
  letter-spacing: 0.03em;
  color: #fff;
  background: linear-gradient(to top, rgba(0, 0, 0, 0.55), rgba(0, 0, 0, 0) 100%);
  opacity: 0;
  transform: translateY(4px);
  transition: opacity 0.25s ease, transform 0.25s ease;
  pointer-events: none;
}
.preview__main:hover .exif,
.tile:hover .exif { opacity: 1; transform: translateY(0); }

.preview__main { z-index: 5; }
.tile {
  transition-delay: calc(var(--fo, 1) * 0.42s);
  z-index: 5;
}
/* hovering a tile lifts it above its neighbours, so a frame you are
   actually looking at is never partly covered by one beside it */
.tile:hover { z-index: 6; }

.field.is-in .preview__main,
.field.is-in .tile,
.field.is-in .preview__soon,
.field.is-in .preview__status { opacity: 1; }

/* — narrow screens ————————————————————————————————————————— *
 * The justified-row layout above needs real width to arrange a centred
 * cluster in; a phone doesn't have it to spare, so this switches to a
 * dense masonry instead — the same technique .stream--masonry uses for
 * /stills, packed into columns rather than a compact centred block.
 * home.js's layoutField() switches which one actually runs; this side
 * only has to undo the wide layout's positioning so the masonry rules
 * below are free to take over (an inline style beats any stylesheet
 * rule regardless of media query, so home.js clears left/top/width/
 * height itself rather than leaving that to CSS, which cannot). */
@media (max-width: 820px) {
  .home {
    grid-template-columns: 1fr;
    /* not align-content:center — the preview's mobile height is real
       content height (a masonry column, or a single image, or a short
       "soon…"/"in production…" note), and it changes a lot switching
       between sections. With the block centered, that height change
       physically moved the words: tapping "other projects" (a couple
       lines vs. stills' tall masonry) shifted it ~180px in testing,
       enough that a real touch's tap-up landed somewhere else entirely
       and the navigation just never happened. Anchoring to the top
       means a shorter preview leaves empty space below instead of
       relocating the words above it. */
    align-content: start;
    /* tight on purpose — this is the gap between the words and the
       preview below them, and the two should read as one close group,
       not two separate blocks with a wide gulf between them */
    gap: clamp(0.75rem, 3vh, 1.75rem);
  }
  .three { padding-left: 0; }
  .three__word { max-width: 86vw; }

  .preview {
    /* reclaim .home's own side padding for the preview specifically —
       a phone's width is the tightest budget the tiles have to work
       with, so every extra pixel here means visibly bigger tiles */
    width: calc(100% + 2 * var(--pad-edge));
    max-width: none;
    margin-inline: calc(-1 * var(--pad-edge));
  }
  .field.is-current {
    --tile-w: clamp(90px, 25vw, 165px);
    /* a flat number, not a vw-scaled one: column-gap and every tile's
       margin-bottom both read this, and a fractional value (2vw is
       rarely a whole pixel) rounds independently at each of those spots
       — same gap in principle, off by a pixel here and there in practice.
       A flat 8px has nothing to round, so it comes out identical
       everywhere. */
    --tile-gap: 8px;
    column-width: var(--tile-w);
    column-gap: var(--tile-gap);
  }
  .preview__main,
  .tile {
    position: relative;        /* was absolute; z-index still needs this */
    width: 100%;
    height: auto;
    aspect-ratio: var(--w, 1) / var(--h, 1);
    margin: 0 0 var(--tile-gap);
    break-inside: avoid;
    -webkit-column-break-inside: avoid;
  }
  /* the label and its cover move through the masonry as one piece — see
     the note in buildField() on why they're wrapped together at all */
  .preview__cover-group {
    margin: 0 0 var(--tile-gap);
    break-inside: avoid;
    -webkit-column-break-inside: avoid;
  }
  .preview__cover-group .preview__main { margin: 0; }
  .preview__status { margin: 0 0 6px; }
}

@media (prefers-reduced-motion: reduce) {
  .glass__canvas { transition: none; }
  .js .three__word .ch { opacity: 1; animation: none; }
  .preview__main,
  .tile { transition: opacity 0.3s linear; transition-delay: 0s; }
  .exif { transition: opacity 0.15s linear; }
}

/* the landing's own type on the sheet (the shared wordmark rules are in
   base.css; these are the three words, which only exist here) */
.glass-lit .three__word { color: rgba(244, 241, 234, 0.66); }
.glass-lit .preview__soon { color: rgba(200, 197, 190, 0.7); }
.glass-lit .three__word.is-active {
  color: var(--paper);
  text-shadow: 0 0 22px rgba(244, 241, 234, 0.28);
}
