/* ============================================================
   Kontakt.io — Video Carousel
   Root class: .kio-video-carousel
   Desktop first, 1200px container. Responsive down to 320px.
   ============================================================ */

@import url('https://fonts.googleapis.com/css2?family=Poppins:wght@200;300;400;500;600&display=swap');

.kio-video-carousel {
  /* Brand tokens — /memory/brand.md */
  --kio-white: #FFFFFF;
  --kio-pink-light: #F9EEF0;
  --kio-red: #FF0032;
  /* K Red Hover. Every hover state in this file is gated behind
     @media (hover: hover) — see the note on the tile button. */
  --kio-red-hover: #E2002C;
  --kio-red-active: #C4000E;
  --kio-purple: #3C2646;

  /* Category tile surface. Supplied by the design and NOT in
     /memory/brand.md — it is the same warm cream as the hero section's
     background, so the two sections read as one page. Kept as a single
     variable so it can be corrected in one place if the page's cream
     shifts. Closest brand token is K Light Pink #F9EEF0, which is cooler
     and does not match the hero. */
  --kio-tile-bg: #F7EDE6;

  /* Section base. Purple, so the module reads as one continuous block
     rather than a band floating on cream. Note this means the shape image
     only reads if it is LIGHTER than the band — a purple shape on a purple
     base is invisible. Switch to var(--kio-pink-light) for the inverse. */
  --kio-section-bg: var(--kio-purple);

  /* Layout */
  --kio-gutter: 20px;
  --kio-radius-md: 16px;
  --kio-radius-lg: 24px;
  --kio-tile-padding: 16px;

  /* Motion — /skills/animations.md */
  --kio-ease: cubic-bezier(0.2, 0, 0.2, 1);
  --kio-transition-fast: 150ms ease-out;

  position: relative;
  /* New stacking context so the deco shape can never slide under a
     neighbouring section's content. */
  isolation: isolate;
  padding: 0;
  background: var(--kio-section-bg);
  font-family: 'Poppins', sans-serif;
  -webkit-font-smoothing: antialiased;
}

.kio-video-carousel *,
.kio-video-carousel *::before,
.kio-video-carousel *::after {
  box-sizing: border-box;
}

/* ------------------------------------------------------------
   Purple band
   Carries the uploaded texture as a background-image.
   ------------------------------------------------------------ */

.kio-video-carousel__band {
  position: relative;
  padding: 72px 0 88px;
  background-color: var(--kio-purple);
  background-repeat: no-repeat;
  background-position: center top;
  background-size: cover;
}

/* ------------------------------------------------------------
   Decorative shape (uploaded asset)
   In normal flow directly beneath the band, so the section always
   ends exactly where the artwork does — whatever height the
   uploaded file happens to be. The Shape Width / Offset X / Offset
   Y fields override width, left and margin-top inline; position:
   relative makes Offset X shift the image without moving its
   layout box, so nudging it never changes the section height.
   ------------------------------------------------------------ */

.kio-video-carousel__deco {
  position: relative;
  left: 0;
  display: block;
  width: 100%; /* Overridable per-instance via the Shape Width field */
  height: auto;
  /* Closes the hairline seam some browsers render at the band edge. */
  margin-top: -1px;
  pointer-events: none;
  user-select: none;
}

.kio-video-carousel__inner {
  position: relative;
  z-index: 1;
  max-width: 1200px;
  margin: 0 auto;
  padding: 0 var(--kio-gutter);
}

/* ------------------------------------------------------------
   The section heading (Heading 2, 50px Poppins Light, white, with a
   48px margin below) was removed here along with the Heading field.
   The band now opens directly on the counter + solution name row, and
   its 72px top padding is the only thing setting that opening space.
   ------------------------------------------------------------ */

/* ------------------------------------------------------------
   Counter + active solution name — Body 2, Poppins Light
   ------------------------------------------------------------ */

.kio-video-carousel__meta {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 16px;
  margin: 0 0 20px;
  color: var(--kio-white);
  font-size: 1.625rem; /* 26px */
  font-weight: 300;
  line-height: 39px;
}

.kio-video-carousel__counter {
  flex: 0 0 auto;
  /* Stops the row shifting when the digit changes width. */
  font-variant-numeric: tabular-nums;
}

.kio-video-carousel__name {
  /* Direction of travel, set by JS before each swap. */
  --kio-dir: 1;
  flex: 1 1 auto;
  min-width: 0;
  text-align: right;
}

/* Enter-only animations. The text swaps instantly and then arrives from
   the direction of travel, so the label reads as spatially attached to
   the video that just slid in. No fade-out first: that adds latency and
   desyncs when slides are flicked in quick succession. */

.kio-video-carousel__name--enter {
  animation: kio-video-carousel-name-in 240ms var(--kio-ease) both;
}

.kio-video-carousel__counter-current--enter {
  animation: kio-video-carousel-fade-in 180ms var(--kio-ease) both;
}

@keyframes kio-video-carousel-name-in {
  from { opacity: 0; transform: translateX(calc(var(--kio-dir) * 14px)); }
  to   { opacity: 1; transform: translateX(0); }
}

@keyframes kio-video-carousel-fade-in {
  from { opacity: 0; }
  to   { opacity: 1; }
}

/* ------------------------------------------------------------
   Video frame
   overflow: hidden is required to clip the track and round the
   video corners — the intended exception to CLAUDE.md rule #7.
   This module has no sticky element and no modal, and the clip
   lives on this inner frame only, never on the section.

   touch-action: pan-y hands vertical gestures back to the page so
   the carousel can never hijack scrolling.
   ------------------------------------------------------------ */

.kio-video-carousel__frame {
  position: relative;
  aspect-ratio: 16 / 9;
  overflow: hidden;
  border-radius: var(--kio-radius-lg);
  background: var(--kio-white);
  touch-action: pan-y;
  cursor: grab;
}

@supports not (aspect-ratio: 16 / 9) {
  .kio-video-carousel__frame::before {
    content: '';
    display: block;
    padding-top: 56.25%;
  }
}

.kio-video-carousel__frame--dragging {
  cursor: grabbing;
}

.kio-video-carousel__frame:focus-visible {
  outline: 2px solid var(--kio-red);
  outline-offset: 3px;
}

.kio-video-carousel__track {
  position: absolute;
  inset: 0;
  display: flex;
  margin: 0;
  padding: 0;
  list-style: none;
  /* Stops a drag turning into a text selection on surrounding content. */
  user-select: none;
  -webkit-user-select: none;
  /* Duration is set per-move in JS; only transform is ever animated. */
  transition-property: transform;
  transition-timing-function: var(--kio-ease);
  transition-duration: 0ms;
}

.kio-video-carousel__slide {
  position: relative;
  flex: 0 0 100%;
  width: 100%;
  height: 100%;
  margin: 0;
  padding: 0;
  list-style: none;
}

.kio-video-carousel__video {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  background: var(--kio-white);
  /* Deliberately NOT pointer-events: none. Pointer events bubble from the
     video up to the frame, so the drag still works while the native
     controls stay clickable. JS ignores any press that starts in the
     bottom strip, so scrubbing the seek bar never turns into a swipe.

     The frame's cursor: grab also shows over the control bar, which is not
     draggable. Nothing to be done about it: Chrome no longer exposes
     ::-webkit-media-controls-enclosure to author stylesheets, so the rule
     that would fix it is silently dropped. Cosmetic only. */
}

/* ------------------------------------------------------------
   Nav row — arrows only

   This was a three-column grid (empty 1fr / arrows / CTA) so the arrows
   held dead-centre no matter how wide the CTA's label ran. With the CTA
   removed the arrows are the only child, so plain centred flex does the
   same job. Restore the grid if a right-hand control ever returns —
   centring the arrows against a sibling needs the balancing column back.
   ------------------------------------------------------------ */

.kio-video-carousel__nav {
  display: flex;
  align-items: center;
  justify-content: center;
  margin: 32px 0 0;
}

.kio-video-carousel__arrows {
  display: flex;
  justify-content: center;
  gap: 16px;
}

.kio-video-carousel__arrow {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 48px;
  height: 48px;
  padding: 0;
  border: 1.5px solid rgba(255, 255, 255, 0.4);
  border-radius: 50%;
  background: transparent;
  color: var(--kio-white);
  cursor: pointer;
  transition: background-color var(--kio-transition-fast),
              border-color var(--kio-transition-fast),
              color var(--kio-transition-fast),
              opacity var(--kio-transition-fast),
              transform var(--kio-transition-fast);
}

/* Gated like every other hover in this file: ungated, a tap on touch
   iPadOS left the arrow inverted to white until something else was
   tapped, which read as "this arrow is selected". */
@media (hover: hover) {
  .kio-video-carousel__arrow:hover:not(:disabled) {
    border-color: var(--kio-white);
    background: var(--kio-white);
    color: var(--kio-purple);
  }
}

.kio-video-carousel__arrow:active:not(:disabled) {
  transform: scale(0.94);
}

/* Dimmed and inert at each end, saying the same thing as the
   rubber-band resistance on the drag. */
.kio-video-carousel__arrow:disabled {
  opacity: 0.15;
  cursor: default;
  pointer-events: none;
}

.kio-video-carousel__arrow:focus-visible {
  outline: 2px solid var(--kio-red);
  outline-offset: 3px;
}

.kio-video-carousel__chevron {
  width: 22px;
  height: 22px;
}

/* ------------------------------------------------------------
   Category tiles

   Vertical rhythm through the whole band, so the two groups read as a
   hierarchy rather than a list of equal parts:

     band top            72px   (now sets the opening space on its own —
     counter + name     ↓ 20px   the 50px section heading that used to sit
     video frame                 here went with the Heading field)
     arrows             ↑ 32px
     ── group break ──  ↑ 72px   (bigger than any gap above it, which is
     tiles                        the whole reason the tiles read as a
     band bottom          88px    separate block and not as carousel chrome)

   Inside a tile the steps run 16 / 16 / 20 — all smaller than the 24px
   grid gutter between tiles, so a tile groups visually before it
   relates to its neighbours. The 16px inner padding is the design's
   spec and is what sets that inner scale.
   ------------------------------------------------------------ */

.kio-video-carousel__categories {
  margin-top: 72px;
}

.kio-video-carousel__tiles {
  display: grid;
  /* minmax(0, 1fr) rather than 1fr: a plain 1fr floors at the content's
     min-content width, so one long unbroken title would widen its column
     and throw the other three out of alignment. */
  grid-template-columns: repeat(4, minmax(0, 1fr));
  /* Row gap deliberately larger than the column gap. Equal gaps read as a
     mesh; a taller row gap reads as rows, which is what a 2x2 on the iPad
     needs to scan cleanly. */
  gap: 32px 24px;
  margin: 0;
  padding: 0;
  list-style: none;
}

/* position: relative is load-bearing — it is the containing block the
   button's stretched ::after resolves against, which is what makes the
   whole tile tappable. Removing it collapses the hit area back to the
   pill.

   align-items: center centres the title and button; the media is width:
   100% so it ignores it. The column layout plus stretched grid rows is
   what lets margin-top: auto on the button bottom-align all four
   buttons even when one title wraps to two lines. */
.kio-video-carousel__tile {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  margin: 0;
  padding: var(--kio-tile-padding);
  border-radius: var(--kio-radius-lg);
  background: var(--kio-tile-bg);
  text-align: center;
  list-style: none;
  transition: transform var(--kio-transition-fast);
}

/* No hover state on the tiles by request — the delivery device is a
   touch iPad, where there is no hovering pointer to respond to. A lift
   and shadow used to live here, gated behind @media (hover: hover)
   because a tap on touch iPadOS applies :hover and LEAVES it applied
   until something else is tapped, which stranded tiles lifted after
   every press. Removed rather than kept for desktop, so there is one
   behaviour to reason about instead of two.

   Press feedback stays, and it is the state that actually fires on
   touch. Scoped to the whole tile rather than the pill: the tap usually
   lands on the image, so scaling only the button would put the feedback
   somewhere the finger is not. Needs :has() because the :active element
   is the anchor, a descendant — browsers without it fall back to the
   button's own :active below. */
/* Pins the hover state flat. Nothing in this file lifts a tile, so this
   exists to stop the Elevate theme doing it — it ships hover transforms
   and shadows for card-like blocks, and a theme selector can outrank a
   single class. Cheaper than hunting the theme rule.

   The :active rule below still wins: :has() contributes its argument's
   specificity, making it (0,3,0) against this rule's (0,2,0). */
.kio-video-carousel__tile:hover {
  transform: none !important;
  box-shadow: none !important;
}

.kio-video-carousel__tile:has(.kio-video-carousel__tile-btn:active) {
  transform: scale(0.985);
}

.kio-video-carousel__tile-media {
  width: 100%;
}

/* Fixed 4:3 with object-fit: cover, so four uploads at four different
   aspect ratios still produce four identical tiles. The radius sits on
   the image itself rather than an overflow: hidden wrapper, keeping
   CLAUDE.md rule #7 intact — 16px is one step inside the tile's 24px,
   which is what reads as concentric at this padding.

   The white base shows while a lazy image is still in flight, so the
   tile does not flash cream-on-cream and then jump. */
.kio-video-carousel__tile-img {
  display: block;
  width: 100%;
  aspect-ratio: 4 / 3;
  object-fit: cover;
  border-radius: var(--kio-radius-md);
  background: var(--kio-white);
}

@supports not (aspect-ratio: 4 / 3) {
  .kio-video-carousel__tile-img {
    height: auto;
  }
}

/* 30px per the design. Not a /memory/brand.md heading step — the scale
   runs H4 38px then Body 1 32px — so it is spelled out here rather than
   pulled from a token, and Light 300 keeps it in the brand's heading
   voice. Purple on the cream tile, inverting the band's white-on-purple. */
.kio-video-carousel__tile-title {
  margin: 16px 0 20px;
  color: var(--kio-purple);
  font-family: 'Poppins', sans-serif;
  font-size: 1.875rem; /* 30px */
  font-weight: 300;
  line-height: 38px;
  letter-spacing: -0.01em;
  text-wrap: balance;
}

/* Brand primary button — /memory/brand.md: Poppins 400 15px/23,
   12px/44px padding, 50px pill. Rendered height 53px, clear of the 44px
   minimum tap target (the stretched overlay makes the real target the
   whole tile anyway).

   Deliberately NOT position: relative. The ::after below uses inset: 0,
   which must resolve against the tile, not against the pill — position
   it and the hit area shrinks to the button. */
.kio-video-carousel__tile-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  /* A <button>, not an anchor — it opens the tile's panel and navigates
     nowhere. Hence the reset trio: the UA gives buttons their own
     background, border, font and margins, none of which an anchor had,
     and Elevate ships button styles of its own that would otherwise
     land on top of this. */
  appearance: none;
  -webkit-appearance: none;
  /* Bottom-aligns every button in a row regardless of title wrap. The
     zero sides matter on a button — the UA adds horizontal margin. */
  margin: auto 0 0;
  padding: 12px 44px;
  border: 3px solid var(--kio-red);
  border-radius: 50px;
  background: var(--kio-red);
  color: var(--kio-white);
  font-family: 'Poppins', sans-serif;
  font-size: 0.9375rem; /* 15px */
  font-weight: 400;
  line-height: 23px;
  text-align: center;
  text-decoration: none;
  cursor: pointer;
  transition: background-color var(--kio-transition-fast),
              border-color var(--kio-transition-fast);
}

/* The tile-wide hit area. One stretched overlay per tile, rather than
   making the whole <li> the button, which would put the image and the
   title inside the button's accessible name. */
.kio-video-carousel__tile-btn::after {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: var(--kio-radius-lg);
}

/* K Red Hover per /memory/brand.md, driven from the tile so hovering
   anywhere on the card lights its button — the whole tile is the target,
   so the whole tile should respond.

   @media (hover: hover) is not optional here. Touch iPadOS applies
   :hover on tap and then LEAVES it applied until something else is
   tapped, so an ungated rule strands a tile in its hover colour after
   every press. The query means the iPad never enters this block at all
   and :active below is the state that actually fires there. */
@media (hover: hover) {
  .kio-video-carousel__tile:hover .kio-video-carousel__tile-btn,
  .kio-video-carousel__tile-btn:hover {
    border-color: var(--kio-red-hover);
    background: var(--kio-red-hover);
  }
}

/* Written as a descendant selector purely to match the hover rule's
   (0,3,0) specificity. A bare .kio-video-carousel__tile-btn:active is
   (0,2,0) and would lose to the tile-driven hover above on a mouse that
   is both hovering and pressing — which is every mouse press. */
.kio-video-carousel__tile .kio-video-carousel__tile-btn:active {
  border-color: var(--kio-red-active);
  background: var(--kio-red-active);
}

/* Ring the whole tile, since the whole tile is the target. The outline
   moves to the stretched ::after; the button's own outline is suppressed
   only because it is replaced here, never merely removed. White, because
   a red ring on a red fill is invisible. */
.kio-video-carousel__tile-btn:focus-visible {
  outline: none;
}

.kio-video-carousel__tile-btn:focus-visible::after {
  outline: 2px solid var(--kio-white);
  outline-offset: 3px;
}

/* Gives each button a unique accessible name — without it a screen
   reader reads four identical "Learn more" buttons. Same visually-hidden
   technique as __status; kept as its own class because that one is a
   live status region and this is a label fragment. */
.kio-video-carousel__tile-btn-context {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  border: 0;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
}

/* ------------------------------------------------------------
   Screen-reader status region
   ------------------------------------------------------------ */

.kio-video-carousel__status {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  border: 0;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
}

/* ------------------------------------------------------------
   Elevate theme override — link underline on hover (CLAUDE.md #15).
   The theme injects text-decoration: underline on :hover through a CSS
   variable. The only anchors this module can contain are ones an editor
   adds inside a panel's rich text, so this is pre-emptive rather than
   load-bearing — it used to cover the tile buttons, which were anchors
   before they became <button>s that open a panel instead of navigating.
   The child selectors matter because a theme rule can target the span
   inside a link rather than the link itself.
   ------------------------------------------------------------ */

.kio-video-carousel a:hover,
.kio-video-carousel a:hover span,
.kio-video-carousel a:focus,
.kio-video-carousel a:focus span {
  text-decoration: none !important;
}

/* Elevate theme override — button text colour.
   Same root cause as the underline: the theme styles buttons with a
   section-scoped selector that outranks a single class, and its colour
   on a red fill is somewhere between low-contrast and invisible.
   !important is the only thing that reliably beats a selector whose
   specificity the theme controls, not this file.

   :link and :visited are absent here because they can never match a
   <button>. This tile control opens a panel; it does not navigate, so
   nothing about it is ever "visited". They ARE listed on the panel's
   CTA further down, which is a real anchor — see the note there.

   Scoped to the button rather than to every element, so a link an
   editor adds inside a panel's rich text is not forced white onto
   cream. The label span is listed because colour inherits: a theme rule
   on the span would repaint the text even with the button corrected. */
.kio-video-carousel__tile-btn,
.kio-video-carousel__tile-btn:hover,
.kio-video-carousel__tile-btn:focus,
.kio-video-carousel__tile-btn:active,
.kio-video-carousel__tile-btn .kio-video-carousel__tile-btn-label {
  color: var(--kio-white) !important;
}

/* ------------------------------------------------------------
   Product panel

   One <dialog> per category tile, opened with showModal(). Native
   dialog buys the focus trap, Esc, ::backdrop and top-layer stacking
   for free — and top layer is the reason this works at all from inside
   a grid cell: the panel paints above the whole page regardless of the
   tile's stacking context, its transform on :active, or anything the
   Elevate theme wraps the module in.

   Cream panel with purple text, inverting the mockup's white-on-red.
   White body copy on K Red measures 3.96:1, which is below the 4.5:1
   WCAG AA minimum for body text — it passes only as large text, and the
   paragraph in this panel is not large text. Cream with K Dark Purple
   measures 11.69:1. The red is still here, on the CTA, where it
   carries white at button size.
   ------------------------------------------------------------ */

.kio-video-carousel__panel {
  /* Native centring: auto margins inside the dialog's own fixed
     containing block. Deliberately not position: fixed with a
     transform — that fights the browser's centring on iPadOS. */
  margin: auto;
  width: calc(100% - 40px);
  max-width: 680px;
  /* The panel is the scroll container, not the page: the page behind is
     locked while this is open. A panel with three stats, a paragraph and
     a button fits an iPad in portrait, so this is the safety net for a
     long editor paragraph or a landscape phone, not the normal case. */
  max-height: calc(100% - 40px);
  overflow-y: auto;
  /* Stops a scroll gesture that reaches the panel's end from chaining
     into the page underneath. */
  overscroll-behavior: contain;
  -webkit-overflow-scrolling: touch;
  padding: 0;
  border: 0;
  border-radius: var(--kio-radius-lg);
  background: var(--kio-tile-bg);
  color: var(--kio-purple);
  font-family: 'Poppins', sans-serif;
  text-align: center;
}

/* Same value as the idle-reset dialog's backdrop, so the two never read
   as two different kinds of overlay. K Dark Purple at high opacity: dark
   enough that the page reads as stopped, light enough that the content
   behind stays faintly visible so it does not look like a crash.

   Deliberately NOT backdrop-filter: blur(). The page behind holds a
   playing video, and a blurred backdrop has to re-composite that
   surface every frame — expensive on an iPad. The panel pauses the
   video instead (see the kio:media-pause contract in module.html),
   which removes the movement, the audio and the GPU cost at once. */
.kio-video-carousel__panel::backdrop {
  background: rgba(60, 38, 70, 0.92);
}

.kio-video-carousel__panel[open] {
  animation: kio-video-carousel-panel-in 260ms var(--kio-ease) both;
}

@keyframes kio-video-carousel-panel-in {
  from { opacity: 0; transform: scale(0.96); }
  to   { opacity: 1; transform: scale(1); }
}

/* The card takes focus itself when it opens, so the ring never lands on
   the close button (see the tabindex note in module.html). It carries
   tabindex="-1" and is therefore not a Tab stop, so suppressing its own
   ring costs no keyboard user anything — everything inside it keeps its
   :focus-visible outline. */
.kio-video-carousel__panel:focus {
  outline: none;
}

/* The padding lives here rather than on the dialog so it scrolls with
   the content: padding on a scroll container is not repeated at the
   bottom of the scrolled area in every engine. */
.kio-video-carousel__panel-inner {
  /* Containing block for the close button, which is positioned out of the
     flow against this element's corner. */
  position: relative;
  padding: 40px;
}

/* The close button used to sit in a flex row of its own, which cost
   40px of padding + 44px of button + 16px of margin before the title
   could start — a 600px-wide band holding one small glyph. Out of the
   flow it costs nothing: the title starts at the panel's own top
   padding and the glyph floats in the corner, which is the popup
   convention anyway.

   Edge case this leaves behind: with Panel Title empty, the stats
   become the top element and a purple glyph would land on a purple
   stat tile. Guarded below. */
.kio-video-carousel__panel-inner:not(:has(.kio-video-carousel__panel-title)) {
  padding-top: 72px;
}

/* Every block in the panel carries a bottom margin so it can sit above
   the next one, and whichever block ends up last then adds that margin
   to the panel's own bottom padding. Every field here is optional, so
   which block is last depends on what the editor filled in. */
.kio-video-carousel__panel-inner > *:last-child {
  margin-bottom: 0;
}

/* A quiet icon button rather than a brand pill. This used to be a red
   "Back to overview" pill, which drew the eye before the stats did —
   exactly backwards for a panel whose whole job is those three numbers.
   A close glyph in the corner is the convention people already know, so
   it needs no emphasis to be found.

   44px square with an 18px glyph: the icon is small, the tap target is
   not. Under 44px is below the iOS minimum and starts producing missed
   taps on a kiosk where people jab rather than aim. */
.kio-video-carousel__panel-close {
  /* Out of the flow, 20px into the panel's 40px padding — half the
     padding, so it reads as sitting in the corner's margin rather than
     as the first item of content. z-index because it overlaps the
     title's line box: the glyph has to stay above it. */
  position: absolute;
  top: 20px;
  right: 20px;
  z-index: 1;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  appearance: none;
  -webkit-appearance: none;
  width: 44px;
  height: 44px;
  margin: 0;
  padding: 0;
  border: 0;
  border-radius: 50%;
  /* K Dark Purple at low opacity rather than a flat tint, so the circle
     sits on the cream without introducing a fourth colour. */
  background: rgba(60, 38, 70, 0.06);
  /* !important on every colour this button takes, in the rule that sets
     it rather than in a separate override block at the bottom of the
     file. The Elevate theme styles buttons with a section-scoped
     selector that outranks a single class, and this control has three
     states — splitting them from the override would mean an !important
     base colour silently beating a non-important hover. CLAUDE.md #15. */
  color: var(--kio-purple) !important;
  cursor: pointer;
  transition: background-color var(--kio-transition-fast),
              color var(--kio-transition-fast);
}

/* K Red Hover per /memory/brand.md, same value as the tile buttons, so
   "the thing under the pointer turns brand red" is one behaviour across
   the module. Gated for the reason spelled out on the tile button:
   ungated, touch iPadOS leaves the hover applied after the tap. */
@media (hover: hover) {
  .kio-video-carousel__panel-close:hover {
    background: var(--kio-red-hover);
    color: var(--kio-white) !important;
  }
}

/* The state that actually fires on the iPad. Deeper than the hover, so a
   press still registers on a device that hovers first. */
.kio-video-carousel__panel-close:active {
  background: var(--kio-red-active);
  color: var(--kio-white) !important;
}

.kio-video-carousel__panel-close:focus-visible {
  outline: 3px solid var(--kio-purple);
  outline-offset: 2px;
}

.kio-video-carousel__panel-close-icon {
  display: inline-flex;
  pointer-events: none;
}

.kio-video-carousel__panel-close-icon svg {
  display: block;
  width: 18px;
  height: 18px;
}

/* Visually hidden, not display: none — it is the button's accessible
   name, so it has to stay in the accessibility tree. Same technique as
   __tile-btn-context. */
.kio-video-carousel__panel-close-label {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  border: 0;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
}

/* Heading 4 rather than Heading 3: 42px in a 680px panel wraps a
   product name onto two lines and pushes the stats below the fold on an
   iPad in landscape. */
.kio-video-carousel__panel-title {
  margin: 0 0 32px;
  /* Keeps a long product name clear of the floating close glyph. Applied
     to both sides, not just the right: the title is centred, and padding
     on one side only would shift it off the panel's centreline. 48px
     leaves 24px of clear space to the button's left edge. */
  padding: 0 48px;
  color: var(--kio-purple);
  font-family: 'Poppins', sans-serif;
  font-size: 2.375rem; /* Heading 4 — 38px */
  font-weight: 300;
  line-height: 50px;
  letter-spacing: 0;
  text-wrap: balance;
}

/* ---- Stats ----

   Three columns, each guarded on its own value in module.html, so a
   category with one strong number gets one tile at a third of the width
   rather than a padded-out row of three. minmax(0, 1fr) rather than 1fr:
   a long stat value would otherwise set the column's minimum to its own
   content width and break the equal thirds. */
.kio-video-carousel__stats {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 16px;
  /* 16px, not the panel's usual 32px step. The paragraph between the
     stat tiles and the CTA is two short lines, so two 32px steps put
     64px of whitespace around 54px of text and the button drifted away
     from the numbers it belongs to. 16px above and 16px below the
     paragraph totals the 32px of whitespace the design wants between
     the tiles and the button. Its partner is on __panel-body. */
  margin: 0 0 16px;
  padding: 0;
  list-style: none;
}

/* With no paragraph between them the two blocks meet directly, and
   16px would crowd them — the full 32px step comes back. Plain
   adjacency rather than :has(), so this needs no support floor.

   32px, not the 16px difference: adjacent vertical margins collapse to
   the LARGER of the two rather than adding up, so this has to state the
   final figure. Written as 16 it collapsed against the stats' 16 and
   produced 16. */
.kio-video-carousel__stats + .kio-video-carousel__panel-cta {
  margin-top: 32px;
}

.kio-video-carousel__stat {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin: 0;
  padding: 24px 16px;
  border-radius: var(--kio-radius-md);
  background: var(--kio-purple);
  color: var(--kio-white);
  list-style: none;
}

/* The brand's Stats style is weight 200 at 144px, which is a full-bleed
   hero number. This is the same voice at tile scale. Tabular figures so
   three values of different digit counts optically line up across the
   row. White on K Dark Purple measures 13.48:1. */
.kio-video-carousel__stat-value {
  margin: 0;
  color: var(--kio-white);
  font-size: 2.625rem; /* 42px */
  font-weight: 200;
  line-height: 48px;
  letter-spacing: -0.02em;
  font-variant-numeric: tabular-nums;
  font-feature-settings: 'tnum' 1;
  /* "5 Minutes" and "-60%" both have to fit a third of the panel. */
  text-wrap: balance;
}

/* margin-top: auto bottom-aligns all three labels. Without it a value
   that wraps to two lines ("5 Minutes" does, at a third of the panel)
   pushes its own label down while the other two stay put, and the row
   reads as three misaligned tiles. Bottom-aligned, the numbers line up
   at the top and the labels line up at the bottom whatever wraps. */
.kio-video-carousel__stat-label {
  margin: auto 0 0;
  color: var(--kio-white);
  font-size: 0.875rem; /* Body 5 — 14px */
  font-weight: 400;
  line-height: 20px;
  text-wrap: pretty;
}

/* ---- Body copy ----

   A rich text field, so the child rules below are what the editor's
   output actually lands in. Kept deliberately short in the design: this
   is read standing up, at arm's length, next to a simulation bed. */
.kio-video-carousel__panel-body {
  /* Pairs with the 16px on __stats — see the note there. */
  margin: 0 0 16px;
  color: var(--kio-purple);
  font-size: 1.25rem; /* Body 3 — 20px */
  font-weight: 300;
  line-height: 27px;
  text-wrap: pretty;
}

.kio-video-carousel__panel-body > *:first-child {
  margin-top: 0;
}

.kio-video-carousel__panel-body > *:last-child {
  margin-bottom: 0;
}

.kio-video-carousel__panel-body p {
  margin: 0 0 16px;
}

.kio-video-carousel__panel-body strong,
.kio-video-carousel__panel-body b {
  font-weight: 500;
}

/* Left-aligned against the centred paragraph, because a centred list
   with bullets hanging off the left of each line is unreadable. */
.kio-video-carousel__panel-body ul,
.kio-video-carousel__panel-body ol {
  margin: 0 0 16px;
  padding-left: 20px;
  text-align: left;
}

.kio-video-carousel__panel-body li {
  margin-bottom: 8px;
}

/* An inline link inside the editor's paragraph. Brand link red on cream;
   no hover state, for the same touch-device reason as the pills above.
   The CTA below is the panel's one designed route off this page, so an
   inline link here is still worth spotting rather than styling. */
.kio-video-carousel__panel-body a {
  color: var(--kio-red);
  font-weight: 400;
  text-decoration: none;
}

/* ---- CTA — brand primary button ---- */

/* Brand primary, matching kiosk-reset's button and /memory/brand.md:
   Poppins 400, 15px/23, 12px 44px, 50px radius.

   display: flex with width: fit-content, not inline-flex, even though
   the panel is text-align: center. An inline-flex pill sits on the
   parent's baseline, which leaves descender space underneath it and
   would read as extra bottom padding on the panel. As a block it
   centres with auto margins and its vertical margins behave like every
   other block in this panel, which is what the adjacency rule above
   depends on. */
.kio-video-carousel__panel-cta {
  display: flex;
  align-items: center;
  justify-content: center;
  width: fit-content;
  max-width: 100%;
  margin: 0 auto;
  padding: 12px 44px;
  border: 3px solid var(--kio-red);
  border-radius: 50px;
  background: var(--kio-red);
  color: var(--kio-white);
  font-family: 'Poppins', sans-serif;
  font-size: 0.9375rem; /* 15px */
  font-weight: 400;
  line-height: 23px;
  text-align: center;
  text-decoration: none;
  cursor: pointer;
  transition: background-color var(--kio-transition-fast),
              border-color var(--kio-transition-fast);
}

/* Gated on a real hover-capable pointer. Touch iPadOS applies :hover on
   tap and leaves it applied, so an ungated rule would strand the button
   in its hover colour after the panel has been opened once. */
@media (hover: hover) {
  .kio-video-carousel__panel-cta:hover {
    border-color: var(--kio-red-hover);
    background: var(--kio-red-hover);
  }
}

.kio-video-carousel__panel-cta:active {
  border-color: var(--kio-red-active);
  background: var(--kio-red-active);
}

/* Purple, not white: the ring paints on the cream panel around the pill,
   not on the red fill. Matches the close button's ring. */
.kio-video-carousel__panel-cta:focus-visible {
  outline: 3px solid var(--kio-purple);
  outline-offset: 3px;
}

/* Elevate theme override — CTA text colour.

   :link and :visited are back, and :visited is the important one: it
   applies the moment a visitor has opened the product page, and the
   theme styles visited links separately. Without it the second person
   to tap this button on the same iPad sees the theme's visited colour
   on red.

   Safe to declare the base !important here, unlike the close button's,
   because every state of this button carries white — only the fill and
   border change on hover and press, and neither is set below. */
.kio-video-carousel__panel-cta,
.kio-video-carousel__panel-cta:link,
.kio-video-carousel__panel-cta:visited,
.kio-video-carousel__panel-cta:hover,
.kio-video-carousel__panel-cta:focus,
.kio-video-carousel__panel-cta:active,
.kio-video-carousel__panel-cta .kio-video-carousel__panel-cta-label {
  color: var(--kio-white) !important;
}

/* The close button's theme override is not here — it lives inline on
   each of its three state rules above, because the button changes colour
   on hover and press and an !important base here would beat them. */

/* ------------------------------------------------------------
   Tablet
   ------------------------------------------------------------ */

@media (max-width: 1023px) {
  .kio-video-carousel__band {
    padding: 56px 0 72px;
  }

  .kio-video-carousel__meta {
    font-size: 1.375rem; /* 22px */
    line-height: 32px;
  }

  .kio-video-carousel__arrow {
    width: 44px;
    height: 44px;
  }

  /* The iPad target: four categories as two rows of two. Title size is
     left at the design's 30px here — this breakpoint IS the delivery
     device, so it is the one place not to shrink it. */
  .kio-video-carousel__categories {
    margin-top: 56px;
  }

  .kio-video-carousel__tiles {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  /* Padding only. The panel's type is deliberately NOT reduced at this
     breakpoint for the same reason as the tile titles: this is the
     delivery device, and the panel has to read at arm's length. */
  .kio-video-carousel__panel-inner {
    padding: 32px;
  }
}

/* ------------------------------------------------------------
   Mobile
   ------------------------------------------------------------ */

@media (max-width: 639px) {
  .kio-video-carousel {
    --kio-gutter: 16px;
    --kio-radius-lg: 16px;
    /* Tracks the tile's radius down so the image stays one step inside
       it. 8px, not 12px — 12px is off the /memory/brand.md radius scale. */
    --kio-radius-md: 8px;
  }

  .kio-video-carousel__band {
    padding: 40px 0 56px;
  }

  .kio-video-carousel__meta {
    margin-bottom: 16px;
    font-size: 1.125rem; /* 18px */
    line-height: 26px;
  }

  .kio-video-carousel__nav {
    margin-top: 24px;
  }

  .kio-video-carousel__arrows {
    gap: 12px;
  }

  .kio-video-carousel__arrow {
    width: 40px;
    height: 40px;
  }

  .kio-video-carousel__chevron {
    width: 18px;
    height: 18px;
  }

  /* One column. Two 4:3 tiles side by side at 375px leave ~160px each,
     which is narrower than the button, so the pill would wrap. */
  .kio-video-carousel__categories {
    margin-top: 48px;
  }

  .kio-video-carousel__tiles {
    grid-template-columns: minmax(0, 1fr);
    /* Equal gaps now — with a single column there are no rows to
       distinguish from columns, so the taller row gap has no job. */
    gap: 24px;
  }

  .kio-video-carousel__tile-title {
    margin: 16px 0 16px;
    font-size: 1.625rem; /* 26px */
    line-height: 34px;
  }

  .kio-video-carousel__tile-btn {
    padding: 12px 36px;
  }

  /* ---- Panel ----
     Away from the delivery device now, so the panel scales down like
     any other section. Three stat tiles at 375px leave ~100px each,
     which is narrower than a two-digit value plus its label, so they
     stack. */
  .kio-video-carousel__panel {
    width: calc(100% - 32px);
    max-height: calc(100% - 32px);
  }

  .kio-video-carousel__panel-inner {
    padding: 24px 20px;
  }

  .kio-video-carousel__panel-inner:not(:has(.kio-video-carousel__panel-title)) {
    padding-top: 60px;
  }

  /* Tighter inset to match the smaller panel padding. The button keeps
     its 44px size — the tap target does not shrink with the viewport. */
  .kio-video-carousel__panel-close {
    top: 12px;
    right: 12px;
  }

  .kio-video-carousel__panel-title {
    margin-bottom: 24px;
    /* 44px clears the button's left edge (12 + 44 = 56px from the panel
       edge, against 20px of inner padding + 44px here = 64px). */
    padding: 0 44px;
    font-size: 1.625rem; /* 26px */
    line-height: 34px;
  }

  /* Same halving as the desktop step: 12 + 12 in place of 24 + 24. */
  .kio-video-carousel__stats {
    grid-template-columns: minmax(0, 1fr);
    gap: 12px;
    margin-bottom: 12px;
  }

  /* The collapsed total, as above — not the difference. */
  .kio-video-carousel__stats + .kio-video-carousel__panel-cta {
    margin-top: 24px;
  }

  .kio-video-carousel__stat {
    padding: 20px 16px;
  }

  .kio-video-carousel__stat-value {
    font-size: 2.125rem; /* 34px */
    line-height: 42px;
  }

  .kio-video-carousel__panel-body {
    margin-bottom: 12px;
    font-size: 1rem; /* Body 4 — 16px */
    line-height: 25px;
  }

}

/* ------------------------------------------------------------
   Small mobile
   ------------------------------------------------------------ */

@media (max-width: 359px) {
  .kio-video-carousel__meta {
    font-size: 1rem; /* 16px */
    line-height: 24px;
  }

  /* The nav row used to re-stack here — arrows on one line, CTA under
     them — because the two together outgrew 360px. Two prev/next
     buttons on their own fit at any width this page will ever see, so
     there is nothing left to stack. */

  .kio-video-carousel__tile-title {
    font-size: 1.5rem; /* 24px */
    line-height: 32px;
  }

  /* 44px padding plus a two-word label overflows a 288px tile. */
  .kio-video-carousel__tile-btn {
    padding: 12px 28px;
  }

  /* Same reason, and the panel's CTA carries a three-word label: at
     320px the panel is 288px wide and drops 20px of inner padding
     either side, leaving 248px for the pill. */
  .kio-video-carousel__panel-cta {
    padding: 12px 28px;
  }

  /* The close control needs nothing here any more. It was a pill whose
     label plus 28px of padding either side overflowed a 320px panel;
     a 44px square fits at every width. */
}

/* ------------------------------------------------------------
   Reduced motion
   Slides still change — they jump instead of sliding. Swiping and
   the arrows keep working; only the travel is removed.
   ------------------------------------------------------------ */

@media (prefers-reduced-motion: reduce) {
  .kio-video-carousel *,
  .kio-video-carousel *::before,
  .kio-video-carousel *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
}
