/**
 * knowledge.php.
 *
 * Loaded after css/services.css, which this page reuses wholesale for its
 * furniture: .svm is the same dark masthead, .svsection the same band, and
 * .svhead, .svstats, .svsearch, .svfacets, .svchip, .svempty and .svrise all
 * behave exactly as they do on Services. Duplicating any of that here would give
 * the two pages two definitions of the same thing and one of them would drift.
 *
 * What is genuinely new is the shape of a use case and the shape of a report,
 * and that is all this file carries:
 *
 *   1. .kcase  - an expandable case row
 *   2. .krep   - a report card
 *
 * A case is a row rather than a card because the eleven of them are read by
 * comparison. Company, sector and headline figure line up in columns down the
 * page, so a reader scanning for "which of these moved productivity" reads one
 * column instead of eleven cards. The narrative is behind a disclosure because
 * four paragraphs times eleven is a page nobody scrolls.
 */

/* ── masthead figures ─────────────────────────────────────────────────────
   .svstats is a light-section component: white blocks, navy numerals, a hairline
   box around them. Dropped onto this page's dark masthead it reads as four
   pasted cards. Fixed here rather than in css/services.css, because nothing on
   Services puts that component on a dark plane and this override should not
   follow it there.

   Four across the full width, hairlines only, no boxes. They sit under the whole
   masthead now rather than in its right column: as a row they read as a footing
   for the plane, where stacked two by two beside the title they were a second
   block of numerals arguing with the headline. */
.svm__stats {
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 0;
  margin: clamp(2.25rem, 5vh, 3.5rem) 0 0;
  padding-top: 1.5rem;
  background: none;
  border: 0;
  border-radius: 0;
  border-top: 1px solid color-mix(in srgb, #fff 15%, transparent);
}

.svm__stats > div {
  padding: 0 clamp(1rem, 2.5vw, 2rem);
  background: none;
  border-left: 1px solid color-mix(in srgb, #fff 12%, transparent);
}

.svm__stats > div:first-child { padding-left: 0; border-left: 0; }
.svm__stats b { color: #fff; font-weight: 400; }

/* Gold on the first figure is the pattern css/services.css already uses to mark
   the headline number; it just needs the lighter tint to survive on navy. */
.svm__stats > div:first-child b { color: var(--gold-300); }
.svm__stats span { color: color-mix(in srgb, #fff 62%, transparent); }

/* The strip is centred on the same measure as everything above it. It used to
   follow .svm__intro out of the wrap by the centring gutter, back when the copy
   made that move to stand level with the Services map; nothing on this page runs
   to the viewport edge, so the pull was buying full-bleed alignment for a
   composition that is not full bleed - it only moved the row left and left the
   space it vacated on the right. */
.svm__stats {
  max-width: var(--kmast);
  margin-inline: auto;
  text-align: center;
}
/* The first cell keeps its inset and loses only the rule. Zeroing the padding
   as well - which is what the left-aligned version did, to square the strip's
   first numeral with the headline above it - now pulls that numeral off the
   centre of its own column while the other three sit on theirs. */
.svm__stats > div:first-child { padding-left: clamp(1rem, 2.5vw, 2rem); border-left: 0; }

@media (max-width: 720px) {
  .svm__stats { grid-template-columns: repeat(2, minmax(0, 1fr)); row-gap: 1.5rem; }
  /* Border only: the padding stays on, because the cells are centred now and
     stripping the left inset off two of four would sit their numerals off the
     middle of their own column. */
  .svm__stats > div:nth-child(odd) { border-left: 0; }
}

/* ── the plane's weather ──────────────────────────────────────────────────
   Three gradients over the two that services.css already paints. They exist
   because of what sits on top of them: a panel that changes every few seconds
   over a ground that never does looks like a widget bolted to a static page.

   Constraints, all of them cheap on purpose. No `filter: blur()` - a blur on
   three elements this size is per-frame GPU work, and a radial gradient is
   already soft. Nothing animates a size or a position property, so the plane
   costs no layout while it moves. The alphas are low because .svm::before is
   still underneath and the headline has to keep its edge; at rest the whole
   layer is a few per cent of warmth.

   The third gradient is the one that matters. It is not on a loop - it is
   parked under whichever case the spotlight is showing and translates across
   the plane as the reader steps through the eleven, so the background is
   reporting the same position the tick row is. Under prefers-reduced-motion the
   two drifters hold still and the tracker stops sliding but still moves - it
   cuts to the new position instead. It is not decoration: it is a second
   reading of where you are in the set, and a reader who has turned motion off
   asked for less movement, not less information. */
.kbg {
  position: absolute;
  inset: 0;
  z-index: -1;
  overflow: hidden;
  pointer-events: none;
}

.kbg__orb {
  position: absolute;
  width: 58vw;
  aspect-ratio: 1;
  border-radius: 50%;
}

.kbg__orb--cool {
  left: -20%;
  top: -38%;
  background: radial-gradient(circle,
    color-mix(in srgb, var(--c-sky) 26%, transparent), transparent 62%);
}

.kbg__orb--warm {
  right: -24%;
  top: -50%;
  background: radial-gradient(circle,
    color-mix(in srgb, var(--c-gold) 17%, transparent), transparent 64%);
}

/* 6.2vw a step, eleven steps, so the walk crosses about two thirds of the plane
   over a full pass and returns with the sequence rather than snapping back. */
.kbg__orb--track {
  width: 40vw;
  left: 4%;
  bottom: -30%;
  background: radial-gradient(circle,
    color-mix(in srgb, var(--c-sky) 24%, transparent), transparent 66%);
  transform: translate3d(calc(var(--k-i, 0) * 6.2vw), 0, 0);
  transition: transform 1100ms cubic-bezier(0.22, 1, 0.36, 1);
}

/* Unequal periods, both long: at 34 and 46 seconds the two never come back into
   the same relation, so there is no loop for the eye to catch. */
@media (prefers-reduced-motion: no-preference) {
  .kbg__orb--cool { animation: kbg-drift-a 34s ease-in-out infinite; }
  .kbg__orb--warm { animation: kbg-drift-b 46s ease-in-out infinite; }
  /* Opacity, not transform - the transform is spoken for by the walk above. */
  .kbg__orb--track { animation: kbg-pulse 11s ease-in-out infinite; }
}

@keyframes kbg-drift-a {
  0%, 100% { transform: translate3d(0, 0, 0) scale(1); }
  50%      { transform: translate3d(5%, 4%, 0) scale(1.09); }
}
@keyframes kbg-drift-b {
  0%, 100% { transform: translate3d(0, 0, 0) scale(1); }
  50%      { transform: translate3d(-6%, 5%, 0) scale(1.13); }
}
@keyframes kbg-pulse {
  0%, 100% { opacity: 0.7; }
  50%      { opacity: 1; }
}

/* The walk is a transition rather than an animation, so it is outside the query
   above and has to be turned off explicitly. Half the viewport of soft gradient
   sliding for a second is exactly what the preference is asking not to see. */
@media (prefers-reduced-motion: reduce) {
  .kbg__orb--track { transition: none; }
}

/* ── the masthead spotlight ───────────────────────────────────────────────
   Services fills this half of its masthead with the region plotted as a network.
   This page has something better to put there than a drawing: eleven real
   results. The panel shows one at a time - the figure a factory actually moved,
   set large, with whose it was, where, and the secondary figures the same story
   reports - and steps through them.

   Three rules shape it:

   1. It is content, not decoration. Every value is read from CASES, the same
      array the library below renders, and the ticks number eleven because the
      library holds eleven. Clicking through opens that case rather than
      scrolling to a vague neighbourhood; clicking the country or the sector
      filters the library to it.
   2. It stops. Motion that never ends is a distraction, so it pauses when the
      pointer or focus enters it, when it scrolls out of view, when the tab is
      hidden, and permanently when the reader takes the wheel. There is also a
      real pause control, because content that moves on its own needs one that
      does not depend on hovering.
   3. Under prefers-reduced-motion it does not move at all. The advance is driven
      by the end of the tick's fill animation, so when that animation is not
      applied there is nothing to advance it - the panel simply shows the first
      case and waits to be clicked. No timer to remember to disable. Every
      entrance, the glow and the counting figure are all inside a
      no-preference query for the same reason.

   Everything that moves here moves opacity and transform only. Nothing in the
   panel animates a size or a position property, so a step costs no layout. */

/* ── the masthead, stacked and centred ────────────────────────────────────
   Services runs its masthead as two columns pulled out to the viewport edge,
   because the thing in the right column is a map: it has no edges of its own,
   it grows to the right, and the copy follows it out so the plane's empty space
   is not all on one side.

   This page has a framed panel there instead, and none of that reasoning
   survives the substitution. The frame has hard edges, it does not want to be
   wider than its own contents, and side by side with a taller column of copy it
   left a column of empty plane whichever way it was aligned - stretched it was a
   drawn border around nothing, bottom-aligned it was air above the frame.

   Stacked and centred on one narrow measure there is no second column for the
   space to collect in. The width below is deliberately well under --maxw: 1240px
   was set for a map and a filter grid, and a title, a paragraph, two buttons and
   a data panel do not need it. */
.svm { --kmast: 46rem; }
.svm__grid {
  max-width: var(--kmast);
  margin-inline: auto;
  justify-items: center;
  text-align: center;
  gap: clamp(1.75rem, 3.5vw, 2.5rem);
}

/* Cancels the full-bleed pull services.css puts on the copy at this width. The
   margins were a shift, not a widening, so zeroing them is enough - the column
   is sized by the measure above. */
@media (min-width: 1024px) {
  .svm__grid { grid-template-columns: minmax(0, 1fr); }
  .svm__intro { margin-inline: 0; }
}

/* justify-items above sizes both children to their contents, which is right for
   the copy's title but wrong for the frame - a bordered panel that shrinks to
   its widest line changes width as it steps. Both take the full measure. */
.svm__intro { width: 100%; }
.khero { width: 100%; }
.svm__lead { margin-inline: auto; }
.svm__actions { justify-content: center; }

.khero {
  --khero-ease: cubic-bezier(0.22, 1, 0.36, 1);
  position: relative;
  z-index: 0;
  display: grid;
  grid-template-rows: minmax(0, 1fr) auto;
  gap: 1.1rem;
  padding: clamp(1.4rem, 2.4vw, 1.9rem);
  overflow: hidden;
  border: 1px solid color-mix(in srgb, #fff 16%, transparent);
  border-radius: var(--corner-box);
  background: color-mix(in srgb, #fff 4%, transparent);
}

/* A gold corner tick, the identity's mark for "this is the figure". Drawn on the
   frame rather than sat inside it so it costs no layout, and redrawn on each
   step - the smallest possible signal that the panel turned over. */
.khero::before {
  content: '';
  position: absolute;
  top: 0;
  left: 0;
  width: 2.25rem;
  height: 2px;
  background: var(--gold-500);
  transform-origin: left center;
}

/* The one piece of pure atmosphere, and it is tied to an event: it re-lights on
   each step, under the figure, so the change registers peripherally for a reader
   whose eyes are still on the headline to the left. Blurred gold at four per
   cent - at rest it is barely a warmth in the corner. */
.khero__halo {
  position: absolute;
  z-index: -1;
  left: -12%;
  bottom: -18%;
  width: 78%;
  aspect-ratio: 1;
  border-radius: 50%;
  background: radial-gradient(circle,
    color-mix(in srgb, var(--c-gold) 26%, transparent), transparent 68%);
  filter: blur(18px);
  opacity: 0.3;
  pointer-events: none;
}

/* Three bands, pinned: where it happened at the top, the figure through the
   middle, what else moved along the bottom. Spread rather than centred, because
   a stretched frame centred on its content is a frame with a third of itself
   empty at both ends - which is what this was. The min-height reserves the
   tallest case so stepping through cannot resize the masthead under the
   reader.

   15.85rem is the measured height of all eleven at this width, not a round
   number chosen for comfort. It was 17.5rem while the panel was the narrow
   column of a two-column masthead and the cases wrapped to different line
   counts; on the full centred measure they all fit the same lines, and the
   difference was 27px of reserved-but-never-used space inside the frame. */
.khero__stage {
  display: grid;
  grid-template-rows: auto minmax(0, 1fr) auto;
  gap: 1rem;
  min-height: 15.85rem;
  touch-action: pan-y;
  cursor: grab;
}

.khero__stage:active { cursor: grabbing; }
.khero__stage:focus-visible { outline: 2px solid var(--gold-300); outline-offset: 4px; }

/* The panel is stretched to the height of the headline column beside it, and a
   block centred in that height left about ninety pixels of dead air above it
   and ninety below - which is the emptiness the frame kept being accused of.
   Distributing spends the same slack as two intentional gaps instead: the
   figure hangs off the line that says where it happened, the company sits on
   the hairline above what else moved, and nothing floats in the middle. */
.khero__body {
  display: grid;
  align-content: space-between;
  gap: 1rem;
}

/* The figure and its caption are one statement and the distribution above must
   not open a gap between them, so they travel as a single row. */
.khero__stat {
  display: grid;
  gap: 0.35rem;
}

/* Exit is shorter than enter, so a step feels answered rather than laboured, and
   it leaves in the direction the step is going - forward slides left, back
   slides right, the way a reader expects a sequence to behave. */
.khero__stage {
  transition: opacity 240ms ease-out, transform 240ms ease-out;
}
.khero__stage.is-out {
  opacity: 0;
  transform: translate3d(calc(var(--khero-dir, 1) * -16px), 0, 0);
  transition-duration: 150ms;
  transition-timing-function: ease-in;
}

/* ── entrance ──────────────────────────────────────────────────────────────
   The four lines and the strip arrive in reading order rather than together, at
   45ms apart: enough for the eye to follow the figure down to what it cost, not
   enough to be waited on. `backwards` holds each at its start until its turn, so
   nothing flashes in place first. */
@media (prefers-reduced-motion: no-preference) {
  .khero.is-in .khero__meta   { animation: khero-line 460ms var(--khero-ease)       backwards; }
  .khero.is-in .khero__figure { animation: khero-line 460ms var(--khero-ease)  45ms backwards; }
  .khero.is-in .khero__note   { animation: khero-line 460ms var(--khero-ease)  90ms backwards; }
  .khero.is-in .khero__who    { animation: khero-line 460ms var(--khero-ease) 135ms backwards; }
  .khero.is-in .khero__gains  { animation: khero-line 460ms var(--khero-ease) 180ms backwards; }
  .khero.is-in::before        { animation: khero-mark 560ms var(--khero-ease); }
  .khero.is-in .khero__halo   { animation: khero-halo 900ms ease-out; }
}

@keyframes khero-line {
  from { opacity: 0; transform: translate3d(calc(var(--khero-dir, 1) * 18px), 5px, 0); }
}
@keyframes khero-mark {
  from { transform: scaleX(0); }
}
@keyframes khero-halo {
  from { opacity: 0.05; transform: scale(0.82); }
}

/* ── where ────────────────────────────────────────────────────────────────── */

.khero__meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: 0.4rem;
  margin: 0;
  font-size: var(--fs-micro);
  font-weight: 500;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: color-mix(in srgb, #fff 66%, transparent);
}

.khero__flag {
  width: 21px;
  height: 14px;
  flex: none;
  border-radius: 1.5px;
  box-shadow: 0 0 0 1px color-mix(in srgb, #fff 22%, transparent);
}

/* The country and the sector are the two facets the library filters on, and
   here they are already named for the case on screen - so they are the controls
   rather than a caption of them. Inline text targets, given 24px of height in
   padding rather than in leading. */
.khero__pin {
  display: inline-flex;
  align-items: center;
  min-height: 24px;
  padding: 0 0.1rem;
  background: none;
  border: 0;
  border-bottom: 1px solid transparent;
  font: inherit;
  color: inherit;
  text-align: left;
  cursor: pointer;
  transition: color 180ms ease, border-color 180ms ease;
}
.khero__pin:hover { color: #fff; border-bottom-color: var(--gold-300); }
.khero__pin:focus-visible { outline: 2px solid var(--gold-300); outline-offset: 2px; color: #fff; }

.khero__sep { opacity: 0.45; }

.khero__count {
  flex: none;
  font-variant-numeric: tabular-nums;
  color: color-mix(in srgb, #fff 42%, transparent);
}

/* ── the figure ───────────────────────────────────────────────────────────── */

/* The one thing on this half of the plane meant to be read from across a room.
   Tabular figures because it counts up on arrival and changes under itself every
   few seconds - proportional digits would make it twitch on both counts. */
.khero__figure {
  margin: 0;
  font-size: clamp(2.75rem, 5.5vw, 4.25rem);
  font-weight: 500;
  line-height: 0.95;
  letter-spacing: -0.035em;
  color: var(--gold-300);
  font-variant-numeric: tabular-nums;
}

.khero__note {
  max-width: 34ch;
  margin-inline: auto;
  font-size: clamp(1rem, 1.15vw, 1.1rem);
  line-height: 1.5;
  color: color-mix(in srgb, #fff 82%, transparent);
}

.khero__who {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: 0.5rem 0.75rem;
  margin: 0;
}

.khero__co { font-size: 0.98rem; font-weight: 550; color: #fff; }

.khero__siri {
  padding: 0.18rem 0.55rem;
  font-size: var(--fs-micro);
  font-weight: 500;
  color: var(--blue-900);
  background: var(--blue-300);
  border-radius: 999px;
  white-space: nowrap;
}

/* Same caveat the library puts on these six, worded the same way. */
.khero__scope {
  padding: 0.18rem 0.55rem;
  font-size: var(--fs-micro);
  color: color-mix(in srgb, #fff 70%, transparent);
  background: color-mix(in srgb, #fff 10%, transparent);
  border-radius: 999px;
  white-space: nowrap;
}

/* ── what else moved ──────────────────────────────────────────────────────── */

/* The secondary figures the same story reports. They are what the panel used to
   leave as empty frame, and they are the answer to the question the headline
   figure raises - 48% of profit, from what? The min-height reserves the row for
   the cases that report only one, so the frame does not breathe as it steps. */
.khero__gains {
  display: flex;
  min-height: 3.4rem;
  margin: 0;
  padding: 0.85rem 0 0;
  list-style: none;
  border-top: 1px solid color-mix(in srgb, #fff 14%, transparent);
}

.khero__gains li {
  flex: 1 1 0;
  min-width: 0;
  padding: 0 0.85rem;
  border-left: 1px solid color-mix(in srgb, #fff 10%, transparent);
}
.khero__gains li:first-child { border-left: 0; }

.khero__gains b {
  display: block;
  font-size: 1.05rem;
  font-weight: 500;
  color: #fff;
  font-variant-numeric: tabular-nums;
}

.khero__gains span {
  display: block;
  margin-top: 0.15rem;
  font-size: 0.76rem;
  line-height: 1.35;
  color: color-mix(in srgb, #fff 58%, transparent);
}

/* ── controls ─────────────────────────────────────────────────────────────── */

/* The ticks take a row of their own. Sharing one with the buttons left eleven
   marks about eighteen pixels each at desktop width, well under the minimum
   target size - and the panel is narrower than the copy beside it, so there was
   never going to be room for both. */
/* min-width: 0 because this is a grid item, and a grid item's automatic minimum
   size is its min-content width - which is the twenty-five marks laid end to
   end. Without it the foot stretches to 648px inside a 356px panel and the
   scrolling strip below never gets the chance to scroll: it is the row holding
   it that overflows, and the frame's `overflow: hidden` cuts the tail off. */
.khero__foot {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: 0.35rem 1rem;
  min-width: 0;
}

/* One mark per case, and there are twenty-five of them since the library grew
   on 2026-09-09. Twenty-five marks at their 24px minimum plus the gaps want
   672px, which is one pixel under the strip's own width at desktop and roughly
   double what it gets on a phone - so below about 700px the marks used to run
   straight out of the panel and be cut off by the frame's `overflow: hidden`,
   thirteen of the twenty-five invisible and unhittable.

   The strip scrolls instead. The marks keep their minimum target size, they
   still grow to fill the row wherever there is room for all of them, and
   js/knowledge.js pulls the selected one into view on every step, so the reader
   always sees where in the set they are without the panel changing height. The
   scrollbar is suppressed: it would sit across a two-pixel hairline, and the
   arrows, the counter and the drag are the controls this shares the row with. */
.khero__ticks {
  flex: 1 0 100%;
  order: -1;
  display: flex;
  gap: 3px;
  overflow-x: auto;
  overscroll-behavior-x: contain;
  scrollbar-width: none;
}
.khero__ticks::-webkit-scrollbar { display: none; }

/* 44px of height on a 2px mark: the bar is what is read, the button is what is
   hit. Padding rather than a taller bar keeps the row visually a hairline. */
.khero__tick {
  flex: 1 1 auto;
  min-width: 24px;
  height: 44px;
  padding: 21px 0;
  background: none;
  border: 0;
  cursor: pointer;
}

.khero__tick > span {
  display: block;
  position: relative;
  height: 2px;
  overflow: hidden;
  border-radius: 1px;
  background: color-mix(in srgb, #fff 22%, transparent);
  transition: background 200ms ease, transform 200ms var(--khero-ease);
}

/* The mark thickens under the pointer instead of only lightening, so the target
   answers before it is clicked. scaleY on a 2px bar, so it costs no layout. */
.khero__tick:hover > span,
.khero__tick:focus-visible > span {
  background: color-mix(in srgb, #fff 45%, transparent);
  transform: scaleY(2);
}

/* The selected tick is marked on its track, not only by the fill sweeping over
   it. A pause landing on nought progress would otherwise leave twenty-five
   identical marks and no way to tell which case is on screen. */
.khero__tick[aria-selected='true'] > span {
  background: color-mix(in srgb, var(--gold-300) 34%, transparent);
}
/* Inset, not offset. The strip is a scroll container now and it is exactly as
   tall as the button, so a ring drawn 2px outside the button is clipped by it. */
.khero__tick:focus-visible { outline: 2px solid var(--gold-300); outline-offset: -2px; border-radius: 2px; }

/* The fill is both the progress of the dwell and the marker for which case is
   showing, so a reader who has motion turned off still sees a full gold bar on
   the current one rather than eleven identical marks. */
.khero__tick > span::after {
  content: '';
  position: absolute;
  inset: 0;
  background: var(--gold-300);
  transform-origin: left center;
  transform: scaleX(0);
}

.khero__tick[aria-selected='true'] > span::after { transform: scaleX(1); }

@media (prefers-reduced-motion: no-preference) {
  .khero__tick[aria-selected='true'] > span::after {
    animation: khero-fill var(--khero-dwell, 6200ms) linear forwards;
  }
}

/* Freezing the animation rather than removing it is what lets a pause resume
   where it stopped instead of restarting the case. */
.khero.is-paused .khero__tick[aria-selected='true'] > span::after {
  animation-play-state: paused;
}

@keyframes khero-fill {
  from { transform: scaleX(0); }
  to   { transform: scaleX(1); }
}

.khero__nav { display: flex; gap: 0.15rem; }
.khero__acts { display: flex; align-items: center; gap: 0.35rem; }

.khero__step,
.khero__toggle {
  display: grid;
  place-items: center;
  width: 44px;
  height: 44px;
  flex: none;
  padding: 0;
  background: none;
  border: 0;
  border-radius: var(--corner-btn);
  color: color-mix(in srgb, #fff 62%, transparent);
  cursor: pointer;
  transition: color 200ms ease, background 200ms ease;
}

.khero__step:hover,
.khero__toggle:hover { color: #fff; background: color-mix(in srgb, #fff 8%, transparent); }
.khero__step:focus-visible,
.khero__toggle:focus-visible { outline: 2px solid var(--gold-300); outline-offset: -2px; color: #fff; }
.khero__step svg { width: 17px; height: 17px; }
.khero__toggle svg { width: 15px; height: 15px; fill: currentColor; }

/* The arrows lean the way they point when pressed - the only feedback a step
   button can give that is not the result itself. */
.khero__step:active svg { transform: translateX(calc(var(--khero-lean, 0) * 2px)); }
.khero__step[data-prev] { --khero-lean: -1; }
.khero__step[data-next] { --khero-lean: 1; }

.khero__open {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  min-height: 44px;
  padding: 0 0.25rem;
  background: none;
  border: 0;
  font: inherit;
  font-size: 0.85rem;
  font-weight: 500;
  color: #fff;
  cursor: pointer;
  white-space: nowrap;
}

.khero__open span { transition: transform 200ms ease; }
.khero__open:hover { color: var(--gold-300); }
.khero__open:hover span { transform: translateX(3px); }
.khero__open:focus-visible { outline: 2px solid var(--gold-300); outline-offset: 2px; border-radius: 2px; }

/* Under the two-column breakpoint the panel sits below the copy, where it has
   the full width and does not need to be as tall. */
/* The reservation is per breakpoint because what is tallest changes with the
   width: at one column it is Luna, whose name runs to three lines beside its
   badge, and at 820px it is Miyahuna, whose caption wraps to two. Each value is
   the measured tallest of the eleven at that width plus nothing - a floor set
   under the shortest case would let the masthead grow and shrink under a reader
   who is only watching it turn over. */
@media (max-width: 1023px) {
  .khero__stage { min-height: 16rem; }
}

@media (max-width: 560px) {
  /* 326px is Luna at this width - the longest company name, which takes three
     lines beside its badge. Every other case is shorter and spends the
     difference as distributed air rather than as a change of height. */
  .khero__stage { min-height: 20.4rem; }

  /* And the strip is reserved at its own three-gain height, so the hairline
     above it holds one line on the screen for all eleven instead of stepping up
     for the two cases that report fewer. */
  .khero__gains { min-height: 4.5rem; }

  /* Country and sector together are wider than a phone at this tracking, and a
     wrapping flex row broke them mid-phrase - "Saudi" over "Arabia", with the
     middle dot stranded. Two named rows instead, so the break falls between the
     two facets rather than inside one of them. Both rows centre on the panel;
     the count trails the sector rather than being pushed to the right edge,
     which is where it went back when the row was left-aligned. */
  .khero__meta {
    display: grid;
    grid-template-columns: auto auto auto;
    justify-content: center;
    grid-template-areas:
      'flag country count'
      'sector sector sector';
    row-gap: 0.15rem;
    align-items: center;
  }
  .khero__flagwrap { grid-area: flag; display: flex; }
  .khero__meta [data-pin-country] { grid-area: country; }
  .khero__meta [data-pin-sector]  { grid-area: sector; justify-self: center; }
  .khero__count { grid-area: count; }
  .khero__sep { display: none; }
  /* Three across on a phone as well, rather than wrapping to two rows. Wrapped,
     the eight cases that report three gains took two rows and the two that
     report one or two took one, so the hairline above the strip and the whole
     panel under it moved every few seconds. Narrower columns and smaller type
     hold all three on one line at 335px, and the case with a single gain gets
     the full width instead of an empty second row. */
  .khero__gains li { padding: 0 0.5rem; }
  .khero__gains b { font-size: 0.95rem; }
  .khero__gains span { font-size: var(--fs-micro); }

  /* The strip scrolls at this width - twenty-five marks have never fitted on a
     phone - so the gap is no longer buying anyone a target size. It is closed
     anyway: it decides how many of the marks are in view at once, and 2px puts
     twelve of the set on screen instead of eleven. */
  .khero__ticks { gap: 2px; }
}
/* The case the spotlight sent the reader to, marked for a moment so it is
   obvious which of eleven rows answered the click. */
.kcase.is-target { animation: kcase-land 1.6s ease-out; }

@keyframes kcase-land {
  from { background: color-mix(in srgb, var(--gold-500) 22%, transparent); }
  to   { background: transparent; }
}

@media (prefers-reduced-motion: reduce) {
  .kcase.is-target { animation: none; outline: 2px solid var(--gold-500); outline-offset: -2px; }
}

/* ── the case library ─────────────────────────────────────────────────── */

.klib { margin-top: clamp(1.5rem, 3vh, 2.25rem); }

.klib__rows {
  margin-top: 1.1rem;
  border-top: 1px solid var(--line);
}

.kcase { border-bottom: 1px solid var(--line); }

/* The grid is the point of the row. Six tracks, each holding the same kind of
   thing on every row, so the columns can be read down rather than across.
   Country first because that is the axis GOIC's own document is organised on.

   Every track but the name is a fixed width, and that is the whole trick. The
   badge track used to be `auto` and the chevron had no track of its own, so a
   row with a short badge gave its spare width to the 1fr name track and pushed
   sector, metric and badge right - by up to 90px, on that row alone, which is
   exactly the column the reader is trying to scan downward. The badge track is
   sized for the widest of the two pills, "International benchmark". */
.kcase__head {
  width: 100%;
  display: grid;
  grid-template-columns: 10.5rem minmax(0, 1fr) 11rem 12rem 9.75rem 0.75rem;
  align-items: center;
  gap: 0.5rem 1.5rem;
  padding: 1.15rem 0.5rem 1.15rem 0;
  background: none;
  border: 0;
  text-align: left;
  font: inherit;
  color: inherit;
  cursor: pointer;
  transition: background 0.2s ease;
}

/* Background only. An earlier pass nudged the row right on hover, which both
   reflowed the grid on every mouseover and broke the column alignment the row
   exists for - the reader is scanning a column downward, and a row that shifts
   under the cursor is the one thing that read cannot survive.

   The corner mark rides on that background. .kcase__head is a button, so it
   takes --corner-btn like every other button on the site; it just has nothing
   to draw it on until it is hovered, opened or focused, because the row itself
   is a hairline in a list rather than a filled box. */
.kcase__head {
  border-radius: var(--corner-btn);
}
.kcase__head:hover,
.kcase__head:focus-visible {
  background: var(--surface-2);
}

.kcase__head:focus-visible { outline: 2px solid var(--blue-600); outline-offset: -2px; }

.kcase__where {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  font-size: 0.8rem;
  color: var(--ink-3);
}

.kcase__flag {
  width: 21px;
  height: 14px;
  flex: none;
  border-radius: 1.5px;
  box-shadow: 0 0 0 1px rgba(20, 30, 38, 0.10);
}

/* min-width:0 on both the track's child and its own grid items, because a grid
   item's default min-width is min-content: without it the longest word in a
   company name sets a floor the 1fr track cannot go under, and the four columns
   to its right are pushed out of line on that row alone. `anywhere` rather than
   `break-word` is deliberate - only `anywhere` also shrinks the min-content
   size, which is the half that actually frees the track. The names that need it
   are the long bilingual ones, e.g. "National Group for Agriculture and Animal
   Products (Mazzraty)". */
.kcase__name { display: grid; gap: 0.1rem; min-width: 0; }
.kcase__name > * { min-width: 0; }
.kcase__name b {
  font-size: 1.02rem;
  font-weight: 550;
  line-height: 1.3;
  overflow-wrap: anywhere;
}

.kcase__ar {
  font-family: 'Noto Kufi Arabic', system-ui, sans-serif;
  font-size: 0.82rem;
  color: var(--ink-3);
}

.kcase__sector { font-size: 0.82rem; color: var(--ink-2); line-height: 1.4; }

/* The one figure GOIC puts under the company name, given the weight that claim
   deserves and nothing more - it is their number, not a measurement of ours. */
.kcase__metric {
  font-size: 0.9rem;
  font-weight: 500;
  color: var(--gold-700);
  line-height: 1.4;
}

/* Holds nought, one or two pills. A column in the row grid either way, so the
   chevron beside it keeps its own track on every row. */
.kcase__tags {
  display: flex;
  flex-wrap: wrap;
  gap: 0.3rem;
  min-width: 0;
}

.kcase__siri {
  justify-self: start;
  padding: 0.2rem 0.55rem;
  font-size: var(--fs-micro);
  font-weight: 500;
  color: var(--blue-700);
  background: var(--blue-50);
  border-radius: 999px;
  white-space: nowrap;
}

/* Quieter than the SIRI badge on purpose. It is a caveat, not a credential. */
.kcase__scope {
  justify-self: start;
  padding: 0.2rem 0.55rem;
  font-size: var(--fs-micro);
  color: var(--ink-3);
  background: var(--surface-3);
  border-radius: 999px;
  white-space: nowrap;
}

.kcase__chev {
  justify-self: end;
  font-size: 1.35rem;
  line-height: 1;
  color: var(--ink-3);
  transition: transform 0.25s ease, color 0.2s ease;
}

.kcase.is-open .kcase__chev { transform: rotate(90deg); color: var(--blue-700); }
/* Not rounded, deliberately: .kcase is a row in a hairline-separated list, and
   its only edge is the border-bottom it shares with the row beneath. A radius
   here curves the tint away from that hairline at the bottom right and leaves a
   wedge of page above the line - it reads as a rendering fault rather than as
   the corner mark. The mark is carried by the row's button instead. */
.kcase.is-open { background: var(--surface-2); }

.kcase__body {
  padding: 0 1.5rem 1.6rem 0.75rem;
  max-width: 62ch;
}

.kcase__body p {
  margin: 0 0 0.85rem;
  font-size: 0.94rem;
  line-height: 1.72;
  color: var(--ink-2);
}

.kcase__body p:last-child { margin-bottom: 0; }

@media (max-width: 1080px) {
  .kcase__head {
    grid-template-columns: minmax(0, 1fr) auto;
    gap: 0.35rem 1rem;
    padding-right: 0;
  }
  .kcase__where { grid-column: 1; order: 1; }
  .kcase__name { grid-column: 1; order: 2; }
  .kcase__sector { grid-column: 1; order: 3; }
  .kcase__metric { grid-column: 1; order: 4; }
  .kcase__tags { grid-column: 1; order: 5; }
  /* Safe to drop only here. Stacked, the cell is one row of a single column and
     an empty one still costs a row gap; wide, removing it would hand the
     chevron the badge's track. */
  .kcase__tags:empty { display: none; }
  .kcase__chev { grid-column: 2; grid-row: 1 / -1; order: 0; align-self: center; }
}

/* ── the reports shelf ────────────────────────────────────────────────── */

/* The corner mark goes on the box, not on the cells. .kreps is a 1px-gap
   mosaic - the gaps are this element's own background showing between the
   cards - so rounding each .krep would cut a notch out of every interior
   junction and leave the mosaic looking chipped. The container carries the
   radius and clips the cards to it, which is what css/services.css already does
   for .svstats, the same construction. */
.kreps {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 19rem), 1fr));
  gap: 1px;
  margin-top: clamp(1.5rem, 3vh, 2.25rem);
  background: var(--line);
  border: 1px solid var(--line);
  border-radius: var(--corner-box);
  overflow: hidden;
}

.krep {
  display: grid;
  align-content: start;
  gap: 0.45rem;
  padding: 1.5rem 1.4rem 1.3rem;
  background: var(--surface);
  color: inherit;
  text-decoration: none;
  transition: background 0.2s ease;
}

/* Dormant since the shelf went dark, 2026-08-29 - the section is
   .svsection--dark now and neither of these matches. Kept because they are the
   tint variant's whole definition and the band is one class away from coming
   back. Nothing else was needed for the dark ground: every colour above reads a
   token, and css/services.css re-points the whole set inside the band. Measured
   ladder there - band #141e26, card #1b2a34, hover #223744, pill #243b49 - each
   step lighter than the last, which is the inversion that block documents. */
.svsection--tint .kreps { background: var(--line); }
.svsection--tint .krep { background: var(--surface-2); }

.krep:hover { background: var(--blue-50); }
.krep:focus-visible { outline: 2px solid var(--blue-600); outline-offset: -2px; }

.krep__cat {
  font-size: var(--fs-micro);
  font-weight: 500;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--gold-700);
}

.krep__title { margin: 0; font-size: 1.05rem; font-weight: 550; line-height: 1.32; }

/* The line under a report's heading is whichever language the heading is not -
   Arabic on an English page, English on an Arabic one - so it was renamed from
   .krep__ar and the Arabic face is now applied by language rather than by
   class. Selecting on the attribute the markup already has to carry for the
   browser's sake means there is only one place that decides. */
.krep__alt {
  margin: 0;
  font-size: 0.85rem;
  color: var(--ink-3);
}

.krep__alt[lang="ar"] { font-family: 'Noto Kufi Arabic', system-ui, sans-serif; }

.krep__desc {
  margin: 0.25rem 0 0;
  font-size: 0.875rem;
  line-height: 1.6;
  color: var(--ink-2);
}

/* Language and size sit together at the foot because they answer the same
   question, which is whether it is worth opening. Four of the six are Arabic
   only and one is 125 MB; both are things to know before the click, not after. */
.krep__foot {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.4rem 0.75rem;
  margin-top: 0.9rem;
  padding-top: 0.8rem;
  border-top: 1px solid var(--line-soft);
  font-size: 0.75rem;
  color: var(--ink-3);
}

.krep__org { font-weight: 500; color: var(--ink-2); }

.krep__lang {
  padding: 0.12rem 0.45rem;
  background: var(--surface-3);
  border-radius: 999px;
}

.krep__size { margin-left: auto; font-variant-numeric: tabular-nums; }

/* ── a chip with nothing behind it ────────────────────────────────────────
   Dimmed rather than removed. Chips that appear and disappear as the reader
   filters make the set feel unstable, and the count already says zero. */
.svchip.is-void { opacity: 0.38; }

