/* ══════════════════════════════════════════════════════════════════════════
   AI EVENTS — the SPEAKER surfaces
   /events/speakers          `.v2x.dir.evx.spkx`   (the roster)
   /events/speakers/{slug}   `.v2x.atd.atdx.evd.spkd`  (the profile)

   NEW 2026-08-21. Both pages were rebuilt in the same pass: the roster onto
   /ai-directory's listing system (the stack /events already runs) and the
   profile onto the ATD detail system (the stack /events/{slug} already runs).
   This file is the DELTA — only what a person needs that an event does not.

   ⚠ ADD NOTHING HERE THAT THE SHARED SHEETS ALREADY CARRY. The card, the
     pills, the ribbon, the toolbar, the band heads, the rails, the pagination
     and the busy state all come from v2-public-directory-listing.css and
     public-events.css. A second copy drifts the moment either one moves —
     which is exactly what happened to the `.evp` kit these pages left behind.

   ── RULES THIS FILE FOLLOWS (inherited from events.css, each a shipped bug) ─
   1. Every hover lift/tint lives inside @media (hover:hover). Without it
      :hover LATCHES after a tap and two tapped cards both look selected.
      :active gives touch its own press feedback.
   2. Grids use minmax(min(N,100%),1fr). A bare minmax min is a HARD FLOOR and
      forces sideways scroll on a 360px phone.
   3. Grid/flex children get min-width:0, or long names refuse to shrink and
      clip out of their column.
   4. Colour is never the only signal — the Featured state carries a star and
      the word, the active pill carries aria-pressed as well as a fill.
   5. Dark rules that must beat a (0,3,1) base rule are written at (0,4,1) or
      higher. Source order cannot settle a specificity difference, and a light
      mode that looks perfect while dark mode does not is how this stack has
      shipped the same defect three times.

   SECTIONS
     §AV      the avatar plate — a person is a circle, a product is a square
     §MONO    the initials inside it, and why they are not a silhouette
     §TILE    the hero orbit tiles
     §ORGS    the organisation filter row
     §CHIP    the active-filter chips
     §CARD    the four card deltas a speaker needs and an event does not
     §FRAIL   the featured spotlight rail
     §PROF    the profile page — hero plate, session cards, empty state
     §RESP    the responsive fixes the above need
     §MOTION  reduced motion
   ══════════════════════════════════════════════════════════════════════════ */


/* ═══ §AV — the avatar plate ════════════════════════════════════════════════
   A CIRCLE, and the shape is the point. `.dir-tc-logo` is a 20px-radius
   rounded square because every other vertical puts a product logo in it; a
   roster of people in rounded squares reads as a roster of companies. The
   circle is the only thing that says "person" before a word is read, and it
   costs one property.

   The tone comes from the SHARED sheet's positional cycle — `--dtc-bg` behind
   `--dtc-ink-dark`, a pair that is contrast-checked as a pair and has a dark
   twin. Nothing here injects a hue: `--dtc-ink-dark` alone on an always-white
   surface goes pale after dark (measured on the deals kit), and the reason it
   is safe here is precisely that its background flips with it.
   ────────────────────────────────────────────────────────────────────────── */
.v2x.spkx .spkx-av{
  border-radius:50%;
  position:relative;
  overflow:visible;
  box-shadow:inset 0 0 0 1px rgba(255,255,255,.5);
  transition:transform .32s cubic-bezier(.34,1.56,.64,1), box-shadow .26s ease;
}
body.theme-dark .v2x.spkx .spkx-av{ box-shadow:inset 0 0 0 1px rgba(255,255,255,.07); }

/* The ring is its own pseudo, NOT a box-shadow on the plate: the plate already
   spends its shadow on the inner hairline, and a second shadow there cannot be
   animated independently of it. */
.v2x.spkx .spkx-av::after{
  content:''; position:absolute; inset:-4px; border-radius:50%;
  border:2px solid var(--dtc-ink, #3680ED);
  opacity:0; transform:scale(.9);
  transition:opacity .26s ease, transform .32s cubic-bezier(.34,1.56,.64,1);
  pointer-events:none;
}
.v2x.spkx .spkx-tile-img{
  width:100%; height:100%; object-fit:cover; border-radius:50%; display:block;
}


/* ═══ §MONO — the initials ══════════════════════════════════════════════════
   ⚠ THIS IS NOT A FALLBACK, IT IS THE DESIGN. `ev_speakers.photo` is NULL on
     every row in the table and has been since the module shipped, so a photo
     slot renders one grey silhouette per card — six of them read as "we could
     not load these people", not as a roster. Initials on the card's own tone
     identify a row at a glance and give the grid six different colours instead
     of six identical grey circles.

     The markup still prefers a real `<img>` the moment one is uploaded; this
     rule simply stops being reached. Do not "simplify" it back to an icon.
   ────────────────────────────────────────────────────────────────────────── */
.v2x.spkx .spkx-mono,
.v2x.spkd .spkx-mono{
  display:grid; place-items:center; width:100%; height:100%;
  font-weight:820; letter-spacing:-.02em; line-height:1;
  color:var(--dtc-ink-dark, #2060C0);
  text-transform:uppercase;
  font-variant-numeric:tabular-nums;
  user-select:none;
}
.v2x.spkx .dir-tc-logo .spkx-mono{ font-size:23px; }
.v2x.spkx .dir-lc-logo .spkx-mono{ font-size:18px; }
.v2x.spkx .dir-hh-tile .spkx-mono{ font-size:24px; }
.v2x.spkx .spkx-mono.is-sm{ font-size:15px; }


/* ═══ §TILE — the hero orbit tiles ═════════════════════════════════════════
   The shared sheet sizes and positions `.dir-hh-tile`; only the inside is
   ours. Six tones so four tiles never repeat, and the plate is round for the
   same reason as §AV.
   ────────────────────────────────────────────────────────────────────────── */
.v2x.spkx .spkx-tile{ border-radius:50%; overflow:visible; padding:0; }
.v2x.spkx .spkx-tile .spkx-mono{
  border-radius:50%;
  background:var(--spkx-soft, #eaf1fe);
  color:var(--spkx-ink, #2060C0);
  box-shadow:inset 0 0 0 1px rgba(255,255,255,.55);
}
body.theme-dark .v2x.spkx .spkx-tile .spkx-mono{ box-shadow:inset 0 0 0 1px rgba(255,255,255,.06); }
.v2x.spkx .spkx-tile .spkx-tile-img{ border-radius:50%; }

/* Six tones, shared by the tiles and the organisation pills so one person's
   colour is the same in both places on the same screen. */
.v2x.spkx .spkx-t0{ --spkx-ink:#2060C0; --spkx-soft:#eaf1fe; --spkx-solid:#3680ED; }
.v2x.spkx .spkx-t1{ --spkx-ink:#5B21B6; --spkx-soft:#f3ecff; --spkx-solid:#7C3AED; }
.v2x.spkx .spkx-t2{ --spkx-ink:#047857; --spkx-soft:#dcfce7; --spkx-solid:#059669; }
.v2x.spkx .spkx-t3{ --spkx-ink:#9A3412; --spkx-soft:#ffedd5; --spkx-solid:#C2410C; }
.v2x.spkx .spkx-t4{ --spkx-ink:#BE185D; --spkx-soft:#fce7f3; --spkx-solid:#DB2777; }
.v2x.spkx .spkx-t5{ --spkx-ink:#0E7490; --spkx-soft:#cffafe; --spkx-solid:#0891B2; }
body.theme-dark .v2x.spkx .spkx-t0{ --spkx-ink:#8CBEFA; --spkx-soft:rgba(54,128,237,.18); }
body.theme-dark .v2x.spkx .spkx-t1{ --spkx-ink:#C4B5FD; --spkx-soft:rgba(139,92,246,.18); }
body.theme-dark .v2x.spkx .spkx-t2{ --spkx-ink:#6EE7B7; --spkx-soft:rgba(16,185,129,.18); }
body.theme-dark .v2x.spkx .spkx-t3{ --spkx-ink:#FDBA74; --spkx-soft:rgba(234,88,12,.18); }
body.theme-dark .v2x.spkx .spkx-t4{ --spkx-ink:#FBCFE8; --spkx-soft:rgba(236,72,153,.18); }
body.theme-dark .v2x.spkx .spkx-t5{ --spkx-ink:#67E8F9; --spkx-soft:rgba(6,182,212,.18); }


/* ═══ §ORGBOX — the organisation filter popover ════════════════════════════
   The pill rail this replaced on 2026-08-21 sat ABOVE the toolbar as a second
   control strip, and grew one pill per organisation while the strip beneath it
   stayed one row — six organisations already made it a scroller. The filter now
   rides IN the toolbar beside the view switch, wearing the shared sheet's own
   sort popover (`.dir-sortbox` / `.dir-sort-btn` / `.dir-sort-menu` /
   `.dir-ts-*`), so two controls sitting side by side are the same control.

   ⚠ ALMOST NOTHING IS RESTYLED HERE, DELIBERATELY. Everything the popover
     needs the listing sheet already ships. This file adds the three things it
     does not: the count badge, the group separator, and the bridge that lets
     an organisation row wear a tone at all.
   ────────────────────────────────────────────────────────────────────────── */

/* THE TONE BRIDGE. `.spkx-t0…t5` define --spkx-ink / --spkx-soft (the hero
   tiles and the cards read them too); the shared icon chip reads --so-bg /
   --so-ink / --so-ln. The cycle is POSITIONAL — an organisation's rank in the
   facet list, exactly as the pill rail cycled it — so it is a palette that
   keeps the rows apart, not an identity the card carries as well.
   ⚠ SCOPED TO ITS OWN CLASS, NOT TO `.dir-ts-ic`. Written as
     `.v2x.spkx .spkx-orgbox .dir-ts-ic` it would be (0,4,0) and out-rank
     `.v2x .dir-ts-ic[data-tone="blue"]` (0,3,0) — repainting the Everyone and
     Featured chips from an undefined var, i.e. grey. Only rows that carry a
     tone class carry `.spkx-ic-t`. */
.v2x.spkx .spkx-ic-t{
  --so-bg:var(--spkx-soft,#EFF2F6);
  --so-ink:var(--spkx-ink,#38455A);
  --so-ln:transparent;
}

/* Count badge — the one thing the pill rail carried that the sort popover has
   no slot for, and the one worth keeping: every row states how many speakers
   it leaves on screen, so nothing on offer is a dead end. */
.v2x.spkx .spkx-ob-ct{
  flex:0 0 auto; min-width:22px; height:20px; padding:0 6px;
  display:inline-grid; place-items:center; border-radius:7px;
  background:var(--v2-accent-soft,#EFF2F6); color:var(--v2-muted);
  font-size:11px; font-weight:800; font-variant-numeric:tabular-nums; line-height:1;
  transition:background-color .18s ease, color .18s ease;
}
body.theme-dark .v2x.spkx .spkx-ob-ct{ background:rgba(255,255,255,.07); }
.v2x.spkx .dir-ts-opt.is-on .spkx-ob-ct{ background:var(--brand-blue); color:#fff; }
.v2x.spkx .spkx-orgbox .dir-sort-btn .spkx-ob-ct{ margin-right:1px; }

/* The trigger. A long organisation name is a real name, not a design case —
   it ellipsises rather than pushing the view switch off the toolbar. */
.v2x.spkx .spkx-orgbox .dir-ts-val{
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap; max-width:15ch;
}
.v2x.spkx .spkx-orgbox .dir-ts-opt-lb{
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
}

/* Separator — the two rows above it are STATES, not organisations, and without
   a break the panel reads as a roster with employers called Everyone and
   Featured. */
.v2x.spkx .spkx-ob-sep{
  display:flex; align-items:center; gap:9px;
  margin:8px 2px 5px; padding:0 9px;
  color:var(--v2-muted); font-size:10px; font-weight:800;
  letter-spacing:.11em; text-transform:uppercase; white-space:nowrap;
}
.v2x.spkx .spkx-ob-sep::after{ content:''; flex:1 1 auto; height:1px; background:var(--v2-line); }
body.theme-dark .v2x.spkx .spkx-ob-sep::after{ background:rgba(255,255,255,.09); }

/* The roster outgrows the panel long before the sort list would — it is one
   row per organisation, forever. The LIST scrolls, not the panel, so the
   context footer stays on screen while you run down the list. */
.v2x.spkx .spkx-ob-list{
  max-height:min(46vh,320px); overflow-y:auto; overscroll-behavior:contain;
  padding-right:2px;
  /* ⚠ `scrollbar-color` IS THE ONE THAT LANDS. Chrome honours the standard
       properties from 121 and then IGNORES every ::-webkit-scrollbar rule on
       the same element, so `scrollbar-width:thin` alone bought the platform
       default — a near-black thumb down the side of a white panel. The webkit
       pair below is kept for the engines that still only read it. */
  scrollbar-width:thin; scrollbar-color:var(--v2-line) transparent;
}
body.theme-dark .v2x.spkx .spkx-ob-list{ scrollbar-color:rgba(255,255,255,.16) transparent; }
.v2x.spkx .spkx-ob-list::-webkit-scrollbar{ width:8px; }
.v2x.spkx .spkx-ob-list::-webkit-scrollbar-track{ background:transparent; }
.v2x.spkx .spkx-ob-list::-webkit-scrollbar-thumb{
  background:var(--v2-line); border:2px solid transparent; background-clip:content-box; border-radius:99px;
}
body.theme-dark .v2x.spkx .spkx-ob-list::-webkit-scrollbar-thumb{ background:rgba(255,255,255,.16); background-clip:content-box; }

/* ═══ §CHIP — the active-filter chips ══════════════════════════════════════ */
.v2x.spkx .spkx-chips{
  display:flex; flex-wrap:wrap; align-items:center; gap:8px; margin:0 0 16px;
}
.v2x.spkx .spkx-chip{
  display:inline-flex; align-items:center; gap:7px;
  padding:6px 8px 6px 12px; border-radius:999px;
  background:var(--v2-accent-soft, #eaf1fe); color:var(--brand-blue-dark, #2060C0);
  font-size:12.5px; font-weight:700; letter-spacing:-.006em;
}
.v2x.spkx .spkx-chip button{
  display:inline-grid; place-items:center; width:18px; height:18px;
  border:0; border-radius:50%; padding:0; cursor:pointer;
  background:rgba(54,128,237,.18); color:inherit;
  transition:background-color .18s ease, transform .2s cubic-bezier(.34,1.56,.64,1);
}
.v2x.spkx .spkx-chip button svg{ width:10px; height:10px; }
@media (hover:hover){
  .v2x.spkx .spkx-chip button:hover{ background:rgba(54,128,237,.34); transform:scale(1.12); }
}
.v2x.spkx .spkx-chip-clear{
  padding:6px 13px; border-radius:999px; cursor:pointer; font-family:inherit;
  border:1px dashed var(--v2-line); background:transparent;
  color:var(--v2-muted); font-size:12.5px; font-weight:700;
  transition:color .18s ease, border-color .18s ease;
}
@media (hover:hover){
  .v2x.spkx .spkx-chip-clear:hover{ color:var(--v2-ink); border-color:var(--v2-ink); }
}
body.theme-dark .v2x.spkx .spkx-chip{
  background:rgba(54,128,237,.18); color:#8CBEFA;
}
body.theme-dark .v2x.spkx .spkx-chip button{ background:rgba(96,164,245,.22); }


/* ═══ §CARD — the speaker card deltas ══════════════════════════════════════
   The card IS `.dir-tc` / `.dir-lc` from the shared sheet. Four things differ,
   and only those four are written here.
   ────────────────────────────────────────────────────────────────────────── */

/* 1 · The role line. `.dir-tc-cat` is styled as a category LINK — tone ink,
      underline on hover. A job title is neither a link nor a taxonomy, so it
      is muted here and the ORGANISATION below it keeps the coloured pill. */
.v2x.spkx .spkx-role{
  color:var(--v2-muted) !important;
  font-weight:600;
}

/* 2 · The organisation pill is a real link inside a card that already has an
      overlay link, so it needs the z-index lift the shared sheet grants only
      to direct children of `.dir-tc`. */
.v2x.spkx .spkx-orgpill{
  position:relative; z-index:2; text-decoration:none;
  transition:transform .2s cubic-bezier(.34,1.56,.64,1), box-shadow .18s ease;
}
@media (hover:hover){
  .v2x.spkx a.spkx-orgpill:hover{
    transform:translateY(-1px); text-decoration:none;
    box-shadow:0 6px 14px -6px rgba(27,91,181,.5);
  }
}

/* 3 · Hover: the whole card lifts (shared sheet) and the avatar's ring comes
      in. Two properties, one gesture — the ring is what tells a reader the
      plate is part of the link rather than a decoration beside it. */
@media (hover:hover){
  .v2x.spkx .dir-tc:hover .spkx-av,
  .v2x.spkx .dir-lc:hover .spkx-av{ transform:scale(1.06); }
  .v2x.spkx .dir-tc:hover .spkx-av::after,
  .v2x.spkx .dir-lc:hover .spkx-av::after{ opacity:.55; transform:scale(1); }
}
.v2x.spkx .dir-tc:focus-within .spkx-av::after,
.v2x.spkx .dir-lc:focus-within .spkx-av::after{ opacity:.55; transform:scale(1); }

/* 4 · The row keeps the avatar from squashing when a long name wraps. */
.v2x.spkx .dir-lc .spkx-av{ flex:0 0 auto; }
.v2x.spkx .dir-lc-side .dir-tc-btn{ margin-top:0; }


/* ═══ §FRAIL — the featured spotlight rail ═════════════════════════════════
   `.dir-cat-card-ico` is a tone-tinted rounded square the shared sheet paints
   from the card's own `data-tone`. Only the SHAPE is ours, for the same reason
   as §AV — and the tint is deliberately left alone, because it is already the
   right colour and re-painting it would put a second, unchecked pair on the
   same plate.
   ────────────────────────────────────────────────────────────────────────── */
.v2x.spkx .spkx-fav{ border-radius:50%; overflow:hidden; }
.v2x.spkx .spkx-fav .spkx-mono{ color:inherit; }
.v2x.spkx .spkx-fcard .dir-cat-card-desc{ min-height:2.9em; }


/* ══════════════════════════════════════════════════════════════════════════
   §PROF — /events/speakers/{slug}
   The profile runs the ATD detail system (`.v2x.atd.atdx.evd.spkd`), the same
   stack as /events/{slug}. Everything below is a delta on THAT kit, not on the
   listing kit above — the two share only §MONO.
   ══════════════════════════════════════════════════════════════════════════ */

/* The hero plate. `.atd-logo-plate` is a rounded square holding either a logo
   or `.atd-logo-letter`; a person gets the circle for the same reason as §AV.
   The halo is the kit's own element — only its radius follows. */
.v2x.spkd .atd-logo-plate,
.v2x.spkd .atd-logo-plate .atd-logo-tile,
.v2x.spkd .atd-logo-halo{ border-radius:50%; }
.v2x.spkd .atd-logo-letter{ letter-spacing:-.03em; }

/* Sessions. `.atdx-rel` is the ATD body-column row card — see §ATDX-REL in
   public-atd-detail.css. A session needs a DATE where a related tool needs a
   logo, so the plate becomes a two-line date and everything else is inherited.
   ⚠ `.atdx-rel-lg` sets `color:#fff` on a `--t-1p` fill; the date must stay
     white on it, so nothing here re-colours the plate's ink. */
.v2x.spkd .spkd-ses-d{
  display:flex; flex-direction:column; align-items:center; justify-content:center;
  width:100%; height:100%; gap:1px; line-height:1;
  font-variant-numeric:tabular-nums;
}
.v2x.spkd .spkd-ses-m{
  font-size:9px; font-weight:800; letter-spacing:.10em; text-transform:uppercase; opacity:.92;
}
.v2x.spkd .spkd-ses-n{ font-size:17px; font-weight:820; letter-spacing:-.03em; }
.v2x.spkd .spkd-ses-ic{ display:grid; place-items:center; width:100%; height:100%; }

/* A live session overrides the positional tone: "happening now" is a state,
   not a slot in a colour cycle, and it must read the same on every card. */
.v2x.spkd .atdx-rel.is-live .atdx-rel-lg{ background-color:#DC2626; }
.v2x.spkd .spkd-live-dot{
  width:7px; height:7px; border-radius:50%; background:#fff;
  animation:spkd-pulse 1.8s ease-out infinite;
}
@keyframes spkd-pulse{
  0%   { box-shadow:0 0 0 0 rgba(255,255,255,.6); }
  70%  { box-shadow:0 0 0 7px rgba(255,255,255,0); }
  100% { box-shadow:0 0 0 0 rgba(255,255,255,0); }
}

/* The section head inside a body card, matching `.v2-section-title` but with
   a count chip on the right. */
.v2x.spkd .spkd-sechead{
  display:flex; align-items:center; gap:10px; flex-wrap:wrap; margin:0 0 14px;
}
.v2x.spkd .spkd-sechead .v2-section-title{ margin:0; flex:1 1 auto; min-width:0; }
.v2x.spkd .spkd-count{
  flex:0 0 auto; padding:3px 10px; border-radius:999px;
  background:rgba(var(--t-rgb,76,141,242),.14); color:var(--t-ink, #1B5BB5);
  border:1px solid rgba(var(--t-rgb,76,141,242),.24);
  font-size:11.5px; font-weight:800; font-variant-numeric:tabular-nums; line-height:1.5;
}
body.theme-dark .v2x.spkd .spkd-count{ background:rgba(var(--t-rgb,76,141,242),.22); }

/* The "no sessions" state. A speaker with nothing published is a real row —
   the profile stays up and says so rather than 404ing or rendering an empty
   card, which is indistinguishable from a broken page. */
.v2x.spkd .spkd-empty{
  display:flex; flex-direction:column; align-items:center; text-align:center;
  gap:6px; padding:34px 24px;
  border:1px dashed var(--v2-line); border-radius:16px;
  color:var(--v2-muted);
}
body.theme-dark .v2x.spkd .spkd-empty{ border-color:var(--dk-line-strong,rgba(255,255,255,.16)); }
.v2x.spkd .spkd-empty-ic{
  display:inline-grid; place-items:center; width:48px; height:48px; margin-bottom:4px;
  border-radius:14px; background:rgba(var(--t-rgb,76,141,242),.14); color:var(--t-ink, #1B5BB5);
}
.v2x.spkd .spkd-empty h3{
  margin:0; font-size:16px; font-weight:800; letter-spacing:-.012em; color:var(--v2-ink);
}
.v2x.spkd .spkd-empty p{ margin:0; font-size:13.5px; line-height:1.55; max-width:46ch; }

/* Every plate holding a PERSON is a circle — the sidebar rail's and the body
   column's alike — so the shape agrees with the hero on the same screen.
   ⚠ THE INK IS THE KIT'S, NOT OURS. `.atdx-rel-lg` fills with `--t-1p` and
     sets `color:#fff`; the first build put `.spkx-mono` on it, which paints
     the LISTING kit's `--dtc-ink-dark` — a dark blue on a saturated fill,
     measured at 1.9:1. Nothing here re-colours these plates. */
.v2x.spkd .spkd-people .atd-alt .lg{ border-radius:50%; }
.v2x.spkd .spkd-peer .atdx-rel-lg{ border-radius:50%; }


/* ═══ §RESP — responsive ═══════════════════════════════════════════════════
   Breakpoints are the SHARED sheet's ladder — 1180 / 820 / 560 — so the
   organisation row and the chips never step out of phase with the grid beside
   them.
   ────────────────────────────────────────────────────────────────────────── */
@media (max-width:820px){
  /* THREE controls in the toolbar's right group now, not two. The shared sheet
     pins that row to `nowrap` for a sort box and a view switch; a third control
     on the same line squeezes both popovers narrower than their own panels, so
     the row wraps here and the switches take the line below. */
  .v2x.spkx .dir-tb-right{ flex-wrap:wrap; }
  .v2x.spkx .dir-sortbox{ flex:1 1 240px; }
  .v2x.spkx .dir-lc-side{ width:100%; }
  .v2x.spkx .dir-lc-side .dir-tc-btn{ width:100%; }
}
@media (max-width:560px){
  .v2x.spkx .dir-tc-logo .spkx-mono{ font-size:21px; }
  /* Full-width trigger down here — the name has the whole row, so capping it
     at 15ch would truncate what already fits. */
  .v2x.spkx .spkx-orgbox .dir-ts-val{ max-width:none; }
  .v2x.spkd .spkd-empty{ padding:26px 16px; }
}


/* ═══ §MOTION ══════════════════════════════════════════════════════════════ */
@media (prefers-reduced-motion:reduce){
  .v2x.spkx .spkx-av,
  .v2x.spkx .spkx-av::after,
  .v2x.spkx .spkx-ob-ct,
  .v2x.spkx .spkx-chip button,
  .v2x.spkx .spkx-orgpill{ transition:none; }
  .v2x.spkx .dir-tc:hover .spkx-av,
  .v2x.spkx .dir-lc:hover .spkx-av{ transform:none; }
  .v2x.spkd .spkd-live-dot{ animation:none; }
}


/* ═══ §BIO — three lines, not two ══════════════════════════════════════════
   `.dir-tc-desc` is a TWO-line clamp in the shared sheet, sized for a tool
   card that also carries a rank chip, a rating row, a price and two pill rows.
   A speaker card carries one pill and one fact, so it has the room — and at
   two lines every bio in the table was cut mid-sentence at every width from
   768 up, measured with scrollHeight against clientHeight.

   ⚠ THE `min-height` MUST MOVE WITH THE CLAMP. It is what keeps the CTA row on
     one baseline across a row of cards; leaving it at two lines while the text
     runs to three makes the buttons stagger by exactly one line — the same
     equal-height defect the card-footer margin trap produces, from the other
     end. 1.55 is the shared sheet's own line-height for this element.
   ────────────────────────────────────────────────────────────────────────── */
.v2x.spkx .dir-tc-desc{
  -webkit-line-clamp:3;
  min-height:calc(3 * 1.55em);
}
@media (max-width:560px){
  /* One card per row on a phone: the full width already fits the bio, and a
     three-line floor on a single-column list is dead space. */
  .v2x.spkx .dir-tc-desc{ min-height:0; }
}
