/* ============================================================================
   BUNDLE DETAIL — `#bundle-detail`

   ⚠️ NO COMP. See the header of src/components/bundle.js. Every measurement
   below is taken from the surface this page reuses, not invented:

     header split      56.7% / rest — video.css's `.vdetail-header__media`,
                       which is 576 of the 1016 body
     card shell        `.detail-card` (detail.css), untouched
     two-column body   NOT used. A bundle's Content tab is a grid the full
                       width of the body; a 424 sidebar beside it would leave
                       the grid too narrow to hold three cards, and there is
                       no sidebar content for a bundle anyway.
     grid card measure 200 min / 234 max — collection.css's `.collection__grid`,
                       so a card in a bundle is the same size as a card
                       anywhere else in the app
     tabs              `Program / Detail / Tabs` 48066:408090 — the one
                       part of this page that DOES have a comp
     content sections  `.channel-section` (channels.css), reused whole — one
                       card per object type, minus its description and See all

   TODO(tokens): every bare px length in this file is a SIZE or a BREAKPOINT.
   DESIGN.md §12.4 records both as not built. No colour, radius, spacing,
   border-width or font-size literal appears anywhere in this file.
   ========================================================================== */

.bdetail {
  display: flex;
  flex-direction: column;
  gap: var(--space-md);
}

/* ============================================================================
   Content Object / Header — the Video detail header without the player
   ========================================================================== */

.bdetail-header {
  display: flex;
  align-items: stretch;
  overflow: clip;
  background: var(--color-surface-raised);
  border-radius: var(--shape-radius-card);
}

.bdetail-header__media {
  position: relative;
  flex: none;
  /* 576 of 1016, kept as a ratio so the card reflows rather than overflows —
     the figure video.css and event.css both use. */
  inline-size: 56.7%;
  /* TODO(tokens): size not built. 324 is the documented header height. */
  min-block-size: 324px;
  background: var(--color-surface-media);
}

/* A still, not a poster with a play disc over it: a bundle has no footage of
   its own, and a disc that plays nothing is the defect video.css removed from
   its own control bar. */
.bdetail-header__image {
  inline-size: 100%;
  block-size: 100%;
  object-fit: cover;
}

.bdetail-header__body {
  display: flex;
  flex: 1 1 auto;
  min-inline-size: 0;
  flex-direction: column;
  gap: var(--space-md);
  padding: var(--space-md);
}

.bdetail-header__main {
  display: flex;
  flex: 1 1 auto;
  flex-direction: column;
  gap: var(--space-xs);
}

.bdetail-header__title {
  margin: 0;
}

.bdetail-header__meta {
  display: flex;
  margin: 0;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2xs);
  color: var(--color-content-primary);
}

.bdetail-header__meta .icon {
  /* TODO(tokens): size not built. 14px is the DS's metadata icon slot — the
     step video.css uses for the same two lines. */
  inline-size: 14px;
  block-size: 14px;
  color: var(--color-content-secondary);
}

.bdetail-header__price-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-md);
  flex-wrap: wrap;
}

/* --- The price, and the two figures that make it an argument -------------- */

.bdetail-price {
  display: flex;
  margin: 0;
  align-items: baseline;
  flex-wrap: wrap;
  gap: var(--space-2xs);
}

.bdetail-price__now {
  color: var(--color-content-headline);
  font-family: var(--font-family-display);
  font-size: var(--font-heading-md-size);
  line-height: var(--font-heading-md-line-height);
  font-weight: var(--font-weight-medium);
  letter-spacing: var(--font-letter-spacing-default);
}

.bdetail-price__was {
  color: var(--color-content-secondary);
  font-size: var(--font-body-md-size);
  line-height: var(--font-body-md-line-height);
  text-decoration: line-through;
}

/* The saving as a chip beside the pair. `soft/success` with its `on-*` partner
   — DESIGN.md §4.4's pairing contract, never `content/primary` on a fill. */
.bdetail-price__save {
  padding: var(--space-3xs) var(--space-xs);
  border-radius: var(--shape-radius-chip);
  background: var(--color-soft-success-default);
  color: var(--color-on-soft-success);
  font-family: var(--font-family-text);
  font-size: var(--font-label-sm-size);
  line-height: var(--font-label-sm-line-height);
  font-weight: var(--font-weight-medium);
  letter-spacing: var(--font-letter-spacing-wide);
}

/* Bought. Hue, glyph and word together — the root CLAUDE.md gap on status
   carried by wording alone. Same optical weight as the price it replaces, so
   the header does not reflow between the two states. */
.bdetail-owned {
  display: flex;
  align-items: center;
  gap: var(--space-2xs);
  margin: 0;
  color: var(--color-content-success);
  font-family: var(--font-family-text);
  font-size: var(--font-body-lg-size);
  line-height: var(--font-body-lg-line-height);
  font-weight: var(--font-weight-medium);
}

/* ============================================================================
   The action row — between the header and the tabs, per the brief
   ========================================================================== */

/* `Communities / Detail / Status / User` — one full-width primary in a
   `p/card/content`, and the first card in the About column. It held a
   reassurance line, a saving column and then the locked notice; all three are
   gone. See actionCard() in bundle.js. */
.bdetail-actions .btn {
  inline-size: 100%;
}

/* ============================================================================
   `Program / Detail / Tabs` — FE Client 48066:408090

   The ONE part of this page that has a comp. Verified against it:

     bar        `General/White` at radius 8, padding-inline 16, tabs full height
     tab        padding 16, label 16/1.5 Roboto Regular
     rest       `Text & Icons/Primary` #595959
     selected   `✅ Action/BigHeart/Primary` #4211a0, plus a 2px rule under the
                tab in the same colour

   TOKENS, not the comp's literals. `#4211a0` is the BigHeart seed and root
   CLAUDE.md non-negotiable 4 says do not add another frozen instance of it —
   the label takes `content/brand` and the rule takes `indicator/brand`, the
   token minted for exactly this and which CLAUDE.md lists as having zero
   consumers. `#595959` is `content/primary`; `#FFFFFF` is `surface/raised`;
   16/1.5 is `Body/LG`.

   It replaces `Tab / Rounded`, borrowed from the Channels organization filter
   while nothing better existed. Those are two controls doing two jobs — a
   filter chip row and a section switcher — and the section switcher has now
   been drawn.

   Named `.dtabs` / `.dtab`, not `.bdetail-*`: this is the detail-page tab bar,
   and the Video and Event pages should take it when they grow sections.
   ========================================================================== */

.dtabs {
  display: flex;
  align-items: stretch;
  padding-inline: var(--space-md);
  background: var(--color-surface-raised);
  border-radius: var(--shape-radius-card);
  /* The bar is a single row that scrolls rather than wraps: a wrapped tab bar
     puts a second row of tabs under a rule that belongs to the first. */
  overflow-x: auto;
  scrollbar-width: none;
}

.dtab {
  display: flex;
  flex: none;
  align-items: center;
  justify-content: center;
  padding: var(--space-md);
  border: 0;
  /* Always present, transparent when not selected — so switching tabs does not
     move every label by 2px. */
  border-block-end: var(--border-width-thick) solid transparent;
  background: none;
  color: var(--color-content-primary);
  font-family: var(--font-family-text);
  font-size: var(--font-body-lg-size);
  line-height: var(--font-body-lg-line-height);
  font-weight: var(--font-weight-regular);
  letter-spacing: var(--font-letter-spacing-default);
  white-space: nowrap;
  cursor: pointer;
  transition: color 100ms ease, border-color 100ms ease; /* TODO(tokens): motion */
}

/* Neither state is in the comp. Both are the known gaps root CLAUDE.md says to
   implement rather than inherit — a tab that does not react to the pointer
   reads as a label. */
.dtab:hover {
  color: var(--color-content-headline);
}

.dtab:active {
  color: var(--color-content-brand);
}

.dtab:focus-visible {
  outline: var(--shape-focus-width) solid var(--color-border-focus);
  /* Inset, not offset: an outward ring on a full-height tab is clipped by the
     bar it sits in. */
  outline-offset: calc(-1 * var(--shape-focus-width));
  border-radius: var(--radius-xs);
}

/* Two channels, never colour alone — the rule is the one that survives a
   monochrome render. */
.dtab[aria-selected="true"] {
  color: var(--color-content-brand);
  border-block-end-color: var(--color-indicator-brand);
}

/* ============================================================================
   The panels
   ========================================================================== */

/* A tab panel is a focus target after a tab change, but it is not a control —
   the ring belongs on the tab that was pressed, not on the region it revealed.
   Same rule base.css applies to #main. */
.bdetail__panel {
  display: flex;
  flex-direction: column;
  gap: var(--space-md);
}

.bdetail__panel:focus {
  outline: none;
}

.bdetail__panel[hidden] {
  display: none;
}

/* The Content tab is a stack of `Channel Header` sections — `.channel-section`
   from channels.css, reused whole. Nothing to declare for it here: the panel is
   already a flex column at `space-md`, which is the gap Channels puts between
   its own sections. */

/* ============================================================================
   `Body` 48109:84645 — the About view's two columns

   576 + 16 + 424 inside the 1016 body: the same arithmetic video.css does for
   `.vdetail__content` and event.css for its own. Three near-identical copies
   of one split now exist.
   TODO(ds): `Body / … / Detail` is one layout wearing three names. It belongs
   in detail.css as a shared shell, not restated per page.
   ========================================================================== */

.bdetail__cols {
  display: flex;
  align-items: start;
  gap: var(--layout-gap);
}

.bdetail__body {
  display: flex;
  flex: 1 1 auto;
  min-inline-size: 0;
  flex-direction: column;
  gap: var(--space-md);
}

.bdetail__side {
  display: flex;
  /* TODO(tokens): size not built. The frame's Sidebar is a fixed 424. */
  inline-size: 424px;
  flex: none;
  flex-direction: column;
  gap: var(--space-md);
}

/* Below the width where 576 + 424 stops fitting, the two stack — the same
   threshold layout.css and video.css use.
   TODO(tokens): breakpoints not built — DESIGN.md §12.4. */
@media (max-width: 1199px) {
  .bdetail__cols {
    flex-direction: column;
    align-items: stretch;
  }
  .bdetail__side {
    inline-size: auto;
  }
}

/* `Community / All / Detail / Members` tiles — `.detail-person` in detail.css,
   the same block the Video detail page draws. They wrap rather than scroll:
   this is the whole list, not a sample of it (see purchasesCard() in
   bundle.js), so there is nothing off the end to reach. */
.bdetail-people {
  display: flex;
  flex-wrap: wrap;
  align-items: start;
  gap: var(--space-md) var(--space-xs);
}

.bdetail-people > .detail-person {
  /* TODO(tokens): size not built. The frame's Avatar tile is 112, which is
     sized for the 4-across row it draws in a 424 sidebar. 112 does not
     actually fit four with the gap between them (4x112 + 3x8 = 472 against a
     392 content box), so the tile is the width that does — the frame's
     COLUMN COUNT is the intent, and its tile width contradicts it. */
  flex: 0 1 88px;
}

/* ============================================================================
   The content grids
   ========================================================================== */

.bdetail-grid {
  display: flex;
  flex-wrap: wrap;
  align-content: start;
  gap: var(--space-lg) var(--space-md);
}

/* The same measure the collection pages give a card, so a card inside a bundle
   is the size it is everywhere else. */
.bdetail-grid > .ccard {
  flex: 1 0 0;
  min-inline-size: 200px;
  max-inline-size: 234px;
}

/* ============================================================================
   About
   ========================================================================== */

.bdetail-prose {
  margin: 0;
  color: var(--color-content-primary);
}

.bdetail-lines {
  display: flex;
  flex-direction: column;
}

/* A hairline between rows, not around each one: the list is one object, and
   boxing every line would read as fifteen cards.
   ⚠️ `border/default` alone is one of the 29 AA failures the token layer
   carries (root CLAUDE.md), so it is never the ONLY thing separating two
   rows here — the thumbnail and the price column carry the structure and this
   is the quiet third channel. */
.bdetail-line + .bdetail-line {
  border-block-start: var(--border-width-thin) solid var(--color-border-default);
}

/* Each row OPENS that item — see routeToItem() in bundle.js — so it is a
   <button> and has to stop looking like one, then behave like the control it
   is. The hover and pressed steps are the same two `.menu__item` uses; there
   is no comp for either, and both are the known gaps root CLAUDE.md says to
   implement rather than inherit. */
.bdetail-line {
  inline-size: 100%;
  padding-inline: var(--space-2xs);
  /* The negative margin keeps the row's text flush with the card's padding
     while the hover fill still reaches past it. */
  margin-inline: calc(-1 * var(--space-2xs));
  border: 0;
  border-radius: var(--radius-xs);
  background: none;
  font: inherit;
  color: inherit;
  text-align: start;
  cursor: pointer;
}

.bdetail-line:hover {
  background: var(--color-soft-neutral-hover);
}

.bdetail-line:active {
  background: var(--color-soft-neutral-pressed);
}

.bdetail-line:focus-visible {
  outline: var(--shape-focus-width) solid var(--color-border-focus);
  outline-offset: calc(-1 * var(--shape-focus-width));
}

.bdetail-line__thumb {
  flex: none;
  /* TODO(tokens): size not built. 64x36 is 16:9 at the row's height. */
  inline-size: 64px;
  block-size: 36px;
  object-fit: cover;
  border-radius: var(--shape-radius-input);
}

.bdetail-line__price {
  flex: none;
  margin: 0;
  color: var(--color-content-primary);
  font-family: var(--font-family-text);
  font-size: var(--font-body-md-size);
  line-height: var(--font-body-md-line-height);
  font-weight: var(--font-weight-medium);
  font-variant-numeric: tabular-nums;
}

/* --- The totals ----------------------------------------------------------- */

.bdetail-totals {
  display: flex;
  margin: 0;
  flex-direction: column;
  gap: var(--space-2xs);
  padding-block-start: var(--space-sm);
  border-block-start: var(--border-width-thin) solid var(--color-border-strong);
}

.bdetail-totals__row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-md);
  font-family: var(--font-family-text);
  font-size: var(--font-body-md-size);
  line-height: var(--font-body-md-line-height);
  color: var(--color-content-secondary);
}

.bdetail-totals__row dt,
.bdetail-totals__row dd {
  display: flex;
  align-items: center;
  gap: var(--space-2xs);
  margin: 0;
}

.bdetail-totals__row dd {
  font-variant-numeric: tabular-nums;
}

/* The line the whole card exists to support: `content/success`, a glyph and
   the word — the same three channels the header's Purchased state uses. */
.bdetail-totals__row--saving {
  color: var(--color-content-success);
  font-size: var(--font-body-lg-size);
  line-height: var(--font-body-lg-line-height);
  font-weight: var(--font-weight-medium);
}

.bdetail-totals__row--saving dd span {
  font-size: var(--font-body-sm-size);
  line-height: var(--font-body-sm-line-height);
  font-weight: var(--font-weight-regular);
}

/* A logo inside an avatar is inset, not cropped to the circle — ASSETS.md §5
   rule 1. The 80% figure and its reasoning are video.css's `.vdetail-logo`. */
.bdetail-logo {
  inline-size: 80%;
  block-size: 80%;
  object-fit: contain;
}

/* ============================================================================
   Narrow bands — the same threshold layout.css and video.css use
   TODO(tokens): breakpoints not built — DESIGN.md §12.4.
   ========================================================================== */

@media (max-width: 1199px) {
  .bdetail-header {
    flex-direction: column;
  }
  .bdetail-header__media {
    inline-size: 100%;
    min-block-size: 0;
    aspect-ratio: 16 / 9;
  }
}
