/* Global Disaster Risk Score - site styles.
   Layered on Bootstrap 5; only what Bootstrap does not already give us. */

:root {
  --gdrs-ink: #212529;
  --gdrs-muted: #6c757d;
  --gdrs-line: #dee2e6;
  --gdrs-surface: #f8f9fa;

  /* Inter for text and numbers: its tabular figures keep the score columns and
     the validation table aligned. Space Grotesk for display type only. Both
     carry full fallback stacks, so a blocked or slow Google Fonts request
     degrades to the system font rather than to nothing. */
  --gdrs-font-body: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
    "Helvetica Neue", Arial, sans-serif;
  --gdrs-font-display: "Space Grotesk", "Inter", -apple-system, BlinkMacSystemFont,
    "Segoe UI", Roboto, sans-serif;
}

body {
  font-family: var(--gdrs-font-body);
  font-feature-settings: "tnum" 1, "cv05" 1;
  -webkit-font-smoothing: antialiased;
}

.navbar-brand,
.display-5,
.score-number,
.city-score,
.profile-value {
  font-family: var(--gdrs-font-display);
  letter-spacing: -0.015em;
}

h1, h2, h3, h4, h5, h6 {
  letter-spacing: -0.01em;
}

/* ------------------------------------------------------------- icons */

/* Lucide renders inline SVG at 24px; these are label-sized. currentColor means
   an icon inherits whatever colour its context already decided. */
.icon {
  width: 1em;
  height: 1em;
  stroke-width: 2;
  flex: 0 0 auto;
  vertical-align: -0.125em;
}

.icon-inline {
  margin-inline-end: 0.25rem;
}

.icon-indicator {
  width: 1.15em;
  height: 1.15em;
}

/* Alpine sets x-cloak until it has initialised; without this the element it
   guards flashes on screen first. */
[x-cloak] {
  display: none !important;
}

/* ------------------------------------------------- map layer switcher */

.layer-switch .btn {
  --bs-btn-padding-x: 0.55rem;
  --bs-btn-padding-y: 0.3rem;
}

.layer-switch .btn.active {
  background: var(--gdrs-ink);
  border-color: var(--gdrs-ink);
  color: #fff;
}

/* Only the selected layer may look selected. Bootstrap fills an outline button
   on focus, which after a click made an unselected button indistinguishable
   from the active one. */
.layer-switch .btn:not(.active):focus,
.layer-switch .btn:not(.active):focus-visible,
.map-reset:focus,
.map-reset:focus-visible {
  background: transparent;
  color: var(--gdrs-ink);
  box-shadow: 0 0 0 0.2rem rgb(33 37 41 / 15%);
}

.map-reset {
  --bs-btn-padding-x: 0.55rem;
  --bs-btn-padding-y: 0.3rem;
}

/* Bootstrap's large gutter classes give a .row negative margins wider than the
   .container's padding, which pushes the page into horizontal scroll on narrow
   screens. Clip rather than scroll: nothing should ever sit out there. */
html,
body {
  overflow-x: clip;
}

/* ----------------------------------------------------------------- nav */

.site-nav {
  background: #fff;
  border-bottom: 1px solid var(--gdrs-line);
}

.site-nav .nav-link {
  color: var(--gdrs-ink);
  font-weight: 500;
}

.site-nav .nav-link:hover,
.site-nav .nav-link:focus-visible {
  color: #0b6bcb;
}

.site-nav .navbar-toggler {
  border-color: var(--gdrs-line);
}

.navbar-brand {
  padding-block: 0.25rem;
}

.site-logo {
  display: block;
  height: 38px;
  width: auto;
}

@media (max-width: 575.98px) {
  .site-logo {
    height: 30px;
  }
}

/* ---------------------------------------------------------------- hero */

.home-hero {
  background: linear-gradient(180deg, #fff7ec 0%, #ffffff 100%);
  border-bottom: 1px solid var(--gdrs-line);
  overflow: hidden;
}

/* The landing page shows the product: a screenshot of a city page's radar chart
   and hazard map, beside the introduction rather than behind it.

   An earlier version put it in the background at low opacity. It never worked -
   the source is a screenshot of a working interface, so it carries axis labels,
   a popup and map controls, and faded behind body copy those read as a second,
   broken paragraph rather than as texture. Blurring them away destroyed the
   chart and the map too. Beside the text there is nothing to hide from: it can
   be shown at full strength, and it is legible. */
.hero-visual {
  display: block;
  border-radius: 0.75rem;
  overflow: hidden;
  border: 1px solid var(--gdrs-line);
  box-shadow: 0 18px 40px rgb(0 0 0 / 8%), 0 2px 6px rgb(0 0 0 / 4%);
  background: #fff;
  transition: transform 0.15s ease, box-shadow 0.15s ease;
}

a.hero-visual:hover,
a.hero-visual:focus-visible {
  transform: translateY(-2px);
  box-shadow: 0 22px 48px rgb(0 0 0 / 12%), 0 3px 8px rgb(0 0 0 / 5%);
}

.hero-visual img {
  display: block;
  width: 100%;
  /* The screenshot catches the first line of the paragraph below the map, which
     lands as a strip of half-cut text along the bottom edge. Trimmed here rather
     than in the file so the intrinsic size stays honest; aspect-ratio still
     reserves the space, so nothing jumps as the image loads. */
  aspect-ratio: 1662 / 543;
  height: auto;
  object-fit: cover;
  object-position: top center;
}

@media (min-width: 1200px) {
  /* The source is 2.9:1, which in half a container is a thin strip. Letting it
     run past the column - and past the viewport edge - gives it enough height to
     read while keeping the left edge aligned with the text column beside it. */
  .hero-visual {
    width: calc(100% + 6rem);
    border-start-end-radius: 0;
    border-end-end-radius: 0;
    border-inline-end: 0;
  }
}

@media (max-width: 991.98px) {
  /* Stacked, the full 2.9:1 strip is barely 120 px tall and nothing in it can be
     read - a thumbnail of a screenshot says less than no image at all. Showing
     the radar chart alone gives it enough height to be legible. */
  .hero-visual img {
    aspect-ratio: 4 / 3;
    object-position: left top;
  }
}

[dir="rtl"] .hero-visual img {
  /* The radar chart is on the left of the source whichever way the page runs. */
  object-position: left top;
}

.city-hero {
  background: var(--gdrs-surface);
  border-bottom: 4px solid var(--band-color, var(--gdrs-line));
}

/* The radar chart and the map are the two things a reader came for, so the hero
   above them is kept deliberately short. On a laptop both should be on screen
   without scrolling. */
.city-hero .breadcrumb {
  margin-bottom: 0.5rem !important;
}

.city-hero .lead {
  font-size: 1.05rem;
}

@media (max-width: 991.98px) {
  .city-hero .display-5 {
    font-size: 1.85rem;
  }

  .city-hero .lead {
    font-size: 0.95rem;
    margin-bottom: 0.5rem !important;
  }

  .city-hero > .container > .row > .col-lg-7 > p {
    font-size: 0.9rem;
    margin-bottom: 0.9rem !important;
  }

  .score-card {
    padding: 0.9rem !important;
  }

  /* The sentence explaining how the score is built is worth keeping, but on a
     phone it costs more vertical space than it earns above the fold. It is
     repeated in full on the methodology page. */
  .score-card > p:last-child {
    display: none;
  }

  .city-facts dt {
    font-size: 0.68rem;
  }

  .city-facts dd {
    font-size: 0.95rem;
  }

  .city-facts {
    --bs-gutter-y: 0.5rem;
  }
}

.city-facts dt {
  font-size: 0.75rem;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  color: var(--gdrs-muted);
  font-weight: 600;
  margin-bottom: 0.15rem;
}

.city-facts dd {
  font-size: 1.05rem;
  font-weight: 600;
  margin-bottom: 0;
}

/* --------------------------------------------------------- score card */

.score-card {
  background: #fff;
  border: 1px solid var(--gdrs-line);
  border-radius: 0.75rem;
  box-shadow: 0 1px 3px rgb(0 0 0 / 6%);
}

.score-number {
  font-size: clamp(2.9rem, 10vw, 4rem);
  font-weight: 700;
  line-height: 1;
  color: var(--band-color, var(--gdrs-ink));
}

.score-max {
  font-size: 1.5rem;
  font-weight: 500;
  color: var(--gdrs-muted);
}

.score-band {
  font-size: 1rem;
  font-weight: 600;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  color: var(--gdrs-muted);
}

/* -------------------------------------------------------- city cards */

.city-card {
  border: 1px solid var(--gdrs-line);
  transition: transform 0.12s ease, box-shadow 0.12s ease;
  color: inherit;
}

a.city-card:hover,
a.city-card:focus-visible {
  transform: translateY(-2px);
  box-shadow: 0 6px 18px rgb(0 0 0 / 8%);
}

.city-card.planned {
  border-style: dashed;
  background: var(--gdrs-surface);
}

.city-score {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 3rem;
  height: 3rem;
  border-radius: 0.6rem;
  font-weight: 700;
  font-size: 1.25rem;
}

/* --------------------------------------------------------- indicators */

.indicator {
  border: 1px solid var(--gdrs-line);
}

.indicator-badge {
  min-width: 2.75rem;
  font-size: 0.95rem;
  padding: 0.45rem 0.6rem;
}

.indicator-progress {
  height: 0.5rem;
  background: #eceff1;
}

.detail-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: 0.5rem 1rem;
  margin: 0;
}

.detail-grid dt {
  color: var(--gdrs-muted);
  font-weight: 500;
  font-size: 0.8rem;
}

.detail-grid dd {
  font-weight: 600;
  margin-bottom: 0;
}

/* --------------------------------------------------------------- map */

.hazard-map {
  height: min(70vh, 560px);
  border: 1px solid var(--gdrs-line);
  border-radius: 0.5rem;
}

/* The map sits beside the radar chart at the top of a city page, so it is sized
   to sit alongside it rather than to fill the viewport on its own. */
.hazard-map-overview {
  height: min(46vh, 390px);
}

@media (max-width: 991.98px) {
  /* Stacked on narrow screens: give the map back some height, but not so much
     that it pushes everything below it off the first screen. */
  .hazard-map-overview {
    height: min(42vh, 320px);
  }
}

.hazard-map-wrapper {
  position: relative;
}

/* Covers #map while MapLibre and its data are still loading. A sibling of
   #map, not a child of it: MapLibre takes over #map's contents the moment
   the map constructor runs, so anything placed inside #map itself would be
   wiped out rather than layered on top. */
.hazard-map-loading {
  position: absolute;
  inset: 0;
  z-index: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0.25rem;
  padding: 1.5rem;
  background: rgb(248 249 250 / 92%);
  border: 1px solid var(--gdrs-line);
  border-radius: 0.5rem;
}

.hazard-map-loading-bar {
  width: min(200px, 60%);
  height: 0.35rem;
  margin-top: 0.5rem;
}

.legend-item {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  color: var(--gdrs-muted);
}

.legend-swatch {
  width: 1rem;
  height: 1rem;
  border-radius: 0.2rem;
  border: 1px solid rgb(0 0 0 / 12%);
}

.hex-popup dl {
  display: grid;
  grid-template-columns: auto auto;
  gap: 0.1rem 0.75rem;
  margin: 0.35rem 0 0;
}

.hex-popup dt {
  color: var(--gdrs-muted);
  font-weight: 500;
}

.hex-popup dd {
  margin: 0;
  font-weight: 600;
  text-align: end;
}

/* ----------------------------------------------------------- caveats */

.caveat {
  background: var(--gdrs-surface);
  border-inline-start: 3px solid #f79f3c;
  padding: 1rem 1.15rem;
  border-radius: 0.35rem;
}

.caveat h3 {
  margin-bottom: 0.4rem;
}

/* ------------------------------------------------------- methodology */

.methodology p {
  max-width: 62ch;
}

.formula {
  background: var(--gdrs-surface);
  border: 1px solid var(--gdrs-line);
  border-radius: 0.35rem;
  padding: 0.85rem 1rem;
  font-size: 0.95rem;
  overflow-x: auto;
}

/* ------------------------------------------------------------ donate */

.donate-band {
  background: var(--gdrs-surface);
  border-top: 1px solid var(--gdrs-line);
  border-bottom: 1px solid var(--gdrs-line);
}

/* ------------------------------------------------------------- radar */

.radar {
  width: 100%;
  max-width: 500px;
  height: auto;
  display: block;
  margin-inline: auto;
}

@media print {
  .navbar,
  .donate-band,
  .hazard-map {
    display: none;
  }
}

/* ------------------------------------------------------ city profile */

.profile-fact {
  background: #fff;
  border: 1px solid var(--gdrs-line);
  border-radius: 0.5rem;
  padding: 0.9rem 1rem;
}

.profile-label {
  font-size: 0.75rem;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  font-weight: 600;
  color: var(--gdrs-muted);
}

.profile-value {
  font-size: 1.5rem;
  font-weight: 700;
  line-height: 1.1;
}

.profile-unit {
  display: block;
  font-size: 0.75rem;
  font-weight: 500;
  color: var(--gdrs-muted);
}

.profile-note {
  font-size: 0.78rem;
  color: var(--gdrs-muted);
  margin-top: 0.4rem;
}

.profile-source {
  font-size: 0.7rem;
  color: #adb5bd;
  margin-top: 0.35rem;
}

/* -------------------------------------------------------- validation */

/* Deliberate column widths. Left to itself the browser gave the first column
   nearly 700px while the note inside it was capped far narrower, so every row
   carried a tall ribbon of text with a wide empty strip beside it. Fixed layout
   plus a min-width means the columns are sized on purpose on a desktop and the
   table scrolls rather than squashes on a phone. */
.validation-table {
  table-layout: fixed;
  min-width: 640px;
}

.validation-table td,
.validation-table th {
  vertical-align: top;
}

.validation-table th:nth-child(1) {
  width: 48%;
}

.validation-table th:nth-child(2),
.validation-table th:nth-child(3) {
  width: 13%;
}

.validation-table th:nth-child(4) {
  width: 9%;
}

.validation-table th:nth-child(5) {
  width: 17%;
}

.validation-table .check-note,
.validation-table .check-source {
  font-size: 0.75rem;
  /* Fill the column, but stop short of a measure too long to scan at this size. */
  max-width: 78ch;
}

/* The attribute ids are long unbroken tokens; with a fixed layout they would
   otherwise push the column open. */
.validation-table code {
  overflow-wrap: anywhere;
}

.validation-table .check-source code {
  font-size: 0.72rem;
  color: inherit;
}

.check-badge {
  font-weight: 600;
  font-size: 0.72rem;
  text-transform: uppercase;
  letter-spacing: 0.03em;
}

.check-badge-ok {
  background: #d7ecd9;
  color: #17692b;
}

.check-badge-warn {
  background: #fde9c8;
  color: #8a5300;
}

.check-badge-fail {
  background: #f8d7da;
  color: #8c142a;
}

.check-badge-skipped {
  background: #e9ecef;
  color: #495057;
}

/* `inset 3px` is physical, so the marker would sit on the wrong edge in an RTL
   build. A border-inline-start does the right thing in both directions. */
tr.check-warn td:first-child {
  border-inline-start: 3px solid #f79f3c;
}

tr.check-fail td:first-child {
  border-inline-start: 3px solid #8c142a;
}

/* --------------------------------------------------- language switcher */

.lang-switch {
  position: relative;
}

.lang-button {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  text-decoration: none;
  border: 0;
  background: none;
  color: var(--gdrs-ink);
  font-weight: 500;
}

.lang-menu {
  position: absolute;
  inset-inline-end: 0;
  top: 100%;
  z-index: 1030;
  min-width: 11rem;
  margin: 0.25rem 0 0;
  padding: 0.3rem;
  list-style: none;
  background: #fff;
  border: 1px solid var(--gdrs-line);
  border-radius: 0.5rem;
  box-shadow: 0 8px 24px rgb(0 0 0 / 14%);
}

.lang-item {
  display: block;
  padding: 0.4rem 0.7rem;
  border-radius: 0.35rem;
  color: var(--gdrs-ink);
  text-decoration: none;
  font-size: 0.95rem;
}

.lang-item:hover,
.lang-item:focus-visible {
  background: var(--gdrs-surface);
}

.lang-item.current {
  font-weight: 700;
  background: var(--gdrs-surface);
}

@media (max-width: 991.98px) {
  /* Inside the collapsed navbar the menu is part of the flow, not an overlay. */
  .lang-menu {
    position: static;
    box-shadow: none;
    border: 0;
    padding-inline-start: 0.5rem;
    background: transparent;
  }
}

/* ------------------------------------------------- translation notice */

.translation-notice {
  background: #fff8e6;
  border-bottom: 1px solid #f3e0b5;
  color: #6b4e00;
}

.translation-notice a {
  color: inherit;
  font-weight: 600;
}

/* --------------------------------------------------------------- RTL */

/* Latin-script proper nouns, licence codes, formulas and figures stay LTR
   inside an RTL page. The `dir="ltr"` attributes in the templates handle the
   direction; this keeps such runs from picking up an RTL text alignment. */
[dir="rtl"] [dir="ltr"] {
  text-align: start;
}

[dir="rtl"] .formula {
  text-align: left;
}

/* ------------------------------------------------------------ about page */
.about-operator th {
  width: 11rem;
  font-weight: 600;
  color: var(--bs-body-color);
}

@media (max-width: 575.98px) {
  .about-operator th {
    width: 8rem;
  }
}

/* ------------------------------------------------------------ consent banner */
/* Dark and fixed to the bottom, deliberately unlike the light informational
   translation-notice banner at the top: this one asks for a decision rather
   than stating a fact, and sits where a reader's eye lands last, not first. */
.consent-banner {
  position: fixed;
  inset-block-end: 0;
  inset-inline: 0;
  z-index: 1040;
  background: #212529;
  color: #f8f9fa;
  box-shadow: 0 -2px 12px rgba(0, 0, 0, 0.15);
}

.consent-banner a {
  color: #8ecbff;
}
