/* Collibra case only — short sticky chapters inside the shared Broadsheet system. */

.case--collibra { overflow-x: clip; }


/* The ledger, the interlude and the chapter head all moved to the block
   vocabulary in styles.css (TASK-67); Collibra's values are the ones that were
   promoted, so nothing here had to be kept to hold its look. What stays is the
   stepper's own service label, which the stepper task will merge. */
.case--collibra .sticky-chapter__number {
  font-family: var(--font-label);
  font-size: var(--fs-caption);
  letter-spacing: var(--tr-label);
  text-transform: uppercase;
}

/* gap живёт в chapter → у следующего блока обнуляем верхний отступ */
.case--collibra .chapter + .interlude { padding-top: 0; }
.case--collibra .chapter + .sticky-chapter > .sticky-chapter__header { padding-top: 0; }

.case--collibra .sticky-chapter {
  max-width: 1680px;
  margin-inline: auto;
  background: var(--white);
  /* the 1680 shell keeps the layout; the ground still has to reach the window
     edges above it, or the paper run shows two white margins (visible at
     browser zoom below 100%). .bleed-ground in styles.css does that without
     touching a single box. */
  --bleed-ink: var(--white);
}
.case--collibra .sticky-chapter--paper { --bleed-ink: var(--ground-paper); }
.case--collibra .sticky-chapter__header { padding-block: clamp(32px, 5vw, 72px); }
.case--collibra .sticky-chapter__header h2 {
  max-width: 15ch;
  margin: var(--sp-3) 0 var(--sp-4);
  font-family: var(--font-display);
  font-size: var(--fs-h2);
  line-height: var(--lh-tight);
  letter-spacing: var(--tr-tight);
  text-transform: uppercase;
}
.case--collibra .sticky-chapter__header > p { max-width: 64ch; margin: 0; color: var(--text-muted); }
.case--collibra .sticky-chapter__number { color: var(--accent); }
.case--collibra .sticky-chapter__stage { display: none; }
.case--collibra .sticky-chapter__steps {
  max-width: var(--maxw);
  margin-inline: auto;
  padding: 0 var(--gutter) var(--sp-8);
}
.case--collibra .sticky-step {
  padding: var(--sp-7) 0;
  border-top: var(--rule-hair) solid var(--rule-muted);
}
.case--collibra .sticky-step__index {
  display: block;
  margin-bottom: var(--sp-3);
  color: var(--accent);
  font-family: var(--font-label);
  font-size: var(--fs-caption);
  letter-spacing: var(--tr-label);
  text-transform: uppercase;
}
.case--collibra .sticky-step h3 {
  max-width: 15ch;
  margin: 0 0 var(--sp-4);
  font-family: var(--font-display);
  font-size: var(--fs-h3);
  line-height: var(--lh-head);
  text-transform: uppercase;
}
.case--collibra .sticky-step > p { max-width: 50ch; margin: 0; color: var(--text-muted); }
.case--collibra .sticky-chapter__viewport {
  position: relative;
  overflow: hidden;
  border: var(--rule-hair) solid var(--rule);
  background: var(--bg-inset);
}
.case--collibra .sticky-chapter__viewport img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: contain;
  transform: translate(var(--pan-x, 0%), var(--pan-y, 0%)) scale(var(--zoom, 1));
  transform-origin: 50% 50%;
}

/* Dead since the exploration chapter was replaced (e58b5cb): .callout,
   .case-collibra__role-note, .crop-figure, .scheme, .detail-zooms and the old
   orange .anno pill had no markup left. Removed with TASK-67. The annotation
   that survives is the before/after one, and it is the vocabulary .anno now. */

@media (min-width: 900px) {
  .case--collibra .sticky-chapter.is-enhanced .sticky-chapter__body {
    display: grid;
    grid-template-columns: minmax(0, 1.42fr) minmax(340px, 0.58fr);
    align-items: start;
    border-top: var(--rule-hair) solid var(--rule);
  }
  .case--collibra .sticky-chapter.is-enhanced .sticky-chapter__stage {
    position: sticky;
    top: 0;
    display: grid;
    /* rows: [frame region] [caption]. The frame region is centred and JS sizes the frame to leave
       room for the caption + air, so a near-square frame + caption fit on a 13" laptop. */
    grid-template-rows: minmax(0, 1fr) auto;
    justify-items: center;
    align-content: center;
    min-width: 0;
    height: 100vh;
    margin: 0;
    padding: clamp(20px, 3vw, 48px);
    background: var(--bg-inset);
  }
  /* the frame region (row 2). Its size is driven by JS fit-scale, which reserves room for the
     caption + vertical air from the stage height — so a near-square frame + caption fit on 13". */
  .case--collibra .sticky-chapter.is-enhanced .sticky-chapter__viewport {
    grid-row: 1;
    min-height: 0;
    width: 100%;
    display: flex;
    align-items: center;
    justify-content: center;
  }
  .case--collibra .sticky-chapter.is-enhanced .sticky-chapter__stage figcaption {
    grid-row: 2;
    margin-top: var(--sp-3);
    color: var(--text-muted);
    font-family: var(--font-label);
    font-size: var(--fs-caption);
    letter-spacing: var(--tr-label);
    text-transform: uppercase;
  }
  .case--collibra .sticky-chapter.is-enhanced .sticky-chapter__steps {
    max-width: none;
    margin: 0;
    /* bottom tail: keep the stage pinned long enough for the LAST step to scroll up to the
       viewport centre (where people read) and hold there before the stage releases. */
    padding: 0 clamp(28px, 4vw, 64px) 42vh;
    border-left: var(--rule-hair) solid var(--rule);
  }
  .case--collibra .sticky-chapter.is-enhanced .sticky-step {
    display: flex;
    min-height: 76vh;
    flex-direction: column;
    justify-content: center;
    opacity: 0.28;
    transform: translateX(0);
    transition: opacity var(--dur) var(--ease), transform var(--dur) var(--ease);
  }
  .case--collibra .sticky-chapter.is-enhanced .sticky-step.is-active { opacity: 1; transform: translateX(8px); }
  .case--collibra .sticky-chapter.is-enhanced .sticky-chapter__viewport img {
    transition: transform 520ms var(--ease-out);
  }
}

@media (max-width: 899px) {
  .case--collibra .sticky-chapter__steps { padding-bottom: var(--sp-6); }
  .case--collibra .sticky-step { padding-block: var(--sp-6); }

  /* TASK-107 · the pinned scene on a phone. Only the solution chapter enhances
     here (case-collibra.js): the frame parks under the top edge and the four
     steps scroll beneath it, each driving the same live 1440×1039 stage the
     desktop uses. Before this the chapter had no picture at all below 900px —
     the frame had no height, so `solutionFit` scaled the stage to 0×0.

     The frame states its own proportion rather than a height, so the fit is the
     one line of geometry here; the steps stay tall enough that only one of them
     sits near the middle of the viewport, which is how stepper.js picks. */
  /* `display: none` is this sheet's default for the stage (:44) and the paper
     chapter clears the stage background further down (:573), so both are
     restated here — with [data-solution] carrying the specificity. The ground
     must stay opaque: the steps scroll underneath the pinned frame. */
  .case--collibra .sticky-chapter[data-solution].is-enhanced .sticky-chapter__stage {
    display: block;
    position: sticky;
    top: 0;
    z-index: 2;
    margin: 0 0 var(--sp-5);
    padding: var(--sp-4) var(--gutter);
    background: var(--ground-paper);
  }
  .case--collibra .sticky-chapter[data-solution].is-enhanced .sticky-chapter__viewport {
    aspect-ratio: 1440 / 1039;
    width: 100%;
  }
  .case--collibra .sticky-chapter[data-solution].is-enhanced .sticky-chapter__stage figcaption {
    margin-top: var(--sp-3);
    color: var(--text-muted);
    font-family: var(--font-label);
    font-size: var(--fs-caption);
    letter-spacing: var(--tr-label);
    text-transform: uppercase;
  }
  /* the reading zone under the frame: one step at a time near the middle */
  .case--collibra .sticky-chapter[data-solution].is-enhanced .sticky-step {
    min-height: 58vh;
    display: flex;
    flex-direction: column;
    justify-content: center;
  }
  /* the last step has to reach the middle before the frame releases */
  .case--collibra .sticky-chapter[data-solution].is-enhanced .sticky-chapter__steps {
    padding-bottom: 30vh;
  }
}

@media (prefers-reduced-motion: reduce) {
  .case--collibra *,
  .case--collibra *::before,
  .case--collibra *::after {
    scroll-behavior: auto !important;
    transition-duration: 0.01ms !important;
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
  }
}

/* ── Interactive before/after (ported from lab-collibra-top-switch, variant C) ── */
/* ── Before/after: what belongs to this case ──────────────────────────────
   The component is `deck` in styles.css. These are the parts that are about
   these two screenshots and nothing else: the plate's proportion, the stamp on
   the workbench, where each annotation sits on the image, and how the two
   sheets are thrown on the table. */

.case--collibra .deck__stage::after {
  content: "OUTPUT PORT / VIEWER";
  position: absolute;
  right: 18px;
  bottom: 16px;
  font-family: var(--font-label);
  font-size: var(--fs-micro);
  letter-spacing: var(--tr-label);
  writing-mode: vertical-rl;
}
.case--collibra .deck__sheets { aspect-ratio: 2880 / 2078; }

/* The label plate is ink; its number dims within the plate rather than taking
   the faint grey the canon gives it on paper. */
.case--collibra .deck__sheet .label--invert .label__no { color: var(--gray-5); }

/* Where each callout sits on its screenshot, as a share of the image. */
.case--collibra .anno--before-tabs { left: 20%; top: 31%; width: 36%; height: 4.5%; transform: translateY(8px); }
.case--collibra .anno--before-tabs .anno__copy { bottom: calc(100% + 16px); left: 0; }
.case--collibra .anno--before-tabs .anno__copy::before { bottom: -17px; height: 16px; }
.case--collibra .anno--before-routes { left: 18.7%; top: 36.7%; width: 23.5%; height: 4.6%; transform: translateY(8px); }
.case--collibra .anno--before-routes .anno__copy { top: calc(100% + 16px); left: 0; }
.case--collibra .anno--before-routes .anno__copy::before { top: -17px; height: 16px; }
.case--collibra .anno--after-cards { left: 18.3%; top: 31.2%; width: 43.5%; height: 59.3%; transform: translateY(8px); }
.case--collibra .anno--after-cards .anno__copy { bottom: calc(100% + 16px); left: 0; }
.case--collibra .anno--after-cards .anno__copy::before { bottom: -17px; height: 16px; }
.case--collibra .anno--after-preview { left: 63.8%; top: 19.7%; width: 35.3%; height: 70.9%; transform: translateY(8px); }
.case--collibra .anno--after-preview .anno__copy { top: calc(100% + 16px); right: 0; }
.case--collibra .anno--after-preview .anno__copy::before { top: -17px; height: 16px; }

/* Two sheets on a table: the inactive one is thrown back, tilted and drained. */
.case--collibra .deck__sheet[data-state="before"] { transform: translate(-5%, 4%) rotate(-2.2deg); filter: grayscale(1) contrast(.92); }
.case--collibra .deck__sheet[data-state="after"] { transform: translate(4%, -3%) rotate(1.6deg); }
.case--collibra .deck__sheets[data-active="before"] .deck__sheet[data-state="before"],
.case--collibra .deck__sheets[data-active="after"] .deck__sheet[data-state="after"] { z-index: 2; transform: translate(0, 0) rotate(0); filter: none; pointer-events: auto; }
.case--collibra .deck__sheets[data-active="before"] .deck__sheet[data-state="after"] { z-index: 1; transform: translate(7%, -4%) rotate(2.4deg) scale(.95); filter: grayscale(.75) contrast(.9); }
.case--collibra .deck__sheets[data-active="after"] .deck__sheet[data-state="before"] { z-index: 1; transform: translate(-7%, 5%) rotate(-2.8deg) scale(.95); filter: grayscale(1) contrast(.88); }

/* ── «Tap to compare»: подсказка, что стопку можно нажать ─────────────────
   До неё сигналом был только `cursor: pointer`, то есть на тач-экране —
   ничего, а мышью — лишь когда указатель уже на кадре.

   Органа управления здесь нет намеренно: нажимается сам кадр. Подсказка
   стоит на одной линии со штампом «01 BEFORE», но нарочно другой породы —
   штамп чернильный и говорит, ЧТО показано; подсказка бумажная с волосяной
   обводкой и говорит, ЧТО можно сделать. Две одинаковые плашки над кадром
   слились бы в одну кашу.

   Кегль намеренно не свой: 11px, выбранные на живой странице, — это ровно
   --fs-micro на ширине 1440, штатный размер служебного слоя. */
.case--collibra .deck__hint {
  position: absolute;
  left: 50%;
  /* Минус толщина рамки — не подгонка на глаз: штамп сидит внутри
     .deck__sheet, у которого рамка 2px, и его bottom:100% отсчитывается от
     padding-бокса. Подсказка живёт в .deck__sheets без рамки, и без поправки
     рельс расходится на 2px. */
  bottom: calc(100% - var(--rule-bold));
  z-index: 3;
  margin: 0;
  padding: 7px 12px;
  transform: translateX(-50%);
  border: var(--rule-hair) solid var(--rule);
  background: var(--ground-paper);
  color: var(--text);
  font-family: var(--font-label);
  font-size: var(--fs-micro);
  line-height: 1;
  letter-spacing: var(--tr-label);
  text-transform: uppercase;
  white-space: nowrap;
  /* Подпись — тоже мишень. Она лежит выше рамки кадра, то есть вне бокса
     .deck__sheets, и клик по ней в стопку не попадал бы; но в DOM она внутри
     .deck__sheets, поэтому событие всплывает до того же обработчика. Кто решит,
     что нажимать надо именно надпись, попадёт туда же, куда и по кадру. */
  cursor: pointer;
  user-select: none;
}

/* Сама плашка 27px в высоту — для пальца мало (в Light хит-зоны заданы по 44px).
   Мишень растим невидимо, не трогая вид плашки; вниз она заходит на кадр, но
   там тот же самый обработчик, так что конфликта нет. */
.case--collibra .deck__hint::after {
  content: "";
  position: absolute;
  inset: -9px -6px;
}

/* Отклик на наведение: задний лист выезжает из-под переднего на 4px и
   возвращается — объект показывает, что он живой, не прибегая к словам.
   Ховер ловит вся сцена, а не только картинка. */
.case--collibra .deck__stage:hover .deck__sheets[data-active="before"] .deck__sheet[data-state="after"] {
  transform: translate(7%, -4%) rotate(2.4deg) scale(.95) translate(4px, -2.4px);
}
.case--collibra .deck__stage:hover .deck__sheets[data-active="after"] .deck__sheet[data-state="before"] {
  transform: translate(-7%, 5%) rotate(-2.8deg) scale(.95) translate(-4px, 2.4px);
}

/* Подсказка движением короткая (канон: до 400 мс), сам переворот — 720 мс.
   Одна длительность на обоих сделала бы переворот дёрганым, поэтому кейсовые
   720 мс возвращаются классом на время самого переключения. */
.case--collibra .deck__sheet {
  transition: transform 260ms var(--ease-out), opacity 320ms var(--ease), filter 560ms var(--ease);
}
.case--collibra .deck__sheets.is-flipping .deck__sheet {
  transition: transform 720ms var(--ease-out), opacity 320ms var(--ease), filter 720ms var(--ease-out);
}

/* Выноски и подсказка делят один и тот же ховер и мешали друг другу: при
   наведении всплывали разом два колаута, сдвиг листа и подсказка. Колауты
   остаются доступны с клавиатуры — на focus-visible они по-прежнему всплывают
   (styles.css), а гасится только их появление от мыши. */
.case--collibra .deck__stage:hover .deck__sheet.is-active:hover .anno { opacity: 0; }

/* Движение — украшение, а не суть: под reduced-motion уходит целиком,
   подсказка словом остаётся. */
@media (prefers-reduced-motion: reduce) {
  .case--collibra .deck__stage:hover .deck__sheets[data-active="before"] .deck__sheet[data-state="after"] {
    transform: translate(7%, -4%) rotate(2.4deg) scale(.95);
  }
  .case--collibra .deck__stage:hover .deck__sheets[data-active="after"] .deck__sheet[data-state="before"] {
    transform: translate(-7%, 5%) rotate(-2.8deg) scale(.95);
  }
}

/* TASK-112 · below 900px the two sheets stop being a switch and become what they
   already are without JS: two captioned frames, one under the other.

   Why the switch goes: at 390px a sheet renders at 0.24 of the 1440 stand it was
   captured from — a 13px interface label lands at 3.1px — and the annotations are
   hidden at this width (TASK-107 P0-3). So the tap was toggling between two
   pictures nothing can be read in, while the same tap is needed for the
   fullscreen view (TASK-110). Light already tells before/after this way.

   Written at the same specificity as the state rules above, and after them, or
   the thrown-back transforms keep winning. */
@media (max-width: 899px) {
  .case--collibra .deck__sheets { display: grid; gap: 64px; cursor: auto; }
  .case--collibra .deck__sheets[data-active] .deck__sheet[data-state] {
    position: relative; inset: auto;
    z-index: auto;
    transform: none;
    filter: none;
    pointer-events: auto;
  }
  .case--collibra .deck__sheet > img { height: auto; object-fit: contain; }
  /* the stage no longer reserves a viewport-sized well for one pinned sheet */
  .case--collibra .deck__stage { min-height: 0; }
  /* Здесь стопка перестаёт быть переключателем, и нажимать нечего —
     подсказка «Tap to compare» на этой ширине соврала бы. */
  .case--collibra .deck__hint { display: none; }
  /* both texts belong to the reader now, each above its own frame */
  .case--collibra .deck__copy { visibility: visible; opacity: 1; transform: none; }
  .case--collibra .deck__copy-stack { display: grid; gap: var(--sp-6); }
  .case--collibra .deck__copy { grid-area: auto; }
}

/* The before/after title is the one place the case shouts; it keeps its own
   ceiling rather than the shared --fs-h2. */
.case--collibra .deck__copy .head--chapter { max-width: 11ch; margin-top: 16px; font-size: clamp(2.8rem, 4.1vw, 5rem); line-height: 0.9; letter-spacing: var(--tr-mega); }

@media (max-width: 860px) {
  .case--collibra .deck__copy .head--chapter { max-width: 13ch; font-size: clamp(3.2rem, 10vw, 5.2rem); }
}
@media (max-width: 560px) {
  .case--collibra .deck__copy .head--chapter { font-size: clamp(2.9rem, 13vw, 4.2rem); }
  .case--collibra .deck__stage::after { display: none; }
}
/* ════════════════════════════════════════════════════════════════════════
   TASK-45 · assembled case — styles transferred from the two lab listalki.
   Everything scoped to .case--collibra; the shared sticky-chapter component
   (@media min-width:900px above) is unchanged.
   ════════════════════════════════════════════════════════════════════════ */

/* Both chapters run frameless screens: drop the component's dark viewport
   border + inset fill (the scene provides its own paper/frame where needed). */
.case--collibra .sticky-chapter__viewport { position: relative; border: 0; background: transparent; overflow: hidden; }

/* ── Listalka №1 · exploration: screen beats + scheme beats on one stage ── */

/* Trim the stage's vertical padding so the frame sits higher (tuned in the lab). */
.case--collibra .sticky-chapter[data-explore].is-enhanced .sticky-chapter__stage {
  padding-block: clamp(16px, 1.7vw, 24px);
}
.case--collibra .stage-image-wrap { position: absolute; inset: 0; overflow: hidden; transition: opacity 300ms var(--ease); }
.case--collibra .stage-image-wrap img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: contain;
  transform: translate(var(--pan-x,0%), var(--pan-y,0%)) scale(var(--zoom,1)); transform-origin: 50% 50%;
  will-change: transform; }
/* Smooth camera-like zoom/pan. Beats the component's own 520ms rule
   (same specificity, later in the file). */
.case--collibra .sticky-chapter.is-enhanced .sticky-chapter__viewport .stage-image-wrap img {
  transition: transform 1100ms cubic-bezier(.33, 0, .12, 1); }
.case--collibra .sticky-chapter__stage[data-mode="scheme"] .stage-image-wrap { opacity: 0; }
.case--collibra .sticky-chapter__stage[data-mode="image"] .stage-image-wrap { opacity: 1; }

/* Schemes get a frame: the diagram reads as a bordered figure on paper. */
.case--collibra .stage-scheme {
  position: absolute; inset: 0;
  display: grid; place-items: center;
  padding: clamp(20px, 3vw, 40px);
  border: var(--rule-hair) solid var(--rule); background: var(--white);
  opacity: 0; visibility: hidden; pointer-events: none;
  transform: translateX(4%);
  transition: opacity 460ms var(--ease-out), transform 620ms var(--ease-out);
}
.case--collibra .stage-scheme.is-shown { opacity: 1; visibility: visible; }
.case--collibra .stage-scheme.is-in { transform: translateX(0); }
.case--collibra .stage-scheme svg { width: 100%; height: auto; max-height: 100%; }

/* Plate scheme (research beat = B2 hybrid): a fixed-width HTML block that scales
   as ONE image via transform:scale(var(--fit)) — not responsive reflow. */
.case--collibra .stage-scheme--plate { padding: clamp(12px, 2vw, 28px); background: var(--white); position: absolute; inset: 0; }
.case--collibra .fit-plate {
  position: absolute; top: 50%; left: 50%;
  width: var(--plate-w, 1120px);
  transform: translate(-50%, -50%) scale(var(--fit, 1));
  transform-origin: center center;
  will-change: transform;
}

/* B2 hybrid plate (tokens unchanged from the gallery lab). */
.case--collibra .hybrid-plate { border-block: 0; }
.case--collibra .hybrid-main { min-width: 0; }
.case--collibra .hybrid-mode-row { display: grid; grid-template-columns: 1fr 1fr; }
.case--collibra .hybrid-mode {
  position: relative; padding: var(--sp-3) var(--sp-4) var(--sp-2);
  color: var(--text-muted); font-family: var(--font-mono); font-size: var(--fs-micro);
  letter-spacing: var(--tr-label); text-align: center; text-transform: uppercase;
}
/* Symmetric underlines: CHOOSING accent (the emphasised phase), INSPECTING thin neutral. */
.case--collibra .hybrid-mode::after { content: ""; display: block; height: var(--rule-thin, 2px); margin-top: var(--sp-2); background: var(--rule-muted); }
.case--collibra .hybrid-mode:first-child::after { background: var(--accent); }
.case--collibra .hybrid-mode:first-child { color: var(--accent-text); }
.case--collibra .hybrid-phases { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); }
.case--collibra .hybrid-phase { min-width: 0; min-height: 14rem; padding: var(--sp-4); border-right: var(--rule-hair) solid var(--rule-muted); }
.case--collibra .hybrid-phase:last-child { border-right: 0; }
.case--collibra .hybrid-phase__number {
  display: block; color: var(--text); font-family: var(--font-display);
  font-size: var(--fs-figure); font-weight: var(--wt-med); letter-spacing: var(--tr-tight); line-height: .9;
}
/* The numeral marks the two hybrid phases and keeps --accent — a large mark, not a
   word. The word under it goes back to ink: this block sits on the paper ground,
   where neither #EF4D02 (3.25:1) nor #D24402 (4.07:1) reaches AA at 20–23px, and
   the numeral already carries the distinction (audit A8). */
.case--collibra .hybrid-phase:nth-child(-n + 2) .hybrid-phase__number { color: var(--accent); }
.case--collibra .hybrid-phase strong { display: block; margin-top: var(--sp-4); font-family: var(--font-display); font-size: var(--fs-lead); font-weight: var(--wt-med); text-transform: uppercase; }
.case--collibra .hybrid-phase small {
  display: block; max-width: 23ch; margin-top: var(--sp-3); padding-top: var(--sp-3);
  border-top: var(--rule-hair) solid var(--rule-muted); color: var(--text-muted);
  font-family: var(--font-label); font-size: var(--fs-caption); line-height: var(--lh-text);
}
.case--collibra .hybrid-canvas { padding: var(--sp-5) var(--sp-4) var(--sp-4); }
.case--collibra .hybrid-scheme { width: 100%; height: auto; display: block; }
.case--collibra .hybrid-scheme text { font-family: var(--font-mono); }
.case--collibra .hybrid-scheme .edge { fill: none; stroke: var(--rule); stroke-linejoin: round; }
.case--collibra .hybrid-scheme .edge--detour { stroke: var(--accent); }
.case--collibra .hybrid-scheme .edge--return { stroke: var(--accent); stroke-dasharray: 6 6; }
.case--collibra .hybrid-scheme .edge-head { fill: var(--rule); }
.case--collibra .hybrid-scheme .edge-head--detour { fill: var(--accent); }

/* Step chrome: the secondary source line. (.beat-kind retired with TASK-67 —
   dead CSS, its markup was gone.) */
.case--collibra .sticky-step__source { max-width: 50ch; margin: var(--sp-4) 0 0; color: var(--caption-ink);
  font-family: var(--font-label); font-size: var(--fs-caption); letter-spacing: var(--tr-label); line-height: 1.6; }

/* Fallback (no-JS / <900px): schemes AND screens render inline under their step. */
.case--collibra .step-inline-scheme { margin: var(--sp-6) 0 0; }
.case--collibra .step-inline-scheme svg { width: 100%; height: auto; padding: var(--edge-card-pad); border: var(--edge-card); background: var(--white); }
.case--collibra .step-inline-shot { margin: var(--sp-6) 0 0; }
.case--collibra .step-inline-shot__viewport { position: relative; aspect-ratio: 16 / 12; overflow: hidden; background: transparent; }
.case--collibra .step-inline-shot__viewport img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: contain;
  transform: translate(var(--pan-x,0%), var(--pan-y,0%)) scale(var(--zoom,1)); transform-origin: 50% 50%; }
.case--collibra .sticky-chapter.is-enhanced .step-inline-scheme,
.case--collibra .sticky-chapter.is-enhanced .step-inline-shot { display: none; }
@media (prefers-reduced-motion: reduce) {
  .case--collibra .stage-scheme { transition: none !important; transform: none !important; }
}

/* ══ Exploration schemes · one drawing system (TASK-65) ═══════════════════
   Four of the five schemes share one frame (viewBox 0 0 1000 800), one grid,
   one set of weights, one type scale. The fifth — the route plate on step
   04 — keeps its own frame and its own composition: Pasha asked for that
   drawing back as it was, so only its craft was lifted (see the plate's own
   note in the markup). Numbers below are viewBox units; the shared frame
   renders at 0.85 px per unit on a 1440 stage and 0.75 on a 1280 one, so
   the smallest label never drops below 11px — the size the old 9px mono
   captions failed at.

   TYPE     24u caption — what a zone is
            18u item    — a named thing (a field, a port)
            15u note    — an aside, and the label on a path
   STROKE   1.5u hairline — a zone's edge, the rule under an item
            3u   heavy    — the thing under discussion, and every accent
                            mark (a cut, a boundary, a path)
   COLOUR   --rule ink, --rule-muted for table rules, --text-faint for
            asides, --accent ONLY on a structural mark (a rule, an edge, a
            path) and only where something changed. Never on a word.
            The route plate predates this rule and keeps its own use of
            orange — the detour and the walk back.
   GRID     frame margin 48, captions on the 96 / 122 baselines, zones open
            at y≈150.
   NAMING   a thing that matters is a word with a rule under it. Anything
            that only needs to be counted, not read, is a bar (.bar): the
            fields below the two that moved up (06), and the page's own
            content around the widget (06, 07). One shape, one job — you
            count bars, you read words.
   NESTING  one shape means one thing inside a frame. A big rectangle is a
            page; the widget inside it must be told apart — either by a
            leader to the same zone drawn larger (06) or by page content
            running above and below it (07). Without that the widget just
            reads as a second sheet of paper. */
.case--collibra .sc { font-family: var(--font-mono); }
.case--collibra .sc text { fill: var(--rule); }
.case--collibra .sc .t-cap  { font-size: 24px; font-weight: 500; letter-spacing: 1.6px; }
.case--collibra .sc .t-item { font-size: 18px; letter-spacing: 1.1px; }
.case--collibra .sc .t-note { font-size: 15px; letter-spacing: 1px; fill: var(--text-faint); }
.case--collibra .sc .t-path { font-size: 15px; letter-spacing: 1px; fill: var(--rule); }
.case--collibra .sc .mid { text-anchor: middle; }
.case--collibra .sc .zone    { fill: none; stroke: var(--rule); stroke-width: 1.5px; }
.case--collibra .sc .subject { fill: none; stroke: var(--rule); stroke-width: 3px; }
.case--collibra .sc .rule      { stroke: var(--rule-muted); stroke-width: 1.5px; }
.case--collibra .sc .rule--ink { stroke: var(--rule); }
/* A leader tying a zone to the same zone drawn larger — the only way the
   reader can tell the big rectangle on the left is the widget from the
   page on the right, and not a second page. */
.case--collibra .sc .rule--lead { stroke: var(--text-faint); stroke-dasharray: 6 6; }
.case--collibra .sc .bar  { fill: var(--rule-muted); }
.case--collibra .sc .path { fill: none; stroke: var(--rule); stroke-width: 3px; }
.case--collibra .sc .path--back { stroke-dasharray: 10 8; }
.case--collibra .sc .head { fill: var(--rule); }
.case--collibra .sc .mark      { fill: none; stroke: var(--accent); stroke-width: 3px; }
.case--collibra .sc .mark--back { stroke-dasharray: 10 8; }
.case--collibra .sc .mark-head { fill: var(--accent); }

/* The route plate keeps its own composition and wording; only its line
   weight is brought onto the same 1.5 / 3 pair as the schemes above, so
   the two kinds of drawing do not read as two different hands.
   Order-dependent: overrides .hybrid-scheme .edge (:596) by coming later. */
.case--collibra .hybrid-scheme .edge { stroke-width: 2.5px; }
.case--collibra .hybrid-scheme #b2-layer-regions line { stroke-width: 1.5px; }

/* ── Listalka №2 · solution: live 1440×1039 Collibra stage (PNG chrome + live cards) ── */

/* The invisible limit box: CSS lays it out to fill the stage region; JS fits the
   1440×1039 screen inside it (--scale = min(w/1440, h/1039)). No fill/shadow. */
.case--collibra .sol-frame {
  --sel:#3f84ec; --scale:1;
  position:relative; width:100%; height:100%;
  display:flex; align-items:center; justify-content:center;
  background:transparent; border:0; overflow:hidden;
}
/* the fitted screen: the 1440×1039 stage scaled to the frame limits, centred, clipped */
.case--collibra .sol-screen { position:relative; width:var(--iw,1000px); height:var(--ih,721px);
  overflow:hidden; border-radius:0; background:#fff; }
/* the fixed 1440×1039 stage; ONE scale fits it into .sol-screen */
.case--collibra .sol-stage { position:absolute; top:0; left:0; width:1440px; height:1039px;
  transform-origin:top left; transform:scale(var(--scale,1)); }
/* guard: the component's viewport img rule (width/height:100%) is more specific
   than plain .sol-frame img → force every piece back to natural size.
   The panel is exempt from the transform reset: it is the one piece of chrome
   that moves (it slides in beside the narrowed field), and a blanket
   `transform:none !important` would silently cancel that slide. */
.case--collibra .sticky-chapter__viewport .sol-frame img:not(.sol-panel) {
  height:auto !important; max-width:none !important; object-fit:fill !important; transform:none !important; }
.case--collibra .sticky-chapter__viewport .sol-frame img.sol-panel {
  height:auto !important; max-width:none !important; object-fit:fill !important;
  transform-origin:50% 50%; }
.case--collibra .sol-frame .sol-head   { width:1440px !important; }
.case--collibra .sol-frame .sol-menu   { width:246px  !important; }
.case--collibra .sol-frame .sol-mid--3 { width:1194px !important; }
.case--collibra .sol-frame .sol-mid--2 { width:678px  !important; }
.case--collibra .sol-frame .sol-panel  { width:516px  !important; }

/* scrolling content layer (content PNG with empty card hole + live cards) */
.case--collibra .sol-body { position:absolute; top:0; left:0; width:1440px;
  transform:translateY(var(--scrollY,0px));
  transition:transform 900ms cubic-bezier(.4,.05,.2,1); will-change:transform; }
/* content column at frame (246,209); each PNG's top set so both card-holes share one frame-y */
.case--collibra .sol-frame .sol-mid { position:absolute; left:246px; z-index:1; }
.case--collibra .sol-frame .sol-mid--3 { top:209px; display:block !important; }
.case--collibra .sol-frame .sol-mid--2 { top:57px;  display:none  !important; }
.case--collibra .sol-frame .sol-scene.open .sol-mid--3 { display:none  !important; }
.case--collibra .sol-frame .sol-scene.open .sol-mid--2 { display:block !important; }

/* The card field. There is ONE live set of cards and it travels: three columns
   and two columns are two seat maps of the same six cards, not two grids
   swapped by `display`. The step's own sentence — "the cards you were comparing
   stay where they were" — is only true if they are visibly the same cards, and
   under a display swap they teleported. So the grid container is just an anchor
   at the hole's origin and every card is placed inside it by the driver;
   `.sol-grid--2` stays in the markup but is never shown (the driver empties it).
   Seat geometry lives in ONE place, the driver's cardPos(). */
.case--collibra .sol-grid { position:absolute; z-index:2; }
.case--collibra .sol-grid--3 { left:var(--g3-x,302px); top:var(--g3-y,1247px); display:block; }
.case--collibra .sol-grid--2 { display:none !important; }

/* the travelling card: slot carries the seat, the width and the stacking;
   -y and -x carry one axis of travel each; .pc carries the lift */
.case--collibra .pc-slot { position:absolute; height:146px; z-index:2; }
.case--collibra .pc-y, .case--collibra .pc-x { width:100%; height:100%; }

/* pinned chrome (does NOT scroll); nav swap follows the page position */
.case--collibra .sol-menu { position:absolute; top:209px; left:0; z-index:4; pointer-events:none; }
.case--collibra .sol-menu--output { display:none !important; }
.case--collibra .sol-scene.scrolled .sol-menu--overview { display:none !important; }
.case--collibra .sol-scene.scrolled .sol-menu--output   { display:block !important; }
.case--collibra .sol-head { position:absolute; top:0; left:0; z-index:5; pointer-events:none; }
/* panel: frame x=924, y=161; above content */
/* The panel slides in beside the narrowed field instead of appearing on a place
   already cleared for it: 31% of its own width, 150ms after the cards start —
   the numbers Pasha set by hand in the lab. Same timing on the way back out. */
.case--collibra .sol-panel { position:absolute; top:161px; left:924px; z-index:6; opacity:0; pointer-events:none; }
/* Written at the viewport's own specificity and after it on purpose: the
   component gives every image in a pinned viewport a pan/zoom transform and a
   520ms transition, and at two classes this slide lost to both — the panel
   computed to identity and never moved. Measured, not guessed. */
.case--collibra .sticky-chapter__viewport .sol-frame img.sol-panel {
  transform:translateX(31%);
  transition:opacity 350ms cubic-bezier(0.40,0.05,0.20,1.00) 150ms,
             transform 350ms cubic-bezier(0.40,0.05,0.20,1.00) 150ms; }
.case--collibra .sticky-chapter__viewport .sol-frame .sol-scene.open img.sol-panel,
.case--collibra .sticky-chapter__viewport .sol-frame .sol-scene.click img.sol-panel {
  opacity:1; transform:translateX(0); }

/* live port card (pixel-matched to Figma) */
/* The frame and the text block are two boxes now. The frame is what narrows;
   the block keeps the wider of the two widths for the whole flight and is
   cropped by the frame, so the type is neither scaled nor re-wrapped in mid-air
   — it re-wraps once, at the end, under a frame that already has its width. */
.case--collibra .pc { position:relative; width:100%; background:#fff; border:1px solid #d3d8de; border-radius:8px;
  height:146px; overflow:hidden; font-family:'Inter',sans-serif; box-sizing:border-box;
  transition:border-color 120ms ease, box-shadow 120ms ease; }
.case--collibra .pc__in { padding:16px; display:flex; flex-direction:column; gap:8px; height:146px;
  box-sizing:border-box; }
.case--collibra .pc.hover { border-color:#9dc0f4; box-shadow:0 0 0 1px #9dc0f4; }
.case--collibra .pc.sel   { border-color:var(--sel); box-shadow:0 0 0 1px var(--sel); }
.case--collibra .pc__t { margin:0; font-weight:500; font-size:14px; line-height:1.6; margin-top:-4.2px; color:#1d2227; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
.case--collibra .pc__a { display:flex; align-items:center; gap:8px; height:20px; font-size:12px; line-height:1.6; color:#617385; }
.case--collibra .pc__s { width:1px; height:12px; background:#d9d9d9; border-radius:30px; }
.case--collibra .pc__b { display:flex; align-items:center; gap:4px; }
.case--collibra .pc__c { width:16px; height:16px; display:flex; align-items:center; justify-content:center; flex:0 0 16px; }
.case--collibra .pc__c svg { display:block; width:10.31px; height:7.48px; }
.case--collibra .pc__d { margin:0; font-size:12px; line-height:1.6; color:#1d2227; overflow:hidden; }
.case--collibra .ripple { position:absolute; z-index:3; width:14px; height:14px; margin:-7px 0 0 -7px; border-radius:50%; background:rgba(63,132,236,.35); pointer-events:none; transform:scale(0); opacity:0; }
.case--collibra .ripple.go { animation:collibra-ripple 520ms cubic-bezier(.2,.6,.2,1) forwards; }
@keyframes collibra-ripple { 0%{transform:scale(0);opacity:.9;} 100%{transform:scale(6);opacity:0;} }
/* ── the pointer (TASK · cursor from lab-cursor.html, variant B "comet") ──
   The old mark was a 12×18 outlined glyph shown only on the "Pick" step; at the
   stage's own fit-scale it measured ≈10px of ink and read as a hairline. It is
   now a filled arrowhead with no stem, in the scene's own accent, trailing a
   short tapering line — and it lives on ALL four steps of the chapter.
   Numbers are Pasha's, set by hand on the живой lab page and copied unchanged:
   B · accent · size 2.00× · trail 7pts · fade 0.24 · spring 0.22 · friction 0.57
   · trail opacity 32% · drift 19% @ 0.2×.
   Both layers sit in stage coordinates, above the panel; neither takes hits. */
.case--collibra .sol-trail { position:absolute; left:0; top:0; width:1440px; height:1039px;
  z-index:29; pointer-events:none; }
.case--collibra .sol-cursor { position:absolute; z-index:30; top:0; left:0; pointer-events:none;
  transform:translate(-200px,-200px); transform-origin:2px 2px; }
.case--collibra .sol-cursor svg { display:block; overflow:visible; }


/* ════════════════════════════════════════════════════════════════════════
   TASK-60 · the case's tone layer: paper grounds, narrative rhythm, service
   typography. Several rules here carry the SAME weight as the base rule they
   override and win by ORDER alone, so THIS SECTION MUST STAY LAST in the
   file. Rules that depend on that: the sticky-chapter background, the header
   h2 margin, the sticky-step__index colour, the stage figcaption margin and
   the .stage-scheme frame. (The chapter head, the ledger and the domain row
   left this file with TASK-67 — they are vocabulary blocks now.)
   ════════════════════════════════════════════════════════════════════════ */

.case--collibra {
  /* Paper, the rhythm intervals and the ledger density all moved to tokens.css
     with TASK-67: every page reads them now, not only this one. */
}

/* ══ 1 · TONE REGISTERS ═══════════════════════════════════════════════════
   Two grounds, one meaning each — the grammar is meant to hold across every
   case in the portfolio, so a reader can tell where they are by tone alone:

     white  — the author speaking: context, reasoning, conclusion
     paper  — showing: the листалки, where evidence is on screen

   An ink register for the result was built and rejected on sight — the black
   Solution chapter read as too much. Do not rebuild it without new grounds. */

/* Both middle chapters are paper, head included, and the tone runs unbroken
   from the rule that opens Exploration to the rule that opens Outcome. A
   chapter head sits on the same ground as the chapter it opens, so no white
   ever appears between a slab and the next rule.
   Order-dependent: ties with .case--collibra .sticky-chapter (:78). */
.case--collibra .sticky-chapter--paper { background: var(--ground-paper); }

/* Every junction inside the paper run collapses to zero, so the ground is one
   continuous field rather than a stack of slabs. */
.case--collibra .chapter--paper + .sticky-chapter--paper,
.case--collibra .sticky-chapter--paper + .chapter { margin-top: 0; }

/* The last slab carries the interval down to the next chapter's rule, so the
   paper ends exactly where the white chapter begins. */
.case--collibra .sticky-chapter--paper { padding-bottom: var(--r-chapter); }

/* Two kinds of object, two treatments — this is the whole point of the paper:

   A screenshot is already a bounded rectangle of interface. It has its own
   edges, so it needs nothing: no sheet behind it, no frame, no grey plane.
   It lies directly on the paper.

   A scheme is line art on nothing. It is a card — a separate picture — so it
   gets a white ground and a hairline edge to be a thing at all.

   The card lives on the stable viewport, keyed off the data-mode the renderer
   already sets, rather than on each incoming scheme. That keeps the frame out
   of the transition, which is what made the old outline blink. */
.case--collibra .sticky-chapter--paper.is-enhanced .sticky-chapter__stage {
  background: transparent;
}
.case--collibra .sticky-chapter--paper .sticky-chapter__viewport {
  border: 0;
  background: transparent;
}
.case--collibra .sticky-chapter--paper .sticky-chapter__stage[data-mode="scheme"] .sticky-chapter__viewport {
  border: var(--rule-hair) solid var(--rule-muted);
  background: var(--white);
}

/* The frame belongs to the stable shell, not to each incoming scheme. Side
   effect worth naming: the black outline takes no part in the slide
   transition, which is most of what read as blinking.
   Order-dependent: ties with .stage-scheme (:538) and .stage-scheme--plate
   (:553) — the plate's own white ground is overridden from here. */
.case--collibra .stage-scheme {
  border: 0;
  background: transparent;
}

/* Fallback (no-JS / <900px): the same hairline, so the two modes agree. */
.case--collibra .sticky-chapter--paper .step-inline-scheme svg {
  border-color: var(--rule-muted);
}

/* ══ 2 · AIR ══════════════════════════════════════════════════════════════
   Air is contrast, not uniform padding. Chapter openings and the stage breathe;
   the ledger and the domain row are tightened, so the page alternates between
   open and compact instead of sitting at one medium density throughout. */

.case--collibra .sticky-chapter__header {
  padding-block: var(--r-chapter-in) var(--r-block);
}
/* The chapter title carries margin-top: 12px, and the header directly under a
   chapter head is given padding-top: 0 (:55, which outweighs the rule above).
   With nothing to push against, that margin collapses out through the section
   and offsets the whole block by 12px. Invisible while everything was white;
   on paper it reads as a white strip between the head and the body. The margin
   has no work to do here — the chapter head already owns that interval. */
.case--collibra .sticky-chapter__header h2 { margin-top: 0; }
.case--collibra .sticky-chapter.is-enhanced .sticky-chapter__stage figcaption {
  margin-top: var(--r-caption);
}

/* ══ 3 · RULES ════════════════════════════════════════════════════════════
   A rule marks a junction, and a junction needs exactly one marker. Where a
   chapter head already opens the section, or a tonal slab already closes it,
   a rule would be the second or third way of saying the same thing.

   So the sticky chapters carry NO rules of their own: the chapter head opens
   them, the next chapter's orange rule closes them, and the paper slab gives
   them an edge of their own. */

@media (min-width: 900px) {
  /* Between steps the whitespace and the active/dimmed state already separate
     one step from the next; the hairline was a line hanging in the air.
     Below 900px the steps stack tightly and it earns its place — it stays. */
  .case--collibra .sticky-chapter.is-enhanced .sticky-step {
    border-top: 0;
  }
  /* Inside a paper chapter no rules survive at all: the slab is the boundary.
     The divider between stage and steps and the hairline under the header both
     go — the tonal edge and the whitespace already do their work. */
  .case--collibra .sticky-chapter--paper.is-enhanced .sticky-chapter__steps {
    border-left: 0;
  }
  .case--collibra .sticky-chapter--paper.is-enhanced .sticky-chapter__body {
    border-top: 0;
  }
}

/* ══ 4 · SERVICE TYPOGRAPHY ═══════════════════════════════════════════════
   One canon everywhere. No interpunct, no slash, no underline, no medium tag:
   the number simply sits ahead of the name and is told apart by tone. The
   separator and the [Screen]/[Scheme] tag are gone from the markup entirely —
   the stage beside a step already shows whether it is a screenshot or a
   diagram. */

.case--collibra .lbl__no {
  margin-right: 0.6em;
  color: var(--text-faint);
}

/* Orange stops being the default ink of every service label. Structure is
   black, sources and numbers are grey, orange means one thing: the live move.
   Order-dependent: ties with .sticky-step__index (:105). */
.case--collibra .sticky-step__index { color: var(--text); }

/* On the paper ground the accent cannot carry words at all: #EF4D02 is 3.25:1
   there and even #D24402 only reaches 4.07:1 (audit A8). The number takes the
   ink the tone pass gave the rest of the service type — the live step is
   already marked twice over, by full opacity and by the driver's 8px offset.
   Not inside a min-width query: the stacked phone layout runs the same driver
   and the same active step, so the number would fall back to grey there. */
.case--collibra .sticky-chapter.is-enhanced .sticky-step.is-active .lbl__no,
.case--collibra .sticky-chapter--paper .sticky-step.is-active .lbl__no {
  color: var(--text);
}


/* ══ 5 · ORANGE SEMANTICS ═════════════════════════════════════════════════
   Per chapter: one orange rule (structure) + one inverted plate (the number).
   The plate is the system default now (--chip-bg / --chip-fg in tokens.css),
   so this case no longer states it. */
