/* Depends on: tokens.css, galeria.css -- linked as separate <link> tags by
   every template that uses this file, NOT @import-ed here. See the note at
   the top of profile.css for why (test_template_lint.py enforces it).

   The Galería viewer's overlay: one card at a time above 640px, a
   full-screen vertical feed at or below it (spec GV-002). Both draw the card
   the same way -- media filling it, author/race/caption stacked bottom-left
   over a gradient, icons with a count down the right, comments in a sheet --
   the language /reels/ already speaks (reels.css). The desktop card is
   simply that arrangement sized to the media (galeria_viewer.js fitCard).

   Which of the two is decided ONCE, when the viewer opens, and written onto
   the overlay as .is-modal / .is-feed (galeria_viewer.js setLayout). The
   layout rules below key off that class, never off a live media query: a
   phone rotated into a landscape width above 640px mid-feed must keep the
   track it is scrolling, not have its scroll-snap and heights pulled out
   from under the live observers (I3). */

.gv-overlay[hidden] { display: none; }
/* 200, like every lightbox on the site: above the floating theme toggle
   (tokens.css, 100) and the sticky nav (nav.css, 100), which would
   otherwise sit on top of the sheet's "Enviar" on a phone (I6). */
.gv-overlay {
  position: fixed; inset: 0; z-index: 200;
  background: rgba(4, 6, 8, 0.96);
}

body.gv-open { overflow: hidden; }

.gv-stage {
  position: absolute; inset: 0; display: flex;
  align-items: center; justify-content: center;
}
.gv-stage.is-loading::after {
  content: ""; width: 26px; height: 26px; border-radius: 50%;
  border: 2px solid var(--line); border-top-color: var(--accent);
  animation: gv-spin 0.7s linear infinite; position: absolute;
}
@keyframes gv-spin { to { transform: rotate(360deg); } }
.gv-cover { max-width: calc(100% - 176px); max-height: 92vh; object-fit: contain; opacity: 0.45; }

/* ---------- Overlay controls: ×, sound, ‹ › ----------
   The overlay is near-black in both themes, so its controls are fixed white
   like .lb-* -- var(--text-primary) is near-black in the light theme (I7).
   z-index 5: above the card's own layers (media 0 .. comment sheet 4), which
   share the overlay's stacking context. On desktop, fitCard() pins the sound
   button and the arrows to the card it has just sized. */
.gv-close, .gv-sound, .gv-nav {
  position: absolute; z-index: 5; padding: 0; border: 0; background: none;
  color: #fff; cursor: pointer; line-height: 1;
  filter: drop-shadow(0 1px 2px rgba(0, 0, 0, 0.5));
}
.gv-close, .gv-sound { display: grid; place-items: center; width: 40px; height: 40px; }
.gv-close svg { width: 26px; height: 26px; }
.gv-sound svg { width: 22px; height: 22px; }
.gv-close { top: 14px; right: 18px; }
.gv-sound { top: 14px; right: 62px; }
.gv-sound .gv-ico-on { display: none; }
.gv-sound.is-on .gv-ico-on { display: block; }
.gv-sound.is-on .gv-ico-off { display: none; }
.gv-nav {
  top: 50%; transform: translateY(-50%); width: 44px; height: 44px; border-radius: 50%;
  background: rgba(255, 255, 255, 0.12); font-size: 30px; filter: none;
}
.gv-nav:hover { background: rgba(255, 255, 255, 0.22); }
.gv-prev { left: 16px; }
.gv-next { right: 16px; }
.gv-sound[hidden], .gv-nav[hidden] { display: none; }

/* ---------- The card: phone reel and desktop modal alike ---------- */
.gd-mode-reel, .gd-mode-modal {
  display: flex; flex-direction: column; justify-content: flex-end; gap: 6px;
  padding: 0 72px calc(22px + env(safe-area-inset-bottom, 0px)) 14px;
  border: 0; color: #fff;
}
.gd-mode-reel { position: absolute; inset: 0; background: none; border-radius: 0; }
/* fitCard() replaces this size with the media's own proportions. */
.gd-mode-modal {
  position: relative; width: min(calc(100vw - 176px), 520px); height: 92vh;
  background: #000; border-radius: var(--radius-lg); overflow: hidden;
}

/* Gradients instead of boxes: the text reads on bright footage without a
   card behind it. Painted between the media (0) and the text (2). */
.gd-mode-reel:not(.gv-official)::before, .gd-mode-modal:not(.gv-official)::before,
.gd-mode-reel:not(.gv-official)::after, .gd-mode-modal:not(.gv-official)::after {
  content: ""; position: absolute; left: 0; right: 0; z-index: 1; pointer-events: none;
}
.gd-mode-reel:not(.gv-official)::before, .gd-mode-modal:not(.gv-official)::before {
  top: 0; height: 120px; background: linear-gradient(rgba(0, 0, 0, 0.5), rgba(0, 0, 0, 0));
}
.gd-mode-reel:not(.gv-official)::after, .gd-mode-modal:not(.gv-official)::after {
  bottom: 0; height: 42%; background: linear-gradient(rgba(0, 0, 0, 0), rgba(0, 0, 0, 0.72));
}

/* Every child sits above the gradients; the media, the rail and the sheet
   are then placed on their own layers below. */
.gd-mode-reel > *, .gd-mode-modal > * { position: relative; z-index: 2; margin: 0; flex: none; }
/* z-index: auto, not 0: the media wrapper must not be a stacking context of its own, or text drawn
   over the picture (.tl-canvas, z-index 3) would sit under the card's bottom gradient (z-index 1) and
   come out dimmed. The video itself is not positioned, so it still paints beneath both. */
.gd-mode-reel .gd-media, .gd-mode-modal .gd-media {
  position: absolute; inset: 0; z-index: auto;
  display: flex; align-items: center; justify-content: center;
}
.gd-mode-reel .gd-slides, .gd-mode-reel .gd-video, .gd-mode-reel .gd-slide,
.gd-mode-modal .gd-slides, .gd-mode-modal .gd-video, .gd-mode-modal .gd-slide {
  width: 100%; height: 100%; max-height: none; object-fit: contain;
}
.gd-mode-modal .gd-video { cursor: pointer; }
/* A Reel with text in its letterbox keeps its whole 9:16 frame on a phone too wide for it (TL-006; see
   reels.css). The desktop card takes the frame's proportions instead (galeria_viewer.js fitCard). */
.gd-mode-reel .gd-framed .gd-video { width: min(100%, 56.25vh); width: min(100%, 56.25dvh); }
/* An album's counter would sit under the rail at the bottom-right. */
.gd-mode-reel .gd-count, .gd-mode-modal .gd-count { top: 64px; bottom: auto; right: 12px; }

.gd-mode-reel .gd-who, .gd-mode-reel .gd-race, .gd-mode-reel .gd-caption,
.gd-mode-modal .gd-who, .gd-mode-modal .gd-race, .gd-mode-modal .gd-caption {
  color: #fff; text-shadow: 0 1px 3px rgba(0, 0, 0, 0.6);
}
.gd-mode-reel .gd-who, .gd-mode-modal .gd-who { padding: 0; justify-content: flex-start; border: 0; }
.gd-mode-reel .gd-when, .gd-mode-modal .gd-when { color: rgba(255, 255, 255, 0.75); }
/* The race line is text, not a card: on the page it is a bordered box, which
   over a clip outweighed the clip itself. */
.gd-mode-reel .gd-race, .gd-mode-modal .gd-race {
  padding: 0; background: none; border: 0; border-radius: 0;
}
.gd-mode-reel .gd-race-l2, .gd-mode-modal .gd-race-l2 {
  font-size: 12.5px; margin-top: 1px; color: rgba(255, 255, 255, 0.8);
}
.gd-mode-reel .gd-race-l2 a, .gd-mode-modal .gd-race-l2 a { color: inherit; font-weight: 500; }
.gd-mode-reel .gd-caption, .gd-mode-modal .gd-caption {
  font-size: 14px; line-height: 1.45;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
/* Walking a highlight, every item is the same pilot's (HL-004) and the bar
   at the top already names them: no second author row on every clip. */
.gv-overlay.is-highlight .gd-who { display: none; }

/* The rail: icons with a count, nothing else (the /reels/ rail, reels.css). */
.gd-mode-reel .gd-actions, .gd-mode-modal .gd-actions {
  position: absolute; z-index: 3; right: 8px; bottom: calc(22px + env(safe-area-inset-bottom, 0px));
  width: 56px; padding: 0; border: 0;
  display: flex; flex-direction: column; align-items: center; gap: 18px;
}
.gd-mode-reel .gd-actions form, .gd-mode-modal .gd-actions form { margin: 0; }
.gd-mode-reel .gd-act, .gd-mode-modal .gd-act {
  display: flex; flex-direction: column; align-items: center; gap: 3px;
  padding: 0; border: 0; background: none; color: #fff;
  font: inherit; font-size: 11.5px; font-weight: 700; cursor: pointer;
  text-shadow: 0 1px 3px rgba(0, 0, 0, 0.5); font-variant-numeric: tabular-nums;
}
.gd-mode-reel .gd-ico, .gd-mode-modal .gd-ico {
  width: 30px; height: 30px; filter: drop-shadow(0 1px 3px rgba(0, 0, 0, 0.5));
}
.gd-mode-reel .gd-unit, .gd-mode-modal .gd-unit { display: none; }
.gd-mode-reel .gd-kudos.on .gd-ico, .gd-mode-modal .gd-kudos.on .gd-ico { fill: var(--amber); stroke: var(--amber); }

/* The author's ⋯ menu: pin, delete and, on a highlight, "Editar destacada". */
.gd-more { position: relative; }
.gd-more > summary { list-style: none; }
.gd-more > summary::-webkit-details-marker { display: none; }
.gd-more-menu {
  position: absolute; right: 0; bottom: calc(100% + 10px); z-index: 1; min-width: 200px;
  display: flex; flex-direction: column; padding: 6px;
  background: var(--surface-raised); border: 1px solid var(--line); border-radius: 12px;
  box-shadow: 0 14px 36px rgba(0, 0, 0, 0.5); color: var(--text-primary); text-shadow: none;
}
.gd-more-menu form { margin: 0; }
.gd-more-menu .gd-visibility { display: flex; justify-content: space-between; width: 100%; padding: 8px 12px; color: inherit; font-size: 14px; }
.gd-more-menu .gd-pin, .gd-more-menu .gd-delete, .gd-more-menu .gd-download, .gd-more-item {
  display: flex; align-items: center; gap: 10px; width: 100%; padding: 10px 12px;
  border: 0; border-radius: 8px; background: none; color: inherit;
  font: inherit; font-size: 14px; text-align: left; text-decoration: none; cursor: pointer;
}
.gd-more-menu .gd-pin svg, .gd-more-menu .gd-delete svg { width: 16px; height: 16px; flex: none; }
.gd-more-menu .gd-pin:hover, .gd-more-menu .gd-delete:hover, .gd-more-menu .gd-download:hover, .gd-more-item:hover,
.gd-more-menu .gd-pin:focus-visible, .gd-more-menu .gd-delete:focus-visible, .gd-more-menu .gd-download:focus-visible, .gd-more-item:focus-visible {
  background: var(--surface-2);
}
.gd-more-menu .gd-pin.on { color: var(--gold); }
.gd-more-menu .gd-delete { color: var(--critical); }

/* A photographer's photo is a product: its buy / "Soy yo" links are words,
   so they stay a row of glass pills under the race line, not a rail. */
.gd-mode-reel.gd-card-photo, .gd-mode-modal.gd-card-photo { padding-right: 14px; }
.gd-mode-reel.gd-card-photo .gd-actions, .gd-mode-modal.gd-card-photo .gd-actions {
  position: relative; right: auto; bottom: auto; z-index: 2; width: auto;
  flex-direction: row; flex-wrap: wrap; gap: 8px;
}
.gd-card-photo.gd-mode-reel .gd-kudos, .gd-card-photo.gd-mode-modal .gd-kudos {
  padding: 7px 14px; color: #fff;
  border-color: rgba(255, 255, 255, 0.35); background: rgba(0, 0, 0, 0.35);
}

/* Comments ride up from the bottom over the media, TikTok-style, on both
   sizes. Themed surface and text: the sheet is its own panel, not overlay
   text. Hidden (not just moved) while closed, so Tab never lands in it. */
.gd-mode-reel .gd-comments, .gd-mode-modal .gd-comments {
  position: absolute; left: 0; right: 0; bottom: 0; z-index: 4;
  max-height: 62%; overflow-y: auto;
  display: flex; flex-direction: column; gap: 8px;
  overscroll-behavior: contain; touch-action: pan-y;
  padding: 16px 16px calc(20px + env(safe-area-inset-bottom, 0px));
  color: var(--text-primary); text-shadow: none;
  background: var(--surface); border-radius: 16px 16px 0 0;
  transform: translateY(100%); visibility: hidden;
  transition: transform 0.22s ease, visibility 0s linear 0.22s;
}
.gd-mode-reel .gd-comments.is-open, .gd-mode-modal .gd-comments.is-open {
  transform: translateY(0); visibility: visible; transition-delay: 0s;
}
/* Desktop: a drawer down the card's right edge rather than a sheet across a
   card that may be a landscape clip 1,200px wide. */
.gd-mode-modal .gd-comments {
  top: 0; left: auto; width: min(100%, 380px); max-height: none;
  padding-bottom: 16px; border-radius: 0; transform: translateX(100%);
}
.gd-mode-modal .gd-comments.is-open { transform: translateX(0); }
/* The highlight bar and the sound button are overlay controls laid over the
   card's top edge: the drawer starts under the bar's row, and the button,
   which would sit on the drawer's own ×, steps aside while it is open. */
.gv-overlay.is-highlight .gd-mode-modal .gd-comments { top: 62px; border-top-left-radius: 12px; }
.gv-overlay:has(.gd-mode-modal .gd-comments.is-open) .gv-sound { display: none; }
.gv-sheet-close { display: none; }
.gd-mode-reel .gv-sheet-close, .gd-mode-modal .gv-sheet-close {
  display: block; align-self: flex-end; margin: -6px -4px 0 0;
  background: none; border: 0; color: var(--text-secondary);
  font-size: 24px; line-height: 1; cursor: pointer;
}

/* The comment form rides at the bottom of the scrolling sheet, so it is on
   screen however long the thread. */
.gv-overlay .gd-comment-form {
  position: sticky; bottom: 0; margin-top: auto;
  padding: 8px 0 2px; background: var(--surface);
}

/* An official card is just the photo plus the .mt-official badge (no
   .gd-media wrapper -- officialCard() has nothing else to put in one). The
   badge is `position: absolute` (galeria.css), so the card is its anchor. */
.gv-official { position: relative; }
/* In the modal it is the photo, shrink-wrapped: fitCard() leaves it alone. */
.gd-mode-modal.gv-official {
  display: block; width: auto; height: auto; max-width: 100%; padding: 0;
  background: none; border: 0; border-radius: 0; overflow: visible;
}
.gd-mode-modal.gv-official .gd-slide {
  display: block; flex: none; width: auto; height: auto;
  max-width: calc(100vw - 176px); max-height: 92vh; border-radius: var(--radius-lg);
}
.gd-mode-reel.gv-official { display: block; padding: 0; }
.gd-mode-reel.gv-official .mt-official { top: 14px; left: 14px; }

/* ---------- Phone feed (spec GV-002, <=640px at open) ----------
   A full-screen vertical scroll-snap track instead of one card.
   openReel()/fillNear() in galeria_viewer.js build it. */
.gv-overlay.is-feed .gv-nav { display: none; } /* the scroll IS the navigation */
.gv-overlay.is-feed .gv-close { top: calc(6px + env(safe-area-inset-top, 0px)); right: 4px; }
.gv-overlay.is-feed .gv-sound { top: calc(6px + env(safe-area-inset-top, 0px)); right: 44px; }

/* .gv-track / .gv-reel only ever exist inside the feed layout, so these need
   no further scoping. */
.gv-track {
  display: block; overflow-y: auto; scroll-snap-type: y mandatory;
  overscroll-behavior: contain; -webkit-overflow-scrolling: touch;
}
.gv-reel {
  position: relative; height: 100dvh; scroll-snap-align: start;
  display: flex; align-items: center; justify-content: center;
  overflow: hidden;
}
.gv-reel .gv-cover {
  position: absolute; inset: 0; width: 100%; height: 100%; max-width: none; max-height: none;
  object-fit: contain; opacity: 1;
}

/* ---------- Highlight bar (Instagram-story look) ----------
   Only present while the viewer walks a highlight (syncHighlight in
   galeria_viewer.js): one bar per item, seen ones full, the current one
   filling as its clip plays, then one row -- avatar, the highlight's title,
   the item's age -- level with the × (and the sound button). On desktop
   fitCard() lays it across the top of the card. */
.gv-hl[hidden] { display: none; }
.gv-hl {
  position: absolute; z-index: 5; top: 10px; left: 50%; transform: translateX(-50%);
  width: min(1100px, calc(100% - 144px)); pointer-events: none;
}
.gv-bars { list-style: none; margin: 0; padding: 0; display: flex; gap: 3px; }
.gv-bars li { flex: 1 1 0; min-width: 0; }
.gv-bar {
  display: block; width: 100%; height: 14px; padding: 6px 0; background: none; border: 0;
  cursor: pointer; pointer-events: auto; position: relative;
}
.gv-bar::before {
  content: ""; position: absolute; left: 0; right: 0; top: 6px; height: 2px; border-radius: 2px;
  background: rgba(255, 255, 255, 0.35);
}
.gv-bar span {
  position: absolute; left: 0; right: 0; top: 6px; height: 2px; border-radius: 2px;
  background: #fff; transform: scaleX(0); transform-origin: left center;
}
.gv-hl-head {
  display: flex; align-items: center; gap: 8px; margin-top: 4px; padding-right: 88px;
  color: #fff; text-shadow: 0 1px 3px rgba(0, 0, 0, 0.8); font-size: 14px; font-weight: 600;
}
.gv-hl-title { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.gv-hl-avatar { width: 28px; height: 28px; border-radius: 50%; overflow: hidden; background: rgba(255, 255, 255, 0.2); flex: none; }
.gv-hl-avatar img { width: 100%; height: 100%; object-fit: cover; display: block; }
.gv-hl-avatar:empty { display: none; }
.gv-hl-age { font-weight: 400; color: rgba(255, 255, 255, 0.75); flex: none; }
/* Phone: bars across the very top of the full-screen reel; the row under
   them is centred on y=40, and so are the × and the sound button. */
.gv-overlay.is-feed .gv-hl { top: calc(8px + env(safe-area-inset-top, 0px)); width: calc(100% - 20px); }
.gv-overlay.is-feed.is-highlight .gv-close,
.gv-overlay.is-feed.is-highlight .gv-sound { top: calc(20px + env(safe-area-inset-top, 0px)); }
