/* --------------------------------------------------------------------------
   Info Block
   Content is bottom-anchored. Revealing the body pushes the title UP — the
   title is not animated from bottom to top. Measured from the mocks: the gap
   from the last line of ink to the card's bottom edge is 42.7pt collapsed and
   43.7pt expanded, i.e. unchanged.
   -------------------------------------------------------------------------- */

/* NOTE: do not put layout on :host. Webflow renders the component inside a
   <code-island> and sets `display: contents` on it as an INLINE style, which
   beats every :host rule and removes the host's box entirely. The real flex
   item is therefore .ib, so all layout lives there. */
:host {
  display: block;
}

.ib {
  --ib-ice-white: #fcfcfc;
  --ib-oxford-blue: #07274b;
  --ib-celestial-blue: #469dc8;
  --ib-carolina-blue: #84bcda;
  --ib-polynesian-blue: #0e4c92;
  --ib-paynes-gray: #4c5870;

  --ib-font-title: "Montserrat", system-ui, -apple-system, "Segoe UI", sans-serif;
  --ib-font-body: "Aptos", "Inter", system-ui, -apple-system, "Segoe UI", sans-serif;

  /* Mock ratios at its 420px width: title 52px = 12.4% of width, body 32px =
     7.6%, padding 20px = 4.8% / 40px = 9.5%. `cqi` is a share of THIS
     component's width, not the viewport, which is what "fills the width it is
     given" actually calls for. Clamped so it stays readable when squeezed and
     does not become a billboard in a wide column. */
  --ib-title-size: clamp(1.75rem, 12.4cqi, 3.25rem);
  --ib-body-size: clamp(1rem, 7.6cqi, 2rem);
  --ib-pad-x: clamp(1rem, 4.8cqi, 1.25rem);
  --ib-pad-y: clamp(1.25rem, 9.5cqi, 2.5rem);

  --ib-bg: var(--ib-celestial-blue);
  --ib-fg: var(--ib-ice-white);
  /* Explicit rather than `rgb(from …)` relative colour, which is too new to
     rely on across the browsers this site has to support. */
  --ib-track: rgba(252, 252, 252, 0.25);
  --ib-thumb: rgba(252, 252, 252, 0.7);

  position: relative;
  /* No in-flow content: the scroll area is absolutely positioned inside, so the
     card's height comes only from its min-height floor and from stretching to
     its row. Revealed text can overflow and scroll, but never push the card
     taller. */
  display: block;
  box-sizing: border-box;
  width: 100%;
  /* `height: 100%` here was the bug behind cards not matching each other:
     `align-items: stretch` only stretches a flex item whose cross size is
     `auto`, so declaring a height opted every card out of stretching and each
     one sized to its own title. `min-height` fills a parent that HAS been
     stretched without suppressing the stretch itself. */
  height: auto;
  /* .ib is the actual flex item (the host has no box), so align-self here
     overrides the parent's align-items — card_flex is set to flex-start, which
     is why cards kept their own heights instead of matching. */
  align-self: stretch;
  /* Fills a stretched row; never below the configured minimum or the measured
     content floor (--ib-content-min, set from JS). */
  min-height: max(100%, var(--ib-min-h, 240px), var(--ib-content-min, 0px));
  /* The query container must be a real element inside the shadow root.
     `container-type` on :host did NOT establish one, so every `cqi` fell back
     to the viewport — 12.4vw of a 1279px page is 158px, which pinned the title
     at its 52px ceiling on a 263px card and overflowed it. Padding lives on
     .ib-scroll so its cqi values resolve against this container. */
  container-type: inline-size;
  border-radius: 16px;
  background: var(--ib-bg);
  box-shadow: 0 6px 18px rgba(7, 39, 75, 0.18);
  color: var(--ib-fg);
  overflow: hidden;
}

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

/* Carolina Blue is light enough that Ice White text fails contrast badly
   (~1.9:1), so that variant flips to Oxford Blue text. */
.ib[data-colour="carolina"] {
  --ib-bg: var(--ib-carolina-blue);
  --ib-fg: var(--ib-oxford-blue);
  --ib-track: rgba(7, 39, 75, 0.2);
  --ib-thumb: rgba(7, 39, 75, 0.6);
}
.ib[data-colour="polynesian"] { --ib-bg: var(--ib-polynesian-blue); }
.ib[data-colour="oxford"] { --ib-bg: var(--ib-oxford-blue); }

/* Holds the title AND the body, so in Title + preview they scroll together —
   the title is not pinned above the text.

   NOT a scroll container by default. `overscroll-behavior: contain` stops a
   scroll gesture chaining out to the page, so leaving it on in Fit all text —
   where there is nothing to scroll — made the card swallow page scrolling
   whenever the pointer was over it. Only Title + preview opts in below. */
.ib-scroll {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  padding: var(--ib-pad-y) var(--ib-pad-x);
  overflow: visible;
  /* Native bar hidden in favour of .ib-scrollbar — iOS overlay scrollbars fade
     out and cannot be made persistent in CSS. */
  scrollbar-width: none;
  --ib-fade: calc(var(--ib-body-size) * 1.2);
}

.ib-scroll::-webkit-scrollbar { display: none; }

/* Title + preview: the only mode whose content can outgrow the card. Here
   containing the scroll is right — scrolling the preview should not also drag
   the page. */
.ib[data-height-mode="preview"] .ib-scroll {
  overflow-y: auto;
  overscroll-behavior: contain;
}

/* Bottom-anchored with an auto margin, not justify-content: flex-end.
   flex-end inside a scroll container overflows UPWARD, which would put the
   title above scroll position 0 where it can never be reached. An auto margin
   absorbs free space while the content fits and collapses to zero once it
   overflows: title at the bottom when closed, at the top when it scrolls. */
.ib-content {
  margin-top: auto;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 8px;
  flex: none;
}

.ib-title {
  flex: none;
  margin: 0;
  font-family: var(--ib-font-title);
  font-weight: 600;
  font-size: var(--ib-title-size);
  line-height: 1.2;
  color: inherit;
  /* Belt and braces: even correctly sized, one long unbroken word would
     otherwise overflow the card. */
  overflow-wrap: anywhere;
  hyphens: auto;
}

/* The heading wraps a real button — the standard disclosure pattern. Keeps
   heading semantics while giving keyboard and screen-reader users a control,
   which a hover-only card would not have. */
.ib-toggle {
  display: block;
  width: 100%;
  margin: 0;
  padding: 0;
  border: 0;
  background: none;
  font: inherit;
  color: inherit;
  text-align: left;
  cursor: pointer;
}

.ib-toggle:focus-visible {
  outline: 3px solid var(--ib-ice-white);
  outline-offset: 4px;
  border-radius: 4px;
}

.ib[data-colour="carolina"] .ib-toggle:focus-visible {
  outline-color: var(--ib-oxford-blue);
}

/* Animated from 0 to the body's natural height. Overflow is handled by the
   outer .ib-scroll, not here. */
.ib-body {
  flex: none;
  width: 100%;
  height: 0;
  overflow: hidden;
  opacity: 0;
  transition: height 0.45s cubic-bezier(0.4, 0, 0.2, 1), opacity 0.3s ease;
}

/* A block formatting context, so the slotted copy's own margins (a paragraph's
   top and bottom margin) are contained here rather than collapsing out through
   it. When they collapsed, offsetHeight left them out: the body was sized ~42px
   short, which clipped its last line in Fit all text. */
.ib-body-content {
  display: flow-root;
}

/* One body line of fade on whichever edge still has content beyond it. The
   mask sits on the scroll container's box, so it stays put while text moves
   under it. */
.ib-scroll[data-fade-top] {
  -webkit-mask-image: linear-gradient(to bottom, transparent 0, #000 var(--ib-fade), #000 100%);
  mask-image: linear-gradient(to bottom, transparent 0, #000 var(--ib-fade), #000 100%);
}

.ib-scroll[data-fade-bottom] {
  -webkit-mask-image: linear-gradient(to bottom, #000 0, #000 calc(100% - var(--ib-fade)), transparent 100%);
  mask-image: linear-gradient(to bottom, #000 0, #000 calc(100% - var(--ib-fade)), transparent 100%);
}

.ib-scroll[data-fade-top][data-fade-bottom] {
  -webkit-mask-image: linear-gradient(to bottom, transparent 0, #000 var(--ib-fade), #000 calc(100% - var(--ib-fade)), transparent 100%);
  mask-image: linear-gradient(to bottom, transparent 0, #000 var(--ib-fade), #000 calc(100% - var(--ib-fade)), transparent 100%);
}

/* The body arrives through <slot>, so it is light-DOM content. ::slotted
   reaches it; ordinary descendant selectors do not.

   Scoped to the body. Unscoped, it matches every slot's assigned node in this
   shadow root, and the title is a slot of its own since it became a Text Node
   — so the body type was landing on the title, shrinking it and adding a
   bottom margin. The card measures the title's height to work out how far it
   opens, so that was a layout bug as well as a type one. */
.ib-body-content ::slotted(*) {
  margin: 0 0 0.75em;
  font-family: var(--ib-font-body);
  font-weight: 400;
  font-size: var(--ib-body-size);
  line-height: 1.2;
  color: inherit;
  overflow-wrap: anywhere;
}

/* The title keeps the heading type set on .ib-title / .ib-toggle. */
.ib-title ::slotted(*) {
  margin: 0;
  font: inherit;
  color: inherit;
  letter-spacing: inherit;
  text-transform: inherit;
  overflow-wrap: anywhere;
}

.ib-body p {
  margin: 0 0 0.75em;
  overflow-wrap: anywhere;
  font-family: var(--ib-font-body);
  font-weight: 400;
  font-size: var(--ib-body-size);
  line-height: 1.2;
  color: inherit;
}

.ib-body p:last-child { margin-bottom: 0; }

/* Shown only while the body actually overflows. */
.ib-scrollbar {
  position: absolute;
  top: var(--ib-pad-y);
  right: calc(var(--ib-pad-x) / 2.5);
  bottom: var(--ib-pad-y);
  /* Matches .ib-scroll's padding, so the track lines up with the content. */
  z-index: 2;
  width: 4px;
  border-radius: 100px;
  background: var(--ib-track);
  pointer-events: none;
}

.ib-scrollbar-thumb {
  position: absolute;
  left: 0;
  width: 100%;
  min-height: 24px;
  border-radius: 100px;
  background: var(--ib-thumb);
}

@media (prefers-reduced-motion: reduce) {
  .ib-body { transition: none; }
}

/* ---------- Tablet and mobile: always open, full text ----------
   Webflow's tablet breakpoint and below (<= 991px). Done in CSS as well as JS
   so the server-rendered first paint is already open — waiting for JS would
   animate every card open on page load.

   The scroll area goes back into normal flow, so the card simply grows to fit
   its content: no internal scrolling, nothing trapping the page scroll. */
@media (max-width: 991px) {
  .ib {
    display: flex;
    flex-direction: column;
  }

  .ib-scroll,
  .ib[data-height-mode] .ib-scroll {
    position: relative;
    inset: auto;
    flex: 1 1 auto;
    overflow: visible;
    overscroll-behavior: auto;
  }

  .ib-body {
    height: auto !important;
    opacity: 1 !important;
    transition: none;
  }

  .ib-scrollbar {
    display: none;
  }

  .ib-scroll[data-fade-top],
  .ib-scroll[data-fade-bottom],
  .ib-scroll[data-fade-top][data-fade-bottom] {
    -webkit-mask-image: none;
    mask-image: none;
  }
}

