
/* The toggle is built from data, so the container is empty on first paint
   and everything below it jumps down when the buttons arrive. Reserving
   one button row keeps the page still: one line of --text-sm, plus the
   pill's padding top and bottom, plus its 1px borders.

   The reservation is DERIVED from the padding rather than restated as a
   number. It used to read `+ 14px`, which was 6 + 6 + 1 + 1 worked out by
   hand — so changing the pill's padding silently broke the reservation
   and the page started jumping again. */
.chart-toggle-group {
  /* The row has to lay itself out. Without this the group is a block box
     and the pills are inline-flex in normal flow, so the space between
     them is whatever whitespace happens to sit between the buttons in
     the source — 3.09px on the period row, whose template literal has
     newlines between buttons, and 0px on the region row, whose JS joins
     them with ''. Two rows of the same control, in the same bar, spaced
     differently by an accident of string building.

     It looked correct on phones only because .ps-pills declares its own
     flex + gap under 720px. This makes the desktop match. */
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2);

  min-height: calc(var(--text-sm) * 1.2 + var(--control-py-pill) * 2 + 2px);
}

.chart-toggle {
  display: inline-flex;
  align-items: center;
  /* Tighter than the gap BETWEEN pills (--space-2), so the count binds to
     its own label rather than floating equidistant from the next pill. */
  gap: var(--space-1);
  padding: var(--control-py-pill) var(--control-px-pill);
  border: 1px solid var(--line-strong);
  background: var(--surface-1);
  color: var(--text-soft);
  border-radius: var(--radius-pill);
  font: 500 var(--text-sm) / 1.2 var(--font-body);
  cursor: pointer;
}

@media (prefers-reduced-motion: no-preference) {
  .chart-toggle {
    transition: background var(--dur-fast) var(--ease-out),
                color var(--dur-fast) var(--ease-out),
                border-color var(--dur-fast) var(--ease-out);
  }
}

.chart-toggle:hover {
  color: var(--text);
  border-color: var(--text-soft);
}

.chart-toggle[aria-selected="true"] {
  background: var(--primary-soft);
  border-color: var(--primary);
  color: var(--primary-bright);
}

/* WHICH PILL IS ON, WHEN THE SYSTEM IS PICKING THE COLORS. All three
   properties above are colors, and Windows high contrast replaces every
   one of them with its own palette — so in forced-colors mode the five
   period pills and the four unit pills rendered identically and nothing
   on screen said which window the page was reading. aria-selected still
   told a screen reader; a sighted low-vision reader, who is most of the
   audience for this mode, was left to guess.

   Highlight/HighlightText are the system's own selected-item pair and
   are honored rather than overridden, so the selected pill is as legible
   as the user's chosen theme allows and still obviously selected. This
   is the platform's own rendering of a selected item, not a look we
   invented for it.

   forced-color-adjust: none is doing something narrower here than its
   name suggests, and it is the reason the label is readable. Chromium
   paints a Canvas-colored BACKPLATE behind text in forced-colors mode so
   that text over a background image stays legible. On an unselected pill
   that backplate is Canvas over Canvas and invisible; on a pill filled
   with Highlight it is a white slab across the middle of the button, and
   the HighlightText label — white, by definition, against Highlight —
   disappears into it. The pill rendered as a solid block with a blank
   rectangle punched out of it and no word on it at all.

   Turning the adjustment off suppresses the backplate. It does NOT
   reintroduce a color of ours: every value below is a system color, so
   the pair is still whatever the reader's own high-contrast theme
   chose. The one thing it does let back through is the gold focus ring
   from the rule beneath, which WOULD be a color of ours, so that is
   restored to a system color explicitly. */
@media (forced-colors: active) {
  .chart-toggle[aria-selected="true"] {
    background: Highlight;
    border-color: Highlight;
    color: HighlightText;
    forced-color-adjust: none;
  }
  .chart-toggle[aria-selected="true"]:focus-visible {
    outline-color: CanvasText;
  }
}

.chart-toggle:focus-visible {
  outline: 2px solid var(--gold);
  outline-offset: 2px;
}

/* ── Touch targets ──────────────────────────────────────────
   The pills size to their text and their inset ladder, which lands them
   at 40px for a word and 30px for "$" or "km" — under the 44px floor,
   and the short ones worst, because a one-character pill is also the
   narrowest thing to aim at.

   min-height rather than more padding: the inset ladder
   (--control-p{y,x}-pill) is deliberately keyed to type size rather
   than the layout grid, so growing the padding here would break the
   one thing it exists to hold. A floor leaves the ladder intact and
   lets align-items keep the label centred in whatever height results.

   This costs the settings bar nothing now that the period strip is a
   single scrolling row again: one row of 44 where there used to be
   three of 40.

   The max-height arm carries the landscape phone, which is 852px wide
   and so passes straight through every width query on the site. */
@media (hover: none) and (pointer: coarse), (max-width: 720px), (max-height: 500px) {
  .chart-toggle {
    min-height: 44px;
    /* The locale pills are "$", "€", "mi", "km" — one or two characters,
       which the inset ladder renders 38px wide. Height alone does not
       make those hittable, and they are the pills a reader uses while
       holding the phone one-handed. */
    min-width: 44px;
    justify-content: center;
    /* A pill in a scrolling strip must not be squeezed by its siblings;
       without this the five period pills compress toward the container
       instead of overflowing it, and the strip has nothing to scroll. */
    flex: 0 0 auto;
  }
}

/* The count beside a region name on the ranking toggles. Tabular figures
   so the buttons do not resize as the period toggle changes the measured
   count under them, and a step down in weight and opacity so the region
   reads first and the number reads as its footnote. */
.chart-toggle-count {
  font-variant-numeric: tabular-nums;
  font-weight: 400;
  opacity: 0.65;
}

.chart-toggle[aria-selected="true"] .chart-toggle-count {
  opacity: 0.8;
}

/* ============================================================
   THE SAME PILL, AS A MENU BUTTON — the continent filter in the
   sticky settings bar (js/ps-select.js).

   A second axis does not fit in a row beside five period pills and
   four locale ones, so it collapses to one trigger. It is NOT a new
   widget: the trigger carries .chart-toggle and inherits every rule
   above, so
   it has the pill's border, padding, radius, type, hover and selected
   states by construction rather than by a second set of values kept in
   sync with them. Only the caret, the popup and the anchoring live
   here.
   ============================================================ */

.ps-select {
  position: relative;
  display: inline-flex;
  flex: 0 0 auto;
}

/* The label sits between the fixed-width count and caret, so a long
   option name shortens rather than pushing the caret out of the pill. */
.ps-select-trigger { max-width: min(320px, 60vw); }
.ps-select-value {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.ps-select-caret {
  width: 12px;
  height: 12px;
  flex: 0 0 auto;
  /* Optical, not metric: the caret is a lighter mark than the words it
     follows and reads as attached to the pill's edge without it. */
  margin-left: 2px;
  opacity: 0.7;
}
@media (prefers-reduced-motion: no-preference) {
  .ps-select-caret { transition: transform var(--dur-fast) var(--ease-out); }
}
.ps-select-trigger[aria-expanded="true"] .ps-select-caret { transform: rotate(180deg); }

/* ── The popup ───────────────────────────────────────────
   Anchored to the trigger, not to the bar: .ps-select is the positioned
   ancestor. --z-popover so it clears anything inside the bar, which is
   itself a stacking context at --z-sticky — the menu opens DOWNWARD, so
   the fixed header above it is never in question.

   data-align is set by js/ps-select.js after measuring: a menu whose
   trigger sits near the right end of the bar would otherwise hang off
   the page. */
.ps-select-menu {
  position: absolute;
  top: calc(100% + var(--space-2));
  left: 0;
  z-index: var(--z-popover);
  display: flex;
  flex-direction: column;
  min-width: 100%;
  width: max-content;
  max-width: min(320px, calc(100vw - var(--space-5) * 2));
  padding: var(--space-2);
  background: var(--surface-1);
  border: 1px solid var(--line-strong);
  /* --radius-md, not --lg: the card radius on a 200px-wide popup eats
     into the first and last option's corners. The options take the pill
     radius the triggers have, so the popup reads as the row it came
     from. */
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-pop);
}
.ps-select-menu[hidden] { display: none; }
.ps-select[data-align="end"] .ps-select-menu { left: auto; right: 0; }

.ps-select-option {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
  padding: var(--control-py-pill) var(--space-3);
  border: 0;
  border-radius: var(--radius-pill);
  background: transparent;
  color: var(--text-soft);
  font: 500 var(--text-sm) / 1.2 var(--font-body);
  text-align: left;
  white-space: nowrap;
  cursor: pointer;
}
.ps-select-option:hover { background: var(--hover-tint); color: var(--text); }
.ps-select-option:focus-visible {
  outline: 2px solid var(--gold);
  outline-offset: -2px;
}
.ps-select-option[aria-checked="true"] {
  background: var(--primary-soft);
  color: var(--primary-bright);
}

/* The option label may ellipsize; the count never does. */
.ps-select-option-label { overflow: hidden; text-overflow: ellipsis; }

/* The option rows are the half of this control a finger actually lands
   on, and they were the half that did not grow. The TRIGGER is a
   .chart-toggle, so it takes the 44px floor from the touch block above
   for free; an option is not a pill and took nothing, leaving a 30px
   row inside a 44px control. Same three-arm query as that block —
   coarse pointer OR narrow OR short — because a touch tablet at 800px
   matches none of the width tests and needs this most. */
@media (hover: none) and (pointer: coarse), (max-width: 720px), (max-height: 500px) {
  .ps-select-option {
    min-height: 44px;
    /* The vertical inset is what the min-height now provides; keeping
       the pill's padding on top of it would make a 58px row. */
    padding-block: 0;
  }
}

/* On a phone the bar is a stretched column and the pills scroll
   horizontally. A trigger is not a strip, so it takes the full row and
   the menu with it — a 44px target rather than a pill floating at the
   left of an empty line. */
@media (max-width: 720px) {
  .ps-select { display: flex; flex: 1 1 auto; min-width: 0; }
  /* .ps-bar-scoped, because page-settings.css sizes `.ps-bar
     .chart-toggle` at this breakpoint and pins it to `flex: 0 0 auto` for
     the scrolling pill strip. That is two class selectors; a bare
     .ps-select-trigger is one and loses regardless of load order. The
     height and padding come from that rule and are not restated here. */
  .ps-bar .ps-select-trigger {
    flex: 1 1 auto;
    max-width: none;
    justify-content: space-between;
  }
  .ps-select-menu { left: 0; right: 0; max-width: none; }
}

/* ============================================================
   THE SAME PILL, AS ONE SYMBOL — prices and units on a phone
   (js/locale.js).

   Currency and units are two options each, and below
   COMPACT_CONTROLS_QUERY drawing both of them twice over costs two
   44px targets to say something the reader can already see. So the
   compact shape is the setting in force and nothing else: "$", "km".

   SQUARE, NOT A STRETCHED PILL. It printed its destination as well —
   "$ ⇄ €" — and stretched to half the bar's width, which made the
   smallest setting on the page the largest thing in the sticky bar
   (2026-10-05 review). One glyph needs the touch target and not a
   millimetre more; the width it gives back is what lets the locale
   pair ride beside the period trigger instead of taking a row.

   Like .ps-select above it is NOT a new widget. The button carries
   .chart-toggle and inherits the border, radius, type, hover and
   focus ring from the rules at the top of this file.

   Unstyled by [aria-selected], deliberately. A switch is never
   "selected" — it is always in one of its two states, and painting it
   with the selected pill's fill would say the opposite of what the
   unfilled period and continent triggers beside it mean.
   ============================================================ */

.ps-switch {
  justify-content: center;
  /* Square on its touch floor and not a pixel over. The inline padding
     is overridden so a one-character label does not sit in a pill
     shaped for a sentence, and min-width matches the 44px height: at
     13px bold "km" measures 19px, so both squares land on the floor
     and neither is wider than the other. It was 48px, which bought
     nothing but 8px of a row that turned out to be 8px short
     (2026-10-05). */
  padding-inline: var(--space-2);
  min-width: 44px;
  white-space: nowrap;
}
/* The state in force reads as the pill's own label. */
.ps-switch-value { font-weight: 700; }
