/* ============================================================
   index.css — category mosaic (BRIEF §5, reference index-v5).
   Geometry lives on a virtual 1440×860 stage; --su reproduces
   the reference scaling min(1.15, vw/1440, vh/860). All canvas
   px become calc(N * var(--su)).
   ============================================================ */

/* chrome scale: --u in tokens.css already carries the vh term,
   so header/nav/switcher match the stage on every page */

.page-index {
  height: 100vh;
  overflow: hidden;
}

.page-index main {
  padding: 0;
}

.mosaic {
  --su: min(calc(100vw / 1440), calc(100vh / 860), 1.15px);
  position: fixed;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  width: calc(1440 * var(--su));
  height: calc(860 * var(--su));
  /* the fixed stage overlays the header — let clicks fall through
     everywhere except the tiles themselves */
  pointer-events: none;
}

.mosaic .tile {
  pointer-events: auto;
}

/* ---- tile (portal) ---- */
.tile {
  position: absolute;
  display: block;
  z-index: 2;
  transform-origin: center;
  transition:
    transform 0.6s var(--ease-slide),
    opacity 0.5s ease,
    filter 0.5s ease;
}

.tile:focus-visible {
  z-index: 4;
  transform: scale(var(--hover-scale));
}

/* ---- hover choreography, POINTER ONLY (see base.css) ---- */
@media (hover: hover) and (pointer: fine) {
  .tile:hover {
    z-index: 4;
    transform: scale(var(--hover-scale));
  }
}

/* NOTE (12.09.2026): the index tile zoom has the SAME stepping the
   category tiles had — 1.02 over 0.6s moves an edge about 3 device
   pixels in total. It is deliberately left alone: the category fix
   was chosen by the author on a probe built from a category tile,
   and the scale here sits on the whole tile, sharing its transition
   with the siblings' drift, so shortening it would change the
   mosaic's choreography and not only its zoom. Untested, and the
   author's to judge. docs/OPEN-ISSUES.md. */

/* siblings recede: dim, desaturate further, drift away from
   the hovered tile's center (unit vector × 10 canvas px).

   The mosaic keeps its own choreography — the grids' dim went to
   opacity alone on 14.09, this one did not: here the filter is part
   of a composed move (dim + desaturate + drift) that the author has
   approved as it stands, and five tiles is not twenty-three. */
@media (hover: hover) and (pointer: fine) {
  .mosaic:has(.tile:hover) .tile:not(:hover) {
    opacity: 0.55;
    filter: saturate(0.7);
    transform: translate(calc(var(--dx) * var(--su)), calc(var(--dy) * var(--su)));
  }
}

/* ---- media: graded at rest, raw copy fades in on hover ---- */
.tile__media {
  position: absolute;
  inset: 0;
  background: #1c1b19;
}

.tile__img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  filter: var(--grade-filter);
}

.tile__img--raw {
  filter: none;
  opacity: 0;
  transition: opacity 0.6s ease;
}

.tile:focus-visible .tile__img--raw {
  opacity: 1;
}

@media (hover: hover) and (pointer: fine) {
  .tile:hover .tile__img--raw {
    opacity: 1;
  }
}

/* the hover loop layer itself is shared chrome — see .hover-loop in
   base.css (grade locked to lift) */

/* ---- meta group above each tile: numeral · client · genre ---- */
.tile__meta {
  position: absolute;
  display: flex;
  gap: calc(14 * var(--su));
  align-items: flex-start;
  white-space: nowrap;
}

.tile__label {
  display: flex;
  flex-direction: column;
  gap: calc(6 * var(--su));
}

.tile__client {
  font-family: var(--font-mono);
  font-size: max(calc(11 * var(--su)), 9px);
  letter-spacing: 0.2em;
  text-transform: uppercase;
  line-height: 1;
  color: var(--meta-strong);
}

.tile__genre {
  font-family: var(--font-mono);
  font-size: max(calc(9 * var(--su)), 8px);
  letter-spacing: 0.14em;
  line-height: 1;
  color: var(--secondary);
}

/* oversized roman numeral: solid #5E6268 in the reference;
   lifted so its cap height tops out level with the client line */
.tile__numeral {
  font-family: var(--font-serif);
  font-weight: 400;
  font-size: calc(58 * var(--su));
  line-height: 1;
  color: var(--numerals);
  transform: translateY(calc(-18 * var(--su)));
  user-select: none;
}

/* ============================================================
   Geometry from reference defaults (canvas 1440×860):
   x · y(anchor) · w × h · numeral nx,ny · label side
   ============================================================ */

.tile--film {          /* bottom-left, 2.20:1, numeral III */
  left: calc(-64 * var(--su));
  bottom: 0;
  width: calc(545 * var(--su));
  height: calc(248 * var(--su));
}
.tile--film .tile__meta { left: calc(415 * var(--su)); top: calc(-51 * var(--su)); }

.tile--music-videos {  /* bottom-center, 2.35:1, numeral IV */
  left: calc(481 * var(--su));
  bottom: 0;
  width: calc(415 * var(--su));
  height: calc(177 * var(--su));
}
.tile--music-videos .tile__meta { left: calc(184 * var(--su)); top: calc(-43 * var(--su)); }

.tile--fashion {       /* bottom-right, 16:9, numeral V */
  left: calc(896 * var(--su));
  bottom: 0;
  width: calc(207 * var(--su));
  height: calc(116 * var(--su));
}
.tile--fashion .tile__meta { left: calc(84 * var(--su)); top: calc(-42 * var(--su)); }

.tile--advertising {   /* left column, 9:16, numeral I */
  left: calc(-64 * var(--su));
  top: calc(207 * var(--su));
  width: calc(228 * var(--su));
  height: calc(405 * var(--su));
}
/* tile bleeds off the left edge: meta stays inside the viewport */
.tile--advertising .tile__meta { left: calc(42 * var(--su)); top: calc(-50 * var(--su)); }

.tile--ai {            /* mid-left, 4:5, numeral II */
  left: calc(164 * var(--su));
  top: calc(404 * var(--su));
  width: calc(166 * var(--su));
  height: calc(208 * var(--su));
}
.tile--ai .tile__meta { left: calc(38 * var(--su)); top: calc(-52 * var(--su)); }

/* ============================================================
   Hover drift vectors: unit vector between tile centers × 10,
   precomputed from the geometry above (dx dy in canvas px)
   ============================================================ */

@media (hover: hover) and (pointer: fine) {
  .mosaic:has(.tile--film:hover)          .tile--music-videos { --dx: 10;   --dy: 0.7; }
  .mosaic:has(.tile--film:hover)          .tile--fashion      { --dx: 10;   --dy: 0.8; }
  .mosaic:has(.tile--film:hover)          .tile--advertising  { --dx: -4.4; --dy: -9;  }
  .mosaic:has(.tile--film:hover)          .tile--ai           { --dx: 1.7;  --dy: -9.9;}

  .mosaic:has(.tile--music-videos:hover)  .tile--film         { --dx: -10;  --dy: -0.7;}
  .mosaic:has(.tile--music-videos:hover)  .tile--fashion      { --dx: 10;   --dy: 1;   }
  .mosaic:has(.tile--music-videos:hover)  .tile--advertising  { --dx: -8.7; --dy: -4.9;}
  .mosaic:has(.tile--music-videos:hover)  .tile--ai           { --dx: -8.6; --dy: -5.1;}

  .mosaic:has(.tile--fashion:hover)       .tile--film         { --dx: -10;  --dy: -0.8;}
  .mosaic:has(.tile--fashion:hover)       .tile--music-videos { --dx: -10;  --dy: -1;  }
  .mosaic:has(.tile--fashion:hover)       .tile--advertising  { --dx: -9.2; --dy: -3.8;}
  .mosaic:has(.tile--fashion:hover)       .tile--ai           { --dx: -9.3; --dy: -3.6;}

  .mosaic:has(.tile--advertising:hover)   .tile--film         { --dx: 4.4;  --dy: 9;   }
  .mosaic:has(.tile--advertising:hover)   .tile--music-videos { --dx: 8.7;  --dy: 4.9; }
  .mosaic:has(.tile--advertising:hover)   .tile--fashion      { --dx: 9.2;  --dy: 3.8; }
  .mosaic:has(.tile--advertising:hover)   .tile--ai           { --dx: 8.9;  --dy: 4.5; }

  .mosaic:has(.tile--ai:hover)            .tile--film         { --dx: -1.7; --dy: 9.9; }
  .mosaic:has(.tile--ai:hover)            .tile--music-videos { --dx: 8.6;  --dy: 5.1; }
  .mosaic:has(.tile--ai:hover)            .tile--fashion      { --dx: 9.3;  --dy: 3.6; }
  .mosaic:has(.tile--ai:hover)            .tile--advertising  { --dx: -8.9; --dy: -4.5;}
}

/* ---- typographic index (mobile variant B): desktop never shows it */
.type-index {
  display: none;
}

/* ============================================================
   Mobile (≤768px) — ONE-SCREEN index, two draft variants for
   phone comparison, switched by a query param read in a tiny
   inline script on the page (?v=b → html[data-index-v="b"];
   anything else = variant A). Desktop stays untouched.

   VARIANT A (default) — film-strip index: five slim full-width
   strips share the space left by the header (≈12–15vh each),
   edge-to-edge, no gaps, no scroll. Image = textural band with a
   per-strip focal point; name + bracket left, numeral right.

   VARIANT B — typographic index: no imagery, a vertically
   centered type list (categories + brackets + numerals, ABOUT /
   CONTACT smaller beneath). The page IS the menu, so the MENU
   trigger is hidden here (index only — index.css loads nowhere
   else).
   ============================================================ */
@media (max-width: 768px) {
  /* One viewport exactly, no scroll (both variants), and the
     viewport meant is the SMALL one — 100svh, with the vh value as
     the fallback line above it. Not dvh: a height that changed as
     the toolbar retracted would resize the strips mid-scroll.

     The min-height pair is load-bearing and not a duplicate of the
     height (14.09.2026). `body { min-height: 100vh }` in base.css is
     the taller LARGE-viewport measure on Android Chrome while the
     toolbar is showing, and min-height beats height: the body came
     out one toolbar taller than the screen, which pushed the Fashion
     strip about 56px below the visible area, where `overflow: hidden`
     then made it unreachable. Both properties have to name the small
     viewport for the promise to hold. */
  .page-index {
    height: 100vh;
    height: 100svh;
    min-height: 100vh;
    min-height: 100svh;
  }

  /* 17.09.2026 — no footer band on the phone index; the five
     strips get that height back. Desktop keeps its corner footer. */
  .site-footer {
    display: none;
  }

  .page-index main {
    display: flex;
    flex-direction: column;
    min-height: 0;
    padding: 0;
  }

  /* INK breathing room between the tagline and the first strip */
  .mosaic {
    position: static;
    transform: none;
    width: auto;
    height: auto;
    pointer-events: auto;
    display: flex;
    flex-direction: column;
    flex: 1;
    min-height: 0;
    margin-top: 32px;
  }

  /* ---- variant A: film strips ---- */
  .tile {
    position: relative;
    inset: auto;
    width: 100%;
    flex: 1;
    min-height: 0;
    display: block;
    z-index: 1;
    transition: none;
  }

  /* These two resets are about the LAYOUT, not the pointer. The
     pointer half moved into the guard at the top of this file on
     14.09, so a phone no longer reaches those rules at all — but a
     narrow window on a laptop still has a fine pointer, and at this
     width the mosaic is a column of full-bleed strips where a scale
     and a drift would be nonsense. So they stay, and they now also
     cover :focus-visible, which is deliberately NOT pointer-guarded
     anywhere else on the site. */
  .tile:hover,
  .tile:focus-visible {
    transform: none;
    z-index: 1;
  }

  .mosaic:has(.tile:hover) .tile:not(:hover) {
    opacity: 1;
    filter: none;
    transform: none;
  }

  .tile__media {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
  }

  /* muted grade stays at rest; the raw hover copy never shows */
  .tile__img--raw {
    display: none;
  }

  /* textural band: focal point per strip, set as --focal on the
     tile by the generator (src/data/categories.json) */
  .tile__img {
    object-position: var(--focal, 50% 50%);
  }

  /* scrim only behind the text block: left-side gradient, the
     right ~60% of the strip shows the image undarkened */
  .tile__media::after {
    content: "";
    position: absolute;
    inset: 0;
    z-index: 1;
    background: linear-gradient(
      90deg,
      rgba(14, 17, 22, 0.58) 0%,
      rgba(14, 17, 22, 0.30) 26%,
      transparent 40%
    );
  }

  /* meta splits: name + bracket left-center, numeral right-center */
  .tile__meta {
    display: contents;
  }

  .tile__label {
    position: absolute;
    left: var(--edge-x);
    top: 50%;
    transform: translateY(-50%);
    z-index: 2;
    gap: 6px;
  }

  .tile__client {
    font-size: 13px;
    letter-spacing: 0.2em;
    color: var(--paper);
  }

  .tile__genre {
    font-size: 9px;
    letter-spacing: 0.14em;
    color: var(--meta-on-image); /* lighter gray over photos */
  }

  /* On a PHONE the numeral sits on the same line as the category
     name, over a photograph, at 44px on a 375px screen — so the
     passport's quiet numerals-gray at 0.55 stops being quiet and
     starts being unreadable, and paper at full opacity (where this
     landed first, on 14.09) took it the whole way to the name's own
     brightness, which the numeral is not.

     It reads at the MIDPOINT of the two (author, 14.09.2026), and
     the midpoint is already a token: `--secondary` #8E9096 is the
     passport's middle value between paper #E9E9E4 and numerals
     #5E6268. At opacity 1 — the dimming is in the colour, not in a
     second lever on top of it. Desktop keeps `--numerals` at the
     passport opacity, untouched. */
  .tile__numeral {
    position: absolute;
    right: 16px;
    top: 50%;
    transform: translateY(-50%);
    z-index: 2;
    font-size: 44px;
    color: var(--secondary);
    opacity: 1;
  }

  /* ---- variant B: typographic index ---- */
  html[data-index-v="b"] .mosaic {
    display: none;
  }

  html[data-index-v="b"] .menu-trigger {
    display: none; /* the page IS the menu */
  }

  html[data-index-v="b"] .type-index {
    display: flex;
    flex-direction: column;
    justify-content: center;
    flex: 1;
    min-height: 0;
    gap: 30px;
    padding: 0 var(--edge-x) 24px;
  }

  .type-index__group {
    display: flex;
    flex-direction: column;
    gap: 8px;
  }

  .type-index__row {
    display: flex;
    align-items: baseline;
  }

  .type-index__numeral {
    flex: 0 0 34px;
    font-family: var(--font-serif);
    font-weight: 400;
    font-size: 16px;
    line-height: 1;
    color: var(--numerals);
    opacity: 0.7;
    user-select: none;
  }

  .type-index__group a {
    font-family: var(--font-grotesque);
    font-size: 20px;
    font-weight: 500;
    letter-spacing: 0.16em;
    text-transform: uppercase;
    line-height: 1;
    color: var(--paper);
  }

  .type-index__bracket {
    font-family: var(--font-mono);
    font-size: 9px;
    letter-spacing: 0.14em;
    color: var(--secondary);
    margin-left: 34px; /* aligns under the name, past the numeral slot */
  }

  /* ABOUT / CONTACT: smaller, quieter, extra air above */
  .type-index__minor {
    margin-top: 14px;
    display: flex;
    flex-direction: column;
    gap: 22px;
  }

  .type-index__minor a {
    font-family: var(--font-grotesque);
    font-size: 13px;
    font-weight: 500;
    letter-spacing: 0.18em;
    text-transform: uppercase;
    line-height: 1;
    color: var(--secondary);
    margin-left: 34px;
  }
}
