/* Panels anchored to a label icon in the pano: the read-only hover card and the editable context menu it opens
   into. They are two states of one panel, so they share this shell — a white card with a neutral hairline, an 8px
   radius, and a tail aimed at the label icon — and are positioned by the same routine (util.anchorPanelToLabel),
   which sets --panel-tail-top and the --flipped modifier. Each panel's own stylesheet adds its size and contents. */
.label-anchored-panel {
  position: absolute;
  box-sizing: border-box;
  background: var(--color-neutral-white);
  border: 1px solid var(--color-neutral-300);
  border-radius: calc(8px * var(--ui-scale));
}

/* The tail is a true triangle drawn as two stacked clip-path layers — a neutral-300 backing triangle with a white
   fill triangle offset 1.5px toward the panel — so it keeps the panel's hairline along its slanted edges and joins
   the panel edge with no seam. (A border+rotate diamond leaves gaps at the base corners; don't reintroduce it.) */
.label-anchored-panel::before,
.label-anchored-panel::after {
  content: "";
  position: absolute;
  top: var(--panel-tail-top, 50%);
  width: calc(8px * var(--ui-scale));
  height: calc(16px * var(--ui-scale));
  margin-top: calc(-8px * var(--ui-scale));
  clip-path: polygon(100% 0, 0 50%, 100% 100%);
}

.label-anchored-panel::before {
  left: calc(-8px * var(--ui-scale));
  background: var(--color-neutral-300);
}

.label-anchored-panel::after {
  left: calc(-6.5px * var(--ui-scale));
  background: var(--color-neutral-white);
}

/* Flipped: the panel sits to the left of the label icon, so the tail moves to the panel's right edge. */
.label-anchored-panel--flipped::before,
.label-anchored-panel--flipped::after {
  left: auto;
  clip-path: polygon(0 0, 100% 50%, 0 100%);
}

.label-anchored-panel--flipped::before {
  right: calc(-8px * var(--ui-scale));
}

.label-anchored-panel--flipped::after {
  right: calc(-6.5px * var(--ui-scale));
}
/* The read-only label card, shared by Explore and Validate. The shell — white card, neutral hairline, 8px radius,
   tail at the icon — comes from .label-anchored-panel, shared in turn with Explore's context menu (the card's
   editable state). What's specific here is the caption scale and the absence of any form affordance, so it stays
   unmistakably read-only. The label color lives in the type icon rather than the card surface: white text on a raw
   label color runs 1.68-2.75:1, under the 4.5:1 AA floor for every label type (#4719, #4726).

   Populated by Explore's Label.js #updateHoverCard() and Validate's LabelCard, positioned by both through
   util.anchorPanelToLabel. Each tool sets its own stacking position and hosts its own footer actions: Explore
   Delete/Edit, Validate a single Hide-label toggle. */
.label-hover-card {
  left: 0;
  top: 0;
  visibility: hidden;
  width: max-content;
  max-width: calc(240px * var(--ui-scale));
  box-shadow: 0 calc(2px * var(--ui-scale)) calc(10px * var(--ui-scale)) rgb(45 42 63 / 28%);
}

.label-hover-card__header {
  display: flex;
  align-items: center;
  gap: calc(7px * var(--ui-scale));
  padding: calc(6px * var(--ui-scale)) calc(10px * var(--ui-scale));
}

.label-hover-card__icon {
  width: calc(20px * var(--ui-scale));
  height: calc(20px * var(--ui-scale));
}

.label-hover-card__type {
  flex: 1;
  font: var(--text-caption-semibold);
  color: var(--color-neutral-black);
  white-space: nowrap;
}

/* The type as a dropdown (components/labelTypeTrigger, Expert Validate), sized to match the plain title above. */
.label-hover-card__type-dropdown {
  flex: 1;
  min-width: 0;
  font: var(--text-caption-semibold);
  color: var(--color-neutral-black);
  white-space: nowrap;
}

.label-hover-card__type-dropdown .label-type-trigger {
  gap: calc(7px * var(--ui-scale));
}

.label-hover-card__type-dropdown .label-type-trigger__icon {
  width: calc(20px * var(--ui-scale));
  height: calc(20px * var(--ui-scale));
}

.label-hover-card__type-dropdown .label-type-trigger__chevron {
  width: calc(14px * var(--ui-scale));
  height: calc(14px * var(--ui-scale));
}

/* Share, in the header rather than the footer: the footer is where a panel's actions on *this* label live (Explore's
   Delete/Edit, Validate's Hide), and at the card's 240px width a third button there would leave all three too narrow
   to read. Icon-only for the same reason — the label beside it would double the header's width. Its accessible name
   comes from aria-label, and the popover it opens says "Share this label" at the top. */
.label-hover-card__share.label-detail__share-trigger {
  flex: none;
  gap: 0;
  padding: calc(3px * var(--ui-scale));
  background: none;
  border: none;
  border-radius: calc(5px * var(--ui-scale));
  color: var(--color-neutral-700);
}

.label-hover-card__share.label-detail__share-trigger:hover {
  background: var(--color-neutral-100);
  border: none;
  color: var(--color-neutral-black);
}

.label-hover-card__share svg {
  width: calc(14px * var(--ui-scale));
  height: calc(14px * var(--ui-scale));
}

/* Provenance strip for AI-generated labels (#5359), between the header and the facts. A tooltip on the marker's AI
   badge is the obvious home for this sentence, but on Validate the hover that would open it is the hover that opens
   this card beside the marker, so the two would stack. Its own strip rather than a body chip because it says where the
   label came from, not what it claims, so the body's empty-state logic doesn't count it (LabelCardView). The wash sets
   it off as a notice; neutral-800 on it holds 8:1, where the description's neutral-700 would sit at 4.7:1 for 10px
   text. Shown per label by LabelCardView. */
.label-hover-card__ai {
  display: none;
  align-items: flex-start;
  gap: calc(6px * var(--ui-scale));
  padding: calc(6px * var(--ui-scale)) calc(10px * var(--ui-scale));
  border-top: 1px solid var(--color-neutral-200);
  background: var(--color-neutral-100);
  font: var(--text-tiny-regular);
  line-height: 1.45;
  color: var(--color-neutral-800);
}

/* 16px rather than 14px: the glyph sits inside a ring, so at 14px it drops to ~9px and the "AI" stops being readable.
   Nudged up 1px so the ring centres on the first line of text rather than hanging below it. */
.label-hover-card__ai-icon {
  flex: none;
  width: calc(16px * var(--ui-scale));
  height: calc(16px * var(--ui-scale));
  margin-top: calc(-1px * var(--ui-scale));
}

.label-hover-card__body {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: calc(6px * var(--ui-scale));
  padding: calc(7px * var(--ui-scale)) calc(10px * var(--ui-scale)) calc(8px * var(--ui-scale));
  border-top: 1px solid var(--color-neutral-200);
}

/* The severity, not-rated, tags, and description rows are shown/hidden per label by the populating JS.

   Severity and not-rated are drawn as filled chips that enclose their icon and text, so the rating reads as one
   object instead of a loose icon-plus-sentence row. They match the tag chips below in height, type, and text color
   and differ only by their fill, which keeps the body one uniform stack of read-only chips rather than a ladder of
   competing sizes. The fill stays neutral: the level's color already comes from the smiley asset, and a
   level-to-color map in CSS would be a hardcoded copy of it. */
.label-hover-card__severity,
.label-hover-card__not-rated {
  display: none;
  align-items: center;
  gap: calc(4px * var(--ui-scale));

  /* Tighter on the icon side so the icon sits just inside the chip's edge. */
  padding: calc(2px * var(--ui-scale)) calc(7px * var(--ui-scale)) calc(2px * var(--ui-scale))
    calc(3px * var(--ui-scale));
  border-radius: 200px;
  background: var(--level-wash, var(--color-neutral-100));
  font: var(--text-tiny-medium);

  /* neutral-800, not the neutral-700 the tag chips use: on a level wash rather than flat neutral-100 this text
     falls to 4.35-4.42:1 for levels 2 and 3, under the AA floor. neutral-800 holds 7.5-8.7:1 on every level. */
  color: var(--color-neutral-800);
}

.label-hover-card__severity-icon {
  flex: none;
  width: calc(12px * var(--ui-scale));
  height: calc(12px * var(--ui-scale));

  /* The smiley is the same asset the context menu uses as a rating button, so it's held back here to read as a
     status glyph rather than something to click. */
  opacity: 0.75;
}

/* Mirrors the red "?" alert drawn on the canvas next to unrated labels (see Label.js #showSeverityAlert). Unlike the
   severity smiley this one keeps its full strength — it's flagging something still to do. Explore only: a validator
   can't rate from this card, so Validate leaves the chip out of its markup rather than nudging about a gap it
   offers no way to close. */
.label-hover-card__not-rated-icon {
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: calc(12px * var(--ui-scale));
  height: calc(12px * var(--ui-scale));
  border-radius: 50%;
  background: var(--color-error-200);
  color: var(--color-neutral-white);
  font: var(--text-tiny-semibold);
}

.label-hover-card__tags {
  display: none;
  flex-wrap: wrap;
  gap: calc(4px * var(--ui-scale));
}

/* A hairline between the rating and the tags, when there is a rating above them to separate from. The two rows are
   both chips, so without it they read as one wrapped set rather than two different kinds of fact. The JS adds the
   class, because the rating is hidden with an inline display:none that no sibling selector can see. */
.label-hover-card__tags--divided {
  align-self: stretch;
  padding-top: calc(7px * var(--ui-scale));
  border-top: 1px solid var(--color-neutral-200);
}

/* The shared pill shape, shrunk and quieted down: these tags are read-only metadata, so they must not carry the
   context menu's tappable-button look (dark outline, roomy padding). The hairline stays — several tags sit side by
   side and need an edge to tell them apart — but drops to neutral-300 with 10px gray text. */
.label-hover-card__tags .tag-pill {
  padding: calc(2px * var(--ui-scale)) calc(7px * var(--ui-scale));
  outline-color: var(--color-neutral-300);
}

.label-hover-card__tags .tag-pill__label {
  font: var(--text-tiny-medium);
  color: var(--color-neutral-700);
}

/* Tiny, matching the chips above it — at caption size it was the largest thing in a body made of 10px chips, so
   the least important field read as the most prominent. The token's own 12px line-height is too tight for a
   description that wraps, so only that is overridden. */
.label-hover-card__description {
  display: none;
  font: var(--text-tiny-regular);
  line-height: 1.45;
  font-style: italic;
  color: var(--color-neutral-700);
  overflow-wrap: anywhere;
}

/* Empty state: no rating, no tags, no description. Validate always has a body to fill because it names the label it
   is asking about; Explore collapses the body instead, since a label with nothing in it is one the user just placed
   and is about to fill in. */
.label-hover-card__no-info {
  display: none;
  font: var(--text-tiny-regular);
  color: var(--color-neutral-700);
}

/* Footer action buttons (design-system button/button--tiny). */
.label-hover-card__actions {
  display: flex;
  gap: calc(6px * var(--ui-scale));
  padding: calc(6px * var(--ui-scale)) calc(10px * var(--ui-scale)) calc(8px * var(--ui-scale));
  border-top: 1px solid var(--color-neutral-200);
}

.label-hover-card__action-button {
  flex: 1;
  justify-content: center;
  gap: calc(5px * var(--ui-scale));
}

.label-hover-card__action-button svg,
.label-hover-card__action-button img {
  flex: none;
  width: calc(12px * var(--ui-scale));
  height: calc(12px * var(--ui-scale));
}
/* Imagery attribution (#4865): the rights holder, provider and licence owed while Project Sidewalk's own copy of the
   imagery — the self-hosted Pannellum pano or the static crop — is on screen. The providers' live viewers draw their
   own. Always visible, not hover-revealed: it is an obligation of the licence, not a control. Dark pill for contrast
   on any imagery; the licence token is the one link.

   Mounted by PanoAttribution.js (the pano-viewer and pano-credit bundles) inside a positioned pano container. This
   file is the look; each host positions the pill where its own overlays leave room (label-detail.css,
   validate/svv-panorama.css, gallery/cards.css, landing-validation-grid.css). */
.pano-attribution {
  position: absolute;
  max-width: calc(100% - 20px);
  padding: 4px 10px;
  border-radius: 999px;
  background: rgb(0 0 0 / 60%);
  color: var(--color-neutral-white);
  font: var(--text-caption-medium);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  pointer-events: none; /* Never intercepts a drag on the imagery beneath it… */
}

.pano-attribution a {
  color: inherit;
  text-decoration: underline;
  pointer-events: auto; /* …except on the licence link itself. */
}

.pano-attribution a:focus-visible {
  outline: 2px solid var(--color-neutral-white);
  outline-offset: 2px;
}

/* Card-sized form (#5202). The source logo opposite it names the provider, so this half carries only the holder and
   the licence. It wraps rather than truncating: the licence name is the last thing that may be cut. */
.pano-attribution--compact {
  max-width: 62%;
  padding: 2px 6px;
  border-radius: 7px;
  font: var(--text-tiny-medium);
  white-space: normal;
  text-align: right;
  text-overflow: clip;
}

.pano-attribution[hidden] { display: none; }
.tool-ui {
  /* === Validate layout dimensions ===
     * --ui-scale is a single uniform scale factor for the whole tool, like browser zoom. JS sets it from the available
     * viewport size (see util.applyToolScale); every dimension below is expressed as base-size * var(--ui-scale) so
     * the pano, validation menu, and text all grow and shrink together in proportion. */
  --ui-scale: 1; /* The single scale knob. Overridden inline by util.applyToolScale() on load and resize. */

  /* Base sizes at --ui-scale = 1. util.applyToolScale() reads these to compute the tool's reference footprint,
     * so they must stay plain px values (no var()/calc()). */
  --pano-base-width: 720px;   /* Pano display size at --ui-scale = 1 (3:2, matching Explore). */
  --pano-base-height: 480px;
  --menu-base-width: 300px;   /* Right-hand validation menu column. */
  --menu-base-gap: 16px;      /* Horizontal gap between the pano and the validation menu. */
  --header-base-height: 80px; /* Mission title + progress bar sitting above the pano. */

  --pano-width: calc(var(--pano-base-width) * var(--ui-scale));
  --pano-height: calc(var(--pano-base-height) * var(--ui-scale));
  --menu-width: calc(var(--menu-base-width) * var(--ui-scale));
  --menu-gap: calc(var(--menu-base-gap) * var(--ui-scale));
  --header-height: calc(var(--header-base-height) * var(--ui-scale));

  /* Derived positions — these keep the rest of the layout in sync with the pano size automatically. */
  --menu-left: calc(var(--pano-width) + var(--menu-gap)); /* Left edge of the validation menu column. */
  --app-width: calc(var(--pano-width) + var(--menu-gap) + var(--menu-width));

  /* Base text size for the whole tool. */
  font: var(--text-body-regular);

  width: var(--app-width);
}

#svv-application-holder {
  background: var(--color-neutral-white);
  margin: 0 auto;
  position: relative;
  height: calc(var(--header-height) + var(--pano-height));
  width: var(--app-width);
}

.normal { font-weight: normal; }
/* Immersive mode (#5560): the pano fills the browser window and the surviving controls float over it.
   body.svv-immersive is toggled by the shared ImmersiveMode (frontend/js/common/ImmersiveMode.js), which also sets
   html.chromeless (main.css) to hide the navbar and the test-server banner. Everything here overrides the boxed
   layout's CSS variables on the same element, so #svv-application-holder, #svv-panorama-holder and the viewer follow
   (see the derivations in svv-general.css) without touching their own rules. The pano's size is CSS-driven here;
   --ui-scale, which JS still sets (Main.applyValidateScale), only sizes the controls floating over it.

   The DOM is the boxed page's: the menu column becomes the dock at the bottom-centre and the mission title and
   progress bar become one pill at the top-centre (#mission-status, layout-free in the boxed page) by position alone,
   so DesktopValidationMenu and StatusField know nothing about the mode. Expert Validate never enters it (Main.js),
   so the edit sections have no rules here yet. */
body.svv-immersive .tool-ui {
  position: fixed;
  inset: 0;
  width: auto;
  margin: 0;
  --pano-width: 100vw;
  --pano-height: 100dvh;
  --app-width: 100vw;

  /* The pill floats over the pano, so the pano starts at the very top: override the derived variable rather than
     zeroing --header-base-height, which applyValidateScale still reads for the fit. */
  --header-height: 0px;

  /* Every floating surface: the boxed page's asphalt at a translucency that keeps white text past 9:1 on any
     imagery while the streetscape still shows through the edges. */
  --svv-scrim: rgb(from var(--color-asphalt-500) r g b / 88%);
}

body.svv-immersive #svv-application-holder {
  width: 100vw;
  height: 100dvh;
  margin: 0;
  background: var(--color-asphalt-400); /* The viewer's own backdrop, for the frame before imagery paints. */
}

/* The screen edge is the frame. The holder too, since it carries the pano's backdrop while a load hides the canvas. */
body.svv-immersive #svv-panorama,
body.svv-immersive #svv-panorama-holder {
  border-radius: 0;
}

body.svv-immersive #pano-border-frame {
  display: none;
}

/* The title, its type icon and the progress bar share one pill at the top-centre: the place the corner controls never
   reach and sky in nearly every pano. The count is worth its pixels, since a validator paces a mission by it. */
body.svv-immersive #mission-status {
  position: absolute;
  top: calc(8px * var(--ui-scale));
  left: 50%;
  transform: translateX(-50%);
  display: flex;
  align-items: center;
  gap: calc(8px * var(--ui-scale));
  max-width: calc(100vw - 260px * var(--ui-scale)); /* Clears the corner controls on a narrow window. */
  padding: calc(5px * var(--ui-scale)) calc(14px * var(--ui-scale)) calc(5px * var(--ui-scale))
    calc(8px * var(--ui-scale));
  color: var(--color-neutral-white);
  background: var(--svv-scrim);
  border-radius: calc(999px * var(--ui-scale));
  z-index: 4;
}

body.svv-immersive .mission-title-icon {
  display: block;
  flex: 0 0 auto;
  width: calc(22px * var(--ui-scale));
  height: calc(22px * var(--ui-scale));
}

/* Sentence case, not the boxed page's letter-spaced capitals: over imagery it is a caption, not a banner. */
body.svv-immersive #mission-title {
  position: static;
  width: auto;
  overflow: hidden;
  font: var(--text-small-bold);
  letter-spacing: normal;
  text-transform: none;
  white-space: nowrap;
  text-overflow: ellipsis;
}

body.svv-immersive #mission-progress-bar-section {
  position: static;
  flex: 0 0 auto;
  width: calc(100px * var(--ui-scale));
  --ps-progress-bar-height: calc(5px * var(--ui-scale));
  gap: calc(8px * var(--ui-scale));
}

body.svv-immersive #mission-progress-bar-section .ps-progress-bar__track {
  background: rgb(from var(--color-neutral-white) r g b / 25%);
}

body.svv-immersive #mission-progress-bar-text {
  padding-bottom: 0;
  white-space: nowrap;
}

/* The menu column becomes the dock: the same column, its boxed width, floated at the bottom-centre on the scrim.
   The verdict is the core loop, so it stays always-on; the reason sections and Back / Submit grow the dock upward
   when a verdict shows them, exactly as they grew the column. Above the view-control layer (2) and below the label
   card (4), which is the transient one of the two. */
body.svv-immersive #validation-menu-holder {
  top: auto;
  bottom: calc(14px * var(--ui-scale));
  left: 50%;
  transform: translateX(-50%);
  height: auto;
  max-height: calc(100dvh - 110px * var(--ui-scale)); /* Clears the pill at the top. */
  overflow-y: auto;
  padding: calc(10px * var(--ui-scale)) calc(14px * var(--ui-scale)) calc(12px * var(--ui-scale));
  gap: calc(12px * var(--ui-scale));
  color: var(--color-neutral-white);
  background: var(--svv-scrim);
  border-radius: calc(10px * var(--ui-scale));
  box-shadow: var(--box-shadow-lg);
  z-index: 3;
}

/* A card opened on load (#5675) isn't transient, so it gives way to the dock: matching the card's z-index (4) puts the
   dock on top, since it comes later in the page. */
body.svv-immersive:has(#label-card.label-card--held) #validation-menu-holder {
  z-index: 4;
}

/* The dock is sized to its content, so the boxed column's push-to-bottom has nothing to push against. */
body.svv-immersive #validate-submit-section {
  align-items: center;
  margin-top: 0;
}

/* The buttons and fields keep their white fills on the scrim, so their text must not inherit the dock's white. The
   chosen states keep their own colours (svv-validation-menu.css). */
body.svv-immersive .validate-page-button,
body.svv-immersive .validation-reason-button,
body.svv-immersive .validate-text-input input {
  color: var(--color-neutral-900);
}

/* The verdict buttons carry the same thumbs mobile Validate does, beside the underlined shortcut letter. */
body.svv-immersive .validate-page-button {
  display: inline-flex; /* The boxed rule sets inline-block, which cannot centre the thumb beside the text. */
  align-items: center;
  justify-content: center;
  gap: calc(6px * var(--ui-scale));
}

body.svv-immersive .validate-page-button__icon {
  display: inline-block;
  width: calc(16px * var(--ui-scale));
  height: calc(16px * var(--ui-scale));
}

/* A chosen verdict's fill is the thumb's own colour, so the thumb goes dark rather than vanish into it. */
body.svv-immersive .validate-page-button.is-chosen .validate-page-button__icon {
  filter: brightness(0);
  opacity: 0.85;
}

/* Back and Submit move between labels rather than answer this one, so neither may look like a fourth verdict. */
body.svv-immersive #validate-undo-button {
  gap: calc(2px * var(--ui-scale));
  padding: calc(2px * var(--ui-scale)) calc(6px * var(--ui-scale)) calc(2px * var(--ui-scale)) 0;
  color: var(--color-neutral-white);
  background: none;
  outline: none;
  font: var(--text-small-regular);
}

body.svv-immersive #validate-undo-button:hover:not(:disabled) {
  text-decoration: underline;
}

body.svv-immersive #validate-undo-button:focus-visible {
  outline: 2px solid var(--color-neutral-white);
  outline-offset: 2px;
}

/* The boxed disabled grey is set for a white fill; on the scrim it needs the white itself, faded. */
body.svv-immersive #validate-undo-button:disabled {
  color: rgb(from var(--color-neutral-white) r g b / 45%);
}

body.svv-immersive #validate-submit-button {
  display: none;
  min-width: calc(140px * var(--ui-scale));
}

body.svv-immersive #validation-menu-holder.has-verdict #validate-submit-button {
  display: inline-block;
}

/* A mask, not an <img>, so the arrow takes the text colour and fades out with it when Back is disabled. */
body.svv-immersive .validate-undo-button__icon {
  display: inline-block;
  width: calc(18px * var(--ui-scale));
  height: calc(18px * var(--ui-scale));
  -webkit-mask-image: url('/assets/images/icons/56fedcabde9ff4eb08f32f3c897106e4-chevron-left-feather.svg');
  mask-image: url('/assets/images/icons/56fedcabde9ff4eb08f32f3c897106e4-chevron-left-feather.svg');
}

/* The close control in the dock's top-right corner, once a verdict has opened it: an X takes the answer back so the
   dock folds to the three buttons again. Shown by the has-verdict class DesktopValidationMenu keeps on the holder. */
body.svv-immersive #validation-menu-holder.has-verdict #validate-verdict-clear {
  display: inline-flex;
}

body.svv-immersive #validate-verdict-clear {
  position: absolute;
  top: calc(8px * var(--ui-scale));
  right: calc(8px * var(--ui-scale));
  align-items: center;
  justify-content: center;
  width: calc(24px * var(--ui-scale));
  height: calc(24px * var(--ui-scale));
  padding: 0;
  border: none;
  border-radius: calc(4px * var(--ui-scale));
  background: none;
  color: var(--color-neutral-white);
  cursor: pointer;
}

/* The default rings are drawn for a white page: Submit's banana ring and the browser's blue both fall under 3:1 on the
   scrim, and the verdict buttons have none at all (svv-validation-menu.css), so the dock's controls get Back's. */
body.svv-immersive #validate-verdict-clear:focus-visible,
body.svv-immersive #validate-submit-button:focus-visible,
body.svv-immersive .validate-page-button:focus-visible,
body.svv-immersive .validation-reason-button:focus-visible {
  outline: 2px solid var(--color-neutral-white);
  outline-offset: 2px;
}

body.svv-immersive #validate-verdict-clear:hover {
  background: rgb(from var(--color-neutral-white) r g b / 15%);
}

body.svv-immersive .validate-verdict-clear__icon {
  width: calc(14px * var(--ui-scale));
  height: calc(14px * var(--ui-scale));
  -webkit-mask-image: url('/assets/images/icons/51f1808efa37a74f83be13de8f3c5abe-cross.svg');
  mask-image: url('/assets/images/icons/51f1808efa37a74f83be13de8f3c5abe-cross.svg');
}

/* The question keeps clear of the close control. */
body.svv-immersive #main-validate-header {
  padding-right: calc(28px * var(--ui-scale));
}

body.svv-immersive .validation-menu-section-header {
  margin-bottom: calc(8px * var(--ui-scale));
}

/* The Hide-label, chevron, zoom and immersive controls keep the boxed layout's top corners (svv-panorama.css). Over
   imagery rather than the boxed layout's asphalt frame, each gets a single light stroke so it reads on a bright or
   dark sky alike, as Explore's do. */
body.svv-immersive .pano-overlay-button,
body.svv-immersive .ps-icon-button,
body.svv-immersive .speed-limit-sign {
  box-shadow: 0 0 0 1px var(--color-neutral-600);
}

/* The mission-start screen is sized to the page under the navbar, which immersive mode hides (html.chromeless), so it
   has to cover the whole window on a page that loads straight into the mode. */
body.svv-immersive .mission-start-tutorial-overlay {
  height: 100%;
}

/* The modals are sized to the boxed pano (svv-modal.css): keep them that size and centre them over the window rather
   than stretching a mission summary across a whole monitor. The ids out-rank the class, so both are named. */
body.svv-immersive .modal-foreground,
body.svv-immersive #modal-mission-complete-foreground {
  top: 50%;
  left: 50%;
  right: auto;
  transform: translate(-50%, -50%);
  width: calc(var(--pano-base-width) * var(--ui-scale));
  height: calc(var(--pano-base-height) * var(--ui-scale));
}

/* The boxed backdrop overshoots its holder to cover the page margins; the holder is now the window. It also has to
   cover the pill, the corner controls and the dock, which float above an unstacked backdrop (z-index 3 and 4). */
body.svv-immersive .modal-background {
  left: 0;
  width: 100%;
  z-index: 5;
}

/* The pano's footprint is nearly the whole window here, far too big for a few counts, so this is sized to content. */
body.svv-immersive #modal-mission-complete-foreground {
  width: min(calc(440px * var(--ui-scale)), calc(100vw - 32px));
  height: auto;
  max-height: calc(100dvh - 32px);
  overflow-y: auto;
  padding: calc(28px * var(--ui-scale)) calc(32px * var(--ui-scale));
  border: none;
  border-radius: calc(12px * var(--ui-scale));
  box-shadow: var(--box-shadow-lg);
}

body.svv-immersive #modal-mission-complete-title {
  margin-top: 0;
}

/* At this width the side-by-side 3:2 row leaves the primary about 150px of text once .button--large's padding is paid,
   so "Start an exploration mission" wrapped to three lines and stretched its neighbour to match. Stacked full-width,
   each label fits on one line in every locale (the French one is the longest), primary on top. */
body.svv-immersive .modal-mission-complete-buttons {
  flex-direction: column;
}

body.svv-immersive #modal-mission-complete-close-button-primary,
body.svv-immersive #modal-mission-complete-close-button-secondary {
  flex: none;
  width: 100%;
}

body.svv-immersive #modal-mission-complete-table {
  height: auto;
  padding-left: 0;
  margin-top: calc(20px * var(--ui-scale));
}

body.svv-immersive .modal-mission-complete-counts {
  width: 100%;
}
#modal-mission-holder {
  position: absolute;
  left: 0;
  top: 0;
  height: 100%;
  width: 100%;
  text-align: justify;
  visibility: hidden;
}

#modal-mission-foreground {
  padding: calc(100px * var(--ui-scale)) calc(20px * var(--ui-scale)) calc(10px * var(--ui-scale));
  visibility: hidden;
}

.modal-foreground {
  top: var(--header-height);
  left: 0;
  right: 0;
  padding: calc(10px * var(--ui-scale));
  height: var(--pano-height);
  background: var(--color-neutral-white);
  color: var(--color-neutral-800);
  font: var(--text-subtitle-regular);
  position: absolute;
  overflow: hidden;
  border: calc(4px * var(--ui-scale)) solid var(--color-neutral-black);
  border-radius: calc(5px * var(--ui-scale));
  z-index: 10;
}

.modal-foreground h1 {
  font: var(--text-h3-medium);
}

.modal-background {
  position: absolute;
  left: calc(-60px * var(--ui-scale));
  top: 0;
  background: rgb(from var(--color-neutral-white) r g b / 50%);
  height: 100%;
  width: 110%;
}

#modal-mission-close-button {
  display: block;
  width: 40%;
  margin: calc(30px * var(--ui-scale)) auto 0;
}

#modal-mission-header,
#modal-mission-instruction {
  text-align: center;
}

/* The dead end's illustration (ModalNoNewMission). */
.modal-mission-images {
  display: block;
  margin: 0 auto;
}

/* Padding, not margin, so the gap can't collapse into the figure's own margin. */
.modal-mission-dead-end-text {
  padding: calc(10px * var(--ui-scale, 1)) 0;
}

#modal-mission-background {
  position: absolute;
  left: 0;
  top: 0;
  background: rgb(from var(--color-neutral-white) r g b / 50%);
  visibility: hidden;
  height: 100%;
  width: 110%;
}

#modal-mission-complete-holder {
  visibility: hidden;
}

#modal-mission-complete-foreground {
  visibility: hidden;
  top: var(--header-height);
}

#modal-mission-complete-title {
  margin-top: calc(70px * var(--ui-scale));
  text-align: center;
}

#modal-mission-complete-message {
  text-align: center;
}

#modal-mission-complete-table {
  height: calc(190px * var(--ui-scale));
  padding-left: calc(175px * var(--ui-scale));
}

#modal-mission-complete-table h3 {
  margin: 0 0 calc(8px * var(--ui-scale, 1));
  font: var(--text-title-bold);
}

.modal-mission-complete-counts {
  width: 80%;
  margin-bottom: calc(20px * var(--ui-scale, 1));
  border-collapse: collapse;
}

.modal-mission-complete-counts th,
.modal-mission-complete-counts td {
  padding: calc(8px * var(--ui-scale, 1));
  border-top: calc(1px * var(--ui-scale, 1)) solid var(--color-neutral-300);
  text-align: left;
}

.modal-mission-complete-counts td {
  text-align: right;
}

.modal-mission-complete-counts tr:first-child > * {
  border-top: 0;
}

.modal-mission-complete-counts #your-overall-total > * {
  border-top-width: calc(3px * var(--ui-scale, 1));
}

.modal-mission-complete-buttons {
  display: flex;
  gap: calc(8px * var(--ui-scale, 1));
}

#modal-mission-complete-close-button-primary {
  flex: 3;
}

#modal-mission-complete-close-button-secondary {
  flex: 2;
}
/* Container for the panorama and its overlay controls (zoom, date, speed limit, etc.). It carries the viewer's dark
   backdrop and shape as well, because PanoManager holds #svv-panorama at visibility: hidden (or out of the layout)
   while a pano loads (#5582), which hides that element's own background with it; without this the area would flash
   the page's white for the length of every load, on boxed desktop and on mobile. */
#svv-panorama-holder {
  position: absolute;
  top: var(--header-height);
  width: var(--pano-width);
  height: var(--pano-height);
  border-radius: calc(4px * var(--ui-scale));
  background-color: var(--color-asphalt-400);
}

/* The panorama viewport. It fills the full pano area (no border) so the marker layer, which is the same size,
   overlays the imagery exactly. The colored frame is drawn on top by #pano-border-frame instead. */
#svv-panorama {
  position: absolute;
  left: 0;
  top: 0;
  height: var(--pano-height);
  width: var(--pano-width);
  border-radius: calc(4px * var(--ui-scale));
  background-color: var(--color-asphalt-400);
  overflow: hidden;

  /* Keeps the provider's own z-indexed elements inside this box. The canvas sits laid out under the Pannellum sibling
     while a live pano loads behind a fallback one (PanoManager.setPanorama, #5453), and without its own stacking
     context a Google control with a z-index would paint, and take taps, over the fallback's imagery. */
  isolation: isolate;
}

/* Pannellum's own progress box. The fallback viewer's canvas is painted only once it holds the current label's pano
   (PanoManager.#showPannellumPano, #5206), so this box can surface in one case: a fallback label following another,
   where the outgoing pano stays up during the load. There it reads as a second provider's progress UI, in its own
   styling, reporting megabytes, over a picture it does not belong to, and it would stack with the Validate loading
   status below (#5581), which says the same thing in the tool's own voice in every layout, mobile included. */
#svv-panorama-pannellum .pnlm-load-box {
  display: none !important;
}

/* The loading status over the pano (#5581, PanoLoadingStatus.js), shared by boxed, immersive and mobile Validate. The
   live region fills the holder so its box centers over whichever viewer canvas is up, but only the box paints, and
   nothing takes the pointer: a drag that starts over it still reaches the pano. Stacked with the corner controls (3)
   and before them in the markup, so those and the label card (4) stay on top of it. */
.svv-pano-loading {
  position: absolute;
  inset: 0;
  z-index: 3;
  display: flex;
  align-items: center;
  justify-content: center;
  pointer-events: none;
}

/* The busy dim (.validate-disabled, svv-validation-menu.css) sits on the whole application holder, and a child cannot
   opt out of an ancestor's opacity, so the status would render washed out exactly when it is the one thing meant to be
   read. The holder therefore dims its parts individually while busy, and the status is the one part left out. The
   mission modals are left out too: the next mission's first label loads behind "Great job!", and a dimmed modal is
   both washed out and a stacking context of its own, which would drop its z-index 10 foreground under the status. */
#svv-application-holder.validate-disabled {
  opacity: 1;
}

#svv-application-holder.validate-disabled > :not(#svv-panorama-holder, [id^="modal-mission"]),
#svv-application-holder.validate-disabled > #svv-panorama-holder > :not(#svv-pano-loading) {
  opacity: 0.6;
}

/* Solid, not a translucent scrim: over the boxed page's white a translucent backdrop took the text under 4.5:1, and
   the darkest asphalt keeps white text far past it on every layout and over imagery. */
.svv-pano-loading__box {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-xxs);
  max-width: calc(100% - 32px * var(--ui-scale, 1));
  padding: var(--space-sm) var(--space-md);
  border-radius: var(--border-radius-lg);
  background: var(--color-asphalt-800);
  box-shadow: var(--box-shadow-lg);
  color: var(--color-neutral-white);
  font: var(--text-small-medium);
  text-align: center;
}

.svv-pano-loading__img {
  width: calc(64px * var(--ui-scale, 1));
  height: calc(64px * var(--ui-scale, 1));
}

/* The .webp animates on its own and cannot be paused, so drop it entirely and let the text carry the message, as the
   label popup's loading overlay does (css/components/label-detail.css). */
@media (prefers-reduced-motion: reduce) {
  .svv-pano-loading__img { display: none; }
}

/* Colored frame on top of the pano and marker layers. Drawing it above the marker (rather than as the pano's own
   border underneath) means a label at the very edge is clipped by the frame instead of rendering on top of it. */
#pano-border-frame {
  position: absolute;
  left: 0;
  top: 0;
  height: var(--pano-height);
  width: var(--pano-width);
  z-index: 3;
  pointer-events: none;
  box-sizing: border-box;
  border: calc(3px * var(--ui-scale)) solid var(--color-asphalt-400);
  border-radius: calc(4px * var(--ui-scale));
}

#view-control-layer {
  position: absolute;
  width: var(--pano-width);
  height: var(--pano-height);
  left: 0;
  top: 0;
  overflow: hidden;
  cursor: url('/assets/images/icons/b06c243f534d9c5461d16528156cd5a8-openhand.cur') 4 4, move;
  z-index: 2;

  /* http://stackoverflow.com/questions/8942805/chrome-bug-cursor-changes-on-mouse-down-move */
  -webkit-user-select: none;
  user-select: none;
}

/* The label marker is created by PanoMarker at a base px size multiplied by the UI scale in JS; its icon and hidden
   state are main.css's shared .label-marker rules, off the --label-icon / --label-color PanoManager writes.

   Pointer-target floor. The marker is what opens the label card — on desktop by hover and as a keyboard stop
   (PanoMarker, #4729), on mobile by touch — but the mark itself is 22px at --ui-scale 1 and shrinks to 14px at the
   0.65x end.

   This is about *acquisition*, not conformance. WCAG 2.5.8 Target Size (Minimum), AA, sizes targets for pointer
   activation, and desktop has no pointer activation here: nothing binds click to the marker (#4843). So the SC does
   not actually reach it, and mobile — which does activate by pointer — is already past 24px at 32px. But a hover
   target is if anything the harder case, since it has to be found and then held, and 14px is small for that.
   24px is still the right number to floor at: it is what 2.5.8 asks of every other target on the page, and it is
   what Explore's placed-label target already uses.

   A transparent ::after widens only the *target*, so the mark stays the size it was drawn: growing the element
   instead would move the marker (PanoMarker positions it from its own size) and push the label card away from it
   (LabelVisibilityControl anchors on offsetWidth).

   max(), not a fixed size, so this only ever adds area — above ~1.09x the mark is already past 24px and the target
   is exactly the mark. Round, matching the mark: browsers hit-test through border-radius, and at every size the
   marker is drawn at (22px desktop base, 38px ceiling, 32px on a phone) .icon-outline's 25px radius clamps to 50%,
   so its target is a circle. Squaring it off here would change that at every scale rather than only flooring it
   where it is too small — opening the card on a hover several px off the visible marker.
   z-index keeps it under the icon and the AI badge; it takes the pointer for the marker either way, since all three
   are the same element as far as the hover handlers are concerned. */
#validate-pano-marker::after {
  /* Mirrors util.LABEL_MIN_SCREEN_TARGET (frontend/js/common/utilities.js), which floors Explore's label target. One
     WCAG number for both tools, so keep the two in step — CSS can't read the JS constant. */
  --label-min-target: 24px;

  content: "";
  position: absolute;
  top: 50%;
  left: 50%;
  width: max(100%, var(--label-min-target));
  height: max(100%, var(--label-min-target));
  transform: translate(-50%, -50%);
  border-radius: 50%;
  z-index: -1;
}

/* Sits in the top-right just left of the stacked zoom control, scaled down to match its height. transform-origin keeps
   the top-right corner pinned so the scaled sign still lines up with the zoom buttons. */
.speed-limit-sign {
  display: none;
  width: calc(60px * var(--ui-scale));
  height: calc(70px * var(--ui-scale));
  padding: calc(3px * var(--ui-scale));
  top: calc(7px * var(--ui-scale));
  right: calc(36px * var(--ui-scale));
  border-radius: calc(5px * var(--ui-scale));
  position: absolute;
  z-index: 3;

  /* Hover must reach the sign for its title tooltip; `auto` also guards against a no-pointer-events ancestor. */
  pointer-events: auto;
  cursor: help;
  transform: scale(0.657);
  transform-origin: top right;
  background: rgb(from var(--color-neutral-white) r g b / 75%);
}

.speed-limit-sign[data-design-style="non-us-canada"] {
  width: calc(75px * var(--ui-scale));
  height: calc(75px * var(--ui-scale));
  padding: 0;
  border-radius: 100%;
  transform: scale(0.613);
}

.speed-limit-holder {
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: calc(5px * var(--ui-scale));
  border: calc(3px * var(--ui-scale)) solid var(--color-neutral-black);
}

.speed-limit-sign[data-design-style="non-us-canada"] .speed-limit-holder {
  border-radius: 100%;
  border: calc(7px * var(--ui-scale)) solid var(--color-error-200);
}

.speed-limit-text {
  display: flex;
  flex-direction: column;
  font: var(--text-small-bold);
  text-align: center;
}

#speed-limit {
  font: var(--text-h3-bold);
  font-size: calc(30px * var(--ui-scale));
}

/* Imagery attribution (#4865; look in css/components/pano-attribution.css, bundled into validate.css) in the pano's
   bottom-right — the top-right is the zoom buttons' and the speed-limit sign's, the bottom-left the logo's and the
   date's. Shown only while the Pannellum fallback is up, so it never competes with GSV's own bottom-right links.
   Above the click-handling view-control-layer (2), which its licence link would otherwise sit under. */
#svv-panorama-holder .pano-attribution {
  bottom: calc(7px * var(--ui-scale));
  right: calc(7px * var(--ui-scale));
  z-index: 3;
  max-width: calc(100% - 14px * var(--ui-scale));
}

/* Shared button look in pano-overlay-buttons.css; this rule only positions the holder in the top-right of the pano. */
#zoom-buttons-holder {
  position: absolute;
  z-index: 3;
  top: calc(7px * var(--ui-scale));
  right: calc(7px * var(--ui-scale));
}

/* The immersive-mode toggle (#5560) sits under the zoom stack (7 + 22 + 2 + 22 = 53px tall, plus a 6px gap), in the
   corner users already scan for view controls, as on Explore. */
#immersive-toggle-holder {
  position: absolute;
  z-index: 3;
  top: calc(59px * var(--ui-scale));
  right: calc(7px * var(--ui-scale));
}

/* The arrows reach their icon's corners, so a smaller icon keeps them off the button's edge. */
#immersive-toggle-icon {
  width: calc(15px * var(--ui-scale));
  height: calc(15px * var(--ui-scale));
}

#svv-panorama-date-holder {
  display: flex;
  justify-content: flex-start;
  height: calc(28px * var(--ui-scale));
  position: absolute;
  bottom: 0;

  /* Sits 8px right of wherever the imagery-source logo's pixels end. How far that is varies by source (Mapillary's
     wordmark is the widest), so the offset tracks the logo through --pano-logo-width, which PanoViewerLogo measures
     and publishes. The fallback covers GSV, which draws its own logo instead of going through that overlay: 66px is
     where Google's Street View logo ends. */
  left: calc((var(--pano-logo-width, 66px) + 8px) * var(--ui-scale));
  z-index: 2;
  font: var(--text-small-medium);
  text-shadow:
    calc(1px * var(--ui-scale)) calc(1px * var(--ui-scale)) calc(1px * var(--ui-scale)) var(--color-neutral-black);
  color: var(--color-neutral-white);
}

/* No left padding — the holder's left offset above owns the whole gap to the imagery-source logo. */
#svv-panorama-date {
  padding: calc(3px * var(--ui-scale)) calc(3px * var(--ui-scale)) calc(3px * var(--ui-scale)) 0;
}

#pano-info-button {
  margin-left: calc(4px * var(--ui-scale));
  margin-top: calc(4.5px * var(--ui-scale));
  height: calc(16px * var(--ui-scale));
}

/* Modify Google's terms / report an issue buttons' color to match our own buttons. */
.gm-style-cc div { background-color: var(--color-asphalt-400); }
.gm-style-cc div div { background-color: var(--color-asphalt-400) !important; }

/* Scale Google's terms / report-a-problem links (bottom-right) with the UI. They're Google's own elements with no
   scale hooks, so we scale them visually, anchored to the bottom-right corner they sit in. Must stay scoped to
   #view-control-layer (where PanoManager relocates them): unscoped, it also matches a wrapper inside GSV's own DOM
   and transform-scales the entire street view scene. */
#view-control-layer div:has(> .gmnoprint) {
  bottom: calc(3px * var(--ui-scale)) !important;
  right: calc(2px * var(--ui-scale)) !important;
  transform: scale(var(--ui-scale));
  transform-origin: bottom right;
}

/* Scale Google's own Street View logo with the UI, anchored to its corner. */
#svv-panorama div:has(> a[aria-label*="Google"]) {
  left: calc(2px * var(--ui-scale)) !important;
  bottom: calc(2px * var(--ui-scale)) !important;
  transform: scale(var(--ui-scale));
  transform-origin: bottom left;
}

/* Scale Mapillary's attribution pill with the UI where the SDK renders it. It stays inside the SDK's DOM because the
   SDK patches its creator and date in place per image, and a node moved out of that tree keeps whatever it last said
   (#5600). That leaves it under the transparent control layer, so its links don't take clicks; the pano info popover
   carries the view-in-Mapillary link instead. Lifting it with a z-index would only hold while #svv-panorama is not a
   stacking context, and the isolation above and the image-adjustment filter (PanoImageAdjustments) both make it one.
   The ID selector is what outranks the vendor's position. */
#svv-panorama .mapillary-attribution-container {
  bottom: calc(3px * var(--ui-scale));
  right: calc(2px * var(--ui-scale));
  transform: scale(var(--ui-scale));
  transform-origin: bottom right;
}

/* Top-left of the pano, where Explore keeps its stuck/sound/feedback controls — and now with the same look, from the
   shared .pano-overlay-button (css/components/pano-overlay-buttons.css). It used to carry a Validate-only
   .button-on-pano style (white fill, 1.5px neutral-800 hairline, asphalt-500 text) that matched nothing else on the
   page (#4726). */
#label-visibility-control-holder {
  position: absolute;
  z-index: 2;
  top: calc(7px * var(--ui-scale));
  left: calc(7px * var(--ui-scale));
}

/* Continues the pill row to the right, as Explore's menu does. Immersive mode (svv-immersive.css) keeps the row: its
   top-centre holds only the mission chip, so nothing sits to the right of this corner for the row to slide under. */
#validate-control-menu {
  display: none;
  flex-direction: row;
  align-items: stretch;
}

#validate-control-buttons-toggle[aria-expanded="true"] + #validate-control-menu {
  display: flex;
}

#validate-control-buttons-toggle[aria-expanded="true"] .pano-overlay-button-icon {
  transform: rotate(180deg);
}

/* A notch smaller than the shared base size (main.css) for the label card's tiny footer button. */
.label-hover-card__action-button .hide-label-button-icon {
  width: calc(12px * var(--ui-scale));
  height: calc(12px * var(--ui-scale));
}

/* Validate's copy of the read-only label card (css/components/label-hover-card.css). Unlike Explore's it is not a click
   target — a validator judges the label, they don't edit it — so it carries no cursor and no Delete/Edit row, only
   the Hide-label toggle. Above #pano-border-frame (z-index: 3), which is drawn over the marker layer. */
#label-card {
  z-index: 4;
}
/* The boxed page places the title and the bar on their own; the wrapper and the type icon are immersive mode's. */
#mission-status {
  display: contents;
}

.mission-title-icon {
  display: none;
}

#mission-title {
  width: var(--app-width);
  position: absolute;
  top: calc(20px * var(--ui-scale));
  font: var(--text-body-bold);
  letter-spacing: 5%;
  text-transform: uppercase;
}

/* The mission progress bar uses the shared .ps-progress-bar component; this positions it and sets its slim height. */
#mission-progress-bar-section {
  --ps-progress-bar-height: calc(7px * var(--ui-scale));
  position: absolute;
  top: calc(50px * var(--ui-scale));
  width: var(--pano-width);
  gap: calc(12px * var(--ui-scale));
}

#mission-progress-bar-text {
  font: var(--text-small-regular);
  padding-bottom: calc(2px * var(--ui-scale));
}

#admin-info-section {
  display: none;
  position: absolute;
  top: calc(43px * var(--ui-scale));
  left: var(--menu-left);
}

/* Parked under its button by AdminInfo.js. It paints in the top layer, so it reads --ui-scale from :root. */
.admin-info-popover {
  margin: 0;
  padding: calc(12px * var(--ui-scale));
  max-width: calc(450px * var(--ui-scale));
  border: 1px solid var(--color-neutral-300);
  border-radius: var(--border-radius-lg);
  background: var(--color-neutral-white);
  box-shadow: var(--box-shadow-lg);
  font: var(--text-small-regular);
}

#admin-info-content h1 {
  font: var(--text-title-bold);
  margin-top: 0;
  margin-bottom: 0;
}

#admin-info-content h2 {
  font: var(--text-body-bold);
  margin-top: calc(8px * var(--ui-scale));
  margin-bottom: 0;
}

#admin-info-content p {
  font: var(--text-caption-regular);
  margin-bottom: 0;
}
#validation-menu-holder {
  margin: 0;
  position: absolute;
  left: var(--menu-left);
  top: var(--header-height);
  width: var(--menu-width);
  height: var(--pano-height);
  display: flex;
  flex-direction: column;
  justify-content: flex-start;
  gap: calc(20px * var(--ui-scale));
}

.validate-disabled {
  opacity: 0.6;
  pointer-events: none;
  cursor: not-allowed;
}

.validation-menu-section {
  display: none; /* Initially hidden, sections shown when validation buttons are clicked. */
}

#main-validate-section {
  display: block;
}

#validate-submit-section {
  display: flex;
  justify-content: space-between;
  margin-top: auto; /* Pushes the section to the bottom of the validation-menu-holder. */
}

.validation-menu-section-header {
  font: var(--text-small-bold);
  margin-bottom: calc(10px * var(--ui-scale));
}

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

/* The verdict thumbs, Back's arrow and the dock's close control are immersive mode's (svv-immersive.css); the
   column keeps its plain text buttons. */
.validate-page-button__icon,
.validate-undo-button__icon,
#validate-verdict-clear {
  display: none;
}

/* Overriding some of our design system tokens, though we could likely bring them more in line with DST. */
.validate-page-button {
  display: inline-block;
  width: calc(86px * var(--ui-scale));
  height: calc(32px * var(--ui-scale));
  border: 1px solid var(--color-asphalt-500);
  text-align: center;

  background-color: var(--color-neutral-white);
  outline: none; /* Removes focus outline that shows up when I press shift after clicking on a button. */
}

.validate-button:hover {
  border: none;
}

.validate-button.is-chosen {
  border: none;
}

#validate-yes-button.is-chosen {
  background: var(--color-pine-500);
}

#validate-no-button.is-chosen {
  background: var(--color-orange-400);
}

/* Unsure is the pale asphalt it is in Gallery, the label card, and mobile Validate. */
#validate-unsure-button.is-chosen {
  background: var(--color-asphalt-200);
}

/* Only show hover states on non-touch devices. */
@media (hover: hover) {
  #validate-yes-button:hover {
    background: var(--color-pine-300);
  }

  #validate-yes-button.is-chosen:hover {
    background: var(--color-pine-500);
  }

  #validate-no-button:hover {
    background: var(--color-orange-300);
  }

  #validate-no-button.is-chosen:hover {
    background: var(--color-orange-400);
  }

  #validate-unsure-button:hover {
    background: var(--color-asphalt-100);
  }

  #validate-unsure-button.is-chosen:hover {
    background: var(--color-asphalt-200);
  }
}

#sidewalk-ai-suggestions-block {
  margin-top: calc(15px * var(--ui-scale));
  margin-bottom: calc(15px * var(--ui-scale));
}

.sidewalk-ai-suggestions-header {
  font: var(--text-tiny-semibold);
  margin-bottom: calc(6px * var(--ui-scale));
}

.ai-icon {
  width: calc(16px * var(--ui-scale));
  height: calc(16px * var(--ui-scale));
}

.sidewalk-ai-suggestions-list-holder {
  display: flex;
  gap: calc(5px * var(--ui-scale));
  flex-wrap: wrap;
}

/* Shared pill look from tag-pills.css; these modifiers add the AI add/remove color semantics. The pill text is set
   directly on the element (no label span), so the pill font lives here. */
.sidewalk-ai-suggested-tag {
  font: var(--text-tiny-semibold);
  cursor: pointer;
  transition: background 0.3s; /* Animating the outline too makes it flash as hover leaves. */
}

.sidewalk-ai-suggested-tag.to-add {
  outline-color: var(--color-pine-700);
  color: var(--color-pine-700);
}

.sidewalk-ai-suggested-tag.to-remove {
  outline-color: var(--color-orange-500);
  color: var(--color-orange-500);
}

.sidewalk-ai-suggested-tag.to-add:hover {
  background: rgb(from var(--color-pine-500) r g b / 21%);
  outline-color: var(--color-pine-600);
}

.sidewalk-ai-suggested-tag.to-remove:hover {
  background: rgb(from var(--color-orange-500) r g b / 15%);
  outline-color: var(--color-orange-500);
}

.validate-text-input {
  width: 100%;
  margin-top: calc(10px * var(--ui-scale));
}

.validate-text-input input, .ts-control {
  display: flex;
  width: 100%;
  height: calc(36px * var(--ui-scale));
  padding: calc(8px * var(--ui-scale)) calc(18px * var(--ui-scale));
  align-items: center;
  gap: calc(4px * var(--ui-scale));
  flex-shrink: 0;
  border-radius: calc(6px * var(--ui-scale));
  border: calc(1px * var(--ui-scale)) solid var(--color-neutral-500);
  background: var(--color-neutral-white);
  font: var(--text-tiny-semibold);
}

/* Tom Select sets its own right padding from this variable, overriding the padding above. */
.ts-wrapper {
  --ts-pr-min: calc(18px * var(--ui-scale));
}

.ts-wrapper.dropdown-active .ts-control {
  border-radius: calc(6px * var(--ui-scale)) calc(6px * var(--ui-scale)) 0 0 !important;
}

.ts-dropdown {
  margin-top: 0;
  border-radius: 0 0 calc(6px * var(--ui-scale)) calc(6px * var(--ui-scale));
}

.ts-control input {
  font: var(--text-tiny-semibold);
}

/* Tom Select gives the typing box a pointer cursor by default. */
.ts-wrapper.single .ts-control input {
  cursor: text;
}

#current-tags-list {
  display: flex; /* display:none when there are no tags, display: flex when there are; updated in JS. */
  flex-wrap: wrap;
  gap: calc(5px * var(--ui-scale));
  margin-bottom: calc(10px * var(--ui-scale));
}

/* Shared pill look (including the active green background) from tag-pills.css; this only adds the X-to-remove gap. */
.current-tag.tag-pill {
  cursor: default;
}

.remove-tag-x {
  display: flex;
  padding: 0;
  border: none;
  background: none;
  color: var(--color-neutral-black);
  cursor: pointer;
}

/* The X is its own span because a mask hides everything it covers, which on the button would include its focus ring. */
.remove-tag-x__icon {
  width: calc(15px * var(--ui-scale));
  height: calc(15px * var(--ui-scale));
  -webkit-mask-image: url('/assets/images/icons/51f1808efa37a74f83be13de8f3c5abe-cross.svg');
  mask-image: url('/assets/images/icons/51f1808efa37a74f83be13de8f3c5abe-cross.svg');
}

.ts-dropdown-content {
  display: flex;
  width: calc(285px * var(--ui-scale));
  height: fit-content;
  max-height: calc(200px * var(--ui-scale)); /* Overrides the fixed 200px cap in Tom Select's stylesheet. */
  padding: calc(12px * var(--ui-scale));
  align-items: center;
  align-content: center;
  gap: calc(6px * var(--ui-scale));
  flex-wrap: wrap;
}

/* The dropdown options are styled as tag pills. These selectors need to out-rank Tom Select's own stylesheet. */
.ts-dropdown .option.tag-pill {
  padding: calc(5px * var(--ui-scale)) calc(10px * var(--ui-scale));
  font: var(--text-tiny-semibold);
  text-align: center;
  transition: background 0.3s; /* Animating the outline too makes it flash as hover leaves. */

  /* Tom Select wraps the matched text in a <span>, so .tag-pill's flex gap would show as a space around it (#4523). */
  gap: 0;
}

/* The arrow-key selection looks like hover. It stays on the last pill hovered, which is the one Enter picks. */
.ts-dropdown .option.tag-pill.active,
.ts-dropdown .option.tag-pill:hover {
  background: var(--color-pine-200);
  color: inherit;
  outline: none;
}

#severity-radio-holder {
  display: flex;
  align-items: center;
  gap: calc(16px * var(--ui-scale));
}

#optional-comment-input {
  margin-top: 0; /* Overrides .validate-text-input margin, since the textbox for agreeing looks different. */
}

#no-reason-options, #unsure-reason-options {
  display: flex;
  flex-direction: column;
  gap: calc(10px * var(--ui-scale));
}

.validation-reason-button {
  display: flex;
  height: calc(36px * var(--ui-scale));
  justify-content: center;
  align-items: center;
  border: calc(1px * var(--ui-scale)) solid var(--color-neutral-700);
  background-color: var(--color-neutral-white);
}

/* Matching style of 'No' and 'Unsure' button for now. */
#no-reason-options .validation-reason-button.is-chosen {
  background: var(--color-orange-400);
  border: none;
  color: var(--color-neutral-white);
}

#add-disagree-comment.is-chosen {
  border-color: var(--color-orange-400);
}

#unsure-reason-options .validation-reason-button.is-chosen {
  background: var(--color-asphalt-200);
  border: none;
}

#add-unsure-comment.is-chosen {
  border-color: var(--color-asphalt-300);
}

/* Only show hover states on non-touch devices. Same CSS as for chosen state. */
@media (hover: hover) {
  #no-reason-options .validation-reason-button:hover {
    background: var(--color-orange-300);
    border: none;
    color: var(--color-neutral-white);
  }

  #no-reason-options .validation-reason-button.is-chosen:hover {
    background: var(--color-orange-400);
  }

  #add-disagree-comment:hover {
    border-color: var(--color-orange-300);
  }

  #add-disagree-comment.is-chosen:hover {
    border-color: var(--color-orange-400);
  }

  #unsure-reason-options .validation-reason-button:hover {
    background: var(--color-asphalt-100);
    border: none;
  }

  #unsure-reason-options .validation-reason-button.is-chosen:hover {
    background: var(--color-asphalt-200);
  }

  #add-unsure-comment:hover {
    border-color: var(--color-asphalt-200);
  }

  #add-unsure-comment.is-chosen:hover {
    border-color: var(--color-asphalt-300);
  }
}

/* One 5:4 box for every example image; the odd one out is cropped to it rather than reshaping the card. */
.validate-tooltip-img {
  width: calc(200px * var(--ui-scale));
  aspect-ratio: 5 / 4;
  object-fit: cover;
}

/* A card holding one narrows to the image plus the card's 9px side padding (main.css), so the text wraps to the
   picture. */
.ps-tooltip:has(.validate-tooltip-img) {
  max-width: calc((200px + 2 * 9px) * var(--ui-scale));
}
.noselect {
  -webkit-touch-callout: none; /* iOS Safari */
  -webkit-user-select: none; /* Safari */
}

.mission-start-tutorial-overlay {
  position: absolute;
  height: calc(100% - var(--navbar-height));
  width: 100%;
  background: var(--color-neutral-white);
  z-index: 11;
  display: none; /* Will be later set to flex by JS after rendering. */
  flex-shrink: 0;
  justify-content: flex-start;
  overflow: auto;
  flex-direction: column;

  --arrow-button-width: calc(30px * var(--ui-scale, 1));
  --arrow-button-margin: calc(30px * var(--ui-scale, 1));

  --mst-base-width: 1095px;
  --mst-h-margin: 40px;
}

.mst-content {
  width: calc(var(--mst-base-width) * var(--ui-scale, 1));
  margin: 0 auto;
}

.mst-instruction-1 {
  font: var(--text-title-bold);
  margin-top: calc(8px * var(--ui-scale, 1));
  padding: 0 calc(70px * var(--ui-scale, 1));
  width: 100%;
  height: fit-content;
}


.mst-instruction-2 {
  font: var(--text-subtitle-bold);
  margin: calc(5px * var(--ui-scale, 1)) 0;
  padding: 0 calc(70px * var(--ui-scale, 1));
  width: 100%;
}

.mst-carousel {
  display: flex;
  flex-shrink: 0;
  width: 100%;
  height: calc(436px * var(--ui-scale, 1));
  margin-top: calc(10px * var(--ui-scale, 1));
}

.previous-slide-button, .next-slide-button {
  height: 100%;
  width: var(--arrow-button-width);
  display: flex;
  color: var(--color-neutral-700);
  cursor: pointer;
}

.previous-slide-button:not(.is-disabled):hover, .next-slide-button:not(.is-disabled):hover {
  color: var(--color-neutral-black);
}

.previous-slide-button {
  margin-right: var(--arrow-button-margin);
}

.next-slide-button {
  margin-left: var(--arrow-button-margin);
}

/* A mask rather than an <img> so the arrow picks up the button's hover color. The chevron fills only the middle third
   of its file, so the mask is drawn wider than the button to make the arrow span it. */
.mst-arrow-icon {
  width: 100%;
  height: 100%;
  -webkit-mask-size: calc(84px * var(--ui-scale, 1));
  mask-size: calc(84px * var(--ui-scale, 1));
}

.previous-slide-button .mst-arrow-icon {
  -webkit-mask-image: url('/assets/images/icons/56fedcabde9ff4eb08f32f3c897106e4-chevron-left-feather.svg');
  mask-image: url('/assets/images/icons/56fedcabde9ff4eb08f32f3c897106e4-chevron-left-feather.svg');
}

.next-slide-button .mst-arrow-icon {
  -webkit-mask-image: url('/assets/images/icons/2e52360e2f54db2850601e885f7b004d-chevron-right-feather.svg');
  mask-image: url('/assets/images/icons/2e52360e2f54db2850601e885f7b004d-chevron-right-feather.svg');
}

.mst-slide {
  height: 100%;
  display: flex;
  flex-shrink: 0;
  flex-wrap: wrap;
  width: calc(100% - 2 * (var(--arrow-button-width) + var(--arrow-button-margin)));
  justify-content: space-between;
}

.mst-text-area {
  width: calc(290px * var(--ui-scale, 1));
  display: flex;
  flex-shrink: 0;
  flex-flow: column wrap;
  position: relative;
}

.label-type-title {
  font: var(--text-title-bold);
  text-align: left;
  margin-bottom: calc(15px * var(--ui-scale, 1));
  padding: 0 calc(10px * var(--ui-scale, 1));
}

.label-type-subtitle {
  font: var(--text-body-bold);
  padding: 0 calc(10px * var(--ui-scale, 1));
}

.label-type-description {
  font: var(--text-body-regular);
  text-align: left;
  padding: 0 calc(10px * var(--ui-scale, 1));
  margin-top: calc(10px * var(--ui-scale, 1));
}

.mission-start-tutorial-overlay-footer {
  margin-top: calc(15px * var(--ui-scale, 1));
  width: 100%;
}

.mission-start-tutorial-done-btn {
  font: var(--text-small-bold);
  position: absolute;
  bottom: 0;
  right: 0;
}

.msts-image-area {
  width: calc(658px * var(--ui-scale, 1));
  height: calc(436px * var(--ui-scale, 1));
  float: right;
  background: var(--color-neutral-300);
  display: flex;
  position: relative;
  border-radius: calc(10px * var(--ui-scale, 1));
  overflow: hidden;
}

.msts-image {
  height: 100%;
  width: 100%;
  object-fit: contain;
}

.label-on-image {
  display: none;
  position: absolute;
  background: var(--color-neutral-white);
  border: calc(3px * var(--ui-scale, 1)) solid;
  border-radius: calc(10px * var(--ui-scale, 1));
  box-sizing: content-box;
  min-height: calc(80px * var(--ui-scale, 1));
  max-height: calc(90px * var(--ui-scale, 1));
  min-width: calc(160px * var(--ui-scale, 1));
  max-width: calc(220px * var(--ui-scale, 1));
}

/* correct/incorrect classes are added to the slide element */
.correct .label-on-image {
  border-color: var(--color-pine-700);
}

/* correct/incorrect classes are added to the slide element */
.incorrect .label-on-image {
  border-color: var(--color-error-300);
}

.label-on-image-type {
  margin: calc(10px * var(--ui-scale, 1));
  display: flex;
  align-items: center;
}

.label-on-image-type-icon {
  height: calc(24px * var(--ui-scale, 1));
  width: calc(24px * var(--ui-scale, 1));
  margin-right: calc(3px * var(--ui-scale, 1));
}

.label-on-image-type-title {
  display: flex;
  font: var(--text-body-bold);
  margin-left: calc(3px * var(--ui-scale, 1));
}

.correct .label-on-image-type-title {
  color: var(--color-pine-700);
}

.incorrect .label-on-image-type-title {
  color: var(--color-error-300);
}

.label-on-image-description {
  font: var(--text-body-regular);
  margin: 0 calc(12px * var(--ui-scale, 1));
}

.example-type-area {
  display: flex;
  padding: calc(10px * var(--ui-scale, 1));
  margin-bottom: calc(20px * var(--ui-scale, 1));
  position: relative;
}

.example-type-area::after {
  background: #30785D;
  background: var(--color-pine-700);
  width: calc(100% - 20px * var(--ui-scale, 1));
  height: calc(8px * var(--ui-scale, 1));
  position: absolute;
  bottom: calc(-6px * var(--ui-scale, 1));
  content: "";
  left: 50%;
  transform: translateX(-50%);
}

.correct .example-type-area::after {
  background: var(--color-pine-700);
}

.incorrect .example-type-area::after {
  background: var(--color-error-300);
}

.example-type-icon {
  height: calc(24px * var(--ui-scale, 1));
  width: calc(24px * var(--ui-scale, 1));
  margin-right: calc(5px * var(--ui-scale, 1));
}

.correct .label-on-image-type-icon,
.correct .example-type-icon {
  color: var(--color-pine-700);
  -webkit-mask-image: url('/assets/images/icons/smileys/2a64cb51e0d559fc545ddc9c90280352-sev-1-positive.svg');
  mask-image: url('/assets/images/icons/smileys/2a64cb51e0d559fc545ddc9c90280352-sev-1-positive.svg');
}

.incorrect .label-on-image-type-icon,
.incorrect .example-type-icon {
  color: var(--color-error-300);
  -webkit-mask-image: url('/assets/images/icons/smileys/7c43fff71ad36e6bd5db40f221897c35-sev-3-positive.svg');
  mask-image: url('/assets/images/icons/smileys/7c43fff71ad36e6bd5db40f221897c35-sev-3-positive.svg');
}

.example-type-label {
  font: var(--text-title-bold);
  display: inline-block;
  margin-left: calc(5px * var(--ui-scale, 1));
}

.correct .example-type-label {
  color: var(--color-pine-700);
}

.incorrect .example-type-label {
  color: var(--color-error-300);
}

.mst-carousel-location-indicator-area {
  height: calc(12px * var(--ui-scale, 1));
  width: 100%;
  padding: calc(5px * var(--ui-scale, 1));
  margin-top: calc(25px * var(--ui-scale, 1));
  display: flex;
  justify-content: center;
  align-items: center;
}

.mst-carousel-location-indicator {
  height: calc(7px * var(--ui-scale, 1));
  width: calc(40px * var(--ui-scale, 1));
  margin: 0 calc(3.5px * var(--ui-scale, 1));
  background: var(--color-neutral-400);
  transition: all 0.4s;
  border-radius: 100px;
}

.mst-carousel-location-indicator.current-location {
  background: var(--color-asphalt-500);
}

/*
 * Explore mission screen tab bar styling
 */
.explore-mission-start-tab-bar {
  width: 100%;
  height: calc(50px * var(--ui-scale, 1));
  display: flex;
  justify-content: space-between;
  margin: calc(5px * var(--ui-scale, 1)) 0;
  padding: 0 calc(2 * var(--arrow-button-margin));
}

.explore-mission-start-tab-content {
  margin: 0 calc(12px * var(--ui-scale, 1));
  display: flex;
  align-items: center;
}

.explore-mission-start-tab {
  border-radius: calc(4px * var(--ui-scale, 1)) calc(4px * var(--ui-scale, 1)) 0 0;
  height: 100%;
  width: 100%;
  color: var(--color-neutral-black);
  cursor: pointer;
  display: flex;
  justify-content: center;
  align-items: center;
  font: var(--text-tiny-semibold);
  text-align: left;
  border-bottom: calc(3px * var(--ui-scale, 1)) solid var(--color-neutral-200);
  padding: 0 calc(3px * var(--ui-scale, 1));
  white-space: nowrap;
  filter: saturate(0);
  transition: all 0.25s;
}

.explore-mission-start-tab:hover, .explore-mission-start-tab.is-active {
  filter: saturate(1);
}

.explore-mission-start-tab[data-label-type="CurbRamp"]:hover {
  background: rgb(from var(--color-label-curb-ramp) r g b / 15%);
}

.explore-mission-start-tab[data-label-type="NoCurbRamp"]:hover {
  background: rgb(from var(--color-label-no-curb-ramp) r g b / 15%);
}

.explore-mission-start-tab[data-label-type="Obstacle"]:hover {
  background: rgb(from var(--color-label-obstacle) r g b / 15%);
}

.explore-mission-start-tab[data-label-type="SurfaceProblem"]:hover {
  background: rgb(from var(--color-label-surface-problem) r g b / 15%);
}

.explore-mission-start-tab[data-label-type="NoSidewalk"]:hover {
  background: rgb(from var(--color-label-no-sidewalk) r g b / 15%);
}

.explore-mission-start-tab[data-label-type="Crosswalk"]:hover {
  background: rgb(from var(--color-label-crosswalk) r g b / 15%);
}

.explore-mission-start-tab[data-label-type="Signal"]:hover {
  background: rgb(from var(--color-label-signal) r g b / 15%);
}


/* Selected tabs */
.explore-mission-start-tab[data-label-type="CurbRamp"].is-active {
  background: rgb(from var(--color-label-curb-ramp) r g b / 15%);
  border-bottom-color: var(--color-label-curb-ramp);
}

.explore-mission-start-tab[data-label-type="NoCurbRamp"].is-active {
  background: rgb(from var(--color-label-no-curb-ramp) r g b / 15%);
  border-bottom-color: var(--color-label-no-curb-ramp);
}

.explore-mission-start-tab[data-label-type="Obstacle"].is-active {
  background: rgb(from var(--color-label-obstacle) r g b / 15%);
  border-bottom-color: var(--color-label-obstacle);
}

.explore-mission-start-tab[data-label-type="SurfaceProblem"].is-active {
  background: rgb(from var(--color-label-surface-problem) r g b / 15%);
  border-bottom-color: var(--color-label-surface-problem);
}

.explore-mission-start-tab[data-label-type="NoSidewalk"].is-active {
  background: rgb(from var(--color-label-no-sidewalk) r g b / 15%);
  border-bottom-color: var(--color-label-no-sidewalk);
}

.explore-mission-start-tab[data-label-type="Crosswalk"].is-active {
  background: rgb(from var(--color-label-crosswalk) r g b / 15%);
  border-bottom-color: var(--color-label-crosswalk);
}

.explore-mission-start-tab[data-label-type="Signal"].is-active {
  background: rgb(from var(--color-label-signal) r g b / 15%);
  border-bottom-color: var(--color-label-signal);
}


.explore-mission-start-tab-icon {
  width: calc(30px * var(--ui-scale, 1));
  height: calc(30px * var(--ui-scale, 1));
  margin-right: calc(7px * var(--ui-scale, 1));
  flex-shrink: 0; /* Keep the icon at full size when the tab row gets squeezed, so it isn't clipped. */
  display: flex;
  justify-content: center;
  align-items: center;
}

/* The map-marker badges have no ring of their own, so the tabs' dark ring is drawn here. */
.explore-mission-start-tab-icon img {
  box-sizing: border-box;
  width: 100%;
  height: 100%;
  border: calc(2px * var(--ui-scale, 1)) solid var(--color-neutral-black);
  border-radius: 50%;
}

.explore-mission-start-tab-text {
  width: 70%;
  white-space: initial;
}
