/* ═══════════════════════════════════════════════════════════════════════════
   ++ WORK — the secondary shelf, one project per screen.

   Loaded after project.css, which it borrows the info column's type and the
   carousel's crossfade from. It overrides nothing there; it only adds the panel.

   The difference from a project page: a project page is one long sticky run and
   the reader chooses their pace. This page is a list, so every project gets
   exactly one screen and the scroll is snapped to it — one gesture, one
   project, never two on screen at once. Because a panel IS the page here, the
   image grid is height-driven (a fixed 2×2 filling the screen) rather than
   ratio-driven like .shot is, which is why none of project.css's media rules
   are reused.
   ═══════════════════════════════════════════════════════════════════════════ */

/* ─── The snap ────────────────────────────────────────────────────────────────
   Document scroll, not a scroll container — so the arrow keys, space, page keys
   and the scrollbar all keep working with no JS at all. `mandatory` is what
   makes it magnetic; `scroll-snap-stop: always` on the panel is what stops a
   hard flick from skipping a project.

   Only from 1024 up. Below that a screen cannot hold the text and four images
   at a readable size, so the panels go back to being ordinary stacked blocks
   and the snap comes off entirely. */

@media (min-width: 1024px){
  html{ scroll-snap-type: y mandatory; }
}

/* ─── Panel ───────────────────────────────────────────────────────────────── */

.panel{
  scroll-snap-align: start;
  scroll-snap-stop: always;
  padding: calc(var(--bar) + 40px) var(--gut) var(--gut);
  display: flex;
  gap: 0;
}

@media (min-width: 1024px){
  /* svh, not vh — on a phone-shaped window vh is the tall viewport and the
     bottom row of images would sit under the browser's own chrome. */
  .panel{ height: 100svh; }
  @supports not (height: 100svh){ .panel{ height: 100vh; } }
}

/* ─── Info column ─────────────────────────────────────────────────────────────
   Same third as a project page's, so the two page types line up. This page
   carries no .bar__title: the column names the project a third across, which is
   exactly where the bar would have put it, so the bar would only say it twice. */

.panel__info{
  width: 33.3333%;
  flex: 0 0 33.3333%;
  padding-right: 8%;
  display: flex;
  flex-direction: column;
}

/* An open Project Info can run past the screen. It scrolls inside its own
   block, which keeps the panel exactly one screen tall so the snap still lands.

   Two things keep the wheel honest. The scroller is the lede rather than the
   whole column, so the index at the foot is outside it and never travels. And
   it only becomes a scroll container once a disclosure is actually open —
   fractional line boxes round a closed column a pixel over its own height, and
   as a standing scroll container it would spend a wheel tick on that pixel
   before the page ever moved.

   Chaining is deliberately left on: the wheel reads the rest of the description
   and then carries on to the next project, where overscroll-behavior:contain
   would have swallowed the gesture and left the page feeling stuck. */
.panel__lede{
  min-height: 0;               /* a flex item will not shrink past its content
                                  without this, and then it cannot scroll */
}
.panel__lede:has(.info__disclose[open]){
  overflow-y: auto;
  scrollbar-width: none;
}
.panel__lede:has(.info__disclose[open])::-webkit-scrollbar{ display: none; }

/* ─── Holding the text column ─────────────────────────────────────────────────
   The words are not supposed to travel — the images scroll, the text changes.

   `position: sticky` cannot do this: a sticky box can only move within its own
   containing block, and a panel is exactly one viewport tall, so there is no
   travel to give. The column has to leave the flow and be held against the
   viewport instead. All five then occupy the same rectangle, stacked, and the
   reveal rule below shows exactly one of them — so what the eye sees is one
   column whose contents swap.

   Both this and the media's offset are gated on a panel actually being live, so
   the no-JS case keeps the plain in-flow layout rather than stacking five
   columns of text on top of each other. Widths are in %, not vw: a percentage
   on a fixed box resolves against the initial containing block, which excludes
   a classic scrollbar, and this page always has one. */

@media (min-width: 1024px){
  .js main:has(.panel.is-on) .panel__info{
    position: fixed;
    top: calc(var(--bar) + 40px);
    left: var(--gut);
    width: calc((100% - var(--gut) * 2) / 3);

    /* height, not max-height: out of flow the box would shrink-wrap its text,
       and then the auto margin that pushes the index to the foot of the column
       would have no free space to push into. This is the same height the column
       had as a stretched flex item — the panel's content box. */
    height: calc(100svh - var(--bar) - 40px - var(--gut));
  }

  /* The column is out of flow now, so the one item left in the row would sit
     against the left margin. This puts it back on the same third. */
  .js main:has(.panel.is-on) .panel__media{ margin-left: 33.3333%; }
}

/* The one per-section reveal on the site. It earns the exception because a
   panel is the whole screen: the text is not being revealed inside a page, it
   IS the page changing. Media does not move — only the words.

   The :has() is the safety catch. Nothing is hidden until some panel is
   actually marked live, so if work.js is missing or throws, all five columns
   simply show — and they are a screen apart, so the one frame between first
   paint and the observer firing is not visible.

   Only one rule touches opacity, and it names the panels that are NOT live —
   two rules fighting over it (hide-all plus show-the-live-one) would come down
   to which selector counted more classes, which is not a thing to rely on. */
@media (min-width: 1024px){
  /* The five columns now share one rectangle, so hiding one has to take it out
     of the way properly: opacity alone would leave four invisible Project Info
     summaries stacked over the live one, catching clicks and still being read
     out. visibility transitions discretely — it flips at the far end of the
     fade going out, and at the near end coming in, which is exactly the timing
     a crossfade wants. */
  .js .panel__info{
    transition: opacity .45s var(--ease-out-quart),
                transform .45s var(--ease-out-quart),
                visibility .45s;
  }
  .js main:has(.panel.is-on) .panel:not(.is-on) .panel__info{
    opacity: 0;
    transform: translateY(6px);
    visibility: hidden;
    pointer-events: none;
  }
}

@media (prefers-reduced-motion: reduce){
  .js .panel__info{ transition: none; }
}

/* Index, then the count — the archive's "00/" habit, turned into an orientation
   cue for a page whose end you cannot see. It sits at the foot of the column,
   pushed there by the auto margin, so the top of the column is still the
   project's name as it is on a project page. Quiet, because it is a label. */
.panel__num{
  flex: none;
  margin: auto 0 0;
  padding-top: 22px;
  color: var(--ink-quiet);
  font-variant-numeric: tabular-nums;
}

/* .info__title carries the 22px below it; the discipline has to sit inside that
   space rather than add to it, so the title's margin comes off here. */
.panel__lede .info__title{ margin-bottom: 6px; }

.panel__role{
  margin: 0 0 22px;
  color: var(--ink-quiet);
}

/* ─── Media ───────────────────────────────────────────────────────────────────
   A 2×2 filling whatever height the panel has left. Every cell is the same
   size, and the cell's ratio is the media box's own — about 473×408, or 1.16, at
   a laptop's 1440×900. Most of these images run wider than that, which is what
   the sizing rule below is about.

   Four cells is the whole budget. A project with more than four images spends
   the extra ones as slides in a carousel cell, which is also how to cut one:
   delete the slide, not the tile. */

.panel__media{
  width: 66.6667%;
  flex: 0 0 66.6667%;
  display: grid;
  grid-template-columns: 1fr 1fr;
  grid-template-rows: 1fr 1fr;
  gap: var(--gap-media);          /* 1px — declared in project.css */
  min-height: 0;                  /* or the grid refuses to shrink to the panel
                                     and the last row spills past the fold */
}

.tile{
  position: relative;
  overflow: hidden;
  margin: 0;
  min-height: 0;
  background: var(--media-ground);
}

/* ─── How an image meets its cell ─────────────────────────────────────────────
   Width first: every image is laid at the full width of its cell and keeps its
   own ratio, centred vertically.

   This is the only setting that satisfies both halves of the brief. `contain`
   leaves a picture floating in its cell with ground down both sides. `cover`
   fills the cell but takes the difference off whichever axis is long — and on
   these images that axis is usually the horizontal one, so it was slicing the
   right-hand page off a brochure spread. Fitting the width instead means
   **nothing is ever cut off at the sides**: an image wider than its cell simply
   sits shorter than it, with a band of ground above and below, and only an image
   taller than its cell loses anything, off its top and bottom.

   Because the box then has the image's own ratio, object-fit has nothing left to
   do — `fill` says so plainly rather than leaving a `cover` that reads as though
   it were still cropping. */
.tile > img,
.tile__caro .caro__slide img{
  position: absolute;
  top: 50%;
  left: 0;
  width: 100%;
  height: auto;
  display: block;
  transform: translateY(-50%);
  object-fit: fill;
}

/* ─── Plates ──────────────────────────────────────────────────────────────────
   Three of these files are transparent PNGs — both brochure mockups at about a
   fifth of their pixels, the Knoll poster at seven tenths. A transparent image
   has no ground of its own, so it composites onto whatever happens to be
   behind: the tile's matte at best, the slide underneath at worst.

   The plate is white in both themes, deliberately. These are mockups of printed
   matter, drawn with soft paper shadows and cut out against white — on the dark
   ground those shadows read as grime around the sheet. The artwork assumes
   white, so the plate gives it white. */
.tile--plate,
.caro__slide--plate{ background: #fff; }

/* ─── Carousel cell ───────────────────────────────────────────────────────────
   carousel.js drives anything with [data-caro] holding .caro__slide children.
   The crossfade comes styled from project.css; all this has to do is be the
   frame the slides lay against — and, here, the control that advances them.

   The cell is the control. project.css's arrow is a small ring in the corner of
   the frame, which on a tile this size is a speck sitting on top of the picture
   — so these carry `data-caro-tap` instead, and carousel.js hands the click to
   the container rather than drawing an arrow at all.

   It is a real <button>, not a span with a listener: that is what makes it
   reachable by tab, operable by Enter and Space, and announced as a control.
   Everything below is the button reset — a button brings its own border,
   background, padding and font, none of which belong on a picture. */
.tile__caro{
  position: absolute;
  inset: 0;
  appearance: none;
  -webkit-appearance: none;
  margin: 0;
  padding: 0;
  border: 0;
  background: none;
  font: inherit;
  color: inherit;
  display: block;
  cursor: pointer;      /* the only standing hint that the picture cycles */
}
/* The ring would otherwise sit under the images, which fill the button. */
.tile__caro:focus-visible{ outline-offset: -3px; }
.tile__caro:disabled{ cursor: default; }

/* ─── Narrow ──────────────────────────────────────────────────────────────────
   No snap, no fixed heights: each panel is an ordinary block, text then images.
   The cells lose their row height with the grid, so they carry a ratio instead
   — the same 4:3 the desktop cell is close to. */

@media (max-width: 1023px){
  .panel{
    display: block;
    padding: 24px var(--gut) 12svh;
  }
  .panel:first-of-type{ padding-top: calc(var(--bar) + 24px); }

  .panel__info{
    width: auto;
    padding-right: 0;
    display: block;
  }
  /* Nothing is held or clipped down here — the description just makes the page
     longer, the way it does on a project page. */
  .panel__lede{ overflow: visible; }
  .panel__num{ margin: 0; padding-top: 14px; }

  .panel__media{
    width: auto;
    margin-top: 6svh;
    grid-template-rows: none;
    grid-auto-rows: auto;
  }
  .tile{ aspect-ratio: 4 / 3; }
}

@media (max-width: 639px){
  .panel__media{ grid-template-columns: 1fr; }
}
