/**
 * Media building blocks (JBC-11): self-hosted video, gallery + lightbox,
 * PDF/image download card. Fork-local, not framework core (see
 * site/partials/media.php's own docblock header). Pairs with
 * site/partials/media.php (markup) and site/assets/js/media.js (lightbox
 * behaviour). Load AFTER tokens.css, before any page-specific <style>:
 *
 *   <link rel="stylesheet" href="<?= site_base_path() ?>/assets/css/tokens.css">
 *   <link rel="stylesheet" href="<?= site_base_path() ?>/assets/css/media.css">
 *
 * Every colour/radius/shadow below is a tokens.css variable, never a
 * hardcoded hex (framework.md hard rule "no hardcoded hex values"), so a
 * fork rebrand (tokens.css's --brand-* overrides) carries through here
 * automatically, same as every other component in this repo.
 */

/* ---------------------------------------------------------------- video */

/* Shared shape for both aspect variants: a plain block, no card chrome of
   its own (a caller wraps it in whatever card/section styling the page
   needs), img/video already get --radius-media from tokens.css's own
   `img, video { border-radius: var(--radius-media); }` rule, so this file
   never repeats it. */
.cm-video { display: block; }
.cm-video video { display: block; background: var(--panel-a); }

/* --cm-video-chrome: how much vertical space ABOVE the video is already
   spoken for by page chrome (a sticky header, typically). Defaults to 0,
   correct for a page with no sticky header (this fork's bausteine demo
   page, and any plain content page). A page that DOES sit under a sticky
   header sets this on an ancestor, see media.php's cm_video() docblock
   "Integration point" paragraph. */
:root { --cm-video-chrome: 0px; }

/* 9:16: the hard C-11 requirement is "never bigger than the viewport",
   not "always as tall as possible" -- `max-height` + `max-width` +
   `aspect-ratio` (no explicit width/height) lets the browser's own
   replaced-element sizing algorithm pick whichever of the two limits binds
   first, so the clip always ends up EITHER edge-to-edge width-limited (a
   phone in portrait) OR height-limited by 100dvh minus the header (a wide
   desktop viewport, where width alone would make a 9:16 clip absurdly
   tall). `100dvh` (not `100vh`) so a mobile browser's address-bar chrome
   never causes a clip that is "tall enough" on load to overflow once the
   bar collapses. */
.cm-video--916 {
  max-width: min(100%, 420px);
  margin-inline: auto;
}
.cm-video--916 video {
  width: auto;
  max-width: 100%;
  height: auto;
  /* code-reviewer (PR #6, R1): dvh fallback. A browser without dvh support
     ignores the whole `max-height` declaration on an unknown unit (CSS
     handles an invalid value by discarding the whole declaration, not by
     falling back partway), which would leave the 9:16 clip with NO height
     cap at all on that browser. The 100vh line below is a real value every
     browser understands; the 100dvh line right after it wins in any
     browser that also understands dvh (a later declaration of the same
     property overrides an earlier one), so this is a true fallback, not a
     duplicate. */
  max-height: calc(100vh - var(--cm-video-chrome));
  max-height: calc(100dvh - var(--cm-video-chrome));
  aspect-ratio: 9 / 16;
  margin-inline: auto;
  object-fit: cover;
}

/* 16:9: full column width, height follows from aspect-ratio. Never needs
   the same height cap as 9:16 -- at any realistic content-column width
   (this framework's own `.wrap` tops out well under 1200px, see
   framework.md's wrap-width caveats) a 16:9 clip's computed height stays
   far short of any real viewport height, portrait or landscape. */
.cm-video--169 video {
  width: 100%;
  aspect-ratio: 16 / 9;
  object-fit: cover;
}

/* ------------------------------------------------------------- gallery */

.cm-gallery-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
  gap: 12px;
}

.cm-gallery-item {
  /* An <a href="<full-size image>">, not a <button> (code-reviewer R1
     finding, PR #6): a genuine no-JS fallback opens the full image, media.js
     intercepts the click to open the in-page lightbox instead when it is
     wired up. Reset the link chrome to a plain image frame. */
  display: block;
  padding: 0;
  border: 0;
  border-radius: var(--radius-media);
  overflow: hidden;
  cursor: zoom-in;
  background: var(--panel-a);
  text-decoration: none;
  aspect-ratio: 1 / 1;
  min-width: 44px;
  min-height: 44px;
}
.cm-gallery-item img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  border-radius: 0; /* the link itself carries the rounding + overflow:hidden clip */
  transition: transform .2s ease;
}
.cm-gallery-item:hover img { transform: scale(1.04); }
@media (prefers-reduced-motion: reduce) {
  .cm-gallery-item img { transition: none; }
  .cm-gallery-item:hover img { transform: none; }
}

/* ------------------------------------------------------------ lightbox
   Same shape as shop.css's .shop-lightbox* (see that file's own long
   comment block for the DOM-order/z-index reasoning this mirrors exactly:
   the close/prev/next buttons are DOM siblings BEFORE .cm-lightbox-slides,
   which is load-bearing for stacking order, no positive z-index needed on
   the buttons, and none may ever land on .cm-lightbox-slides or its image
   without repainting it back over the buttons). Prefixed `cm-` instead of
   `shop-` because this is a generic, non-shop component; kept deliberately
   parallel in naming and structure so the same mental model transfers. */
.cm-lightbox {
  max-width: min(92vw, 900px);
  max-height: 88vh;
  margin: auto;
  padding: 20px;
  border: 0;
  border-radius: var(--radius-md);
  background: var(--surface);
  color: var(--text);
  box-shadow: var(--shadow-3);
}
.cm-lightbox::backdrop { background: rgba(10, 37, 64, .6); }

.cm-lightbox-slides {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 40vh;
}
.cm-lightbox-slide {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  margin: 0;
  max-width: 100%;
}
.cm-lightbox-slide[hidden] { display: none; }
.cm-lightbox-slide img {
  max-width: 100%;
  max-height: 72vh;
  object-fit: contain;
  display: block;
  margin: 0 auto;
}
.cm-lightbox-slide figcaption {
  color: var(--text-2);
  /* design-reviewer WARN (PR #6, R1): DESIGN.md minimum body size is
     .9375rem (15px); this was .9rem (14.4px). */
  font-size: .9375rem;
  text-align: center;
}

.cm-lightbox-close,
.cm-lightbox-prev,
.cm-lightbox-next {
  position: absolute;
  z-index: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  min-width: 44px;
  min-height: 44px;
  padding: 0;
  border: 1px solid var(--line-strong);
  border-radius: 50%;
  background: var(--surface);
  color: var(--text);
  font-size: 1.4rem;
  line-height: 1;
  cursor: pointer;
}
.cm-lightbox-close { top: 8px; right: 8px; }
.cm-lightbox-prev { top: 50%; left: 8px; transform: translateY(-50%); }
.cm-lightbox-next { top: 50%; right: 8px; transform: translateY(-50%); }
.cm-lightbox-close:hover,
.cm-lightbox-prev:hover,
.cm-lightbox-next:hover { background: var(--surface-2); }
.cm-lightbox-close:focus-visible,
.cm-lightbox-prev:focus-visible,
.cm-lightbox-next:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

/* ------------------------------------------------------- download card */

/* design-reviewer VETO (PR #6, R1): --line is decorative (tokens.css's own
   docblock, "not held to the 3:1 rule"), measured 1.38:1 light / 1.50:1
   dark here, well under the 3:1 WCAG 1.4.11 floor for a UI-bounding border.
   --line-strong is the token tokens.css itself names for exactly this case
   ("bounds a UI component"), measured 3.34:1 light / 4.07:1 dark. */
.cm-download-card {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 14px 16px;
  min-height: 44px;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-md);
  background: var(--surface);
  color: var(--text);
  text-decoration: none;
}
.cm-download-card:hover { background: var(--surface-2); }

.cm-download-icon {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 40px;
  height: 40px;
  border-radius: var(--radius-sm);
  background: var(--btn-2-bg);
  color: var(--btn-2-text);
}
.cm-download-icon svg { width: 22px; height: 22px; fill: currentColor; }

.cm-download-text {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.cm-download-title {
  font-weight: 500;
  color: var(--text);
  overflow-wrap: anywhere;
}
/* design-reviewer WARN (PR #6, R1): DESIGN.md minimum body size is
   .9375rem (15px); this was .85rem (13.6px). */
.cm-download-meta { font-size: .9375rem; color: var(--text-2); }

/* Solid, not a ghost/outline button (framework.md hard rule): the same
   filled-pill/border pairing as .btn-secondary in tokens.css, at a smaller
   size deliberately, this is a small trailing span inside a larger
   `.cm-download-card` link, not its own separate click target (the whole
   card is the `<a>`), so it does not need .btn-secondary's full padding. */
.cm-download-action {
  flex: 0 0 auto;
  padding: 8px 14px;
  min-height: 36px;
  display: inline-flex;
  align-items: center;
  border-radius: var(--radius-sm);
  background: var(--btn-2-bg);
  color: var(--btn-2-text);
  border: 1px solid var(--btn-2-line);
  font-size: .9rem;
  font-weight: 500;
  white-space: nowrap;
}

@media (max-width: 480px) {
  .cm-download-card { flex-wrap: wrap; }
  .cm-download-action { margin-inline-start: 54px; }
}
