/* Domain Radar — public site theme.
 *
 * Implements the palette, typography and motion rules in DESIGN.md for the public
 * pages. Bootstrap 5.3 is themed through its own CSS variables rather than
 * overridden with utility classes, so density and colour live in one place.
 *
 * The public site is dark-first (DESIGN.md section 2). The light palette defined in
 * DESIGN.md section 3 belongs to the logged-in product and is not used here.
 */

:root {
  /* Dark palette — DESIGN.md section 3 */
  --dr-black: #06080c;
  --dr-navy: #101823;
  --dr-ice: #dce7ef;
  --dr-steel: #8fa9bb;
  --dr-radar: #738bff;
  --dr-radar-hi: #a7b5ff;
  --dr-neutral: #c6ccd2;

  /* Risk ramp, dark mode — DESIGN.md section 3 */
  --dr-risk-low: #4fb286;
  --dr-risk-medium: #e0b056;
  --dr-risk-high: #f0708a;
  --dr-risk-unknown: #8fa9bb;

  /* Hairlines and raised surfaces */
  --dr-line: rgba(143, 169, 187, 0.18);
  --dr-line-strong: rgba(143, 169, 187, 0.34);
  --dr-surface: #0c121b;

  /* Editorial type scale. Public headings are large, short and airy. */
  --dr-step--1: clamp(0.78rem, 0.76rem + 0.1vw, 0.84rem);
  --dr-step-0: clamp(0.95rem, 0.92rem + 0.15vw, 1.05rem);
  --dr-step-1: clamp(1.2rem, 1.1rem + 0.5vw, 1.5rem);
  --dr-step-2: clamp(1.6rem, 1.35rem + 1.2vw, 2.3rem);
  --dr-step-3: clamp(2.2rem, 1.6rem + 3vw, 4rem);
  --dr-step-4: clamp(2.8rem, 1.6rem + 6vw, 6.5rem);

  /* Motion — DESIGN.md section 8 */
  --dr-t-ui: 150ms;
  --dr-t-panel: 210ms;

  /* Bootstrap theming */
  --bs-body-bg: var(--dr-black);
  --bs-body-color: var(--dr-neutral);
  --bs-body-font-size: 1rem;
  --bs-body-font-family: "Inter", "Segoe UI", system-ui, -apple-system, sans-serif;
  --bs-emphasis-color: var(--dr-ice);
  --bs-link-color: var(--dr-radar-hi);
  --bs-link-hover-color: var(--dr-ice);
  --bs-border-color: var(--dr-line);
  --bs-primary: var(--dr-radar);
}

/* Bootstrap's own dark mode, so its components inherit the palette. */
[data-bs-theme="dark"] {
  --bs-body-bg: var(--dr-black);
  --bs-body-color: var(--dr-neutral);
  --bs-emphasis-color: var(--dr-ice);
  --bs-secondary-color: var(--dr-steel);
  --bs-border-color: var(--dr-line);
}

body {
  background-color: var(--dr-black);
  color: var(--dr-neutral);
  font-size: var(--dr-step-0);
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
}

/* Numeric data is primary content, not decoration — DESIGN.md section 4. */
.dr-num {
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum" 1;
}

/* ---------------------------------------------------------------- type ---- */

.dr-eyebrow {
  font-size: var(--dr-step--1);
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--dr-steel);
  margin-bottom: 1.5rem;
}

.dr-display {
  font-size: var(--dr-step-3);
  line-height: 1.04;
  letter-spacing: -0.025em;
  font-weight: 600;
  color: var(--dr-ice);
  text-wrap: balance;
}

.dr-heading {
  font-size: var(--dr-step-2);
  line-height: 1.15;
  letter-spacing: -0.02em;
  font-weight: 600;
  color: var(--dr-ice);
  text-wrap: balance;
}

.dr-lede {
  font-size: var(--dr-step-1);
  line-height: 1.5;
  color: var(--dr-steel);
  max-width: 46ch;
}

.dr-figure {
  font-size: var(--dr-step-4);
  line-height: 0.92;
  letter-spacing: -0.04em;
  font-weight: 600;
  color: var(--dr-ice);
  display: block;
}

.dr-figure-label {
  font-size: var(--dr-step-1);
  color: var(--dr-steel);
  letter-spacing: 0.01em;
}

/* ------------------------------------------------------------- sections ---- */

.dr-section {
  padding-block: clamp(4rem, 3rem + 6vw, 9rem);
  border-top: 1px solid var(--dr-line);
  position: relative;
}

.dr-section--flush {
  border-top: 0;
}

.dr-container {
  width: 100%;
  max-width: 1320px;
  margin-inline: auto;
  /* The minimum is 1.5rem, not 1rem, because Bootstrap's g-5 rows pull themselves
   * 1.5rem wider than their parent on each side and expect the container's padding
   * to absorb it. With 1rem the page scrolled sideways on a phone. */
  padding-inline: clamp(1.5rem, 0.5rem + 2vw, 3rem);
}

/* Full-bleed photographic section. The image is a real <img> rather than a CSS
 * background so the browser discovers it during preload scanning and can pick a
 * srcset candidate — both matter for LCP. It sits behind the content via z-index,
 * with a scrim on top so light text stays AA-legible over any photograph. */
.dr-bleed,
.dr-hero--photo {
  position: relative;
  isolation: isolate;
  overflow: hidden;
}

.dr-bleed__img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  z-index: -2;
}

.dr-bleed::after,
.dr-hero--photo::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  /* The source images are already very dark — measured median luminance behind the
   * text column is 0.0022, so body copy clears AA at roughly 7:1 with no scrim at
   * all. This gradient therefore only anchors the text edge and fades out fast,
   * letting the photograph read at close to full strength. */
  background:
    linear-gradient(
      100deg,
      rgba(6, 8, 12, 0.72) 0%,
      rgba(6, 8, 12, 0.5) 28%,
      rgba(6, 8, 12, 0.18) 52%,
      rgba(6, 8, 12, 0) 72%
    ),
    linear-gradient(
      180deg,
      rgba(6, 8, 12, 0.2) 0%,
      rgba(6, 8, 12, 0) 35%,
      rgba(6, 8, 12, 0.45) 100%
    );
}

/* The closing section is read top-to-bottom, so it dims vertically instead. */
.dr-bleed::after {
  background: linear-gradient(
    180deg,
    rgba(6, 8, 12, 0.72) 0%,
    rgba(6, 8, 12, 0.42) 45%,
    rgba(6, 8, 12, 0.78) 100%
  );
}

/* Scrim for photographic sections. Background photographs sit behind light text,
 * so they are dimmed here rather than re-exported darker. */
.dr-photo {
  position: relative;
  isolation: isolate;
  background-size: cover;
  background-position: center;
}

.dr-photo::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background: linear-gradient(
    180deg,
    rgba(6, 8, 12, 0.72) 0%,
    rgba(6, 8, 12, 0.82) 55%,
    rgba(6, 8, 12, 0.95) 100%
  );
}

/* Text sitting directly on a photograph. The scrim handles the broad background,
 * but a photo can still put a bright point (a city light) right behind a letter.
 * A soft shadow protects those cases locally, so the image keeps its full
 * brightness instead of being dimmed everywhere to solve a few pixels. */
.dr-hero--photo .dr-display,
.dr-hero--photo .dr-lede,
.dr-hero--photo .dr-eyebrow,
.dr-hero--photo .dr-layers li,
.dr-bleed .dr-heading,
.dr-bleed .dr-lede,
.dr-bleed .dr-eyebrow,
.dr-bleed .dr-audience li {
  text-shadow: 0 1px 12px rgba(6, 8, 12, 0.85), 0 1px 3px rgba(6, 8, 12, 0.7);
}

/* --------------------------------------------------------------- header ---- */

.dr-masthead {
  position: sticky;
  top: 0;
  z-index: 100;
  background: rgba(6, 8, 12, 0.82);
  backdrop-filter: blur(10px);
  border-bottom: 1px solid var(--dr-line);
}

.dr-wordmark {
  font-size: 0.95rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  font-weight: 600;
  color: var(--dr-ice);
  text-decoration: none;
  line-height: 1.2;
}

.dr-wordmark small {
  display: block;
  font-size: 0.62rem;
  letter-spacing: 0.2em;
  color: var(--dr-steel);
  font-weight: 400;
}

.dr-nav-link {
  color: var(--dr-steel);
  text-decoration: none;
  font-size: var(--dr-step--1);
  letter-spacing: 0.04em;
  transition: color var(--dr-t-ui) ease;
}

.dr-nav-link:hover,
.dr-nav-link:focus-visible {
  color: var(--dr-ice);
}

/* The header is sticky, so an anchor jump would land its heading underneath it.
 * The padding is the header's height plus a little air. */
html {
  scroll-padding-top: 5.5rem;
}

.dr-masthead__bar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  column-gap: 1rem;
}

.dr-masthead__compact {
  display: flex;
  align-items: center;
  gap: 0.75rem;
}

/* Three hairlines drawn in CSS: no icon font, and the button is 44px square so it
 * is an easy target on a phone. */
.dr-menu-toggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  padding: 0;
  border: 1px solid var(--dr-line-strong);
  border-radius: 2px;
  background: transparent;
  color: var(--dr-ice);
}

.dr-menu-toggle__icon,
.dr-menu-toggle__icon::before,
.dr-menu-toggle__icon::after {
  display: block;
  width: 18px;
  height: 1.5px;
  background: currentColor;
}

.dr-menu-toggle__icon {
  position: relative;
}

.dr-menu-toggle__icon::before,
.dr-menu-toggle__icon::after {
  content: "";
  position: absolute;
  left: 0;
}

.dr-menu-toggle__icon::before {
  top: -6px;
}

.dr-menu-toggle__icon::after {
  top: 6px;
}

.dr-menu-toggle:hover,
.dr-menu-toggle[aria-expanded="true"] {
  border-color: var(--dr-radar-hi);
}

/* Folded: the menu takes the full width under the bar, one link per row. */
.dr-nav {
  flex-basis: 100%;
}

.dr-nav .dr-nav-link {
  display: block;
  padding-block: 0.8rem;
  border-top: 1px solid var(--dr-line);
  font-size: var(--dr-step-0);
}

.dr-nav .dr-btn {
  margin-block: 0.75rem 0.25rem;
}

/* Wide enough for one line: always shown, whatever the collapse state says. */
@media (min-width: 992px) {
  .dr-masthead .dr-nav.collapse {
    display: flex;
    flex-basis: auto;
    align-items: center;
    gap: 1.5rem;
  }

  .dr-nav .dr-nav-link {
    display: inline;
    padding-block: 0;
    border-top: 0;
    font-size: var(--dr-step--1);
  }

  .dr-nav .dr-btn {
    margin-block: 0;
  }
}

/* -------------------------------------------------------------- buttons ---- */

.dr-btn {
  display: inline-flex;
  align-items: center;
  gap: 0.6rem;
  padding: 0.7rem 1.4rem;
  border-radius: 2px;
  font-size: var(--dr-step--1);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  text-decoration: none;
  border: 1px solid var(--dr-line-strong);
  color: var(--dr-ice);
  background: transparent;
  transition: border-color var(--dr-t-ui) ease, background-color var(--dr-t-ui) ease;
}

.dr-btn:hover,
.dr-btn:focus-visible {
  border-color: var(--dr-radar-hi);
  background: rgba(115, 139, 255, 0.1);
  color: var(--dr-ice);
}

.dr-btn--primary {
  background: var(--dr-radar);
  border-color: var(--dr-radar);
  color: #060810;
  font-weight: 600;
}

.dr-btn--primary:hover,
.dr-btn--primary:focus-visible {
  background: var(--dr-radar-hi);
  border-color: var(--dr-radar-hi);
  color: #060810;
}

/* Visible keyboard focus — DESIGN.md section 19. */
a:focus-visible,
button:focus-visible {
  outline: 2px solid var(--dr-radar-hi);
  outline-offset: 3px;
}

/* ----------------------------------------------------------------- hero ---- */

.dr-hero {
  min-height: min(92vh, 900px);
  display: flex;
  align-items: center;
  padding-block: clamp(3rem, 2rem + 6vw, 7rem);
}

.dr-hero-grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: clamp(2.5rem, 2rem + 4vw, 5rem);
  align-items: center;
}

@media (min-width: 992px) {
  .dr-hero-grid {
    /* Asymmetric, per DESIGN.md section 5 — not a 50/50 split. */
    grid-template-columns: minmax(0, 0.92fr) minmax(0, 1.08fr);
  }
}

/* The hero's second column: the layer legend. It sits at the foot of the column,
 * not its middle: the cloud occupies Sweden in the photo, top right, and a
 * centred legend landed on the country's southern tip. */
.dr-hero-aside {
  justify-content: flex-end;
  align-items: flex-end;
  align-self: stretch;
}

/* ------------------------------------------------------- hero map stage ---- */

/* A layer with the photograph's exact box, so anything placed in it in the
 * image's own percent coordinates lands on the same spot of the map at every
 * window size. The stage fills the hero like the <img> does and is a size
 * container; .dr-hero-map then re-derives object-fit: cover from the container's
 * width and height — scale to the larger of the two ratios, placed by the same
 * --hero-focus the <img>'s object-position uses.
 *
 * The ratio is the image's: width="2800" height="1200" on the <img>, which every
 * rendered size of 01-hero-europe-map shares. Change both together. */
.dr-hero-stage {
  position: absolute;
  inset: 0;
  /* Above the photo (-2) and its scrim (-1), below the copy (1). */
  z-index: 0;
  container-type: size;
  pointer-events: none;
}

.dr-hero--photo > .dr-container {
  position: relative;
  z-index: 1;
}

/* Where the photograph is framed horizontally, as a fraction: 0.5 is centred.
 * One variable, read by both the <img>'s object-position and the map layer
 * below, so the cloud can never drift off Sweden when the framing changes. */
.dr-hero--photo {
  --hero-focus: 0.5;
}

/* Single-column hero. A centred crop of a 21:9 image shows only the Atlantic, UK
 * and France, so Sweden fell outside the frame; the crop is shifted east to keep
 * Scandinavia in view. That puts the text over the brightest part of the map —
 * measured, the lede dropped to 2.8:1 and the eyebrow to 2.0:1 — so the photo
 * becomes a band at the top instead of a full background: Sweden sits in the
 * band, and the copy starts below it where the photo fades out to black.
 *
 * The band has a fixed height rather than the hero's. The map's size follows
 * its box, so if the box followed the text, Sweden would grow and shrink with
 * the copy. */
@media (max-width: 991.98px) {
  .dr-hero--photo {
    --hero-focus: 0.72;
    --hero-band: clamp(520px, 140vw, 760px);
    display: block;
    min-height: 0;
    /* Sweden ends at 45.8% of the band; start the copy just below it. */
    padding-top: calc(var(--hero-band) * 0.53);
  }

  .dr-hero--photo .dr-bleed__img,
  .dr-hero--photo .dr-hero-stage {
    bottom: auto;
    height: var(--hero-band);
  }

  /* Only the photograph fades; the cloud sits above the fade. */
  .dr-hero--photo .dr-bleed__img {
    -webkit-mask-image: linear-gradient(to bottom, #000 32%, transparent 78%);
    mask-image: linear-gradient(to bottom, #000 32%, transparent 78%);
  }
}

.dr-hero--photo .dr-bleed__img {
  object-position: calc(var(--hero-focus) * 100%) 50%;
}

.dr-hero-map {
  --map-w: max(100cqw, 100cqh * 2800 / 1200);
  --map-h: max(100cqh, 100cqw * 1200 / 2800);
  position: absolute;
  width: var(--map-w);
  height: var(--map-h);
  /* object-position P% places the image at (box - image) * P; same here. */
  left: calc((100cqw - var(--map-w)) * var(--hero-focus));
  top: calc((100cqh - var(--map-h)) / 2);
}

/* Sweden's bounding box on the photo; position and size come inline from the
 * view's traced outline. */
.dr-hero-field {
  position: absolute;
  isolation: isolate;
}

/* A soft local scrim behind the cloud. Sweden is the brightest part of the
 * photograph, and brighter dots cannot win against it; knocking the map back
 * under the cloud can, without dimming the rest of the image. */
.dr-hero-field::before {
  content: "";
  position: absolute;
  inset: -7% -32%;
  z-index: -1;
  background: radial-gradient(
    closest-side,
    rgba(6, 8, 12, 0.92) 0%,
    rgba(6, 8, 12, 0.84) 66%,
    rgba(6, 8, 12, 0) 100%
  );
}

/* Sized against the map, not the viewport: the map grows past the viewport when
 * the window is tall and narrow, and the dots should grow with the country. */
.dr-hero-field .dr-cloud__dot {
  width: max(2px, calc(var(--dot-r, 1) * var(--map-w) * 0.003));
}

.dr-hero-field .dr-cloud__dot--hi {
  box-shadow: 0 0 5px 1px rgba(167, 181, 255, 0.5);
}

/* Layer legend beside the hero copy. The hairline gives it a spine so it reads as
 * an index of what is measured, not as floating labels. */
.dr-layers {
  list-style: none;
  margin: 0;
  padding: 0 0 0 1.25rem;
  border-left: 1px solid var(--dr-line);
  font-size: var(--dr-step--1);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--dr-steel);
}

.dr-layers li {
  padding-block: 0.35rem;
  transition: color var(--dr-t-ui) ease;
}


/* ----------------------------------------------------------- population ---- */

/* Three facts under the population figure. Read as one strip rather than three
 * loose blocks: a shared top rule, hairlines between the columns, and values
 * large enough to sit under the headline figure without competing with it. */
.dr-scale {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  margin: clamp(3rem, 2rem + 3vw, 5rem) 0 0;
  border-top: 1px solid var(--dr-line);
}

.dr-scale__item {
  padding-block: 1.5rem;
}

/* Stacked, each fact gets its own rule instead of a column divider. */
.dr-scale__item + .dr-scale__item {
  border-top: 1px solid var(--dr-line);
}

@media (min-width: 768px) {
  .dr-scale {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }

  .dr-scale__item {
    padding: 1.75rem clamp(1.25rem, 0.75rem + 1.5vw, 2.5rem) 0;
  }

  .dr-scale__item:first-child {
    padding-left: 0;
  }

  .dr-scale__item + .dr-scale__item {
    border-top: 0;
    border-left: 1px solid var(--dr-line);
  }
}

.dr-scale__value {
  margin: 0;
  font-size: var(--dr-step-3);
  line-height: 1;
  letter-spacing: -0.03em;
  font-weight: 600;
  color: var(--dr-ice);
}

/* The space before the unit is a real &nbsp; in the markup, so it is read aloud
 * and never wraps; no margin here. */
.dr-scale__unit {
  font-size: 0.42em;
  letter-spacing: 0;
  font-weight: 500;
  color: var(--dr-steel);
}

.dr-scale__label {
  margin: 0.85rem 0 0;
  font-size: var(--dr-step--1);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--dr-steel);
}

/* The evidence behind the years figure: when the record starts. */
.dr-scale__note {
  display: block;
  margin-top: 0.35rem;
  letter-spacing: 0.04em;
  text-transform: none;
}

/* Sparkline-style activity bars. Decorative; the figures carry the meaning. */
.dr-spark {
  display: flex;
  align-items: flex-end;
  gap: 2px;
  height: clamp(70px, 8vw, 120px);
}

.dr-spark span {
  flex: 1 1 auto;
  background: var(--dr-radar);
  opacity: 0.55;
  border-radius: 1px 1px 0 0;
  transform-origin: bottom;
}

/* -------------------------------------------------------------- metrics ---- */

.dr-metric {
  border-top: 1px solid var(--dr-line);
  padding-top: 1.1rem;
}

.dr-metric__label {
  font-size: var(--dr-step--1);
  color: var(--dr-steel);
  display: block;
  margin-bottom: 0.5rem;
}

.dr-metric__value {
  font-size: var(--dr-step-2);
  color: var(--dr-ice);
  font-weight: 600;
  line-height: 1;
}

/* Trend direction is semantic, never decorative: whether a rise is good depends
 * on what is being measured, so the caller picks the modifier. */
.dr-delta {
  font-size: var(--dr-step--1);
  margin-left: 0.6rem;
  letter-spacing: 0.02em;
}

.dr-delta--good { color: var(--dr-risk-low); }
.dr-delta--bad { color: var(--dr-risk-high); }

/* --------------------------------------------------------------- motion ---- */

/* Nothing is hidden by CSS waiting for JavaScript. Entrances are built with
 * gsap.from(), so the CSS state IS the final state and the page is fully readable
 * with no JS, no GSAP and no motion. */

/* Point cloud in the hero. It settles in once on load and is not driven by
 * scroll; the markup holds every dot at its final opacity. */
.dr-cloud {
  position: absolute;
  inset: 0;
}

.dr-cloud__dot {
  position: absolute;
  width: calc(var(--dot-r, 1) * 0.9%);
  aspect-ratio: 1;
  border-radius: 50%;
  background: var(--dr-radar);
  transform: translate(-50%, -50%);
  will-change: opacity, transform;
}

.dr-cloud__dot--hi {
  background: var(--dr-radar-hi);
}

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

/* ---------------------------------------------------------------- cards ---- */

/* Used by both "What we observe" and "Detect structural risk". One component,
 * two contents: a labelled layer, or a question the product answers. */
.dr-card {
  border: 1px solid var(--dr-line);
  background: var(--dr-surface);
  padding: 1.25rem;
  display: flex;
  flex-direction: column;
  gap: 0.85rem;
  transition: border-color var(--dr-t-ui) ease;
}

.dr-card:hover {
  border-color: var(--dr-line-strong);
}

.dr-card__label {
  font-size: var(--dr-step--1);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--dr-ice);
  font-weight: 600;
  margin: 0;
}

.dr-card__question {
  font-size: var(--dr-step-1);
  line-height: 1.25;
  color: var(--dr-ice);
  font-weight: 500;
  margin: 0;
  text-wrap: balance;
}

.dr-card__body {
  margin: 0;
  flex: 1 1 auto;
  color: var(--dr-neutral);
}

/* The source art is square; cropping it to a band keeps the card's weight on its
 * words. margin-top:auto pins the figure to the bottom edge whatever the copy
 * length, so a row of cards lines up across different text heights. */
.dr-card__figure {
  width: 100%;
  /* The width/height attributes on the <img> are a presentational hint, which is
   * still a CSS declaration: without height:auto it wins and aspect-ratio never
   * applies, leaving every figure 800px tall. */
  height: auto;
  aspect-ratio: 3 / 2;
  object-fit: cover;
  display: block;
  margin-top: auto;
  border-radius: 2px;
}

.dr-card--question .dr-card__figure {
  aspect-ratio: 4 / 3;
}

/* ------------------------------------------------ from a domain to a nation -- */

.dr-steps {
  list-style: none;
  margin: clamp(2.5rem, 2rem + 2vw, 4rem) 0 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
  gap: clamp(1.5rem, 1rem + 2vw, 2.5rem);
  text-align: center;
}

.dr-step__figure {
  width: 100%;
  max-width: 240px;
  height: auto;
  aspect-ratio: 1;
  object-fit: cover;
  display: block;
  margin-inline: auto;
  /* 07, 08 and 09 each draw the arrow to the next step just inside their right
   * edge (verified in 07 and 09). Stacked, that arrow points at nothing, so the
   * right edge is cropped away here and only the single-row layout below keeps it.
   * Only the right side is trimmed hard: the circle is not centred the same way in
   * every image — in 09 it starts about 2% from the left — so an even crop would
   * cut into the artwork. */
  clip-path: inset(2.5% 10% 2.5% 2.5%);
}

/* The measured step is the one the eye should end on. */
.dr-step--full .dr-step__name {
  color: var(--dr-radar-hi);
}

.dr-step__name {
  margin: 0.75rem 0 0;
  color: var(--dr-ice);
  font-size: var(--dr-step-0);
}

.dr-step__count {
  margin: 0.2rem 0 0;
  color: var(--dr-steel);
  font-size: var(--dr-step--1);
  letter-spacing: 0.04em;
}

/* No CSS arrow between the steps: 07, 08 and 09 each carry the arrow to the next
 * step at their own right edge, and a second one drawn here doubled it. For the
 * same reason the figures are not masked into circles — the circle is part of the
 * artwork, and border-radius cropped the arrow in half. */
@media (min-width: 992px) {
  .dr-steps {
    grid-template-columns: repeat(4, minmax(0, 1fr));
  }

  /* All four on one line: the arrows point at the next step, so keep them. The
   * right edge is left untrimmed — the arrowhead sits in the last 3% of the
   * artwork, and even a 2.5% crop cut it down to a dash. */
  .dr-step__figure {
    clip-path: inset(2.5% 0 2.5% 2.5%);
  }
}

/* -------------------------------------------------------------- audience ---- */

.dr-audience {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 0.9rem;
  font-size: var(--dr-step-1);
  color: var(--dr-ice);
}

.dr-audience li {
  position: relative;
  padding-left: 1.75rem;
  line-height: 1.35;
}

.dr-audience li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0.45em;
  width: 8px;
  height: 8px;
  border: 1px solid var(--dr-radar-hi);
  border-radius: 50%;
}

/* ------------------------------------------------------------ resilience ---- */

/* Measured figures on the plain ground between the audience and closing photographs.
 * Built like the population strip: a shared top rule, a hairline between the columns. */
.dr-resilience {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  margin-top: clamp(3rem, 2rem + 3vw, 5rem);
  border-top: 1px solid var(--dr-line);
}

.dr-resilience__item {
  padding-block: 1.75rem;
}

.dr-resilience__item + .dr-resilience__item {
  border-top: 1px solid var(--dr-line);
}

@media (min-width: 768px) {
  .dr-resilience {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  .dr-resilience__item {
    padding: 1.75rem clamp(1.25rem, 0.75rem + 1.5vw, 2.5rem) 0;
  }

  .dr-resilience__item:first-child {
    padding-left: 0;
  }

  .dr-resilience__item + .dr-resilience__item {
    border-top: 0;
    border-left: 1px solid var(--dr-line);
  }
}

.dr-resilience__label {
  margin: 0 0 1.25rem;
  font-size: var(--dr-step--1);
  font-weight: 400;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--dr-steel);
}

.dr-resilience__value {
  margin: 0;
  font-size: var(--dr-step-3);
  line-height: 1;
  letter-spacing: -0.03em;
  font-weight: 600;
  color: var(--dr-ice);
}

.dr-resilience__note {
  margin: 1rem 0 0;
  font-size: var(--dr-step--1);
  color: var(--dr-steel);
}

/* One figure per zone (.SE, .NU): the zone as a small label beside its figure. */
.dr-zone-figures {
  margin: 0;
  display: grid;
  gap: 0.75rem;
}

.dr-zone-figures > div {
  display: flex;
  align-items: baseline;
  gap: 1rem;
}

.dr-zone-figures dt {
  min-width: 2.5rem;
  font-size: var(--dr-step--1);
  font-weight: 400;
  letter-spacing: 0.1em;
  color: var(--dr-radar-hi);
}

/* A ranking without values: the order is the finding, so the numbers are the markers. */
.dr-rank {
  list-style: none;
  counter-reset: rank;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 0.6rem;
  font-size: var(--dr-step-1);
  color: var(--dr-ice);
}

.dr-rank li {
  counter-increment: rank;
  display: flex;
  align-items: baseline;
  gap: 1rem;
}

.dr-rank li::before {
  content: counter(rank, decimal-leading-zero);
  min-width: 1.6rem;
  font-size: var(--dr-step--1);
  letter-spacing: 0.1em;
  font-variant-numeric: tabular-nums;
  color: var(--dr-radar-hi);
}

/* ------------------------------------------------------- drawn figures ---- */

/* Two of the "Detect structural risk" cards are drawn instead of photographed so
 * they can be animated. Both are decorative: no labels, no figures, no axis. The
 * markup already holds the final shape, so they are complete and correct with no
 * JavaScript and under prefers-reduced-motion. */

.dr-bars {
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 0.55rem;
  padding-block: 0.75rem;
}

.dr-bars span {
  display: block;
  height: 7px;
  border-radius: 1px;
  transform-origin: left center;
  background: linear-gradient(90deg, var(--dr-radar) 0%, rgba(115, 139, 255, 0.32) 100%);
}

.dr-donut {
  fill: none;
  overflow: visible;
}

.dr-donut__track {
  stroke: var(--dr-line);
  stroke-width: 13;
}

.dr-donut__arc {
  stroke-width: 13;
  /* Butt caps, so neighbouring segments meet cleanly instead of overlapping. */
  stroke-linecap: butt;
}

/* Four steps down one hue rather than the risk ramp: red and amber are reserved
 * for semantics (DESIGN.md section 3), and this shape means nothing. */
.dr-donut__arc--1 { stroke: var(--dr-radar); }
.dr-donut__arc--2 { stroke: var(--dr-radar-hi); }
.dr-donut__arc--3 { stroke: var(--dr-steel); }
.dr-donut__arc--4 { stroke: rgba(198, 204, 210, 0.55); }

/* Radar pings over the infrastructure heat map. The wrapper takes the card
 * figure's box and the photo fills it the way the <img> figures do. */
.dr-hotspots {
  position: relative;
  overflow: hidden;
}

.dr-hotspots__img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* A ring centred on a hotspot, sized from the view. Invisible at rest: it only
 * exists as motion. The warm tone is the photograph's own, not the risk ramp. */
.dr-ping {
  position: absolute;
  width: var(--ping-size, 6%);
  aspect-ratio: 1;
  border: 1px solid rgba(255, 176, 120, 0.9);
  border-radius: 50%;
  transform: translate(-50%, -50%);
  opacity: 0;
  pointer-events: none;
}

/* The drawn line chart. */
.dr-line__grid {
  stroke: var(--dr-line);
  stroke-width: 0.4;
}

.dr-line__stroke {
  fill: none;
  stroke: var(--dr-radar-hi);
  stroke-width: 1.2;
  stroke-linejoin: round;
  stroke-linecap: round;
}

.dr-line__end {
  fill: var(--dr-radar-hi);
}

.dr-line__ping {
  fill: none;
  stroke: var(--dr-radar-hi);
  stroke-width: 0.6;
  opacity: 0;
}

/* --------------------------------------------------------------- footer ---- */

.dr-footer {
  border-top: 1px solid var(--dr-line);
  padding-block: 3rem;
  font-size: var(--dr-step--1);
  color: var(--dr-steel);
}

.dr-footer__grid {
  display: grid;
  gap: 2rem;
  grid-template-columns: minmax(0, 1fr);
}

@media (min-width: 768px) {
  .dr-footer__grid {
    grid-template-columns: minmax(0, 2fr) minmax(0, 1fr) minmax(0, 1fr);
  }
}

.dr-footer__lede {
  margin: 0;
  max-width: 34ch;
}

.dr-footer__links {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 0.6rem;
}

.dr-footer__address {
  margin: 0;
  font-style: normal;
  line-height: 1.7;
}

/* ------------------------------------------------------------ page head ---- */

/* The opening of a content page (about, contact): the hero's type, without the
 * hero's height or photograph. */
.dr-page-head {
  padding-block: clamp(3.5rem, 2.5rem + 5vw, 7rem) clamp(2.5rem, 2rem + 3vw, 4.5rem);
}

.dr-page-head__figure {
  display: block;
  width: 100%;
  max-width: 440px;
  height: auto;
  margin-left: auto;
  /* The scale artwork carries a faint frame at its edges; trim it off. */
  clip-path: inset(3%);
}

/* Running text on content pages: body size, a readable measure. */
.dr-prose {
  max-width: 62ch;
  color: var(--dr-neutral);
}

/* Long running text with headings and lists: the privacy notice. */
.dr-notice {
  max-width: 68ch;
  color: var(--dr-neutral);
}

.dr-notice h2 {
  font-size: var(--dr-step-1);
  margin: clamp(2.5rem, 2rem + 1.5vw, 3.5rem) 0 1rem;
}

.dr-notice h2:first-child {
  margin-top: 0;
}

.dr-notice ul {
  padding-left: 1.25rem;
}

.dr-notice li + li {
  margin-top: 0.75rem;
}

.dr-notice strong {
  color: var(--dr-ice);
  font-weight: 600;
}

/* Label / value pairs: contact details, data residency, standards. */
.dr-facts {
  margin: 0;
  border-top: 1px solid var(--dr-line);
}

.dr-facts > div {
  display: grid;
  gap: 0.35rem;
  padding-block: 1.1rem;
  border-bottom: 1px solid var(--dr-line);
}

@media (min-width: 576px) {
  .dr-facts > div {
    grid-template-columns: 11rem minmax(0, 1fr);
    gap: 1.5rem;
  }
}

.dr-facts dt {
  font-size: var(--dr-step--1);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--dr-steel);
  font-weight: 400;
}

.dr-facts dd {
  margin: 0;
  color: var(--dr-ice);
}

.dr-bleed .dr-facts dt,
.dr-bleed .dr-facts dd {
  text-shadow: 0 1px 8px rgba(6, 8, 12, 0.85);
}

/* The four-step process on the about page: numbered by CSS, since the order is
 * the content. */
.dr-process {
  list-style: none;
  margin: clamp(2.5rem, 2rem + 2vw, 4rem) 0 0;
  padding: 0;
  display: grid;
  gap: clamp(1.5rem, 1rem + 2vw, 2.5rem);
  grid-template-columns: minmax(0, 1fr);
  counter-reset: dr-step;
}

@media (min-width: 768px) {
  .dr-process {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

@media (min-width: 1200px) {
  .dr-process {
    grid-template-columns: repeat(4, minmax(0, 1fr));
  }
}

.dr-process li {
  counter-increment: dr-step;
  border-top: 1px solid var(--dr-line-strong);
  padding-top: 1.25rem;
}

.dr-process li::before {
  content: counter(dr-step, decimal-leading-zero);
  display: block;
  font-size: var(--dr-step--1);
  letter-spacing: 0.16em;
  color: var(--dr-radar-hi);
  margin-bottom: 0.75rem;
  font-variant-numeric: tabular-nums;
}

.dr-process h3 {
  font-size: var(--dr-step-1);
  color: var(--dr-ice);
  font-weight: 600;
  margin: 0 0 0.5rem;
}

.dr-process p {
  margin: 0;
}

/* Funders' logos as a credits strip: one small white tile per logo, in a row under
 * the funding text. The artwork is dark-on-light and may not be recoloured, and the
 * EU emblem needs a white ground; separate tiles give it that without putting a
 * large white panel on a dark page. */
.dr-logos {
  list-style: none;
  margin: clamp(2rem, 1.5rem + 2vw, 3rem) 0 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem;
}

/* Each tile is as wide as its logo: the white is a frame around the mark, not a
 * band across the page. */
.dr-logos li {
  display: flex;
  align-items: center;
  height: clamp(4.25rem, 3.75rem + 1vw, 5rem);
  padding: 0.6rem 1rem;
  background: #fff;
  border-radius: 2px;
}

.dr-logos picture {
  display: contents;
}

.dr-logos img {
  display: block;
  width: auto;
  height: 100%;
}

/* ------------------------------------------------------------ auth pages ---- */

/* One centred panel over a dimmed photograph: sign-in, password reset, errors. */
.dr-auth {
  min-height: calc(100svh - 5rem);
  display: flex;
  align-items: center;
  padding-block: clamp(3rem, 2rem + 5vw, 6rem);
}

.dr-auth .dr-bleed__img {
  opacity: 0.55;
}

.dr-auth__panel {
  width: 100%;
  max-width: 30rem;
  margin-inline: auto;
  padding: clamp(1.75rem, 1.25rem + 2vw, 2.75rem);
  background: rgba(12, 18, 27, 0.9);
  border: 1px solid var(--dr-line);
  backdrop-filter: blur(8px);
}

.dr-auth__title {
  font-size: var(--dr-step-2);
  line-height: 1.15;
  letter-spacing: -0.02em;
  font-weight: 600;
  color: var(--dr-ice);
  margin: 0 0 1.25rem;
  text-wrap: balance;
}

.dr-auth__copy {
  color: var(--dr-neutral);
  margin: 0 0 1rem;
}

.dr-auth__actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem;
  margin-top: 1.75rem;
}

.dr-auth__meta {
  margin: 1.75rem 0 0;
  padding-top: 1.25rem;
  border-top: 1px solid var(--dr-line);
  font-size: var(--dr-step--1);
  color: var(--dr-steel);
}

/* ------------------------------------------------------------------ forms ---- */

.dr-form {
  display: grid;
  gap: 1.25rem;
}

.dr-field {
  display: grid;
  gap: 0.45rem;
}

.dr-field__row {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 1rem;
}

.dr-label {
  font-size: var(--dr-step--1);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--dr-steel);
}

.dr-field__link {
  font-size: var(--dr-step--1);
}

.dr-input {
  width: 100%;
  padding: 0.75rem 0.9rem;
  font: inherit;
  color: var(--dr-ice);
  background: var(--dr-black);
  border: 1px solid var(--dr-line-strong);
  border-radius: 2px;
  transition: border-color var(--dr-t-ui) ease, box-shadow var(--dr-t-ui) ease;
}

.dr-input:hover {
  border-color: var(--dr-steel);
}

.dr-input:focus-visible {
  outline: none;
  border-color: var(--dr-radar-hi);
  box-shadow: 0 0 0 3px rgba(115, 139, 255, 0.28);
}

/* Browsers paint autofilled fields light; keep them on the dark ground. */
.dr-input:-webkit-autofill {
  -webkit-text-fill-color: var(--dr-ice);
  box-shadow: 0 0 0 100px var(--dr-black) inset;
  caret-color: var(--dr-ice);
}

.dr-field__error {
  margin: 0;
  font-size: var(--dr-step--1);
  color: var(--dr-risk-high);
}

/* Django's password-validator help, a <ul>. */
.dr-field__help {
  font-size: var(--dr-step--1);
  color: var(--dr-steel);
}

.dr-field__help ul {
  margin: 0.25rem 0 0;
  padding-left: 1.1rem;
}

.dr-alert {
  padding: 0.9rem 1rem;
  margin-bottom: 1.25rem;
  border: 1px solid rgba(240, 112, 138, 0.45);
  border-left-width: 3px;
  background: rgba(240, 112, 138, 0.08);
  color: var(--dr-ice);
  font-size: var(--dr-step--1);
}

.dr-alert p {
  margin: 0;
}

.dr-alert p + p {
  margin-top: 0.5rem;
}

.dr-btn--block {
  width: 100%;
  justify-content: center;
}

/* Short trailing links ("Contact us") read as one unit and never break. */
.dr-auth__meta a {
  white-space: nowrap;
}
