/* /track - full-page track detail (rich + sticky proof sidebar)

   Layout follows the site rail tokens (--rail / --rail-x) the
   same way landing-section does: each top-level section caps at
   `calc(var(--rail) + 2 * var(--rail-x))`, has horizontal
   padding of var(--rail-x), and is centred via margin: 0 auto.
   Body has nav-follows-body so the nav inherits bone instead of
   the chartreuse default. */

/* ============== Page ==============
   .ta-page is centred and wider than the standard rail because
   it carries TWO columns: a rail-wide main column (matching the
   /library content width so list-view → detail-view feels like
   a continuous frame) PLUS a 320px sticky sidebar with the
   certificate + credits cards. Total inner width:
       --rail            (main column, matches library)
     + column-gap        (clamp 48–88px)
     + 320px             (sidebar)
   Outer padding stays at --rail-x on both sides.

   Body already adds padding-top: var(--nav-h) globally, so the
   page padding-top is just the gap between the nav and the
   back link. */
.ta-page {
  /* --ta-aside-w / --ta-gap are referenced by .ta-body to keep
     its grid columns in sync with what's allotted on this page. */
  --ta-aside-w: 320px;
  --ta-gap: clamp(48px, 6vw, 88px);
  background: var(--bone);
  min-height: 100vh;
  /* width: 100% + max-width together force this block to fill
     its parent up to the cap, regardless of intrinsic content
     width. Without `width: 100%`, .ta-page (as a flex item of
     .page-main) was sizing to its CONTENT min-width - and
     because .ta-title has white-space: nowrap, its min-content
     equals the full untruncated text. Net effect: a long title
     made the page wider, a short title made it narrow. The
     explicit width: 100% defeats the shrink-to-fit so the page
     is always the same width regardless of what the user types
     into the title. */
  width: 100%;
  min-width: 0;
  /* Standard rail width - matches /library exactly so the
     left/right page margins on the track detail view sit on
     the same column as the library list view. The certificate
     + credits sidebar lives INSIDE this rail (sharing space
     with the main column), not outside it. */
  max-width: calc(var(--rail) + 2 * var(--rail-x));
  margin: 0 auto;
  padding:
    clamp(16px, 2.5vw, 32px)
    var(--rail-x)
    clamp(96px, 12vw, 160px);
}

/* ============== Body grid ==============
   Single grid row, two columns: column 1 holds the metadata
   + tags + work sections (natural height); column 2 holds the
   sticky aside (cert + credits). Single-row layout means the
   aside's height never stretches a row underneath it - the
   work area below the metadata flows tight against the tags
   row, no phantom whitespace. */
.ta-body {
  display: grid;
  /* Aside col + gap match .ta-page's --ta-aside-w / --ta-gap so
     the page max-width math stays consistent - change them in
     one place and both widths track. */
  grid-template-columns: minmax(0, 1fr) var(--ta-aside-w);
  column-gap: var(--ta-gap);
  align-items: start;
}
.ta-body-main { min-width: 0; }
.ta-back-row { margin: 0 0 10px; }
/* Back to Library - Apple iOS-style nav: chevron + sentence-
   case destination label, outdented so the label's left edge
   aligns with the title below. Sans body font (not mono) so
   it reads as an interactive control rather than a system
   eyebrow tag. Subtle pill bg on hover for the tap-target
   affordance Apple uses on iPad Settings / Music. */
.ta-back {
  display: inline-flex;
  align-items: center;
  gap: 0;
  /* Negative margin-left compensates for the padding so the
     visible text's left edge sits at x=0 - same column as
     the title below it. */
  margin-left: -10px;
  /* Asymmetric padding - extra room on the right of the
     hover-pill so the highlight doesn't crowd the "Library"
     label's last glyph. The negative margin still aligns the
     chevron's optical left with the title's column below. */
  padding: 6px 16px 6px 10px;
  border-radius: 999px;
  font-family: inherit;
  font-weight: 500;
  font-size: 0.95rem;
  letter-spacing: -0.005em;
  /* Solid ink - no dim opacity. Hover keeps the soft pill
     fill as the affordance signal. */
  color: var(--ink);
  opacity: 1;
  text-decoration: none;
  transition: background 120ms ease;
}
@media (hover: hover) {
  .ta-back:hover {
  background: rgba(10, 10, 10, 0.06);
}
}
.ta-back-chevron {
  /* SVG chevron with rounded line caps + corners - same shape
     iOS / Apple Music use. Sized 18px so it reads larger than
     the label without overpowering. flex-shrink: 0 keeps the
     icon from squishing if the row narrows. */
  width: 18px;
  height: 18px;
  flex-shrink: 0;
  display: block;
}
/* Track name - matches the .lib-h-title heading on the
   /library page so the typographic relationship between
   "list of tracks" and "this track" is consistent. */
.ta-title {
  margin: 0;
  font-size: clamp(2.25rem, 7vw, 4.25rem);
  font-weight: 600;
  letter-spacing: -0.04em;
  /* line-height: 1.2 gives full vertical room for descenders
     ("g", "j", "p", "q", "y") with no clipping. The earlier
     0.96/1.05 values were tight enough to crop descender
     pixels - at headline sizes (~64px) even 4–6 missing pixels
     reads as "letters got cut off". 1.2 is the safe minimum
     for any sans-serif at headline weight. */
  line-height: 1.2;
  color: var(--ink);
  /* Single-line + ellipsis. Long titles get cut off with "…"
     instead of wrapping to a second line - the full title
     still lives in the page <title> and the title attribute
     for accessibility / hover tooltip. */
  white-space: nowrap;
  /* overflow: clip + overflow-clip-margin lets the line-box
     render its descenders outside the clip rect. Plain
     `overflow: hidden` would chop them off again at the
     padding edge regardless of line-height, because the
     line-box bottom sits at the content edge. */
  overflow: clip;
  overflow-clip-margin: 0.25em;
  text-overflow: ellipsis;
  /* min-width: 0 prevents the title's nowrap min-content from
     forcing its parent grid item / flex item wider. Defends
     the "page width changes when title length changes" bug
     in case .ta-page ever loses its own min-width: 0. */
  min-width: 0;
}
/* Edit-mode - text becomes editable inline. No border, no
   box, no focus outline; only the caret signals interactivity.
   The user explicitly asked for "don't add a box around it".
   While editing we drop the ellipsis clip so the full text is
   visible as the user types past the line; saving exits this
   state and the static .ta-title rules re-engage. */
.ta-title[contenteditable] {
  outline: none;
  cursor: text;
  overflow: visible;
  text-overflow: clip;
}
.ta-title[contenteditable]:focus {
  outline: none;
  box-shadow: none;
  background: transparent;
  border: 0;
}
.ta-sub {
  /* First item in .ta-body-main column. Its margin-top is
     mirrored on .ta-aside so the sub-line's text top and the
     certificate card's top sit on the same baseline. */
  margin: 14px 0 0;
  font-family: var(--font-mono);
  font-weight: 500;
  font-size: 0.78rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  /* Solid ink - no dim opacity - so the metadata reads at the
     same weight as the title. Used to be var(--fg-dim) but
     that pushed the chips into the visual background and
     made them feel like an afterthought. */
  color: var(--ink);
}
/* Each metadata segment is a nowrap span; separators sit in
   their own spans so JS can flip them to visibility: hidden
   when they straddle a line break — the dot disappears but
   the layout width is preserved, so the wrap point doesn't
   bounce (no flicker / paradox where removing the dot lets
   the next segment fit on the prior line again). The sep
   keeps the default `white-space: normal` so its internal
   spaces ARE wrap candidates — otherwise the whole line
   becomes one non-breaking unit and overflows on the right. */
.ta-sub-part { white-space: nowrap; }
/* Hero tag pill row - sits below the sub line. */
.ta-hero-tags {
  margin-top: 22px;
}

/* ============== Work area ============== */
/* Top gap from the hero tags row above - restores the editorial
   breathing room between the tag pill row and the first
   section header so the page reads as hero → list-of-sections
   rather than one continuous block. */
.ta-main {
  margin-top: clamp(60px, 7vw, 96px);
}

/* ============== Main column - sections ============== */
.ta-main { min-width: 0; }
.ta-loading {
  font-family: var(--font-mono);
  font-weight: 500;
  font-size: 0.78rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--fg-dim);
  margin: 0;
}

.ta-section + .ta-section {
  margin-top: clamp(48px, 6vw, 72px);
}
.ta-section-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 16px;
  margin-bottom: 20px;
}
.ta-section-title {
  margin: 0;
  /* Section headers (Leak Tracking, Details, Activity) - bumped
     up from clamp(1.25, 2vw, 1.5) so they read as proper page
     section dividers, not just slightly-bigger row labels.
     Sits a tier below the page <h1> (~3rem desktop) and a tier
     above the row body text (0.95rem). */
  font-size: clamp(1.625rem, 2.6vw, 2rem);
  font-weight: 600;
  letter-spacing: -0.025em;
  color: var(--ink);
}
.ta-section-meta {
  margin: 0;
  font-family: var(--font-mono);
  font-weight: 500;
  font-size: 0.7rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--fg-dim);
}

/* Editing host slot - track-edit widgets render inside */
.ta-edit-slot {
  margin-top: 4px;
}

/* Activity feed - date-grouped layout. Each day is a row
   with a mono-caps date label on the left and event
   sentences stacked on the right. Days with multiple
   events stack under one date eyebrow. */
.actc {
  display: grid;
  gap: 18px;
}
.actc-group {
  display: grid;
  /* Label column matches .ta-dl-row's so the Details and
     Activity value columns line up vertically — picked as
     the midpoint of the previous Details (200px) and
     Activity (96px) widths so neither label gets cramped
     and the dt/dd alignment reads as one consistent grid
     across both sections. */
  grid-template-columns: 148px 1fr;
  gap: 24px;
  align-items: start;
}
.actc-when {
  margin: 4px 0 0;
  font-family: var(--font-mono);
  font-size: 0.7rem;
  font-weight: 500;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink);
  opacity: 0.55;
}
.actc-events {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 6px;
}
.actc-event {
  margin: 0;
  font-size: 0.92rem;
  /* Lock body weight at 400 so the <strong> proper-noun
     contrast is unmistakable. The global body weight is
     500 (Medium); without overriding here, .actc-event
     would inherit 500 and the .actc-event strong rule's
     600 would only be a one-step bump on the variable
     font - visually indistinguishable from the body text.
     400 → 600 is two steps, which reads as proper bold. */
  font-weight: 400;
  color: var(--ink);
}
.actc-event strong { font-weight: 700; }
.ta-empty {
  margin: 0;
  padding: 14px 0;
  font-family: var(--font-mono);
  font-weight: 500;
  font-size: 0.7rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--fg-dim);
}

/* Recipients list (rendered in the Leak Tracking section).
   Two-column row: identifier+date on the left, single-line
   note input on the right. The input grows to fill the
   remaining row width. */
.ta-recipients {
  margin: 0;
  padding: 0;
  list-style: none;
}
.ta-recipients li {
  padding: 8px 0;
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.6fr);
  gap: 24px;
  align-items: center;
}
.ta-recipient-main { min-width: 0; }
.ta-recipient-name {
  margin: 0 0 2px;
  font-size: 0.95rem;
  font-weight: 600;
  color: var(--ink);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.ta-recipient-meta {
  margin: 0;
  font-size: 0.78rem;
  font-weight: 500;
  color: var(--ink);
  opacity: 0.55;
}
/* Mobile-only read-only echo of the saved note, rendered as
   an italic eyebrow above the email. The editable .ta-recipient-
   note input stays for desktop; on phones we hide that and show
   this static display instead. */
.ta-recipient-note-display {
  display: none;
}
/* Single-line note input - quiet outline pill that
   matches the Add-collaborator pill family. Owner-only
   note (placeholder makes that clear). 140-char cap is
   enforced via maxlength so the cap surfaces in the
   browser, not just on save. */
.ta-recipient-note {
  appearance: none;
  width: 100%;
  padding: 8px 14px;
  border: 1px solid rgba(10, 10, 10, 0.15);
  border-radius: 999px;
  background: transparent;
  font: inherit;
  font-size: 0.85rem;
  color: var(--ink);
  outline: none;
  transition: border-color 120ms ease, background 120ms ease;
}
.ta-recipient-note::placeholder {
  color: var(--ink);
  opacity: 0.4;
}
@media (hover: hover) {
  .ta-recipient-note:hover {
  background: rgba(10, 10, 10, 0.02);
  border-color: rgba(10, 10, 10, 0.25);
}
}
.ta-recipient-note:focus {
  border-color: var(--ink);
  background: var(--bone);
}
/* Saved-confirm flash - JS toggles this for ~600ms after a
   successful POST. Quiet ink border, no toast. */
.ta-recipient-note--saved {
  border-color: var(--ink);
  transition: border-color 320ms ease;
}
.ta-recipient-note:disabled {
  opacity: 0.6;
  cursor: default;
}

/* Section meta + info tooltip group - sits at the right
   end of the section head row. Reuses the .settings-info
   pattern from /account but namespaced .ta-info so the
   styling is local to /track. */
.ta-section-meta-group {
  display: inline-flex;
  align-items: center;
  gap: 6px;
}
.ta-info {
  position: relative;
  display: inline-flex;
  align-items: center;
}
.ta-info-btn {
  width: 16px;
  height: 16px;
  padding: 0;
  background: transparent;
  border: none;
  color: var(--fg-dim);
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  transition: color 120ms ease;
}
.ta-info-btn:hover,
.ta-info-btn:focus-visible { color: var(--ink); outline: none; }
.ta-info-btn svg { width: 16px; height: 16px; display: block; }
.ta-info-tip {
  position: absolute;
  /* Bubble sits above the icon. Right-anchored so it grows
     leftward and doesn't overflow the page gutter when the
     status sits at the right end of the section head. */
  bottom: calc(100% + 8px);
  right: 0;
  transform: translateY(2px);
  width: max-content;
  max-width: 280px;
  padding: 10px 12px;
  background: var(--ink);
  color: var(--bone);
  font-family: inherit;
  font-size: 0.78rem;
  font-weight: 400;
  text-transform: none;
  letter-spacing: 0;
  line-height: 1.45;
  border-radius: 8px;
  box-shadow: 0 8px 20px rgba(10, 10, 10, 0.18);
  pointer-events: none;
  opacity: 0;
  transition:
    opacity 160ms cubic-bezier(0.32, 0.72, 0, 1),
    transform 160ms cubic-bezier(0.32, 0.72, 0, 1);
  z-index: 5;
  white-space: normal;
}
.ta-info:hover .ta-info-tip,
.ta-info-btn:focus-visible ~ .ta-info-tip {
  opacity: 1;
  transform: translateY(0);
}
@media (prefers-reduced-motion: reduce) {
  .ta-info-tip { transition: none; }
}

/* Details (metadata) */
.ta-dl {
  margin: 0;
}
.ta-dl-row {
  display: grid;
  /* Label column shares its width with .actc-group so the
     Details and Activity value columns line up vertically
     into one consistent visual grid down the page. */
  grid-template-columns: 148px 1fr;
  gap: 24px;
  padding: 6px 0;
}
.ta-dl-row dt {
  margin: 0;
  font-family: var(--font-mono);
  font-weight: 500;
  font-size: 0.7rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--fg-dim);
  align-self: center;
}
.ta-dl-row dd {
  margin: 0;
  font-size: 0.95rem;
  color: var(--ink);
  align-self: center;
}
.ta-dl-val--mono {
  font-family: var(--font-mono);
  font-weight: 500;
  font-size: 0.85rem;
  letter-spacing: 0.02em;
}

/* ============== Sidebar - proof card ==============
   Right column of .ta-body. align-self: start anchors the
   card's TOP to the top of its grid cell, and margin-top: 14px
   matches .ta-sub's own margin-top so the certificate's top
   sits on the same baseline as the metadata sub-line on the
   left. As the user scrolls, sticky pins the card under the
   nav. */
.ta-aside {
  align-self: start;
  margin-top: 14px;
  position: sticky;
  top: calc(var(--nav-h) + clamp(16px, 2.5vw, 32px));
}
/* Proof card (Apple-A: stripped). One eyebrow, one focal
   sentence, hash-as-copy-button, one primary action. No stats -
   those duplicate what's in the main column's section headers. */
.ta-proof {
  position: relative;
  background: var(--chartreuse);
  color: var(--ink);
  border-radius: 24px;
  /* Tighter top padding than the rest - the eyebrow row
     (CERTIFICATE OF OWNERSHIP + mark) reads better closer
     to the upper edge, otherwise the chartreuse fill
     leaves a heavy unbalanced strip above the label. */
  padding: 18px 24px 24px;
  border: 1px solid transparent;
  transition: background 280ms ease, border-color 280ms ease;
}
/* ============== Credits card ==============
   Sits below the certificate in the sticky aside. Same shape
   as the certificate (24px radius, 24px padding) but always in
   the outline-only state - visually subordinate to the proof
   above. Title sits in the same eyebrow voice as
   "CERTIFICATE OF OWNERSHIP" so the two cards read as a pair.

   Listens to the collab panel's mounted markup via .ce-list
   etc. (defined in track-edit.css). Those rows were originally
   designed for the wider main column; the narrower aside makes
   them stack cleanly with no further overrides. */
.ta-credits {
  margin-top: 16px;
  background: transparent;
  border: 1px solid rgba(10, 10, 10, 0.2);
  border-radius: 24px;
  /* Tighter top than the sides - the eyebrow sits closer
     to the outline. Sides stay at 24px so the row contents
     (avatars, names, Remove) keep their rail alignment
     with the certificate above. Bottom is 18px so that
     combined with the .ce-row's 6px bottom padding, the
     last avatar sits 24px from the outline - matching the
     24px gap on the left. */
  padding: 18px 24px;
  color: var(--ink);
  /* Width is fixed by the sidebar grid - only the height
     needs to animate as collabs come and go and the inline
     pill morphs open/closed. CSS can't transition `height:
     auto`, but we can use the standard min/max gimmick: the
     intrinsic content height grows naturally and the
     transition runs against the same property's value. */
  transition: padding 280ms cubic-bezier(0.32, 0.72, 0, 1);
}
.ta-credits-head {
  /* Tighter title-to-body gap - the eyebrow clusters with
     the credit rows below as a single content block. Equal
     four-sided padding (24px) read as floaty / disconnected
     from the rows. */
  margin: 0 0 14px;
  display: flex;
  align-items: center;
}
.ta-credits-label {
  margin: 0;
  font-family: var(--font-mono);
  font-weight: 500;
  font-size: 0.66rem;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  color: var(--ink);
}
/* Body collapses alongside the head when the inline pill
   opens - see the :has() rule further down. Same max-height
   transition pattern. */
.ta-credits-body {
  max-height: 600px;
  overflow: hidden;
  transition:
    opacity 200ms ease,
    max-height 280ms cubic-bezier(0.32, 0.72, 0, 1),
    margin 280ms cubic-bezier(0.32, 0.72, 0, 1);
}
/* Strip the .ce-list / .ce-empty top border that the panel
   inherits from its old in-main-column home - the credits
   card already provides a visual container, so the extra
   rule reads as visual noise here. */
.ta-credits-body .ce-list,
.ta-credits-body .ce-empty {
  border-top: 0;
}
.ta-credits-body .ce-empty {
  padding: 0;
  border-bottom: 0;
}
.ta-credits-body .ce-row:last-child {
  border-bottom: 0;
  padding-bottom: 0;
}
.ta-credits-body .ce-row:first-child {
  padding-top: 0;
}

/* ============== Add-collaborator pill (empty state) ==============
   Standalone outline pill rendered in the aside INSTEAD of the
   credits card when a stakeholder loads a track with zero
   collaborators. Full width of the certificate above so the
   two cards/buttons share a consistent column edge; text
   centred via the inherited .secondary-button flex layout.
   Margin-top mirrors .ta-credits's margin-top so swapping the
   pill out for the card on click doesn't shift anything below. */
/* ============== Add-collaborator morph container ==============
   Single element that flips between two states:
     collapsed → outlined pill reading "Add collaborator"
     open      → outlined panel with library-style search input
                 + a 3-row result list

   The label and the panel are stacked inside; the label
   collapses (height + opacity) while the panel expands
   (grid-template-rows 0fr → 1fr + opacity). Outer container
   stays one DOM element so the user perceives a single pill
   growing taller, not two elements swapping.

   Same outline (--ta-aside outline tone, 0.2 opacity) as the
   certificate awaiting state and credits card so the sidebar
   reads as one visual family. */
.ta-credits-add {
  margin-top: 16px;
  background: transparent;
  border: 1px solid rgba(10, 10, 10, 0.2);
  border-radius: 24px;
  /* overflow: hidden clips the label + panel-inner during their
     height transitions so nothing escapes the rounded outline. */
  overflow: hidden;
}
/* Inline pill - standalone trigger above the credits card.
   No morph panel; the click toggles a search takeover INSIDE
   the credits card below (see .ta-credits.is-searching). The
   pill keeps its calm 999px outline through both states. */
.ta-credits-add--inline {
  margin-top: 16px;
  border: 1px solid rgba(10, 10, 10, 0.2);
  border-radius: 999px;
  width: 100%;
  overflow: hidden;
  transition: border-color 200ms ease;
}
@media (hover: hover) {
  .ta-credits-add--inline:hover { background: rgba(10, 10, 10, 0.04); }
}
/* Active state - when the takeover is open, lift the pill's
   border to full ink so the user reads it as the active
   "session". A second click on the pill closes. */
.ta-aside:has(.ta-credits.is-searching) .ta-credits-add--inline {
  border-color: var(--ink);
}
.ta-credits-add--inline .ta-credits-add-trigger {
  display: block;
  width: 100%;
  background: transparent;
  border: 0;
  cursor: pointer;
  font: inherit;
  text-align: center;
  padding: 13px 22px;
  color: var(--ink);
  font-weight: 600;
}

/* ============== Card-takeover animation ==============
   Two stacked sub-views inside .ta-credits - list (default)
   and search (.is-searching). The inactive row is removed
   from layout via display: none (so card height = padding +
   ACTIVE row's natural height). The JS handler in track.js
   measures the current and target heights and pins
   max-height on .ta-credits before/after the toggle, so the
   browser interpolates between two definite px values. The
   active row also cross-fades via opacity. */
.ta-credits {
  /* overflow: hidden so the height clip doesn't reveal the
     inactive row's content during the transition; pinned
     `height` (in JS) transitions over 320ms with the same
     easing used elsewhere in the sidebar.
     Border transitions over 200ms so the outline shift to
     solid ink (when entering search mode) reads as part of
     the same gesture as the height swap.
     Why `height` and not `max-height`: max-height only
     constrains in one direction. When closing (large →
     small), the active row swaps to the smaller list and
     the card's actual height = min(content, max-height) -
     the card snaps to the new smaller content size BEFORE
     max-height can transition. Pinning `height` directly
     forces the card to a definite px size regardless of
     content, so transitions interpolate correctly in both
     directions. */
  overflow: hidden;
  transition:
    height 320ms cubic-bezier(0.32, 0.72, 0, 1),
    border-color 200ms ease;
}
/* Active "session" outline - solid ink while the takeover
   is open. Same affordance signal as the .ta-credits-add--inline
   pill's border bump. */
.ta-credits.is-searching {
  border-color: var(--ink);
}
.ta-credits-grid {
  display: block;
  position: relative;
}
/* Both rows are ALWAYS in the DOM as display: block - the
   INACTIVE one is position: absolute so it's lifted out of
   layout flow (card height = active row's height). Why both
   rendered: the search input must be focusable IMMEDIATELY
   when the pill is clicked, which requires display: block.
   focus() on a display: none element is a no-op. */
.ta-credits-row {
  /* Transition set inline by the JS swap handler so each
     phase has its own duration / easing. */
  /* will-change alone promotes each row to its own
     compositor layer without conflicting with the JS-set
     translateX (a CSS `transform: translateZ(0)` here would
     get overwritten by `style.transform = "translateX(N)"`
     in animateSwap, knocking the row off its layer mid-
     animation and causing a visible teleport). */
  will-change: transform, opacity;
}
.ta-credits-row--search {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  opacity: 0;
  pointer-events: none;
}
.ta-credits.is-searching .ta-credits-row--list {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  opacity: 0;
  pointer-events: none;
}
.ta-credits.is-searching .ta-credits-row--search {
  position: static;
  opacity: 1;
  pointer-events: auto;
}

/* Third state - confirm-remove. Same lift-out + slide pattern
   as the search row; lives in the same .ta-credits-grid so
   the animator in track.js can morph between any two of
   {list, search, confirm} with one generalised translateX
   slide. Replaces the legacy <dialog id="ta-confirm-dialog">
   popup that the X button used to open - the confirm UI is
   now inline within the same card frame, no backdrop. */
.ta-credits-row--confirm {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  opacity: 0;
  pointer-events: none;
  will-change: transform, opacity;
}
.ta-credits.is-confirming .ta-credits-row--list,
.ta-credits.is-confirming .ta-credits-row--search {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  opacity: 0;
  pointer-events: none;
}
.ta-credits.is-confirming .ta-credits-row--search {
  /* Override the search row's default left:0 / right:0 lift
     so when confirm takes over from list the search row stays
     fully lifted out (no flash). */
  transform: translateX(calc(100% + 24px));
}
.ta-credits.is-confirming .ta-credits-row--confirm {
  position: static;
  opacity: 1;
  pointer-events: auto;
}
/* Active-takeover outline darkens to ink in confirm too -
   mirrors .is-searching. */
.ta-credits.is-confirming {
  border-color: var(--ink);
}

/* Confirm-body prose + actions row. Body is the question; the
   collaborator's name is interpolated as <strong> by JS at
   open time. Actions are full-card-width 50/50 - destructive
   (Remove) balanced against recoverable (Cancel), iOS / two-
   button-row convention for binary commit-or-back pairs. */
.ta-credits-confirm-body {
  margin: 0 0 18px;
  font-size: 0.92rem;
  line-height: 1.5;
  color: var(--ink);
}
.ta-credits-confirm-body strong {
  font-weight: 600;
}
.ta-credits-confirm-actions {
  display: flex;
  gap: 10px;
}
.ta-credits-confirm-cancel,
.ta-credits-confirm-ok {
  appearance: none;
  flex: 1;
  font-family: inherit;
  font-weight: 500;
  font-size: 0.9rem;
  line-height: 1;
  min-height: 44px;
  padding: 0 18px;
  border-radius: 999px;
  cursor: pointer;
  border: 1px solid rgba(10, 10, 10, 0.2);
  background: transparent;
  color: var(--ink);
  transition:
    background-color 120ms ease,
    border-color 120ms ease,
    color 120ms ease;
}
.ta-credits-confirm-ok {
  background: var(--ink);
  color: var(--bone);
  border-color: var(--ink);
}
@media (hover: hover) {
  .ta-credits-confirm-cancel:hover {
    border-color: var(--ink);
    background: rgba(10, 10, 10, 0.04);
  }
  .ta-credits-confirm-ok:hover {
    background: #1a1a1a;
  }
}

/* Add pill dims (instead of hides) while the card is in
   confirm state - the inline Cancel button below handles
   back-out, so the pill doesn't need to double as a cancel
   affordance like it does in search mode. Dim + non-
   interactive keeps the column layout stable so the credits
   card doesn't visually jump when entering/leaving confirm,
   and signals "this is paused" without removing it. */
.ta-aside:has(.ta-credits.is-confirming) .ta-credits-add--inline {
  opacity: 0.4;
  pointer-events: none;
  transition: opacity 200ms ease;
}

@media (prefers-reduced-motion: reduce) {
  .ta-credits-grid,
  .ta-credits-row { transition: none; }
}

/* Collapsed-state trigger - full-width invisible button
   covering the pill so click anywhere opens it. The label
   text inside is what users see at rest. */
.ta-credits-add-trigger {
  appearance: none;
  display: block;
  width: 100%;
  background: transparent;
  border: 0;
  padding: 0;
  cursor: pointer;
  font: inherit;
  color: inherit;
  text-align: center;
  /* Height + padding animate to 0 when the pill opens. */
  transition:
    background 120ms ease,
    padding 280ms cubic-bezier(0.32, 0.72, 0, 1),
    max-height 280ms cubic-bezier(0.32, 0.72, 0, 1);
  padding: 13px 22px;
  max-height: 60px;          /* generous cap covering label + padding */
}
@media (hover: hover) {
  .ta-credits-add-trigger:hover {
  background: rgba(10, 10, 10, 0.05);
}
}
.ta-credits-add-label {
  display: inline-block;
  font-weight: 600;
  font-size: 0.95rem;
  letter-spacing: -0.005em;
  color: var(--ink);
  /* Fades first (faster than the height collapse) so the text
     disappears before the pill snaps shorter. */
  transition: opacity 140ms ease;
}

/* Expanded-state panel - collapsed via CSS grid 0fr → 1fr so
   it animates height from 0 to its natural content height
   without needing a JS-measured max value. */
.ta-credits-add-panel {
  display: grid;
  grid-template-rows: 0fr;
  transition: grid-template-rows 280ms cubic-bezier(0.32, 0.72, 0, 1);
}
.ta-credits-add-panel-inner {
  /* min-height: 0 + overflow: hidden are required for the
     0fr → 1fr grid trick to actually compress to zero. */
  min-height: 0;
  overflow: hidden;
  /* Padding fades in WITH the content; starts at 0 so the
     panel has zero height when collapsed. */
  padding: 0 16px;
  opacity: 0;
  transition:
    padding 280ms cubic-bezier(0.32, 0.72, 0, 1),
    opacity 200ms ease;
}

/* Open state - toggle by track.js */
.ta-credits-add.is-open {
  /* No outer change; everything happens via the children. */
}
.ta-credits-add.is-open .ta-credits-add-trigger {
  /* Collapse the trigger: zero padding + zero max-height.
     Pointer-events off so a stray click on the now-invisible
     button doesn't re-trigger anything. */
  padding-top: 0;
  padding-bottom: 0;
  max-height: 0;
  pointer-events: none;
  cursor: default;
}
@media (hover: hover) {
  .ta-credits-add.is-open .ta-credits-add-trigger:hover {
  background: transparent;
}
}
.ta-credits-add.is-open .ta-credits-add-label {
  opacity: 0;
  transition: opacity 120ms ease;
}
.ta-credits-add.is-open .ta-credits-add-panel {
  grid-template-rows: 1fr;
}
.ta-credits-add.is-open .ta-credits-add-panel-inner {
  padding: 16px;
  opacity: 1;
  /* Delay opacity fade-in so it kicks off AFTER the height
     animation finishes - text appears in an already-grown
     box, not mid-grow. */
  transition:
    padding 280ms cubic-bezier(0.32, 0.72, 0, 1),
    opacity 200ms ease 220ms;
}

@media (prefers-reduced-motion: reduce) {
  .ta-credits-add-trigger,
  .ta-credits-add-label,
  .ta-credits-add-panel,
  .ta-credits-add-panel-inner {
    transition: none;
  }
}

/* ---- Library-style search field ----
   Mirrors `.library-search` from /library so the two surfaces
   share one visual language. Search icon on the left + pill
   input + soft hover/focus background fill. */
.ta-credits-search {
  position: relative;
  display: block;
}
.ta-credits-search-icon {
  position: absolute;
  left: 14px;
  top: 50%;
  transform: translateY(-50%);
  width: 18px;
  height: 18px;
  color: var(--ink);
  opacity: 0.58;
  pointer-events: none;
  transition: opacity 120ms ease;
}
.ta-credits-search:focus-within .ta-credits-search-icon { opacity: 1; }
.ta-credits-search-input {
  appearance: none;
  width: 100%;
  box-sizing: border-box;
  background: transparent;
  color: var(--ink);
  border: 1px solid var(--border-strong);
  border-radius: 999px;
  padding: 11px 16px 11px 40px;
  font-family: inherit;
  font-weight: 500;
  font-size: 0.95rem;
  line-height: 1;
  outline: none;
  appearance: none;
  -webkit-appearance: none;
  min-height: 0;
  transition: border-color 120ms ease, background 120ms ease;
}
/* Mobile: pin to a true 16px so iOS Safari doesn't auto-zoom
   the page on focus (same fix as the library search input).
   scroll-margin-top clears the fixed nav + iOS safe-area so
   scrollIntoView({block:"start"}) parks the input just under
   the nav rather than behind it — important when the
   on-screen keyboard pops up and the dropdown results below
   need room above it. */
@media (max-width: 720px) {
  .ta-credits-search-input {
    font-size: 16px;
    scroll-margin-top: calc(var(--nav-h) + env(safe-area-inset-top) + 16px);
  }
}
.ta-credits-search-input::placeholder {
  color: var(--fg-dim);
}
@media (hover: hover) {
  .ta-credits-search-input:hover { background: rgba(10, 10, 10, 0.04); }
}
.ta-credits-search-input:focus {
  border-color: var(--ink);
  background: rgba(10, 10, 10, 0.04);
}

.ta-credits-search-list {
  margin-top: 12px;
  display: flex;
  flex-direction: column;
  gap: 4px;
  /* min-height reserves room for ~3 result rows so the card
     opens at its FINAL height the moment the user clicks
     "Add collaborator" - no flicker / second-frame jump
     when results land after the fetch returns. (Each row is
     ~52px: avatar 40 + 6/6 padding; 3 rows × 52 + 2 × 4 gap
     = 164px.) Empty-state copy sits at the top of this
     reserved area; the height is the same whether results
     are loading, present, or absent.

     Cap height so a long result list doesn't blow out the
     sticky aside (max 3 results from the server anyway). */
  min-height: 164px;
  max-height: 320px;
  overflow-y: auto;
}
.ta-credits-search-row {
  /* No padding on the row - the avatar lines up with the
     search input's left edge, the Add button with its right.
     The search input's pill border defines the left/right
     anchors for the column visually; the rows track those.
     Pointer cursor across the whole row signals it's
     clickable everywhere, not just the small Add button. */
  display: grid;
  grid-template-columns: 40px minmax(0, 1fr) auto;
  align-items: center;
  gap: 12px;
  padding: 6px 0;
  border-radius: 12px;
  cursor: pointer;
}
.ta-credits-search-avatar {
  display: inline-flex;
  width: 40px;
  height: 40px;
  border-radius: 999px;
  background: var(--chartreuse);
  color: var(--ink);
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  overflow: hidden;
}
.ta-credits-search-avatar svg { display: block; }
.ta-credits-search-meta { min-width: 0; }
.ta-credits-search-name {
  margin: 0;
  font-size: 0.875rem;
  font-weight: 600;
  /* Tight line-height pulls the @username row right up under
     the artist name (was ~1.5 default, leaving a visible
     baseline gap). 1.2 is the minimum that still avoids
     descender clipping on a sans body font. */
  line-height: 1.2;
  color: var(--ink);
  letter-spacing: -0.005em;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.ta-credits-search-handle {
  /* @username - sits under the display name in the same
     body font (not mono). Small margin-top adds a breath of
     leading between the two lines so the name doesn't sit
     directly on top of the handle's baseline. */
  margin: 2px 0 0;
  font-family: inherit;
  font-weight: 500;
  font-size: 0.78rem;
  line-height: 1.2;
  letter-spacing: -0.005em;
  color: var(--ink);
  opacity: 0.55;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  transition: opacity 120ms ease;
}
@media (hover: hover) {
  /* Hovering anywhere on the row pulls the @username up to
   full ink - reinforces that the whole row is the hit
   target (not just the Add pill) and matches the same
   row-level "lights up the Add button" pattern below. */
.ta-credits-search-row:hover .ta-credits-search-handle {
  opacity: 1;
}
}
.ta-credits-search-add {
  appearance: none;
  border: 1px solid var(--ink);
  background: transparent;
  border-radius: 999px;
  padding: 5px 12px;
  font-family: inherit;
  font-weight: 500;
  font-size: 0.78rem;
  letter-spacing: -0.005em;
  color: var(--ink);
  cursor: pointer;
  transition: background 120ms ease, color 120ms ease;
}
@media (hover: hover) {
  .ta-credits-search-add:hover:not(:disabled),
/* Row-level hover also lights up the Add button - gives the
   whole row an "actionable" feel without needing the user to
   reach the small button itself. */
.ta-credits-search-row:hover .ta-credits-search-add:not(:disabled) {
  background: var(--ink);
  color: var(--bone);
}
}
.ta-credits-search-add:disabled {
  opacity: 0.5;
  cursor: default;
}
.ta-credits-search-empty {
  margin: 0;
  max-width: 18ch;
  font-weight: 400;
  font-size: 0.82rem;
  line-height: 1.35;
  letter-spacing: -0.005em;
  color: var(--ink);
  opacity: 0.55;
  text-align: center;
  text-wrap: balance;
}
/* Center the empty-state copy in the reserved 164px result-
   list area when it's the ONLY child (no result rows yet).
   The :has() selector flexes the list to centre on both axes
   only in the empty state; once result rows render, the list
   reverts to its default top-aligned column flow.

   The translateY pulls the text up so it lands in the visual
   centre of the WHOLE card, not just the result-list region -
   roughly half the height of the head row + search pill + gap
   sitting above this list (~84px). */
.ta-credits-search-list:has(> .ta-credits-search-empty:only-child) {
  justify-content: center;
  align-items: center;
}
.ta-credits-search-list:has(> .ta-credits-search-empty:only-child)
  > .ta-credits-search-empty {
  transform: translateY(-42px);
}

/* Mivia mark - small ink squiggle on the right of the head
   row, vertically centered with the eyebrow via the parent's
   align-items: center. */
.ta-proof-mark {
  width: 36px;
  height: auto;
  opacity: 0.85;
  pointer-events: none;
  user-select: none;
  flex-shrink: 0;
}
/* Card head - eyebrow on the left, brand mark on the right.
   Flex row + align-items: center keeps both vertically aligned
   on their typographic centers no matter how the eyebrow text
   wraps or the mark is sized. */
.ta-proof-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin: 0 0 4px;
}
.ta-proof-label {
  margin: 0;
  font-family: var(--font-mono);
  font-weight: 500;
  font-size: 0.66rem;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  color: var(--ink);
}
/* Anchoring (partial) state - outline-only, no fill, matching
   the 1px border weight of the .te-pill-add tag button. The
   chartreuse is reserved for the "anchored" state where the
   proof is complete; until then the card reads as a placeholder
   waiting to fill in. */
.ta-proof--anchoring {
  background: transparent;
  border-color: rgba(10, 10, 10, 0.2);
}

/* Mivia ID line. Single-line inline "Mivia ID: 9F88..."
   sitting under the artist (creator) meta row, before the
   Bitcoin block. Standard 14px bottom margin shares the
   cert's vertical rhythm; word-break: break-all lets the
   hex wrap if a future longer ID overflows the column. The
   element is `hidden` by default in markup; cert.js +
   track.js's paintProof reveal + populate it when a manifest
   hash_prefix is present. */
.ta-proof-hash--inline {
  margin: 0 0 14px;
  font-family: var(--font-mono);
  font-weight: 500;
  font-size: 0.7rem;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  line-height: 1.3;
  color: var(--ink);
  word-break: break-all;
  /* Opacity lives on each CHILD (label span + anchor) rather
     than the parent here - mirrors the .ta-proof-btc rationale
     above: a parent opacity caps the child's effective opacity
     multiplicatively, so a hover state that wants to brighten
     past the parent's 0.5 floor would never actually appear. */
}
.ta-proof-hash-label {
  opacity: 0.5;
}
/* The hash portion of the Mivia ID line is an <a> to the
   public /c/<hash> page (same target the QR points to). Base
   opacity matches the label so the row reads as one tone;
   hover brightens the linked hash to 0.95 with the same
   underline + 1px thickness as a.ta-proof-btc-detail so both
   linked hex strings on the cert behave identically. */
a.ta-proof-hash-link {
  color: inherit;
  text-decoration: none;
  opacity: 0.5;
  transition: opacity 120ms;
}
@media (hover: hover) {
  a.ta-proof-hash-link:hover {
    opacity: 0.95;
    text-decoration: underline;
    text-decoration-thickness: 1px;
    text-underline-offset: 2px;
  }
}
/* Signed date - focal element of the card. The proof is
   primarily a "this happened on this date" statement; title
   and artist are supporting metadata below. 14px below to
   stay on the same uniform stride as every other block. */
.ta-proof-sentence {
  margin: 0 0 14px;
  font-size: clamp(1.125rem, 1.6vw, 1.375rem);
  font-weight: 600;
  letter-spacing: -0.02em;
  line-height: 1.25;
  color: var(--ink);
}
/* Generic two-line label/value meta block - same visual
   pattern as .ta-proof-btc. Used for Title and Artist so
   the certificate's WHO/WHAT reads with the same Wallet-pass
   restraint as its WHEN/WHERE. */
.ta-proof-meta {
  /* 14px below - uniform vertical rhythm between every
     mono block on the card (title, artist, BTC, info). */
  margin: 0 0 14px;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 4px;
  font-family: var(--font-mono);
  font-weight: 500;
  font-size: 0.7rem;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  line-height: 1.3;
  color: var(--ink);
}
.ta-proof-meta-label,
.ta-proof-meta-value {
  opacity: 0.5;
  word-break: break-word;
}

/* Bottom area - flex row: BTC + verify-wrap (left column),
   QR (right column). align-items: flex-end is the load-bearing
   line: it forces both columns to end at the same vertical
   position, so the QR's bottom and the (i) button's bottom
   sit on the same line regardless of how many wrap-lines the
   BTC text has. Replaces an earlier absolute-positioning
   approach that was unreliable (parent sometimes extended to
   contain the absolute child, breaking alignment). */
.ta-proof-bottom {
  display: flex;
  align-items: flex-end;
  gap: 12px;
}
.ta-proof-bottom-left {
  flex: 1;
  min-width: 0;
}
/* Cert link - the QR is the entire affordance: clicking
   opens /c/<hash> in a new tab; scanning does the same
   from a phone. No hover opacity change - the QR's visual
   stability is what makes it read as a verifiable artifact. */
.ta-proof-cert {
  flex-shrink: 0;
  display: block;
  text-decoration: none;
  line-height: 0;
}
.ta-proof-cert-qr {
  width: 96px;
  height: 96px;
}
.ta-proof-cert-qr svg {
  width: 100%;
  height: 100%;
  display: block;
  shape-rendering: crispEdges;
}
/* qrcode-generator's createSvgTag emits a white background
   rect; override to transparent so the chartreuse card shows
   through. Ink-on-chartreuse contrast (~5:1) is well above the
   QR scanner floor (~3:1) so it stays scannable. */
.ta-proof-cert-qr svg rect[fill="white"] {
  fill: none;
}

/* Footer row - (i) disclosure trigger only. The QR is now
   absolute-positioned at the card's bottom-right corner
   (separate flex flow), so the footer just contains the
   info button. Right padding reserves space so the (i) text
   wraps cleanly to the QR's left edge if it ever overflows. */
.ta-proof-footer {
  display: flex;
  align-items: flex-end;
  gap: 12px;
}
/* ============== OpenTimestamps state subline ==============
   Sits directly under the "Signed [date]" focal sentence.
   Two stacked lines, Wallet-pass-style:
     - top:  status verb ("Anchoring to Bitcoin" /
             "Anchored on Bitcoin")
     - bot:  detail (ETA for partial, Block + tx link for
             upgraded). Slightly dimmer than the label so the
             verb reads first. */
.ta-proof-btc {
  /* No top margin (artist meta provides the 14px above it).
     14px bottom margin matches the rhythm of every other
     mono block on the card. Internal gap (between label
     and detail) also 14 for a single uniform stride between
     all mono elements top-to-bottom. */
  margin: 0 0 14px;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 14px;
  font-family: var(--font-mono);
  font-weight: 500;
  font-size: 0.7rem;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  line-height: 1.3;
  color: var(--ink);
  /* Opacity lives on each child rather than the parent so the
     hover state on the tx-hash link can rise *above* the
     0.5 baseline. CSS opacity is multiplicative across
     nesting - putting 0.5 on the parent caps every child's
     effective opacity at 0.5, so a child's own opacity: 1
     would only reach 0.5 effective. Per-child opacity sidesteps
     that. */
}
.ta-proof-btc-label,
.ta-proof-btc-detail {
  color: inherit;
  text-decoration: none;
  opacity: 0.5;
}
a.ta-proof-btc-detail {
  text-decoration: none;
  /* Allow the full 64-char txid to wrap mid-string instead
     of overflowing the card. Reserves the canonical hex
     value so users can scan or copy it.
     text-wrap: balance asks the browser to equalise line
     lengths when the hex wraps - without it, the first line
     fills greedily and the second tail comes out short.
     Falls back gracefully (no-op) on browsers that don't
     support the property yet. */
  word-break: break-all;
  text-wrap: balance;
  transition: opacity 120ms;
}
@media (hover: hover) {
  a.ta-proof-btc-detail:hover {
  opacity: 0.95;
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 2px;
}
}

/* Verify block - sits below the Bitcoin substatus. Single
   centered "How to verify" disclosure on the surface; the
   inset panel below holds the explainer + Download CTA when
   expanded. Card surface stays informational; action lives
   with its explanation. */
.ta-proof-verify {
  /* No top margin - the BTC block above already provides
     14px below itself. Cross-container margins don't
     collapse here (BTC + verify are siblings inside
     .ta-proof-bottom-left, but the bottom-left flex
     context still respects the BTC margin). 0 keeps the
     uniform 14px stride. */
  margin: 0;
}

/* "How it works" disclosure - styled as a quiet system label
   (mono uppercase) so it matches the .ta-proof-btc-label
   voice on the same card. No icon: in mono uppercase the
   label itself reads as the affordance.
   On hover it goes fully solid AND animates to a heavier
   weight via the variable-weight axis. NOTE: Geist Mono is
   currently loaded as static 500 only (per styles.css), so
   browsers will faux-bold the 600 - the transition will read
   as a slight snap rather than a smooth interpolation. To
   get true smooth weight animation, swap in the variable
   Geist Mono woff2 (covers 100–900 on the wght axis). */
.ta-proof-info {
  appearance: none;
  display: inline-flex;
  align-items: center;
  padding: 0;
  background: transparent;
  border: 0;
  color: var(--ink);
  opacity: 0.5;
  font-family: var(--font-mono);
  font-weight: 500;
  font-size: 0.7rem;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  line-height: 1.3;
  cursor: pointer;
  transition: opacity 200ms ease, font-weight 200ms ease;
}
@media (hover: hover) {
  .ta-proof-info:hover,
.ta-proof-info[aria-expanded="true"] {
  opacity: 1;
  font-weight: 600;
}
}

/* Disclosure - no separate container, just inline content
   below the (i) button. Container animates open via
   grid-template-rows: 0fr → 1fr (fits content's natural
   height with no max guess). Children stay opacity: 0 during
   the 280ms expansion, then fade in TOGETHER (paragraph +
   download pill) with a 280ms delay so neither one "wipes"
   visibly during expansion. */
.ta-proof-detail {
  display: grid;
  grid-template-rows: 0fr;
  transition: grid-template-rows 280ms ease;
}
.ta-proof-detail-inner {
  min-height: 0;
  overflow: hidden;
  text-align: left;
  /* IMPORTANT: no padding here. padding lives outside the
     content box, so even when grid-template-rows: 0fr
     collapses the row, the padding still contributes to the
     element's box-height. The download pill instead carries
     its own margin-bottom (below) for the open-state gap. */
}
.ta-proof-detail-inner p {
  margin: 14px 0;
  font-family: inherit;
  font-weight: 500;
  font-size: 0.78rem;
  line-height: 1.5;
  letter-spacing: -0.005em;
  text-transform: none;
  color: var(--ink);
  opacity: 0;
  transition: opacity 240ms ease;
}
.ta-proof-detail-inner a {
  color: var(--ink);
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 2px;
  font-weight: 600;
}
/* Solid ink pill - full-width below the explainer. Text
   colour matches the certificate's background so the button
   reads as the brand's contrast pair regardless of card
   state:
     - upgraded (chartreuse card)  → chartreuse text
     - anchoring (bone card)       → bone text (overridden
       below in .ta-proof--anchoring scope)
   !important defeats any global `a { color: inherit }`. */
.ta-proof-download {
  display: block;
  width: 100%;
  box-sizing: border-box;
  /* No bottom margin - the card's own padding-bottom (24px)
     gives the gap from button to card edge. That matches
     the 24px card padding-left/right that frames the
     button horizontally, so the button is symmetrically
     inset on all three sides. */
  margin: 0;
  padding: 11px 22px;
  background: var(--ink);
  color: var(--chartreuse) !important;
  border: 0;
  border-radius: 999px;
  font-family: inherit;
  font-weight: 500;
  font-size: 0.9rem;
  letter-spacing: -0.005em;
  text-align: center;
  text-decoration: none !important;
  cursor: pointer;
  white-space: nowrap;
  opacity: 0;
  /* Only opacity transitions - the button has no hover state
     (the user wanted it visually static), so the background
     never changes after the initial render. */
  transition: opacity 240ms ease;
}
/* Anchoring state - the card is bone (transparent +
   ink outline), so the Download pill text switches to bone
   to match the card's background. Same brand-pair principle
   applied to a different card colour. */
.ta-proof--anchoring .ta-proof-download {
  color: var(--bone) !important;
}

/* When the disclosure opens, fade BOTH the paragraph and the
   download pill in after the container expansion is done
   (delay = expansion duration). */
.ta-proof-detail.is-open {
  grid-template-rows: 1fr;
}
.ta-proof-detail.is-open .ta-proof-detail-inner p {
  opacity: 1;
  transition: opacity 240ms ease 280ms;
}
.ta-proof-detail.is-open .ta-proof-detail-inner .ta-proof-download {
  opacity: 1;
  transition: opacity 240ms ease 280ms;
}

/* ============== Mobile ============== */
@media (max-width: 880px) {
  /* Flatten the two wrappers (.ta-body-main + .ta-aside) via
     display: contents and re-stack their grandchildren with
     explicit `order` values so the certificate + credits stack
     slot in between the tags row and the leak-tracking
     sections. Desktop's 2-col layout untouched.
     Order rhythm:
       1  sub / eyebrow line
       2  hero tags row
       3  CERTIFICATE OF OWNERSHIP
       4  Add collaborator + CREDITS card
       5  .ta-main (leak tracking + Details + Activity) */
  .ta-body {
    display: flex;
    flex-direction: column;
    /* row-gap: 0 — gaps controlled per-child via margin-top
       below so each pair can have its own rhythm. A blanket
       row-gap would lock every neighbour to the same value
       AND stack on top of any explicit margin, defeating
       per-item tightening. */
    row-gap: 0;
    column-gap: 0;
  }
  .ta-body-main,
  .ta-aside { display: contents; }
  /* Two-line clamp on the title for phones — desktop's
     single-line + ellipsis crops too aggressively at narrow
     widths. -webkit-line-clamp + line-clamp give a 2-line
     cap with native ellipsis on overflow. white-space: normal
     reopens the wrap that the desktop `nowrap` had closed.
     `overflow-wrap: anywhere` forces a break on an oversize
     single word — without it, an unbreakable string just
     extends past the container edge with no ellipsis at all,
     since line-clamp's ellipsis only fires once the content
     exceeds the clamp line count. With anywhere wrap, the
     word breaks at any character and the ellipsis lands at
     the visible truncation point. */
  .ta-title {
    white-space: normal;
    overflow: hidden;
    overflow-wrap: anywhere;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    line-clamp: 2;
    -webkit-box-orient: vertical;
  }
  .ta-sub          { order: 1; margin-top: 16px !important; }
  .ta-hero-tags    { order: 2; margin-top: 16px !important; }
  .ta-proof {
    order: 3;
    margin-top: 32px !important;
    /* Same full-width treatment as the credits stack — the
       certificate card stretches edge-to-edge of the page
       content area regardless of which state (signed,
       anchoring, upgraded) the card is currently in. */
    width: 100%;
    max-width: none;
    margin-left: 0;
    margin-right: 0;
    align-self: stretch;
    box-sizing: border-box;
  }
  .ta-credits-add,
  .ta-credits-add--inline { order: 4; margin-top: 24px !important; }
  .ta-credits      { order: 4; margin-top: 16px !important; }
  .ta-main         { order: 5; margin-top: 48px !important; }
  /* Card internal-padding trim. Most of the perceived gap
     between Tags ↔ Certificate and Add-collaborator ↔ Credits
     is actually the cards' own top/bottom padding above the
     eyebrow / below the last row, not margin between siblings.
     Tighten on mobile so the content-to-content distance
     matches the smaller box-to-box distance. */
  .ta-proof { padding-top: 14px; padding-bottom: 16px; }
  .ta-credits { padding-top: 12px; padding-bottom: 12px; }
  .ta-credits-add--inline .ta-credits-add-trigger { padding-block: 10px; }
  /* Full-width treatment for the credits stack — desktop
     sidebar styling on these elements doesn't carry a width,
     so make it explicit on phones so the card matches the
     certificate above and the leak-tracking section below. */
  .ta-credits-add,
  .ta-credits-add--inline,
  .ta-credits {
    width: 100%;
    max-width: none;
    margin-left: 0;
    margin-right: 0;
    align-self: stretch;
    box-sizing: border-box;
  }
  .ta-dl-row { grid-template-columns: 1fr; gap: 4px; padding: 12px 0; }

  /* Leak-tracking recipient row: stack name → note input →
     created-date vertically so the note input runs the full
     column width on a phone instead of the desktop side-by-
     side 1fr / 1.6fr grid. display: contents on the inner
     wrapper lets the name + date land as direct flex children
     of the row so `order` reaches them. */
  .ta-recipients li {
    display: flex;
    flex-direction: column;
    align-items: stretch;
    gap: 8px;
    /* More breathing room between adjacent recipients on
       mobile — desktop kept its tighter 8px because the
       side-by-side layout there already separates rows
       horizontally. */
    padding: 20px 0;
  }
  /* Tighten the gap between every section title and its
     content (Leak Tracking, Details, Activity). Desktop's
     20px reads as breath; on a phone it stacks awkwardly
     above an already-padded first row. */
  .ta-section-head { margin-bottom: 8px; }
  /* Activity entry: collapse the desktop 148px label + 1fr
     content side-by-side grid into a single column so the
     date eyebrow sits on top and the event sentence(s) flow
     beneath it — more legible on a phone where the 148px
     label column was eating ~40% of the width. */
  .actc-group {
    grid-template-columns: 1fr;
    gap: 4px;
  }
  .ta-recipient-main { display: contents; }
  /* On phones: hide the editable note input entirely (composing
     a note is a desk task), and instead show any already-saved
     note as italic text ABOVE the email. Order:
       note (italic, only if non-empty) → email → date. */
  .ta-recipient-note { display: none; }
  .ta-recipient-note-display {
    display: block;
    order: 1;
    margin: 0;
    /* Same eyebrow voice as .actc-when (activity dates):
       mono, uppercase, tracked, dimmed ink. Reads as a
       metadata stamp on the recipient rather than the
       desktop italic body-text feel. */
    font-family: var(--font-mono);
    font-weight: 500;
    font-size: 0.7rem;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--ink);
    opacity: 0.55;
  }
  /* Same voice as the Details section's value column (the
     "Watermark & fingerprint" line under PROTECTION): body
     sans, regular weight, full-ink colour. Reads as the
     primary identifier rather than a bolded header. */
  .ta-recipient-name {
    order: 2;
    margin-bottom: 0;
    font-family: var(--font);
    font-weight: 400;
    font-size: 1rem;
    color: var(--ink);
    overflow: hidden;
    text-overflow: ellipsis;
  }
  .ta-recipient-meta { order: 3; }
}
