/* ==========================================================================
   ABTB 2026 — project page: description, photo grid, lightbox.

   Hand-written, unlike abtb-2026-{trendy,elevated}.css which are generated from
   the prototypes. Loaded only by the project page templates, on top of the skin
   stylesheet, and it reads that file's tokens (--ink, --accent, --line, …) so it
   follows the theme without redefining anything.

   The photo grid needs no JS row engine: every tile is one column with the same
   aspect-ratio, so rows are uniform by construction. The engine in abtb-2026.js
   exists for the listing grids, where cards span two columns.
   ========================================================================== */

/* ------------------------------------------------------------------ intro */
.project-lead {
  max-width: 68ch;
  color: var(--ink-soft);
  font-size: 17px;
  line-height: 1.65;
}
.project-lead p { margin: 0 0 1em; }
.project-lead p:last-child { margin-bottom: 0; }
.project-lead sup { font-size: .7em; }
.project-lead a { color: var(--accent); text-decoration: underline; text-underline-offset: 2px; }

/* facts row under the title: year, tag list */
.project-facts {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 10px 22px;
  margin: 0 0 26px;
  font-size: 13.5px;
  color: var(--ink-soft);
}
.project-facts .fact { display: inline-flex; align-items: center; gap: 8px; }
.project-facts .fact-label {
  font-size: 10.5px;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--ink-faint);
}
.project-facts .fact-value { color: var(--ink); font-weight: 600; }
.project-facts .fact-tags { display: inline-flex; flex-wrap: wrap; gap: 6px; }
.project-facts .fact-tags span {
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: .07em;
  text-transform: uppercase;
  color: var(--accent);
  background: var(--accent-soft);
  padding: 3px 9px;
  border-radius: 100px;
}

/* ------------------------------------------------------------- photo grid */
.photo-section { padding: clamp(28px, 5vw, 52px) 0 clamp(48px, 8vw, 90px); }
.photo-section h2 {
  font-size: clamp(20px, 2.6vw, 26px);
  margin: 0 0 6px;
  color: var(--ink);
}
.photo-count { font-size: 13.5px; color: var(--ink-soft); margin: 0 0 22px; }
.photo-count strong { color: var(--accent); font-weight: 700; }

/* Justified rows, not a uniform grid.

   The first version put every photograph in a 4/3 tile with object-fit: cover.
   On the live Реновация касс page two of the four shots are portrait
   (2250x3000), so a third of each picture was cropped away to make it fit a
   landscape thumbnail. For an architecture practice the proportions of a
   photograph are part of the work.

   Each tile now publishes its own ratio as --ar (from the real pixel size the
   CMS stores) and asks for exactly the width that ratio needs at the row
   height. flex-grow is set to the same ratio, so the leftover pixels on each
   line are shared out in proportion to width: wide photographs stay wide, tall
   ones stay tall, and the rows still come out flush on both edges. The residual
   crop is a percent or two rather than a third of the frame.

   No JS: the browser's own line-breaking does the work the row engine in
   abtb-2026.js does for the spanning cards on the listing grids. */
.photo-grid {
  display: flex;
  flex-wrap: wrap;
  gap: clamp(8px, 1.2vw, 14px);
}
/* Without this the final line stretches its one or two survivors across the
   whole width. A greedy invisible item soaks up the leftover instead, so the
   last row keeps the same scale as the rest. */
.photo-grid::after { content: ''; flex-grow: 999999; }

.photo-item {
  --ar: 1.5;                              /* overwritten inline per photo */
  --row-h: clamp(170px, 22vw, 290px);
  position: relative;
  display: block;
  flex: var(--ar) 1 calc(var(--ar) * var(--row-h));
  height: var(--row-h);
  min-width: 0;
  border: 0;
  padding: 0;
  border-radius: 14px;
  overflow: hidden;
  background: var(--paper);
  cursor: zoom-in;
  transition: box-shadow .25s ease;
}
.photo-item img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  transition: transform .5s cubic-bezier(.2,.8,.2,1);
}
.photo-item:hover { box-shadow: 0 18px 36px -18px var(--glow, rgba(0,0,0,.35)); }
.photo-item:hover img { transform: scale(1.045); }
.photo-item:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }

/* The opening shot runs the full width at its own proportions, capped so a
   portrait photograph cannot fill the whole screen before anything else. */
.photo-item.lead {
  flex: 1 1 100%;
  height: auto;
  aspect-ratio: var(--ar);
  max-height: min(74vh, 700px);
}

/* On a phone a justified row is two postage stamps. One photograph per line at
   its own proportions is the whole point of the page. */
@media (max-width: 560px) {
  .photo-item {
    flex: 1 1 100%;
    height: auto;
    aspect-ratio: var(--ar);
    max-height: 76vh;
  }
}

/* A real caption from the CMS, shown over the foot of the tile. Upload
   filenames ("7_final_22") are dropped in the template, so anything that
   reaches here is something a person wrote. */
.photo-caption {
  position: absolute;
  left: 0; right: 0; bottom: 0;
  padding: 26px 14px 11px;
  font-size: 12.5px;
  line-height: 1.35;
  text-align: left;
  color: #fff;
  background: linear-gradient(to top, rgba(6,8,14,.72), rgba(6,8,14,0));
  opacity: 0;
  transform: translateY(6px);
  transition: opacity .22s ease, transform .22s ease;
  pointer-events: none;
}
.photo-item:hover .photo-caption,
.photo-item:focus-visible .photo-caption { opacity: 1; transform: none; }

@media (prefers-reduced-motion: reduce) {
  .photo-item, .photo-item img, .photo-caption { transition: none; }
  .photo-item:hover img { transform: none; }
}

/* --------------------------------------------------------------- lightbox */
.lightbox {
  position: fixed;
  inset: 0;
  z-index: 200;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(6, 8, 14, .93);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
  opacity: 0;
  visibility: hidden;
  transition: opacity .22s ease, visibility .22s;
  /* On a tablet a double tap was zooming the whole PAGE to wherever the finger
     landed -- the browser's own gesture, on top of the viewer's. `manipulation`
     switches that one off and keeps pinch and pan, which are the two a person
     actually wants on a plan. */
  touch-action: manipulation;
  /* scrolling the zoomed photograph must not start scrolling the page behind */
  overscroll-behavior: contain;
}
.lightbox.open { opacity: 1; visibility: visible; }
.lightbox-figure {
  position: relative;          /* the loading spinner is ::after on this box */
  margin: 0;
  max-width: min(1400px, 94vw);
  max-height: 88svh;
  display: flex;
  flex-direction: column;
  gap: 12px;
  align-items: center;
}
.lightbox-figure img {
  max-width: 100%;
  /* leave room for the caption so a tall photo cannot push it off screen */
  max-height: calc(88svh - 52px);
  object-fit: contain;
  border-radius: 8px;
  background: #0b0d13;
}
.lightbox-caption {
  color: rgba(255,255,255,.82);
  font-size: 13.5px;
  text-align: center;
  max-width: 70ch;
}

/* click the photo to switch between fit-to-screen and true size: on a plan or
   a section the detail is only readable at 1:1 */
/* Лупа на курсоре — обещание, которое выполняется только мышью: пальцем
   увеличение по нажатию отключено (см. abtb-2026-gallery.js). Там, где курсора
   нет, и обещать нечего. */
@media (hover: hover) and (pointer: fine) {
  .lightbox-figure img { cursor: zoom-in; }
}
.lightbox.zoomed .lightbox-figure {
  max-width: 100vw;
  max-height: 100svh;
  overflow: auto;
  /* the enlarged photograph is dragged with a finger, so panning is allowed
     here even though the box above forbids the browser's own gestures */
  touch-action: pan-x pan-y;
  -webkit-overflow-scrolling: touch;
}
.lightbox.zoomed .lightbox-figure img {
  max-width: none;
  max-height: none;
  width: auto;
  height: auto;
  cursor: zoom-out;
}
.lightbox.zoomed .lightbox-caption { display: none; }

/* a full-size architectural photograph takes a moment even on a good line */
.lightbox-figure::after {
  content: '';
  position: absolute;
  top: 50%; left: 50%;
  width: 34px; height: 34px;
  margin: -17px 0 0 -17px;
  border: 2px solid rgba(255,255,255,.25);
  border-top-color: rgba(255,255,255,.9);
  border-radius: 100px;
  opacity: 0;
  pointer-events: none;
  animation: lightbox-spin .8s linear infinite;
}
.lightbox.loading .lightbox-figure::after { opacity: 1; }
.lightbox.loading .lightbox-figure img { opacity: .35; }
.lightbox-figure img { transition: opacity .2s ease; }
@keyframes lightbox-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) {
  .lightbox-figure::after { animation: none; }
}

.lightbox-close,
.lightbox-nav {
  position: absolute;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 46px;
  height: 46px;
  border-radius: 100px;
  border: 1px solid rgba(255,255,255,.22);
  background: rgba(255,255,255,.08);
  color: #fff;
  cursor: pointer;
  transition: background .18s ease, border-color .18s ease, transform .18s ease;
}
.lightbox-close:hover,
.lightbox-nav:hover { background: rgba(255,255,255,.18); border-color: rgba(255,255,255,.5); }
.lightbox-close:focus-visible,
.lightbox-nav:focus-visible { outline: 2px solid #fff; outline-offset: 2px; }
.lightbox-close svg, .lightbox-nav svg { width: 20px; height: 20px; }
.lightbox-close { top: 18px; right: 18px; }
.lightbox-nav.prev { left: 18px; }
.lightbox-nav.next { right: 18px; }
@media (max-width: 640px) {
  .lightbox-nav { bottom: 18px; top: auto; width: 44px; height: 44px; }
  .lightbox-nav.prev { left: calc(50% - 56px); }
  .lightbox-nav.next { right: calc(50% - 56px); }
}

.lightbox-counter {
  position: absolute;
  top: 22px;
  left: 22px;
  color: rgba(255,255,255,.7);
  font-size: 13px;
  font-variant-numeric: tabular-nums;
}

/* a single-photo album has nothing to page through */
.lightbox.single .lightbox-nav,
.lightbox.single .lightbox-counter { display: none; }

/* `overflow: hidden` alone does NOT stop touch scrolling on iOS and iPadOS:
   the page kept moving under the open viewer. The body is fixed instead, the
   same way the mobile menu does it, and the script puts the scroll position
   back on close. */
body.lightbox-open {
  position: fixed;
  width: 100%;
  overflow: hidden;
}

/* --------------------------------------------------------------------------
   One photograph on its own page.

   UMI gives every photo in an album its own URL, and those URLs are indexed
   and shared whether or not anyone meant them to be. The page carries noindex
   and a canonical to the project, but a person who lands on one still gets a
   real page: the picture at its own proportion, and the way back.
   -------------------------------------------------------------------------- */
.photo-single {
  margin: 0 0 clamp(40px, 7vw, 88px);
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 22px;
}
.photo-single img {
  width: 100%;
  height: auto;
  max-height: 78vh;
  object-fit: contain;
  display: block;
  border-radius: 16px;
  background: var(--paper);
}
.photo-single figcaption { margin: 0; }
