/* The composition atlas, underneath the pack catalogue on
   /ressources/produits/packs/.

   products.css owns the base rules for the .bundle* classes the pack cards use
   and the packs page already links it, so this file only styles the atlas, which
   has its own vocabulary and shares nothing with them. It used to @import
   products.css as well, which fetched the same 900 lines twice and put them
   behind a render-blocking chain.

   The visual grammar is adapted from assets/design/design-system/atomic-design.
   That library's whole idea is that composition is legible from the name — a
   filename is its path down the tree. The catalogue already had the same idea in
   its signature column (F+O+C+P+K14) and was rendering it as an inert caption.
   So here the five delivery primitives get a token colour each, from the brand
   ramp, ordered by depth of engagement; a configuration leads with its formula
   in those colours; and the parts list underneath resolves each letter to a
   named format. Read top to bottom the page goes t0 (the key) → t2 (formula) →
   t3 (card) → t4 (one expertise) → t5 (the atlas). */

/* The five t0 primitive tones moved to tokens.css when the configuration ladder
   started reading them too. A brick must be the same colour on the packs atlas
   and on the landing page that sells it, and that is only guaranteed while there
   is one declaration of the ramp. */

/* No global .sr-only ships in this codebase, so the atlas declares its own
   rather than quietly inventing a name other pages might expect to exist. */
.composition-atlas .atlas-sr{
  position:absolute;
  width:1px;height:1px;
  margin:-1px;padding:0;border:0;
  clip-path:inset(50%);
  overflow:hidden;
  white-space:nowrap;
}

.atlas-head{
  display:grid;
  gap:14px;
  justify-items:start;
  max-width:760px;
  margin:0 0 34px;
}
.atlas-head h2{
  margin:0;
  font-size:clamp(1.9rem,3.2vw,2.6rem);
  line-height:1.08;
  letter-spacing:-.02em;
  text-wrap:balance;
}
.atlas-head .lead{
  margin:0;
  color:var(--silver);
  font-size:1.02rem;
  line-height:1.66;
  text-wrap:pretty;
}

/* ---- t0 · the key ------------------------------------------------------- */

.primitive-key{
  border:1px solid var(--line);
  border-radius:var(--radius);
  background:linear-gradient(145deg,var(--surface),var(--surface-2));
  padding:22px 24px;
  margin:0 0 34px;
}
.primitive-key-title{
  margin:0 0 16px;
  font-family:var(--font-body);
  font-size:.74rem;
  font-weight:780;
  letter-spacing:.08em;
  text-transform:uppercase;
  color:var(--silver);
}
.primitive-key-list{
  display:grid;
  grid-template-columns:repeat(auto-fit,minmax(178px,1fr));
  gap:14px 20px;
  margin:0;
  padding:0;
  list-style:none;
}
.primitive{
  display:grid;
  grid-template-columns:auto minmax(0,1fr);
  gap:12px;
  align-items:center;
}
.primitive-glyph{
  display:grid;
  place-items:center;
  width:38px;height:38px;
  border-radius:11px;
  font-family:var(--font-display);
  font-size:1.32rem;
  font-weight:700;
  line-height:1;
  color:var(--tone);
  background:color-mix(in srgb,var(--tone) 15%,transparent);
  border:1px solid color-mix(in srgb,var(--tone) 38%,transparent);
}
.primitive-body{display:grid;gap:3px;min-width:0}
.primitive-body strong{font-size:.92rem;line-height:1.25}
.primitive-body small{color:var(--silver);font-size:.78rem;line-height:1.45;text-wrap:pretty}
.primitive-unit{
  margin-top:3px;
  font-size:.72rem;
  line-height:1.4;
  color:color-mix(in srgb,var(--paper) 76%,var(--silver));
}
.primitive-unit-label{
  margin-right:5px;
  font-size:.62rem;
  letter-spacing:.08em;
  text-transform:uppercase;
  color:var(--silver);
}
/* The rule that sets the band, in the token's own colour: it is the one line
   that ties a brick's price to the day rate above it. */
.primitive-rule{
  font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;
  font-size:.68rem;
  line-height:1.45;
  color:var(--tone);
  overflow-wrap:anywhere;
}
.primitive-key-note{
  margin:18px 0 0;
  padding-top:16px;
  border-top:1px solid var(--line);
  color:var(--silver);
  font-size:.84rem;
  line-height:1.6;
}

/* The token map, applied once to everything that carries a data-medium. */
.composition-atlas [data-medium="framework"]{--tone:var(--t0-framework)}
.composition-atlas [data-medium="office_hours"]{--tone:var(--t0-office_hours)}
.composition-atlas [data-medium="cohort"]{--tone:var(--t0-cohort)}
.composition-atlas [data-medium="programme"]{--tone:var(--t0-programme)}
.composition-atlas [data-medium="capacity"]{--tone:var(--t0-capacity)}

/* ---- The derivation, stated ---------------------------------------------- */

/* Collapsed by default: a visitor who trusts the number should not have to
   scroll past the arithmetic, and one who does not should not have to ask. */
.method-panel{
  border:1px solid color-mix(in srgb,var(--cyan) 26%,var(--line));
  border-radius:var(--radius);
  background:color-mix(in srgb,var(--cyan) 4%,transparent);
  margin:0 0 34px;
  overflow:hidden;
}
.method-panel>summary{
  display:flex;
  flex-wrap:wrap;
  align-items:baseline;
  gap:6px 14px;
  padding:16px 22px;
  cursor:pointer;
  list-style:none;
}
.method-panel>summary::-webkit-details-marker{display:none}
.method-panel>summary::after{
  content:"";
  width:8px;height:8px;
  margin-left:auto;
  align-self:center;
  border-right:2px solid var(--cyan);
  border-bottom:2px solid var(--cyan);
  transform:rotate(45deg) translate(-2px,-2px);
  transition:transform .18s ease;
}
.method-panel[open]>summary::after{transform:rotate(-135deg) translate(-2px,-2px)}
.method-panel>summary:focus-visible{
  outline:3px solid color-mix(in srgb,var(--cyan) 40%,transparent);
  outline-offset:-3px;
}
.method-panel-title{font-weight:760;font-size:.96rem}
.method-panel-hint{color:var(--silver);font-size:.82rem}
.method-body{padding:0 22px 20px}

/* The five steps, read as one running calculation rather than five facts. */
.method-steps{
  display:grid;
  grid-template-columns:repeat(auto-fit,minmax(190px,1fr));
  gap:12px;
  margin:0 0 16px;
  padding:0;
  list-style:none;
  counter-reset:none;
}
.method-steps li{
  display:grid;
  gap:3px;
  align-content:start;
  padding:12px 14px;
  border:1px solid var(--line);
  border-radius:12px;
  background:color-mix(in srgb,var(--ink) 24%,transparent);
}
.method-step-label{
  font-size:.66rem;
  letter-spacing:.07em;
  text-transform:uppercase;
  color:var(--silver);
}
.method-steps strong{
  font-family:var(--font-display);
  font-size:1.12rem;
  line-height:1.2;
  font-variant-numeric:tabular-nums;
}
.method-steps small{color:var(--silver);font-size:.74rem;line-height:1.5;text-wrap:pretty}
.method-note{
  margin:0;
  color:color-mix(in srgb,var(--paper) 80%,var(--silver));
  font-size:.82rem;
  line-height:1.6;
  text-wrap:pretty;
}
.method-note+.method-note{margin-top:8px}
.method-note--muted{color:var(--silver);font-size:.76rem}

/* The day rate this expertise's every band is built from, shown as the actual
   multiplication rather than as a finished figure. */
.shelf-rate{
  display:flex;
  flex-wrap:wrap;
  align-items:baseline;
  justify-content:space-between;
  gap:8px 18px;
  margin:0 0 20px;
  padding:12px 16px;
  border:1px solid color-mix(in srgb,var(--cyan) 22%,var(--line));
  border-radius:12px;
  background:color-mix(in srgb,var(--cyan) 4%,transparent);
}
.shelf-rate-label{
  font-size:.66rem;
  letter-spacing:.07em;
  text-transform:uppercase;
  color:var(--silver);
}
.shelf-rate-sum{
  display:flex;
  align-items:baseline;
  gap:8px;
  font-variant-numeric:tabular-nums;
}
.shelf-rate-term{font-size:.9rem;color:color-mix(in srgb,var(--paper) 82%,var(--silver))}
.shelf-rate-op{color:var(--silver);font-size:.8rem}
.shelf-rate-total{font-family:var(--font-display);font-size:1.16rem;color:var(--cyan)}

/* ---- t5 · the shelves --------------------------------------------------- */

.atlas-shelves{display:grid;gap:12px}

.shelf{
  border:1px solid var(--line);
  border-radius:var(--radius);
  background:color-mix(in srgb,var(--surface) 52%,transparent);
  overflow:hidden;
}
.shelf[open]{
  border-color:color-mix(in srgb,var(--violet) 30%,var(--line));
  background:linear-gradient(145deg,var(--surface),var(--surface-2));
}

/* The summary carries the whole header, so the default disclosure triangle has
   to go — it would sit beside a two-line heading. The chevron below replaces it. */
.shelf-summary{
  display:grid;
  grid-template-columns:auto minmax(0,1fr) auto auto auto;
  align-items:center;
  gap:10px 20px;
  padding:20px 24px;
  cursor:pointer;
  list-style:none;
}
.shelf-summary::-webkit-details-marker{display:none}
.shelf-summary:hover{background:color-mix(in srgb,var(--violet) 5%,transparent)}
.shelf-summary:focus-visible{
  outline:3px solid color-mix(in srgb,var(--cyan) 40%,transparent);
  outline-offset:-3px;
}
.shelf-summary::after{
  content:"";
  width:9px;height:9px;
  border-right:2px solid var(--silver);
  border-bottom:2px solid var(--silver);
  transform:rotate(45deg) translate(-2px,-2px);
  transition:transform .18s ease;
}
.shelf[open] .shelf-summary::after{transform:rotate(-135deg) translate(-2px,-2px)}

.shelf-kind{
  align-self:start;
  padding:5px 11px;
  border-radius:999px;
  background:color-mix(in srgb,var(--violet) 14%,transparent);
  color:var(--cyan);
  font-size:.68rem;
  font-weight:800;
  letter-spacing:.08em;
  text-transform:uppercase;
  white-space:nowrap;
}
.shelf-identity{display:grid;gap:5px;min-width:0}
.shelf-title{
  font-family:var(--font-display);
  font-size:clamp(1.25rem,1.9vw,1.6rem);
  font-weight:700;
  line-height:1.15;
  letter-spacing:-.015em;
}
.shelf-promise{
  color:var(--silver);
  font-size:.88rem;
  line-height:1.55;
  text-wrap:pretty;
}

/* The seven rungs, shown closed: what the expertise looks like before you open
   it. Purely a shape — the codes are decorative here and the real ones live on
   the cards, so the whole strip is aria-hidden in the template. */
.shelf-ladder{display:flex;gap:3px;align-items:flex-end}
.ladder-rung{
  display:grid;
  place-items:end center;
  width:14px;
  border-radius:3px 3px 0 0;
  background:color-mix(in srgb,var(--lavender) 26%,transparent);
  font-size:0;
}
.ladder-rung:nth-child(1){height:9px}
.ladder-rung:nth-child(2){height:13px}
.ladder-rung:nth-child(3){height:17px}
.ladder-rung:nth-child(4){height:21px}
.ladder-rung:nth-child(5){height:25px}
.ladder-rung:nth-child(6){height:29px}
.ladder-rung:nth-child(7){height:33px;background:color-mix(in srgb,var(--violet) 62%,transparent)}
.shelf[open] .ladder-rung{background:color-mix(in srgb,var(--violet) 46%,transparent)}

.shelf-count{display:grid;gap:1px;text-align:right;white-space:nowrap}
.shelf-count strong{font-family:var(--font-display);font-size:1.3rem;line-height:1}
.shelf-count span{font-size:.68rem;color:var(--silver);letter-spacing:.06em;text-transform:uppercase}

.shelf-body{padding:0 24px 24px}

/* ---- t4 · one expertise ------------------------------------------------- */

.shelf-anchor{
  display:grid;
  grid-template-columns:repeat(auto-fit,minmax(220px,1fr));
  gap:14px;
  margin:0 0 26px;
  padding:18px;
  border:1px solid var(--line);
  border-radius:16px;
  background:color-mix(in srgb,var(--ink) 22%,transparent);
}
.shelf-anchor>div{display:grid;gap:5px;min-width:0}
.shelf-anchor dt{
  font-size:.66rem;
  letter-spacing:.09em;
  text-transform:uppercase;
  color:var(--silver);
}
.shelf-anchor dd{
  margin:0;
  font-size:.85rem;
  line-height:1.55;
  text-wrap:pretty;
}

.config-grid{
  display:grid;
  grid-template-columns:repeat(auto-fit,minmax(288px,1fr));
  gap:14px;
  align-items:stretch;
}

/* ---- t3 · one configuration --------------------------------------------- */

.config{
  position:relative;
  isolation:isolate;
  overflow:hidden;
  display:flex;
  flex-direction:column;
  gap:12px;
  padding:20px;
  border:1px solid var(--line);
  border-radius:18px;
  background:color-mix(in srgb,var(--ink) 30%,transparent);
  scroll-margin-top:96px;
}

/* Plate, pane, content — the same three-layer stack as .rung in
   components/config-ladder.css, because a pack and a configuration are the
   same card seen from two catalogues. Absolutely positioned, so neither layer
   joins the flex column. */
.config-bg{
  position:absolute;
  inset:0;
  inline-size:100%;
  block-size:100%;
  object-fit:cover;
  z-index:0;
  transition:transform .35s ease;
}

/* 8px, not 16px, for the reason spelled out on .rung-glass: the pool is soft
   line-work on a near-black ground and over-blurring turns every plate into
   the same flat violet fill. */
.config-glass{
  position:absolute;
  inset:0;
  z-index:1;
  -webkit-backdrop-filter:blur(8px) saturate(125%);
  backdrop-filter:blur(8px) saturate(125%);
  background:linear-gradient(
    to bottom,
    color-mix(in srgb,var(--glass) 34%,transparent) 0%,
    color-mix(in srgb,var(--glass) 62%,transparent) 26%,
    color-mix(in srgb,var(--glass) 80%,transparent) 100%
  );
}

html[data-theme="light"] .config-bg{opacity:.5}
html[data-theme="light"] .config-glass{
  background:linear-gradient(
    to bottom,
    color-mix(in srgb,var(--glass) 78%,transparent) 0%,
    color-mix(in srgb,var(--glass) 90%,transparent) 26%,
    color-mix(in srgb,var(--glass) 96%,transparent) 100%
  );
}

.config>*:not(.config-bg):not(.config-glass){position:relative;z-index:2}

.config:hover .config-bg{transform:scale(1.04)}

@supports not ((backdrop-filter:blur(1px)) or (-webkit-backdrop-filter:blur(1px))){
  .config-glass{background:color-mix(in srgb,var(--glass) 95%,transparent)}
}

@media (prefers-reduced-transparency:reduce){
  .config-glass{
    -webkit-backdrop-filter:none;
    backdrop-filter:none;
    background:var(--glass);
  }
}
.config:hover{border-color:color-mix(in srgb,var(--violet) 34%,var(--line))}
.config:target{
  border-color:var(--cyan);
  box-shadow:0 0 0 3px color-mix(in srgb,var(--cyan) 18%,transparent);
}

/* One abstract identity glyph per bundle, matching .rung-visual in
   components/config-ladder.css so the same seven bundles read as the same
   seven icons on both the atlas and the expertise-page shelf. */
.config-visual{
  display:grid;
  place-items:center;
  inline-size:52px;
  block-size:52px;
  border-radius:50%;
  color:var(--cyan);
  background:color-mix(in srgb,var(--cyan) 12%,transparent);
}
.config-visual svg{inline-size:26px;block-size:26px}

/* t2 — the formula. Included letters carry their token colour, excluded ones
   drop to an outline so the shape of the composition reads at a glance without
   the card having to say "not included" five times. */
.config-formula{
  display:flex;
  flex-wrap:wrap;
  align-items:center;
  gap:5px;
  margin:0;
}
.glyph{
  display:grid;
  place-items:center;
  width:30px;height:30px;
  border-radius:9px;
  font-family:var(--font-display);
  font-size:1.08rem;
  font-weight:700;
  line-height:1;
  color:var(--tone);
  background:color-mix(in srgb,var(--tone) 16%,transparent);
  border:1px solid color-mix(in srgb,var(--tone) 46%,transparent);
}
.glyph--out{
  color:var(--silver);
  background:transparent;
  border:1px dashed color-mix(in srgb,var(--tone) 30%,var(--line));
  opacity:.7;
}
.glyph-join{color:var(--silver);font-size:.8rem;font-weight:700}

/* What kind of thing this is, toned from the same five-colour vocabulary the
   glyph legend teaches. Six types, six distinct tones: a visitor scanning a
   mixed catalogue sorts PDFs from cohort seats from capacity plans by colour
   before reading a word, which is the job a second grey pill could not do
   once the card grew a plate behind it. */
[data-product-type="framework_pdf"]{--tone:var(--t0-framework)}
[data-product-type="office_hours"]{--tone:var(--t0-office_hours)}
[data-product-type="cohort_access"]{--tone:var(--t0-cohort)}
[data-product-type="bundle"]{--tone:var(--t0-programme)}
[data-product-type="capacity_plan"]{--tone:var(--t0-capacity)}
[data-product-type="webinar"]{--tone:var(--cyan)}

.config-type{
  display:inline-flex;
  align-items:center;
  gap:6px;
  padding:4px 11px;
  border-radius:999px;
  font-size:.68rem;
  font-weight:800;
  letter-spacing:.06em;
  text-transform:uppercase;
  white-space:nowrap;
  color:var(--tone,var(--cyan));
  background:color-mix(in srgb,var(--tone,var(--cyan)) 18%,transparent);
  border:1px solid color-mix(in srgb,var(--tone,var(--cyan)) 46%,transparent);
}

/* A generated dot, never a child element: core.js swaps language with
   ``el.textContent = …``, which deletes every child node. A pseudo-element
   survives the toggle; a nested <span> would vanish on the first click. */
.config-type::before{
  content:"";
  inline-size:6px;
  block-size:6px;
  border-radius:50%;
  background:currentColor;
  flex:none;
}

.config-head{display:flex;align-items:center;gap:10px;flex-wrap:wrap}
.config-code{
  padding:3px 8px;
  border:1px solid color-mix(in srgb,var(--violet) 40%,var(--line));
  border-radius:999px;
  color:color-mix(in srgb,var(--lavender) 70%,var(--paper));
  font-size:.68rem;
  font-weight:800;
  letter-spacing:.04em;
}
.config-head h4{
  margin:0;
  font-family:var(--font-display);
  font-size:1.28rem;
  font-weight:700;
  line-height:1.18;
  letter-spacing:-.01em;
}
.config-reason{
  margin:0;
  color:var(--silver);
  font-size:.88rem;
  line-height:1.55;
  text-wrap:pretty;
}

/* t1 — the atoms, resolved. Each row is one primitive with the same letter the
   formula used, so the two are read as one object. */
.config-parts{
  display:grid;
  gap:1px;
  margin:2px 0 0;
  padding:0;
  list-style:none;
  border:1px solid var(--line);
  border-radius:12px;
  overflow:hidden;
  background:var(--line);
}
.part{
  display:grid;
  grid-template-columns:auto minmax(0,1fr) auto;
  align-items:start;
  gap:4px 10px;
  padding:10px 11px;
  background:color-mix(in srgb,var(--navy) 82%,transparent);
  font-size:.79rem;
}
.part-body{display:grid;gap:2px;min-width:0}
/* The published price of the product this brick maps to — the figure a visitor
   can leave the page and verify. */
.part-reference{color:var(--silver);font-size:.68rem;line-height:1.4;overflow-wrap:anywhere}
.part-term{color:var(--tone);font-size:.68rem;line-height:1.4}
.part--out{color:var(--silver);opacity:.52}
.part-glyph{
  display:grid;
  place-items:center;
  width:19px;height:19px;
  border-radius:6px;
  background:color-mix(in srgb,var(--tone) 16%,transparent);
  border:1px solid color-mix(in srgb,var(--tone) 42%,transparent);
  color:var(--tone);
  font-size:.68rem;
  font-weight:800;
  line-height:1;
}
.part--out .part-glyph{
  background:transparent;
  border:1px dashed color-mix(in srgb,var(--tone) 26%,var(--line));
  color:var(--silver);
}
.part-name{min-width:0;line-height:1.35;overflow-wrap:anywhere}
.part-name a{color:var(--paper);text-decoration:none;border-bottom:1px solid color-mix(in srgb,var(--cyan) 44%,transparent)}
.part-name a:hover,.part-name a:focus-visible{color:var(--cyan)}
.part-state{
  display:grid;
  justify-items:end;
  gap:1px;
  white-space:nowrap;
  font-size:.74rem;
  color:color-mix(in srgb,var(--paper) 78%,var(--silver));
  font-variant-numeric:tabular-nums;
}
.part-amount{font-weight:700}
.part-scoped{color:var(--silver);font-style:italic;font-size:.66rem}

/* The one figure on the card, pushed to the bottom so every card in a row lines
   its price up with its neighbours regardless of how long the reason ran. */
.config-band{
  display:flex;
  align-items:baseline;
  justify-content:space-between;
  gap:12px;
  margin:auto 0 0;
  padding-top:12px;
  border-top:1px solid var(--line);
}
.config-band-label{
  font-size:.66rem;
  letter-spacing:.08em;
  text-transform:uppercase;
  color:var(--silver);
}
.config-band strong{
  display:flex;
  align-items:baseline;
  justify-content:flex-end;
  flex-wrap:wrap;
  gap:6px;
  text-align:right;
  font-family:var(--font-display);
  font-size:1.24rem;
  font-weight:700;
  letter-spacing:-.01em;
  font-variant-numeric:tabular-nums;
}
/* A configuration holding a one-off amount and a subscription states both, so
   the total needs a word between them. It reads as prose, not as a figure —
   the numbers are what the eye should land on. */
.config-band-join{
  font-family:var(--font-body);
  font-size:.72rem;
  font-weight:600;
  letter-spacing:.04em;
  color:var(--silver);
}
.config--scoped .config-band strong{color:color-mix(in srgb,var(--paper) 88%,var(--silver))}
/* The one claim the card makes about its own arithmetic. Rendered only when the
   view has verified it, so it never states a sum that does not add up. */
.config-check{
  display:flex;
  align-items:baseline;
  gap:6px;
  margin:0;
  color:color-mix(in srgb,var(--cyan) 82%,var(--paper));
  font-size:.72rem;
  line-height:1.45;
}
.config-check::before{content:"=";font-weight:800;color:var(--cyan)}
.config-note{
  margin:0;
  color:var(--silver);
  font-size:.75rem;
  line-height:1.5;
  text-wrap:pretty;
}

.config-cta{
  display:inline-flex;
  align-items:center;
  justify-content:center;
  gap:8px;
  min-height:44px;
  padding:10px 16px;
  border-radius:12px;
  background:color-mix(in srgb,var(--violet) 16%,transparent);
  border:1px solid color-mix(in srgb,var(--violet) 40%,transparent);
  color:var(--paper);
  font-size:.88rem;
  font-weight:760;
  text-decoration:none;
  transition:background .16s ease,border-color .16s ease,transform .16s ease;
}
.config-cta:hover{
  background:color-mix(in srgb,var(--violet) 30%,transparent);
  border-color:color-mix(in srgb,var(--violet) 62%,transparent);
  transform:translateY(-1px);
}
.config-cta:focus-visible{
  outline:3px solid color-mix(in srgb,var(--cyan) 40%,transparent);
  outline-offset:3px;
}

/* What the same pieces cost bought separately, printed above the total it is
   being compared with. Deliberately quiet and struck through: it is the number
   the visitor is *not* paying, and a pack whose two figures compete reads as
   two prices rather than as one price and its reference point. */
.config-separate{
  display:flex;
  align-items:baseline;
  justify-content:space-between;
  gap:12px;
  margin:0;
  color:var(--silver);
  font-size:.76rem;
}
.config-separate-label{letter-spacing:.06em;text-transform:uppercase;font-size:.66rem}
.config-separate-amount{
  text-decoration:line-through;
  text-decoration-thickness:1px;
  font-variant-numeric:tabular-nums;
}

/* The composition on a product's own page. One card, not a grid, so it is
   constrained rather than stretched across the full container: an itemised bill
   two columns wide is harder to read than the same bill in one. */
.product-composition{margin:0 0 32px;max-width:560px}
.product-composition-title{
  font-family:var(--font-display);
  font-size:1.06rem;
  line-height:1.3;
  margin:0 0 14px;
  text-wrap:balance;
}

/* The catalogue grid is the page's own subject rather than a shelf inside an
   expertise, so its cards get room to breathe and a heading one level up. The
   card itself is unchanged — that is the point of it being one component. */
.product-catalogue-grid{grid-template-columns:repeat(auto-fit,minmax(320px,1fr));gap:16px}
.product-catalogue-grid .config{padding:22px}
.product-catalogue-grid .config-head h2{
  font-family:var(--font-display);
  font-size:1.12rem;
  line-height:1.25;
  margin:0;
}

/* A pack quoted per engagement, printed where an amount would be on the pack
   cards above. It is not a free product, so it must not borrow
   .product-price--free's colour. */
.product-price--quoted{color:var(--silver);font-style:italic}

@media(prefers-reduced-motion:reduce){
  .shelf-summary::after,.config-cta{transition:none}
  .config-cta:hover{transform:none}
  .config-bg{transition:none}
  .config:hover .config-bg{transform:none}
}

@media(max-width:900px){
  .shelf-summary{
    grid-template-columns:minmax(0,1fr) auto;
    align-items:start;
    padding:18px;
  }
  .shelf-kind{grid-column:1;grid-row:1}
  .shelf-summary::after{grid-column:2;grid-row:1;align-self:center}
  .shelf-identity{grid-column:1/-1;grid-row:2}
  .shelf-ladder{grid-column:1;grid-row:3;margin-top:4px}
  .shelf-count{grid-column:2;grid-row:3;align-self:end}
  .shelf-body{padding:0 18px 18px}
}

@media(max-width:640px){
  .config-grid{grid-template-columns:1fr}
  .primitive-key{padding:18px}
  .shelf-promise{display:none}
  .method-body{padding:0 18px 18px}
  .shelf-rate{flex-direction:column;align-items:flex-start}
}

/* A category that is published but holds nothing yet. Deliberately a panel and
   not a grid cell: it is the page's whole answer, and a lone card floating in a
   three-column grid reads as a loading state rather than as a statement. */
.category-waiting{
  display:grid;
  justify-items:start;
  gap:16px;
  max-width:640px;
  padding:clamp(20px,4vw,28px);
  border:1px dashed var(--line-strong);
  border-radius:18px;
  background:color-mix(in srgb,var(--ink) 24%,transparent);
}
.category-waiting-lead{margin:0;color:var(--silver);line-height:1.65;text-wrap:pretty}

/* ============================================================
   Split hero — heading on the left, qualifier on the right.

   Used by /expertises/solutions/, where the page asks the visitor
   to choose between five stacks, twelve expertises and seven
   engagement configurations. The form arbitrates that from their
   own answers, so it belongs at the point the decision is made
   rather than at the foot of the page.

   The copy column keeps the wider share, and the two collapse to
   one column well before the form gets narrow enough for its
   option buttons to wrap. Below that breakpoint the form comes
   second, because the heading is what tells a visitor arriving
   from the menu what the page is.
   ============================================================ */
.page-hero--split{padding-bottom:34px}
.hero-split{
  display:grid;
  grid-template-columns:1fr minmax(340px,.82fr);
  gap:44px;
  align-items:start;
}
.hero-split-copy{display:grid;gap:14px;align-content:start}
.hero-split-copy h1{margin:0}
.hero-split-copy .lead{margin:0}
.hero-split-note{
  margin:0;
  font-size:.85rem;
  line-height:1.6;
  color:var(--silver);
  padding-left:14px;
  border-left:2px solid color-mix(in srgb,var(--cyan) 34%,transparent);
  max-width:56ch;
}
/* The form owns its own card in the `hero` tone, so the column is a
   positioning context and nothing else. Sticky is deliberate: the
   copy column is short and the form is tall, and a form that scrolls
   away from a visitor still reading the heading beside it is a form
   they have to scroll back up to find. */
.hero-split-form{position:sticky;top:96px}

@media (max-width:1000px){
  .hero-split{grid-template-columns:1fr;gap:26px}
  .hero-split-form{position:static}
}

/* What the diagnostic returns, beside the form that produces it. Four lines
   rather than one sentence naming four things: the prose version sat next to a
   form whose own promise said the same thing at the same length, so the payoff
   was written twice and scanned neither time. */
.hero-split-yield{
  list-style:none;margin:4px 0 0;padding:0;display:grid;gap:12px;max-width:52ch;
}
.hero-split-yield li{
  display:grid;grid-template-columns:auto 1fr;gap:12px;align-items:center;
  font-size:.87rem;line-height:1.45;color:var(--paper);
}
.hero-split-yield img{
  width:34px;height:34px;padding:5px;border-radius:10px;
  background:color-mix(in srgb,var(--violet) 14%,transparent);
  border:1px solid color-mix(in srgb,var(--violet) 22%,transparent);
}
