/* ════════════════════════════════════════════════════════════════════════
   css/firm-pages-motion.css — DISCLOSURE AND FILTER FOR THE FIRM PAGES
   service.tnaado.ca · Tier 2 · linked ONLY from /firm, /office, /team

   WHY A SECOND SHEET. css/firm-pages.css is shared with /sister, which is
   another task's page, so it is read and never written. Everything here is
   ADDITIVE: new class names only, no selector in this file also exists in
   that one, and nothing here overrides chrome.css.

   ── WHAT MOTION IS ALLOWED HERE, AND WHY IT IS NOT A CONTRADICTION ──────
   firm-pages.css records "Ethan: no scroll animations, not what I asked
   for." That still stands and nothing in this file is driven by scroll
   position: there is no observer, no scrub, no pin, no parallax, no
   animation-timeline. Every transition in this file is started by the
   READER — a click, a keypress, a hover, a focus. That is the thing he did
   ask for by name: "dynamic buttons/click to read more/dropdowns".

   ── THE DISCLOSURE, AND WHY IT IS NOT hidden="until-found" ──────────────
   Each disclosure is a real <details>/<summary>. It opens with no
   JavaScript, it is keyboard operable for free, and Chromium's
   auto-expanding find-in-page already opens a closed <details> when the
   match is inside it — verified in a browser on these three pages, not
   assumed.

   hidden="until-found" is deliberately NOT nested inside these. That
   attribute hides its element with content-visibility:hidden, which does
   not care whether an ancestor <details> is open — so a summary click
   would flip [open] and reveal nothing until a beforematch fired. The two
   mechanisms solve the same problem and stack into a bug. <details> alone
   is the one that works.

   Content is never removed from the DOM. A closed row's text is served,
   parsed, indexable and reachable by find-in-page.

   ── THE ROSTER FILTER ───────────────────────────────────────────────────
   Radio inputs and :has(). No script, so it works with JS blocked, and
   the radios are a real focusable group with arrow-key semantics the
   platform provides. "Everyone" is checked in the served HTML, so a cold
   page load — and every crawler — sees all twelve people at once. The
   filter can only ever narrow something the reader has already been
   shown.
   ════════════════════════════════════════════════════════════════════════ */

/* ══ DISCLOSURE ROWS ══════════════════════════════════════════════════════
   Same rhythm as .fp-dl so a page can carry both without reading as two
   systems: hairline-separated rows, mono term, body copy under it. The
   difference is that the body copy is behind a control. */
.fpd { margin: clamp(1.5rem, 3.5vw, 2.25rem) 0 0; }
.fpd--tight { margin-top: 1.1rem; }

/* The mono label that heads a disclosure group. Not .fp-panel__k: that one
   is written for the inside of a bordered panel and carries no top margin,
   so it collided with the lede above it. */
.fpd__k {
  margin: clamp(1.5rem, 3.5vw, 2.25rem) 0 .25rem;
  font-family: var(--mono); font-size: .6875rem; font-weight: 500;
  letter-spacing: .16em; text-transform: uppercase; color: var(--dim-ink);
}
.fp-band--bone .fpd__k, .fp-band--ivory .fpd__k { color: var(--dim-bone); }

.fpd__d {
  border-top: 1px solid var(--hair-ink);
  interpolate-size: allow-keywords;
}
.fpd__d:first-child { border-top: 0; }
.fpd > .fpd__d:last-child { border-bottom: 1px solid var(--hair-ink); }
.fp-band--bone .fpd__d,
.fp-band--ivory .fpd__d,
.fp-band--bone .fpd > .fpd__d:last-child,
.fp-band--ivory .fpd > .fpd__d:last-child { border-color: var(--hair-bone); }

/* THE SUMMARY. A grid, not a flex row, so the sign column is the same
   width on every row and the labels line up down the list. */
.fpd__s {
  display: grid;
  grid-template-columns: 1fr 1.5rem;
  align-items: baseline;
  gap: 0 1rem;
  padding: .95rem 0;
  cursor: pointer;
  list-style: none;
  transition: color .18s var(--ease);
}
.fpd__s::-webkit-details-marker { display: none; }
.fpd__s::marker { content: ''; }

.fpd__t {
  font-family: var(--mono);
  font-size: .75rem;
  font-weight: 500;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--red-ink);
}
.fp-band--bone .fpd__t, .fp-band--ivory .fpd__t { color: var(--red-bone); }

/* The one-line answer, always visible. The disclosure holds the detail,
   never the point — a row that says nothing until it is opened is a
   riddle, and riddles are the thing being removed from this site. */
.fpd__lead {
  display: block;
  margin: .3rem 0 0;
  font-size: 1rem;
  line-height: 1.55;
  color: var(--bone);
}
.fp-band--bone .fpd__lead, .fp-band--ivory .fpd__lead { color: var(--ink); }

/* THE SIGN. A plus that becomes a minus, drawn in two 1px rules so it is
   crisp at every zoom level and needs no glyph. */
.fpd__sign {
  position: relative;
  justify-self: end;
  width: 1.0625rem;
  height: 1.0625rem;
  margin-top: .1rem;
  border: 1px solid var(--hair-ink);
  border-radius: 2px;
  transition: border-color .18s var(--ease), background-color .18s var(--ease);
}
.fp-band--bone .fpd__sign, .fp-band--ivory .fpd__sign { border-color: var(--hair-bone); }
.fpd__sign::before,
.fpd__sign::after {
  content: '';
  position: absolute;
  inset: 50% 22%;
  background: currentColor;
  transform: translateY(-50%);
  height: 1px;
  transition: transform .3s var(--ease), opacity .2s var(--ease);
}
.fpd__sign::after { transform: translateY(-50%) rotate(90deg); }
.fpd__d[open] .fpd__sign::after { transform: translateY(-50%) rotate(0deg); opacity: 0; }

@media (hover: hover) {
  .fpd__s:hover .fpd__sign { border-color: var(--red-ink); background: rgba(200, 16, 46, .1); }
  .fp-band--bone .fpd__s:hover .fpd__sign,
  .fp-band--ivory .fpd__s:hover .fpd__sign { border-color: var(--red-bone); }
  .fpd__s:hover .fpd__t { color: var(--red); }
  .fp-band--bone .fpd__s:hover .fpd__t, .fp-band--ivory .fpd__s:hover .fpd__t { color: var(--red-bone); }
}

/* THE BODY. block-size 0 → auto needs interpolate-size, declared on the
   <details> above. content-visibility is transitioned with
   allow-discrete so the text is not paintable while the row is shut but
   is still in the DOM and still found by find-in-page. */
.fpd__b {
  margin: 0 0 1.05rem;
  padding-right: 2.5rem;
  max-width: 62ch;
  font-size: .9375rem;
  line-height: 1.7;
  color: var(--dim-ink);
}
.fp-band--bone .fpd__b, .fp-band--ivory .fpd__b { color: var(--dim-bone); }
.fpd__b:last-child { margin-bottom: 1.05rem; }
.fpd__b a { color: var(--red-ink); text-decoration: underline; text-underline-offset: .18em; }
.fp-band--bone .fpd__b a, .fp-band--ivory .fpd__b a { color: var(--red-bone); }

@supports (interpolate-size: allow-keywords) {
  .fpd__d::details-content {
    block-size: 0;
    overflow: clip;
    opacity: 0;
    content-visibility: hidden;
    transition:
      block-size .34s var(--ease),
      opacity .26s var(--ease),
      content-visibility .34s allow-discrete;
  }
  .fpd__d[open]::details-content {
    block-size: auto;
    opacity: 1;
    content-visibility: visible;
  }
}

/* Two columns of disclosures where the list is long enough to earn it.
   Rows keep their own hairlines, so the columns read as one list broken
   over two, not as two lists. */
@media (min-width: 880px) {
  .fpd--2 { display: grid; grid-template-columns: 1fr 1fr; column-gap: clamp(2rem, 4vw, 3rem); }
  .fpd--2 > .fpd__d:nth-child(2) { border-top: 0; }
  .fp-band--bone .fpd--2 > .fpd__d:nth-child(2),
  .fp-band--ivory .fpd--2 > .fpd__d:nth-child(2) { border-top: 0; }
}


/* ══ THE ROSTER FILTER ════════════════════════════════════════════════════ */
.fpx { margin: clamp(1.4rem, 3vw, 2rem) 0 clamp(1.75rem, 4vw, 2.5rem); }

.fpx__legend {
  padding: 0;
  margin: 0 0 .7rem;
  font-family: var(--mono);
  font-size: .6875rem;
  font-weight: 500;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--dim-bone);
}
.fpx__set { border: 0; padding: 0; margin: 0; }
.fpx__row { display: flex; flex-wrap: wrap; gap: .5rem; }

/* The input stays in the accessibility tree and keeps its own focus ring;
   only its native box is taken off screen. */
.fpx__in {
  position: absolute;
  width: 1px; height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}
.fpx__lab {
  display: inline-flex;
  align-items: center;
  gap: .5rem;
  padding: .5rem .95rem;
  border: 1px solid var(--hair-bone);
  border-radius: 2px;
  font-family: var(--mono);
  font-size: .75rem;
  letter-spacing: .07em;
  text-transform: uppercase;
  color: var(--dim-bone);
  cursor: pointer;
  transition:
    background-color .2s var(--ease),
    border-color .2s var(--ease),
    color .2s var(--ease),
    transform .2s var(--ease);
}
@media (hover: hover) {
  .fpx__lab:hover { border-color: var(--red-bone); color: var(--ink); transform: translateY(-1px); }
}
.fpx__in:focus-visible + .fpx__lab { outline: 2px solid var(--red-bone); outline-offset: 2px; }
.fpx__in:checked + .fpx__lab {
  background: var(--ink);
  border-color: var(--ink);
  color: var(--bone);
}

/* WHICH GROUPS SHOW. The filter narrows; it never reorders, and every
   group is in the served markup in reading order whatever is checked. */
.fpx-scope:has(#fx-tech:checked)  .fpx-g:not([data-fx="tech"]),
.fpx-scope:has(#fx-media:checked) .fpx-g:not([data-fx="media"]),
.fpx-scope:has(#fx-firm:checked)  .fpx-g:not([data-fx="firm"]) {
  display: none;
}

/* A group arriving because the reader pressed a button. Keyed off the
   :checked state, so it runs on the press and never on scroll. */
.fpx-scope:has(#fx-tech:checked)  .fpx-g[data-fx="tech"],
.fpx-scope:has(#fx-media:checked) .fpx-g[data-fx="media"],
.fpx-scope:has(#fx-firm:checked)  .fpx-g[data-fx="firm"] {
  animation: fpx-in .34s var(--ease) both;
  border-top: 0;
  margin-top: 0;
  padding-top: 0;
}
@keyframes fpx-in {
  from { opacity: 0; transform: translateY(.55rem); }
  to   { opacity: 1; transform: none; }
}

/* Portrait rows get a small lift on hover, staggered by their position in
   the list, so the roster answers the pointer instead of sitting there.
   --i is set inline on each <li>. */
.fp-people > li { transition: transform .22s var(--ease); }
@media (hover: hover) {
  .fp-people:hover > li { transition-delay: calc(var(--i, 0) * 18ms); }
  .fp-people > li:hover { transform: translateY(-3px); }
}


/* ══ A CARD WITH NOTHING TO CLICK ═════════════════════════════════════════
   css/components/carousel.css puts the card's column layout, background and
   border on .pcard__link, because every card in that component was written to
   be a link. The systems strip on /firm and the software strip on /office are
   screenshots of our own internal screens — there is no public page to send a
   reader to, and a card that looks clickable and is not is worse than a card
   that plainly is not. So .pcard--flat inherits the same box without the
   anchor. No hover lift and no focus ring, because there is no target.

   Nothing here overrides a selector in carousel.css: .pcard--flat does not
   exist in that file. */
.pcard--flat {
  flex-direction: column;
  background: var(--plate);
  border: 1px solid var(--hair-ink);
}
.fp-band--bone .pcard--flat,
.fp-band--ivory .pcard--flat {
  background: var(--bone);
  border-color: var(--hair-bone);
}
/* The strip label sits directly above the viewport, so it loses .fpd__k's
   generous top margin and keeps its bottom one. */
.fpd__k--strip { margin-top: clamp(1.75rem, 4vw, 2.5rem); margin-bottom: .6rem; }


/* ══ REDUCED MOTION ═══════════════════════════════════════════════════════
   Read as a media query rather than trusted to a global reset, because
   this sheet introduces the only animation on these three pages. State
   still changes; it just changes instantly. */
@media (prefers-reduced-motion: reduce) {
  .fpd__s,
  .fpd__sign,
  .fpd__sign::before,
  .fpd__sign::after,
  .fpx__lab,
  .fp-people > li { transition: none; }

  @supports (interpolate-size: allow-keywords) {
    .fpd__d::details-content,
    .fpd__d[open]::details-content { transition: none; }
  }

  .fpx-scope:has(#fx-tech:checked)  .fpx-g[data-fx="tech"],
  .fpx-scope:has(#fx-media:checked) .fpx-g[data-fx="media"],
  .fpx-scope:has(#fx-firm:checked)  .fpx-g[data-fx="firm"] { animation: none; }
}


/* ══ A BIO IS NOT A QUOTE ═════════════════════════════════════════════════
   /team's Grace block used <blockquote>, which css/firm-pages.css sets in
   SERIF ITALIC because that element carries Ethan's one verbatim founder
   quote. The Grace text is third-person prose ABOUT her, not words she said,
   so quote styling was asserting a quotation that does not exist -- and that
   sheet's own header says the single blockquote on these pages is his and is
   never to be joined by a paraphrase.

   Same optical weight so the layout does not move, roman rather than italic
   so the two blocks are not claiming the same thing. Declared here because
   css/firm-pages.css is shared with /sister and is read-only to this task. */
.fp-founder__bio {
  margin: 0; max-width: 46ch;
  font-family: var(--serif); font-variation-settings: 'opsz' 72;
  font-size: clamp(1.2rem, 2.4vw, 1.6rem); line-height: 1.28;
}


/* ══ PRESS FEEDBACK ═══════════════════════════════════════════════════════
   Ethan asked for "dynamic buttons" by name. Every control on these three
   pages answered the POINTER — hover lift, hover colour — and none of them
   answered the PRESS, so a tap on a touch screen (where there is no hover at
   all) got nothing back until the row had already finished opening.

   Purpose is feedback, not decoration: the only job is to confirm the
   interface heard the press. 120ms, inside the 100-160ms press budget, on
   transform alone so it composites off the main thread and cannot touch the
   storm's follower. Nothing here is driven by scroll position.

   SCOPE IS DELIBERATELY NARROW. .fpd__sign and .fpx__lab are declared in this
   file and appear on /firm, /office and /team only, so pressing them here
   diverges no other page. The controls that DO want the same treatment and
   are not touched:

     .carou__btn  — css/components/carousel.css, the ported component. A press
                    state added from here would press on /firm and /team and
                    sit dead on every other page carrying the same carousel.
     .fp-go, .fp-onward a — css/firm-pages.css, shared with /sister.

   Both belong in their own files. Reported rather than reached into.
   ════════════════════════════════════════════════════════════════════════ */

/* The plus/minus is the thing that looks pressable, so it is the thing that
   presses. Scaling the whole summary row would move the text under the
   reader's finger, which reads as a layout shift rather than as feedback. */
.fpd__sign { transform: scale(1); transition: border-color .18s var(--ease), background-color .18s var(--ease), transform .12s var(--ease); }
.fpd__s:active .fpd__sign { transform: scale(.88); }

/* The chip already lifts 1px on hover. :active is authored after that rule so
   it wins on a pointer that is both hovering and pressing, and it returns the
   lift to 0 in the same declaration — two rules fighting over one transform
   is how a press ends up looking like a stutter. */
.fpx__lab:active { transform: translateY(0) scale(.965); }

/* A published figure inside a disclosure body stays at full strength. .fpd__b
   is set in the dimmed body colour, which is right for explanation and wrong
   for a price the reader is being asked to act on. Same colour pairing the
   rest of this file uses, so it is correct on an ink band and on a bone one. */
.fpd__b b { color: var(--bone); font-weight: 600; }
.fp-band--bone .fpd__b b, .fp-band--ivory .fpd__b b { color: var(--ink); }

@media (prefers-reduced-motion: reduce) {
  /* Not "gentler" — removed. A press scale with the transition stripped is a
     jump, which is the specific thing this preference is asking us not to do.
     The border and background feedback on the same control still fires. */
  .fpd__sign { transition: border-color .18s var(--ease), background-color .18s var(--ease); }
  .fpd__s:active .fpd__sign,
  .fpx__lab:active { transform: none; }
}
