/*
* Style tweaks
* --------------------------------------------------
*/

/* Global constants, primarily from our Design System Tokens. */
:root {
  /* Project Sidewalk primary color pallete. */
  --color-asphalt-100: #D5D3E4;
  --color-asphalt-200: #9F9DB1;
  --color-asphalt-300: #615E78;
  --color-asphalt-400: #424055;
  --color-asphalt-500: #2D2A3F;
  --color-asphalt-600: #242232;
  --color-asphalt-700: #1B1926;
  --color-asphalt-800: #121119;
  --color-asphalt-900: #09080D;

  --color-pine-100: #E4F4EE;
  --color-pine-200: #C9E9DD;
  --color-pine-300: #AEDFCD;
  --color-pine-400: #93D4BC;
  --color-pine-500: #78C9AB;
  --color-pine-600: #60A189;
  --color-pine-700: #487967;
  --color-pine-800: #305044;
  --color-pine-900: #182822;

  --color-orange-100: #FDEDE8;
  --color-orange-200: #FBE3DC;
  --color-orange-300: #F6B6A2;
  --color-orange-400: #F29173;
  --color-orange-500: #EB724E;
  --color-orange-600: #A74D32;
  --color-orange-700: #8D442F;
  --color-orange-800: #5E2E1F;
  --color-orange-900: #2F1710;

  --color-banana-100: #FEF7E8;
  --color-banana-200: #FDF0D1;
  --color-banana-300: #FDE8BA;
  --color-banana-400: #FCE1A3;
  --color-banana-500: #FBD98C;
  --color-banana-600: #FFCD64;
  --color-banana-700: #F1B12A;
  --color-banana-800: #CB8F11;
  --color-banana-900: #A87303;

  /* Jade. Added for the "good" end of the severity/quality scale (#4724): levels 1 and 2 previously both sat on
     banana, separated only by saturation, which reads at full strength but disappears once either is lightened
     into a background wash. 400 is the face colour, 700 the darkened edge, 100 the wash; the rest are
     interpolated. Sized to the other rating faces' perceptual band — they sit at CIE L* 90/76/70, 400 lands at 78. */
  --color-jade-100: #F2FBF6;
  --color-jade-200: #CCEEDB;
  --color-jade-300: #A5E0C0;
  --color-jade-400: #7FD3A5;
  --color-jade-500: #6FB890;
  --color-jade-600: #5F9E7B;
  --color-jade-700: #4F8366;
  --color-jade-800: #395E49;
  --color-jade-900: #263F31;

  --color-neutral-white: #FFFFFF;
  --color-neutral-100: #F0F0F0;
  --color-neutral-200: #E1E1E1;
  --color-neutral-300: #D1D1D1;
  --color-neutral-400: #C2C2C2;
  --color-neutral-500: #B3B3B3;
  --color-neutral-600: #8F8F8F;
  --color-neutral-700: #6B6B6B;
  --color-neutral-800: #484848;
  --color-neutral-900: #242424;
  --color-neutral-black: #000000;

  /* Project Sidewalk secondary color palette. Each ramp's 500 is a canonical label-type color (see below). */
  --color-blue-100: #F6FAFE;
  --color-blue-200: #F1F7FD;
  --color-blue-300: #E8F2FC;
  --color-blue-400: #C6E0FA;
  --color-blue-500: #78B0EA;

  --color-mint-100: #EDF8F5;
  --color-mint-200: #DBF0EC;
  --color-mint-300: #AEDFCD;
  --color-mint-400: #92D3C5;
  --color-mint-500: #62C0AC;

  --color-lime-100: #F6FCE9;
  --color-lime-200: #ECF8D3;
  --color-lime-300: #C7EB7A;
  --color-lime-400: #A7D447;
  --color-lime-500: #8FC31E;

  --color-purple-100: #F5ECF9;
  --color-purple-200: #EAD9F2;
  --color-purple-300: #D6B2E6;
  --color-purple-400: #C99ADE;
  --color-purple-500: #BE87D8;

  --color-pink-100: #FBEAF3;
  --color-pink-200: #F7D4E7;
  --color-pink-300: #EFA9CF;
  --color-pink-400: #F092C6;
  --color-pink-500: #E679B5;

  --color-pumpkin-100: #FEF1E7;
  --color-pumpkin-200: #FDE2CE;
  --color-pumpkin-300: #FAC69E;
  --color-pumpkin-400: #F8A96D;
  --color-pumpkin-500: #F68D3E;

  --color-yellow-100: #FEFAF0;
  --color-yellow-200: #FEF7E6;
  --color-yellow-300: #FEEECD;
  --color-yellow-400: #FCD977;
  --color-yellow-500: #FABF1C;

  /* Project Sidewalk functional color pallete. */
  --color-success-100: #43EF8E;
  --color-success-200: #11C961;

  --color-error-100: #F47178;
  --color-error-200: #ED1C24;
  --color-error-300: color-mix(in srgb, var(--color-error-200) 78%, black); /* Needed for accessibility compliance. */

  --color-warning-100: #FFEF92;
  --color-warning-200: #FFDE24;

  --color-link-100: #3E8BD9;
  --color-link-200: #0A58CA;

  /* Explore minimap's route-ahead line: link-100 mixed 55% with white, so the way forward reads as the forward
     arrow's blue without matching the peg. A flat value because Google Maps polylines can't take color-mix(). */
  --color-route-ahead: #95BFEA;

  /* Project Sidewalk label-type color palette. The canonical color for each accessibility label type. */
  --color-label-curb-ramp: var(--color-lime-500);
  --color-label-no-curb-ramp: var(--color-pink-500);
  --color-label-obstacle: var(--color-blue-500);
  --color-label-surface-problem: var(--color-pumpkin-500);
  --color-label-no-sidewalk: var(--color-purple-500);
  --color-label-crosswalk: var(--color-yellow-500);
  --color-label-signal: var(--color-mint-500);

  /* AccessScore ramp, read low → high: red-orange for the worst scores, yellow at 50, green for the best. A hue
     at the midpoint rather than a grey because the grey vanished against the basemap's grey land. Validated with
     the dataviz palette checks: every adjacent pair clears the CVD floor (worst ΔE 14.5, protan) and the
     normal-vision floor (16.2); yellow-500 is only 1.4:1 against the basemap, so anything colored by this ramp also
     carries its value as text (tooltip, legend, table). Read through public/js/common/scoreRamp.js — a chart or map
     layer never lists these hexes itself. */
  --color-score-ramp-1: var(--color-orange-600);
  --color-score-ramp-2: var(--color-orange-500);
  --color-score-ramp-3: var(--color-yellow-500);
  --color-score-ramp-4: var(--color-jade-600);
  --color-score-ramp-5: var(--color-jade-800);

  /* The same ramp re-stepped for the AccessScore tool's dark basemap (Mapbox dark-v11, land #292929): every step
     is >= 3:1 on that surface, which rules out the light ramp's dark poles. Forcing every step light compresses the
     warm arm — steps 1 and 2 sit in the 7 ΔE band — but the ramp is interpolated and its poles are far apart, so the
     arm reads as one hue gradient rather than two levels. Same CVD story as the light ramp: values travel as text. */
  --color-score-ramp-dark-1: var(--color-orange-500);
  --color-score-ramp-dark-2: var(--color-pumpkin-400);
  --color-score-ramp-dark-3: var(--color-yellow-500);
  --color-score-ramp-dark-4: var(--color-jade-500);
  --color-score-ramp-dark-5: var(--color-jade-300);

  /* Street audit-state colors. The map layer and the legend swatch both read these, so a swatch can't drift from
     the line it stands for. Unaudited is warm rather than grey: the greys it used to sit in are already spoken for
     by the region outline, the region fill and the basemap's own roads, so the streets most in need of a mapper
     were the ones that receded. */
  --color-street-audited: var(--color-asphalt-500);
  --color-street-unaudited: var(--color-orange-600);

  /* Place markers on the AccessScore map (#5311): one color for every category, with the glyph and the sidebar's icon
     list carrying the category. The map already spends its hues on the score ramp and the seven label types, and a
     dark disc with a light halo holds >= 3:1 on both basemaps, so a place reads as a landmark rather than as data.
     The api-docs preview reads the same two tokens. */
  --color-place-marker: var(--color-asphalt-800);
  --color-place-marker-halo: var(--color-neutral-white);

  /* Font stacks. Mulish handles ~all type (body, UI, headings H3 and below); Raleway is reserved for top-level H1/H2
       and the Small-Accent style. Mulish doesn't ship CJK glyphs, so its stack ends with Traditional-Chinese system
       fonts (PingFang TC on macOS/iOS, Microsoft JhengHei on Windows; Linux/Android fall through via sans-serif). The
       browser does per-glyph fallback: Latin renders in Mulish, CJK in the OS CJK font.
       Avoid --font-accent (Raleway) for text that shows numbers — its digits aren't tabular, so figures don't align
       vertically in columns or stay steady in changing counters/timers. Use --font-primary for numeric content. */
  --font-primary: "Mulish", "PingFang TC", "Microsoft JhengHei", sans-serif;
  --font-accent:  "Raleway", sans-serif;

  /* Monospace, for content whose alignment carries meaning: code, BibTeX, fixed-width identifiers. The platform faces
       behind JetBrains Mono cover the scripts its Latin subsets don't. */
  --font-mono:    "JetBrains Mono", sfmono-regular, consolas, "Liberation Mono", menlo, monospace;

  /* Typography scale: mirrors the Figma Design System Tokens file.
       Format: font-weight font-size/line-height font-family. The font-size and line-height are each multiplied by
       var(--ui-scale, 1) so every type token scales with uniform UI scaling that we have on some pages. Pages that
       don't set --ui-scale fall back to 1, leaving the tokens at their native sizes. */
  --text-h1-xl-bold:       700 calc(80px * var(--ui-scale, 1))/calc(80px * var(--ui-scale, 1)) var(--font-primary);

  --text-h1-bold:          700 calc(32px * var(--ui-scale, 1))/calc(38px * var(--ui-scale, 1)) var(--font-accent);

  --text-h2-bold:          700 calc(28px * var(--ui-scale, 1))/calc(34px * var(--ui-scale, 1)) var(--font-accent);

  --text-h3-bold:          700 calc(24px * var(--ui-scale, 1))/calc(28px * var(--ui-scale, 1)) var(--font-primary);
  --text-h3-medium:        500 calc(24px * var(--ui-scale, 1))/calc(28px * var(--ui-scale, 1)) var(--font-primary);

  --text-title-bold:       700 calc(20px * var(--ui-scale, 1))/calc(24px * var(--ui-scale, 1)) var(--font-primary);
  --text-title-medium:     500 calc(20px * var(--ui-scale, 1))/calc(24px * var(--ui-scale, 1)) var(--font-primary);

  --text-subtitle-bold:    700 calc(18px * var(--ui-scale, 1))/calc(22px * var(--ui-scale, 1)) var(--font-primary);
  --text-subtitle-medium:  500 calc(18px * var(--ui-scale, 1))/calc(22px * var(--ui-scale, 1)) var(--font-primary);
  --text-subtitle-regular: 400 calc(18px * var(--ui-scale, 1))/calc(22px * var(--ui-scale, 1)) var(--font-primary);

  --text-body-bold:        700 calc(16px * var(--ui-scale, 1))/calc(22px * var(--ui-scale, 1)) var(--font-primary);
  --text-body-medium:      500 calc(16px * var(--ui-scale, 1))/calc(22px * var(--ui-scale, 1)) var(--font-primary);
  --text-body-regular:     400 calc(16px * var(--ui-scale, 1))/calc(22px * var(--ui-scale, 1)) var(--font-primary);

  /* Body size with the looser leading long-form reading wants (documentation, articles, multi-paragraph copy);
       --text-body-* stays the token for UI copy — labels, cells, cards, short blurbs. Not yet in the Figma file. */
  --text-prose-regular:    400 calc(16px * var(--ui-scale, 1))/calc(26px * var(--ui-scale, 1)) var(--font-primary);

  --text-small-bold:       700 calc(14px * var(--ui-scale, 1))/calc(18px * var(--ui-scale, 1)) var(--font-primary);
  --text-small-medium:     500 calc(14px * var(--ui-scale, 1))/calc(18px * var(--ui-scale, 1)) var(--font-primary);
  --text-small-regular:    400 calc(14px * var(--ui-scale, 1))/calc(18px * var(--ui-scale, 1)) var(--font-primary);
  --text-small-accent:     400 calc(14px * var(--ui-scale, 1))/calc(18px * var(--ui-scale, 1)) var(--font-accent);

  --text-caption-semibold: 600 calc(12px * var(--ui-scale, 1))/calc(18px * var(--ui-scale, 1)) var(--font-primary);
  --text-caption-medium:   500 calc(12px * var(--ui-scale, 1))/calc(18px * var(--ui-scale, 1)) var(--font-primary);
  --text-caption-regular:  400 calc(12px * var(--ui-scale, 1))/calc(18px * var(--ui-scale, 1)) var(--font-primary);
  --text-underline-medium: 500 calc(12px * var(--ui-scale, 1))/calc(18px * var(--ui-scale, 1)) var(--font-primary);

  /* Try to avoid using such tiny text!! */
  --text-tiny-semibold: 600 calc(10px * var(--ui-scale, 1))/calc(12px * var(--ui-scale, 1)) var(--font-primary);
  --text-tiny-medium:   500 calc(10px * var(--ui-scale, 1))/calc(12px * var(--ui-scale, 1)) var(--font-primary);
  --text-tiny-regular:  400 calc(10px * var(--ui-scale, 1))/calc(12px * var(--ui-scale, 1)) var(--font-primary);

  /* Code blocks. Inline code keeps a relative size instead (font-family + 0.9em), so it tracks whatever text it
       sits in. Not yet in the Figma file. */
  --text-code-regular:  400 calc(14px * var(--ui-scale, 1))/calc(21px * var(--ui-scale, 1)) var(--font-mono);

  /* Spacing scale. Scales with --ui-scale like the type tokens, so it's safe inside tool UI too. */
  --space-xxs:  calc(4px * var(--ui-scale, 1));
  --space-xs:   calc(8px * var(--ui-scale, 1));
  --space-sm:   calc(12px * var(--ui-scale, 1));
  --space-md:   calc(16px * var(--ui-scale, 1));
  --space-lg:   calc(24px * var(--ui-scale, 1));
  --space-xl:   calc(32px * var(--ui-scale, 1));
  --space-xxl:  calc(48px * var(--ui-scale, 1));
  --space-xxxl: calc(64px * var(--ui-scale, 1));

  /* Corner radii. These are the values the docs/dashboard surfaces settled on; the DST buttons use larger ones
       (8–16px, see .button--*), so the set still needs reconciling against the Figma file (#4300). */
  --border-radius-sm: calc(3px * var(--ui-scale, 1));
  --border-radius:    calc(4px * var(--ui-scale, 1));
  --border-radius-lg: calc(8px * var(--ui-scale, 1));

  /* Elevation. */
  --box-shadow-sm: 0 1px 3px rgb(0 0 0 / 10%);
  --box-shadow:    0 2px 5px rgb(0 0 0 / 15%);
  --box-shadow-lg: 0 4px 12px rgb(0 0 0 / 15%);

  /* Motion. */
  --transition-fast:   0.15s ease;
  --transition-medium: 0.3s ease;
  --transition-slow:   0.5s ease;

  /* Stacking layers, lowest to highest. */
  --z-index-dropdown:       1000;
  --z-index-sticky:         1020;
  --z-index-fixed:          1030;
  --z-index-modal-backdrop: 1040;
  --z-index-modal:          1050;
  --z-index-popover:        1060;
  --z-index-tooltip:        1070;

  /* Responsive breakpoints. var() can't be used inside a media query, so these are the reference values — write the
       pixel value in the @media rule and name the token in a comment beside it. */
  --breakpoint-xs: 480px;
  --breakpoint-sm: 768px;
  --breakpoint-md: 992px;
  --breakpoint-lg: 1200px;
  --breakpoint-xl: 1400px;

  /* Other globals. */
  --navbar-height: 38px;
  --navbar-z-index: var(--z-index-fixed);
}


/* Global visibility utilities. !important so they hide an element regardless of the display/visibility its own
   classes set; scripts show the element again by removing the class (never by setting an inline style). */
.ps-hidden {
  display: none !important;
}

.ps-invisible {
  visibility: hidden !important;
}

/* For icons colored from CSS, since an <img> can't be recolored: paints the shape of the mask-image file in `color`.
   No autoprefixer in this build, so the -webkit- siblings are written out; without them old Safari paints a block. */
.ps-mask-icon {
  display: inline-block;
  flex: 0 0 auto;
  background-color: currentcolor;
  -webkit-mask-position: center;
  mask-position: center;
  -webkit-mask-size: contain;
  mask-size: contain;
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
}


/* General styles */

/* Raleway's three weights, as weights of one family — which is what lets `font-weight: 700` on --font-accent pick
   Raleway Bold. A weight the family doesn't declare is drawn by smearing the regular outlines instead, so the
   --text-h1-bold / --text-h2-bold headings are the ones this is here for.
   `swap` on all of them: Raleway sets headings, and the alternative is a heading that is simply absent until the
   face arrives. The flash of fallback it trades that for is short — these are same-origin now, not a round trip to
   Google — and it matches what the Mulish/Inter/JetBrains Mono faces in css/fonts.css already do. */
@font-face {
  font-family: raleway;
  src: url("../fonts/Raleway/b6e895e1c814e96955ba7aa99f31a112-Raleway-Regular.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: raleway;
  src: url("../fonts/Raleway/afc0b7c6e5112118dc03ca0f09480dc8-Raleway-Bold.woff2") format("woff2");
  font-weight: 700;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: raleway;
  src: url("../fonts/Raleway/24eca4a5d1ea008802d5b44c4b60a1fd-Raleway-ExtraBold.woff2") format("woff2");
  font-weight: 800;
  font-style: normal;
  font-display: swap;
}

html,
body {
  font-family: var(--font-primary);
  color: var(--color-neutral-black);
}

body {
  overflow-x: clip; /* Prevent horizontal scroll on narrow devices */

  /* Offsets content below the fixed navbar, plus the test-server banner when present. --test-banner-height is
     published by the banner's inline script and defaults to 0 (banner absent/dismissed, and always on prod). */
  padding-top: calc(var(--navbar-height) + var(--test-banner-height, 0px));
}

/* The landing page draws its hero under the fixed navbar and pads itself (homepage.css), so main.scala.html gives
   its <body> this class to opt out of the navbar offset above. */
body.no-navbar-offset {
  padding-top: 0;
}

.spacer10 { height: 10px; }
.spacer20 { height: 20px; }

/*
 * Accessibility styling updates.
 */

/* A hairline sitting clear of the descenders, rather than the browser's thick baseline-hugging default. */
a {
  color: var(--color-link-200);
  text-decoration-thickness: calc(1px * var(--ui-scale, 1));
  text-underline-offset: calc(2px * var(--ui-scale, 1));
}

/* Replacing bootstrap 3 colors with bootstrap 5, which have a better color contrast. */
.btn-primary {
  background-color: var(--color-link-200);
  border: var(--color-link-200);
}


/*
 * Custom styles
 */

/* TODO when we get rid of bootstrap, can rename this to just "button". */
.button-ps {
  border: none;
  display: inline-flex;
  align-items: center;
  font-weight: 500;
  font-family: var(--font-primary);
  text-decoration: none;
  transition: background-color 0.4s ease 0s;
  cursor: pointer;
}

.button-ps:hover {
  text-decoration: none;
}

.button-ps:disabled {
  cursor: not-allowed;
  pointer-events: none;
}

/* TODO This one is used in the navbar, which we aren't scaling with --ui-scale. Need to address this another way. */
.button__static--tiny {
  font-size: 12px;
  font-style: var(--font-primary);
  padding: 3px 10px;
  border-radius: 8px;
}

.button--tiny {
  font: var(--text-caption-medium);
  padding: calc(3px * var(--ui-scale, 1)) calc(10px * var(--ui-scale, 1));
  border-radius: calc(8px * var(--ui-scale, 1));
}

.button--small {
  font: var(--text-caption-medium);
  padding: calc(6px * var(--ui-scale, 1)) calc(16px * var(--ui-scale, 1));
  border-radius: calc(10px * var(--ui-scale, 1));
}

.button--medium {
  font: var(--text-small-regular);
  padding: calc(8px * var(--ui-scale, 1)) calc(20px * var(--ui-scale, 1));
  border-radius: calc(12px * var(--ui-scale, 1));
}

.button--large {
  font-size: calc(15px * var(--ui-scale, 1));
  padding: calc(12px * var(--ui-scale, 1)) calc(36px * var(--ui-scale, 1));
  border-radius: calc(16px * var(--ui-scale, 1));
}

.button--primary {
  background-color: var(--color-banana-500);
  color: var(--color-neutral-black);
}

.button--primary:hover {
  background-color: var(--color-banana-200);
  color: var(--color-neutral-black);
}

.button--primary:focus {
  outline: 2px solid var(--color-banana-900);
  outline-offset: 2px;
  color: var(--color-neutral-black);
}

.button--primary:disabled {
  background-color: var(--color-neutral-200);
  color: var(--color-neutral-700)
}

.button--secondary {
  background-color: var(--color-neutral-white);
  color: var(--color-neutral-900);
  outline: 1px solid var(--color-neutral-800);
}

.button--secondary:hover {
  background-color: var(--color-banana-200);
  color: var(--color-neutral-900);
}

.button--secondary:focus {
  outline: 2px solid var(--color-banana-900);
  color: var(--color-neutral-900);
}

.button--secondary:disabled {
  color: var(--color-neutral-500);
  outline: 1px solid var(--color-neutral-500);
}

/* Form controls. `.ps-input` is a text-like <input> or <textarea>; `.ps-select` a native <select>, where only the
   closed control is restyled and the platform keeps its own dropdown and arrow. They share one box so a select sits
   flush beside an input in the same row. `--large` is the size for a settings-style form (body type, roomier
   padding); the default is the toolbar/filter size that matches .button--medium. */
.ps-input,
.ps-select {
  font: var(--text-small-regular);
  color: var(--color-neutral-900);
  background-color: var(--color-neutral-white);
  border: 1px solid var(--color-neutral-300);
  border-radius: var(--border-radius);
  padding: calc(6px * var(--ui-scale, 1)) calc(10px * var(--ui-scale, 1));
}

.ps-input--large,
.ps-select--large {
  font: var(--text-body-regular);
  padding: calc(9px * var(--ui-scale, 1)) calc(12px * var(--ui-scale, 1));
}

.ps-select {
  cursor: pointer;
}

textarea.ps-input {
  resize: vertical;
}

.ps-input:hover,
.ps-select:hover {
  border-color: var(--color-neutral-400);
}

.ps-input:focus-visible,
.ps-select:focus-visible {
  outline: 2px solid var(--color-link-100);
  outline-offset: 1px;
}

/* :read-only matches any non-editable control, a file input included — exempt it, or the picker would always wear
   the greyed-out fill this rule is meant to give a locked text field. */
.ps-input:disabled,
.ps-input:read-only:not([type="file"]),
.ps-select:disabled {
  background-color: var(--color-neutral-100);
  color: var(--color-neutral-600);
  cursor: default;
}

/* Data table. The API docs and both dashboards draw their tables from this; the shared sticky-header and scroller
   variants are in components/tables.css, and a page adds only what its own table needs on top. Mark a numeric cell
   with `.num` so it right-aligns with tabular figures; `.ps-table--compact` tightens the cells for dense admin data. */
.ps-table-wrapper {
  overflow-x: auto;
  margin-bottom: var(--space-lg);
  border-radius: var(--border-radius);
  box-shadow: var(--box-shadow-sm);
}

/* Inside the scroller the wrapper owns the vertical rhythm; a table margin would just push the scrollbar away from
   the content. */
.ps-table-wrapper > table {
  margin: 0;
}

.ps-table {
  width: 100%;
  border-collapse: collapse;
  border-spacing: 0;
  font: var(--text-small-regular);

  /* Cells often hold a paragraph with inline code in it; the token's 18px leading is sized for one-line UI copy. */
  line-height: 1.65;
}

.ps-table th,
.ps-table td {
  padding: var(--space-sm) var(--space-md);
  text-align: left;
  vertical-align: middle;
  border-bottom: 1px solid var(--color-neutral-200);
}

.ps-table th {
  font: var(--text-small-bold);
  line-height: 1.65;
  background-color: var(--color-neutral-100);
  border-bottom-width: 2px;
  white-space: nowrap;
}

/* A row header (<th scope="row">) is a body cell that happens to label its row, so it keeps the header's weight
   but none of its column-header chrome: the fill, the heavier rule, and the nowrap that would push a long name out
   of the table. */
.ps-table tbody th {
  background-color: transparent;
  border-bottom-width: 1px;
  white-space: normal;
}

/* A blue wash rather than neutral-100: a link in a hovered row keeps 4.5:1 on it, and drops under on the grey. Set
   on the cells, not the row, so it also paints over a row header (<th scope="row">). */
.ps-table tbody tr:hover > td,
.ps-table tbody tr:hover > th {
  background-color: var(--color-blue-200);
}

.ps-table th.num,
.ps-table td.num {
  text-align: right;
  font-variant-numeric: tabular-nums;
}

.ps-table--compact,
.ps-table--compact th {
  line-height: 1.3;
}

.ps-table--compact th,
.ps-table--compact td {
  padding: var(--space-xs) var(--space-sm);
}

/* Below var(--breakpoint-xs) a cell's padding is a large share of the width, so trade some of it for content. */
@media (width <= 480px) {
  .ps-table th,
  .ps-table td {
    padding: var(--space-xs) var(--space-sm);
  }
}

.label-icon {
  position: absolute;
  border: 1px solid var(--color-neutral-black);
  border-radius: 100%;
  outline: 0.5px solid var(--color-neutral-white);
}

.icon-outline {
  border: 1px solid var(--color-neutral-black);
  border-radius: 25px;
  outline: .5px solid var(--color-neutral-white);
}

#header {
  position: fixed;

  /* Drops below the test-server banner when present; --test-banner-height is 0 otherwise (and on prod). */
  top: var(--test-banner-height, 0);
  right: 0;
  left: 0;
  z-index: var(--navbar-z-index);
  height: var(--navbar-height);
  transition: height 0.2s ease 0s;
  background-color: var(--color-neutral-white);
  border-bottom: 1px solid #e7e7e7;
  display: flex;
}

.navbar-container {
  display: flex;
  align-items: center;
  flex: 1;
  padding: 0 4.5%;
}

.navbar-lnk {
  display: inline;
  margin-left: 8px;
  position: relative; /* Anchor each dropdown panel beneath its own trigger. */
}

#navbar {
  flex: 1;
  display: flex !important;
  align-items: center;
  justify-content: space-between; /* Primary nav left, utility (city/account/language) right. */
  gap: 4px;
}

.navbar-toggle {
  display: none; /* Shown only at the mobile breakpoint below. */
  border: none;
  background: transparent;
  cursor: pointer;
  padding: 6px 8px;
  border-radius: 8px;
  color: var(--color-asphalt-500);
}

.navbar-toggle-icon {
  display: block;
  width: 24px;
  height: 24px;
}

/* Holds the collapsed bar's icon-only links; empty and hidden until the breakpoint below fills it. */
.navbar-quick {
  display: none;
  margin: 0;
  padding: 0;
  list-style: none;
}

#language-icon {
  height: 16px;
  width: 16px;
}

.navbar-nav > li > .navbar-button {
  display: inline-flex;
  align-items: center;
  gap: 3px;

  /* A navbar item never wraps: the bar is a fixed-height single line, so a wrapped label overflows it. Items are
     shed instead when space runs short (see Navbar.js). */
  white-space: nowrap;
  cursor: pointer;
  color: var(--color-asphalt-500);
  font-size: 13px;
  font-family: Mulish, sans-serif;
  padding: 5px 10px;
  text-decoration: none;
  border: none;
  border-radius: 8px;
  background-color: transparent;
  line-height: 1.4;
  transition: color 0.2s ease, background-color 0.2s ease, border-color 0.2s ease;
}

.navbar-nav > li > .navbar-button#language-button {
  padding: 4px 12px;
}

/*
 * The bar splits into two semantic groups: left = text-link destinations, right = bordered-pill state selectors
 * (city / account / language). The styling backs that split so yellow is reserved for the active indicator and CTAs.
 */
.navbar-nav > li > .navbar-button:hover {
  color: var(--color-asphalt-700);
  background-color: var(--color-asphalt-100);
}

/* Active destination: weight + a gold underline (never color-only), paired with aria-current in the markup. */
.navbar-nav--primary > li > .navbar-button.is-active {
  position: relative;
  color: var(--color-asphalt-700);
  font-weight: 700;
}

.navbar-nav--primary > li > .navbar-button.is-active::after {
  content: "";
  position: absolute;
  left: 10px;
  right: 10px;
  bottom: 2px;
  height: 2px;
  border-radius: 2px;
  background-color: var(--color-banana-600);
}

/* Divider between the brand lockup and the primary destinations. */
.navbar-nav--primary::before {
  content: "";
  align-self: center;
  width: 1px;
  height: 22px;
  margin: 0 18px 0 28px;
  background-color: var(--color-neutral-300);
}

/* Leading icon on a top-level destination. The inline bar keeps its destinations as plain text links, so this shows
   only in the stacked hamburger panel (see the breakpoint below). */
.navbar-lnk-icon {
  display: none;
  width: 16px;
  height: 16px;
  flex: none;
}

/* Leading icon on each selector pill (city / account / language); keeps the three the same height. */
.navbar-select-icon {
  width: 16px;
  height: 16px;
  flex: none;
}

/* Right group: state selectors read as bordered pills, grouped tighter than the destinations. */
.navbar-nav--utility {
  gap: 6px;
}

.navbar-nav--utility .navbar-lnk {
  margin-left: 0;
}

.navbar-nav--utility > li > .navbar-button {
  border: 1px solid var(--color-neutral-300);
  border-radius: 99px;
  padding: 4px 12px;
}

.navbar-nav--utility > li > .navbar-button:hover {
  background-color: var(--color-asphalt-100);
  border-color: var(--color-neutral-400);
}

/* Active selector pill (a page from its menu is current): a filled brand-yellow pill clearly marks the section. */
.navbar-nav--utility > li > .navbar-button.is-active {
  background-color: var(--color-banana-300);
  border-color: var(--color-banana-300);
  color: var(--color-neutral-black);
  font-weight: 700;
}

.navbar-nav--utility > li > .navbar-button.is-active:hover {
  background-color: var(--color-banana-500);
  border-color: var(--color-banana-500);
}

.navbar-nav > li > .navbar-button:focus {
  outline: none;
}

.navbar-nav > li > .navbar-button:focus-visible,
.navbar-toggle:focus-visible,
.nav-city-search:focus-visible {
  outline: 2px solid var(--color-banana-800);
  outline-offset: 2px;
}

.caret {
  margin-left: 2px;
}

/* Taller navbar variant used on the landing page (and removed on scroll). */
#header.header--tall {
  height: 50px;
}

/* In the taller landing bar the row inherits the base .navbar-container centering, so brand + nav + divider sit
   vertically centered in the 50px bar. The divider is lengthened to suit the taller bar; the compact scrolled bar
   keeps the base 22px. */
#header.header--tall .navbar-nav--primary::before {
  height: 30px;
}

#header.header--tall #navbar-brand img {
  height: 40px;
}

#navbar-brand img {
  transition: height 0.2s ease 0s;
}

.new-city {
  font-style: italic;
}

.current-city {
  cursor: default;
}


#content {
  position: relative;
  top: 0;
  background-color: var(--color-neutral-white);
}

#wrap {
  min-height: 100%;
  height: auto;
}

/*
 * Full-window app pages (e.g. /labelMap) opt in via this class: #wrap becomes a fixed-height flex
 * column so a flex-growing content area (the map) fills the viewport below the navbar and the fixed
 * test-server banner. --test-banner-height is 0 when the banner is absent/dismissed or on prod, so this
 * reduces to 100dvh minus the navbar. No effect on other pages, which don't get the class. dvh rather than vh
 * so the map tracks the visible viewport under a mobile browser's collapsing toolbar.
 */
#wrap.wrap--full-window {
  display: flex;
  flex-direction: column;
  height: calc(100dvh - var(--navbar-height) - var(--test-banner-height, 0px));
  min-height: 0;
  overflow: hidden;
}

#footer-container {
  position: relative;
  top: 0;
  width: 100%;
  background-image: url("../images/796911a2bcb1a65a40fdc70fcca4f09b-footer.jpg");
  min-height: 200px;
  padding-top: 50px;
  padding-bottom: 50px;
  background-attachment: fixed;
  background-position: center;
  background-repeat: no-repeat;
  background-size: cover;
  display: flex;
  justify-content: center;
  color: var(--color-asphalt-500);
  font-size: 12px;
  line-height: 1.8em;
  font-family: var(--font-accent);
}

#footer-container a {
  color: var(--color-asphalt-500);
  cursor: pointer;
}

.footer-link {
  color: var(--color-asphalt-500);
}

/* Brand marks in the "Connect" links (markup in common/socialIcon.scala.html). They fill with the link's own text
   color so the set reads as one monochrome group rather than four competing brand palettes. */
.social-icon {
  width: 15px;
  height: 15px;
  vertical-align: -2px;
  fill: currentcolor;
}

#info-footer {
  position: relative;
  width: 100%;
  min-height: 220px;
  background-color: var(--color-asphalt-500);
  text-align: center;
  font-family: var(--font-accent);
  font-size: 12px;
  color: var(--color-neutral-white);
  line-height: 1.6em;
}

#application-version {
  font-family: var(--font-primary);
}

#info-footer a {
  color:var(--color-neutral-white);
  text-decoration:underline;
}

/* The three columns don't fill the 900px evenly, so centering the row alone leaves the group visibly left of centre;
   the offset is what makes the *content* read as centered. */
#footer-container .footer-links-row {
  width: 900px;
  position: relative;
  left: 80px;
}

/*
 * Below the width that fits 900px plus that 80px offset, both turn into overflow: the row is laid out past the right
 * edge, which widens the page. Every page carries this footer, so every page scrolled sideways on a phone — and since
 * the navbar is fixed to the initial containing block that the overflow widens, the navbar was laid out wider than
 * the screen too, pushing the hamburger off it (#4857).
 *
 * Bootstrap's gutters have to go with it: .row pulls itself 15px wider on each side expecting a block container to
 * absorb the difference, and #footer-container is a flex container, so that width lands outside the viewport instead.
 * The columns keep their own padding, which is what still indents the text once the row stops overhanging.
 */
@media (width <= 1060px) {
  #footer-container .footer-links-row {
    width: 100%;
    left: 0;
    margin-left: 0;
    margin-right: 0;
  }
}

#info-footer .footer-logos-row {
  max-width: 1000px;
  margin: 0 auto;
}

#google-link img {
  margin-top: 20px;
}

#sloan-link img {
  width: 200px;
  margin-top: 20px;
}

#create-link img {
  width: 160px;
  margin-top: 10px;
}

/* Pano Info Popover styling. */

/* Sizes scale with --ui-scale for pages that have it, falling back to a multiplier of 1 on other pages. */
.pano-info-popover {
  position: fixed;
  inset: unset;        /* Override the browser's default centered placement. */
  margin: 0;
  left: -9999px;       /* Off-screen until JS positions it. */
  top: -9999px;
  overflow: visible;   /* Allow the CSS arrow to extend beyond the box. */
  min-width: calc(280px * var(--ui-scale, 1));
  max-width: calc(400px * var(--ui-scale, 1));
  padding: calc(14px * var(--ui-scale, 1));
  background: var(--color-neutral-white);
  border: 1px solid var(--color-neutral-300);
  border-radius: calc(12px * var(--ui-scale, 1));
  box-shadow: 0 6px 24px rgb(0 0 0 / 15%);
}

/* Downward-pointing arrow: ::before is the border, ::after is the white fill on top. */
.pano-info-popover::before,
.pano-info-popover::after {
  content: "";
  position: absolute;
  left: 50%;
  transform: translateX(-50%);
  width: 0;
  height: 0;
  border-style: solid;
}

.pano-info-popover::before {
  bottom: calc(-10px * var(--ui-scale, 1));
  border-width: calc(10px * var(--ui-scale, 1)) calc(9px * var(--ui-scale, 1)) 0;
  border-color: var(--color-neutral-300) transparent transparent;
}

.pano-info-popover::after {
  bottom: calc(-9px * var(--ui-scale, 1));
  border-width: calc(9px * var(--ui-scale, 1)) calc(8px * var(--ui-scale, 1)) 0;
  border-color: var(--color-neutral-white) transparent transparent;
}

.pano-info-popover__header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: calc(10px * var(--ui-scale, 1));
}

/* Matches the label-detail card's section eyebrows. */
.pano-info-popover__title {
  font: var(--text-caption-semibold);
  color: var(--color-neutral-700);
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

.pano-info-popover__actions {
  display: flex;
  align-items: center;
  gap: calc(2px * var(--ui-scale, 1));
}

.pano-info-popover__clipboard,
.pano-info-popover__close {
  background: none;
  border: none;
  padding: calc(3px * var(--ui-scale, 1));
  border-radius: calc(4px * var(--ui-scale, 1));
  cursor: pointer;
  line-height: 0;
  opacity: 0.75;
}

.pano-info-popover__clipboard:hover,
.pano-info-popover__close:hover {
  background: rgb(0 0 0 / 6%);
  opacity: 1;
}

.pano-info-popover__clipboard:active,
.pano-info-popover__close:active {
  opacity: 0.5;
}

.pano-info-popover__clipboard:focus-visible,
.pano-info-popover__close:focus-visible {
  outline: 2px solid var(--color-link-100);
  outline-offset: 1px;
}

.pano-info-popover__clipboard img,
.pano-info-popover__close img {
  width: calc(16px * var(--ui-scale, 1));
  height: calc(16px * var(--ui-scale, 1));
}

/* Shown briefly after clipboard copy. Absolutely positioned so it doesn't shift the body rows. */
.pano-info-popover__copied:not([hidden]) {
  display: block;
  position: absolute;
  top: calc(44px * var(--ui-scale, 1));   /* below the header (12px padding-top + ~30px header + 2px gap) */
  right: calc(8px * var(--ui-scale, 1));
  background: rgb(40 40 40 / 85%);
  color: var(--color-neutral-white);
  font-size: calc(11px * var(--ui-scale, 1));
  padding: calc(3px * var(--ui-scale, 1)) calc(8px * var(--ui-scale, 1));
  border-radius: calc(4px * var(--ui-scale, 1));
  white-space: nowrap;
  pointer-events: none;
  z-index: 1;
}

/* Spec-sheet body: muted labels left, semibold values right. */
.pano-info-popover__body {
  display: flex;
  flex-direction: column;
  gap: calc(6px * var(--ui-scale, 1));
  margin: 0;
}

.pano-info-popover__row {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: calc(12px * var(--ui-scale, 1));
}

.pano-info-popover__row dt {
  flex-shrink: 0;
  font: var(--text-caption-medium);
  color: var(--color-neutral-700);
}

.pano-info-popover__row--hidden {
  display: none;
}

/* overflow-wrap keeps long pano IDs inside the popover; tabular numerals hold the id/coordinate columns steady. */
.pano-info-popover__val {
  margin: 0;
  min-width: 0;
  overflow-wrap: anywhere;
  text-align: right;
  font: var(--text-caption-semibold);
  color: var(--color-neutral-900);
  font-variant-numeric: tabular-nums;
}

.pano-info-popover__view-link {
  display: block;
  margin-top: calc(10px * var(--ui-scale, 1));
  padding-top: calc(10px * var(--ui-scale, 1));
  border-top: 1px solid var(--color-neutral-200);
  text-align: center;
  font: var(--text-caption-semibold);
  color: var(--color-link-200);
  text-decoration: none;
}

.pano-info-popover__view-link::after {
  content: " ↗";
}

.pano-info-popover__view-link:hover {
  text-decoration: underline;
}

.pano-info-popover__view-link:focus-visible {
  outline: 2px solid var(--color-link-100);
  outline-offset: 2px;
}

/* Sized as a multiple of its own column because the award text is far wider than the narrow .col-sm-1 it sits under,
   and pulled back left by the matching offset so it stays under the NSF logo. */
#award-num {
  font-size:10px;
  color:#c0becf;
  width: 220%;
  right: 35%;
}

/*
 * Below Bootstrap's sm breakpoint the columns stop floating and stack full-width, so a percentage picked for a narrow
 * column becomes multiples of the viewport — 220% of 737px is 1621px of page, and the fixed navbar is sized from the
 * containing block that widens with it (#4857). Keyed on `width` because the viewport is what the columns lay out
 * against: `device-width` is the physical screen, which ignores a resized desktop window entirely and reports the
 * wrong dimension on a rotated tablet.
 */
@media (width <= 767px) {
  #award-num {
    width: 100%;
    right: 0;
  }
}

#funding-title {
  font-weight: bold;
}


kbd {
  /* overriding bootstraps's ugly kbd tag */
  background-color: var(--color-neutral-white);
  box-shadow: 0 1px 0 rgb(0 0 0 / 20%);
  border: 1px solid rgb(0 0 0 / 20%);
  border-radius: 3px;
  color: #444444;
  font-weight: normal;
  font-size: 0.9em; /* Scale with surrounding text rather than a fixed size. */
  padding: 2px 6px;
  vertical-align: middle; /* Center the pill on the text line instead of hanging it off the baseline. */
}

/* The site's one loading spinner, so the pages that render it don't each arrive at their own size and color. */
.loading-spinner {
  width: 40px;
  height: 40px;
  border: 4px solid var(--color-neutral-100);
  border-top: 4px solid var(--color-pine-500);
  border-radius: 50%;
  animation: spin 1s linear infinite;
}

@keyframes spin {
  0% { transform: rotate(0deg); }
  100% { transform: rotate(360deg); }
}

#page-loading {
  position:absolute;
  z-index: 1;
  max-width:45%;
  max-height:45%;
  top:33%;
  left:50%;
  overflow:visible;
}

#loading-animation {
  position: relative;
  width: 200px;
  height: 200px;
  margin-top: -50%;
  margin-left: -50%;
}

#loading-animation img {
  display: block;
  margin: 0 auto;
  width: 200px;
  height: 200px;
}

.loading-text {
  font-family: raleway, sans-serif;
  text-align: center;
  font-size: 45px;
  color: var(--color-asphalt-500);
}

.loading-sub-text {
  font-family: raleway, sans-serif;
  text-align: center;
  font-size: 15px;
  color: var(--color-asphalt-500);
  margin-top: 5px;
}

/*
 * CSS for the footer header
*/
.footer-header {
  font-family: var(--font-accent);
  font-weight: 800;
  color: var(--color-asphalt-500);
  font-size: 12px;
  line-height: 2.1em;
  letter-spacing: 0.05em;
}


/*
 * Test server warning banner: a soft amber notice shown only on non-prod servers. Colors, type, and spacing come from
 * the Design System Tokens (banana/neutral/link palettes) rather than the old hardcoded yellow + red "WARNING!" text.
 */
.test-server-banner {
  /*
   * Fixed bar pinned to the very top, above the navbar. It reserves its own space via --test-banner-height
   * (published by the inline script in common/main.scala.html) instead of covering content, and staying above
   * the navbar keeps it clear of the navbar's height animation on the landing page.
   */
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: var(--navbar-z-index);
  background-color: var(--color-banana-200);
  border-bottom: 1px solid var(--color-banana-500);
}

.test-server-banner-content {
  display: flex;
  align-items: center;
  gap: 12px;

  /* Cap tuned to be just wide enough for the notice + a long deployment URL on one row, so the content still shows
     centered margins on typical laptops. It wraps gracefully on narrower viewports. */
  max-width: 1320px;
  margin: 0 auto;
  padding: 10px 20px;
  flex-wrap: wrap;
}

/* Warning triangle: the severity cue, so the message text can stay calm and informative. */
.test-server-banner-icon {
  width: 20px;
  height: 20px;
  color: var(--color-banana-800);
  -webkit-mask-image: url("../images/icons/9a36ef849863d9d1e18934ad2f0f2778-alert-triangle-feather.svg");
  mask-image: url("../images/icons/9a36ef849863d9d1e18934ad2f0f2778-alert-triangle-feather.svg");
}

.test-server-banner-text {
  flex: 1 1 240px;
  min-width: 0;
  margin: 0;
  font: var(--text-small-regular);
  color: var(--color-neutral-800);
}

.test-server-banner-text > strong {
  font-weight: 700;
  color: var(--color-neutral-900);
}

.test-server-banner-link {
  font-weight: 600;
  color: var(--color-link-200);
  text-decoration: underline;
  overflow-wrap: anywhere;
}

.test-server-banner-actions {
  display: flex;
  align-items: center;
  gap: 4px;
  margin-left: auto;
}

/* Secondary action styled as an outlined amber pill; the amber border ties it to the icon and band. */
.test-server-banner-dont-show-again {
  padding: 5px 14px;
  border: 1px solid var(--color-banana-800);
  border-radius: 999px;
  background: transparent;
  font: var(--text-small-medium);
  color: var(--color-neutral-900);
  white-space: nowrap;
  cursor: pointer;
  transition: background-color 0.15s ease, border-color 0.15s ease, color 0.15s ease;
}

.test-server-banner-dont-show-again:hover {
  background-color: var(--color-banana-300);
  border-color: var(--color-banana-900);
}

.test-server-banner-close {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 auto;
  width: 28px;
  height: 28px;
  border: none;
  border-radius: 50%;
  background: transparent;
  color: var(--color-neutral-700);
  cursor: pointer;
  transition: background-color 0.15s ease, color 0.15s ease;
}

.test-server-banner-close svg {
  width: 16px;
  height: 16px;
}

.test-server-banner-close:hover {
  background-color: var(--color-banana-300);
  color: var(--color-neutral-900);
}

.test-server-banner :focus-visible {
  outline: 2px solid var(--color-link-200);
  outline-offset: 2px;
}

@media (prefers-reduced-motion: reduce) {
  .test-server-banner-dont-show-again,
  .test-server-banner-close {
    transition: none;
  }
}

/*
 * Test server warning banner on narrow screens. mobile-validate.css only repositions the banner on /mobile — under
 * that tool's own header band rather than over it — so a phone held in portrait gets this stacked layout there too.
 * Landscape is wider than the query, and keeps the one-row layout at that same offset.
 */
@media only screen and (width <= 600px) {
  /* Top-align so the icon sits on the first line once the message wraps to several lines. */
  .test-server-banner-content {
    align-items: flex-start;
    gap: 10px;
    padding: 12px 14px;
  }

  /* Optical nudge to line the triangle up with the first line's cap height. */
  .test-server-banner-icon {
    margin-top: 1px;
  }

  /*
   * Actions drop to their own full-width row: indent the pill to the message's text column (icon width + row gap)
   * so it lines up under the text rather than the icon, while the close stays anchored at the right edge.
   */
  .test-server-banner-actions {
    box-sizing: border-box;
    width: 100%;
    padding-inline-start: 30px;
    justify-content: space-between;
  }

  /* Roomier tap target for touch. */
  .test-server-banner-close {
    width: 32px;
    height: 32px;
  }
}


.navbar-nav {
  display: flex;
  align-items: center;
  list-style: none;
  margin: 0;
  padding: 0;

  /* Neither group compresses: squeezing is what forced labels onto a second line. When the two groups no longer fit
     side by side, Navbar.js sheds items instead. */
  flex: none;
}

.navbar-nav > li {
  flex: none;
}

/* Applied by Navbar.js to items dropped from the inline bar; each one stays reachable elsewhere (site footer, or
   the Tools menu for Label Map), and all of them return in the stacked hamburger panel. */
.navbar-nav > li.is-shed {
  display: none;
}

/* Last step before the hamburger: the language selector keeps its globe icon and drops its label. The button's
   aria-label still names the control, so this is visual only. */
#language-dropdown.is-compact .navbar-lang-label {
  display: none;
}

/* A long username can't be allowed to push the bar past its width. */
.navbar-user-name {
  display: block;
  max-width: 14ch;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Dropdown panels are hidden until Navbar.js adds .is-open to the wrapping .navbar-lnk. */
.navbar-lnk > .dropdown-menu {
  display: none;
}

.navbar-lnk.is-open > .dropdown-menu {
  position: absolute;
  top: 100%;
  right: 0;
  left: auto;
  z-index: 1000;
  display: block;
  min-width: 180px;
  padding: 6px 0;
  margin: 2px 0 0;
  font-size: 13px;
  list-style: none;
  background-color: var(--color-neutral-white);
  background-clip: padding-box;
  border: 1px solid rgb(0 0 0 / 15%);
  border-radius: 8px;
  box-shadow: 0 6px 16px rgb(0 0 0 / 17.5%);
}

.dropdown-menu li {
  list-style: none;
}

.dropdown-menu li > a {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 5px 16px;
  font-size: 13px;
  color: var(--color-asphalt-600);
  text-decoration: none;
  white-space: nowrap;
}

/* Leading icon on a menu item; sized to the 13px item text, never shrinking as labels get long. */
.dropdown-icon {
  width: 16px;
  height: 16px;
  flex: none;
  opacity: 0.75;
}

.dropdown-menu li > a:hover .dropdown-icon,
.dropdown-menu li > a:focus .dropdown-icon,
.dropdown-menu li > a.is-active .dropdown-icon {
  opacity: 1;
}

/* Rule setting Sign out apart from the destinations above it. */
.dropdown-divider {
  height: 1px;
  margin: 6px 0;
  background-color: var(--color-neutral-200);
}

.dropdown-menu li > a:hover,
.dropdown-menu li > a:focus {
  background-color: #FCECBE;
  color: var(--color-asphalt-700);
  outline: none;
}

.dropdown-menu li > a:focus-visible {
  outline: 2px solid var(--color-banana-800);
  outline-offset: -2px;
}

/* Active menu item (its page is current): weight + a gold rail, echoing the top-level underline. */
.dropdown-menu li > a.is-active {
  font-weight: 700;
  color: var(--color-asphalt-700);
  box-shadow: inset 3px 0 0 var(--color-banana-600);
}

/* Non-interactive section labels grouping a menu's items. */
.dropdown-section {
  padding: 12px 16px 2px;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--color-asphalt-400);
  pointer-events: none;
}

.dropdown-section:first-child {
  padding-top: 2px;
}

/* Searchable city switcher panel. */
.nav-city-panel {
  width: 260px;
}

.navbar-lnk.is-open > .dropdown-menu.nav-city-panel {
  padding: 0;
  overflow: hidden;
}

.nav-city-search-wrap {
  padding: 8px;
  border-bottom: 1px solid var(--color-neutral-200);
}

.nav-city-search {
  width: 100%;
  box-sizing: border-box;
  padding: 6px 10px;
  font-size: 13px;
  border: 1px solid var(--color-neutral-300);
  border-radius: 8px;
}

.nav-city-list {
  list-style: none;
  margin: 0;
  padding: 4px 0;
  max-height: 320px;
  overflow-y: auto;
}

.nav-city-group-list {
  list-style: none;
  margin: 0;
  padding: 0;
}

.nav-city-group-header {
  display: block;
  padding: 8px 16px 2px;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--color-asphalt-400);
}

.nav-city-item > a {
  padding-left: 20px;
}

.nav-city-current {
  padding: 6px 16px;
}

.nav-city-current .current-city {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 13px;
  font-weight: 700;
  color: var(--color-neutral-900);
}

.nav-city-check {
  width: 16px;
  height: 16px;
  flex: none;
}

.nav-city-empty {
  padding: 10px 16px;
  font-size: 13px;
  color: var(--color-asphalt-400);
}

.nav-city-footer {
  border-top: 1px solid var(--color-neutral-200);
  padding: 4px 0;
}

.nav-city-footer a {
  display: block;
  padding: 6px 16px;
  font-size: 12px;
  color: var(--color-asphalt-500);
  text-decoration: none;
}

.nav-city-footer a:hover {
  background-color: #FCECBE;
}

.tool-ui {
  position: relative;
}

.dropdown-toggle {
  margin-top: 0;
}

/* Overlay box - start */
#area-completion-overlay-wrapper, #already-completed-region-overlay {
  display:none;
}

.overlay-text {
  height: 100%;
  width: 100%;
  position: fixed;
  top: 0;
  left: 0;
  background: rgb(0 0 0 / 89%);
  z-index: 5;
}

#area-completion-overlay, #already-completed-region-text {
  color: var(--color-neutral-white);
  text-align: center;
  width: 70%;
  position: relative;
  margin: auto;
  border-radius: 5px;
  font-size: 21px;
  top: 28%;
}

#area-completion-overlay p, #already-completed-region-text p {
  border-radius: 5px;
  padding: 7px;
}

.overlay-header {
  text-align: center;
  font-size: 30px;
  font-weight: bold;
}

.overlay-option {
  margin: 20px 5px;
  padding: 7px;
  border: 1px solid var(--color-neutral-white);
  border-radius: 5px;
  display: inline-block;
}

.overlay-option:hover {
  background-color: var(--color-neutral-white);
  color: var(--color-neutral-black);
  cursor: pointer;
}

#accept-redirect {
  width: 75px;
}

/* Overlay box - end */

.anchor {
  display: block;
  position: relative;
  top: calc(-1 * var(--navbar-height) - 15px);
  visibility: hidden;
}

.accuracy-tooltip {
  display:none;
  position:absolute;
  z-index:100;
  border:1px;
  border-radius: 5px;
  background-color:#eeeeee;
  border-style:solid;
  border-color: var(--color-neutral-black);
  padding:3px;
  color: var(--color-neutral-black);
  left:20px;
  font-size: 14px;
  font-weight: normal;
  width: 175px;
}

#your-overall-total {
  border-top: 3px solid #999999;
}

#validation-button-holder {
  display: flex;
  justify-content: space-between;
}

#validation-comment-holder {
  padding-top: 5px;
  padding-bottom: 5px;
}

#comment-textarea {
  height: 33px;
  width: 388px;
  background-color: #F5F5F5;
  color: var(--color-neutral-black);
  border-radius: 5px;
  padding: 5px;
  line-height: normal;
  border-width: 2px;
  border-color: grey;
  resize: none;
  outline: none;
  vertical-align: middle;
  margin-right: 2px;
}

/* Contexts that need a different size (e.g. the label card's Details strip) set the custom property on an
   ancestor instead of fighting this id selector's specificity. */
#pano-info-button {
  cursor: pointer;
  width: var(--pano-info-button-size, 24px);
  height: var(--pano-info-button-size, auto);
}

/* Beacon marking the label tied to the /labelMap popup. A reticle whose center carries the label type's
   canonical color (set inline from getLabelColors()), so the label reads even when the map's own dot is tiny;
   the expanding ::after ring is what draws the eye on a busy map. Both modes pulse exactly once: 'spotlight'
   pulses on landing then stays as a calm reticle while the popup is open, and 'flash' pulses once on close
   to re-anchor the user (its marker is then removed). */
.labelmap-beacon {
  position: relative;
  width: 26px;
  height: 26px;
  border-radius: 50%;
  border: 3px solid var(--color-asphalt-600);
  box-shadow: 0 0 0 2px rgb(255 255 255 / 90%), 0 1px 4px rgb(0 0 0 / 40%);
  pointer-events: none;
}

/* Center fill in the label type's color (background set inline by the /labelMap script). */
.labelmap-beacon__dot {
  position: absolute;
  inset: 3px;
  border-radius: 50%;
  border: 1.5px solid rgb(255 255 255 / 90%);
}

.labelmap-beacon::after {
  content: "";
  position: absolute;
  inset: -3px;
  border-radius: 50%;
  border: 3px solid var(--color-asphalt-600);
}

.labelmap-beacon--spotlight::after {
  animation: labelmap-beacon-ring 1.5s ease-out forwards;
}

.labelmap-beacon--flash::after {
  animation: labelmap-beacon-ring 1.3s ease-out 1;
}

@keyframes labelmap-beacon-ring {
  from {
    transform: scale(1);
    opacity: 0.85;
  }

  to {
    transform: scale(2.4);
    opacity: 0;
  }
}

@media (prefers-reduced-motion: reduce) {
  .labelmap-beacon::after { animation: none; }
}

/* A label marker on a panorama (PanoMarker). The type icon is drawn by ::before from --label-icon rather than as
   the element's own background, so hiding the label can fade the icon independently of the ring around it. */
.label-marker {
  transition: border-color 0.5s ease, outline-color 0.5s ease, outline-width 0.5s ease;
}

.label-marker::before {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: 50%;
  background-image: var(--label-icon);
  background-size: 100% 100%;
  transition: opacity 0.5s ease;
}

/* Hidden state, from the Hide-label toggle on Validate and on the label detail card (#2477). */
.label-marker--hidden::before {
  opacity: 0;
}

.label-marker--hidden {
  border-color: transparent;
  outline: calc(2px * var(--ui-scale, 1)) dashed rgb(from var(--label-color, #ffffff) r g b / 70%);
  outline-offset: calc(1px * var(--ui-scale, 1));
}

/* The eye on every Hide-label button (LabelVisibilityToggle), sized to sit with the type beside it. */
.hide-label-button-icon {
  flex-shrink: 0;
  width: calc(14px * var(--ui-scale, 1));
  height: calc(14px * var(--ui-scale, 1));
}

/* One-shot halo drawing the eye to a label marker on a panorama: Validate replays it on every new label (#4790),
   the Label Detail Card plays it when the card opens (#4572). Both markers are small and easy to lose against a
   busy streetscape.

   The spread is a fraction of the marker's own diameter, which PanoMarker publishes as --marker-diameter, because
   the same marker is drawn at very different sizes — 22px on desktop Validate but 52px on mobile, where a fixed
   spread would leave the halo a thin fringe on the surface that can least afford one. That property is already in
   ui-scaled pixels, so the halo needs no --ui-scale of its own.

   The dark shadow carries the same spread as the white one but blurred, so it reads only as a soft edge just
   outside the halo rather than as a second ring. Without it the halo disappears over concrete and sky, which is
   most of what a marker sits on. */
@keyframes label-marker-pulse {
  from {
    box-shadow: 0 0 0 0 rgb(255 255 255 / 90%), 0 0 var(--marker-pulse-blur) 0 rgb(0 0 0 / 50%);
  }

  to {
    box-shadow:
      0 0 0 var(--marker-pulse-spread) rgb(255 255 255 / 0%),
      0 0 var(--marker-pulse-blur) var(--marker-pulse-spread) rgb(0 0 0 / 0%);
  }
}

.label-marker-pulse {
  --marker-pulse-spread: calc(var(--marker-diameter, 22px) * 0.8);
  --marker-pulse-blur: calc(var(--marker-diameter, 22px) * 0.18);

  animation: label-marker-pulse 1.4s ease-out;
}

@media (prefers-reduced-motion: reduce) {
  .label-marker-pulse { animation: none; }
}

/* Chat-bubble tail tying the /labelMap popup dialog to the spotlighted label's beacon: a full-viewport fixed
   SVG whose one polygon the page script repositions on every map/dialog change. Sits below the dialog's top
   layer (so the light backdrop dims it slightly) but above the map and its controls. */
.labelmap-tail {
  position: fixed;
  inset: 0;
  display: none;
  width: 100%;
  height: 100%;
  pointer-events: none;
  z-index: 30;
}

.labelmap-tail polygon {
  fill: var(--color-neutral-white);
  stroke: rgb(0 0 0 / 12%);
  stroke-width: 1;
  filter: drop-shadow(0 2px 5px rgb(0 0 0 / 18%));
}


/*
* Responsive navigation.
*
* Shedding lower-priority items as the window narrows is measured at runtime in Navbar.js rather than pinned to
* breakpoints here, because the widths that matter depend on the active language's label lengths and on the
* signed-in username. Only the collapse-to-hamburger step below is a fixed breakpoint.
*/

/*
* Hamburger menu: at the narrowest widths the whole nav collapses into a single stacked menu opened by the
* hamburger. The menu box lives on #navbar so both nav groups render inside one shared panel.
*/
@media only screen and (width <= 860px) {
  .navbar-brand-area {
    display: flex;
    align-items: center;
    width: 100%;
    min-height: var(--navbar-height);
  }

  /* None of the three may shrink: Navbar.js decides how many quick links fit by comparing their summed widths against
     the row, and a shrunk child would always measure as fitting. */
  .navbar-logo,
  .navbar-quick,
  .navbar-toggle {
    flex: none;
  }

  .navbar-toggle {
    display: block;
    margin: 0;
    padding: 6px 10px;
  }

  .navbar-toggle:hover,
  .navbar-toggle:focus {
    background-color: #dddddd;
  }

  .navbar-container {
    position: relative;
  }

  .navbar-container > .navbar-brand-area {
    margin-left: 0;
  }

  /* Wide enough to read as the page's whole navigation rather than a 220px card hanging off one corner, but capped
     so it stays under the hamburger that opened it instead of stretching across a tablet. On a phone the cap is
     wider than the screen, so it goes full-bleed. */
  #navbar {
    position: absolute;
    top: var(--navbar-height);
    right: 0;
    left: auto;
    width: 100%;
    max-width: 420px;
    padding: 6px 0;
    z-index: var(--navbar-z-index);
    background-color: var(--color-neutral-white);
    border-bottom: 1px solid var(--color-neutral-300);
    border-radius: 0 0 8px 8px;
    box-shadow: 0 6px 16px rgb(0 0 0 / 17.5%);
  }

  #header.header--tall #navbar {
    top: 50px;
  }

  #navbar:not(.is-open) {
    display: none !important;
  }

  #navbar.is-open {
    display: block !important;
    overflow: hidden !important;
  }

  /* Scoped to the panel: the quick strip is a .navbar-nav too, and it stays a horizontal row in the bar. */
  #navbar .navbar-nav {
    display: block !important;
    width: 100%;
    margin: 0;
    padding: 0;
    background: transparent;
    box-shadow: none;
    border-radius: 0;
  }

  .navbar-nav > li {
    float: none;
  }

  .navbar-lnk {
    display: block;
    margin-left: 0;
  }

  .navbar-nav > li > .navbar-button {
    border-radius: 0;
    border: none;
    outline: none;
    width: 100%;
    display: flex;
    justify-content: flex-start;

    /* One padding and one gap for every row — including the city/language pills, which carry different values in the
       inline bar — so the icons form a single column and the labels start on a single edge. */
    padding: 6px 16px;
    gap: 10px;

    /* This vertical menu grows downward, so drop the bar's nowrap: a long translated label should wrap onto a second
       line rather than be clipped by the panel's overflow: hidden. */
    white-space: normal;
  }

  /* Carries its own padding in the inline bar, at a higher specificity than the rule above. */
  .navbar-nav > li > .navbar-button#language-button {
    padding: 6px 16px;
  }

  /* Destination icons are the stacked menu's reason for existing: with them the panel reads as one icon column
     top to bottom, matching the selector pills that show theirs at every width. */
  .navbar-lnk-icon {
    display: block;
  }

  .navbar-nav--primary::before {
    display: none; /* No brand divider in the stacked menu. */
  }

  .navbar-lnk.is-open > .dropdown-menu {
    position: relative;
    border: none;
    border-radius: 0;
    width: 100%;
    box-shadow: none;
  }

  .navbar-lnk.is-open > .dropdown-menu.nav-city-panel {
    width: 100%;
  }

  /*
   * Quick strip: the destinations worth one tap, shown as icons between the logo and the hamburger. Navbar.js moves
   * as many `data-nav-quick` items here as fit and leaves the rest in the panel, so this styles whichever it picked.
   */
  .navbar-quick {
    display: flex;
    align-items: center;
    gap: 2px;
    margin: 0 4px 0 auto; /* auto pushes the strip and the hamburger to the trailing edge. */
    padding: 0;
    list-style: none;
  }

  .navbar-quick > .navbar-lnk {
    display: block;
    margin-left: 0;
  }

  /*
   * Destinations keep their labels here: an icon alone is only reliable for glyphs people have already learned, and
   * "About" and "API" aren't among them. Labelling costs width, which is why Navbar.js measures — fewer items reach
   * the bar on a small phone, and the rest stay one tap away in the panel.
   */
  .navbar-nav.navbar-quick > li > .navbar-button {
    display: flex;
    align-items: center;
    justify-content: center;
    width: auto;
    min-width: 40px;
    height: 36px;
    padding: 0 10px;
    gap: 4px;
    border: none;
    border-radius: 8px;

    /* The panel lets labels wrap onto a second line; in this single-line bar that would break the row. */
    white-space: nowrap;
  }

  .navbar-quick .navbar-lnk-icon,
  .navbar-quick .navbar-select-icon {
    display: block;
    width: 18px;
    height: 18px;
  }

  /*
   * The signed-in account control is the exception, and only because the person glyph is one of the learned ones. A
   * username is also the longest, least predictable label in the bar — showing it would crowd out the destinations on
   * a phone, and the button's aria-label still names it.
   *
   * Scoped to that one button rather than the <li>, which also wraps the signed-out sign-in link: a log-in arrow is
   * not a learned glyph, "Sign in" is short and predictable, and the label is that link's whole accessible name. It
   * keeps its text and the shared pill padding above; Navbar.js measures, so the wider pill just means one fewer
   * destination fits.
   */
  #navbar-quick #nav-user-dropdown {
    min-width: 40px;
    padding: 0;
  }

  .navbar-quick .navbar-user-name {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
    border: 0;
  }

  .navbar-quick .caret {
    display: none;
  }

  /* Current page: a filled pill, since the underline the inline bar uses has no room under a 20px icon. */
  .navbar-nav.navbar-quick > li > .navbar-button.is-active {
    background-color: var(--color-banana-300);
    color: var(--color-neutral-black);
  }

  /* The panel's rules above flatten dropdowns into the stacked list; a strip dropdown is a real popover again,
     anchored to its icon and kept inside the viewport. */
  .navbar-quick .navbar-lnk.is-open > .dropdown-menu {
    position: absolute;
    width: max-content;
    max-width: min(320px, calc(100vw - 24px));
    border: 1px solid rgb(0 0 0 / 15%);
    border-radius: 8px;
    box-shadow: 0 6px 16px rgb(0 0 0 / 17.5%);
  }
}

/* Common class used to hide template elements. */
.template {
  display: none !important;
}

/* Permalink styling and behavior. */
.permalink-anchor {
  scroll-margin-top: calc(var(--navbar-height) + 15px);
}

.permalink-icon {
  height: 18px;
  opacity: 0;
  transition: opacity 200ms ease-in-out;
}

.permalink-anchor:hover .permalink-icon {
  opacity: 1;
}

.ai-icon-marker {
  position: absolute;
  width: calc(14px * var(--ui-scale, 1));
  height: calc(14px * var(--ui-scale, 1));
  top: calc(-6px * var(--ui-scale, 1));
  left: calc(-6px * var(--ui-scale, 1));
  pointer-events: auto;
  cursor: pointer;
  z-index: 3;
}

/* Validate's badge is display-only (#5359): it sits on the marker whose hover opens the label card, and that card
   carries the AI disclaimer, so the badge must not intercept the pointer — its own tooltip and cursor would fire on top
   of the card. With no pointer events it can't be the target of the delegated .ai-icon-marker hover handlers either
   (aiLabelIndicator.js). */
.ai-icon-marker-validate {
  pointer-events: none;
}

/* LabelMap / LabelView section */
#ai-validation-header {
  position: relative;
}

.label-view-ai-icon {
  position: absolute;
  top: 0;
  height: 60%;
  z-index: 3;
}

.ai-tooltip .tooltip-inner {
  max-width: calc(220px * var(--ui-scale, 1));
}

.tooltip.ai-tooltip {
  /* Keep AI tooltips above map/canvas overlays without unnecessarily high stacking */
  z-index: 1160;
}

/* Severity face button — a smiley icon + label pair used across the site. Per-context CSS still owns the container
   grid/flex layout that arranges multiple buttons. */
.severity-button {
  position: relative; /* contains the absolutely positioned hidden radio input */
  display: inline-flex;
  flex-direction: column;
  align-items: center;
  gap: calc(5px * var(--ui-scale, 1));
  padding: calc(4px * var(--ui-scale, 1)) calc(6px * var(--ui-scale, 1));
  margin: 0;
  border-radius: 6px;
  background: none;
  border: none;
  cursor: pointer;
}

.severity-button:hover {
  background: var(--color-neutral-100);
}

/* Non-interactive variant used by LabelDetail. */
.severity-button--static {
  cursor: default;
  padding: 0;
}

.severity-button--static:hover {
  background: none;
}

/* Visually hidden radio input wrapped by the label-button (Explore, ExpertValidate). The wrapping
   label still toggles it natively, and it stays in the accessibility tree. */
.severity-button__radio {
  position: absolute;
  opacity: 0;
  width: 0;
  height: 0;
  pointer-events: none;
}

/* The ring has to go on the wrapping label: the radio is what takes focus, but it has no size and paints nothing,
   so a ring on the radio itself is a ring around a 0x0 box. Without this the group is fully keyboard-operable —
   Tab reaches it, arrows rove it, per the radio-group pattern — with nothing on screen to say focus is there.
   Beside the primitive rather than in a page stylesheet: Explore had this rule and Expert Validate, which uses the
   same markup, silently did not. */
.severity-button:has(.severity-button__radio:focus-visible) {
  outline: calc(2px * var(--ui-scale, 1)) solid var(--color-link-100);
  outline-offset: calc(-2px * var(--ui-scale, 1));
}

.severity-button__icon {
  width: calc(24px * var(--ui-scale, 1));
  height: calc(24px * var(--ui-scale, 1));
  display: block;
}

.severity-button__label {
  font: var(--text-caption-medium);
  text-align: center;
  white-space: nowrap;
}

/* ---------- Shared tooltip (psTooltip.js) ---------- */

/* The single fixed-position card that psTooltip.js shows for the active [data-ps-tooltip] trigger. Parked off-screen
   until positioned; pointer-events stays off so the card never steals the hover that opened it. Sits above the navbar
   and Bootstrap modals (~1050) since a tooltip is always the most transient thing on screen.

   Dark fill, not a white card: a tooltip is a transient aside over whatever it explains, and the panels it opens over
   (the label card, the context menu, the pano overlays) are themselves white cards — a fourth white surface stacked on
   those reads as another panel rather than as a passing note. Specifically asphalt-500, the same surface the dark
   toast uses (common/toast.css), because these two are the same idea at two sizes: a note laid over the tool rather
   than a piece of it. It also keeps the tooltip clear of the asphalt-400 pano-overlay buttons it most often opens
   above, which a neutral dark gray sat too close to. 13.9:1 white-on-asphalt-500, comfortably past AAA. */
.ps-tooltip {
  position: fixed;
  left: -9999px;
  top: -9999px;
  max-width: calc(280px * var(--ui-scale, 1));
  padding: calc(5px * var(--ui-scale, 1)) calc(9px * var(--ui-scale, 1));
  background: var(--color-asphalt-500);
  border-radius: calc(6px * var(--ui-scale, 1));
  box-shadow: 0 2px 8px rgb(0 0 0 / 20%);
  font: var(--text-caption-regular);

  /* Tighter than the token's 18px leading, which is set for running body copy: a tooltip is a line or two of aside,
     and at that length the token's spacing opens the card up more than the text inside it warrants. */
  line-height: calc(15px * var(--ui-scale, 1));
  color: var(--color-neutral-white);
  pointer-events: none;
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.12s ease;
  z-index: calc(var(--navbar-z-index) + 70);
}

/* Tail aimed at the trigger. Its x comes from the JS rather than a flat 50%, because the card is clamped to the
   viewport and so does not stay centered on its trigger near a screen edge. One clip-path triangle here, not the
   label panel's two stacked layers (css/components/label-anchored-panel.css): the fill is solid and borderless, so
   there is no hairline to carry along the slanted edges and nothing to seam. */
.ps-tooltip::after {
  content: "";
  position: absolute;
  left: var(--ps-tooltip-tail-left, 50%);
  top: 100%;
  width: calc(12px * var(--ui-scale, 1));
  height: calc(6px * var(--ui-scale, 1));
  margin-left: calc(-6px * var(--ui-scale, 1));
  background: var(--color-asphalt-500);
  clip-path: polygon(0 0, 100% 0, 50% 100%);
}

/* Flipped: there was no room above, so the card sits below the trigger and the tail moves to its top edge. */
.ps-tooltip--flipped::after {
  top: auto;
  bottom: 100%;
  clip-path: polygon(0 100%, 100% 100%, 50% 0);
}

/* A card too tall to fit on either side of its trigger gets clamped into the viewport, which leaves its edges away
   from the trigger — a tail would point at nothing, so there is none. */
.ps-tooltip--untailed::after {
  display: none;
}

.ps-tooltip--visible {
  opacity: 1;
  visibility: visible;
}
