/* fl718-nyc-2023-v2.css - NYC 2023 desktop styles
   Version: 1.0.2
   Last updated: 2026-10-08
   v3 of -v2 (same day): hero first-paint = the START state (the end-state block keys on
   .fl718-no-gsap, not on :not(.fl718-js-loaded), which means "JS pending"); hero stage
   geometry computed in CSS (container units) so layout() no longer moves it (CLS);
   quiet lightbox (solid #0a0908, no counter, glyph-only X).
   v2 of -v1 (same day): hero height 100vh with no -56px margin (live has no 56px spacer; the
   v1 hero sat 56px above the viewport and clipped the title).
   Extracted from nyc-2023-index.html (desktop prototype cleared by Seamus,
   Steps 1-4). Dropped on purpose: the prototype's .fl718-ny23-navstub (WordPress
   supplies the real 56px) and its after-content stand-in, superseded by the
   shared template-parts/after-content.php + fl718-after-content CSS (BUG-061).
   Change vs prototype: the lightbox control :hover is wrapped in
   (hover: hover) and (pointer: fine), the touch-state guard.
   This sheet loads on BOTH templates (registry: css is unguarded); the mobile
   sheet is enqueued after it and resets what it must. */

    /* ============================================================
       ALL CSS SCOPED TO: body.fl718-nyc-2023-page
       Every rule below uses this body class as its scope prefix.
       Never write an unscoped rule.
       ============================================================ */

    /* ------------------------------------------------------------
       BASE. Palette (measured 2026-10-06, approved by Seamus):
       chrome a half-step darker than NYC 2015-2022. No accent, and
       never #c44536 (reserved site-wide for the menu/footer frame).
         Hero bg  #0b0b0b   Body bg #ebe9e6   Ink #1c1b1a
         Chrome   #403e3c   Muted    #66635e  Rules #d3d0cb
       ------------------------------------------------------------ */
    body.fl718-nyc-2023-page {
      margin: 0;
      padding: 0;
      /* overflow-x: clip, NOT hidden: hidden creates a scroll container
         that breaks position:sticky and ScrollTrigger pins site-wide. */
      overflow-x: clip;
      background-color: #ebe9e6;
      color: #1c1b1a;
      -webkit-font-smoothing: antialiased;
    }
    :where(body.fl718-nyc-2023-page) *,
    :where(body.fl718-nyc-2023-page) *::before,
    :where(body.fl718-nyc-2023-page) *::after { box-sizing: border-box; }
    body.fl718-nyc-2023-page img { max-width: 100%; display: block; }
    body.fl718-nyc-2023-page .fl718-ny23-visually-hidden {
      position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
      overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0;
    }

    /* ============================================================
       HERO. Locked: photograph (row 83 export), title typography and
       its entrance, byline, placement band. NOT locked: the photograph's
       own entrance (the two "veils" below, first pass).

       Hero height rule: 100vh, no negative margin. The prototype assumed GeneratePress leaves 56px above the
       first element (its navstub); measured live, body/.site-content margins are 0 and the sibling NYC heroes sit at top 0.

       ANCHORING. The photograph is object-fit:cover, so the frame's
       crop changes with the window. Everything that must stay clear of
       the two women lives inside .fl718-ny23-hero__stage, a box that
       Script 2 sizes and positions to EXACTLY the cover-rendered image
       rect. Inside it, percentages and cqw are image-space, so the veils
       and the text block track the photograph at any aspect ratio.
       ============================================================ */
    body.fl718-nyc-2023-page .fl718-ny23-hero {
      position: relative;
      width: 100%;
      height: 100vh;   /* live: no 56px spacer above the hero (measured 2026-10-08), so no -56px pull */
      overflow: clip;
      background-color: #0b0b0b;
    }
    body.fl718-nyc-2023-page .fl718-ny23-hero__img {
      position: absolute;
      inset: 0;
      width: 100%;
      height: 100%;
      object-fit: cover;
      object-position: 50% 50%; /* Script 2 sets the vertical value on wide windows */
      display: block;
    }
    /* image-space stage: size/position set by Script 2 (fallback: the hero box) */
    body.fl718-nyc-2023-page .fl718-ny23-hero__stage {
      position: absolute;
      left: 0; top: 0; width: 100%; height: 100%;
      container-type: size;
      --k: 1; /* type scale, set by Script 2 so the block never reaches the pedestrians right of the pillar */
      pointer-events: none;
    }
    body.fl718-nyc-2023-page .fl718-ny23-hero__scrim {
      position: absolute; inset: 0; z-index: 1;
      background: radial-gradient(ellipse 48% 42% at 20% 14%, rgba(6,5,5,.5), transparent 72%);
      opacity: 0;
    }

    /* --- PHOTO ENTRANCE, first pass: "The glass catches up" ---------
       The street arrives first; the reflection in the glass surfaces a beat
       later, rising out of the black pillar. Two veils, both the hero's own
       #0b0b0b, in image space. The glass veil's hard edge sits on the pillar's
       right edge (48.9% of the frame), so the seam is the photograph's own
       edge. Transforms and opacity only; the photograph is never filtered,
       scaled or shifted. */
    body.fl718-nyc-2023-page .fl718-ny23-hero__veil-street {
      position: absolute; z-index: 2;
      left: 48.8%; top: 0; width: 51.2%; height: 100%;
      background: #0b0b0b;
    }
    body.fl718-nyc-2023-page .fl718-ny23-hero__glass {
      position: absolute; z-index: 2;
      left: 0; top: 0; width: 48.9%; height: 100%;
      overflow: clip;
    }
    body.fl718-nyc-2023-page .fl718-ny23-hero__veil-glass {
      position: absolute; left: 0; top: 0; height: 100%; width: 150%;
      background: linear-gradient(to right, #0b0b0b 0%, #0b0b0b 66.6%, rgba(11,11,11,0) 100%);
    }

    /* Text block: LOCKED type. Image-space position (left/top set by Script 2,
       these are the 16:9 baseline: left 4.5%, top 4%). */
    body.fl718-nyc-2023-page .fl718-ny23-hero__text {
      position: absolute; left: 4.5%; top: 4%; z-index: 5;
      white-space: nowrap; pointer-events: none;
      text-shadow: 0 2px 18px rgba(6,5,5,.5);
      color: #f4f2ec;
    }
    body.fl718-nyc-2023-page .fl718-ny23-hero__title {
      display: block; margin: 0;
      font: 400 calc(4.5cqw * var(--k)) / 1 'Archivo Black', sans-serif;
      letter-spacing: .015em; text-transform: uppercase; color: #f4f2ec;
      --bar: 0em; --pitch: .125em;
      -webkit-mask-image: repeating-linear-gradient(to bottom, #000 0, #000 var(--bar), transparent var(--bar), transparent var(--pitch));
              mask-image: repeating-linear-gradient(to bottom, #000 0, #000 var(--bar), transparent var(--bar), transparent var(--pitch));
      filter: drop-shadow(0 2px 14px rgba(6,5,5,.5));
    }
    body.fl718-nyc-2023-page .fl718-ny23-hero__yearrow {
      display: flex; align-items: center;
      gap: calc(.9cqw * var(--k));
      margin: calc(1cqw * var(--k)) 0 0 .1em;
      opacity: 0;
    }
    body.fl718-nyc-2023-page .fl718-ny23-hero__rule {
      display: block; width: calc(2.2cqw * var(--k)); height: 1px;
      background: #f4f2ec; opacity: .8; transform-origin: left center;
    }
    body.fl718-nyc-2023-page .fl718-ny23-hero__year {
      font: 500 calc(1.35cqw * var(--k)) / 1 'DM Sans', sans-serif;
      letter-spacing: .3em;
    }
    body.fl718-nyc-2023-page .fl718-ny23-hero__by {
      display: block; margin: calc(.9cqw * var(--k)) 0 0 .1em;
      font: italic 300 calc(1.2cqw * var(--k)) / 1.3 'DM Sans', sans-serif;
    }
    body.fl718-nyc-2023-page .fl718-ny23-hero__by span {
      display: inline-block; opacity: 0; margin-right: .32em;
    }

    /* Matched pair rule: every CSS start state above (veils opaque, title bars
       at 0, year/byline/scrim at opacity 0) has its end state here for
       reduced motion and for a visitor whose GSAP never loaded (.fl718-no-gsap,
       added by Script 1). NOT keyed on :not(.fl718-js-loaded): that class
       means "the footer script has not run YET", and keying the end state on it
       painted the finished photograph first, then black, then the entrance.
       JS-disabled visitors get the end state from a <noscript><style> in the
       template. Ship both or neither. */
    body.fl718-nyc-2023-page.fl718-no-gsap .fl718-ny23-hero__title { --bar: .082em; }
    @media (prefers-reduced-motion: reduce) {
      body.fl718-nyc-2023-page .fl718-ny23-hero__title { --bar: .082em; }
    }
    body.fl718-nyc-2023-page.fl718-no-gsap .fl718-ny23-hero__veil-street,
    body.fl718-nyc-2023-page.fl718-no-gsap .fl718-ny23-hero__glass { display: none; }
    body.fl718-nyc-2023-page.fl718-no-gsap .fl718-ny23-hero__scrim { opacity: 1; }
    body.fl718-nyc-2023-page.fl718-no-gsap .fl718-ny23-hero__yearrow { opacity: .9; }
    body.fl718-nyc-2023-page.fl718-no-gsap .fl718-ny23-hero__by span { opacity: .95; }
    @media (prefers-reduced-motion: reduce) {
      body.fl718-nyc-2023-page .fl718-ny23-hero__veil-street,
      body.fl718-nyc-2023-page .fl718-ny23-hero__glass { display: none; }
      body.fl718-nyc-2023-page .fl718-ny23-hero__scrim { opacity: 1; }
      body.fl718-nyc-2023-page .fl718-ny23-hero__yearrow { opacity: .9; }
      body.fl718-nyc-2023-page .fl718-ny23-hero__by span { opacity: .95; }
    }

    /* ============================================================
       GALLERY GRID. CSS-columns masonry, 3 columns (desktop sheet steps
       3 -> 2 by width; 1 column is the mobile template's job). 82 photos,
       three of them portrait (4:5): see QA_NOTES for how they sit.
       ============================================================ */
    body.fl718-nyc-2023-page .fl718-ny23-grid {
      columns: 3;
      column-gap: 8px;
      padding: 8px;
      padding-top: 14vh;
      padding-bottom: 12vh;
    }
    @media (max-width: 768px) {
      body.fl718-nyc-2023-page .fl718-ny23-grid { columns: 2; padding-top: 10vh; }
    }
    /* Explicit column starts (Seamus's sequenced columns, 27/27/28, 2026-10-07).
       CSS columns balance by height and put a different photo at the top of column 2,
       so the sequenced starts are forced. Desktop 3-column only; update the two
       nth-child numbers if the column sizes change (they are positions, not photos). */
    @media (min-width: 769px) {
      body.fl718-nyc-2023-page .fl718-ny23-item:nth-child(28),
      body.fl718-nyc-2023-page .fl718-ny23-item:nth-child(55) { break-before: column; }
    }
    body.fl718-nyc-2023-page .fl718-ny23-item {
      break-inside: avoid;
      margin: 0 0 8px 0;
      position: relative;
      display: block;
      cursor: pointer;
      isolation: isolate;
    }
    body.fl718-nyc-2023-page .fl718-ny23-item:focus { outline: none; }
    body.fl718-nyc-2023-page .fl718-ny23-item:focus-visible { outline: 2px solid #403e3c; outline-offset: 3px; }

    /* --- GRID ENTRANCE, first pass: "Blinds" -------------------------
       Each photo opens through horizontal slats that thicken until they
       meet, continuing the title's bars. The slat mask lives on the <img>
       only while it is arriving; once the photo is in, the mask is removed
       entirely (class --in) so a resting photograph is untouched.
       START STATE (bars at 0 = fully hidden) pairs with the JS tween;
       no-JS and reduced motion get the end state below. */
    body.fl718-nyc-2023-page .fl718-ny23-item img {
      width: 100%; height: auto; display: block;
      --bar: 0px; --pitch: 12px;
      -webkit-mask-image: repeating-linear-gradient(to bottom, #000 0, #000 var(--bar), transparent var(--bar), transparent var(--pitch));
              mask-image: repeating-linear-gradient(to bottom, #000 0, #000 var(--bar), transparent var(--bar), transparent var(--pitch));
    }
    body.fl718-nyc-2023-page .fl718-ny23-item.fl718-ny23-in img {
      -webkit-mask-image: none; mask-image: none;
    }
    body.fl718-nyc-2023-page:not(.fl718-js-loaded) .fl718-ny23-item img {
      -webkit-mask-image: none; mask-image: none;
    }
    @media (prefers-reduced-motion: reduce) {
      body.fl718-nyc-2023-page .fl718-ny23-item img { -webkit-mask-image: none; mask-image: none; }
    }

    /* --- HOVER, first pass: "Held breath" ----------------------------
       (Seamus picked it from concepts/nyc-2023-hover-concepts.html, 2026-10-06;
       replaces "Ghost frame". Still to be felt on the page.)
       The whole tile lifts 3px and a soft shadow opens in the gutter beneath
       it, as if the print were picked up a hair off the wall. Nothing is laid
       over the photograph: the shadow falls outside it (negative spread keeps
       it below, not around). Armed only once the photo has arrived (--in),
       so it never fights the Blinds mask. */
    body.fl718-nyc-2023-page .fl718-ny23-item {
      transition: transform .8s cubic-bezier(.2, .7, .2, 1), box-shadow .8s cubic-bezier(.2, .7, .2, 1);
    }
    @media (hover: hover) and (pointer: fine) {
      body.fl718-nyc-2023-page .fl718-ny23-item.fl718-ny23-in:hover {
        transform: translateY(-3px);
        box-shadow: 0 14px 18px -10px rgba(40, 38, 36, .38);
      }
    }
    body.fl718-nyc-2023-page .fl718-ny23-item.fl718-ny23-in:focus-visible {
      transform: translateY(-3px);
      box-shadow: 0 14px 18px -10px rgba(40, 38, 36, .38);
    }
    @media (prefers-reduced-motion: reduce) {
      body.fl718-nyc-2023-page .fl718-ny23-item { transition: none; }
      body.fl718-nyc-2023-page .fl718-ny23-item.fl718-ny23-in:hover,
      body.fl718-nyc-2023-page .fl718-ny23-item.fl718-ny23-in:focus-visible { transform: none; }
    }

    /* ============================================================
       LIGHTBOX. Layer 1 (9000) dark | Layer 2 (9100) stage | Layer 3 (9200) UI.
       State-scoping contract: every control is pointer-events:none by default;
       auto is granted ONLY under the --active class. A closed lightbox must
       never swallow a click on the photo beneath it.
       Signature, first pass: "Slats lift". On open a row of slats covers the
       photograph and lifts off it, top to bottom; on close they drop back.
       Arrival/departure only: nothing on arrow-navigate, nothing held.
       ============================================================ */
    body.fl718-nyc-2023-page .fl718-ny23-lb-dark {
      position: fixed; inset: 0; background: #0a0908;
      opacity: 0; z-index: 9000; pointer-events: none;
    }
    body.fl718-nyc-2023-page .fl718-ny23-lb-stage {
      position: fixed; inset: 0; z-index: 9100;
      display: flex; align-items: center; justify-content: center;
      padding: 60px; opacity: 0; pointer-events: none;
    }
    @media (max-width: 900px) {
      body.fl718-nyc-2023-page .fl718-ny23-lb-stage { padding: 40px; }
    }
    body.fl718-nyc-2023-page .fl718-ny23-lb-stage--active { pointer-events: auto; }
    body.fl718-nyc-2023-page .fl718-ny23-lb-backdrop { position: absolute; inset: 0; }
    body.fl718-nyc-2023-page .fl718-ny23-lb-image-wrap {
      position: relative; max-width: 100%; max-height: 100%;
      display: flex; align-items: center; justify-content: center;
    }
    body.fl718-nyc-2023-page .fl718-ny23-lb-image {
      max-width: 100%; max-height: calc(100vh - 120px);
      display: block; opacity: 0;
    }
    body.fl718-nyc-2023-page .fl718-ny23-lb-slats {
      position: absolute; inset: 0; z-index: 2; pointer-events: none;
      display: flex; flex-direction: column;
    }
    body.fl718-nyc-2023-page .fl718-ny23-lb-slat {
      flex: 1 1 0; min-height: 0; background: #0a0908;
      transform-origin: 50% 0; transform: scaleY(0);
      margin-bottom: -.5px; /* slats overlap by half a pixel so no seam shows between them */
    }
    body.fl718-nyc-2023-page .fl718-ny23-lb-caption {
      position: absolute; bottom: -34px; left: 0; right: 0;
      text-align: center;
      font-family: 'DM Sans', system-ui, sans-serif;
      font-size: clamp(0.75rem, 1vw, 0.9rem);
      color: #aaa; opacity: 0; margin: 0;
    }
    body.fl718-nyc-2023-page .fl718-ny23-lb-ui {
      position: fixed; inset: 0; z-index: 9200; opacity: 0; pointer-events: none;
    }
    /* Close and arrows: lightly apparent, 44px hit area, GeneratePress's
       button padding zeroed. pointer-events:auto only under --active. */
    body.fl718-nyc-2023-page .fl718-ny23-lb-close-btn,
    body.fl718-nyc-2023-page .fl718-ny23-lb-nav {
      position: fixed; background: transparent; border: none; border-radius: 50%;
      width: 44px; height: 44px; padding: 0;
      display: flex; align-items: center; justify-content: center;
      cursor: pointer; pointer-events: none;
      color: rgba(238, 236, 233, .36);
      transition: background .35s ease, color .35s ease;
    }
    body.fl718-nyc-2023-page .fl718-ny23-lb-ui--active .fl718-ny23-lb-close-btn,
    body.fl718-nyc-2023-page .fl718-ny23-lb-ui--active .fl718-ny23-lb-nav { pointer-events: auto; }
    @media (hover: hover) and (pointer: fine) {
      body.fl718-nyc-2023-page .fl718-ny23-lb-nav:hover {
        background: rgba(238, 236, 233, .06); color: rgba(238, 236, 233, .85);
      }
    }
    /* Quiet close (Seamus 2026-10-08, as on the mobile template): a faint glyph, no ring
       or disc in any state. GeneratePress styles every <button> (button:focus at 0,1,1),
       so every state is declared at this scope. Keyboard users keep a 1px faint outline. */
    body.fl718-nyc-2023-page .fl718-ny23-lb-close-btn:focus,
    body.fl718-nyc-2023-page .fl718-ny23-lb-close-btn:active {
      background: transparent; color: rgba(238, 236, 233, .36);
    }
    body.fl718-nyc-2023-page .fl718-ny23-lb-close-btn:focus-visible { outline: 1px solid rgba(214, 208, 198, .22); outline-offset: 0; }
    body.fl718-nyc-2023-page .fl718-ny23-lb-close-btn svg,
    body.fl718-nyc-2023-page .fl718-ny23-lb-nav svg {
      width: 18px; height: 18px; stroke: currentColor; stroke-width: 1.25; fill: none;
    }
    body.fl718-nyc-2023-page .fl718-ny23-lb-close-btn { top: 24px; right: 24px; }
    body.fl718-nyc-2023-page .fl718-ny23-lb-prev { left: 24px; top: 50%; transform: translateY(-50%); }
    body.fl718-nyc-2023-page .fl718-ny23-lb-next { right: 24px; top: 50%; transform: translateY(-50%); }
    /* Quiet lightbox: no photograph counter (removed 2026-10-08, as on mobile). */

    /* ============================================================
       HERO FIRST PAINT + CLS (v3). The stage's geometry used to start at
       left:0/top:0/100%/100% and be moved by Script 2 layout() after load,
       a layout shift on every wide window. The same numbers are computed here
       from the hero's own size (container units), so the very first frame is
       already the final one. Script 2 writes them only when container units
       are unsupported. Must follow the stage's earlier rule at equal specificity.
       ============================================================ */
    body.fl718-nyc-2023-page .fl718-ny23-hero {
      container-type: size;
      --ny23-ry: calc(-1 * min((max(100cqh, 56.25cqw) - 100cqh) / 2, .02 * max(100cqh, 56.25cqw)));
    }
    body.fl718-nyc-2023-page .fl718-ny23-hero__img { object-position: 50% var(--ny23-ry); }
    body.fl718-nyc-2023-page .fl718-ny23-hero__stage {
      left: calc((100cqw - max(100cqw, 100cqh * 16 / 9)) / 2);
      top: var(--ny23-ry);
      width: max(100cqw, 100cqh * 16 / 9);
      height: max(100cqh, 56.25cqw);
    }
    /* The text block is positioned by JS: hide it until the footer script has run,
       for visitors who have scripting (a no-JS visitor never gets the class, and the
       media query keeps them out of this rule). */
    @media (scripting: enabled) {
      body.fl718-nyc-2023-page:not(.fl718-js-loaded) .fl718-ny23-hero__text { visibility: hidden; }
    }
    /* Dark first-viewport band: whatever paints before the hero (or beside it
       while it lays out) is the hero's own black, never white. */
    body.fl718-nyc-2023-page {
      background-image: linear-gradient(#0b0b0b, #0b0b0b);
      background-repeat: no-repeat; background-position: 0 0; background-size: 100% 100vh;
    }
