/* =============================================================
   ZG Design System — Dash / Bootstrap adaptation
   ============================================================= */

/* Both of these came from a CDN until Edge's strict tracking prevention was
   found to block them: fonts.googleapis.com, and cdn.jsdelivr.net — which Edge
   lists under its operator's name, Volentio JSD. That left the page in its
   fallback font with every bi-* glyph an empty box. They are mirrored under
   assets/vendor/ instead, which also keeps type and icons correct without
   outbound internet access; each mirror's own header says how to refresh it.
   Bootstrap itself is vendored the same way, linked from app.py rather than
   here so it keeps loading in parallel with this file instead of after it. */
@import url('vendor/inter/inter.css');
@import url('vendor/bootstrap-icons/bootstrap-icons.min.css');

/* ===== Mode-independent design tokens (mirror of $color-* shared across zg-light/dark.scss) ===== */
:root {
  --color-primary:      #252342;
  --color-secondary:    #0070B8;
  --color-accent:       #e07031;
  --color-border-focus: #7CA5D0;
  /* Gemeindeportrait-specific (no equivalent in website-framework design system) */
  --zg-trend-up:   #4EA97C;
  --zg-trend-down: #ff6666;
}

/* ===== Light mode — design tokens + semantic aliases ===== */
:root,
[data-bs-theme="light"] {
  /* What the browser paints itself — scrollbars, form controls, the canvas
     behind the page. Without this they stay light while everything else
     follows the theme. It tracks the theme actually shown rather than the
     system setting, so the toggle takes them along too. */
  color-scheme: light;

  /* Design tokens (mirror of $color-* in zg-light.scss) */
  --color-bg:                #FFFFFF;
  --color-hover:             #F5F9FF;
  --color-btn-primary:       var(--color-primary);
  --color-btn-secondary:     transparent;
  --color-btn-tertiary:      #486695;
  --color-btn-interactive:   #F5F9FF;
  --color-btn-surface:       #F5F9FF;
  --color-btn-hover:         #435075;
  --color-btn-hover-accent:  var(--color-accent);
  --color-btn-text-high:     #F5F9FF;
  --color-btn-text-medium:   #2D4487;
  --color-btn-text-low:      #57699F;
  --color-btn-text-accent:   #150023;
  --color-text-high:         #150023;
  --color-text-medium:       #353F72;
  --color-text-low:          #57699F;
  --color-text-link:         #353F72;
  --color-text-link-hover:   #848DB4;
  --color-border-default:    #ABB3CC;
  --color-border-hover:      #2D4487;
  --color-notify-error:      #C53030;
  --color-notify-success:    var(--color-secondary);

  /* Bootstrap / component semantic aliases */
  --bs-body-bg:          var(--color-bg);
  --bs-body-color:       var(--color-text-high);
  /* All three neutral: the arrow says which way the number went, the colour
     used to say whether that was good news. The cell itself is body text now,
     so these are left only to tint the mobile trend tooltip. */
  --trend-up-color:      var(--color-secondary);
  --trend-down-color:    var(--color-secondary);
  --trend-neutral-color: var(--color-secondary);
  --bs-heading-color:    var(--color-text-high);
  --bs-color-hover:      var(--color-hover);

  --bs-primary:          var(--color-primary);
  --bs-secondary:        var(--color-secondary);
  --bs-success:          var(--color-notify-success);
  --bs-danger:           var(--color-notify-error);

  --bs-border-color:     var(--color-border-default);
  --bs-link-color:       var(--color-text-link);
  /* text-low rather than the design token's #848DB4, which reads 3.3:1 on
     white; this keeps the lighter-on-hover look at 5.4:1 (WCAG 1.4.3). */
  --bs-link-hover-color: var(--color-text-low);
  --focus-ring-color:    var(--color-secondary);

  --text-high-contrast:   var(--color-text-high);
  --text-medium-contrast: var(--color-text-medium);
  --text-low-contrast:    var(--color-text-low);

  --primary-blue:      var(--color-secondary);
  --primary-blue-dark: var(--color-border-hover);

  /* The value boxes of the Statistikportal: its blue with white text. */
  --stat-banner-bg:    var(--color-secondary);
  --stat-banner-fg:    #ffffff;

  /* The link cards of the Statistikportal and zg.ch: filled, with the
     outline coming in on hover. */
  --link-card-bg:           var(--color-hover);
  --link-card-border:       transparent;
  --link-card-hover-bg:     var(--color-hover);
  --link-card-hover-border: var(--color-border-hover);
  --link-card-hover-arrow:  var(--color-accent);

  /* The centre, lowest and highest point, on the map and in its key in the
     Geografie banner: one blue for all three, told apart by their shape.
     The bright one of our blues: lighter than the dark blue highlight of
     the selected Gemeinde they mostly sit on, and clear on the banner. */
  --map-symbol:        #4BAEE7;
  --accent-color:      var(--color-accent);
  --content-bg:        var(--color-hover);
  --modal-bg:          var(--color-bg);

  --navbar-bg-color:   var(--color-bg);
  --navbar-fg-color:   var(--color-primary);

  --code-bg-color:     rgba(230, 117, 53, 0.1);
  --code-text-color:   var(--color-accent);

  --card-bg:           transparent;
  --card-border:       var(--color-border-default);
  --card-border-hover: var(--color-border-hover);

  --updatemenu-hover-brightness: 80%;
  /* Zebra-stripe tint: reactable's own default is rgba(0,0,0,.03) in light
     mode (site_libs/reactable-0.4.5/reactable.css: .rt-tr-striped) */
  --table-stripe-rgb:  0, 0, 0;
  --dt-odd-opacity:    0.03;
  --dt-hover-opacity:  0.035;

  /* Button variables */
  --btn-primary-bg:         var(--color-btn-primary);
  --btn-secondary-bg:       var(--color-btn-secondary);
  --btn-tertiary-bg:        var(--color-btn-tertiary);
  --btn-interactive-bg:     var(--color-btn-interactive);
  --btn-interactive-border: var(--color-btn-interactive);
  --btn-surface-bg:         var(--color-btn-surface);
  --btn-hover-bg:           var(--color-btn-hover);
  --btn-hover-accent-bg:    var(--color-btn-hover-accent);
  --btn-text-high:          var(--color-btn-text-high);
  --btn-text-medium:        var(--color-btn-text-medium);
  --btn-text-low:           var(--color-btn-text-low);
  --btn-text-accent:        var(--color-btn-text-accent);
  --btn-category-bg:        transparent;
  --btn-category-color:     var(--color-btn-text-medium);
  --btn-category-border:    var(--color-btn-tertiary);
  --btn-category-hover-bg:  var(--color-btn-hover);
  --btn-category-hover-color:  var(--color-btn-text-high);
  --btn-category-hover-border: var(--color-btn-hover);
  --btn-category-active-bg:    var(--color-btn-tertiary);
  --btn-category-active-color: var(--color-btn-text-high);
  --btn-category-active-border: var(--color-btn-hover);
}

/* ===== Dark mode — design tokens + semantic aliases ===== */
[data-bs-theme="dark"] {
  /* See the light block above. */
  color-scheme: dark;

  /* Design tokens (mirror of $color-* in zg-dark.scss) */
  --color-bg:                #272624;
  --color-hover:             #486695;
  --color-btn-primary:       transparent;
  --color-btn-secondary:     transparent;
  --color-btn-tertiary:      transparent;
  --color-btn-interactive:   transparent;
  --color-btn-surface:       var(--color-primary);
  --color-btn-hover:         #486695;
  --color-btn-hover-accent:  var(--color-accent);
  --color-btn-text-high:     #F5F9FF;
  --color-btn-text-medium:   #A3B5C1;
  --color-btn-text-low:      #848DB4;
  --color-btn-text-accent:   #E0EEF6;
  --color-text-high:         #E0EEF6;
  --color-text-medium:       #A3B5C1;
  --color-text-low:          #ABB3CC;
  --color-text-link:         #A3B5C1;
  --color-text-link-hover:   #F5F9FF;
  --color-border-default:    #848DB4;
  --color-border-hover:      #F5F9FF;
  --color-notify-error:      #F56565;
  --color-notify-success:    #7CA5D0;

  /* Bootstrap / component semantic aliases */
  --bs-body-bg:          var(--color-bg);
  --bs-body-color:       var(--color-text-high);
  --bs-heading-color:    var(--color-text-high);
  /* Neutral in dark mode too, see the light block above. */
  --trend-up-color:      var(--color-secondary);
  --trend-down-color:    var(--color-secondary);
  --trend-neutral-color: var(--color-secondary);
  --bs-color-hover:      var(--color-hover);

  --bs-primary:          var(--color-primary);
  --bs-secondary:        var(--color-secondary);
  --bs-success:          var(--color-notify-success);
  --bs-danger:           var(--color-notify-error);

  --bs-border-color:     var(--color-border-default);
  --bs-link-color:       var(--color-text-link);
  --bs-link-hover-color: var(--color-border-hover);
  --focus-ring-color:    var(--color-border-focus);

  --text-high-contrast:   var(--color-text-high);
  --text-medium-contrast: var(--color-text-medium);
  --text-low-contrast:    var(--color-text-low);

  --primary-blue:      var(--color-secondary);
  --primary-blue-dark: var(--color-hover);

  /* The portal's dark value boxes: a light blue with dark text (8.6:1). */
  --stat-banner-bg:    #7CA5D0;
  --stat-banner-fg:    #000000;

  /* The portal's dark link cards: outlined, filled on hover. */
  --link-card-bg:           transparent;
  --link-card-border:       var(--color-border-default);
  --link-card-hover-bg:     var(--color-hover);
  --link-card-hover-border: var(--color-hover);
  --link-card-hover-arrow:  var(--color-text-high);

  /* The same bright blue: the highlight comes out pale and greyish on the
     inverted dark map, and a muted light blue sank into it. */
  --map-symbol:        #4BAEE7;
  --accent-color:      var(--color-accent);
  --content-bg:        var(--color-hover);
  --modal-bg:          var(--color-bg);

  --navbar-bg-color:   var(--color-bg);
  --navbar-fg-color:   var(--color-text-high);

  --code-bg-color:     #150023;
  --code-text-color:   var(--color-accent);

  --card-bg:           transparent;
  --card-border:       var(--color-border-default);
  --card-border-hover: var(--color-border-hover);

  --updatemenu-hover-brightness: 200%;
  /* Zebra-stripe tint: website-framework overrides reactable to
     rgba(255,255,255, var(--rt-odd-opacity)) in dark mode, with
     --rt-odd-opacity: 0.1 (assets/css/zg-dark.scss) */
  --table-stripe-rgb:  255, 255, 255;
  --dt-odd-opacity:    0.1;
  --dt-hover-opacity:  0.35;

  /* Button variables */
  --btn-primary-bg:         var(--color-btn-primary);
  --btn-secondary-bg:       var(--color-btn-secondary);
  --btn-tertiary-bg:        var(--color-btn-tertiary);
  --btn-interactive-bg:     var(--color-btn-interactive);
  --btn-interactive-border: var(--color-secondary);
  --btn-surface-bg:         var(--color-btn-surface);
  --btn-hover-bg:           var(--color-btn-hover);
  --btn-hover-accent-bg:    var(--color-btn-hover-accent);
  --btn-text-high:          var(--color-btn-text-high);
  --btn-text-medium:        var(--color-btn-text-medium);
  --btn-text-low:           var(--color-btn-text-low);
  --btn-text-accent:        var(--color-btn-text-accent);
  --btn-category-bg:        transparent;
  --btn-category-color:     var(--color-btn-text-medium);
  --btn-category-border:    var(--color-border-default);
  --btn-category-hover-bg:  var(--color-btn-hover);
  --btn-category-hover-color:  var(--color-btn-text-high);
  --btn-category-hover-border: var(--color-btn-hover);
  --btn-category-active-bg:    var(--color-btn-hover);
  --btn-category-active-color: var(--color-btn-text-high);
  --btn-category-active-border: var(--color-btn-hover);
}

/* ===== Global resets ===== */
/* Font-size scale matches website-framework's body rule exactly
   (assets/css/styles.scss, "Typography Scale (aligned to zg.ch)") — mobile-
   first, stepped at zg.ch's own tablet/desktop breakpoints (720px, 1025px).
   Bootstrap's heading sizes are rem-based, so h1-h6 scale with this too. */
/* Room the browser keeps free when it scrolls a focused element into view, so
   the keyboard focus never lands under the sticky header at the top or the
   floating Gemeinde selector at the bottom (WCAG 2.4.11). The selector is 60px
   tall and floats 20px above the edge. */
html {
  scroll-padding-top: 3.5rem;
  scroll-padding-bottom: 6rem;
}

html, body {
  font-family: 'Inter', sans-serif !important;
  font-size: 1.0rem !important;
  line-height: 1.25rem !important;
  background-color: var(--bs-body-bg) !important;
  color: var(--bs-body-color) !important;
}

@media (min-width: 720px) {
  html, body {
    font-size: 1rem !important;
    line-height: 1.5rem !important;
  }
}

@media (min-width: 1025px) {
  html, body {
    font-size: 1.125rem !important;
    line-height: 1.75rem !important;
  }
}

h1, h2, h3, h4, h5, h6 {
  font-weight: 700;
  color: var(--bs-heading-color);
}

a {
  color: var(--bs-link-color);
  text-decoration: underline;
  text-underline-offset: 3px;
}

hr {
  border-top: 1px solid var(--bs-border-color);
  opacity: 1;
}

code {
  color: var(--code-text-color);
  background-color: var(--code-bg-color);
  padding: 0.2rem 0.4rem;
  border-radius: 10px;
}

/* ===== Bootstrap component overrides ===== */

/* Tabs. One size for the topic tabs at the top (.zg-main-tabs) and the
   tabs inside a card, which otherwise took the larger body size. */
.nav-tabs .nav-link {
  font-size: 0.95rem;
  font-weight: 600;
  color: var(--text-medium-contrast);
}

.nav-tabs .nav-link.active {
  color: var(--text-high-contrast);
  background-color: transparent;
  border-color: transparent;
  border-bottom: 3px solid var(--text-high-contrast) !important;
}

[data-bs-theme="dark"] .nav-tabs .nav-link.active {
  color: var(--color-text-link-hover);
  background-color: transparent;
  border-color: transparent;
  border-bottom: 3px solid var(--color-text-link-hover) !important;
}

.nav-tabs .nav-link:hover:not(.active) {
  background-color: transparent;
  border-color: transparent;
  color: var(--text-high-contrast);
}

/* Bootstrap's own focus shadow stays off; the outline below takes its place,
   for keyboard focus only (:focus-visible), so a click leaves no ring behind. */
.nav-tabs .nav-link:focus,
.nav-tabs .nav-link:focus-visible {
  box-shadow: none;
}

/* Keyboard focus, everywhere. One rule for every control rather than one per
   component, so nothing the theme restyles can end up without it (WCAG 2.4.7).
   The colour holds 3:1 against the page in either theme. */
:focus-visible {
  outline: 2px solid var(--focus-ring-color) !important;
  outline-offset: 2px;
}

/* Card */
.card {
  background-color: var(--card-bg);
  border-color: var(--card-border);
}

.card-header {
  background-color: transparent;
  border-bottom-color: var(--card-border);
  color: var(--text-high-contrast);
  padding-top: 1.0rem;
  padding-bottom: 1.0rem;
}

/* Card title and the graph/table toggle share the header row. Bootstrap's
   .row wraps, so a title that fills the line pushes the auto-width button
   column onto a line of its own — on a phone that is most cards. The button
   keeps its place at the right and the title gives way instead: min-width
   lets its column shrink past its content, which a flex item otherwise
   refuses to do, and the titles are German compounds long enough that
   wrapping has to be allowed inside the word. */
.card-header-row {
  flex-wrap: nowrap;
}

.card-header-row > :first-child {
  min-width: 0;
}

.card-header-row h5 {
  overflow-wrap: break-word;
  hyphens: auto;
}

/* Dropdown */
.dropdown-menu {
  background-color: var(--modal-bg);
  border-color: var(--bs-border-color);
  font-family: 'Inter', sans-serif;
}

.dropdown-item {
  color: var(--text-high-contrast);
}

.dropdown-item:hover {
  background-color: var(--content-bg);
  color: var(--text-high-contrast);
}

/* Tab content area */
.tab-content {
  background-color: var(--bs-body-bg);
}

@media (min-width: 1025px) {
  .tab-content {
    padding-top: 10px;
  }
}

/* Keep all tab panes in the DOM so Plotly can measure width and pre-render */
.tab-content .tab-pane {
  display: block !important;
  visibility: hidden;
  height: 0 !important;
  overflow: hidden;
  pointer-events: none;
}
/* Positioned, so that the overflow clips what is positioned absolutely
   inside a hidden pane too: the trend labels hidden off sight on a phone
   (.trend-label) otherwise kept their place down where the hidden tab's
   tables would be, and the page scrolled on far past the footer. */
.tab-content .tab-pane:not(.active) {
  position: relative;
}
.tab-content .tab-pane.active {
  visibility: visible;
  height: auto !important;
  overflow: visible;
  pointer-events: auto;
}

/* Plotly — fixed dimensions, no layout shift */
.dash-graph {
  width: 100% !important;
  height: 420px !important;
}
.js-plotly-plot,
.js-plotly-plot .plot-container,
.js-plotly-plot .svg-container {
  width: 100% !important;
  height: 100% !important;
}

/* Year selector dropdown */
.year-selector-wrapper {
  display: flex;
  align-items: center;
  gap: 8px;
}

.year-selector-dropdown {
  width: 150px;
  font-size: 1.0rem !important;
}

/* Control box */
.year-selector-dropdown .Select-control {
  position: relative;
  padding-right: 40px !important;
  border-color: var(--bs-border-color) !important;
  background-color: var(--bs-body-bg) !important;
  color: var(--bs-body-color) !important;
  cursor: pointer;
  border-radius: 10px;
  transition: background-color 0.15s ease, border-color 0.15s ease, color 0.15s ease;
}

/* Hover — same fill treatment as website-framework's .dropdown-toggle-btn:hover */
.year-selector-dropdown .Select-control:hover {
  background-color: var(--btn-category-hover-bg) !important;
  border-color: var(--btn-category-hover-border) !important;
  color: var(--btn-category-hover-color) !important;
}
.year-selector-dropdown .Select-control:hover .Select-value-label,
.year-selector-dropdown .Select-control:hover .Select-placeholder {
  color: var(--btn-category-hover-color) !important;
}
.year-selector-dropdown .Select-control:hover::after {
  background-color: var(--btn-category-hover-color);
  border-radius: 10px;
}

/* react-select's own arrow markup/positioning isn't reliable to target from
   here, so hide it entirely and draw the chevron as a pseudo-element on
   .Select-control instead — that way it can only ever be positioned
   relative to the control itself, and has no open/closed state to move
   between since it isn't part of react-select's own DOM at all. */
.year-selector-dropdown .Select-arrow-zone {
  display: none !important;
}

/* Arrow — same chevron glyph as website-framework's .chevron-icon
   (see website-framework/assets/css/reactable.css) */
.year-selector-dropdown .Select-control::after {
  content: '';
  position: absolute;
  top: 50%;
  right: 8px;
  width: 24px;
  height: 24px;
  transform: translateY(-50%) rotate(90deg);
  background-color: var(--accent-color);
  pointer-events: none;
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 64 64' fill='none'%3E%3Cpath stroke='white' stroke-linecap='square' stroke-miterlimit='10' stroke-width='4' d='m23 49 18-18-18-18'/%3E%3C/svg%3E") no-repeat center / contain;
  mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 64 64' fill='none'%3E%3Cpath stroke='white' stroke-linecap='square' stroke-miterlimit='10' stroke-width='4' d='m23 49 18-18-18-18'/%3E%3C/svg%3E") no-repeat center / contain;
}

/* Focus: react-select keeps the focus on a 1px-wide element left of the
   value, whose ring showed as a lone vertical line. The ring goes round
   the whole box instead, for keyboard focus as everywhere else: drawn as
   a shadow, which keeps to the box's rounded corners, 2px out as the
   outline is elsewhere. */
.year-selector-dropdown .Select-input:focus,
.year-selector-dropdown .Select-input:focus-visible {
  outline: none !important;
}
.year-selector-dropdown .Select-control:has(.Select-input:focus-visible) {
  border-radius: 10px !important;
  box-shadow: 0 0 0 2px var(--bs-body-bg), 0 0 0 4px var(--focus-ring-color) !important;
}

/* Selected value text */
.year-selector-dropdown .Select-value-label,
.year-selector-dropdown .Select-placeholder,
.year-selector-dropdown .Select--single > .Select-control .Select-value {
  color: var(--bs-body-color) !important;
  line-height: 36px;
}

/* Dropdown menu panel */
.year-selector-dropdown .Select-menu-outer {
  background-color: var(--bs-body-bg) !important;
  border-color: var(--bs-border-color) !important;
  border-radius: 10px;
}

/* Dropdown options — Dash uses VirtualizedSelect internally, these are the real class names */
.VirtualizedSelectOption {
  /* background-color: var(--bs-body-bg) !important; */
  color: var(--bs-body-color) !important;
  border-radius: 10px;
}

.VirtualizedSelectFocusedOption {
  background-color: var(--bs-color-hover) !important;
  color: var(--bs-body-color) !important;
}

.VirtualizedSelectSelectedOption {
  /* background-color: var(--bs-secondary) !important; */
  color: var(--bs-body-color) !important;
}

/* Entries summary ("1 bis 10 von 48 Einträgen") next to dbc.Pagination —
   mirrors chicoree's reactable table footer (website-framework) */
.table-entries-summary {
  font-size: inherit;
  color: var(--text-medium-contrast);
}

/* dbc.Pagination — circular controls, matching chicoree's reactable table
   (see website-framework/assets/css/reactable.css: .rt-page-button /
   .rt-prev-button / .rt-next-button) */
.pagination {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  list-style: none;
  margin: 0;
  padding: 0;
}

.pagination .page-item .page-link {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 40px;
  height: 40px;
  padding: 0;
  font-size: inherit;
  line-height: 1;
  border: 1px solid transparent;
  background: transparent;
  color: var(--bs-body-color);
  border-radius: 10px;
  cursor: pointer;
  box-shadow: none;
  vertical-align: middle;
  transition: background-color 0.15s, border-color 0.15s, color 0.15s;
}

/* Hover fills the button solid instead of a light tint, matching
   .rt-page-button:not(:disabled):hover in website-framework/assets/css/reactable.css */
.pagination .page-item:not(.active):not(.disabled) .page-link:hover {
  background-color: var(--btn-category-hover-bg);
  border-color: var(--btn-category-hover-border);
  color: var(--btn-category-hover-color);
}

.pagination .page-item.active .page-link {
  background: transparent;
  font-weight: 700;
  cursor: default;
  pointer-events: none;
}

.pagination .page-item.disabled .page-link {
  opacity: 0.35;
  cursor: default;
  pointer-events: none;
}

/* Prev/next: bordered square with a single-arrow glyph instead of the
   default «/» characters, matching the reactable prev/next buttons
   (website-framework/assets/css/reactable.css: .rt-prev-button / .rt-next-button) */
.pagination .page-item:first-child .page-link,
.pagination .page-item:last-child .page-link {
  border-color: var(--btn-category-border);
  font-size: 0;
  position: relative;
}

.pagination .page-item:first-child .page-link::after,
.pagination .page-item:last-child .page-link::after {
  content: '';
  display: inline-block;
  width: 16px;
  height: 16px;
  background-color: var(--accent-color);
  transition: background-color 0.15s;
}

/* Prev uses a thin chevron bracket, matching .rt-prev-button::after exactly */
.pagination .page-item:first-child .page-link::after {
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 16 16' fill='none' stroke='white' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M15 8H1m0 0 6-6M1 8l6 6'/%3E%3C/svg%3E") no-repeat center / contain;
  mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 16 16' fill='none' stroke='white' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M15 8H1m0 0 6-6M1 8l6 6'/%3E%3C/svg%3E") no-repeat center / contain;
}

/* Next uses a thicker shaft-arrow, not a mirror of the prev glyph — matching
   .rt-next-button::after exactly (website-framework deliberately crops this
   one differently since the source icon has more empty margin than prev's) */
.pagination .page-item:last-child .page-link::after {
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='6 6 52 52' fill='none' stroke='white' stroke-width='6'%3E%3Cpath d='M9.01058 32.002L53.875 32.002' stroke-miterlimit='10'/%3E%3Cpath d='M36.5684 51L54.9863 32L36.5684 13' stroke-miterlimit='10' stroke-linecap='square'/%3E%3C/svg%3E") no-repeat center / contain;
  mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='6 6 52 52' fill='none' stroke='white' stroke-width='6'%3E%3Cpath d='M9.01058 32.002L53.875 32.002' stroke-miterlimit='10'/%3E%3Cpath d='M36.5684 51L54.9863 32L36.5684 13' stroke-miterlimit='10' stroke-linecap='square'/%3E%3C/svg%3E") no-repeat center / contain;
}

/* Arrow glyph also inverts to the hover text color, matching
   .rt-prev-button:hover::after / .rt-next-button:hover::after */
.pagination .page-item:first-child:not(.disabled) .page-link:hover::after,
.pagination .page-item:last-child:not(.disabled) .page-link:hover::after {
  background-color: var(--btn-category-hover-color);
}

/* Mobile: shrink the controls so prev/next + page numbers fit on one row
   instead of wrapping, with horizontal scroll as a fallback for sections
   with an unusually large page count (same idiom as ul.zg-main-tabs). */
@media (max-width: 1024px) {
  .pagination {
    flex-wrap: nowrap;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
  }
  .pagination::-webkit-scrollbar {
    display: none;
  }
  .pagination .page-item .page-link {
    width: 2.4rem;
    height: 2.4rem;
    font-size: 0.9rem;
    flex-shrink: 0;
  }
  .pagination .page-item:first-child .page-link::after,
  .pagination .page-item:last-child .page-link::after {
    width: 13px;
    height: 13px;
  }
  /* Ellipsis ("...") item doesn't need a full button-width square —
     narrow it to free up room for the prev/next + page-number buttons. */
  .pagination .page-item.disabled:not(:first-child):not(:last-child) .page-link {
    width: 16px;
  }
}

/* Summary table */
.summary-table {
  width: 100%;
  margin-bottom: 20px;
  border-collapse: collapse;
  background: transparent;
  table-layout: fixed;
  /* The responsive html/body line-height (up to 1.75rem at desktop) is meant
     for paragraph text; inherited into table rows it roughly doubles row
     height. Reactable resets this on header cells (.rt-thead .rt-td/.rt-th
     { line-height: normal }) — apply the same tight line-height table-wide
     so row height tracks the 7px cell padding, not the page's line-height. */
  line-height: normal;
}

.summary-table td { padding: 7px 8px; vertical-align: middle; border-top: 1px solid var(--bs-border-color); }
.summary-table td:nth-child(2) { white-space: nowrap; font-variant-numeric: tabular-nums; }
.summary-table td:nth-child(3) { white-space: nowrap; text-align: right; padding-right: 0.75rem; }

/* Header: same font-size as the body text (reactable's .rt-th only bumps
   font-weight, it doesn't shrink/enlarge the text) and a 2px bottom border,
   matching reactable's own .rt-th exactly */
.summary-table th { padding: 7px 8px; font-size: 1rem; font-weight: 600; color: var(--bs-body-color); border-bottom: 2px solid var(--bs-border-color); }
.summary-table th:nth-child(2) { font-variant-numeric: tabular-nums; }
.summary-table th:nth-child(3) { text-align: right; padding-right: 0.75rem; }

.summary-table tr:nth-child(odd) td { background-color: rgba(var(--table-stripe-rgb), var(--dt-odd-opacity)); }
.summary-table tr:hover td { background-color: var(--bs-color-hover); }

/* No card-inset margin at wider viewports — reactable tables always run
   edge-to-edge in their container, they don't shrink and center themselves
   with side margins. */
@media (min-width: 1025px) {
  .summary-table td { padding: 7px 0; }
  .summary-table td:nth-child(1) { padding-left: 0.75rem; }
  .summary-table td:nth-child(3) { padding-right: 0.75rem; }
  /* A transfer table's first column holds a year rather than a category name,
     so the amount would otherwise sit against the left edge with a gap before
     the wide change column. Centring it in its own band closes that gap.
     Desktop only — the mobile widths and hidden trend label balance the row
     differently, see the max-width block further down. */
  .summary-table-transfer td:nth-child(2),
  .summary-table-transfer th:nth-child(2) { text-align: center; }
}

/* Trend cell: icon + inline label on desktop. Body colour throughout — the
   arrow already carries the direction, so the column reads as table text and
   not as a highlighted figure. The icons inherit it via currentColor, see
   callbacks.py. */
.trend-icon {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  cursor: default;
  color: var(--bs-body-color);
}

.trend-label {
  font-size: 0.8rem;
}

/* On mobile: hide label, show tooltip instead. Hidden from sight only, so a
   screen reader still reads the change (display: none would drop it). */
@media (max-width: 1024px) {
  .trend-label {
    position: absolute !important;
    width: 1px !important;
    height: 1px !important;
    padding: 0 !important;
    margin: -1px !important;
    overflow: hidden !important;
    clip: rect(0, 0, 0, 0) !important;
    white-space: nowrap !important;
    border: 0 !important;
  }
  .trend-icon { cursor: help; }
  .trend-icon::after {
    font-size: 0.85rem;
    font-weight: 500;
    padding: 4px 10px;
    border-radius: 10px;
    pointer-events: none;
    opacity: 0;
    transition: opacity 0.15s ease;
    z-index: 100;
    content: attr(data-tooltip);
    /* The label above already says it; the empty alt keeps a screen reader
       from reading it twice. The line before is for browsers without it. */
    content: attr(data-tooltip) / "";
    position: absolute;
    bottom: calc(100% + 6px);
    right: 0;
    white-space: nowrap;
  }
  .trend-icon { position: relative; }
  .trend-icon:hover::after { opacity: 1; }
  .trend-icon-up::after    { background: color-mix(in srgb, var(--trend-up-color) 15%, var(--bs-body-bg)); color: var(--trend-up-color); border: 1px solid var(--trend-up-color); }
  .trend-icon-down::after  { background: color-mix(in srgb, var(--trend-down-color) 15%, var(--bs-body-bg)); color: var(--trend-down-color); border: 1px solid var(--trend-down-color); }
  .trend-icon-neutral::after { background: color-mix(in srgb, var(--trend-neutral-color) 15%, var(--bs-body-bg)); color: var(--trend-neutral-color); border: 1px solid var(--trend-neutral-color); }
}

/* Change column header. Named wherever the column is wide enough to carry the
   word; on mobile it shrinks to "+/−" (see the max-width block further down),
   which is the only one of the two that needs a tooltip to say what it holds. */
.th-change {
  text-align: right;
  padding-right: 0.75rem;
}

.th-change-full {
  display: inline-block;
  line-height: 1.2;
  /* table-layout is fixed, so the column will not widen for the heading:
     "Vorjahresvergleich" hyphenates where it has to rather than spilling into
     the value column. hyphens needs the document language, set in app.py. */
  overflow-wrap: break-word;
  hyphens: auto;
}

.th-change-short { display: none; }

.th-change-label {
  cursor: help;
  position: relative;
  border-bottom: 1px dotted currentColor;
}

.th-change-label::after {
  content: attr(data-tooltip);
  position: absolute;
  bottom: calc(100% + 6px);
  right: 0;
  white-space: nowrap;
  font-size: 0.75rem;
  font-weight: 400;
  text-transform: none;
  letter-spacing: 0;
  padding: 4px 10px;
  border-radius: 10px;
  background: var(--bs-body-bg);
  color: var(--text-medium-contrast);
  border: 1px solid var(--bs-border-color);
  pointer-events: none;
  opacity: 0;
  transition: opacity 0.15s ease;
  z-index: 100;
}

.th-change-label:hover::after,
.th-change-label:focus::after {
  opacity: 1;
}

/* Pagination footer row — border-top + padding match reactable's own
   .rt-pagination exactly (site_libs/reactable-0.4.5/reactable.css) */
.table-footer-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 0.5rem;
  padding: 6px 4px;
  font-size: 1.0rem;
  border-top: 1px solid var(--bs-border-color);
}


/* Dropdown (gemeinde selector) */
.dropdown-menu.show {
  background-color: var(--modal-bg) !important;
}

.dropdown-menu .dropdown-item {
  background-color: transparent !important;
  color: var(--text-high-contrast) !important;
  font-weight: bold;
}

.dropdown-menu .dropdown-item:hover,
.dropdown-menu .dropdown-item:focus {
  background-color: var(--primary-blue-dark) !important;
  color: white !important;
}

/* Theme toggle: sun / moon icon, swapped via [data-bs-theme] */
.theme-toggle-btn {
  display: inline-flex;
  align-items: center;
  /* 24px at least, however small the icon (WCAG 2.5.8). */
  justify-content: center;
  min-width: 24px;
  min-height: 24px;
  cursor: pointer;
  color: var(--text-medium-contrast);
  transition: color 0.15s ease;
}

.theme-toggle-btn:hover {
  color: var(--text-high-contrast);
}

.theme-toggle-icon {
  display: none;
}

[data-bs-theme="light"] .theme-toggle-icon-moon {
  display: inline-block;
}

[data-bs-theme="dark"] .theme-toggle-icon-sun {
  display: inline-block;
}

/* Buttons */
.btn-primary {
  background-color: var(--btn-primary-bg);
  border-color: var(--btn-primary-bg);
  color: var(--btn-text-high);
}
.btn-primary:hover {
  background-color: var(--btn-hover-bg);
  border-color: var(--btn-hover-bg);
  color: var(--btn-text-high);
}
.btn-secondary {
  background-color: var(--btn-secondary-bg);
  color: var(--btn-category-color);
  border-color: var(--btn-category-border);
}
.btn-secondary:hover {
  background-color: var(--btn-hover-bg);
  border-color: var(--btn-hover-bg);
  color: var(--btn-text-high);
}

/* External link button (Statistikportal, OGD Katalog, ZugMap, the links
   under a topic tab) — the .link-card of the Statistikportal, which has it
   from zg.ch: a filled card in bold, its arrow in the accent colour, and an
   outline on hover. */
.link-button {
  display: flex;
  align-items: center;
  gap: 1rem;
  width: 400px;
  max-width: 90vw;
  padding: 1.5rem;
  border: 1px solid var(--link-card-border);
  border-radius: 0.3125rem;
  background-color: var(--link-card-bg);
  color: var(--text-high-contrast);
  font-size: 1rem;
  line-height: 1.25rem;
  font-weight: 700;
  hyphens: manual;
  text-decoration: none;
}
.link-button:hover {
  outline: 1px solid var(--link-card-hover-border);
  background-color: var(--link-card-hover-bg);
  color: var(--text-high-contrast);
}
.link-button:hover .ext-link-icon {
  background-color: var(--link-card-hover-arrow);
}
.link-button:focus-visible {
  outline: 3px solid var(--color-border-focus);
  outline-offset: 2px;
}
@media (min-width: 720px) {
  .link-button {
    padding: 2rem;
    border-radius: 0.625rem;
  }
}
@media (min-width: 1025px) {
  .link-button {
    font-size: 1.25rem;
    line-height: 1.5rem;
  }
}

/* ZG main tabs — larger font, tighter padding, horizontal scroll on mobile */
ul.zg-main-tabs {
  flex-wrap: nowrap !important;
  overflow-x: auto;
  overflow-y: hidden;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none; /* Firefox */
}
ul.zg-main-tabs::-webkit-scrollbar {
  display: none; /* Chrome/Safari */
}
.zg-main-tabs .nav-link {
  padding: 0.5rem 0.9rem;
  white-space: nowrap;
}
/* Inside the link rather than around it: the bar scrolls (overflow-x above),
   which clips anything drawn beyond its edges, focus outline included. */
.zg-main-tabs .nav-link:focus-visible {
  outline-offset: -2px;
}


/* Expandable source info */
.source-details {
  font-size: 0.7rem;
  /* html/body set line-height as an absolute length (up to 1.75rem), which
     this block inherits despite its much smaller font — giving line boxes
     ~2.5x the text size. Use a unitless value so it scales with 0.7rem. */
  line-height: 1.45;
}
/* Markdown wraps the text in a <p>; drop its Bootstrap bottom margin so the
   block ends as tightly as it is spaced. */
.source-details p {
  margin-bottom: 0;
}
.source-details summary {
  cursor: pointer;
  list-style: none;
  display: inline;
}
.source-details summary::-webkit-details-marker {
  display: none;
}
.source-details[open] summary {
  display: block;
  margin-bottom: 0.25rem;
}

/* Charts — shorter height on mobile */
@media (max-width: 1024px) {
  .dash-graph {
    height: 380px !important;
  }
  .js-plotly-plot .xtick text {
    font-size: 9px !important;
  }
  .js-plotly-plot .legend .layers {
    transform: scaleX(0.6);
    transform-origin: left center;
  }
}

/* Summary data tables — smaller font on mobile */
@media (max-width: 1024px) {
  .summary-table { font-size: 1rem; }
  /* !important needed: html.Colgroup sets inline style="width:..." per
     <col> in callbacks.py's _make_colgroup(), which otherwise always
     wins over any stylesheet rule regardless of media query/specificity. */
  .summary-table col:nth-child(1) { width: 55% !important; }
  .summary-table col:nth-child(2) { width: 32% !important; }
  .summary-table col:nth-child(3) { width: 13% !important; }
  /* Temporal tables (col 1 = "Jahr"/period, not a category name) need far
     less width for column 1 — give the difference to the count column,
     column 3 stays the same since its content (trend icon) doesn't change. */
  .summary-table.summary-table-temporal col:nth-child(1) { width: 42% !important; }
  .summary-table.summary-table-temporal col:nth-child(2) { width: 45% !important; }
  .summary-table.summary-table-temporal col:nth-child(3) { width: 13% !important; }
  /* Trend label is hidden on mobile (icon only, 32px) — tighten the
     column's own padding to match, freeing width for the name column. */
  .summary-table td:nth-child(3),
  .summary-table th:nth-child(3) {
    padding-right: 0.5rem;
  }
  /* 13% of the table is nowhere near enough for the heading, so it gives way
     to "+/−", which keeps the wording in its tooltip. */
  .th-change-full { display: none; }
  .th-change-short { display: inline; white-space: nowrap; }
  #summary-table-container h5 {
    font-size: 1rem;
  }
}

/* Pie chart labels — always dark so they're readable on light slices */
[data-bs-theme="dark"] .js-plotly-plot .slicetext {
  fill: #000000 !important;
}

/* ===== Gemeinde map (OpenLayers over ZugMap, assets/gemeinde_map.js) ===== */
.gemeinde-map {
  width: 100%;
  height: 360px;
  position: relative;
  border-radius: 10px;
  overflow: hidden;
}
/* The box OpenLayers draws the map into (assets/gemeinde_map.js). */
.gemeinde-map-canvas {
  position: absolute;
  inset: 0;
}
/* The name of the Gemeinde under the pointer, beside it. */
.map-tooltip {
  position: absolute;
  left: 0;
  top: 0;
  z-index: 3;
  padding: 2px 8px;
  border: 1px solid var(--color-border-default);
  border-radius: 6px;
  background-color: var(--bs-body-bg);
  color: var(--text-high-contrast);
  font-size: 0.85rem;
  font-weight: 600;
  white-space: nowrap;
  pointer-events: none;
}
/* Beside the Geografie banner and the links (md up), the map takes the
   height of that column rather than giving the row one of its own. */
@media (min-width: 768px) {
  .gemeinde-map {
    height: 100%;
    min-height: 320px;
  }
}
/* The points over the map (assets/gemeinde_map.js), each an overlay of
   OpenLayers placed at its position, and centred on it here. */
.map-pin {
  position: absolute;
  left: 0;
  top: 0;
  font-size: 18px;
  margin: -0.5em 0 0 -0.5em;
  pointer-events: auto;
  cursor: default;
}

/* The symbols of those points, on the map and in the Geografie banner: a
   circle for the centre, a triangle up for the highest point and one down
   for the lowest, in one colour (--map-symbol). Sized by the font size. */
.map-symbol {
  display: inline-block;
  width: 1em;
  height: 1em;
  flex-shrink: 0;
}
.map-symbol-ctr {
  border-radius: 50%;
  background: var(--map-symbol);
}
.map-symbol-max {
  clip-path: polygon(50% 0, 100% 100%, 0 100%);
  background: var(--map-symbol);
}
.map-symbol-min {
  clip-path: polygon(0 0, 100% 0, 50% 100%);
  background: var(--map-symbol);
}
/* In the banner, as the key before Zentrum, Min and Max. */
.geo-banner-label .map-symbol {
  font-size: 0.85rem;
  vertical-align: -0.12em;
  margin-right: 0.45rem;
}

/* The buttons over the map: back to the whole canton, and below it the
   shading on and off. */
.map-button {
  position: absolute;
  right: 10px;
  z-index: 2;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  padding: 0;
  border: 1px solid var(--color-border-default);
  border-radius: 6px;
  background-color: var(--bs-body-bg);
  color: var(--text-high-contrast);
  cursor: pointer;
}
.map-home-button { top: 10px; }
.map-shading-button { top: 50px; }
/* The fill on the map, told by the button's own: while the shading is on,
   it wears the selected Gemeinde's shading as the map shows it, its fill
   and outline (COLOURS in assets/gemeinde_map.js) over the basemap; with it
   off, it is plain like the reset button. On a layer of its own beneath
   the icon. */
.map-shading-button[aria-pressed="true"],
.map-shading-button[aria-pressed="true"]:hover {
  border-color: transparent;
}
.map-shading-button[aria-pressed="true"]::before {
  content: "";
  position: absolute;
  inset: -1px;
  border: 1px solid #2D4487;
  border-radius: inherit;
  background:
    linear-gradient(rgba(45, 68, 135, 0.30), rgba(45, 68, 135, 0.30)),
    #ffffff;
}
[data-bs-theme="dark"] .map-shading-button[aria-pressed="true"]::before {
  border-color: #9DB4F0;
  background:
    linear-gradient(rgba(157, 180, 240, 0.30), rgba(157, 180, 240, 0.30)),
    var(--bs-body-bg);
}
.map-shading-button svg {
  position: relative;
  z-index: 1;
}
/* Only where there is a pointer to hover with: on a touch screen :hover
   sticks to the button last tapped, and it stayed lit after the reset. */
@media (hover: hover) {
  .map-button:hover {
    background-color: var(--color-hover);
    border-color: var(--color-border-hover);
  }
  /* The buttons' labels (data-tooltip), in the box of the Gemeinde's name
     (.map-tooltip): beside the button, on hover only. No title: a phone
     shows that for a moment on every tap. The credit's button labelled
     only while folded, beside it to the right. */
  .map-button[data-tooltip]:hover::after,
  .gemeinde-map .ol-attribution.ol-collapsed button[data-tooltip]:hover::after {
    content: attr(data-tooltip);
    position: absolute;
    top: 50%;
    z-index: 3;
    padding: 2px 8px;
    border: 1px solid var(--color-border-default);
    border-radius: 6px;
    background-color: var(--bs-body-bg);
    color: var(--text-high-contrast);
    font-family: 'Inter', sans-serif;
    font-size: 0.85rem;
    font-style: normal;
    font-weight: 600;
    line-height: 1.5;
    white-space: nowrap;
    pointer-events: none;
    transform: translateY(-50%);
  }
  .map-button[data-tooltip]:hover::after {
    right: calc(100% + 6px);
  }
  .gemeinde-map .ol-attribution.ol-collapsed button[data-tooltip]:hover::after {
    left: calc(100% + 6px);
  }
}
.gemeinde-map .ol-attribution button {
  position: relative;
}

/* The map's credit (Quelle: GIS Kanton Zug) to the bottom left: the bottom
   right corner is where the Gemeinde selector floats over the map. */
.gemeinde-map .ol-attribution {
  right: auto;
  left: 0.5em;
  bottom: 0.5em;
  flex-flow: row;
  /* The button at the bottom of the box, where it is folded too, rather
     than centred in a box the credit's text might make taller. */
  align-items: flex-end;
  gap: 0.25em;
  padding: 2px;
  border-radius: 12px;
  background-color: var(--bs-body-bg);
  font-size: 0.75rem;
}
/* Folded, the round button alone, without the box around it: the box is
   only not painted, its padding kept, so that the button stays where it is
   as the credit opens. */
.gemeinde-map .ol-attribution.ol-collapsed {
  background-color: transparent;
}
.gemeinde-map .ol-attribution ul {
  padding: 0 6px 0 0;
  background: none;
  color: var(--text-high-contrast);
  font-size: inherit;
  /* As high as the button, so the box is no higher open than folded. */
  line-height: 22px;
  text-shadow: none;
}
/* The button that folds and unfolds it: the info icon of the Geografie
   banner and the charts' sources (mdi:information-outline), on a disc of the
   page's background so it reads over the map. No border: the icon draws its
   own ring. */
.gemeinde-map .ol-attribution button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 22px;
  height: 22px;
  margin: 0;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background-color: var(--bs-body-bg);
  color: var(--text-high-contrast);
  line-height: 1;
}
.gemeinde-map .ol-attribution button svg {
  width: 18px;
  height: 18px;
  /* The banner's icon at rest (.geo-banner-info-icon), full on hover. */
  opacity: 0.7;
}
.gemeinde-map .ol-attribution button:hover svg,
.gemeinde-map .ol-attribution button:focus-visible svg {
  opacity: 1;
}

/* While a Gemeinde switch is being drawn (assets/gemeinde_map.js). */
.gemeinde-map.switching { cursor: progress; }
/* ZugMap has no dark Ortsplan: the light one, inverted and turned back to
   its own hues, reads as one. The basemap only; the Gemeinden over it take
   colours of their own there (COLOURS in assets/gemeinde_map.js). */
[data-bs-theme="dark"] .gemeinde-map .gp-basemap {
  filter: invert(1) hue-rotate(180deg) brightness(0.95) contrast(0.9);
}

/* ===== Overview geo info panel ===== */
#gemeinde-info {
  font-size: 1.05rem;
}

/* Room between the content and the footer. The Gemeinde selector, which
   once took a row of its own there, floats over the content now and leaves
   none. */
.page-footer {
  margin-top: 3rem;
}

.footer-inner {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  align-items: center;
}

/* One size for every line of footer text: the link used to drop to 0.6rem
   on a phone, which left the version beside it looking a size larger. On a
   phone the size follows the screen width, so "Fachstelle für Daten und
   Statistik" keeps to one line between the logo and the theme toggle. */
.footer-text,
.footer-text a {
  font-size: clamp(0.5rem, 2.3vw, 0.7rem);
}

@media (min-width: 1025px) {
  .footer-text,
  .footer-text a {
    font-size: 0.75rem;
  }
}

.footer-text {
  text-align: center;
}

/* Below the mobile breakpoint the version takes a line of its own. */
@media (max-width: 767.98px) {
  .footer-version-sep {
    display: none;
  }
  .footer-version {
    display: block;
  }
}

.footer-inner .theme-toggle-btn {
  justify-self: end;
}

.footer-logo {
  width: 90px;
  height: auto;
  padding-right: 1rem;
}

@media (min-width: 1025px) {
  .footer-logo {
    width: 150px;
  }
}

[data-bs-theme="light"] .footer-logo-dark  { display: none; }
[data-bs-theme="dark"]  .footer-logo-light { display: none; }

#overview-links {
  margin-bottom: 10px;
}
/* Beside the map (md up) the last link ends flush with the map's foot, as
   the banner starts flush with its top: the map takes the height of this
   column, margin and all, and ran 10px past the link. The margin is for a
   phone, where the links come last. */
@media (min-width: 768px) {
  #overview-links {
    margin-bottom: 0;
  }
}
/* Beside the map, the links fill their column rather than keep the fixed
   width they have under a topic tab. */
#overview-links .link-button {
  width: 100%;
  max-width: none;
}

/* ===== Miscellaneous component styles ===== */
.source-info-icon {
  cursor: pointer;
  opacity: 0.6;
  font-size: 1rem;
}

.ext-link-icon {
  flex: 0 0 auto;
  width: 1.5rem;
  height: 1.5rem;
  margin-left: auto;
  background-color: var(--accent-color);
  /* The arrow of the portal's link cards. */
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Cg transform='rotate(-45 32 32)' fill='none' stroke='%23000' stroke-width='5.33'%3E%3Cpath d='M9 32H53.9'/%3E%3Cpath d='M36.6 51 55 32 36.6 13'/%3E%3C/g%3E%3C/svg%3E") center / contain no-repeat;
          mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Cg transform='rotate(-45 32 32)' fill='none' stroke='%23000' stroke-width='5.33'%3E%3Cpath d='M9 32H53.9'/%3E%3Cpath d='M36.6 51 55 32 36.6 13'/%3E%3C/g%3E%3C/svg%3E") center / contain no-repeat;
}

.show-more-summary {
  cursor: pointer;
  font-size: 0.75rem;
  opacity: 0.6;
}

/* ===== Card view toggle ===== */
.btn-link.float-end {
  color: var(--bs-secondary) !important;

}
.btn-link.float-end:hover {
  color: var(--bs-link-hover-color) !important;
}

/* The page title, "Gemeindeportrait Zug": larger on a large screen, where
   24px was lost above the full width of the page. */
.page-title {
  font-size: 24px;
  font-weight: bold;
  line-height: inherit;
}
@media (min-width: 992px) {
  .page-title {
    font-size: 34px;
  }
}

/* ===== Stat banners ===== */
/* .stat-banner is shared by the clickable key-figure banners and the geo
   banner. The clickable ones take after the value boxes of the
   Statistikportal — its colours, the number in regular weight with its unit
   written out — at the size they had before, which suits the map beside
   them. */
.stat-banner {
  background-color: var(--stat-banner-bg, #0070B8);
  color: var(--stat-banner-fg, #fff);
  border-radius: 10px;
  padding: 1.4rem 1.6rem;
  display: flex;
  align-items: center;
  gap: 1rem;
  height: 100%;
}

.stat-banner-clickable {
  cursor: pointer;
  transition: filter 0.15s;
  /* A <button>: drop what the browser gives one. */
  border: 0;
  width: 100%;
  text-align: start;
  font: inherit;
  color: var(--stat-banner-fg, #fff);
}
.stat-banner-clickable:hover {
  filter: brightness(1.1);
}
.stat-banner-icon {
  flex-shrink: 0;
  display: flex;
  justify-content: center;
  /* A column as wide for each icon, so the texts of the banners line up. */
  width: 56px;
  opacity: 0.85;
}

.stat-banner-body {
  display: flex;
  flex-direction: column;
  min-width: 0;
}

.stat-banner-header {
  display: flex;
  flex-direction: column;
  margin-bottom: 0.35rem;
}

.stat-banner-title {
  font-weight: 700;
  font-size: 1rem;
  line-height: 1.2;
}

.geo-banner {
  position: relative;
}
.geo-banner-info-icon {
  position: absolute;
  /* A 24px target (WCAG 2.5.8) around the 13–18px icon, in the card's
     bottom left corner, a little in from its rounding. */
  bottom: 6px;
  left: 6px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 24px;
  min-height: 24px;
  /* 0.4 was right at 3:1 against the banner; 0.7 is 5.8:1. */
  opacity: 0.7;
  cursor: pointer;
  color: white;
  line-height: 1;
  /* A <button>: drop what the browser gives one. */
  background: none;
  border: 0;
  padding: 0;
}
@media (min-width: 1025px) {
  .geo-banner-info-icon svg {
    width: 18px !important;
    height: 18px !important;
  }
  .source-info-icon {
    font-size: 1.2rem;
  }
}
.geo-banner-info-icon:hover,
.geo-banner-info-icon:focus-visible {
  opacity: 1;
}
/* Open, the button sits in the box (below), on its background. */
.geo-banner-info-icon[aria-expanded="true"] {
  z-index: 6;
  opacity: 1;
  color: var(--text-high-contrast);
}
/* The source, unfolded from the button as the map's credit is: a box of
   the page's background around the button, the text beside it, as wide as
   the card and wrapping within it, its top at the button's so that it opens
   downwards, over the link below. */
.geo-banner-info-panel {
  position: absolute;
  z-index: 5;
  top: calc(100% - 32px);
  left: 4px;
  right: 4px;
  padding: 4px 12px 8px 32px;
  border: 1px solid var(--color-border-default);
  border-radius: 12px;
  background-color: var(--bs-body-bg);
  color: var(--text-high-contrast);
  font-size: 0.85rem;
  line-height: 1.5;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.25);
}
.geo-banner-info-panel[hidden] {
  display: none;
}
.geo-banner-info-panel a {
  text-decoration: none;
}

.stat-banner-subtitle {
  font-size: 0.8rem;
  /* 0.9 for 4.5:1 on the blue banner; 0.8 was 3.9:1. */
  opacity: 0.9;
}

.stat-banner-value {
  font-size: 1.6rem;
  font-weight: 400;
  line-height: 1.1;
}
.stat-banner-unit {
  display: block;
  font-size: 0.9rem;
  line-height: 1.4;
  /* "Millionen Franken" in one piece, not broken across two lines. */
  white-space: nowrap;
}

/* The three key-figure banners: stacked on a narrow screen, in a row once
   each has the room for its icon, title and number side by side. */
.stat-banners-row {
  display: grid;
  grid-template-columns: 1fr;
  gap: 0.5rem;
}
@media (min-width: 992px) {
  .stat-banners-row {
    grid-template-columns: repeat(3, 1fr);
    gap: 1rem;
  }
}

/* ===== Geo banner variant ===== */
.geo-banner {
  background-color: #353f72;
  /* Its own dark blue in both themes, so white in both. */
  color: white;
}
.geo-banner-table {
  width: calc(100% + 1.6rem);
  margin-top: 4px;
  margin-right: -1.6rem;
}
.geo-banner-label {
  color: rgba(255,255,255,0.75);
  font-size: 0.82rem;
  padding: 1px 20px 1px 0;
  width: 100%;
}
.geo-banner-value {
  color: white;
  font-size: 0.82rem;
  font-weight: 600;
  text-align: right;
  white-space: nowrap;
}
.geo-banner-indent {
  padding-left: 1rem;
}

/* ===== Plotly theme via CSS (no server callback needed on theme change) ===== */

/* Spikelines */
/* Plotly draws each spike twice: the line itself (spikethickness=1) over a
   wider halo (thickness + 2). The halo is hidden to keep a true hairline. */
.js-plotly-plot .spikeline[stroke-width="1"] { stroke: var(--bs-body-color) !important; }
.js-plotly-plot .spikeline[stroke-width="3"] { display: none; }

/* Grid lines */
.js-plotly-plot .ygrid.crisp,
.js-plotly-plot .xgrid.crisp {
  stroke: var(--bs-body-color) !important;
  stroke-opacity: 0.3 !important;
}

/* Hover labels */
.js-plotly-plot .hoverlayer path {
  fill: var(--bs-body-bg) !important;
  stroke: var(--bs-border-color) !important;
  stroke-width: 1.5px !important;
}
.js-plotly-plot .hoverlayer .hovertext text { fill: var(--bs-body-color) !important; }

/* Update menu (dropdown buttons inside plots) */
.js-plotly-plot .updatemenu-item-rect {
  fill: var(--bs-body-bg) !important;
  stroke: var(--bs-border-color) !important;
  rx: 3px; ry: 3px;
}
.js-plotly-plot .updatemenu-item-rect:hover { filter: brightness(var(--updatemenu-hover-brightness)) !important; }
.js-plotly-plot .updatemenu-item-text { pointer-events: none; fill: var(--bs-body-color) !important; }
.js-plotly-plot .updatemenu-header-arrow { fill: var(--bs-body-color) !important; }

/* Animation slider */
.js-plotly-plot rect.slider-grip-rect {
  width: 6px; height: 20px; rx: 1px; translate: 7px 0px;
  fill: var(--bs-border-color) !important;
}
.js-plotly-plot rect.slider-rail-rect {
  fill: var(--bs-body-bg) !important;
  stroke: var(--bs-border-color) !important;
  height: 6px !important; stroke-width: 1px !important;
}
.js-plotly-plot .slider-tick-rect { fill: var(--bs-body-color) !important; }
.js-plotly-plot .slider-label { fill: var(--bs-body-color) !important; }

/* Axis lines and tick marks — target all crisp paths/lines except grid lines */
.js-plotly-plot path.crisp:not(.xgrid):not(.ygrid),
.js-plotly-plot .xtick line,
.js-plotly-plot .ytick line { stroke: var(--bs-body-color) !important; }

/* Plot text — always matches body color */
.js-plotly-plot text {
  fill: var(--bs-body-color) !important;
}

/* Reference line of a diverging stacked bar chart (the Wanderungssaldo over the
   migration flows). Its color is baked into the figure when it is built, so
   after a theme switch it would keep the old mode's near-black or near-white
   and lose contrast. Follow the axis lines above instead.
   Scoped to plots that actually draw bars, and to scatter-specific classes, so
   ordinary line charts keep their palette colors.
   A graph whose config names a colour for its line_category carries
   .fixed-reference-line and is left out of this: the colour was chosen, so it
   outranks the theme, and !important here would otherwise beat the figure's
   own inline stroke. See graph_block() in layout.py. */
.graph-block-wrapper:not(.fixed-reference-line) .js-plotly-plot .barlayer:has(.trace) ~ .scatterlayer .js-line,
.graph-block-wrapper:not(.fixed-reference-line) .js-plotly-plot:has(.barlayer .trace) .legend .legendlines path {
  stroke: var(--bs-body-color) !important;
}
.graph-block-wrapper:not(.fixed-reference-line) .js-plotly-plot .barlayer:has(.trace) ~ .scatterlayer .points path.point,
.graph-block-wrapper:not(.fixed-reference-line) .js-plotly-plot:has(.barlayer .trace) .legend .legendpoints path.scatterpts {
  fill: var(--bs-body-color) !important;
}



/* ===== Compact sticky header =====
   Takes over from the page header once it scrolls away, merging the active tab
   and the Gemeinde into one line ("Bevölkerung Gemeinde Risch"). Slides in and
   out via transform so it is off-screen — not just invisible — while at rest.
   Sits above the floating Gemeinde selector (z-index 1000) and below the
   Gemeinde switch overlay (9999). */
.sticky-header {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 1020;
  padding: 0.5rem 1rem;
  text-align: center;
  font-size: 1.05rem;
  font-weight: 600;
  color: var(--bs-body-color);
  background: var(--bs-body-bg);
  border-bottom: 1px solid var(--bs-border-color);
  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.08);
  transform: translateY(-100%);
  transition: transform 0.2s ease;
}
.sticky-header.visible {
  transform: translateY(0);
}

/* ?topic= / ?card= views: the compact header replaces the page header rather
   than waiting for a scroll. Sticky instead of fixed so it keeps its place in
   the flow and the content below it starts where it should. */
.sticky-header.sticky-header--pinned {
  position: sticky;
  transform: none;
  margin-bottom: 0.75rem;
}

/* A single card is its own heading — no page header, no compact one. */
.sticky-header.sticky-header--off {
  display: none;
}
@media (max-width: 767.98px) {
  .sticky-header {
    font-size: 0.95rem;
    padding: 0.4rem 0.75rem;
  }
}
@media (prefers-reduced-motion: reduce) {
  .sticky-header {
    transition: none;
  }
  /* The overlay flag and the loading bar stand still: still there to say
     that something is loading, without the pulse and the sweep. */
  .loading-gemeinde-pulse,
  .graph-loading-bar {
    animation: none;
  }
}

/* ===== Gemeinde switch loading overlay ===== */
.gemeinde-loading-overlay {
  position: fixed;
  inset: 0;
  z-index: 9999;
  display: flex;
  align-items: center;
  justify-content: center;
  background: color-mix(in srgb, var(--color-bg) 60%, transparent);
  backdrop-filter: blur(2px);
  -webkit-backdrop-filter: blur(2px);
}

.loading-gemeinde-pulse {
  width: 80px;
  height: 80px;
  object-fit: contain;
  opacity: 0.7;
  animation: gemeinde-pulse 1s ease-in-out infinite;
}

@keyframes gemeinde-pulse {
  0%, 100% { opacity: 0.7;  transform: scale(1);    }
  50%       { opacity: 0.2;  transform: scale(0.85); }
}

/* ===== Graph loading bar ===== */
.graph-loading-bar {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 3px;
  background: linear-gradient(90deg, transparent 0%, var(--color-accent) 50%, transparent 100%);
  background-size: 200% 100%;
  animation: graph-loading-sweep 1.2s ease-in-out infinite;
}
@keyframes graph-loading-sweep {
  0%   { background-position:  200% 0; }
  100% { background-position: -200% 0; }
}

/* A tab whose charts are not drawn yet for what is selected (the tab-stale
   callback in callbacks.py): its charts kept out of sight, with the flag of
   the Gemeinde pulsing where they will be, as on the switch overlay, and
   faded in once they are drawn — rather than a moment of empty axes, or of
   the previous Gemeinde. The callback sets the flag as --gemeinde-flag. */
[id^="tab-body-"] .js-plotly-plot {
  transition: opacity 0.2s ease-in;
}
.tab-stale .js-plotly-plot {
  opacity: 0;
}
.tab-stale [role="img"] > div {
  position: relative;
}
.tab-stale [role="img"] > div::before {
  content: "";
  position: absolute;
  top: 50%;
  left: 50%;
  width: 80px;
  height: 80px;
  margin: -40px 0 0 -40px;
  z-index: 1;
  pointer-events: none;
  background: var(--gemeinde-flag) center / contain no-repeat;
  opacity: 0.7;
  animation: gemeinde-pulse 1s ease-in-out infinite;
}
/* The flag says it all: no loading bar of dcc.Loading beside it while the
   tab's request is out. */
.tab-stale .graph-loading-bar {
  display: none;
}
@media (prefers-reduced-motion: reduce) {
  .tab-stale [role="img"] > div::before {
    animation: none;
  }
}

/* ===== Graph download buttons ===== */
.graph-block-wrapper {
  container-type: inline-size;
}
.graph-block-header {
  gap: 0.5rem;
  flex-wrap: wrap;
  padding-top: 0.25rem;
  padding-bottom: 0.25rem;
}
@container (max-width: 520px) {
  .graph-block-header {
    flex-direction: column;
  }
}
.graph-title-range {
  display: block;
  font-size: 1.0rem;
  font-weight: normal;
  opacity: 0.5;
  margin-top: 1px;
}
.graph-btn-row {
  flex-shrink: 0;
  align-items: center !important;
}
/* OGD link + camera dropdown toggle — colors and height match
   website-framework's .btn-outline-success (plot toolbar OGD/download
   buttons), which was reskinned onto the shared --btn-category-* palette
   and a common 2.5rem/40px toolbar height (see website-framework commits
   "unify plot/table toolbar button colors and height" and "style plot
   toolbar dropdowns (OGD/download) to match filter dropdowns"). */
.graph-dl-btn,
.graph-dl-dropdown .btn {
  box-sizing: border-box !important;
  display: inline-flex !important;
  align-items: center !important;
  justify-content: center !important;
  height: 2.5rem !important;
  min-height: 2.5rem !important;
  max-height: 2.5rem !important;
  padding: 0 10px !important;
  font-size: 0.78rem;
  line-height: 1 !important;
  color: var(--btn-category-color) !important;
  border-color: var(--btn-category-border) !important;
  background: transparent !important;
  border-radius: 10px !important;
  transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, color 0.15s ease-in-out !important;
}
.graph-dl-btn:hover,
.graph-dl-btn:hover:focus,
.graph-dl-dropdown .btn:hover {
  background-color: var(--btn-category-hover-bg) !important;
  border-color: var(--btn-category-hover-border) !important;
  color: var(--btn-category-hover-color) !important;
}
.graph-dl-btn:focus,
.graph-dl-btn:focus-visible,
.graph-dl-btn:active,
.graph-dl-dropdown .btn:focus,
.graph-dl-dropdown .btn:focus-visible,
.graph-dl-dropdown .btn:active {
  background: transparent !important;
  color: var(--btn-category-color) !important;
  box-shadow: none !important;
}

.graph-dl-btn {
  font-size: 1.2rem;
}

/* Replace Bootstrap's default triangle caret on the camera dropdown toggle
   with the same orange chevron used elsewhere on the site (year-selector
   dropdown, pagination prev/next — see website-framework/assets/css/
   styles.scss ".btn-outline-success.dropdown-toggle::after"). */
.graph-dl-dropdown .dropdown-toggle::after {
  content: "";
  display: inline-block;
  width: 24px;
  height: 24px;
  margin-left: 2px;
  vertical-align: middle;
  border: 0;
  background-color: var(--accent-color);
  transition: background-color 0.15s;
  transform: rotate(90deg);
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 64 64' fill='none'%3E%3Cpath stroke='white' stroke-linecap='square' stroke-miterlimit='10' stroke-width='4' d='m23 49 18-18-18-18'/%3E%3C/svg%3E") no-repeat center / contain;
  mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 64 64' fill='none'%3E%3Cpath stroke='white' stroke-linecap='square' stroke-miterlimit='10' stroke-width='4' d='m23 49 18-18-18-18'/%3E%3C/svg%3E") no-repeat center / contain;
}
/* .show is what Bootstrap actually adds to the toggle button while its menu
   is open, so both are needed here to keep the chevron matching the hover
   color for the entire pressed/open state. */
.graph-dl-dropdown .dropdown-toggle:hover::after,
.graph-dl-dropdown .dropdown-toggle:active::after,
.graph-dl-dropdown .dropdown-toggle.show::after {
  background-color: var(--btn-category-hover-color);
}

/* OGD logo — single-color vector masked instead of swapping between
   separately pre-colored image files, so hover just recolors it like every
   other icon in this button family (same var(--btn-category-hover-color)
   the bi-/DashIconify icons already switch to on .graph-dl-btn:hover). */
.ogd-logo-icon {
  display: inline-block;
  width: 40px;
  height: 25px;
  background-color: var(--btn-category-color);
  transition: background-color 0.15s ease-in-out;
  -webkit-mask: url("OpenDataZG_Logo_vectorized.svg") no-repeat center / contain;
  mask: url("OpenDataZG_Logo_vectorized.svg") no-repeat center / contain;
}
.graph-dl-btn:hover .ogd-logo-icon {
  background-color: var(--btn-category-hover-color);
}

/* Dropdown menu items — PNG/SVG entries. Overrides the generic
   .dropdown-menu .dropdown-item rule (gemeinde selector) with the same
   --btn-category-* text/hover colors website-framework uses for its
   .btn-outline-success dropdown-toggle + .dropdown-menu .dropdown-item
   (bi-filetype-png/svg icons inherit via currentColor). */
.graph-dl-dropdown .dropdown-item {
  gap: 6px;
  color: var(--btn-category-color) !important;
}
.graph-dl-dropdown .dropdown-item:hover,
.graph-dl-dropdown .dropdown-item:focus {
  background-color: var(--btn-category-hover-bg) !important;
  color: var(--btn-category-hover-color) !important;
}
