/* =====================================================================
   MyvanCMS — project / unit photo gallery
   ---------------------------------------------------------------------
   The static site ships no gallery at all, so there is nothing to copy.
   Everything here is built from the tokens palace.css already defines
   (--pl-ink, --pl-brand, --pl-line, --pl-panel) and the motion vocabulary
   the cards already use (.project-card lifts 4px in .3s; its image scales
   1.06 in .6s ease). Bootstrap 5 is loaded but its grid is deliberately NOT
   used for the tiles: a Bootstrap row cannot express "this cell is two
   columns wide AND two rows tall", which is the whole point of the layout.

   THE LAYOUT SYSTEM ------------------------------------------------------
   A CSS grid with an explicit column count and FIXED-HEIGHT auto rows:

     grid-template-columns : repeat(4, 1fr)
     grid-auto-rows        : var(--pgal-row)      <- a real length, not auto
     first child           : grid-column: span 2 / grid-row: span 2

   Fixed rows matter for two reasons.

   1. DETERMINISM. If the rows were auto-sized they would be derived from the
      tiles' own aspect-ratio, and a 2-image gallery (feature + one supporting
      tile) would leave row 2 with no auto-height contributor — the feature
      would silently collapse to a single row tall. With a fixed row height the
      feature is exactly 2 rows + 1 gap tall for ANY number of images.
   2. NO CLS. The grid's height is known from the stylesheet before a single
      byte of image data arrives, so nothing reflows as the photos decode.
      Each <img> is absolutely positioned to fill its tile with object-fit:
      cover, which is also why no width/height attributes are written into the
      markup: img_src() falls back to the ORIGINAL file when a -380x250
      derivative is missing, so any hardcoded intrinsic size would be a guess.
      The tile owns the geometry; the photo is cropped into it.

   Because the feature spans 2 of 4 columns, the supporting tiles fill the
   remaining 2x2 block beside it and then run 4-across underneath — no gaps,
   no reordering, and DOM order is preserved (which CSS multi-column could not
   promise). At each breakdown the column count drops but the feature keeps its
   2x2 span, so it stays proportionally dominant, until the single-column
   mobile layout where it becomes a taller full-width lead image FIRST.
   ===================================================================== */

.pgal{
  /* Row height is the one number the whole layout hangs off. Tuned so a tile
     lands near the 380x250 (1.52) aspect of the thumbnails it serves. */
  --pgal-row:clamp(150px,15vw,215px);
  --pgal-gap:18px;
  --pgal-radius:16px;
}

/* --- the grid ------------------------------------------------------- */
.pgal-grid{
  display:grid;
  grid-template-columns:repeat(4,1fr);
  grid-auto-rows:var(--pgal-row);
  gap:var(--pgal-gap);
}

/* --- a tile --------------------------------------------------------- */
/*
   Each tile is a <button>: clicking it opens a dialog, which is a button's
   job, not a link's. That means resetting the UA button styling completely.
*/
.pgal-item{
  position:relative;
  display:block;
  padding:0;
  margin:0;
  border:0;
  overflow:hidden;
  border-radius:var(--pgal-radius);
  background:var(--pl-panel);   /* a calm placeholder while the photo decodes */
  cursor:zoom-in;
  appearance:none;
  -webkit-appearance:none;
  font:inherit;
  color:inherit;
  text-align:left;
  box-shadow:0 6px 20px rgba(27,21,24,.07);
  transition:box-shadow .3s ease,transform .3s ease;
}

.pgal-item > img{
  position:absolute;
  inset:0;
  width:100%;
  height:100%;
  object-fit:cover;
  display:block;
  /* Same 1.06 / .6s ease scale the project + blog cards already use. */
  transition:transform .6s ease;
}

/* THE FEATURE TILE — two columns wide, two rows tall. */
.pgal-item--feature{
  grid-column:span 2;
  grid-row:span 2;
}

/* --- the gradient + caption ----------------------------------------
   A base gradient sits at low opacity so a pale photo edge never runs into
   the white page, exactly as blog-card.css does for the post covers. On hover
   it deepens and the caption rises into it.
   ------------------------------------------------------------------ */
.pgal-item::after{
  content:"";
  position:absolute;
  inset:auto 0 0 0;
  height:62%;
  background:linear-gradient(to top,rgba(27,21,24,.82),rgba(27,21,24,.28) 45%,transparent);
  opacity:.42;
  pointer-events:none;
  transition:opacity .32s ease;
}

.pgal-cap{
  position:absolute;
  inset:auto 0 0 0;
  z-index:2;
  padding:14px 16px;
  color:#fff;
  font-size:.875rem;
  font-weight:600;
  line-height:1.45;
  letter-spacing:.01em;
  text-shadow:0 1px 10px rgba(27,21,24,.55);
  opacity:0;
  transform:translateY(10px);
  transition:opacity .32s ease,transform .32s ease;
  /* Long captions clamp rather than covering the photograph. */
  display:-webkit-box;
  -webkit-line-clamp:2;
  -webkit-box-orient:vertical;
  overflow:hidden;
}
.pgal-item--feature .pgal-cap{
  padding:20px 22px;
  font-size:1rem;
  -webkit-line-clamp:3;
}

/* The expand affordance — tells you the tile is clickable before you click. */
.pgal-zoom{
  position:absolute;
  top:12px;
  right:12px;
  z-index:2;
  width:38px;
  height:38px;
  display:grid;
  place-items:center;
  border-radius:50%;
  background:rgba(255,255,255,.94);
  color:var(--pl-brand);
  font-size:1rem;
  opacity:0;
  transform:scale(.82);
  transition:opacity .28s ease,transform .28s ease;
  pointer-events:none;
  box-shadow:0 6px 18px rgba(27,21,24,.22);
}

/* --- hover + keyboard focus get the SAME treatment ------------------ */
.pgal-item:hover,
.pgal-item:focus-visible{
  transform:translateY(-4px);
  box-shadow:0 22px 48px rgba(27,21,24,.16);
}
.pgal-item:hover > img,
.pgal-item:focus-visible > img{transform:scale(1.06)}

.pgal-item:hover::after,
.pgal-item:focus-visible::after{opacity:1}

.pgal-item:hover .pgal-cap,
.pgal-item:focus-visible .pgal-cap{opacity:1;transform:translateY(0)}

.pgal-item:hover .pgal-zoom,
.pgal-item:focus-visible .pgal-zoom{opacity:1;transform:scale(1)}

.pgal-item:focus-visible{
  outline:3px solid var(--pl-brand);
  outline-offset:3px;
}

/* =====================================================================
   THE LIGHTBOX
   Built by gallery.js and appended to <body> once, so it is never nested
   inside a transformed / overflow-hidden ancestor that would clip it.
   ===================================================================== */
.pgal-lb{
  position:fixed;
  inset:0;
  z-index:2000;
  display:none;
  /* The scrim. Not pure black — the ink token keeps it in the palette. */
  background:rgba(20,15,17,.94);
  -webkit-backdrop-filter:blur(6px);
  backdrop-filter:blur(6px);
  opacity:0;
  transition:opacity .26s ease;
}
.pgal-lb.is-open{display:block}
.pgal-lb.is-visible{opacity:1}

/* Layout: a stage that takes all remaining height, with a bar above and a
   caption/counter strip below, so the photo is never covered by the chrome. */
.pgal-lb__inner{
  position:absolute;
  inset:0;
  display:flex;
  flex-direction:column;
  padding:14px clamp(14px,4vw,72px) 18px;
}

.pgal-lb__bar{
  flex:0 0 auto;
  display:flex;
  align-items:center;
  justify-content:space-between;
  gap:16px;
  min-height:48px;
  color:rgba(255,255,255,.72);
}

.pgal-lb__count{
  font-size:.8125rem;
  font-weight:700;
  letter-spacing:.14em;
  text-transform:uppercase;
}

.pgal-lb__stage{
  flex:1 1 auto;
  position:relative;
  display:flex;
  align-items:center;
  justify-content:center;
  min-height:0;              /* lets the flex child actually shrink */
}

.pgal-lb__fig{
  margin:0;
  max-width:100%;
  max-height:100%;
  display:flex;
  align-items:center;
  justify-content:center;
}

.pgal-lb__img{
  max-width:100%;
  max-height:100%;
  width:auto;
  height:auto;
  display:block;
  object-fit:contain;
  border-radius:8px;
  box-shadow:0 30px 90px rgba(0,0,0,.55);
  opacity:0;
  transition:opacity .28s ease;
}
.pgal-lb__img.is-loaded{opacity:1}

/* A quiet spinner for the moment between opening and the full-size arriving. */
.pgal-lb__spin{
  position:absolute;
  width:34px;
  height:34px;
  border-radius:50%;
  border:2px solid rgba(255,255,255,.22);
  border-top-color:#fff;
  animation:pgal-spin .8s linear infinite;
  opacity:0;
  transition:opacity .2s ease;
  pointer-events:none;
}
.pgal-lb.is-loading .pgal-lb__spin{opacity:1}
@keyframes pgal-spin{to{transform:rotate(360deg)}}

.pgal-lb__foot{
  flex:0 0 auto;
  min-height:46px;
  padding-top:12px;
  text-align:center;
  color:rgba(255,255,255,.9);
  font-size:.9375rem;
  line-height:1.6;
  max-width:70ch;
  margin-inline:auto;
}

/* --- lightbox controls --------------------------------------------- */
.pgal-lb__btn{
  appearance:none;
  -webkit-appearance:none;
  border:1px solid rgba(255,255,255,.24);
  background:rgba(255,255,255,.08);
  color:#fff;
  border-radius:50%;
  display:grid;
  place-items:center;
  cursor:pointer;
  padding:0;
  transition:background .22s ease,border-color .22s ease,transform .22s ease;
}
.pgal-lb__btn:hover{
  background:var(--pl-brand);
  border-color:var(--pl-brand);
}
.pgal-lb__btn:focus-visible{
  outline:3px solid #fff;
  outline-offset:3px;
}

.pgal-lb__close{
  width:46px;
  height:46px;
  font-size:1.35rem;
  line-height:1;
}

.pgal-lb__nav{
  position:absolute;
  top:50%;
  transform:translateY(-50%);
  width:54px;
  height:54px;
  font-size:1.5rem;
  z-index:3;
}
.pgal-lb__nav:hover{transform:translateY(-50%) scale(1.06)}
.pgal-lb__prev{left:clamp(-6px,-1vw,0px)}
.pgal-lb__next{right:clamp(-6px,-1vw,0px)}
/* A one-image gallery has nothing to navigate to; the buttons are removed
   from the DOM flow AND from the focus trap. */
.pgal-lb.is-single .pgal-lb__nav{display:none}

/* =====================================================================
   RESPONSIVE — the column count drops, the 2x2 feature span survives
   ===================================================================== */

/* Tablet landscape: 4 -> 3 columns. The feature keeps cols 1-2 / rows 1-2 and
   the supporting tiles stack down column 3. */
@media (max-width:991.98px){
  .pgal{--pgal-row:clamp(140px,20vw,190px);--pgal-gap:14px}
  .pgal-grid{grid-template-columns:repeat(3,1fr)}
}

/* Tablet portrait / large phone: 2 columns, so the feature is full-bleed and
   twice as tall, then pairs beneath it. */
@media (max-width:767.98px){
  .pgal{--pgal-row:clamp(130px,26vw,170px)}
  .pgal-grid{grid-template-columns:repeat(2,1fr)}
}

/* Phone: ONE column. The span is cancelled and the rows go back to auto,
   sized by aspect-ratio, because a fixed row height at this width would
   letterbox badly. The feature stays FIRST (DOM order) and taller. */
@media (max-width:575.98px){
  .pgal{--pgal-gap:12px;--pgal-radius:14px}
  .pgal-grid{
    grid-template-columns:1fr;
    grid-auto-rows:auto;
  }
  .pgal-item{aspect-ratio:16/10}
  .pgal-item--feature{
    grid-column:auto;
    grid-row:auto;
    aspect-ratio:4/3;
  }
  /* No hover on touch, so the caption is simply always legible. */
  .pgal-cap{opacity:1;transform:none}
  .pgal-item::after{opacity:.85}
  .pgal-zoom{display:none}

  .pgal-lb__inner{padding:10px 10px 14px}
  .pgal-lb__nav{width:44px;height:44px;font-size:1.25rem}
  .pgal-lb__prev{left:2px}
  .pgal-lb__next{right:2px}
  .pgal-lb__foot{font-size:.875rem}
}

/* Coarse pointers of any size: never hide a caption behind a hover. */
@media (hover:none){
  .pgal-cap{opacity:1;transform:none}
  .pgal-item::after{opacity:.85}
  .pgal-zoom{display:none}
  .pgal-item{cursor:pointer}
}

/* =====================================================================
   REDUCED MOTION — no transforms, no transitions. Everything that was
   revealed by movement is simply revealed.
   ===================================================================== */
@media (prefers-reduced-motion:reduce){
  .pgal-item,
  .pgal-item > img,
  .pgal-item::after,
  .pgal-cap,
  .pgal-zoom,
  .pgal-lb,
  .pgal-lb__img,
  .pgal-lb__btn,
  .pgal-lb__spin{transition:none}

  .pgal-item:hover,
  .pgal-item:focus-visible{transform:none}
  .pgal-item:hover > img,
  .pgal-item:focus-visible > img{transform:none}
  .pgal-lb__nav:hover{transform:translateY(-50%)}
  .pgal-zoom{transform:none}
  .pgal-cap{transform:none}

  /* The spinner would be the only thing still moving. */
  .pgal-lb__spin{animation:none;border-top-color:rgba(255,255,255,.22)}
}
