/* =============================================================================
   accordion-01 : styles

   PREFIX: every class and custom property this component owns is prefixed
   `ac01`. Nothing here is a bare element selector at top level and nothing
   reaches for a class the host project happens to have, so this can sit on a
   page beside components lifted from other projects without either one
   touching the other. See references/component-authoring.md.

   The one unprefixed class in the whole component is `is-revealed`, applied by
   behaviour.js. That is deliberate: it belongs to the HOST's scroll-reveal
   system, not to this component. See the `scroll-reveal-component-ownership`
   capability.

   TOKENS READ (all with fallbacks, so a project defining none still renders):
     --accent  --rule  --ink  --ink-70  --ease

   Four layers:
     1. row rhythm and the trigger      -- the ruled index look
     2. the plus-to-minus icon
     3. the panel                       -- height morph + the visibility rule
     4. desktop two-column, reduced motion, no-JS
   ============================================================================= */

/* --- 1. row rhythm and the trigger ---------------------------------------- */

/* JS adds .ac01 to the authored container. `display: block` is set WITHOUT
   !important on purpose: unlike stack-list-01, nothing here needs to defeat a
   host grid on the same element, because each row lays itself out. */
.ac01 { display: block; }

.ac01__item {
  display: block;
  padding-block: 0;
  border-top: 1px solid var(--rule, rgba(11, 11, 12, 0.12));
}
.ac01__item:last-child {
  border-bottom: 1px solid var(--rule, rgba(11, 11, 12, 0.12));
}

/* The authored heading stays the heading; it only loses its margins, because
   the generated button now carries all the padding. */
.ac01__heading { margin: 0; font-size: inherit; }

/* A REAL button, restyled to look like a line of prose. Enter, Space, the
   focus ring and the implicit `button` role all come free, which is why this
   component refuses to fake a trigger with a div. */
.ac01__trigger {
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1.5rem;
  padding: clamp(1.25rem, 2.5vw, 1.75rem) 0;
  background: none;
  border: 0;
  color: inherit;
  font: inherit;
  text-align: left;
  cursor: pointer;
}
.ac01__trigger:focus-visible {
  outline: 2px solid var(--accent, #1d4ed8);
  outline-offset: 6px;
  border-radius: 4px;
}
.ac01__label { transition: color 0.3s var(--ease, cubic-bezier(0.16, 1, 0.3, 1)); }
.ac01__trigger:hover .ac01__label,
.ac01__item.is-ac01-open .ac01__label { color: var(--accent, #1d4ed8); }

/* --- 2. the plus-to-minus icon -------------------------------------------- */

/* Two bars in a ring. The vertical one rotates flat, so the plus becomes a
   minus rather than being swapped for a different glyph. It is aria-hidden in
   the markup: the button's aria-expanded already states the state, and a
   screen reader announcing "plus" adds nothing. */
.ac01__icon {
  position: relative;
  flex: 0 0 auto;
  width: 30px;
  height: 30px;
  border: 1px solid var(--rule, rgba(11, 11, 12, 0.12));
  border-radius: 999px;
  transition:
    border-color 0.3s var(--ease, cubic-bezier(0.16, 1, 0.3, 1)),
    background 0.3s var(--ease, cubic-bezier(0.16, 1, 0.3, 1));
}
.ac01__icon i {
  position: absolute;
  left: 50%; top: 50%;
  width: 12px; height: 1.5px;
  background: var(--ink, #0b0b0c);
  transform: translate(-50%, -50%);
  transition:
    transform 0.45s var(--ease, cubic-bezier(0.16, 1, 0.3, 1)),
    background 0.3s var(--ease, cubic-bezier(0.16, 1, 0.3, 1));
}
.ac01__icon i + i { transform: translate(-50%, -50%) rotate(90deg); }
.ac01__trigger:hover .ac01__icon { border-color: var(--accent, #1d4ed8); }
.ac01__item.is-ac01-open .ac01__icon {
  background: var(--accent, #1d4ed8);
  border-color: var(--accent, #1d4ed8);
}
.ac01__item.is-ac01-open .ac01__icon i { background: #fff; }
.ac01__item.is-ac01-open .ac01__icon i + i { transform: translate(-50%, -50%) rotate(180deg); }

/* --- 3. the panel --------------------------------------------------------- */

/*  READ THIS BEFORE TOUCHING THE TRANSITION.

    `height: 0` with `overflow: hidden` clips the copy but does NOT take it out
    of the tab order or the accessibility tree: browsers still focus content
    inside a zero-height clipped box, so a keyboard visitor tabs into answers
    they cannot see. `visibility: hidden` is what actually closes it, and it
    inherits, so the whole subtree goes with it.

    The transition on `visibility` is the trap. `transition: opacity .4s,
    visibility .4s` flips a discrete property at the HALFWAY point, so a panel
    is painted for 200ms while still computing `hidden`, and it swallows every
    click and focus attempt aimed at it. That was a real live bug in the origin
    stylesheet, found and fixed on 2026-09-10.

    The fix is a zero-duration visibility transition with an asymmetric delay:
      at rest   -> `visibility 0s linear .4s`, so the panel stays visible for
                   the whole close animation and only then hides
      when open  -> `visibility 0s linear 0s`, so it is hittable on frame one

    Do not fold these two declarations into one. */
.ac01__panel {
  height: 0;
  overflow: hidden;
  visibility: hidden;
  will-change: height;
  transition: visibility 0s linear 0.4s;
}
.ac01__item.is-ac01-open .ac01__panel {
  height: auto;
  visibility: visible;
  transition: visibility 0s linear 0s;
}

/* The mask is the fold: it slides out from under the trigger inside the
   panel's clip while the panel's height grows. Its own box is what the height
   morph measures, so the bottom padding lives here and not on the panel. */
.ac01__mask {
  padding-bottom: clamp(1.25rem, 2.5vw, 1.75rem);
  will-change: transform;
}
.ac01__copy {
  margin: 0;
  max-width: 62ch;
  color: var(--ink-70, rgba(11, 11, 12, 0.7));
}
.ac01__copy > :first-child { margin-top: 0; }
.ac01__copy > :last-child { margin-bottom: 0; }

/* --- 4. desktop two-column, reduced motion, no-JS ------------------------- */

/* The open copy sits in the row's right column beside its heading rather than
   underneath it, and the plus/minus pins to the row's right edge. The row
   itself becomes the grid, so this needs no wrapper and no host layout class. */
@media (min-width: 800px) {
  .ac01__item {
    position: relative;
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1.85fr);
    column-gap: clamp(2rem, 5vw, 4.5rem);
    align-items: start;
    padding-right: 3.25rem;
  }
  .ac01__heading { grid-column: 1; grid-row: 1; }
  .ac01__panel   { grid-column: 2; grid-row: 1; }
  .ac01__mask    { padding-top: clamp(1.25rem, 2.5vw, 1.75rem); }
  .ac01__icon {
    position: absolute;
    right: 0;
    top: clamp(1.25rem, 2.5vw, 1.75rem);
  }
}

/* Reduced motion: the height snaps, with no blur and no spring. behaviour.js
   already stops driving GSAP, so all that is left is to stop the CSS easing
   anything either. Every state stays reachable and readable; only motion goes.
   The visibility delay drops to zero here too, because there is no longer a
   close animation for it to outlast. */
@media (prefers-reduced-motion: reduce) {
  .ac01__mask,
  .ac01__icon,
  .ac01__icon i,
  .ac01__label { transition-duration: 0s; will-change: auto; }
  .ac01__panel { transition: visibility 0s linear 0s; will-change: auto; }
}

/* No JS at all: there is no trigger and no panel wrapper, so the rows render
   as plain heading-plus-copy blocks on the same ruled rhythm. Nothing is
   hidden, which is the only acceptable no-JS state for content people came to
   read. Selectors target the ATTRIBUTE because no ac01 class exists yet. */
[data-ac01-accordion]:not(.ac01) {
  display: block;
}
[data-ac01-accordion]:not(.ac01) > * {
  display: block;
  padding-block: clamp(1.25rem, 2.5vw, 1.75rem);
  border-top: 1px solid var(--rule, rgba(11, 11, 12, 0.12));
}
[data-ac01-accordion]:not(.ac01) > *:last-child {
  border-bottom: 1px solid var(--rule, rgba(11, 11, 12, 0.12));
}
