/* ---------------------------------------------------------------------------
   Flexible Gallery - static styles.
   Everything driven by module fields lives in the scoped block in module.html.
   Class names are prefixed bf-gallery__ so nothing here can reach into
   img-card, process-cards or the portfolio grids, which all share generic
   .card / .cards class names in this theme.
--------------------------------------------------------------------------- */

/* Module-local variables (2026-09-17, pattern from tabber-horizontal/module.css).

   Set by module.html from the editor's fields, so NOT declared here, only read with a fallback
   at the point of use: --bf-gallery-gap, --bf-gallery-corner, --bf-gallery-overlay,
   --bf-gallery-control. Declaring them here too would make the result depend on which of the
   two stylesheets HubSpot happens to print last.

   Declared here, because no field sets them. A child theme can override them on .bf-gallery:
   the reveal distance is how far a row travels when it fades in, a motion value rather than a
   layout gap, so it deliberately does not read --bf-space-md; the stage limits keep the
   lightbox image inside the viewport and are written once instead of twice. */
.bf-gallery {
  --bf-gallery-reveal-distance: 24px;
  --bf-gallery-stage-max-height: 88vh;
  --bf-gallery-stage-max-width: 88vw;
  display: flex;
  flex-direction: column;
  gap: var(--bf-gallery-gap, var(--bf-space-md, 24px));
}

/* ------------------------------ rows ------------------------------ */

.bf-gallery__row {
  margin: 0;
  padding: 0;
}

.bf-gallery__row--items {
  align-items: flex-start;
  display: flex;
  gap: var(--bf-gallery-gap, var(--bf-space-md, 24px));
  list-style: none;
}

.bf-gallery__row--justified .bf-gallery__cell {
  flex-grow: var(--bf-cell-grow, 1);
}

.bf-gallery__cell {
  flex: 1 1 0;
  margin-bottom: 0;
  min-width: 0;
  position: relative;
}

/* Two row models, chosen per row in the editor.

   Equal widths is the default and is what every cell above already does:
   flex: 1 1 0 splits the row evenly, so photos of different shapes end up
   different heights and the bottom of the row is ragged.

   Irregular widths is the justified model used by Flickr, Google Photos and
   Unsplash. module.html gives each cell a flex-grow equal to its aspect
   ratio, so widths differ but heights match: height = width / aspect, and
   with width proportional to aspect that quotient is the same for every cell
   in the row. The row fills its full width, spacing stays even and nothing is
   cropped.

   The growth factor arrives from module.html as a custom property rather than
   as an inline flex-grow, so the breakpoints below can take it back off
   without resorting to !important.

   There used to be a per-row alignment field here offering stretch, top,
   middle and bottom. It was dropped on 2026-08-25: align-items stretches the
   <li>, never the picture inside it, so `stretch` rendered pixel for pixel
   the same as `flex-start` in every combination tested. The field promised
   equal heights and could not deliver them, which is what justified rows are
   actually for. Rows now always align to the top. */

/* foundation/css/elements/_typography.css puts an absolutely positioned em
   dash before every `ul li`, and 0.15rem of margin-bottom on top of that. The
   gallery rows are lists for semantics only, so both have to be cancelled here
   or every cell renders a stray dash and picks up 2.4px of slack that the gap
   setting knows nothing about.

   Reset on the cell class rather than through `ul.no-list`. Builder Funnel
   Migration Base has that class too (css/elements/_typography.css), but it
   resets list-style, not the ::before dash foundation adds, so it would not
   cover this. The cell reset behaves the same in both themes. (Comment
   corrected 2026-09-17: it used to say the hook did not exist in BFMB.)

   Rich text rows are deliberately left alone. A list typed into one of those
   should still get the theme bullet. */
.bf-gallery__cell::before {
  content: none;
}

/* ---------------------------- images ---------------------------- */

.bf-gallery__image {
  border-radius: var(--bf-gallery-corner, 0px);
  display: block;
  width: 100%;
}

/* Cropped cells take the row ratio from module.html and fill it. */
.bf-gallery__cell--crop,
.bf-gallery__cell--crop .bf-gallery__opener,
.bf-gallery__cell--crop .bf-gallery__image {
  height: 100%;
}

.bf-gallery__cell--crop .bf-gallery__image {
  object-fit: cover;
}

/* Full-image cells keep their own proportions. */
.bf-gallery__cell--natural .bf-gallery__image {
  height: auto;
}

/* The opener is a button so the lightbox is reachable by keyboard.
   It must not look like one, so every button style is reset here. */
.bf-gallery__opener {
  background: none;
  border: 0;
  border-radius: var(--bf-gallery-corner, 0px);
  cursor: zoom-in;
  display: block;
  padding: 0;
  width: 100%;
}

/* The same wrapper is an anchor when the image links somewhere instead of opening the lightbox.
   zoom-in would be a lie there, and the theme underlines anchors globally, so both are undone. */
.bf-gallery__opener--link {
  color: inherit;
  cursor: pointer;
  text-decoration: none;
}

/* The theme's global `button:hover, button:focus` rule (0,1,1) outranks the single class above
   and paints the theme button's hover background behind the photo, visible in rounded corners.
   Restating the reset for both states matches its score (2026-09-17). */
.bf-gallery__opener:hover,
.bf-gallery__opener:focus {
  background: none;
  border: 0;
}

.bf-gallery__opener--link:hover,
.bf-gallery__opener--link:focus {
  text-decoration: none;
}

/* Accent, not currentColor (2026-09-17). The theme's `button` rule gives every button the button
   text colour, white by default, so the ring was white on a white page and keyboard focus was
   invisible. */
.bf-gallery__opener:focus-visible {
  outline: 2px solid var(--bf-color-accent, #A64B24);
  outline-offset: 3px;
}

/* --------------------------- text cells --------------------------- */

/* A text cell is a sibling of the photos, not a row of its own. Trim the
   outer margins so the copy lines up with the top of the images next to it
   instead of floating half a line lower. Anything the editor types inside,
   including a list with the theme bullet, is left alone. */
.bf-gallery__cell--text > *:first-child {
  margin-top: 0;
}

.bf-gallery__cell--text > *:last-child {
  margin-bottom: 0;
}

/* Where a text cell sits against the photos beside it. The row aligns its cells to the top,
   which leaves a short block of copy hanging at the top edge of a tall row. Padding and margins
   inside the rich text cannot fix that: they grow the cell downwards instead of moving it, and
   the rule above strips the outer ones anyway. align-self is the only lever, so it is exposed
   as a field on the cell. Requested by the delivery team for Cillessen's Project Spotlight,
   2026-09-25. */
.bf-gallery__cell--valign-center {
  align-self: center;
}

.bf-gallery__cell--valign-top {
  align-self: flex-start;
}

.bf-gallery__cell--valign-bottom {
  align-self: flex-end;
}

/* --------------------------- animation --------------------------- */

.bf-gallery[data-animation-enabled="true"] .bf-gallery__row {
  opacity: 0;
  transform: translateY(var(--bf-gallery-reveal-distance, 24px));
  transition: opacity 0.5s ease-out, transform 0.5s ease-out;
}

.bf-gallery[data-animation-enabled="true"] .bf-gallery__row.is-visible {
  opacity: 1;
  transform: none;
}

/* --------------------------- lightbox --------------------------- */

.bf-gallery__lightbox {
  align-items: center;
  display: flex;
  inset: 0;
  justify-content: center;
  position: fixed;
  /* Literal on purpose: the theme has no z-index scale. It has to clear the sticky header (20)
     and the menu dropdowns (99). pricing-card's own overlay uses 999999 and would still cover
     this, a known edge case, not worth a theme-wide scale for one pair of modals. */
  z-index: 1000;
}

.bf-gallery__lightbox[hidden] {
  display: none;
}

/* --bf-gallery-overlay comes from the Overlay colour field (module.html). The fallback is that
   field's default, so a copy without the HubL block looks the same. */
.bf-gallery__overlay {
  background-color: var(--bf-gallery-overlay, rgba(0, 0, 0, 0.9));
  inset: 0;
  position: absolute;
}

.bf-gallery__stage {
  margin: 0;
  max-height: var(--bf-gallery-stage-max-height, 88vh);
  max-width: var(--bf-gallery-stage-max-width, 88vw);
  position: relative;
}

/* Only the image transitions. The container and the overlay stay put. */
.bf-gallery__stage__image {
  display: block;
  max-height: var(--bf-gallery-stage-max-height, 88vh);
  max-width: var(--bf-gallery-stage-max-width, 88vw);
  object-fit: contain;
  opacity: 1;
  transition: opacity 0.25s ease-out;
}

.bf-gallery__stage__image.is-swapping {
  opacity: 0;
}

/* foundation applies the theme button padding, border and radius to every
   bare <button> through the generic `button, .button` rule in
   css/theme-overrides.css. With that padding on, the inline SVG from
   {% icon %} has no room left and the arrows render squashed. Padding is
   zeroed here and the controls get a fixed box instead, so their size no
   longer depends on what Theme Settings has under button spacing. */
.bf-gallery__control {
  align-items: center;
  background: none;
  border: 0;
  /* Literal white on purpose (reviewed in step 3, 2026-09-03): the lightbox always sits on its
     own fixed near-black overlay (.bf-gallery__overlay below), regardless of what the client
     picks as their brand colors, so the icon color is not a brand value to tokenize.
     2026-09-17: read through --bf-gallery-control, which the Control colour field sets, with the
     same white as fallback. */
  color: var(--bf-gallery-control, #fff);
  cursor: pointer;
  display: flex;
  /* Size of the close glyph only (the arrows are SVGs in a fixed box). An icon size, not body
     typography, so it deliberately does not follow the theme's font scale. Same reasoning for the
     control box sizes below. */
  font-size: 28px;
  justify-content: center;
  line-height: 1;
  padding: 0;
  position: absolute;
}

/* Same leak as the opener: without these states the theme's button:hover / button:focus painted
   a button-coloured rectangle behind the lightbox arrows and the close glyph (2026-09-17). */
.bf-gallery__control:hover,
.bf-gallery__control:focus {
  background: none;
  border: 0;
  color: var(--bf-gallery-control, #fff);
}

.bf-gallery__control--prev,
.bf-gallery__control--next {
  height: 50px;
  width: 30px;
}

.bf-gallery__control--close {
  height: 40px;
  width: 40px;
}

/* {% icon %} does not put extra_classes on the SVG. It wraps the graphic in
   its own <span class="hs_cos_wrapper ..."> and the class lands there, so
   .bf-gallery__control__icon is the wrapper and never the glyph itself.

   The SVG that tag emits carries a viewBox but no width and no height. That
   resolves to height: 100% of a parent whose height is auto, so the arrow
   collapses to nothing and the button looks empty. Both the wrapper and the
   SVG need a real box.

   100% x 100% does not distort the glyph: SVG defaults to
   preserveAspectRatio="xMidYMid meet", so a 256x512 viewBox letterboxes
   inside a 30x50 button instead of stretching. */
.bf-gallery__control__icon,
.bf-gallery__control svg {
  display: block;
  height: 100%;
  width: 100%;
}

.bf-gallery__control[hidden] {
  display: none;
}

/* The {% icon %} tag emits an inline SVG whose paths carry no fill, so they
   paint black and disappear against the overlay. `color` never reaches SVG
   fill on its own. foundation/modules/portfolio-grid.module sets fill
   explicitly; here it follows currentColor instead, so the Control colour
   field in Styles keeps driving the arrows and the close glyph together.
   Targeting path as well as svg matters: CSS beats a fill presentation
   attribute, an inherited value would not. */
.bf-gallery__control svg,
.bf-gallery__control__icon,
.bf-gallery__control__icon path,
.bf-gallery__control svg path {
  fill: currentColor;
}

.bf-gallery__control--prev {
  left: var(--bf-space-xs, 8px);
}

.bf-gallery__control--next {
  right: var(--bf-space-xs, 8px);
}

.bf-gallery__control--close {
  right: var(--bf-space-xs, 8px);
  top: var(--bf-space-xs, 8px);
}

.bf-gallery__control:focus-visible {
  outline: 2px solid currentColor;
  outline-offset: 2px;
}

/* --------------------------- responsive --------------------------- */

/* Tablet. Equal width rows drop to two per line.

   768 to 1024 is this module's own tablet range. The theme has no tablet breakpoint (HubSpot's
   theme.json accepts only `mobile`), and its phone breakpoint, 767, is the one used below. */

   Justified rows deliberately stay on one line. Wrapping needs a definite
   flex-basis, and the moment the basis stops being 0 the widths are no longer
   proportional to the aspect ratios: every cell starts from the same 50% and
   only the leftover space is shared out by the growth factor. Heights then
   stop matching and the flush row falls apart into exactly the ragged look
   this model exists to avoid. A justified row of three on a tablet reads
   fine; a broken one does not.

   Bounded at the bottom by min-width on purpose. `:not()` contributes to
   specificity, so these selectors carry three classes against the phone
   block's two. Left open-ended, the tablet rule would keep winning below
   768px and rows would stay two per line on a phone. */
@media screen and (min-width: 768px) and (max-width: 1024px) {
  .bf-gallery__row--items:not(.bf-gallery__row--justified) {
    flex-wrap: wrap;
  }

  .bf-gallery__row--items:not(.bf-gallery__row--justified) .bf-gallery__cell {
    flex: 1 1 calc(50% - (var(--bf-gallery-gap, var(--bf-space-md, 24px)) / 2));
  }
}

/* Phone. One image per line in both models. Sits last on purpose: it matches
   `.bf-gallery__row--justified .bf-gallery__cell` on specificity, so source
   order is what lets it win. */
@media screen and (max-width: 767px) {
  .bf-gallery__row--items {
    flex-wrap: wrap;
  }

  .bf-gallery__row--items .bf-gallery__cell {
    flex: 1 1 100%;
  }

  .bf-gallery__control {
    font-size: 22px;
  }

  .bf-gallery__control--prev,
  .bf-gallery__control--next {
    height: 40px;
    width: 24px;
  }
}

/* ----------------------- reduced motion ----------------------- */

@media (prefers-reduced-motion: reduce) {
  .bf-gallery[data-animation-enabled="true"] .bf-gallery__row {
    opacity: 1;
    transform: none;
    transition: none;
  }

  .bf-gallery__stage__image {
    transition: none;
  }
}
