/* ============================================================================
   Custom Photo Viewer — ramy.photos
   ----------------------------------------------------------------------------
   Two-column desktop viewer (image dominant, quiet metadata rail) with a mobile
   bottom-sheet layout. Pairs with /storage/custom/photo-viewer.js.

   Design intent: gallery wall, not social feed. One continuous dark ground with
   no panels or cards; separation comes from space and hairlines. Type inherits
   the theme's Source Sans 3 and echoes its uppercase, wide-tracked labels so the
   viewer reads as part of the site rather than an overlay bolted onto it.

   Not an IO200 file. Safe across IO200 updates.
   ========================================================================== */

.pv {
  /* ---- ground: one value, shared by stage and rail ---- */
  --pv-bg:          #0b0b0b;

  /* ---- ink ---- */
  --pv-ink:         #ededed;   /* titles */
  --pv-ink-2:       #969696;   /* body */
  --pv-ink-3:       #616161;   /* labels, quiet meta */
  --pv-ink-4:       #3d3d3d;   /* disabled */

  /* ---- lines: hairlines only, never boxes ---- */
  --pv-hair:        rgba(255, 255, 255, .07);
  --pv-hair-strong: rgba(255, 255, 255, .14);

  --pv-focus:       #7fb2e8;

  /* ---- metrics ---- */
  --pv-aside-w:     clamp(300px, 19vw, 420px);
  --pv-pad:         2rem;
  --pv-sheet-peek:  46px;
  /* Sheet height when open. Under two thirds so a 3:2 photo stays fully
     visible above it — the image is never buried by its own metadata. */
  --pv-sheet-h:     64dvh;
  --pv-ease:        cubic-bezier(.32, .72, 0, 1);

  position: fixed;
  inset: 0;
  z-index: 1000000;               /* above GLightbox (999999) */
  display: grid;
  grid-template-columns: 1fr var(--pv-aside-w);
  height: 100dvh;
  background: var(--pv-bg);
  color: var(--pv-ink-2);
  font-family: inherit;           /* Source Sans 3, from the theme */
  font-size: 15px;
  line-height: 1.6;
  letter-spacing: .01em;
  -webkit-font-smoothing: antialiased;
  overscroll-behavior: contain;
}

.pv[hidden] { display: none; }

html.pv-locked, html.pv-locked body { overflow: hidden !important; }

.pv *,
.pv *::before,
.pv *::after { box-sizing: border-box; }

/* Elements below set an explicit `display`, which would otherwise beat the
   user-agent [hidden] rule. JS toggles .hidden on these, so make it win. */
.pv [hidden] { display: none !important; }


/* ==========================================================================
   STAGE — the photograph. Nothing here may consume layout height.
   ========================================================================== */

.pv__stage {
  position: relative;
  display: grid;
  place-items: center;
  min-width: 0;
  min-height: 0;
  overflow: hidden;
  background: var(--pv-bg);
}

/* Fills the stage's content box, so the mobile sheet padding is respected.
   Both image layers stack inside it — no reflow between photos, ever. */
.pv__frame {
  position: relative;
  width: 100%;
  height: 100%;
}

.pv__img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: contain;
  opacity: 0;
  transition: opacity .32s ease;
  pointer-events: none;
}

.pv__img.is-active { opacity: 1; }

/* While the next photo decodes, the outgoing one stays put and dims very
   slightly — motion without a black flash. */
.pv.is-loading .pv__img.is-active { opacity: .55; }


/* ---- loading indicator: a hairline sweep, not a spinner ---- */

.pv__progress {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 1px;
  overflow: hidden;
  opacity: 0;
  transition: opacity .2s ease;
  pointer-events: none;
}

.pv.is-loading .pv__progress { opacity: 1; }

.pv__progress::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(90deg,
              transparent, var(--pv-ink-2) 45%, var(--pv-ink) 50%, var(--pv-ink-2) 55%, transparent);
  transform: translateX(-100%);
}

.pv.is-loading .pv__progress::after { animation: pv-sweep 1.15s ease-in-out infinite; }

@keyframes pv-sweep {
  from { transform: translateX(-100%); }
  to   { transform: translateX(100%); }
}


/* ==========================================================================
   CONTROLS — bare marks, no chips or circles. A drop-shadow keeps them
   legible over bright frames (snow, sand, sky) without drawing a container.
   ========================================================================== */

/* One chrome for every control: counter, close, prev, next. A bare mark with a
   drop-shadow was legible on most frames but undefined on all of them — it read
   as text lying on the photograph rather than as a control placed over it. A
   translucent near-black chip with a hairline edge holds contrast against snow
   and against a night sky without becoming a browser-style button. */
.pv__btn,
.pv__counter {
  background: rgba(12, 12, 12, .72);        /* fallback: no blur, so more opaque */
  border: 1px solid rgba(255, 255, 255, .13);
  color: #f2f2f2;
  box-shadow: 0 2px 10px rgba(0, 0, 0, .35);
}

/* Where the platform can blur, the chip can be lighter and the photograph reads
   through it. Saturate a little so the blur does not grey out a colourful frame. */
@supports ((backdrop-filter: blur(10px)) or (-webkit-backdrop-filter: blur(10px))) {
  .pv__btn,
  .pv__counter {
    background: rgba(12, 12, 12, .58);
    -webkit-backdrop-filter: blur(10px) saturate(115%);
    backdrop-filter: blur(10px) saturate(115%);
  }
}

.pv__btn {
  position: absolute;
  display: grid;
  place-items: center;
  padding: 0;
  border-radius: 50%;
  cursor: pointer;
  opacity: 1;
  transition: background .18s ease, border-color .18s ease, transform .18s ease;
}

.pv__btn:hover {
  background: rgba(20, 20, 20, .78);
  border-color: rgba(255, 255, 255, .22);
}

.pv__btn:active { transform: scale(.94); }
.pv__btn svg { width: 30px; height: 30px; fill: currentColor; display: block; }

/* Unchanged: a nav control at the end of the album disappears rather than
   sitting there greyed out. */
.pv__btn[disabled] { opacity: 0; pointer-events: none; }

.pv__nav {
  top: 50%;
  width: 48px;
  height: 48px;
  margin-top: -24px;
}

/* 20px in from the stage edge — enough to read as placed rather than pinned.
   The stage is the positioning context, so these can never reach the sidebar. */
.pv__nav--prev { left: 20px; }
.pv__nav--next { right: 20px; }
.pv__nav svg { width: 24px; height: 24px; }

.pv__close { top: 22px; right: 22px; width: 46px; height: 46px; }
.pv__close svg { width: 23px; height: 23px; }

.pv__counter {
  position: absolute;
  top: 24px;
  left: 24px;
  padding: 7px 11px;
  border-radius: 999px;
  font-size: 13px;
  font-weight: 600;
  letter-spacing: .10em;         /* tighter than before: a pill needs less air */
  line-height: 1;
  font-variant-numeric: tabular-nums;
  pointer-events: none;
}


/* ==========================================================================
   RAIL — same ground as the stage, separated by a hairline and by space.
   ========================================================================== */

.pv__aside {
  display: flex;
  flex-direction: column;
  min-width: 0;
  min-height: 0;
  background: var(--pv-bg);
  border-left: 1px solid var(--pv-hair);
}

.pv__handle { display: none; }

.pv__scroll {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
  display: flex;
  flex-direction: column;
  gap: 1.5rem;
  padding: calc(var(--pv-pad) + .4rem) var(--pv-pad) var(--pv-pad);
  scrollbar-width: thin;
  scrollbar-color: var(--pv-ink-4) transparent;
}

.pv__scroll::-webkit-scrollbar { width: 3px; }
.pv__scroll::-webkit-scrollbar-thumb { background: var(--pv-ink-4); }

.pv__section > * + * { margin-top: .75rem; }

.pv__label {
  font-size: 1rem;
  font-weight: 600;
  letter-spacing: .18em;
  text-transform: uppercase;
  color: var(--pv-ink-3);
  margin: 0 0 .7rem;
}

.pv__title {
  font-size: 2.25rem;
  font-weight: 400;
  line-height: 1.3;
  letter-spacing: .015em;
  color: var(--pv-ink);
  margin: 0;
  text-wrap: balance;
}

.pv__title.is-untitled { color: var(--pv-ink-3); }

/* Museum caption: the title sits on its own, with air beneath it, before the
   description begins. Separation by space, never by a rule. */
.pv__info .pv__desc { margin-top: 1.35rem; }

.pv__desc {
  font-size: 1.125rem;
  font-weight: 300;
  line-height: 1.68;
  color: var(--pv-ink-2);
  margin: 0;
}

/* ---- promoted facets: capture year / camera / location ----
   Two-column caption rows — label left, value right — with labels aligned
   across rows. Each row is omitted entirely when the photo has no tag for it,
   so nothing reserves empty space. ---- */

.pv__facets {
  display: grid;
  grid-template-columns: minmax(0, max-content) minmax(0, 1fr);
  gap: .35rem .875rem;
  align-items: baseline;
  line-height: 1.4;              /* tighter than the rail's 1.6, so rows stack */
}

.pv__facet-label {
  margin: 0;
  font-size: 1rem;
  font-weight: 600;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--pv-ink-3);
  white-space: nowrap;
}

.pv__facet-values {
  display: flex;
  flex-wrap: wrap;
  gap: .2rem .7rem;
  margin: 0;
  font-size: 1.3125rem;
}

/* Metadata values read one step above a tag: they are the answer to the label
   beside them, where a tag is a navigational aside. Two classes, so this wins
   over .pv__tag on specificity without needing !important. */
.pv__facets .pv__tag { font-size: 1.3125rem; }


/* ---- tags: plain text, hairline-separated. No pills. ---- */

/* Tags read as secondary to the metadata rows: the label sits closer to its
   list than a metadata label does to its value. */
.pv__tagsection .pv__label { margin-bottom: .4rem; }

.pv__tags {
  display: flex;
  flex-wrap: wrap;
  gap: .3rem .85rem;
  list-style: none;
  margin: 0;
  padding: 0;
}

.pv__tags li { margin: 0; }

.pv__tag {
  font-size: 1.25rem;
  font-weight: 300;
  color: var(--pv-ink-2);
  text-decoration: none;
  border-bottom: 1px solid transparent;
  padding-bottom: 1px;
  transition: color .18s ease, border-color .18s ease;
}

a.pv__tag:hover { color: var(--pv-ink); border-bottom-color: var(--pv-hair-strong); }


/* ---- capture details ---- */

.pv__meta {
  display: grid;
  grid-template-columns: minmax(0, auto) minmax(0, 1fr);
  gap: .5rem 1.4rem;
  margin: 0;
  font-size: .82rem;
  font-weight: 300;
}

.pv__meta dt {
  color: var(--pv-ink-3);
  font-size: .68rem;
  font-weight: 600;
  letter-spacing: .12em;
  text-transform: uppercase;
  padding-top: .15rem;
}

.pv__meta dd { color: var(--pv-ink-2); margin: 0; overflow-wrap: anywhere; }
.pv__meta dd.is-empty { color: var(--pv-ink-4); }


/* ---- interaction: bare marks on a hairline, no buttons-as-boxes ---- */

/* Flows directly after the content above it. Deliberately NOT pinned to the
   bottom — an auto margin opened a large void on photos with little metadata. */
.pv__social {
  padding-top: 1.3rem;
  border-top: 1px solid var(--pv-hair);
}

.pv__actions {
  display: flex;
  gap: 2rem;
  margin-bottom: 1.5rem;
}

.pv__action {
  display: inline-flex;
  align-items: center;
  gap: .5em;
  font: inherit;
  font-size: 1rem;
  font-weight: 600;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--pv-ink-3);
  background: none;
  border: 0;
  padding: 0;
  cursor: pointer;
  transition: color .18s ease;
}

.pv__action:hover { color: var(--pv-ink); }
.pv__action svg { width: 20px; height: 20px; fill: currentColor; transition: fill .18s ease; }
.pv__action[aria-pressed="true"] { color: var(--pv-ink); }

/* ---- like: outline heart at rest, filled once liked ---- */

.pv__like .heart-fill { display: none; }
.pv__like[aria-pressed="true"] .heart-line { display: none; }
.pv__like[aria-pressed="true"] .heart-fill { display: block; }

.pv__like-count {
  font-variant-numeric: tabular-nums;
  letter-spacing: .06em;      /* digits need less tracking than the label */
  opacity: .75;
}

.pv__like[aria-pressed="true"] .pv__like-count { opacity: 1; }

.pv__comments-empty {
  font-size: 1.25rem;
  font-weight: 300;
  color: var(--pv-ink-3);
  margin: 0;
}



/* ==========================================================================
   FOCUS
   ========================================================================== */

.pv :focus-visible {
  outline: 1px solid var(--pv-focus);
  outline-offset: 3px;
}


/* ==========================================================================
   MOBILE — single column, photograph first, rail as a bottom sheet.
   Breakpoint matches IO200's own (max-width: 999px).
   ========================================================================== */

@media (max-width: 999px) {

  .pv {
    grid-template-columns: 1fr;
    --pv-pad: 1.4rem;
  }

  /* The stage reserves room for the sheet, so `place-items: center` keeps the
     photograph centred in what's left rather than hidden behind it. */
  .pv__stage {
    grid-column: 1;
    grid-row: 1;
    padding-bottom: var(--pv-sheet-peek);
    transition: padding-bottom .3s var(--pv-ease);
  }

  .pv--sheet-open .pv__stage { padding-bottom: var(--pv-sheet-h); }

  .pv__aside {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    height: var(--pv-sheet-h);
    max-height: var(--pv-sheet-h);
    border-left: 0;
    border-top: 1px solid var(--pv-hair);
    border-radius: 14px 14px 0 0;
    transform: translateY(calc(100% - var(--pv-sheet-peek)));
    transition: transform .3s var(--pv-ease);
    will-change: transform;
  }

  .pv--sheet-open .pv__aside { transform: translateY(0); }

  /* While a finger is down, follow it with no easing */
  .pv__aside.is-dragging { transition: none; }

  .pv__handle {
    display: flex;
    align-items: center;
    justify-content: center;
    flex: 0 0 auto;
    height: var(--pv-sheet-peek);
    width: 100%;
    padding: 0;
    border: 0;
    background: transparent;
    cursor: grab;
    touch-action: none;          /* JS owns the vertical drag on the handle */
  }

  .pv__handle::before {
    content: "";
    width: 32px;
    height: 3px;
    border-radius: 999px;
    background: var(--pv-ink-4);
    transition: background .18s ease;
  }

  .pv__handle:hover::before { background: var(--pv-ink-3); }

  /* Sheet content is inert until the sheet is open */
  .pv__scroll {
    opacity: 0;
    visibility: hidden;
    transition: opacity .22s ease, visibility .22s;
    padding-top: .4rem;
    padding-bottom: calc(var(--pv-pad) + env(safe-area-inset-bottom, 0px));
    touch-action: pan-y;         /* native scrolling stays with the browser */
  }

  .pv--sheet-open .pv__scroll { opacity: 1; visibility: visible; }

  /* Thumb-friendly, and stated per control: a blanket `.pv__btn svg` rule here
     shares specificity with the close/nav icon rules above but comes later, so
     it would silently resize both. */
  .pv__nav { width: 44px; height: 44px; margin-top: -22px; }
  .pv__nav svg { width: 22px; height: 22px; }
  .pv__nav--prev { left: 8px; }
  .pv__nav--next { right: 8px; }
  .pv__close { top: 16px; right: 16px; width: 44px; height: 44px; }
  .pv__close svg { width: 22px; height: 22px; }
  .pv__counter { top: 20px; left: 16px; }

  /* The sheet dims the photograph a little as it rises */
  .pv__stage::after {
    content: "";
    position: absolute;
    inset: 0;
    background: #000;
    opacity: 0;
    pointer-events: none;
    transition: opacity .3s var(--pv-ease);
  }

  .pv--sheet-open .pv__stage::after { opacity: .28; }
}

/* Short/landscape phones: give the sheet less room so the photo survives */
@media (max-width: 999px) and (max-height: 520px) {
  .pv { --pv-sheet-h: 78dvh; }
}


/* ==========================================================================
   REDUCED MOTION
   ========================================================================== */

@media (prefers-reduced-motion: reduce) {
  .pv,
  .pv *,
  .pv__aside,
  .pv__img,
  .pv__scroll,
  .pv__stage,
  .pv__stage::after,
  .pv__progress::after {
    transition: none !important;
    animation: none !important;
  }
  .pv.is-loading .pv__img.is-active { opacity: 1; }
}


/* ==========================================================================
   SHARE SHEET
   Desktop: centred modal over the viewer. Mobile: bottom sheet, swipe down.
   Same ground, same hairlines, same type as the rail — no cards, no colour.
   ========================================================================== */

.pv-share {
  position: fixed;
  inset: 0;
  z-index: 1000001;              /* above the viewer */
  display: grid;
  place-items: center;
  font-family: inherit;
}

.pv-share[hidden] { display: none !important; }

/* Navigation lock — the viewer is inert while the Share modal is open.
   go() is the authority that refuses to change photo; this is a second,
   structural guarantee. The Share overlay is fixed inset:0 above the viewer,
   so nothing underneath is reachable today — but if that stacking ever
   changed, a stray tap or swipe on the stage still could not navigate.
   The arrow buttons are also set [disabled], which the existing
   .pv__btn[disabled] rule already fades out and makes non-interactive. */
.pv--navlocked .pv__stage,
.pv--navlocked .pv__nav { pointer-events: none; }

.pv-share__backdrop {
  position: absolute;
  inset: 0;
  /* Deep enough that the viewer reads as a faint ground behind the sheet,
     but never solid black — the photograph should still be sensed. */
  background: rgba(0, 0, 0, .78);
  opacity: 0;
  transition: opacity .22s ease;
}

.pv-share.is-open .pv-share__backdrop { opacity: 1; }

/* Appearance only. Sizing lives in the two media blocks below, because the
   desktop panel and the mobile sheet size themselves on different rules and
   sharing a base value here made both harder to read. */
.pv-share__panel {
  position: relative;
  background: var(--pv-bg, #0b0b0b);
  border: 1px solid var(--pv-hair, rgba(255,255,255,.07));
  border-radius: 10px;
  opacity: 0;
  transform: translateY(8px) scale(.99);
  transition: opacity .22s ease, transform .22s var(--pv-ease, cubic-bezier(.32,.72,0,1));
}

.pv-share.is-open .pv-share__panel { opacity: 1; transform: none; }

/* ---- desktop: centred panel ---- */

@media (min-width: 1000px) {
  .pv-share__panel {
    /* border-box so the number below is the width actually rendered. This
       panel is a sibling of .pv in <body>, so the viewer's own reset never
       reached it and it inherited the browser default — which made the old
       `600px` render 658px, and made the narrow-desktop fallback eat almost
       all of its intended 4rem margin. Scoped to desktop on purpose: the
       mobile sheet below has always sized against content-box and is
       deliberately left exactly as it is. */
    box-sizing: border-box;

    /* 720px, up from an effective 658px. Wide enough that the 1200x630 card
       reads as a share preview rather than a thumbnail, still under half of a
       1600px viewport, and it still yields to a narrow window. */
    width: min(720px, calc(100vw - 4rem));

    /* 1.75rem x 1.2, in step with the width, so the card keeps roughly 90% of
       the panel to itself rather than growing a wider frame. */
    padding: 2.1rem;

    /* Safeguard rather than layout: at normal desktop heights the panel is
       ~505px and this never applies. It only stops a short window from
       clipping the panel now that it is taller. */
    max-height: calc(100vh - 4rem);
    overflow-y: auto;
  }
}

.pv-share__handle { display: none; }

.pv-share__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 1.1rem;
}

.pv-share__title {
  margin: 0;
  font-size: .625rem;
  font-weight: 600;
  letter-spacing: .18em;
  text-transform: uppercase;
  color: var(--pv-ink-3, #616161);
}

.pv-share__close {
  border: 0;
  background: none;
  padding: 0;
  cursor: pointer;
  color: var(--pv-ink-3, #616161);
  display: grid;
  place-items: center;
  transition: color .18s ease;
}

.pv-share__close:hover { color: var(--pv-ink, #ededed); }
.pv-share__close svg { width: 15px; height: 15px; fill: currentColor; display: block; }

/* ---- card preview: the exact image crawlers receive ---- */

.pv-share__card {
  border: 1px solid var(--pv-hair, rgba(255,255,255,.07));
  border-radius: 6px;
  overflow: hidden;
  background: #0b0b0b;
}

.pv-share__cardimg {
  aspect-ratio: 1200 / 630;
  background: #0b0b0b;
}

.pv-share__cardimg img {
  width: 100%;
  height: 100%;
  object-fit: contain;
  display: block;
}

.pv-share__cardfoot { display: none; }   /* the card image already carries it */

/* ---- actions ---- */

.pv-share__actions {
  display: flex;
  gap: 1.6rem;
  margin-top: 1.3rem;
  padding-top: 1.2rem;
  border-top: 1px solid var(--pv-hair, rgba(255,255,255,.07));
}

.pv-share__action {
  font: inherit;
  font-size: .68rem;
  font-weight: 600;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--pv-ink-3, #616161);
  background: none;
  border: 0;
  padding: 0;
  cursor: pointer;
  transition: color .18s ease;
}

.pv-share__action:hover { color: var(--pv-ink, #ededed); }
.pv-share__action[hidden] { display: none !important; }

.pv-share :focus-visible {
  outline: 1px solid var(--pv-focus, #7fb2e8);
  outline-offset: 3px;
}

/* ---- mobile: bottom sheet ---- */

@media (max-width: 999px) {
  .pv-share { place-items: end center; }

  .pv-share__panel {
    width: 100%;
    border-radius: 14px 14px 0 0;
    border-left: 0;
    border-right: 0;
    border-bottom: 0;
    padding: 0 1.15rem calc(1.4rem + env(safe-area-inset-bottom, 0px));
    transform: translateY(100%);
  }

  .pv-share.is-open .pv-share__panel { transform: translateY(0); }
  .pv-share__panel.is-dragging { transition: none; }

  .pv-share__handle {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 100%;
    height: 44px;
    border: 0;
    background: transparent;
    cursor: grab;
    touch-action: none;
  }

  .pv-share__handle::before {
    content: "";
    width: 32px;
    height: 3px;
    border-radius: 999px;
    background: var(--pv-ink-4, #3d3d3d);
  }

  .pv-share__head { margin-bottom: .9rem; }
}


/* ==========================================================================
   DEEP-LINK — hide the standalone photo page until the viewer is up.
   The class is stamped by an inline head script, so it is applied only when
   JavaScript is running; no-JS visitors always see the normal page. A 1.5s
   failsafe in the viewer removes it regardless of what happens.
   ========================================================================== */

/* Scoped twice over: the class is only emitted server-side on photo pages
   (CustomOutputRenderer::ThemeCodeInjectionHeader), and this selector also
   requires body.template-photo — so even a stray class on the homepage,
   /tags, /contact or /stats could not hide anything. */
html.pv-deeplink-pending body.template-photo > #container { visibility: hidden; }


@media (prefers-reduced-motion: reduce) {
  .pv-share__backdrop,
  .pv-share__panel { transition: none !important; }
}
