/* Image accordion.

   The rows are accordion.module's rows: same <details>/<summary>, same card,
   same marker pair, same class names. module.html copies its markup rather
   than approximating it, so the two cannot drift apart visually.

   The behavioural CSS has to be repeated here, and that is a platform
   constraint rather than an oversight. HubSpot loads a module's module.css
   only on pages where that module is placed, so accordion.module's stylesheet
   is simply absent unless an editor happens to have put one on the same page.
   Relying on it would give a row that collapses on some pages and not others.
   The rules below marked "from accordion.module" are that module's, carried
   verbatim; change them there and change them here.

   On top of them this module adds what is its own: the runway and its pin, the
   image cross-fade, the design's 1px closed border and purple open heading,
   and the ruled list inside a row. The parent DnD section still owns page
   width, gutters and vertical spacing, and .auxo-prose still owns the rich
   text.

   --- Read this before changing anything above it in the cascade ------------

   The pin depends on src/css/foundation.css declaring overflow-x: clip, not
   hidden, on html and body. When one axis takes a scrollable value the other's
   visible computes to auto, so hidden would quietly make those elements scroll
   containers — and a sticky element pins against its nearest scrolling
   ancestor rather than the viewport. The effect would simply never start, with
   no error and nothing on screen to explain it. Do not put hidden back.

   --- The shape of this file ------------------------------------------------

   Everything defaults to the static presentation: no runway, nothing pinned,
   every row operable by click like any other accordion. The scrolled pacing is
   layered on under .auxo-image-accordion--scrubbed, a class module.js adds
   only when the viewport is at least lg and the reader has not asked for
   reduced motion.

   That ordering is the point. Mobile, JavaScript disabled, reduced motion and
   a browser without sticky all land on the same path, and on that path the
   rows are still a working accordion rather than a list frozen shut. */

.auxo-image-accordion {
  /* Raw values rather than theme tokens: theme.css exposes no duration or
     easing alias, and module.css is served by HubSpot uncompiled, so it cannot
     use a utility or read one that is not a plain custom property. The
     reasoning accordion.module gives for the same choice.

     400ms against that module's 250ms: a panel answering a click should feel
     immediate, while this one is being paced by the scroll position and wants
     to feel carried rather than switched. */
  --auxo-ia-duration: 400ms;
  --auxo-ia-ease: ease-in-out;
}

/* --- Card -----------------------------------------------------------------
   accordion.module draws the open border, by way of the
   open:border-auxo-purple-blue utility in module.html. Only the two things
   the design adds on top of it are here. */

.auxo-image-accordion__step,
.auxo-image-accordion__step-heading {
  transition:
    border-color var(--auxo-ia-duration) var(--auxo-ia-ease),
    box-shadow var(--auxo-ia-duration) var(--auxo-ia-ease),
    color var(--auxo-ia-duration) var(--auxo-ia-ease);
}

/* The design's closed card is 1px where accordion.module's is 2px. The width
   stays at 2px and the closed state paints only its inner pixel, the outer one
   left transparent: an actual 1px-to-2px change would shift the card's
   contents by a pixel on every open and close. */
.auxo-image-accordion__step:not([open]) {
  border-color: transparent;
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--auxo-ink) 30%, transparent);
}

/* The design puts the open row's heading in the brand purple and leaves the
   closed ones in ink, where accordion.module keeps all of its questions in
   ink. Direct child, so a nested disclosure cannot recolour its parent. */
.auxo-image-accordion__step[open] > .auxo-image-accordion__step-heading {
  color: var(--auxo-purple-blue);
}

/* Decorative scroll meter, inset along the active card's bottom edge.
   Absolute positioning keeps the card's height unchanged as rows switch.

   Drawn by the card's own two pseudo-elements — ::before the track, ::after
   the fill — rather than by an element inside the answer. Everything after a
   <summary> is swallowed by ::details-content, so an element in the markup has
   nowhere to sit but inside the body, and the body is what travels while a
   focused card is being read: a meter in there would scroll away with the text
   it is meant to be measuring. The card is the thing the meter belongs to, and
   this attaches it to the card. */
.auxo-image-accordion__step {
  position: relative;
}

@media (width >= 64rem) and (prefers-reduced-motion: no-preference) {
  .auxo-image-accordion--scrubbed .is-scroll-active[open]::before,
  .auxo-image-accordion--scrubbed .is-scroll-active[open]::after {
    content: "";
    position: absolute;
    inset-inline: 16px;
    bottom: 5px;
    height: 3px;
    border-radius: 999px;
    pointer-events: none;
    /* Above the answer, which reaches the card's bottom edge while a focused
       card is being read. */
    z-index: 1;
  }

  .auxo-image-accordion--scrubbed .is-scroll-active[open]::before {
    background: color-mix(in srgb, var(--auxo-purple-blue) 15%, transparent);
  }

  .auxo-image-accordion--scrubbed .is-scroll-active[open]::after {
    background: var(--auxo-purple-blue);
    transform: scaleX(var(--auxo-ia-progress, 0));
    transform-origin: left;
  }
}

/* --- Ruled list -----------------------------------------------------------
   The design rules each item of a list inside one of these cards. It belongs
   here rather than in prose.css: it is this module's treatment of a list, not
   the theme's.

   :where() keeps the specificity at zero so a utility in markup still wins,
   and scopes it to a list that is a direct child of the body — a nested list
   inside one of these items is a sub-list and should read as one. */

.auxo-image-accordion__body > :where(ul) {
  /* The rule spans the card, so the list carries no indent of its own and each
     item is inset by its marker instead. */
  margin: 0;
  padding: 0;
  list-style: none;
}

.auxo-image-accordion__body > :where(ul) > :where(li) {
  border-block-end: 1px solid color-mix(in srgb, var(--auxo-purple-blue) 27%, transparent);
  padding-block: 0.625rem;
  /* The design's 24px indent, with the bullet drawn inside it so the rule
     still runs the full width of the card behind it. */
  padding-inline-start: 1.5rem;
  position: relative;
}

.auxo-image-accordion__body > :where(ul) > :where(li)::before {
  content: "•";
  position: absolute;
  inset-inline-start: 0.5rem;
  color: var(--auxo-ink);
}

/* --- Panel (from accordion.module) ----------------------------------------
   ::details-content is the panel the browser generates for everything after
   the summary, so the row animates without a wrapper element.

   The height is a grid track going 0fr to 1fr. The obvious spelling — height
   from 0 to auto, via interpolate-size: allow-keywords — is Chromium-only:
   neither Firefox nor Safari has shipped it, and the theme supports the
   current release of all four browsers. The grid track interpolates in all of
   them and has since 2022.

   content-visibility with allow-discrete is load-bearing rather than a
   flourish. <details> flips that property discretely, so without it the panel
   is removed the instant the row closes and only the opening half of the
   transition is ever seen. */

.auxo-image-accordion details::details-content {
  display: grid;
  grid-template-rows: 0fr;
  overflow: hidden;
  opacity: 0;

  /* The space between heading and body starts at nothing and opens to 14px
     with the row. Padding on this clipping box rather than a margin on the
     body inside it: a margin on the grid item sits outside the animating track
     and holds its full height from the first frame, so the row would jump open
     to 14px before the height began to move. */
  padding-block-start: 0;

  transition:
    grid-template-rows var(--auxo-ia-duration) var(--auxo-ia-ease),
    padding-block-start var(--auxo-ia-duration) var(--auxo-ia-ease),
    opacity var(--auxo-ia-duration) var(--auxo-ia-ease),
    content-visibility var(--auxo-ia-duration) allow-discrete;
}

.auxo-image-accordion details[open]::details-content {
  grid-template-rows: 1fr;
  padding-block-start: 0.875rem;
  opacity: 1;
}

/* --- Disclosure (from accordion.module) -----------------------------------
   The triangle belongs to the summary's default display: list-item, which the
   flex utility in module.html displaces — enough on its own in Chrome and
   Firefox. Safari draws its own marker through a non-standard pseudo-element
   that has to be named to be turned off, so both are stated. */

.auxo-image-accordion summary {
  list-style: none;
}

.auxo-image-accordion summary::-webkit-details-marker {
  display: none;
}

/* --- Marker (from accordion.module) ---------------------------------------
   Both glyphs are in the markup and cross-fade between them, so the state
   change costs no script. module.html places the pair in one grid cell, so
   they overlap and the fade has something to fade against; hiding one with
   display would leave nothing to transition.

   The counter-rotation gives the swap a direction: the mark turns as it
   changes rather than dissolving in place. Each glyph is only ever rotated
   while it is fading out, so the one the reader can actually see is at rest at
   0deg in both settled states. */

.auxo-image-accordion summary .auxo-accordion__plus,
.auxo-image-accordion summary .auxo-accordion__minus {
  transition:
    opacity var(--auxo-ia-duration) var(--auxo-ia-ease),
    transform var(--auxo-ia-duration) var(--auxo-ia-ease);
  /* A fully transparent glyph still answers a hit test, and the pair is
     stacked in one cell. */
  pointer-events: none;
}

.auxo-image-accordion details:not([open]) > summary .auxo-accordion__minus,
.auxo-image-accordion details[open] > summary .auxo-accordion__plus {
  opacity: 0;
}

.auxo-image-accordion details:not([open]) > summary .auxo-accordion__minus {
  transform: rotate(-90deg);
}

.auxo-image-accordion details[open] > summary .auxo-accordion__plus {
  transform: rotate(90deg);
}

/* --- Images ---------------------------------------------------------------
   All of them sit in one grid cell, stacked, so they overlap and the fade has
   something to fade against — the arrangement accordion.module's marker pair
   uses. A display swap would leave nothing to transition.

   Only the first is visible by default. Without JavaScript none of them
   carries the active class, so this is what stops the last image in the markup
   winning by source order. */

.auxo-image-accordion__image {
  opacity: 0;
  transition: opacity var(--auxo-ia-duration) var(--auxo-ia-ease);
  /* A fully transparent image still answers a hit test, and they are stacked. */
  pointer-events: none;
}

.auxo-image-accordion__image:first-child {
  opacity: 1;
}

.auxo-image-accordion__image.is-active {
  opacity: 1;
}

/* Once a row is selected, show its image or the nearest preceding row's image.
   With no preceding image, keep the media area blank. */
.auxo-image-accordion--image-selected
  .auxo-image-accordion__image:first-child:not(.is-active) {
  opacity: 0;
}

/* ===========================================================================
   Scrubbed — the scroll-paced presentation.

   Gated on a class module.js adds only at lg and above, and only when the
   reader has not asked for reduced motion.
   =========================================================================== */

/* --- Runway and pin -------------------------------------------------------
   The pin supplies the reading/hold distance. Its sticky child is only as tall
   as the heading and visible columns, so neither end reserves an empty part
   of the viewport. JavaScript centres that child with a measured top offset. */

.auxo-image-accordion--scrubbed .auxo-image-accordion__pin {
  height: var(--auxo-ia-runway, auto);
  display: flow-root;
  overflow-anchor: none;
}

.auxo-image-accordion--scrubbed .auxo-image-accordion__inner {
  position: sticky;
  top: var(--auxo-ia-pin-top, 0px);
  height: var(--auxo-ia-frame-height, auto);
  overflow: clip;
}

/* --- The image stays put --------------------------------------------------
   Centre it within the usable viewport and preserve its aspect ratio, capped
   by both the column width and the screen height. */

.auxo-image-accordion--scrubbed .auxo-image-accordion__media {
  max-height: 100%;
  max-width: 100%;
  width: min(100%, calc(var(--auxo-ia-content-height, 50svh) * 681 / 589));
  place-self: center;
}

/* Page scrolling moves a tall list upward until its bottom is visible, then
   holds that position for the row's dwell. Short lists start below the heading. */
.auxo-image-accordion--scrubbed .auxo-image-accordion__text {
  min-height: 0;
  align-self: start;
  transform: translateY(var(--auxo-ia-text-y, 0px));
}

/* --- Focused card ---------------------------------------------------------
   What happens when one open row is taller than the whole reading frame.

   Travelling the column is the right motion while the column fits: the rows
   move as a set and the reader sees where they are in it. Once a single card
   outgrows the frame that motion stops working, because the card's own top
   edge leaves the frame on the first pixel of travel and its bottom edge —
   which is where the meter is — does not arrive until the last. At 1024x600
   with a long answer, neither edge is in view at any point of the read, and
   the meter appears about two thirds of the way through.

   So past that size the card becomes the frame. Its height is capped to the
   frame's, which pins its border, its corners and its meter exactly where the
   frame is, and the answer travels inside it instead. --auxo-ia-text-y stops
   being the reading motion for such a row and becomes a fixed offset that puts
   its top at the frame's top; --auxo-ia-body-y carries the reading. The cap is
   --auxo-ia-stage-height, which is already the frame's own height.

   The collapsed rows are still in the column below it, outside the frame and
   clipped away: a focused card takes the space rather than sharing it.

   module.js decides this per row and adds .is-focused-card, so a module can
   hold one long row and two short ones and each is paced the way it needs. */
.auxo-image-accordion--scrubbed .is-focused-card[open] {
  height: var(--auxo-ia-stage-height, auto);
  /* The answer is taller than the card now, so the card has to do the
     clipping its own contents used to do for it. */
  overflow: hidden;
}

/* The summary is the card's fixed head and the answer its moving body, so the
   card lays them out rather than leaving both to flow: the panel has to be
   told to take the space the summary leaves and no more, or its content's
   automatic minimum size holds it open at full height and the cap does
   nothing. */
.auxo-image-accordion--scrubbed .is-focused-card[open] {
  display: flex;
  flex-direction: column;
}

.auxo-image-accordion--scrubbed .is-focused-card[open]::details-content {
  flex: 1 1 auto;
  min-height: 0;
}

.auxo-image-accordion--scrubbed .is-focused-card[open] > .auxo-image-accordion__body {
  transform: translateY(var(--auxo-ia-body-y, 0px));
}

/* The real title and disclosure marker stay together as the answer passes
   underneath. An opaque backing keeps rich text from showing through them.

   A focused card needs half of this. Its summary is the fixed head of a fixed
   box, so it already stays put and module.js leaves --auxo-ia-summary-y at 0 —
   the counter-translation has nothing to do. The backing it still wants: the
   answer scrolls to within a line of the title, and without the rule beneath
   it the first half-clipped line reads as a stray fragment of the heading
   rather than the top of the text. So .is-summary-pinned goes on for the whole
   of a focused row's read, and it is the backing alone that it buys there. */
@media (width >= 64rem) and (prefers-reduced-motion: no-preference) {
  .auxo-image-accordion--scrubbed .is-scroll-active[open] > .auxo-image-accordion__step-heading {
    position: relative;
    z-index: 2;
    transform: translateY(var(--auxo-ia-summary-y, 0px));
  }

  .auxo-image-accordion--scrubbed .is-summary-pinned[open] > .auxo-image-accordion__step-heading::before {
    content: "";
    position: absolute;
    z-index: -1;
    /* Include the card border so no strip of answer peeks above the title. */
    inset: -18px -16px -14px;
    background: var(--auxo-ia-summary-background, var(--auxo-off-white));
    border-bottom: 1px solid color-mix(in srgb, var(--auxo-purple-blue) 27%, transparent);
    pointer-events: none;
  }
}

/* ===========================================================================
   Fallbacks
   =========================================================================== */

/* Reduced motion is answered by not doing this at all, rather than by doing it
   faster. accordion.module cuts its duration to 1ms because its motion is
   incidental to opening a row; here the pacing IS the feature, so a reader who
   has asked for none has asked for this not to happen. module.js checks the
   same query and never adds --scrubbed, and this is the belt to that braces.

   The rows remain a working accordion throughout — they are <details>, so they
   answer a click whether or not any of this applies. */
@media (prefers-reduced-motion: reduce) {
  .auxo-image-accordion--scrubbed .auxo-image-accordion__pin {
    height: auto;
  }

  .auxo-image-accordion--scrubbed .auxo-image-accordion__inner {
    position: static;
    height: auto;
    overflow: visible;
  }

  .auxo-image-accordion--scrubbed .auxo-image-accordion__text {
    transform: none;
  }

  .auxo-image-accordion--scrubbed .auxo-image-accordion__media {
    max-height: none;
  }

  /* The duration is cut rather than the transition dropped: the
     content-visibility step must stay in the list for the panel to leave the
     layout on close, and dropping the rule would take it with it. 1ms keeps
     that plumbing intact while the movement stops being perceptible.
     accordion.module carries the same note. */
  .auxo-image-accordion {
    --auxo-ia-duration: 1ms;
  }

  .auxo-image-accordion__image {
    transition: none;
  }
}

/* Every browser the theme supports has had sticky for years, so this is
   insurance rather than a live concern. It earns its place by documenting the
   dependency: without sticky the runway would be dead scroll with nothing held
   inside it, so it collapses to the plain accordion instead. */
@supports not (position: sticky) {
  .auxo-image-accordion--scrubbed .auxo-image-accordion__pin,
  .auxo-image-accordion--scrubbed .auxo-image-accordion__inner {
    height: auto;
    overflow: visible;
  }
  .auxo-image-accordion--scrubbed .auxo-image-accordion__text {
    transform: none;
  }
}

/* Adaptive layout. Set --auxo-ia-top-offset on the module or an ancestor when
   the page has a fixed header; the current theme header scrolls with the page. */
.auxo-image-accordion {
  --auxo-ia-edge-space: clamp(1rem, 4svh, 4rem);
}

.auxo-image-accordion__inner {
  display: flex;
  flex-direction: column;
  gap: 2rem;
}

.auxo-image-accordion__intro {
  flex: none;
}

.auxo-image-accordion--scrubbed .auxo-image-accordion__content {
  flex: 0 0 var(--auxo-ia-stage-height, auto);
  min-height: 0;
  grid-template-rows: minmax(0, 1fr);
  /* Text can move only within the reading area, never across the heading. */
  overflow: clip;
}

@media (width >= 64rem) {
  .auxo-image-accordion__inner {
    /* The section-spacing token is a figure for the space between sections of
       a page, and on a 600-tall screen spending it here costs 72px of the 400
       the rows were going to get. Held to a share of the screen instead, so it
       is the token's full value wherever there is room for it and proportionate
       where there is not.

       8svh, not 4: the share has to reach the token at an ordinary desktop
       height or this stops being a short-screen adjustment and becomes a
       redesign of the heading gap everywhere. 8svh is 72px at 900 tall, so
       every normal screen keeps the figure it has now and only a genuinely
       short one gives any of it back — 48px at 600, 40px at 500. */
    gap: clamp(1.5rem, 8svh, var(--auxo-section-space, 4.5rem));
  }

  .auxo-image-accordion--natural .auxo-image-accordion__media {
    position: sticky;
    top: calc(var(--auxo-ia-top-offset, 0px) + var(--auxo-ia-edge-space));
    width: min(100%, calc((100svh - var(--auxo-ia-top-offset, 0px) - 2 * var(--auxo-ia-edge-space)) * 681 / 589));
    align-self: start;
  }
}

/* The fit probe occupies no page space and never animates. Its columns retain
   the live width, so line wrapping and font metrics match the real content. */
.auxo-image-accordion--measuring {
  position: fixed;
  top: 0;
  left: -100000px;
  visibility: hidden;
  pointer-events: none;
}
.auxo-image-accordion--measuring .auxo-image-accordion__pin {
  height: auto;
}
.auxo-image-accordion--measuring .auxo-image-accordion__inner {
  position: static;
  /* Only the measuring copy fills the viewport. Edge space limits tall
     content; short compositions use a smaller sticky box on the live page. */
  top: var(--auxo-ia-top-offset, 0px);
  height: calc(100svh - var(--auxo-ia-top-offset, 0px));
  padding-block: var(--auxo-ia-edge-space);
}
/* Measure the full available area, independent of the current row's height. */
.auxo-image-accordion--measuring .auxo-image-accordion__content {
  flex: 1 1 0;
}
.auxo-image-accordion--measuring .auxo-image-accordion__text,
.auxo-image-accordion--measuring .is-scroll-active[open] > .auxo-image-accordion__step-heading {
  transform: none;
}
.auxo-image-accordion--measuring *,
.auxo-image-accordion--measuring details::details-content {
  transition: none !important;
  animation: none !important;
}

/* Keep the CSS fallback usable even before the media-query listener runs. */
@media (prefers-reduced-motion: reduce) {
  .auxo-image-accordion--scrubbed .auxo-image-accordion__content {
    flex: none;
    grid-template-rows: auto;
    overflow: visible;
  }
}
@supports not (position: sticky) {
  .auxo-image-accordion--scrubbed .auxo-image-accordion__content {
    flex: none;
    grid-template-rows: auto;
    overflow: visible;
  }
}
