/* =============================================================================
   PrivacyWall Design System — Components
   Requires: pw-tokens.css, pw-base.css
   Behaviour for the interactive parts lives in pw-components.js
   ============================================================================= */

/* =============================================================================
   BUTTON
   Variants  primary · secondary · tertiary · ghost · danger · link
   Sizes     sm (32) · md (40) · lg (44) · xl (48)
   States    rest · hover · active · focus-visible · disabled · loading
   ============================================================================= */
.pw-btn {
  --pw-btn-height: 40px;
  --pw-btn-px: var(--pw-space-8);
  --pw-btn-gap: var(--pw-space-4);
  --pw-btn-font: var(--pw-text-sm-size);
  --pw-btn-radius: var(--pw-radius-pill);   /* fully rounded is the default */

  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--pw-btn-gap);
  height: var(--pw-btn-height);
  padding-inline: var(--pw-btn-px);
  border: 1px solid transparent;
  border-radius: var(--pw-btn-radius);
  background: transparent;
  color: inherit;
  font-size: var(--pw-btn-font);
  font-weight: var(--pw-font-weight-bold);
  line-height: 1;
  white-space: nowrap;
  cursor: pointer;
  user-select: none;
  text-decoration: none;
  transition:
    background-color var(--pw-duration-fast) var(--pw-ease-out),
    border-color     var(--pw-duration-fast) var(--pw-ease-out),
    color            var(--pw-duration-fast) var(--pw-ease-out),
    box-shadow       var(--pw-duration-fast) var(--pw-ease-out);
}
.pw-btn:focus-visible { outline: 2px solid var(--pw-color-brand-600); outline-offset: 2px; }

/* -- Sizes ------------------------------------------------------------------ */
/* XS is for actions that sit INSIDE a small panel -- a popover's presets, a
   card's secondary row -- where a 14px label outweighs the panel's own title.
   Not for a page's primary action: 28px is the smallest box a pointer should
   be asked to hit, and 12px the smallest label the system sets. Semibold, not
   bold: at 12px bold closes the counters up and reads as a smudge. */
.pw-btn--xs { --pw-btn-height: 28px; --pw-btn-px: var(--pw-space-5); --pw-btn-font: var(--pw-text-xs-size); --pw-btn-gap: var(--pw-space-2); font-weight: var(--pw-font-weight-semibold); }
.pw-btn--sm { --pw-btn-height: 32px; --pw-btn-px: var(--pw-space-6); --pw-btn-font: var(--pw-text-sm-size); --pw-btn-gap: var(--pw-space-3); }
.pw-btn--md { --pw-btn-height: 40px; }
.pw-btn--lg { --pw-btn-height: 44px; --pw-btn-px: var(--pw-space-10); --pw-btn-font: var(--pw-text-md-size); }
.pw-btn--xl { --pw-btn-height: 48px; --pw-btn-px: var(--pw-space-12); --pw-btn-font: var(--pw-text-md-size); }

/* -- Shapes -----------------------------------------------------------------
   `--pill` stays as a no-op alias so existing markup keeps working.
   `--squared` is the escape hatch for the two places a pill cannot go: a welded
   button group, and a control that shares an edge with a flat surface. */
.pw-btn--pill    { --pw-btn-radius: var(--pw-radius-pill); }
.pw-btn--squared { --pw-btn-radius: var(--pw-radius-md); }
.pw-btn--block   { display: flex; width: 100%; }

/* -- Variant: social --------------------------------------------------------
   "Continue with Google" and its siblings. These are always read as a stack,
   and a stack is where the ordinary button fails: `justify-content: center`
   centres the mark AND the label together, so every row's text starts at a
   different x and the column has no left edge to read down. The grid fixes
   that without absolute positioning — the mark holds the first track, the
   label centres in the second, and a third track the same width as the first
   balances it, so the label is optically centred in the button rather than in
   what is left of it.

   Deliberately not a colour variant. Pair it with `--secondary` and the mark
   is the only colour on the row, which is what a sign-in stack wants: six
   providers competing at full saturation is a decision the reader cannot make.
   `--brandfill`, with the colour variants below, is for the one provider whose
   own terms require it. */
.pw-btn--social {
  display: grid;
  grid-template-columns: var(--pw-icon-md) 1fr var(--pw-icon-md);
  gap: 0;
  justify-items: center;
  font-weight: var(--pw-font-weight-medium);
}
/* Never wider than what holds it. The label sits in a `1fr` column between two
   icon columns, so the intrinsic width is the longest label plus both gutters —
   428px for "Continue with Microsoft", which is more than a phone. The cap lets
   the middle column give way instead of the page. */
.pw-btn--social { max-width: 100%; min-width: 0; }
.pw-btn--social.pw-btn--block { width: 100%; }
.pw-btn--social > :first-child { justify-self: start; }
.pw-btn--social .pw-brand-mark, .pw-btn--social .pw-brand-glyph { grid-column: 1; }


/* -- Variant: primary ------------------------------------------------------- */
.pw-btn--primary {
  background: var(--pw-action-bg);
  border-color: var(--pw-action-border);
  color: var(--pw-action-fg);
  box-shadow: var(--pw-shadow-xs);
}
.pw-btn--primary:hover  { background: var(--pw-action-bg-hover); }
.pw-btn--primary:active { background: var(--pw-action-bg-active); box-shadow: none; }

/* -- Variant: secondary ----------------------------------------------------- */
.pw-btn--secondary {
  background: var(--pw-action-2-bg);
  border-color: var(--pw-action-2-border);
  color: var(--pw-action-2-fg);
  box-shadow: var(--pw-shadow-xs);
}
.pw-btn--secondary:hover  { background: var(--pw-action-2-bg-hover); border-color: var(--pw-action-2-border-hover); }
.pw-btn--secondary:active { background: var(--pw-action-2-bg-active); box-shadow: none; }

/* -- Variant: tertiary (filled neutral, no border) -------------------------- */
.pw-btn--tertiary {
  background: var(--pw-bg-muted);
  color: var(--pw-fg-secondary);
}
.pw-btn--tertiary:hover  { background: var(--pw-bg-strong); color: var(--pw-fg-primary); }
.pw-btn--tertiary:active { background: var(--pw-action-3-bg-active); }

/* -- Variant: ghost (no chrome until hover) --------------------------------- */
.pw-btn--ghost { color: var(--pw-action-3-fg); }
.pw-btn--ghost:hover  { background: var(--pw-action-3-bg-hover); color: var(--pw-fg-primary); }
.pw-btn--ghost:active { background: var(--pw-action-3-bg-active); }

/* -- Variant: danger -------------------------------------------------------- */
.pw-btn--danger {
  background: var(--pw-color-danger-600);
  color: var(--pw-color-white);
  box-shadow: var(--pw-shadow-xs);
}
.pw-btn--danger:hover  { background: var(--pw-color-danger-700); }
.pw-btn--danger:active { background: var(--pw-color-danger-800); box-shadow: none; }

.pw-btn--danger-quiet {
  background: transparent;
  border-color: var(--pw-border-danger);
  color: var(--pw-fg-danger);
}
.pw-btn--danger-quiet:hover { background: var(--pw-color-danger-25); }
html.dark .pw-btn--danger-quiet:hover, [data-pw-theme="dark"] .pw-btn--danger-quiet:hover { background: rgba(240, 68, 56, 0.12); }

/* -- Variant: link ---------------------------------------------------------- */
/* A link button sits in a row of chips and labels, not in a wall of body
   text, so it takes the darker end of the brand ramp: brand-600 against a
   white card is only 3.2:1, which fails for a control this small. */
.pw-btn--link {
  height: auto; padding: 0; border: 0; background: none;
  color: var(--pw-action-link);
  font-weight: var(--pw-font-weight-bold);
}
.pw-btn--link:hover { color: var(--pw-action-link-hover); text-decoration: underline; text-underline-offset: 2px; }
.pw-btn--link:active { color: var(--pw-action-link-active); }

/* Neutral link: the same shape, without spending the brand colour.
 *
 * Use it wherever a text action sits beside controls that are already
 * carrying meaning — the filter bar is the case this exists for. Blue there
 * competes with the current vertical and with every result title below, and
 * "Advanced Search" is not more important than either. It is still obviously
 * a control: semibold, and it underlines on hover. */
.pw-btn--link-neutral {
  height: auto; padding: 0; border: 0; background: none;
  color: var(--pw-fg-secondary);
  font-weight: var(--pw-font-weight-bold);
}
.pw-btn--link-neutral:hover {
  color: var(--pw-fg-primary);
  text-decoration: underline;
  text-underline-offset: 2px;
}

/* The brand's own fill, for the providers that publish one and mean it. Both
   colours come from the call site, because they belong to the trademark owner
   and not to us — there is no token for them and there should not be: a brand
   colour that entered our ramps would start theming, and a sign-in button that
   is a different blue in dark mode is no longer the brand's button. Fixed by
   design, in both themes, on purpose. */
.pw-btn--brandfill {
  background: var(--pw-brand-bg);
  color: var(--pw-brand-fg, var(--pw-color-white));
  border-color: var(--pw-brand-bg);
}
.pw-btn--brandfill:hover  { filter: brightness(0.93); }
.pw-btn--brandfill:active { filter: brightness(0.87); }

/* A share row: neutral until you reach for one, then the brand's own colour.
   This is where the mask route pays for itself — the glyph is `currentColor`,
   so it turns white inside the fill on its own, with no second file and no
   swap to manage. An <img> of the coloured mark cannot do it. */
.pw-btn--brandhover:hover {
  background: var(--pw-brand-bg);
  border-color: var(--pw-brand-bg);
  color: var(--pw-brand-fg, var(--pw-color-white));
}

/* A stack of sign-in buttons. Full width and one gap, because the choice being
   offered is between the labels and nothing else. */
.pw-btn-stack { display: grid; gap: var(--pw-space-4); }
.pw-btn-stack > .pw-btn { width: 100%; }
.pw-btn--link-neutral:active { color: var(--pw-fg-primary); }
.pw-btn--link-neutral:disabled,
.pw-btn--link-neutral[aria-disabled="true"] { color: var(--pw-fg-disabled); background: transparent; }

/* -- States ----------------------------------------------------------------- */
.pw-btn:disabled,
.pw-btn[aria-disabled="true"] {
  cursor: not-allowed;
  box-shadow: none;
  background: var(--pw-bg-disabled);
  border-color: transparent;
  color: var(--pw-fg-disabled);
}
.pw-btn--ghost:disabled,
.pw-btn--link:disabled { background: transparent; }

.pw-btn.is-loading { cursor: progress; pointer-events: none; }
.pw-btn.is-loading .pw-btn__label,
.pw-btn.is-loading .pw-icon:not(.pw-btn__spinner) { opacity: 0; }
.pw-btn__spinner {
  position: absolute;
  inset: 0;
  margin: auto;
  width: 18px; height: 18px;
  opacity: 0;
  animation: pw-spin 900ms linear infinite;
}
.pw-btn.is-loading .pw-btn__spinner { opacity: 1; }

/* -- Icon-only -------------------------------------------------------------- */
.pw-btn--icon {
  --pw-btn-px: 0;
  width: var(--pw-btn-height);
  padding: 0;
}
.pw-btn--icon { border-radius: var(--pw-radius-circle); }
.pw-btn--icon.pw-btn--squared { border-radius: var(--pw-radius-md); }

/* -- The glyph size of a label-less control --------------------------------
   One decision, one place, for every control that has no word beside it: the
   icon button, the icon-only chip, the overflow trigger, a pinned Tools
   button. Where an icon accompanies a label it is an accent and takes `sm`;
   alone it IS the control's meaning and takes `md`, or the row reads as a
   smudge inside a circle.

   The chips cannot literally BE `.pw-btn` — a chip is a `.pw-tab`, and the two
   bases disagree about height, padding, weight and the underline — so what is
   shared is the decision rather than the class. Sizes that are not the default
   say so here, and nowhere else.

   This is deliberately not left to the markup: `pw-icon--sm` is what gets
   copied, because it is what the labelled chip beside it uses.
   -------------------------------------------------------------------------- */
.pw-btn--icon,
.pw-vertical--icon,
.pw-tab--icon { --pw-glyph: var(--pw-icon-md); }
.pw-btn--sm.pw-btn--icon { --pw-glyph: var(--pw-icon-sm); }
.pw-btn--xl.pw-btn--icon { --pw-glyph: var(--pw-icon-lg); }
.pw-btn--icon > .pw-icon,
.pw-vertical--icon > .pw-icon,
.pw-tabs--chip .pw-tab--icon > .pw-icon {
  width: var(--pw-glyph);
  height: var(--pw-glyph);
  /* The reset caps every svg at `max-width: 100%`, which is right for content
     images and wrong for a glyph: inside a fixed-width circle it lets the
     control's padding shrink the icon instead of overflowing it, so the icon
     silently gets smaller the tighter the button. A sized glyph is sized. */
  max-width: none;
}

/* -- Emoji -------------------------------------------------------------------
   A picture, not a character. The platform's own emoji font draws the same
   codepoint four different ways across four operating systems, none of them
   ours and none of them reviewable — and a codepoint the platform has no glyph
   for renders as a hollow box, which is what a half-written flag did in the
   holidays list for as long as nobody looked.

   `<img>`, exactly as the coloured brand marks are, and for the same reason:
   these carry their own palettes and have no `currentColor` to follow. See
   assets/emoji/README.md.

   Sized on the TEXT it sits in — 1em, with the baseline nudge that makes a
   square box sit like a glyph rather than on the line — so a row of names with
   an emoji each has one baseline and no reflow when the font size changes. */
.pw-emoji {
  display: inline-block;
  width: 1em; height: 1em;
  /* A flag is not square; every other emoji is. `contain` letterboxes the odd
     one out instead of stretching it into a shape it is not. */
  object-fit: contain;
  vertical-align: -0.14em;
  /* An emoji is content, not chrome: it must not inherit the icon system's
     stroke, and the reset's `max-width: 100%` must not shrink it inside a
     tight box the way it does a content image. */
  max-width: none;
  flex: 0 0 auto;
  user-select: none;
}
/* Both bigger than the base, and named for it. `--sm` was here and was 1.25em:
   a modifier called small that made the thing larger, which is the kind of name
   that survives until somebody uses it and gets the opposite of what they
   asked for. */
.pw-emoji--lg { width: 1.25em; height: 1.25em; }
.pw-emoji--xl { width: 1.6em;  height: 1.6em; }
/* Sizes that do NOT follow the text, for the places an emoji is the object on
   the surface rather than a mark inside a sentence. */
/* The floor, for a character Noto does not draw. It keeps `.pw-emoji` so the
   surrounding layout is unchanged, but it is TEXT now — and a 1em box is the
   wrong box for a glyph most fonts draw slightly wider than its em, so the size
   goes back to the text's own. */
.pw-emoji[data-pw-emoji-fallback] {
  width: auto; height: auto;
  line-height: 1;
}
.pw-emoji--24 { width: 24px; height: 24px; vertical-align: middle; }
.pw-emoji--32 { width: 32px; height: 32px; vertical-align: middle; }
.pw-emoji--48 { width: 48px; height: 48px; vertical-align: middle; }

/* -- Floating on top of content --------------------------------------------
   A button that floats above content instead of sitting inside a row has to
   supply its own edge, and which edge is correct depends entirely on what is
   behind it. So there are two variants, and the HOST picks — never the
   component:

     --float        neutral host (paper, gray-50, a plain card, a light map
                    tile you control): hairline border + small shadow.
     --float-media  coloured or photographic host (imagery, gradients, video,
                    brand fills, any surface the product does not choose):
                    NO border, shadow only.

   The border is calibrated against neutral surfaces, which is exactly why it
   fails on a coloured one. Over a mid-grey photo it disappears; over a
   saturated fill it reads as a dirty grey ring belonging to no palette; and
   because the imagery moves, it looks different on every frame. A shadow has
   none of those problems: it darkens whatever is already there, so it holds
   its edge on any hue, in both themes.

   The tokens differ too — `--pw-shadow-overlay` is heavier than `sm` and
   carries a tight contact layer, because here it is doing the border's job
   rather than sitting next to it.
   -------------------------------------------------------------------------- */
.pw-btn--float,
.pw-btn--float-media {
  background: var(--pw-bg-surface);
  color: var(--pw-fg-secondary);
}
.pw-btn--float       { border: 1px solid var(--pw-border-subtle); box-shadow: var(--pw-shadow-sm); }
.pw-btn--float-media { border: 0;                                 box-shadow: var(--pw-shadow-overlay); }

.pw-btn--float:hover,
.pw-btn--float-media:hover { background: var(--pw-bg-hover); color: var(--pw-fg-primary); }
.pw-btn--float:active,
.pw-btn--float-media:active { background: var(--pw-bg-active); }

.pw-btn--float[aria-pressed="true"],
.pw-btn--float-media[aria-pressed="true"] {
  background: var(--pw-bg-selected);
  color: var(--pw-fg-brand);
}

/* `.pw-btn` focuses with a 2px outline at a 2px offset. Over imagery that gap
   shows the photo, so a blue outline on a blue sky separates from nothing —
   fill the gap with the surface colour. */
.pw-btn--float-media:focus-visible {
  box-shadow: 0 0 0 2px var(--pw-bg-surface), var(--pw-shadow-overlay);
}

.pw-btn--float:disabled,
.pw-btn--float-media:disabled { color: var(--pw-fg-disabled); box-shadow: none; }

/* -- Trailing chevron on a menu button -------------------------------------- */
/* The negative margin pulls a trailing caret closer to the label it follows.
   In an icon-only button there is no label, so it just shifts the glyph 2px
   off-centre inside a circle — small enough to read as a rendering artefact
   rather than a mistake, which is why it survived this long. */
.pw-btn__caret { margin-inline-start: calc(var(--pw-space-1) * -1); transition: transform var(--pw-duration-fast) var(--pw-ease-out); }
.pw-btn--icon > .pw-btn__caret { margin-inline-start: 0; }
.pw-btn[aria-expanded="true"] .pw-btn__caret { transform: rotate(180deg); }

/* =============================================================================
   BUTTON GROUP  ·  SPLIT BUTTON
   ============================================================================= */
.pw-btn-group { display: inline-flex; --pw-btn-radius: var(--pw-radius-pill); }
.pw-btn-group > .pw-btn { border-radius: 0; }
.pw-btn-group > .pw-btn:first-child { border-start-start-radius: var(--pw-btn-radius); border-end-start-radius: var(--pw-btn-radius); }
.pw-btn-group > .pw-btn:last-child  { border-start-end-radius: var(--pw-btn-radius); border-end-end-radius: var(--pw-btn-radius); }
.pw-btn-group > .pw-btn + .pw-btn { margin-inline-start: -1px; }
.pw-btn-group > .pw-btn:hover,
.pw-btn-group > .pw-btn:focus-visible { z-index: 1; }

/* =============================================================================
   SEGMENTED CONTROL
   Appearance toggle (sun / moon), directions transport modes, view switchers.
   ============================================================================= */
.pw-segmented {
  position: relative;
  display: inline-flex;
  gap: var(--pw-space-1);
  padding: 3px;
  border: 1px solid var(--pw-border-subtle);
  border-radius: var(--pw-radius-pill);
  background: var(--pw-bg-muted);
}
/* The selection is ONE pill that travels, not a fill each item paints for
   itself. In a row of two, two pills cross-fading is the case where the eye
   cannot tell what happened — nothing moves, so nothing says "this became
   that". The thumb is injected by PW.mount(), which is why the per-item
   background below still exists: without JavaScript it is the whole selected
   state, and `is-thumbed` is what stands it down once a thumb has arrived. */
.pw-segmented__thumb {
  position: absolute;
  inset-block: 3px;
  inset-inline-start: 0;
  width: var(--pw-seg-w, 0);
  translate: var(--pw-seg-x, 0) 0;
  border-radius: var(--pw-radius-pill);
  background: var(--pw-bg-surface);
  box-shadow: var(--pw-shadow-xs);
  pointer-events: none;
  transition:
    translate var(--pw-duration-base) var(--pw-ease-out),
    width var(--pw-duration-base) var(--pw-ease-out);
}
html.dark .pw-segmented__thumb,
[data-pw-theme="dark"] .pw-segmented__thumb { background: var(--pw-color-gray-700); }
/* The items sit above the thumb and stop painting their own. */
.pw-segmented.is-thumbed .pw-segmented__item { position: relative; }
.pw-segmented.is-thumbed .pw-segmented__item[aria-pressed="true"],
.pw-segmented.is-thumbed .pw-segmented__item[aria-selected="true"],
.pw-segmented.is-thumbed .pw-segmented__item.is-active {
  background: transparent;
  box-shadow: none;
}
html.dark .pw-segmented.is-thumbed .pw-segmented__item[aria-pressed="true"],
[data-pw-theme="dark"] .pw-segmented.is-thumbed .pw-segmented__item[aria-pressed="true"],
html.dark .pw-segmented.is-thumbed .pw-segmented__item[aria-selected="true"],
[data-pw-theme="dark"] .pw-segmented.is-thumbed .pw-segmented__item[aria-selected="true"],
html.dark .pw-segmented.is-thumbed .pw-segmented__item.is-active,
[data-pw-theme="dark"] .pw-segmented.is-thumbed .pw-segmented__item.is-active { background: transparent; }
@media (prefers-reduced-motion: reduce) {
  .pw-segmented__thumb { transition: none; }
}
.pw-segmented__item {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--pw-space-3);
  min-width: 34px;
  height: 30px;
  padding-inline: var(--pw-space-5);
  border: 0;
  border-radius: var(--pw-radius-pill);
  background: transparent;
  color: var(--pw-fg-tertiary);
  font-size: var(--pw-text-sm-size);
  font-weight: var(--pw-font-weight-semibold);
  cursor: pointer;
  transition:
    background-color var(--pw-duration-fast) var(--pw-ease-out),
    color var(--pw-duration-fast) var(--pw-ease-out),
    box-shadow var(--pw-duration-fast) var(--pw-ease-out);
}
.pw-segmented__item:hover { color: var(--pw-fg-primary); }
.pw-segmented__item[aria-pressed="true"],
.pw-segmented__item[aria-selected="true"],
.pw-segmented__item.is-active {
  background: var(--pw-bg-surface);
  color: var(--pw-fg-primary);
  box-shadow: var(--pw-shadow-xs);
}
html.dark .pw-segmented__item[aria-pressed="true"],
[data-pw-theme="dark"] .pw-segmented__item[aria-pressed="true"],
html.dark .pw-segmented__item[aria-selected="true"],
[data-pw-theme="dark"] .pw-segmented__item[aria-selected="true"],
html.dark .pw-segmented__item.is-active, [data-pw-theme="dark"] .pw-segmented__item.is-active { background: var(--pw-color-gray-700); }
.pw-segmented__item:disabled { color: var(--pw-fg-disabled); cursor: not-allowed; }

/* Large / tile flavour — the maps directions mode picker */
.pw-segmented--tiles { gap: var(--pw-space-4); padding: 0; border: 0; background: none; border-radius: 0; }
.pw-segmented--tiles .pw-segmented__item {
  flex: 1;
  height: 40px;
  border: 1px solid var(--pw-border-subtle);
  background: var(--pw-bg-surface);
}
.pw-segmented--tiles .pw-segmented__item[aria-pressed="true"],
.pw-segmented--tiles .pw-segmented__item.is-active {
  background: var(--pw-action-bg);
  border-color: transparent;
  color: var(--pw-action-fg);
}

/* =============================================================================
   CHIP  ·  FILTER PILL
   The SERP filter bar, find-nearby chips, image category rail.
   ============================================================================= */
.pw-chip {
  display: inline-flex;
  align-items: center;
  gap: var(--pw-space-3);
  height: 32px;
  padding-inline: var(--pw-space-6);
  border: 1px solid var(--pw-border-default);
  border-radius: var(--pw-radius-pill);
  background: var(--pw-bg-surface);
  color: var(--pw-fg-secondary);
  font-size: var(--pw-text-sm-size);
  font-weight: var(--pw-font-weight-semibold);
  white-space: nowrap;
  cursor: pointer;
  transition:
    background-color var(--pw-duration-fast) var(--pw-ease-out),
    border-color var(--pw-duration-fast) var(--pw-ease-out),
    color var(--pw-duration-fast) var(--pw-ease-out);
}
.pw-chip:hover { background: var(--pw-bg-hover); border-color: var(--pw-border-strong); color: var(--pw-fg-primary); }
.pw-chip:active { background: var(--pw-bg-active); }
/* `.is-current` is the overflow controller's word, and it lands on the More
   chip when the menu behind it holds the chip that is selected. A More that is
   hiding your current filter has to look like it is holding something, and the
   look it needs is the one a selected chip already has. */
.pw-chip[aria-pressed="true"],
.pw-chip.is-selected,
.pw-chip.is-current {
  background: var(--pw-bg-selected);
  border-color: var(--pw-color-brand-300);
  color: var(--pw-fg-brand);
}
html.dark .pw-chip[aria-pressed="true"],
[data-pw-theme="dark"] .pw-chip[aria-pressed="true"],
html.dark .pw-chip.is-selected, [data-pw-theme="dark"] .pw-chip.is-selected,
html.dark .pw-chip.is-current, [data-pw-theme="dark"] .pw-chip.is-current { border-color: var(--pw-color-brand-700); color: var(--pw-color-brand-300); }
.pw-chip:disabled, .pw-chip[aria-disabled="true"] {
  background: var(--pw-bg-disabled); border-color: transparent;
  color: var(--pw-fg-disabled); cursor: not-allowed;
}
/* -- Soft: the same chip without the hairline ------------------------------
   A bordered pill is right on paper -- a white page, a row of filters, nothing
   else to separate it from. On a TINTED bar it is a second edge: the bar has
   already separated the controls from the page, and the outline is then drawing
   a box around something that was never in danger of being lost. So the soft
   chip states itself by material instead: a raised white pill at rest, the
   brand tint when it is on.

   The border is turned transparent rather than dropped, so the two variants
   measure the same and a row can hold both without a pixel of drift. */
.pw-chip--soft {
  border-color: transparent;
  background: var(--pw-bg-surface);
  box-shadow: var(--pw-shadow-xs);
  color: var(--pw-fg-secondary);
}
.pw-chip--soft:hover { border-color: transparent; background: var(--pw-bg-surface); box-shadow: var(--pw-shadow-sm); color: var(--pw-fg-primary); }
.pw-chip--soft:active { background: var(--pw-bg-hover); }
/* ON IS NEUTRAL, and it is a fill. Brand on a chip means "this is the product's
   colour"; it does not mean "this is switched on", and a bar with two brand
   chips and two plain ones reads as two links beside two controls. The inverse
   pair says on without saying anything else: near-black on white, near-white on
   black, one step of the same neutral ramp the page is already made of.

   It stops being raised at the same time. The fill is the state now, and a
   shadow under a filled chip would say it is also in front of something. */
.pw-chip--soft[aria-pressed="true"],
.pw-chip--soft.is-selected,
.pw-chip--soft.is-current {
  border-color: transparent;
  box-shadow: none;
  background: var(--pw-bg-inverse);
  color: var(--pw-fg-inverse);
}
.pw-chip--soft[aria-pressed="true"]:hover,
.pw-chip--soft.is-selected:hover,
.pw-chip--soft.is-current:hover { background: var(--pw-bg-inverse); color: var(--pw-fg-inverse); }
/* In dark, the bordered chip's own selected rule -- `html.dark .pw-chip.is-selected`
   -- outranks the variant by one selector and put the brand back on the label.
   Restated at the same weight rather than fought with `!important`: the soft
   chip's whole point is that on is neutral, and it has to be neutral in both
   themes or it is a variant that only works in one. */
html.dark .pw-chip--soft[aria-pressed="true"],
html.dark .pw-chip--soft.is-selected,
html.dark .pw-chip--soft.is-current,
[data-pw-theme="dark"] .pw-chip--soft[aria-pressed="true"],
[data-pw-theme="dark"] .pw-chip--soft.is-selected,
[data-pw-theme="dark"] .pw-chip--soft.is-current {
  border-color: transparent;
  background: var(--pw-bg-inverse);
  color: var(--pw-fg-inverse);
}

/* The dismiss is already inheriting; on a filled chip it needs its own hover
   ground, because the ink overlay is mixed for a light surface. */
.pw-chip--soft.is-selected .pw-chip__dismiss:hover,
.pw-chip--soft[aria-pressed="true"] .pw-chip__dismiss:hover { background: var(--pw-bg-on-overlay); }
.pw-chip--soft:disabled, .pw-chip--soft[aria-disabled="true"] { box-shadow: none; }

.pw-chip--sm { height: 28px; padding-inline: var(--pw-space-4); font-size: var(--pw-text-xs-size); }
.pw-chip--lg { height: 40px; padding-inline: var(--pw-space-8); font-size: var(--pw-text-sm-size); }
.pw-chip__caret { color: var(--pw-fg-quaternary); transition: transform var(--pw-duration-fast) var(--pw-ease-out); }
.pw-chip[aria-expanded="true"] .pw-chip__caret { transform: rotate(180deg); }
/* 22px visual, hit area padded out past it. Small enough not to unbalance
   the chip, big enough to actually hit with a thumb -- but padded out WITHIN
   the chip, not past it. Eight pixels all round overhung a 28px chip by five
   top and bottom, which meant two things: a press in the empty bar above a chip
   removed the tool, and the overhang counted towards the scrollable area of
   every ancestor, so the tools rail (which scrolls sideways) could scroll five
   pixels DOWN and cut the tops off its own chips. Three is the exact slack
   between the dismiss and the smallest chip it sits in. */
.pw-chip__dismiss {
  position: relative;
  display: inline-flex; align-items: center; justify-content: center;
  width: 22px; height: 22px; margin-inline-end: -6px;
  border: 0; border-radius: var(--pw-radius-circle);
  background: transparent; color: var(--pw-fg-tertiary); cursor: pointer;
}
.pw-chip__dismiss::after { content: ""; position: absolute; inset-block: -3px; inset-inline: -8px; }
.pw-chip__dismiss:hover { background: var(--pw-ink-hover); color: var(--pw-fg-primary); }
/* On a SELECTED chip it takes the chip's own colour. `--pw-fg-tertiary` is the
   right grey beside a grey label and the wrong one beside a brand label: the
   chip reads brand, brand, near-black, and the near-black is the part you are
   meant to press. A dismiss belongs to its chip and should be the same
   temperature as it. */
.pw-chip[aria-pressed="true"] .pw-chip__dismiss,
.pw-chip.is-selected .pw-chip__dismiss { color: inherit; }
.pw-chip[aria-pressed="true"] .pw-chip__dismiss:hover,
.pw-chip.is-selected .pw-chip__dismiss:hover { background: var(--pw-ink-hover); color: inherit; }
.pw-chip__dismiss:focus-visible { outline: none; box-shadow: var(--pw-focus-ring); }
.pw-chip__dismiss:hover { background: var(--pw-ink-hover); color: var(--pw-fg-primary); }
/* Leading colour dot / icon medallion, used by find-nearby and saved lists */
/* The category mark. Its hue arrives as DATA — `--pw-mark-fg` for the glyph and
   `--pw-mark-bg` for the fill — rather than as `background`/`color` written
   straight onto the element, and that difference is the whole point: a value
   set inline cannot be reinterpreted, so a pale category tint stayed pale in
   dark mode and burned a hole in the panel it sat on. As custom properties the
   component owns the rendering, and in dark it derives the pair from the hue
   itself: a 20% wash for the fill and a lightened hue for the glyph, which is
   the same relationship the hand-written dark tints use.

   Categories are open-ended — a new one should be data, not a stylesheet edit —
   so this has to work for any hue rather than for a fixed list. */
.pw-chip__medallion {
  display: inline-flex; align-items: center; justify-content: center;
  width: 20px; height: 20px; margin-inline-start: -4px;
  border-radius: var(--pw-radius-circle);
  background: var(--pw-mark-bg, var(--pw-bg-muted));
  color: var(--pw-mark-fg, var(--pw-fg-tertiary));
}
html.dark .pw-chip__medallion,
[data-pw-theme="dark"] .pw-chip__medallion {
  background: color-mix(in srgb, var(--pw-mark-fg, var(--pw-fg-tertiary)) 20%, transparent);
  color: color-mix(in srgb, var(--pw-mark-fg, var(--pw-fg-tertiary)) 55%, white);
}

/* Horizontal scrolling rail of chips (image categories, mobile filter bar) */
.pw-chip-rail {
  display: flex; gap: var(--pw-space-4);
  overflow-x: auto; overscroll-behavior-x: contain;
  scrollbar-width: none; -ms-overflow-style: none;
  padding-block: var(--pw-space-2);
}
.pw-chip-rail::-webkit-scrollbar { display: none; }
.pw-chip-rail > * { flex: 0 0 auto; }
/* A chip cut in half at the rail's edge reads as a bug; the same chip fading
   reads as "keep going". Only on the side that has more to show. */
.pw-chip-rail {
  --pw-fade-start: transparent 0, #000 var(--pw-fade-lead, 0px);
  --pw-fade-end: #000 calc(100% - var(--pw-fade-trail, 0px)), transparent 100%;
  -webkit-mask-image: linear-gradient(to right, var(--pw-fade-start), var(--pw-fade-end));
  mask-image: linear-gradient(to right, var(--pw-fade-start), var(--pw-fade-end));
}
.pw-chip-rail.has-lead  { --pw-fade-lead: 32px; }
.pw-chip-rail.has-trail { --pw-fade-trail: 32px; }
/* A rail that has stopped scrolling has no edge to soften — and a mask on it
   would clip the overflow menu its trigger opens. */
.pw-chip-rail:has([data-pw-overflow-trigger]) { -webkit-mask-image: none; mask-image: none; }
/* Scrolling and a More menu are two answers to the same question, and one rail
   cannot give both: a row you can scroll has nothing left to put in a menu, and
   the scrollport would clip the menu the trigger opens. A rail that carries an
   overflow trigger stops being a scrollport and lets the controller decide what
   fits instead. */
.pw-chip-rail:has([data-pw-overflow-trigger]) { overflow: visible; }

/* A chip that has overflowed into the menu is not a chip any more. In the rail
   the pill is what separates it from its neighbours; in a column of rows the
   pill fights the rows above and below it, and five of them read as a bag of
   loose buttons rather than a list. It keeps its label, its mark and its
   selected state, and gives up the shape. */
.pw-menu > .pw-chip {
  display: flex; justify-content: flex-start;
  width: 100%; height: auto; min-height: 36px;
  padding: var(--pw-space-3) var(--pw-space-5);
  border-color: transparent; border-radius: var(--pw-radius-md);
  background: transparent;
  font-size: var(--pw-text-sm-size);
}
.pw-menu > .pw-chip:hover { background: var(--pw-bg-hover); border-color: transparent; }
.pw-menu > .pw-chip[aria-pressed="true"],
.pw-menu > .pw-chip.is-selected { background: var(--pw-bg-selected); border-color: transparent; }

/* =============================================================================
   TAG  ·  BADGE
   Non-interactive labels. Tag = rectangular, Badge = pill, Dot = status.
   ============================================================================= */
.pw-tag {
  display: inline-flex; align-items: center; gap: var(--pw-space-2);
  height: 22px; padding-inline: var(--pw-space-5);
  border: 1px solid var(--pw-border-default);
  border-radius: var(--pw-radius-pill);
  background: var(--pw-bg-subtle);
  color: var(--pw-fg-secondary);
  font-size: var(--pw-text-xs-size);
  font-weight: var(--pw-font-weight-semibold);
  white-space: nowrap;
}
.pw-badge { border-radius: var(--pw-radius-pill); }   /* alias — tags are already pills */
.pw-tag--squared { border-radius: var(--pw-radius-sm); }
.pw-tag--sm { height: 20px; font-size: var(--pw-text-xs-size); padding-inline: var(--pw-space-4); }
.pw-tag--lg { height: 26px; font-size: var(--pw-text-sm-size); padding-inline: var(--pw-space-5); }

.pw-tag--brand   { background: var(--pw-color-brand-50);   border-color: var(--pw-color-brand-100);   color: var(--pw-color-brand-700); }
.pw-tag--success { background: var(--pw-color-success-50);  border-color: var(--pw-color-success-150); color: var(--pw-color-success-700); }
.pw-tag--warning { background: var(--pw-color-warning-25);  border-color: var(--pw-color-warning-100);color: var(--pw-color-warning-700); }
.pw-tag--danger  { background: var(--pw-color-danger-25);   border-color: var(--pw-color-danger-150); color: var(--pw-color-danger-700); }
.pw-tag--info    { background: var(--pw-color-info-50);     border-color: var(--pw-color-brand-200);  color: var(--pw-color-info-700); }
.pw-tag--ai      { background: var(--pw-color-purple-50);   border-color: var(--pw-color-purple-100); color: var(--pw-color-purple-700); }

html.dark .pw-tag--brand, [data-pw-theme="dark"] .pw-tag--brand { background: rgba(76,144,254,.16);  border-color: rgba(76,144,254,.32);  color: var(--pw-color-brand-300); }
html.dark .pw-tag--success, [data-pw-theme="dark"] .pw-tag--success { background: rgba(18,183,106,.16);  border-color: rgba(18,183,106,.32);  color: var(--pw-color-success-300); }
html.dark .pw-tag--warning, [data-pw-theme="dark"] .pw-tag--warning { background: rgba(247,144,9,.16);   border-color: rgba(247,144,9,.32);   color: var(--pw-color-warning-400); }
html.dark .pw-tag--danger, [data-pw-theme="dark"] .pw-tag--danger { background: rgba(240,68,56,.16);   border-color: rgba(240,68,56,.32);   color: var(--pw-color-danger-250); }
html.dark .pw-tag--info, [data-pw-theme="dark"] .pw-tag--info { background: rgba(11,165,236,.16);  border-color: rgba(11,165,236,.32);  color: var(--pw-color-brand-300); }
html.dark .pw-tag--ai, [data-pw-theme="dark"] .pw-tag--ai { background: rgba(122,90,248,.18);  border-color: rgba(122,90,248,.34);  color: #c3b5fd; }

/* Outline flavour — the "Beta" pill on a coloured surface */
.pw-tag--outline { background: transparent; border-color: currentColor; }
.pw-tag--on-brand { background: transparent; border-color: rgba(255,255,255,.7); color: var(--pw-color-white); }

.pw-dot {
  display: inline-block; width: 8px; height: 8px; flex: 0 0 auto;
  border-radius: var(--pw-radius-circle); background: var(--pw-fg-quaternary);
}
.pw-dot--success { background: var(--pw-color-success-500); }
.pw-dot--warning { background: var(--pw-color-warning-500); }
.pw-dot--danger  { background: var(--pw-color-danger-500); }
.pw-dot--brand   { background: var(--pw-color-brand-500); }
.pw-dot--pulse::after {
  content: ""; position: absolute; inset: -4px;
  border-radius: inherit; background: inherit; opacity: .4;
  animation: pw-pulse 1.6s var(--pw-ease-out) infinite;
}
@keyframes pw-pulse { to { transform: scale(2.2); opacity: 0; } }

/* Count badge that sits on an icon button */
.pw-count {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 18px; height: 18px; padding-inline: 5px;
  border-radius: var(--pw-radius-pill);
  background: var(--pw-color-danger-500); color: var(--pw-color-white);
  font-size: var(--pw-text-xs-size); font-weight: var(--pw-font-weight-semibold);
  line-height: 1;
}

/* =============================================================================
   AVATAR
   ============================================================================= */
.pw-avatar {
  position: relative;
  display: inline-flex; align-items: center; justify-content: center;
  width: 40px; height: 40px; flex: 0 0 auto;
  border-radius: var(--pw-radius-circle);
  background: var(--pw-bg-muted);
  color: var(--pw-fg-secondary);
  font-size: var(--pw-text-sm-size);
  font-weight: var(--pw-font-weight-semibold);
  overflow: hidden;
  user-select: none;
}
.pw-avatar > img { width: 100%; height: 100%; object-fit: cover; }
.pw-avatar--xs { width: 24px; height: 24px; font-size: var(--pw-text-xs-size); }
.pw-avatar--sm { width: 32px; height: 32px; font-size: var(--pw-text-xs-size); }
.pw-avatar--lg { width: 48px; height: 48px; font-size: var(--pw-text-md-size); }
.pw-avatar--xl { width: 64px; height: 64px; font-size: var(--pw-text-xl-size); }
.pw-avatar--square { border-radius: var(--pw-radius-md); }
.pw-avatar--ring { box-shadow: 0 0 0 2px var(--pw-bg-canvas), 0 0 0 4px var(--pw-color-brand-500); }
.pw-avatar__status {
  position: absolute; right: 0; bottom: 0;
  width: 10px; height: 10px;
  border-radius: var(--pw-radius-circle);
  border: 2px solid var(--pw-bg-canvas);
  background: var(--pw-color-success-500);
}
.pw-avatar-stack { display: inline-flex; }
.pw-avatar-stack > .pw-avatar + .pw-avatar { margin-inline-start: -10px; }
.pw-avatar-stack > .pw-avatar { box-shadow: 0 0 0 2px var(--pw-bg-canvas); }

/* Favicon holder used by search results and place rows */
.pw-favicon {
  display: inline-flex; align-items: center; justify-content: center;
  width: 26px; height: 26px; flex: 0 0 auto;
  border: 1px solid var(--pw-border-subtle);
  border-radius: var(--pw-radius-circle);
  background: var(--pw-bg-surface);
  overflow: hidden;
}
.pw-favicon > img { width: 16px; height: 16px; object-fit: contain; }

/* =============================================================================
   FIELD SHELL
   Label + control + hint/error. Every form control shares this wrapper so
   spacing and error colouring are defined once.
   ============================================================================= */
.pw-field { display: flex; flex-direction: column; gap: var(--pw-space-3); min-width: 0; }
.pw-field--inline { flex-direction: row; align-items: center; gap: var(--pw-space-6); }

.pw-label {
  display: inline-flex; align-items: center; gap: var(--pw-space-2);
  font-size: var(--pw-text-sm-size);
  font-weight: var(--pw-font-weight-semibold);
  color: var(--pw-fg-secondary);
}
.pw-label__required { color: var(--pw-fg-danger); }
.pw-label__optional { color: var(--pw-fg-quaternary); font-weight: var(--pw-font-weight-regular); }

.pw-hint  { font-size: var(--pw-text-xs-size); line-height: var(--pw-text-xs-line); color: var(--pw-fg-tertiary); }
/* Under a field: the hint on the leading side, a count on the trailing one.
   The hint takes the room and wraps if it must; the count keeps to its own
   width and to the end of the line, where the eye checks it. */
.pw-field__foot { display: flex; align-items: baseline; justify-content: space-between; gap: var(--pw-space-6); }
.pw-field__foot > .pw-hint { flex: 1 1 auto; min-width: 0; }
.pw-field__foot > .pw-char-count { flex: none; margin-inline-start: auto; font-variant-numeric: tabular-nums; }

/* A vertical stack of fields. The gap is larger than the gap *inside* a
   field (label → control → hint), so a field reads as one block and the
   form as a list of them. Getting these two gaps the same way round is
   what makes a long form scannable. */
/* A group of fields that belong together. Deliberately borderless: every
   field inside is already a rounded outline, so a box around them draws a
   second boundary around the first and the eye has to resolve two shapes
   where there is one group. A tint and padding say "these belong together"
   without competing with the controls. */
.pw-fieldset {
  padding: var(--pw-space-8);
  border: 0;
  border-radius: var(--pw-radius-lg);
  background: var(--pw-bg-subtle);
}

.pw-form { display: flex; flex-direction: column; gap: var(--pw-space-10); min-width: 0; }
.pw-form--tight { gap: var(--pw-space-6); }

/* Two controls that mean one value — a numeric range, a date range, a
   min/max pair. Wrap in role="group" with the visible label, and give each
   half its own aria-label, or a screen reader reads the same label twice. */
.pw-field-row { display: flex; align-items: center; gap: var(--pw-space-5); min-width: 0; }
.pw-field-row > * { flex: 1 1 0; min-width: 0; }
.pw-field-row__sep {
  flex: 0 0 auto;
  color: var(--pw-fg-tertiary);
  font-size: var(--pw-text-sm-size);
}
.pw-error {
  display: flex; align-items: flex-start; gap: var(--pw-space-2);
  font-size: var(--pw-text-xs-size); line-height: var(--pw-text-xs-line);
  color: var(--pw-fg-danger);
}
.pw-success-note {
  display: flex; align-items: flex-start; gap: var(--pw-space-2);
  font-size: var(--pw-text-xs-size); color: var(--pw-fg-success);
}

/* =============================================================================
   INPUT  ·  TEXTAREA
   Sizes    sm (32) · md (40) · lg (48)
   States   rest · hover · focus · filled · error · success · disabled · readonly
   Addons   leading icon · trailing icon · clear · prefix/suffix · inline action
   ============================================================================= */
.pw-input-wrap {
  --pw-input-height: 40px;
  --pw-input-px: var(--pw-space-6);

  position: relative;
  display: flex;
  align-items: center;
  gap: var(--pw-space-4);
  min-height: var(--pw-input-height);
  padding-inline: var(--pw-input-px);
  border: 1px solid var(--pw-field-border);
  border-radius: var(--pw-radius-pill);     /* fully rounded is the default */
  background: var(--pw-field-bg);
  transition:
    border-color var(--pw-duration-fast) var(--pw-ease-out),
    box-shadow var(--pw-duration-fast) var(--pw-ease-out),
    background-color var(--pw-duration-fast) var(--pw-ease-out);
}
.pw-input-wrap:hover { border-color: var(--pw-field-border-hover); }
.pw-input-wrap:focus-within {
  border-color: var(--pw-field-border-focus);
  box-shadow: var(--pw-focus-ring);
}
.pw-input-wrap--sm { --pw-input-height: 32px; --pw-input-px: var(--pw-space-5); }
.pw-input-wrap--lg { --pw-input-height: 48px; --pw-input-px: var(--pw-space-8); }
.pw-input-wrap--pill    { border-radius: var(--pw-radius-pill); }   /* alias */
.pw-input-wrap--squared { border-radius: var(--pw-radius-md); }

.pw-input {
  flex: 1 1 auto;
  min-width: 0;
  width: 100%;
  border: 0;
  padding: 0;
  background: transparent;
  color: var(--pw-field-fg);
  font-size: var(--pw-text-sm-size);
  line-height: var(--pw-text-sm-line);
  outline: none;
}
.pw-input::placeholder { color: var(--pw-field-placeholder); }
.pw-input-wrap--lg .pw-input { font-size: var(--pw-text-md-size); line-height: var(--pw-text-md-line); }
.pw-input-wrap--sm .pw-input { font-size: var(--pw-text-sm-size); }

.pw-input__icon { color: var(--pw-field-icon); }
.pw-input__affix { color: var(--pw-fg-tertiary); font-size: var(--pw-text-sm-size); white-space: nowrap; font-weight: var(--pw-font-weight-medium); }
/* Inline field actions — clear, reveal, copy, submit.
   32px visual, with the hit area pushed out to 44 via ::after so a small glyph
   inside a 40px pill is still comfortable on touch. */
.pw-input__action {
  --pw-input-action-size: 32px;
  position: relative;
  display: inline-flex; align-items: center; justify-content: center;
  width: var(--pw-input-action-size); height: var(--pw-input-action-size); flex: 0 0 auto;
  margin-inline-end: calc(var(--pw-space-2) * -1);
  border: 0; border-radius: var(--pw-radius-circle);
  background: transparent; color: var(--pw-field-icon);
  cursor: pointer;
  transition: background-color var(--pw-duration-fast) var(--pw-ease-out), color var(--pw-duration-fast) var(--pw-ease-out);
}
.pw-input__action::after { content: ""; position: absolute; inset: -6px; }
.pw-input__action .pw-icon { width: var(--pw-icon-md); height: var(--pw-icon-md); }
/* Translucent, not a fixed grey: this button sits inside a field whose
   background the caller controls. On the results header that field is tinted,
   and a solid --pw-bg-hover would be *lighter* than it — the hover disc reads
   as a hole punched in the field instead of a highlight. Ink darkens whatever
   is behind it, so it works on white and on any tint. */
.pw-input__action:hover { background: var(--pw-ink-hover); color: var(--pw-fg-primary); }
.pw-input__action:active { background: var(--pw-ink-active); }
.pw-input__action:focus-visible { outline: 2px solid var(--pw-color-brand-600); outline-offset: 1px; }
/* Small fields keep a smaller disc, but the extended hit area stays. */
.pw-input-wrap--sm .pw-input__action,
.pw-search--sm .pw-input__action { --pw-input-action-size: 26px; }
.pw-input-wrap--sm .pw-input__action .pw-icon { width: var(--pw-icon-sm); height: var(--pw-icon-sm); }

/* =============================================================================
   ONE-TIME CODE  ·  .pw-otp
   A verification code, one box per character. ONE real <input> does the work:
   it lies over the boxes, invisible, so typing, paste, SMS autofill
   (autocomplete="one-time-code"), undo and the screen reader all behave as
   they do for any input -- the boxes only draw what it holds. Without the
   script it is a plain field and still works.
   Sizes    sm (40) · md (48, default) · lg (56)
   States   empty · typing · filled · invalid · success · disabled
   ============================================================================= */
.pw-otp {
  --pw-otp-size: 48px;
  position: relative;
  display: inline-flex; align-items: center; gap: var(--pw-space-5);
  direction: ltr;            /* a code reads left to right in every language */
  max-width: 100%;
}
.pw-otp--sm { --pw-otp-size: 40px; }
.pw-otp--lg { --pw-otp-size: 56px; }
/* Before the script: an ordinary field, wide enough for the code. */
.pw-otp__input {
  width: calc(var(--pw-otp-size) * 4.5); height: var(--pw-otp-size);
  padding-inline: var(--pw-space-6);
  border: 1px solid var(--pw-field-border); border-radius: var(--pw-radius-lg);
  background: var(--pw-field-bg); color: var(--pw-field-fg);
  font: inherit; font-size: var(--pw-text-lg-size); letter-spacing: .3em; font-variant-numeric: tabular-nums;
}
/* After: stretched over the boxes and invisible. 16px keeps iOS from zooming
   the page on focus; the caret and the selection are the boxes' to show. */
.pw-otp.is-ready .pw-otp__input {
  position: absolute; inset: 0; z-index: 2;
  width: 100%; height: 100%; padding: 0; border: 0;
  font-size: 16px; letter-spacing: 0;
  color: transparent; caret-color: transparent; background: transparent;
  opacity: 0; cursor: text;
}
.pw-otp.is-ready .pw-otp__input::selection { background: transparent; }
.pw-otp__group { display: flex; gap: var(--pw-space-3); }
.pw-otp__slot {
  position: relative;
  display: grid; place-items: center;
  width: var(--pw-otp-size); height: calc(var(--pw-otp-size) * 1.12);
  border: 1px solid var(--pw-field-border); border-radius: var(--pw-radius-lg);
  background: var(--pw-field-bg); color: var(--pw-field-fg);
  font-size: calc(var(--pw-otp-size) * .42); font-weight: var(--pw-font-weight-semibold);
  font-variant-numeric: tabular-nums; line-height: 1;
  transition: border-color var(--pw-duration-fast) var(--pw-ease-out), background-color var(--pw-duration-fast) var(--pw-ease-out);
}
.pw-otp:hover .pw-otp__slot { border-color: var(--pw-field-border-hover); }
.pw-otp__char { display: block; }
.pw-otp__ph { color: var(--pw-field-placeholder); font-weight: var(--pw-font-weight-regular); }
/* The dash between groups: where a person pauses when they read it out. */
.pw-otp__sep { flex: none; width: 10px; height: 2px; border-radius: var(--pw-radius-pill); background: var(--pw-fg-quaternary); }
/* Where the next character goes: one ring that travels from box to box
   rather than a focus style on each, so the eye follows it. */
.pw-otp__ring {
  position: absolute; top: 0; left: 0; z-index: 1;
  border: 1.5px solid var(--pw-field-border-focus); border-radius: var(--pw-radius-lg);
  box-shadow: var(--pw-focus-ring);
  pointer-events: none; opacity: 0;
}
.pw-otp.is-focused .pw-otp__ring { opacity: 1; }
/* Wrong code: every box says so, and the ring with them. The code stays, so
   the person can see what they typed. */
.pw-otp[data-state="invalid"] .pw-otp__slot { border-color: var(--pw-border-danger); }
.pw-otp[data-state="invalid"] .pw-otp__ring { border-color: var(--pw-fg-danger); box-shadow: 0 0 0 4px color-mix(in srgb, var(--pw-fg-danger) 22%, transparent); }
.pw-otp[data-state="success"] .pw-otp__slot { border-color: var(--pw-fg-success); background: var(--pw-tint-success-bg); color: var(--pw-fg-success); }
.pw-otp[data-state="success"] .pw-otp__ring { opacity: 0; }
.pw-otp:has(.pw-otp__input:disabled) { cursor: not-allowed; }
.pw-otp:has(.pw-otp__input:disabled) .pw-otp__slot { background: var(--pw-field-bg-disabled); border-color: var(--pw-border-subtle); color: var(--pw-fg-disabled); }
.pw-otp:has(.pw-otp__input:disabled) .pw-otp__input { cursor: not-allowed; }

/* Error / success / disabled / readonly */
.pw-input-wrap.is-error { border-color: var(--pw-color-danger-500); }
.pw-input-wrap.is-error:focus-within { box-shadow: 0 0 0 4px rgba(240, 68, 56, 0.24); }
.pw-input-wrap.is-success { border-color: var(--pw-color-success-500); }
.pw-input-wrap.is-success:focus-within { box-shadow: 0 0 0 4px rgba(18, 183, 106, 0.22); }
.pw-input-wrap.is-disabled,
.pw-input-wrap:has(.pw-input:disabled) {
  background: var(--pw-field-bg-disabled);
  border-color: var(--pw-border-subtle);
  cursor: not-allowed;
}
.pw-input:disabled { color: var(--pw-fg-disabled); cursor: not-allowed; }
.pw-input:disabled::placeholder { color: var(--pw-fg-disabled); }
.pw-input-wrap.is-readonly { background: var(--pw-bg-subtle); border-color: var(--pw-border-subtle); }

/* Textarea */
/* A multi-line field cannot be a pill — the corners would clip the first and
   last lines. It takes the largest radius that still clears the text. */
.pw-textarea-wrap { align-items: stretch; padding-block: var(--pw-space-5); border-radius: var(--pw-radius-xl); }
.pw-textarea {
  resize: vertical;
  min-height: 88px;
  line-height: var(--pw-text-md-line);
}
.pw-textarea--autosize { resize: none; overflow: hidden; }
.pw-char-count { align-self: flex-end; font-size: var(--pw-text-xs-size); color: var(--pw-fg-quaternary); }
.pw-char-count.is-over { color: var(--pw-fg-danger); }

/* Grouped input — prefix/suffix blocks welded to the field */
.pw-input-group { display: flex; align-items: stretch; }
.pw-input-group > .pw-input-wrap { flex: 1; border-radius: 0; }
.pw-input-group > :first-child { border-start-start-radius: var(--pw-radius-pill); border-end-start-radius: var(--pw-radius-pill); }
.pw-input-group > :last-child  { border-start-end-radius: var(--pw-radius-pill); border-end-end-radius: var(--pw-radius-pill); }
.pw-input-group > * + * { margin-inline-start: -1px; }
.pw-input-group__addon {
  display: inline-flex; align-items: center; gap: var(--pw-space-3);
  padding-inline: var(--pw-space-6);
  border: 1px solid var(--pw-field-border);
  background: var(--pw-bg-subtle);
  color: var(--pw-fg-tertiary);
  font-size: var(--pw-text-sm-size);
  white-space: nowrap;
}

/* =============================================================================
   SEARCH FIELD
   The product's signature control. Pill, generous height, trailing clear and
   submit. The leading slot is empty by default — see the rule in pw-search.css.
   Used on the home page, the SERP header and the maps launcher.
   ============================================================================= */
.pw-search {
  display: flex; align-items: center; gap: var(--pw-space-6);
  width: 100%;
  /* Height and inline padding are custom properties, not literals, because two
     things inside the field are derived from them — the mode disc's diameter
     and the inset that keeps it optically centred in the field's cap. Written
     as literals, a size variant would move the field and leave the disc behind. */
  --pw-search-height: 48px;
  --pw-search-px: var(--pw-space-8);
  height: var(--pw-search-height);
  padding-inline: var(--pw-search-px);
  border: 1px solid var(--pw-border-default);
  border-radius: var(--pw-radius-pill);
  background: var(--pw-field-bg);
  box-shadow: var(--pw-shadow-xs);
  transition: border-color var(--pw-duration-fast) var(--pw-ease-out), box-shadow var(--pw-duration-base) var(--pw-ease-out);
}
.pw-search:hover { box-shadow: var(--pw-shadow-md); }
.pw-search:focus-within { border-color: var(--pw-border-strong); box-shadow: var(--pw-shadow-lg); }
.pw-search--sm { --pw-search-height: 40px; --pw-search-px: var(--pw-space-6); gap: var(--pw-space-4); }
.pw-search--xs {
  --pw-search-height: 32px;
  --pw-search-px: var(--pw-space-5);
  min-height: 32px;
  gap: var(--pw-space-3);
  font-size: var(--pw-text-sm-size);
}
.pw-search--xs .pw-search__input { font-size: var(--pw-text-sm-size); line-height: var(--pw-text-sm-line); }


/* A field inside a popover is a *filter for the list below it*, not a place
   you go to compose something. At full height it out-weighs every option it
   is meant to be narrowing, and it eats the vertical space the list needs.
   So any search field in any layer is forced down to 32px, whatever size the
   caller asked for — this is not a suggestion the markup can get wrong. */
.pw-filter-menu .pw-search,
.pw-select__menu .pw-search,
.pw-menu .pw-search,
.pw-settings-menu .pw-search,
.pw-suggest .pw-search {
  height: 32px;
  min-height: 32px;                 /* the base sets min-height: 48px */
  padding-inline: var(--pw-space-5);
  gap: var(--pw-space-3);
  box-shadow: none;
}
.pw-filter-menu .pw-search .pw-search__input,
.pw-select__menu .pw-search .pw-search__input,
.pw-menu .pw-search .pw-search__input,
.pw-settings-menu .pw-search .pw-search__input,
.pw-suggest .pw-search .pw-search__input {
  font-size: var(--pw-text-sm-size);
  line-height: var(--pw-text-sm-line);
}
/* A field in a layer is 32px, so its glyphs step down with it. */
.pw-filter-menu .pw-search,
.pw-select__menu .pw-search,
.pw-menu .pw-search,
.pw-settings-menu .pw-search,
.pw-suggest .pw-search { --pw-search-glyph: var(--pw-icon-sm); }
.pw-search--lg { --pw-search-height: 56px; }
/* Every glyph in a field is one size, declared once.
   The leading slot and the trailing actions sit in the same pill an inch
   apart, and they had drifted to 16 and 20: the markup asked for `pw-icon--sm`
   on the left while `.pw-input__action` forced `md` on the right, and the
   per-size overrides then disagreed with each other about which was which.
   A single custom property removes the possibility rather than correcting the
   instances — the field states the size, both ends read it. */
.pw-search { --pw-search-glyph: var(--pw-icon-md); }
.pw-search--sm,
.pw-search--xs { --pw-search-glyph: var(--pw-icon-sm); }
.pw-search__icon { color: var(--pw-field-icon); }
.pw-search .pw-search__icon:not(.pw-mark-pin),
.pw-search .pw-input__action .pw-icon {
  width: var(--pw-search-glyph);
  height: var(--pw-search-glyph);
}
/* A brand mark is exempt: it has its own aspect ratio, and forcing it into the
   glyph's square flattens it. It matches on height instead. */
.pw-search .pw-mark-pin { height: calc(var(--pw-search-glyph) * 1.25); width: auto; }
.pw-search__input {
  flex: 1; min-width: 0; border: 0; padding: 0; background: transparent;
  color: var(--pw-field-fg);
  font-size: var(--pw-text-md-size); line-height: var(--pw-text-md-line);
  outline: none;
}
.pw-search__input::placeholder { color: var(--pw-field-placeholder); }
.pw-search__input::-webkit-search-cancel-button { display: none; }
.pw-search__divider {
  width: 1px; height: 22px;
  background: var(--pw-border-default);
  flex: 0 0 auto;
  /* Spacing is one value used on both sides, never a pair. A divider is read
     as the middle of the gap it sits in, so the moment the two halves differ
     it stops looking like a separator and starts looking misplaced. Variants
     tune the amount, never the balance. */
  margin-inline: var(--pw-search-divider-gap, 0px);
}
/* `.pw-input__action` carries a negative end margin so the LAST control in a
   field sits optically at the padding edge rather than a whole button-box in
   from it. A button followed by a divider is not at that edge — it is one of
   the two things the divider separates — so it hands the correction back.
   Without this the gap before the divider is short by exactly that margin,
   which is what made the clear button sit tight against it. */
.pw-search .pw-input__action:has(+ .pw-search__divider) { margin-inline-end: 0; }
/* A leading control inside the field — the tablet layout puts the menu here,
   because at that width the rail costs more than it earns and the field is the
   one thing always on screen. Its divider is structural, not conditional: it
   separates two permanent things, so unlike the clear button's divider it does
   not fade with the value. */
.pw-search__lead { margin-inline-start: calc(var(--pw-space-3) * -1); }
.pw-search__divider--lead { opacity: 1 !important; visibility: visible !important; }

/* -- Mode switcher ---------------------------------------------------------
   The leading slot as the search SCOPE — Web, Shopping, Hotels, Flights — and
   the one thing that may live there other than a lead control, a spinner or
   the maps mark.

   This is not the magnifier coming back. The rule the magnifier broke was that
   the leading edge must not hold a decoration that repeats what the field
   already says; the mode trigger is a control you press, it changes what the
   query means, and its glyph is the only place the current scope is stated.
   Search itself stays where it always is, on the trailing edge.

   It is the icon button, composed in the markup — the trigger inherits every
   state from it and this class only sizes it against the field and rounds it:

     class="pw-btn pw-btn--icon pw-search__mode"

   Sized from the field so the two never drift: the trigger is the field's
   height less the padding it sits in, which keeps it a disc rather than a pill
   at every size. */
.pw-search { position: relative; }
.pw-search__mode {
  /* 90% of the field, so the disc reads as filling the cap rather than floating
     in it, and the leftover 5% top and bottom is the only air around it. */
  --pw-btn-height: calc(var(--pw-search-height) * 0.9);
  --pw-btn-radius: var(--pw-radius-circle);
  /* Pulled back out of the field's padding so the gap at the leading edge
     matches the gap above and below. Anything else and a circle inset 16px on
     one side and 2px on the other reads as misaligned even though it is
     centred. */
  /* The extra 1px is the field's own border: the 5% is measured off the border
     box, but the padding this margin cancels starts inside it, so without it
     the leading gap lands exactly one border-width wider than the one above. */
  margin-inline-start: calc(var(--pw-search-height) * 0.05 - var(--pw-search-px) - 1px);
  flex: 0 0 auto;
  background: var(--pw-bg-muted);
  color: var(--pw-fg-secondary);
}
.pw-search__mode:hover { background: var(--pw-bg-strong); color: var(--pw-fg-primary); }
.pw-search__mode[aria-expanded="true"] {
  background: var(--pw-bg-selected);
  color: var(--pw-fg-brand);
}
/* The glyph swaps with the mode, so it animates rather than cutting. */
.pw-search__mode .pw-icon {
  transition: scale var(--pw-duration-fast) var(--pw-ease-out);
}
.pw-search__mode[aria-expanded="true"] .pw-icon { scale: 1.08; }

/* The menu hangs off the field's leading edge, under the trigger it belongs to. */
.pw-search__modes {
  position: absolute;
  top: calc(100% + var(--pw-space-4));
  inset-inline-start: 0;
  z-index: var(--pw-z-dropdown);
  width: 264px;
  max-width: 100%;
}

/* Off — the setting that hides the switcher leaves a plain, non-interactive
   mark in its place rather than closing the gap. Removing it would shift the
   placeholder by 40px the moment someone flipped a setting, and the field is
   the most position-sensitive thing on the page. */
.pw-search__mode--static {
  pointer-events: none;
  background: transparent;
  color: var(--pw-field-icon);
}

/* -- Multi-line ------------------------------------------------------------
   A long query that wraps instead of scrolling sideways. Three lines, then it
   scrolls — past that the field is taller than the results it is filtering.

   Enter still submits: the newlines here come from WRAPPING, not from typing
   them. That is the whole difference between this and a composer, and it is
   why the field grows without ever offering to hold a paragraph.

   Three things change the moment it is more than one line tall:

   1. The pill relaxes. A pill radius on a three-line box clips the first and
      last lines against the curve — the house rule already says a textarea
      takes `xl` for exactly this reason.
   2. Everything aligns to the FIRST line and stays there. Pinning the clear
      and submit buttons to the growing edge would walk them down the screen
      while someone is typing, which is the same mistake as a stepper that
      resizes under the finger.
   3. The row switches from centred to top-aligned, so line one sits where the
      single-line field's text sat. Nothing jumps at the moment it wraps.
   -------------------------------------------------------------------------- */
/* Doubled up so it outweighs the per-surface height overrides — the SERP
   header pins its field to 48px, and a variant whose whole job is to grow has
   to beat that wherever it is used. */
.pw-search.pw-search--multiline { height: auto; min-height: 48px; }
/* Only the grown state changes the layout. At one line the field keeps the
   base's centring and its 48px min-height, so it is not merely similar to the
   single-line field — it is pixel-identical to it. */
.pw-search.pw-search--multiline.is-multiline {
  align-items: flex-start;
  padding-block: 12px;                       /* (48 − 24 line-height) ÷ 2 */
}
.pw-search.pw-search--multiline.pw-search--sm.is-multiline { padding-block: 8px; }
.pw-search--multiline .pw-search__input {
  display: block;
  resize: none;
  overflow-y: auto;
  /* The gutter is reserved whether or not the thumb is visible, so the text
     never reflows at the moment the query outgrows three lines. */
  padding-inline-end: var(--pw-space-3);
  scrollbar-width: none;                     /* Firefox: no thumb at all */
  max-height: calc(var(--pw-text-md-line) * 3);
  scrollbar-width: thin;
}
/* Everything that is not the text centres on the FIRST line, whatever its own
   height. Each offset is (line box − own height) ÷ 2, computed from the tokens
   rather than nudged by eye — which is why the action button's size had to
   become a variable. Top-aligning them instead would drop each control by half
   its own height at the exact moment the query wraps: a visible twitch on a
   button the person is about to click. */
.pw-search--multiline.is-multiline > * { align-self: flex-start; }
.pw-search--multiline.is-multiline > .pw-search__icon {
  margin-block-start: calc((var(--pw-text-md-line) - var(--pw-icon-md)) / 2);
}
.pw-search--multiline.is-multiline > .pw-input__action {
  margin-block-start: calc((var(--pw-text-md-line) - var(--pw-input-action-size)) / 2);
}
.pw-search--multiline.is-multiline > .pw-search__divider {
  height: var(--pw-text-md-line);          /* the divider spans the first line */
}
.pw-search.pw-search--multiline.is-multiline { border-radius: var(--pw-radius-2xl); }

/* The thumb appears only while the field is actually being scrolled, then
   fades. A bar parked permanently against the clear button reads as chrome the
   field has grown; a bar that arrives when you scroll reads as an answer to
   what you just did. `.is-scrolling` is stamped by the controller and cleared
   on a timer.
   Firefox gets no thumb rather than a permanent one — `scrollbar-width` has no
   fadeable part, and of the two states the quiet one is the better default. */
.pw-search--multiline .pw-search__input::-webkit-scrollbar { width: 6px; }
.pw-search--multiline .pw-search__input::-webkit-scrollbar-track { background: transparent; }
.pw-search--multiline .pw-search__input::-webkit-scrollbar-thumb {
  border-radius: var(--pw-radius-pill);
  background-color: transparent;
  transition: background-color var(--pw-duration-slow) var(--pw-ease-out);
}
.pw-search--multiline .pw-search__input.is-scrolling::-webkit-scrollbar-thumb,
.pw-search--multiline .pw-search__input:hover::-webkit-scrollbar-thumb {
  background-color: var(--pw-border-strong);
  transition-duration: var(--pw-duration-fast);
}

/* -- A submit button standing beside the field -----------------------------
   The maps launcher puts its search button outside the pill. It is the same
   control at the same height, so it is sized from the field rather than from
   the button scale — and laid out in a row rather than positioned by hand,
   which is what let it drift out of alignment in the first place. */
.pw-search-row { display: flex; align-items: center; gap: var(--pw-space-6); }
.pw-search-row > .pw-combobox,
.pw-search-row > .pw-search { flex: 1 1 auto; min-width: 0; }
.pw-search-row > .pw-btn { --pw-btn-height: 48px; flex: 0 0 auto; }
.pw-search-row:has(.pw-search--sm) > .pw-btn { --pw-btn-height: 40px; }
.pw-search-row:has(.pw-search--lg) > .pw-btn { --pw-btn-height: 56px; }


/* Set the variable, not the literals: the multiline variant centres these on
   the first line with (line box − size) ÷ 2, and a hard-coded width leaves that
   arithmetic reading 32 while the button is really 34. The glyph size is gone
   from here entirely — the field owns it now, through `--pw-search-glyph`, so
   both ends of the pill cannot disagree. */
.pw-search .pw-input__action { --pw-input-action-size: 34px; }
.pw-search--lg .pw-input__action { --pw-input-action-size: 38px; }
/* The clear button and its divider fade rather than blink.
   `display: none` is a cut: the button is simply not there on one frame and
   fully there on the next, at the exact moment someone types their first
   character — the busiest moment the field has. Hidden by opacity instead, it
   arrives with the text. It also keeps its seat, so the submit button does not
   jump sideways on that same keystroke. */
.pw-search__clear,
.pw-search__divider {
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transition:
    opacity var(--pw-duration-base) var(--pw-ease-out),
    visibility 0s linear var(--pw-duration-base);
}
.pw-search.has-value .pw-search__clear,
.pw-search.has-value .pw-search__divider {
  opacity: 1;
  visibility: visible;
  pointer-events: auto;
  transition:
    opacity var(--pw-duration-base) var(--pw-ease-out),
    visibility 0s;
}
/* A field with no clear button at all keeps its divider — there is nothing
   conditional about it. */
.pw-search:not(:has(.pw-search__clear)) .pw-search__divider {
  opacity: 1; visibility: visible;
}

/* =============================================================================
   SELECT  (custom — never the native control)
   The chevron is always the trailing affordance so the control reads the same
   everywhere, and so we can style the menu, options and states consistently.
   Structure
     .pw-select > button.pw-select__trigger > .pw-select__value + .pw-select__caret
                > .pw-select__menu > .pw-select__option
   ============================================================================= */
.pw-select { position: relative; display: inline-flex; min-width: 0; }
.pw-select--block { display: flex; width: 100%; }

.pw-select__trigger {
  --pw-select-height: 40px;
  display: flex;
  align-items: center;
  gap: var(--pw-space-4);
  width: 100%;
  min-width: 0;
  height: var(--pw-select-height);
  padding-inline: var(--pw-space-6);
  border: 1px solid var(--pw-field-border);
  border-radius: var(--pw-radius-pill);     /* fully rounded is the default */
  background: var(--pw-field-bg);
  color: var(--pw-field-fg);
  font-size: var(--pw-text-sm-size);
  text-align: start;
  cursor: pointer;
  transition:
    border-color var(--pw-duration-fast) var(--pw-ease-out),
    box-shadow var(--pw-duration-fast) var(--pw-ease-out),
    background-color var(--pw-duration-fast) var(--pw-ease-out);
}
.pw-select__trigger:hover { border-color: var(--pw-field-border-hover); }
.pw-select__trigger:focus-visible,
.pw-select__trigger[aria-expanded="true"] {
  outline: none;
  border-color: var(--pw-field-border-focus);
  box-shadow: var(--pw-focus-ring);
}
.pw-select--sm .pw-select__trigger { --pw-select-height: 32px; padding-inline: var(--pw-space-5); font-size: var(--pw-text-sm-size); }
.pw-select--lg .pw-select__trigger { --pw-select-height: 48px; padding-inline: var(--pw-space-8); font-size: var(--pw-text-md-size); }
.pw-select--pill .pw-select__trigger    { border-radius: var(--pw-radius-pill); }  /* alias */
.pw-select--squared .pw-select__trigger { border-radius: var(--pw-radius-md); }
/* Quiet flavour — the SERP filter bar chips */
.pw-select--quiet .pw-select__trigger {
  height: 32px;
  background: var(--pw-bg-surface); border-color: var(--pw-border-default);
  color: var(--pw-fg-secondary); font-size: var(--pw-text-sm-size);
  font-weight: var(--pw-font-weight-medium);
}
.pw-select--quiet .pw-select__trigger:hover { background: var(--pw-bg-hover); }

.pw-select__value { flex: 1 1 auto; min-width: 0; display: flex; align-items: center; gap: var(--pw-space-4); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-weight: var(--pw-font-weight-medium); }
.pw-select__value.is-placeholder { color: var(--pw-field-placeholder); font-weight: var(--pw-font-weight-regular); }
/* Trailing chevron — pinned to the end of the trigger, rotates when open. */
.pw-select__caret {
  flex: 0 0 auto;
  margin-inline-start: auto;
  color: var(--pw-fg-quaternary);
  transition: transform var(--pw-duration-base) var(--pw-ease-out);
}
.pw-select__trigger[aria-expanded="true"] .pw-select__caret { transform: rotate(180deg); }

.pw-select__trigger:disabled,
.pw-select.is-disabled .pw-select__trigger {
  background: var(--pw-field-bg-disabled);
  border-color: var(--pw-border-subtle);
  color: var(--pw-fg-disabled);
  cursor: not-allowed;
}
.pw-select.is-error .pw-select__trigger { border-color: var(--pw-color-danger-500); }

/* -- Menu ------------------------------------------------------------------- */
.pw-select__menu {
  position: absolute;
  z-index: var(--pw-z-dropdown);
  inset-inline-start: 0;
  top: calc(100% + var(--pw-space-2));
  min-width: 100%;
  max-width: 100%;          /* a menu wider than its field drags the page sideways */
  max-height: 320px;
  padding: var(--pw-space-2);
  overflow-y: auto;
  overscroll-behavior: contain;
  border: 1px solid var(--pw-border-default);
  border-radius: var(--pw-radius-lg);
  background: var(--pw-bg-surface);
  box-shadow: var(--pw-shadow-xl);
  opacity: 0;
  transform: translateY(-4px) scale(0.985);
  transform-origin: top center;
  pointer-events: none;
  transition:
    opacity var(--pw-duration-fast) var(--pw-ease-out),
    transform var(--pw-duration-fast) var(--pw-ease-out),
    visibility var(--pw-duration-fast);
  visibility: hidden;
}
.pw-select.is-open > .pw-select__menu {
  opacity: 1; transform: none; pointer-events: auto; visibility: visible;
}
.pw-select__menu--end { inset-inline-start: auto; inset-inline-end: 0; }
.pw-select__menu--above { top: auto; bottom: calc(100% + var(--pw-space-2)); transform-origin: bottom center; }

.pw-select__search {
  position: sticky; top: calc(var(--pw-space-2) * -1);
  z-index: 1;
  margin: calc(var(--pw-space-2) * -1) calc(var(--pw-space-2) * -1) var(--pw-space-2);
  padding: var(--pw-space-3);
  border-bottom: 1px solid var(--pw-border-subtle);
  background: var(--pw-bg-surface);
}

.pw-select__group-label {
  padding: var(--pw-space-4) var(--pw-space-5) var(--pw-space-2);
  font-size: var(--pw-text-xs-size);
  font-weight: var(--pw-font-weight-bold);
  letter-spacing: var(--pw-tracking-caps);
  text-transform: uppercase;
  color: var(--pw-fg-quaternary);
}

.pw-select__option {
  display: flex;
  align-items: center;
  gap: var(--pw-space-5);
  width: 100%;
  min-height: 38px;
  padding: var(--pw-space-3) var(--pw-space-5);
  border: 0;
  border-radius: var(--pw-radius-md);
  background: transparent;
  color: var(--pw-fg-primary);
  font-size: var(--pw-text-sm-size);
  text-align: start;
  cursor: pointer;
  transition: background-color var(--pw-duration-instant) var(--pw-ease-out);
  font-weight: var(--pw-font-weight-medium);
}
.pw-select__option:hover,
.pw-select__option.is-active { background: var(--pw-bg-hover); }
.pw-select__option[aria-selected="true"] { color: var(--pw-fg-brand); font-weight: var(--pw-font-weight-medium); }
.pw-select__option[aria-disabled="true"] { color: var(--pw-fg-disabled); cursor: not-allowed; }
.pw-select__option[aria-disabled="true"]:hover { background: transparent; }
/* A RICH option: a glyph, a name, and a line saying what choosing it does.
   Used where the options are not obviously different from each other -- four
   tones are four words until something tells you what they mean -- and the
   selection is the whole row rather than a tick at the end, because the row is
   what was chosen. */
.pw-select__option--rich { align-items: flex-start; padding: var(--pw-space-5); border-radius: var(--pw-radius-lg); }
.pw-select__option--rich > .pw-icon { margin-block-start: 2px; color: var(--pw-fg-tertiary); }
/* The name keeps the menu item's medium weight. Semibold here made a submenu of
   14px names read a size larger than the 14px menu that opened it -- weight is
   what the eye reads as size when the two lists sit side by side. */
.pw-select__option--rich .pw-select__option-title { font-weight: var(--pw-font-weight-medium); }
/* Chosen is the row being filled, in the page's own neutral. A brand tint would
   make the selected tone a different KIND of thing from the three beside it --
   a link among options -- when all it is is the one that is on. */
.pw-select__option--rich[aria-selected="true"] { background: var(--pw-bg-muted); color: var(--pw-fg-primary); }
.pw-select__option--rich[aria-selected="true"] > .pw-icon { color: var(--pw-fg-primary); }
.pw-select__option--rich[aria-selected="true"] .pw-select__option-desc { color: var(--pw-fg-secondary); }

.pw-select__option-body { flex: 1; min-width: 0; }
.pw-select__option-title { display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* The weight is set, not inherited: the description is a sentence, and a row
   that goes medium or semibold must not take its explanation with it. */
.pw-select__option-desc { display: block; font-size: var(--pw-text-xs-size); line-height: var(--pw-text-xs-line); font-weight: var(--pw-font-weight-regular); color: var(--pw-fg-tertiary); }
.pw-select__option-check { margin-inline-start: auto; color: var(--pw-fg-brand); opacity: 0; }
.pw-select__option[aria-selected="true"] .pw-select__option-check { opacity: 1; }
.pw-select__flag { width: 20px; height: 20px; border-radius: var(--pw-radius-circle); flex: 0 0 auto; }

.pw-select__empty { padding: var(--pw-space-8) var(--pw-space-5); text-align: center; font-size: var(--pw-text-sm-size); color: var(--pw-fg-tertiary); }
.pw-select__footer {
  position: sticky; bottom: calc(var(--pw-space-2) * -1);
  display: flex; gap: var(--pw-space-4); justify-content: flex-end;
  margin: var(--pw-space-2) calc(var(--pw-space-2) * -1) calc(var(--pw-space-2) * -1);
  padding: var(--pw-space-4) var(--pw-space-5);
  border-top: 1px solid var(--pw-border-subtle);
  background: var(--pw-bg-surface);
}

/* Multi-select: selected values render as removable tokens inside the trigger */
.pw-select__tokens { display: flex; flex-wrap: wrap; gap: var(--pw-space-2); align-items: center; }
/* Tokens wrap, so the trigger grows past one line and a pill stops working. */
.pw-select--multi .pw-select__trigger { height: auto; min-height: 40px; padding-block: var(--pw-space-2); border-radius: var(--pw-radius-xl); }
.pw-select__token {
  display: inline-flex; align-items: center; gap: var(--pw-space-2);
  height: 24px; padding-inline: var(--pw-space-4);
  border-radius: var(--pw-radius-sm);
  background: var(--pw-bg-muted);
  font-size: var(--pw-text-xs-size);
}
.pw-select__token button { border: 0; background: none; color: var(--pw-fg-quaternary); cursor: pointer; display: inline-flex; }
.pw-select__token button:hover { color: var(--pw-fg-primary); }

/* =============================================================================
   COMBOBOX  (type-ahead over a select menu)
   Maps place lookup, destination pickers, anything with server-side suggestions.
   ============================================================================= */
.pw-combobox { position: relative; display: flex; width: 100%; }
.pw-combobox .pw-select__menu { max-height: 380px; }
.pw-combobox__row {
  display: flex; align-items: center; gap: var(--pw-space-6);
  width: 100%;
  padding: var(--pw-space-5);
  border: 0; border-radius: var(--pw-radius-md);
  background: transparent; text-align: start; cursor: pointer;
}
.pw-combobox__row:hover, .pw-combobox__row.is-active { background: var(--pw-bg-hover); }
/* The medallion is back, and it fits now.
   It had to go while the field carried a leading magnifier: a 32px disc cannot
   centre on a 16px glyph without shrinking to nothing or dragging the field's
   text inward to meet it. With the magnifier gone from the leading edge there
   is nothing left to align against, so the constraint went with it — the row
   is free to lead with a medallion, and the list reads as a set of results
   rather than a column of loose text. */
.pw-combobox__row-icon {
  display: inline-flex; align-items: center; justify-content: center;
  width: 32px; height: 32px; flex: 0 0 auto;
  border-radius: var(--pw-radius-circle);
  background: var(--pw-bg-muted); color: var(--pw-fg-tertiary);
}
.pw-combobox__row-body { flex: 1; min-width: 0; }
.pw-combobox__row-title { display: block; font-size: var(--pw-text-sm-size); color: var(--pw-fg-primary); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.pw-combobox__row-title mark { background: none; color: inherit; font-weight: var(--pw-font-weight-semibold); }
.pw-combobox__row-sub { display: block; font-size: var(--pw-text-sm-size); color: var(--pw-fg-tertiary); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* A row that carries its own trailing action cannot BE a button: a button
   inside a button is invalid, and the parser hoists the inner one out of the
   row, which takes the row apart. So the row becomes a container, the title
   becomes the control, and the stretched overlay hands it back the whole row
   to be clicked on. Same shape as `.pw-suggest__item`, for the same reason. */
button.pw-combobox__row-title {
  width: 100%;
  border: 0; padding: 0;
  background: transparent;
  font-family: inherit; font-weight: inherit; text-align: start;
  cursor: pointer;
}

/* =============================================================================
   CHECKBOX  ·  RADIO
   The native input stays in the DOM for forms and assistive tech; it is made
   invisible and the visual box is drawn by ::before on the control span.
   ============================================================================= */
.pw-choice {
  display: flex;
  align-items: flex-start;
  gap: var(--pw-space-5);
  cursor: pointer;
}
.pw-choice input { position: absolute; opacity: 0; width: 0; height: 0; }

.pw-choice__box {
  position: relative;
  display: inline-flex; align-items: center; justify-content: center;
  width: 18px; height: 18px; flex: 0 0 auto;
  margin-top: 1px;
  border: 1px solid var(--pw-field-border);
  border-radius: var(--pw-radius-xs);
  background: var(--pw-field-bg);
  color: transparent;
  transition:
    background-color var(--pw-duration-fast) var(--pw-ease-out),
    border-color var(--pw-duration-fast) var(--pw-ease-out),
    color var(--pw-duration-fast) var(--pw-ease-out),
    box-shadow var(--pw-duration-fast) var(--pw-ease-out);
}
.pw-choice--radio .pw-choice__box { border-radius: var(--pw-radius-circle); }
.pw-choice--lg .pw-choice__box { width: 20px; height: 20px; }

.pw-choice:hover .pw-choice__box { border-color: var(--pw-field-border-hover); background: var(--pw-bg-hover); }
.pw-choice input:focus-visible + .pw-choice__box { box-shadow: var(--pw-focus-ring); border-color: var(--pw-field-border-focus); }

.pw-choice input:checked + .pw-choice__box,
.pw-choice input:indeterminate + .pw-choice__box {
  background: var(--pw-action-bg);
  border-color: var(--pw-action-bg);
  color: var(--pw-color-white);
}
.pw-choice:hover input:checked + .pw-choice__box { background: var(--pw-action-bg-hover); border-color: var(--pw-action-bg-hover); }

/* Checkbox tick */
.pw-choice__box .pw-icon { width: 12px; height: 12px; stroke-width: 3.5; opacity: 0; transition: opacity var(--pw-duration-fast) var(--pw-ease-out); }
.pw-choice input:checked + .pw-choice__box .pw-icon { opacity: 1; }
/* Indeterminate bar */
.pw-choice input:indeterminate + .pw-choice__box::after {
  content: ""; position: absolute; width: 9px; height: 2px; border-radius: 1px; background: currentColor;
}
.pw-choice input:indeterminate + .pw-choice__box .pw-icon { opacity: 0; }
/* Radio dot */
.pw-choice--radio .pw-choice__box::after {
  content: ""; width: 6px; height: 6px; border-radius: var(--pw-radius-circle);
  background: currentColor; transform: scale(0);
  transition: transform var(--pw-duration-fast) var(--pw-ease-spring);
}
.pw-choice--radio input:checked + .pw-choice__box::after { transform: scale(1); }

/* -- On imagery rather than on paper ----------------------------------------
   A checkbox standing on a photograph, a poster or a lightbox scrim. Same
   control, same size, same checked state -- only the RESTING material changes,
   because `--pw-field-bg` is paper white and a paper-white box on a dark scrim
   reads as a hole punched in the picture rather than as something to tick.

   The scrim under it is dark in both themes, so nothing here inverts: this is
   the same pair of on-overlay values the lightbox's own controls are made of.
   Checked is deliberately left alone -- solid brand with a white tick already
   carries on imagery, and a modifier should change only what the surface
   actually requires. */
.pw-choice--on-media .pw-choice__box {
  border-color: var(--pw-bg-on-overlay-strong);
  background: var(--pw-bg-on-overlay);
}
.pw-choice--on-media:hover .pw-choice__box {
  border-color: var(--pw-bg-on-overlay-strong);
  background: var(--pw-bg-on-overlay-strong);
}
.pw-choice--on-media .pw-choice__label { color: var(--pw-fg-on-overlay); }
/* The second line keeps its step down from the first, but by opacity rather
   than by a paper grey -- `--pw-fg-tertiary` is mixed for a white page and
   disappears into a scrim. */
.pw-choice--on-media .pw-choice__desc { color: var(--pw-fg-on-overlay); opacity: 0.72; }
/* The ring needs a dark gap to sit in, the way the lightbox's buttons do -- the
   page's own focus ring assumes paper behind it. */
.pw-choice--on-media input:focus-visible + .pw-choice__box {
  border-color: var(--pw-fg-on-overlay);
  box-shadow: 0 0 0 2px var(--pw-bg-lightbox), 0 0 0 4px var(--pw-fg-on-overlay);
}

.pw-choice input:disabled + .pw-choice__box {
  background: var(--pw-bg-disabled);
  border-color: var(--pw-border-subtle);
  color: var(--pw-fg-disabled);
}
.pw-choice:has(input:disabled) { cursor: not-allowed; color: var(--pw-fg-disabled); }

.pw-choice__body { min-width: 0; }
.pw-choice__label { display: block; font-size: var(--pw-text-sm-size); line-height: var(--pw-text-sm-line); color: var(--pw-fg-primary); font-weight: var(--pw-font-weight-medium); }
.pw-choice__desc  { display: block; margin-top: 2px; font-size: var(--pw-text-sm-size); line-height: var(--pw-text-sm-line); color: var(--pw-fg-tertiary); }

/* Card flavour — the whole tile is the hit target (units of measure, plan picker) */
.pw-choice--card {
  padding: var(--pw-space-6);
  border: 1px solid var(--pw-border-default);
  border-radius: var(--pw-radius-lg);
  background: var(--pw-bg-surface);
  transition: border-color var(--pw-duration-fast) var(--pw-ease-out), background-color var(--pw-duration-fast) var(--pw-ease-out);
}
.pw-choice--card:hover { border-color: var(--pw-border-strong); }
.pw-choice--card:has(input:checked) { border-color: var(--pw-color-brand-500); background: var(--pw-bg-selected); }

.pw-choice-group { display: flex; flex-direction: column; gap: var(--pw-space-5); }
.pw-choice-group--row { flex-direction: row; flex-wrap: wrap; gap: var(--pw-space-10); }

/* =============================================================================
   SWITCH
   ============================================================================= */
.pw-switch {
  --pw-switch-w: 36px;
  --pw-switch-h: 20px;
  --pw-switch-pad: 2px;

  position: relative;
  display: inline-block;
  width: var(--pw-switch-w);
  height: var(--pw-switch-h);
  flex: 0 0 auto;
  cursor: pointer;
}
.pw-switch--sm { --pw-switch-w: 28px; --pw-switch-h: 16px; }
.pw-switch--lg { --pw-switch-w: 44px; --pw-switch-h: 24px; --pw-switch-pad: 3px; }
.pw-switch input { position: absolute; inset: 0; opacity: 0; margin: 0; cursor: pointer; }
.pw-switch__track {
  position: absolute; inset: 0;
  border-radius: var(--pw-radius-pill);
  background: var(--pw-color-gray-200);
  transition: background-color var(--pw-duration-base) var(--pw-ease-out);
}
html.dark .pw-switch__track, [data-pw-theme="dark"] .pw-switch__track { background: var(--pw-color-gray-700); }

/* The knob is drawn BY THE TRACK, not by a separate element.
 *
 * It used to need its own `<span class="pw-switch__thumb">`, and a switch
 * written without one rendered as a bare pill with nothing in it — which is
 * not obviously broken, just a switch that appears to have no state. A part
 * you can forget is a part that will be forgotten. Now the markup is the
 * input plus the track, and the knob cannot go missing.
 * `.pw-switch__thumb` is still accepted so existing markup keeps working;
 * it is hidden, because the track is already drawing one. */
.pw-switch__track::after {
  content: "";
  position: absolute;
  top: var(--pw-switch-pad);
  inset-inline-start: var(--pw-switch-pad);
  width: calc(var(--pw-switch-h) - var(--pw-switch-pad) * 2);
  height: calc(var(--pw-switch-h) - var(--pw-switch-pad) * 2);
  border-radius: var(--pw-radius-circle);
  background: var(--pw-color-white);
  box-shadow: 0 1px 2px rgba(16, 24, 40, 0.22);
  transition: transform var(--pw-duration-base) var(--pw-ease-out);
}
.pw-switch__thumb { display: none; }

.pw-switch input:checked ~ .pw-switch__track { background: var(--pw-action-bg); }
.pw-switch input:checked ~ .pw-switch__track::after { transform: translateX(calc(var(--pw-switch-w) - var(--pw-switch-h))); }
[dir="rtl"] .pw-switch input:checked ~ .pw-switch__track::after { transform: translateX(calc((var(--pw-switch-w) - var(--pw-switch-h)) * -1)); }
.pw-switch input:focus-visible ~ .pw-switch__track { box-shadow: var(--pw-focus-ring); }
.pw-switch input:disabled ~ .pw-switch__track { opacity: .5; }
.pw-switch input:disabled { cursor: not-allowed; }

/* Row pattern: label + description on the left, switch on the right */
.pw-switch-row {
  display: flex; align-items: center; gap: var(--pw-space-8);
  padding: var(--pw-space-6) 0;
}
.pw-switch-row__body { flex: 1; min-width: 0; }
.pw-switch-row__label { font-size: var(--pw-text-sm-size); color: var(--pw-fg-primary); font-weight: var(--pw-font-weight-semibold); }
.pw-switch-row__desc  { margin-top: 2px; font-size: var(--pw-text-sm-size); color: var(--pw-fg-tertiary); }

/* =============================================================================
   STEPPER  (numeric − / value / +)
   Title size, paragraph size, guest counts, room counts.
   ============================================================================= */
.pw-stepper {
  display: inline-flex; align-items: center; gap: var(--pw-space-2);
  height: 36px; padding: 3px;
  border: 1px solid var(--pw-border-default);
  border-radius: var(--pw-radius-pill);
  background: var(--pw-bg-surface);
}
.pw-stepper__btn {
  display: inline-flex; align-items: center; justify-content: center;
  width: 28px; height: 28px;
  border: 0; border-radius: var(--pw-radius-circle);
  background: transparent; color: var(--pw-fg-secondary);
  cursor: pointer;
  transition: background-color var(--pw-duration-fast) var(--pw-ease-out), color var(--pw-duration-fast) var(--pw-ease-out);
}
.pw-stepper__btn { position: relative; }
.pw-stepper__btn::after { content: ""; position: absolute; inset: -5px; }
.pw-stepper__btn:hover { background: var(--pw-bg-hover); color: var(--pw-fg-primary); }
.pw-stepper__btn:disabled { color: var(--pw-fg-disabled); cursor: not-allowed; background: transparent; }
/* An <input> with no width takes the UA default of about 20 characters —
   roughly 180px — and `min-width` cannot cap that, so the stepper stretched
   to fit a number that is almost always one digit. The width is explicit and
   the value is centred in it.
 *
 * Fixed, not content-sized: a stepper that resizes as you click it moves the
   + button out from under the finger that is pressing it. `--pw-stepper-value-w`
   widens it for values that carry a unit ("20 pt"). */
.pw-stepper__value {
  width: var(--pw-stepper-value-w, 44px);
  min-width: 0;
  padding: 0;
  text-align: center;
  border: 0; background: transparent; color: var(--pw-fg-primary);
  font-size: var(--pw-text-sm-size); font-weight: var(--pw-font-weight-semibold);
  font-variant-numeric: tabular-nums;
  outline: none;
}
/* A unit needs the room; the number alone does not. */
.pw-stepper--unit { --pw-stepper-value-w: 60px; }
.pw-stepper--boxed { border-radius: var(--pw-radius-md); }
.pw-stepper--boxed .pw-stepper__btn { border-radius: var(--pw-radius-sm); }

/* =============================================================================
   SLIDER
   ============================================================================= */
.pw-slider { position: relative; display: flex; align-items: center; height: 20px; width: 100%; }
.pw-slider input[type="range"] {
  -webkit-appearance: none; appearance: none;
  width: 100%; height: 4px; margin: 0;
  border-radius: var(--pw-radius-pill);
  background: var(--pw-bg-strong);
  outline: none;
}
.pw-slider input[type="range"]::-webkit-slider-thumb {
  -webkit-appearance: none; appearance: none;
  width: 18px; height: 18px;
  border: 2px solid var(--pw-color-white);
  border-radius: var(--pw-radius-circle);
  background: var(--pw-action-bg);
  box-shadow: var(--pw-shadow-sm);
  cursor: pointer;
}
.pw-slider input[type="range"]::-moz-range-thumb {
  width: 14px; height: 14px;
  border: 2px solid var(--pw-color-white);
  border-radius: var(--pw-radius-circle);
  background: var(--pw-action-bg);
  box-shadow: var(--pw-shadow-sm);
  cursor: pointer;
}
.pw-slider input[type="range"]:focus-visible::-webkit-slider-thumb { box-shadow: var(--pw-focus-ring); }
.pw-slider input[type="range"]:disabled { opacity: .5; }
.pw-slider__fill {
  position: absolute; left: 0; height: 4px;
  border-radius: var(--pw-radius-pill);
  background: var(--pw-action-bg);
  pointer-events: none;
}
.pw-slider-scale { display: flex; justify-content: space-between; margin-top: var(--pw-space-3); font-size: var(--pw-text-xs-size); color: var(--pw-fg-quaternary); }

/* =============================================================================
   DATE PICKER  (custom — never the native control)
   Trigger reads like a select: value on the left, calendar glyph on the right.
   Panel supports single date, range, presets and an optional time strip.
   ============================================================================= */
.pw-datepicker { position: relative; display: inline-flex; }
.pw-datepicker--block { display: flex; width: 100%; }

.pw-datepicker__trigger {
  display: flex; align-items: center; gap: var(--pw-space-4);
  width: 100%; height: 40px;
  padding-inline: var(--pw-space-6);
  border: 1px solid var(--pw-field-border);
  border-radius: var(--pw-radius-pill);     /* fully rounded is the default */
  background: var(--pw-field-bg);
  color: var(--pw-field-fg);
  font-size: var(--pw-text-sm-size);
  text-align: start;
  cursor: pointer;
  transition: border-color var(--pw-duration-fast) var(--pw-ease-out), box-shadow var(--pw-duration-fast) var(--pw-ease-out);
}
.pw-datepicker__trigger:hover { border-color: var(--pw-field-border-hover); }
.pw-datepicker__trigger[aria-expanded="true"] { border-color: var(--pw-field-border-focus); box-shadow: var(--pw-focus-ring); outline: none; }
.pw-datepicker__trigger:disabled { background: var(--pw-field-bg-disabled); color: var(--pw-fg-disabled); cursor: not-allowed; }
.pw-datepicker__value { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-weight: var(--pw-font-weight-medium); }
.pw-datepicker__value.is-placeholder { color: var(--pw-field-placeholder); font-weight: var(--pw-font-weight-regular); }
/* Trailing glyph, same position as the select chevron. */
.pw-datepicker__glyph { flex: 0 0 auto; margin-inline-start: auto; color: var(--pw-fg-quaternary); }

.pw-datepicker__panel {
  position: absolute;
  z-index: var(--pw-z-dropdown);
  top: calc(100% + var(--pw-space-2));
  inset-inline-start: 0;
  padding: var(--pw-space-6);
  border: 1px solid var(--pw-border-default);
  border-radius: var(--pw-radius-lg);
  background: var(--pw-bg-surface);
  box-shadow: var(--pw-shadow-xl);
  opacity: 0; visibility: hidden;
  transform: translateY(-4px);
  transition: opacity var(--pw-duration-fast) var(--pw-ease-out), transform var(--pw-duration-fast) var(--pw-ease-out), visibility var(--pw-duration-fast);
}
.pw-datepicker.is-open > .pw-datepicker__panel { opacity: 1; visibility: visible; transform: none; }
.pw-datepicker__panel--end { inset-inline-start: auto; inset-inline-end: 0; }

.pw-calendar { width: 280px; }
.pw-calendar-pair { display: flex; gap: var(--pw-space-8); }
.pw-calendar__header {
  display: flex; align-items: center; justify-content: space-between;
  margin-bottom: var(--pw-space-5);
}
.pw-calendar__title {
  display: inline-flex; align-items: center; gap: var(--pw-space-2);
  padding: var(--pw-space-2) var(--pw-space-4);
  border: 0; border-radius: var(--pw-radius-sm);
  background: transparent; color: var(--pw-fg-primary);
  font-size: var(--pw-text-sm-size); font-weight: var(--pw-font-weight-bold);
  cursor: pointer;
}
.pw-calendar__title:hover { background: var(--pw-bg-hover); }
.pw-calendar__nav {
  display: inline-flex; align-items: center; justify-content: center;
  width: 30px; height: 30px;
  border: 0; border-radius: var(--pw-radius-circle);
  background: transparent; color: var(--pw-fg-secondary); cursor: pointer;
}
.pw-calendar__nav:hover { background: var(--pw-bg-hover); color: var(--pw-fg-primary); }
.pw-calendar__nav:disabled { color: var(--pw-fg-disabled); cursor: not-allowed; background: transparent; }

.pw-calendar__grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 2px; }
.pw-calendar__dow {
  height: 28px; display: grid; place-items: center;
  font-size: var(--pw-text-xs-size); font-weight: var(--pw-font-weight-medium);
  color: var(--pw-fg-quaternary);
}
/* The cell is full-width so a range fill can run edge to edge, while the
   selected marker is a fixed 36px circle drawn behind the number. Without this
   the "circle" becomes an ellipse in any grid wider than 36px. */
.pw-calendar__day {
  position: relative;
  isolation: isolate;
  height: 36px;
  display: grid; place-items: center;
  border: 0;
  background: transparent;
  color: var(--pw-fg-primary);
  font-size: var(--pw-text-xs-size);
  font-variant-numeric: tabular-nums;
  cursor: pointer;
  transition: color var(--pw-duration-instant) var(--pw-ease-out);
}
.pw-calendar__day::before {
  content: "";
  position: absolute;
  z-index: -1;
  top: 50%; left: 50%;
  translate: -50% -50%;
  width: 36px; height: 36px;
  max-width: 100%;
  border-radius: var(--pw-radius-circle);
  background: transparent;
  transition: background-color var(--pw-duration-instant) var(--pw-ease-out);
}
.pw-calendar__day:hover::before { background: var(--pw-bg-hover); }
.pw-calendar__day.is-outside { color: var(--pw-fg-disabled); }
.pw-calendar__day.is-today { font-weight: var(--pw-font-weight-semibold); color: var(--pw-fg-brand); }
.pw-calendar__day.is-today::after {
  content: ""; position: absolute; bottom: 4px;
  width: 4px; height: 4px; border-radius: var(--pw-radius-circle); background: currentColor;
}
.pw-calendar__day.is-selected,
.pw-calendar__day.is-range-start,
.pw-calendar__day.is-range-end {
  color: var(--pw-action-fg);
  font-weight: var(--pw-font-weight-semibold);
}
.pw-calendar__day.is-selected::before,
.pw-calendar__day.is-range-start::before,
.pw-calendar__day.is-range-end::before { background: var(--pw-action-bg); }
.pw-calendar__day.is-selected::after { background: var(--pw-action-fg); }

/* Range fill sits on the cell itself, so it meets the neighbouring cell with
   no gap. The end cells are half-filled towards the middle of the range. */
.pw-calendar__day.is-in-range { background: var(--pw-bg-selected); }
.pw-calendar__day.is-range-start:not(.is-range-end) {
  background: linear-gradient(to right, transparent 50%, var(--pw-bg-selected) 50%);
}
.pw-calendar__day.is-range-end:not(.is-range-start) {
  background: linear-gradient(to left, transparent 50%, var(--pw-bg-selected) 50%);
}
[dir="rtl"] .pw-calendar__day.is-range-start:not(.is-range-end) {
  background: linear-gradient(to left, transparent 50%, var(--pw-bg-selected) 50%);
}
[dir="rtl"] .pw-calendar__day.is-range-end:not(.is-range-start) {
  background: linear-gradient(to right, transparent 50%, var(--pw-bg-selected) 50%);
}

.pw-calendar__day:disabled, .pw-calendar__day.is-disabled {
  color: var(--pw-fg-disabled); cursor: not-allowed; text-decoration: line-through;
}
.pw-calendar__day:disabled::before, .pw-calendar__day.is-disabled::before { background: transparent; }

.pw-calendar__day .pw-calendar__price {
  position: absolute; bottom: 2px;
  font-size: 9px; line-height: 1; color: var(--pw-fg-success);
}
.pw-calendar__day.is-selected .pw-calendar__price { color: inherit; }

/* -- Time picker: the date picker's trigger and panel, with two columns ------ */
.pw-timepicker { position: relative; display: inline-flex; }
.pw-timepicker--block { display: flex; width: 100%; }
.pw-timepicker.is-open > .pw-datepicker__panel { opacity: 1; visibility: visible; transform: none; }
.pw-timepicker__panel { width: 232px; padding: var(--pw-space-5); }
.pw-timepicker__presets { display: flex; flex-wrap: wrap; gap: var(--pw-space-2); margin-block-end: var(--pw-space-5); }
.pw-timepicker__cols { display: grid; grid-template-columns: 1fr 1fr; gap: var(--pw-space-3); }
.pw-timepicker__col { height: 196px; overflow-y: auto; scrollbar-width: thin; padding: var(--pw-space-1); border-radius: var(--pw-radius-md); outline: none; scroll-behavior: smooth; }
.pw-timepicker__col:focus-visible { box-shadow: var(--pw-focus-ring); }
.pw-timepicker__opt { padding: var(--pw-space-3) 0; border-radius: var(--pw-radius-md); text-align: center; font-size: var(--pw-text-sm-size); font-variant-numeric: tabular-nums; color: var(--pw-fg-secondary); cursor: pointer; }
.pw-timepicker__opt:hover { background: var(--pw-bg-hover); color: var(--pw-fg-primary); }
.pw-timepicker__opt.is-selected { background: var(--pw-action-bg); color: var(--pw-action-fg); font-weight: var(--pw-font-weight-semibold); }

.pw-datepicker__presets {
  display: flex; flex-direction: column; gap: var(--pw-space-1);
  padding-inline-end: var(--pw-space-6);
  margin-inline-end: var(--pw-space-6);
  border-inline-end: 1px solid var(--pw-border-subtle);
  min-width: 140px;
}
.pw-datepicker__preset {
  padding: var(--pw-space-3) var(--pw-space-4);
  border: 0; border-radius: var(--pw-radius-md);
  background: transparent; color: var(--pw-fg-secondary);
  font-size: var(--pw-text-sm-size); text-align: start; cursor: pointer;
}
.pw-datepicker__preset:hover { background: var(--pw-bg-hover); color: var(--pw-fg-primary); }
.pw-datepicker__preset.is-active { background: var(--pw-bg-selected); color: var(--pw-fg-brand); font-weight: var(--pw-font-weight-medium); }

.pw-datepicker__footer {
  display: flex; align-items: center; justify-content: space-between; gap: var(--pw-space-6);
  margin-top: var(--pw-space-6); padding-top: var(--pw-space-6);
  border-top: 1px solid var(--pw-border-subtle);
}
.pw-datepicker__readout { font-size: var(--pw-text-sm-size); color: var(--pw-fg-secondary); font-variant-numeric: tabular-nums; font-weight: var(--pw-font-weight-medium); }

.pw-timegrid { display: grid; grid-template-columns: repeat(4, 1fr); gap: var(--pw-space-2); margin-top: var(--pw-space-5); }
.pw-timegrid__slot {
  height: 32px; border: 1px solid var(--pw-border-default); border-radius: var(--pw-radius-pill);
  background: var(--pw-bg-surface); color: var(--pw-fg-secondary);
  font-size: var(--pw-text-sm-size); font-variant-numeric: tabular-nums; cursor: pointer;
  font-weight: var(--pw-font-weight-medium);
}
.pw-timegrid__slot:hover { background: var(--pw-bg-hover); }
.pw-timegrid__slot.is-selected { background: var(--pw-action-bg); border-color: transparent; color: var(--pw-action-fg); }
.pw-timegrid__slot:disabled { color: var(--pw-fg-disabled); cursor: not-allowed; background: var(--pw-bg-disabled); }

/* =============================================================================
   CARD  ·  SURFACE
   ============================================================================= */
/* No `overflow: hidden` here. A card that clips would cut off any select
   menu, date picker or popover opened from inside it — the same bug the row
   group had. Only the media block clips, and it clips itself. */
.pw-card {
  display: flex; flex-direction: column;
  border: 1px solid var(--pw-border-subtle);
  border-radius: var(--pw-radius-lg);
  background: var(--pw-bg-surface);
}
/* A card holding an open layer lifts above its neighbours so the menu is not
   painted under the next card. */
.pw-card:has(.pw-select[aria-expanded="true"]),
.pw-card:has(.pw-select.is-open),
.pw-card:has([aria-expanded="true"]) { position: relative; z-index: 1; }
.pw-card--raised   { box-shadow: var(--pw-shadow-sm); }
/* Never wider than what holds it. A floating surface has a width that suits the
   screen it was drawn for, and on a phone that width is bigger than the phone —
   which does not make the surface narrower, it makes the PAGE wider, and every
   row on it starts scrolling sideways. The cap costs nothing where there is
   room and is the whole difference where there is not. */
.pw-card--floating { border-color: transparent; box-shadow: var(--pw-shadow-lg); max-width: 100%; }
.pw-card--flat     { border-color: transparent; background: var(--pw-bg-subtle); }
.pw-card--interactive { cursor: pointer; transition: border-color var(--pw-duration-fast) var(--pw-ease-out), box-shadow var(--pw-duration-fast) var(--pw-ease-out); }
.pw-card--interactive:hover { border-color: var(--pw-border-strong); box-shadow: var(--pw-shadow-md); }
.pw-card--selected { border-color: var(--pw-color-brand-500); box-shadow: 0 0 0 1px var(--pw-color-brand-500); }

.pw-card__media {
  position: relative; aspect-ratio: 16 / 10; background: var(--pw-bg-muted);
  overflow: hidden;                       /* clips itself, so the card need not */
}
.pw-card > .pw-card__media:first-child {
  border-start-start-radius: calc(var(--pw-radius-lg) - 1px);
  border-start-end-radius: calc(var(--pw-radius-lg) - 1px);
}
.pw-card > .pw-card__media:last-child {
  border-end-start-radius: calc(var(--pw-radius-lg) - 1px);
  border-end-end-radius: calc(var(--pw-radius-lg) - 1px);
}
.pw-card__media > img { width: 100%; height: 100%; object-fit: cover; }
.pw-card__header { display: flex; align-items: flex-start; gap: var(--pw-space-6); padding: var(--pw-space-8); }
.pw-card__body   { padding: var(--pw-space-8); }
.pw-card__header + .pw-card__body { padding-top: 0; }
.pw-card__footer {
  display: flex; align-items: center; gap: var(--pw-space-6);
  padding: var(--pw-space-6) var(--pw-space-8);
  border-top: 1px solid var(--pw-border-subtle);
  background: var(--pw-bg-subtle);
}
.pw-card__title { font-size: var(--pw-text-md-size); line-height: var(--pw-text-md-line); font-weight: var(--pw-font-weight-bold); letter-spacing: var(--pw-tracking-snug); }
.pw-card__subtitle { margin-top: 2px; font-size: var(--pw-text-sm-size); color: var(--pw-fg-tertiary); }

/* Icon medallion used at the head of feature rows and cards.

   RULE — a glyph on a coloured background always inverts in dark mode.
   Light: pale tint + saturated glyph.  Dark: deep translucent tint + light
   glyph. Carrying the light tint into dark mode leaves a bright patch on a dark
   card and drops the glyph's contrast against it. This applies to every tinted
   container in the system: medallions, tags, chips, list marks, badges. */
.pw-medallion {
  display: inline-flex; align-items: center; justify-content: center;
  width: 36px; height: 36px; flex: 0 0 auto;
  border-radius: var(--pw-radius-lg);
  background: var(--pw-bg-muted);
  color: var(--pw-fg-secondary);
}
.pw-medallion--sm { width: 28px; height: 28px; border-radius: var(--pw-radius-md); }
.pw-medallion--lg { width: 44px; height: 44px; border-radius: var(--pw-radius-xl); }
.pw-medallion--circle { border-radius: var(--pw-radius-circle); }
/* One declaration per tone, and the per-theme values live in the tokens — the
   five dark-mode overrides that used to sit here were the same five pairs
   written a second time, and every other tinted patch in the system had to
   copy them or go without. */
.pw-medallion--brand   { background: var(--pw-tint-brand-bg);   color: var(--pw-tint-brand-fg); }
.pw-medallion--success { background: var(--pw-tint-success-bg); color: var(--pw-tint-success-fg); }
.pw-medallion--warning { background: var(--pw-tint-warning-bg); color: var(--pw-tint-warning-fg); }
.pw-medallion--danger  { background: var(--pw-tint-danger-bg);  color: var(--pw-tint-danger-fg); }
.pw-medallion--ai      { background: var(--pw-tint-ai-bg);      color: var(--pw-tint-ai-fg); }

/* =============================================================================
   LIST ROW  ·  SETTING ROW
   The workhorse of the settings surfaces and the maps side panels.
   ============================================================================= */
.pw-row {
  display: flex; align-items: center; gap: var(--pw-space-6);
  width: 100%;
  min-height: 52px;
  padding: var(--pw-space-5) var(--pw-space-6);
  border: 0;
  border-radius: var(--pw-radius-md);
  background: transparent;
  color: inherit;
  text-align: start;
  transition: background-color var(--pw-duration-fast) var(--pw-ease-out);
}
.pw-row--interactive { cursor: pointer; }
.pw-row--interactive:hover { background: var(--pw-bg-hover); }
.pw-row--interactive:active { background: var(--pw-bg-active); }
.pw-row.is-selected { background: var(--pw-bg-selected); }
/* Keeps an open select above the rows beneath it. */
.pw-row:has(.pw-select.is-open),
.pw-row:has([aria-expanded="true"]) { position: relative; z-index: 1; }
.pw-row__icon { color: var(--pw-fg-quaternary); flex: 0 0 auto; }
.pw-row__body { flex: 1; min-width: 0; }
.pw-row__title { font-size: var(--pw-text-sm-size); color: var(--pw-fg-primary); font-weight: var(--pw-font-weight-semibold); }
.pw-row__desc  { margin-top: 2px; font-size: var(--pw-text-sm-size); line-height: var(--pw-text-sm-line); color: var(--pw-fg-tertiary); }
.pw-row__value { font-size: var(--pw-text-sm-size); color: var(--pw-fg-tertiary); white-space: nowrap; }
.pw-row__end   { display: flex; align-items: center; gap: var(--pw-space-4); flex: 0 0 auto; }
.pw-row__chevron { color: var(--pw-fg-quaternary); }
[dir="rtl"] .pw-row__chevron { transform: scaleX(-1); }

/* No `overflow: hidden` — it would clip a select menu or a popover opening out
   of a row. The end rows round their own outer corners instead. */
.pw-row-group {
  display: flex; flex-direction: column;
  border: 1px solid var(--pw-border-subtle);
  border-radius: var(--pw-radius-lg);
  background: var(--pw-bg-surface);
}
.pw-row-group > .pw-row { border-radius: 0; }
.pw-row-group > :first-child {
  border-start-start-radius: calc(var(--pw-radius-lg) - 1px);
  border-start-end-radius: calc(var(--pw-radius-lg) - 1px);
}
.pw-row-group > :last-child {
  border-end-start-radius: calc(var(--pw-radius-lg) - 1px);
  border-end-end-radius: calc(var(--pw-radius-lg) - 1px);
}
.pw-row-group > .pw-row + .pw-row { border-top: 1px solid var(--pw-border-subtle); }
/* A row that carries its own controls is a <div>, so the title stretches to be
   the row's hit area. Never nest a <button> inside a <button>: the parser hoists
   it out of the row entirely, which is how a trailing icon ends up below it. */
.pw-row { position: relative; }
/* Stretches a link over its whole row, so the row is one click target while
   the sibling controls inside it still work.
 *
 * THE HOST MUST BE POSITIONED. `inset: 0` resolves against the nearest
 * positioned ancestor — if the row is `position: static` the overlay escapes
 * all the way to the viewport and silently swallows every click on the page.
 * The rule below positions each container in this system that can host one;
 * add yours to it rather than relying on it happening to work. */
.pw-stretched::after { content: ""; position: absolute; inset: 0; }

.pw-row:has(.pw-stretched),
.pw-suggest__item:has(.pw-stretched),
.pw-card:has(.pw-stretched),
.pw-result:has(.pw-stretched),
.pw-result-card:has(.pw-stretched),
.pw-place-card:has(.pw-stretched),
.pw-property:has(.pw-stretched),
.pw-tile:has(.pw-stretched),
.pw-settings-row:has(.pw-stretched),
.pw-combobox__row:has(.pw-stretched) { position: relative; }
/* `position: relative` alone is enough to sit above the stretched link's
   ::after — a positioned element already paints over a non-positioned one.
   It must NOT take a z-index: that puts every row's end zone into the group's
   stacking context at the same level as an open row, DOM order then decides,
   and a switch two rows further down paints over an open menu. */
.pw-row__end { position: relative; }

.pw-row-group__label {
  padding: 0 var(--pw-space-2) var(--pw-space-4);
  font-size: var(--pw-text-sm-size);
  font-weight: var(--pw-font-weight-semibold);
  color: var(--pw-fg-tertiary);
}

/* =============================================================================
   MENU  (dropdown / context / kebab)
   ============================================================================= */
.pw-menu {
  min-width: 200px;
  padding: var(--pw-space-2);
  border: 1px solid var(--pw-border-default);
  border-radius: var(--pw-radius-lg);
  background: var(--pw-bg-surface);
  box-shadow: var(--pw-shadow-xl);
}

/* A menu attached to a trigger floats: it is a layer, and a layer that takes
   layout space is not a layer. Left static it reserves a box in the flow —
   in a flex row that means the row grows to the menu's height and every
   sibling shifts to meet it.
   Scoped to the popover wrapper rather than applied to `.pw-menu` outright,
   because the docs also render menus flat, in the page, to show their
   contents; those have no trigger and must keep their box. */
.pw-filter-root > .pw-menu,
.pw-menu[data-pw-menu-anchored] {
  position: absolute;
  z-index: var(--pw-z-dropdown);
  top: calc(100% + var(--pw-space-2));
  inset-inline-start: 0;
}
/* Near the trailing edge, hang the menu from that edge instead. */
.pw-filter-root > .pw-menu--end,
.pw-menu--end[data-pw-menu-anchored] { inset-inline-start: auto; inset-inline-end: 0; }
.pw-menu__item {
  display: flex; align-items: center; gap: var(--pw-space-5);
  width: 100%; min-height: 36px;
  padding: var(--pw-space-3) var(--pw-space-5);
  border: 0; border-radius: var(--pw-radius-md);
  background: transparent; color: var(--pw-fg-primary);
  font-size: var(--pw-text-sm-size); text-align: start; cursor: pointer;
  transition: background-color var(--pw-duration-instant) var(--pw-ease-out);
  font-weight: var(--pw-font-weight-medium);
}
.pw-menu__item:hover, .pw-menu__item.is-active { background: var(--pw-bg-hover); }
/* Direct children only. A bare glyph leading a menu row is quiet because it is
   a label's companion, not its equal. An icon inside a medallion is neither —
   the medallion has already declared a background/foreground pair, and the
   glyph is the half of it that carries the hue. Reaching inside would repaint
   the identity the mark exists to state. */
.pw-menu__item > .pw-icon { color: var(--pw-fg-quaternary); }
.pw-menu__item:hover > .pw-icon { color: var(--pw-fg-secondary); }
.pw-menu__item--danger { color: var(--pw-fg-danger); }
.pw-menu__item--danger > .pw-icon, .pw-menu__item--danger:hover > .pw-icon { color: currentColor; }
.pw-menu__item[aria-disabled="true"], .pw-menu__item:disabled { color: var(--pw-fg-disabled); cursor: not-allowed; background: transparent; }
/* An item that carries a list mark needs room for it: the mark is a 28px
   medallion, and a 36px row leaves it 4px of air top and bottom, which reads as
   a row that has burst. Written here rather than inline on each item — five
   copies of `min-height:48px` in one menu is a rule waiting to be named. */
.pw-menu__item:has(.pw-list-mark) { min-height: 48px; gap: var(--pw-space-6); }
/* A radio menu marks its current choice by staying lit, so the selection does
   not disappear the moment the pointer moves off it. */
.pw-menu__item[aria-checked="true"] { background: var(--pw-bg-hover); }
/* The chevron on an item that opens another menu. At the trailing edge, and
   quiet: it is telling you the direction the next list will arrive from, not
   asking to be pressed on its own. */
.pw-menu__more { margin-inline-start: auto; color: var(--pw-fg-quaternary); }
[aria-expanded="true"] > .pw-menu__more { color: var(--pw-fg-secondary); }

.pw-menu__shortcut { margin-inline-start: auto; color: var(--pw-fg-quaternary); font-size: var(--pw-text-xs-size); }
/* The item's own label. A menu item is usually icon + text and nothing else, so
   the text needs no wrapper — but once there is something AFTER it (a check, a
   count) the label has to be the part that grows, and it has to be readable on
   its own so a controller can take the name without the glyphs coming too. */
.pw-menu__item-text { flex: 1; min-width: 0; }
.pw-menu__label {
  padding: var(--pw-space-4) var(--pw-space-5) var(--pw-space-2);
  font-size: var(--pw-text-xs-size); font-weight: var(--pw-font-weight-bold);
  letter-spacing: var(--pw-tracking-caps); text-transform: uppercase;
  color: var(--pw-fg-quaternary);
}
.pw-menu__separator { height: 1px; margin: var(--pw-space-2) calc(var(--pw-space-2) * -1); background: var(--pw-border-subtle); }

/* A row inside a menu is an option, not a settings row. `.pw-row` is built for
   a settings list — 52px tall, so it can carry a title, a description and a
   control at reading scale. Dropped into a menu next to a 36px item, that
   reads as two different lists stacked, and the switches drift apart with
   nothing between them.

   So the layer re-scales what is put in it, exactly as it does for fields:
   menu geometry, and the title takes the menu item's medium weight because it
   is now an option row, not a row title. It is still a `min-height`, so a row
   with a description or a stepper grows past it.

   This exists because six specimens had each invented their own inline
   `padding` here — 6px 10px, 8px 10px, 10px 12px — and none of them touched
   the 52px floor that was the actual problem.
   -------------------------------------------------------------------------- */
.pw-menu > .pw-row,
.pw-select__menu > .pw-row {
  min-height: 36px;
  padding: var(--pw-space-3) var(--pw-space-5);
  gap: var(--pw-space-5);
}
.pw-menu > .pw-row .pw-row__title,
.pw-select__menu > .pw-row .pw-row__title { font-weight: var(--pw-font-weight-medium); }

/* =============================================================================
   TOOLTIP
   ============================================================================= */
.pw-tooltip {
  position: absolute;
  z-index: var(--pw-z-tooltip);
  max-width: 260px;
  padding: var(--pw-space-3) var(--pw-space-5);
  border-radius: var(--pw-radius-md);
  background: var(--pw-bg-inverse);
  color: var(--pw-fg-inverse);
  font-size: var(--pw-text-xs-size);
  line-height: var(--pw-text-xs-line);
  box-shadow: var(--pw-shadow-lg);
  pointer-events: none;
  opacity: 0;
  transform: translateY(2px);
  transition: opacity var(--pw-duration-fast) var(--pw-ease-out), transform var(--pw-duration-fast) var(--pw-ease-out);
}
/* It arrives from the control it names — so the movement points back at what
   it is labelling, whichever side it ended up on. The controller writes the
   side it actually used, which is not always the one that was asked for. */
.pw-tooltip[data-pw-side="bottom"] { transform: translateY(-2px); }
.pw-tooltip[data-pw-side="left"]   { transform: translateX(2px); }
.pw-tooltip[data-pw-side="right"]  { transform: translateX(-2px); }
.pw-tooltip.is-visible { opacity: 1; transform: none; }
/* A shortcut, after the name: keycaps in a share of the tooltip's own ink, so
   they hold in both themes. */
.pw-tooltip__keys { display: inline-flex; gap: var(--pw-space-1); margin-inline-start: var(--pw-space-4); vertical-align: 1px; }
/* The system's keycap, redrawn for the tooltip's inverse ground. */
.pw-tooltip__keys .pw-kbd {
  min-width: 18px; height: 18px; padding: 0 5px;
  border: 0; border-radius: var(--pw-radius-xs);
  background: color-mix(in srgb, currentColor 18%, transparent);
  color: inherit;
  font-family: inherit; font-size: 11px; font-weight: var(--pw-font-weight-medium); line-height: 1;
}
.pw-tooltip__arrow { position: absolute; width: 8px; height: 8px; background: inherit; transform: rotate(45deg); }
.pw-tooltip__title { display: block; font-weight: var(--pw-font-weight-semibold); margin-bottom: 2px; }

/* =============================================================================
   POPOVER
   ============================================================================= */
.pw-popover {
  position: absolute;
  z-index: var(--pw-z-popover);
  width: 320px;
  max-width: 100%;          /* never wider than what holds it */
  border: 1px solid var(--pw-border-default);
  border-radius: var(--pw-radius-xl);
  background: var(--pw-bg-surface);
  box-shadow: var(--pw-shadow-xl);
  overflow: hidden;
}
.pw-popover__header {
  padding: var(--pw-space-6) var(--pw-space-8) var(--pw-space-4);
}
.pw-popover__title { font-size: var(--pw-text-sm-size); font-weight: var(--pw-font-weight-bold); }
.pw-popover__subtitle { margin-top: 2px; font-size: var(--pw-text-sm-size); color: var(--pw-fg-tertiary); }
.pw-popover__body { padding: var(--pw-space-2) var(--pw-space-4) var(--pw-space-4); }
.pw-popover__footer { padding: var(--pw-space-5) var(--pw-space-6); border-top: 1px solid var(--pw-border-subtle); }

/* =============================================================================
   MODAL  ·  DIALOG
   ============================================================================= */
.pw-overlay {
  position: fixed; inset: 0;
  z-index: var(--pw-z-overlay);
  background: var(--pw-bg-overlay);
  backdrop-filter: blur(var(--pw-blur-scrim));
  opacity: 0;
  transition: opacity var(--pw-duration-base) var(--pw-ease-out);
}
.pw-overlay.is-open { opacity: 1; }

.pw-modal {
  position: fixed;
  z-index: var(--pw-z-modal);
  inset: 0;
  display: grid;
  place-items: center;
  padding: var(--pw-space-8);
  pointer-events: none;
}
/* A modal stops growing at 720px, or at the visible viewport less its gutter
   if that is smaller, and from there its BODY scrolls -- the title, the close
   and the footer's actions stay where they are. Grown to the full height of a
   desktop window, a dialog reads as a page that has lost its chrome, and its
   Done button walks off the bottom of the eye's range.
   `dvh`, with `vh` before it for older engines: on a phone 100vh includes the
   address bar, and the footer would sit under it. */
.pw-modal__panel {
  width: min(520px, 100%);
  max-height: min(720px, calc(100vh - var(--pw-space-16)));
  max-height: min(720px, calc(100dvh - var(--pw-space-16)));
  display: flex; flex-direction: column;
  border-radius: var(--pw-radius-2xl);
  background: var(--pw-bg-surface);
  box-shadow: var(--pw-shadow-modal);
  pointer-events: auto;
  transition: opacity var(--pw-duration-base) var(--pw-ease-out), transform var(--pw-duration-base) var(--pw-ease-out);
}
/* Same rule as the other layers: the closed state only applies once the JS
   controller has claimed the modal. A panel rendered on its own — a docs
   example, a server render before hydration — must not sit at opacity 0
   with nothing coming to turn it on. */
.pw-modal[data-pw-layer=""] .pw-modal__panel {
  opacity: 0;
  transform: translateY(8px) scale(0.98);
}
.pw-modal[data-pw-layer=""].is-open .pw-modal__panel { opacity: 1; transform: none; }
.pw-modal__panel--sm { width: min(400px, 100%); }
.pw-modal__panel--lg { width: min(720px, 100%); }
.pw-modal__panel--xl { width: min(960px, 100%); }
.pw-modal__panel--full { width: 100%; height: 100%; max-height: none; border-radius: 0; }

/* Grid, not flex: the title and subtitle stack in the first column on their
   own and the close button pins to the second, so the header lays out
   correctly whether or not the text is wrapped in a div and whether or not
   there is a close button at all. As a flex row the subtitle became a
   sibling column and sat beside the title. */
.pw-modal__header {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: start;
  column-gap: var(--pw-space-6);
  padding: var(--pw-space-10) var(--pw-space-10) var(--pw-space-6);
}
.pw-modal__header > * { grid-column: 1; min-width: 0; }
.pw-modal__title { font-size: var(--pw-text-xl-size); line-height: var(--pw-text-xl-line); font-weight: var(--pw-font-weight-bold); letter-spacing: var(--pw-tracking-snug); }
.pw-modal__subtitle { margin-top: var(--pw-space-1); font-size: var(--pw-text-sm-size); line-height: var(--pw-text-md-line); color: var(--pw-fg-tertiary); }
.pw-modal__close { grid-column: 2; grid-row: 1; margin-top: -4px; }
/* `min-height: 0` is what lets it scroll. A flex item will not shrink below
   its content by default, so without it the body pushes the panel past its
   cap instead of scrolling inside it. `overscroll-behavior` keeps a scroll
   that reaches the end from moving the page behind the scrim. */
/* A scrolling box clips whatever is drawn outside it, and a field's focus ring
   is a 4px shadow OUTSIDE the field -- so the first field in a body with no
   top padding lost the top of its ring. The body keeps 4px of room top and
   bottom and gives it straight back with a negative margin: nothing moves,
   but a ring at either edge now has space inside the clip. The sides already
   have 20px. */
.pw-modal__body {
  --pw-ring-room: 4px;
  padding: var(--pw-ring-room) var(--pw-space-10);
  margin-block: calc(var(--pw-ring-room) * -1);
  overflow-y: auto; overscroll-behavior: contain; flex: 1 1 auto; min-height: 0;
}
.pw-modal__footer {
  display: flex; align-items: center; justify-content: flex-end; gap: var(--pw-space-4);
  padding: var(--pw-space-8) var(--pw-space-10) var(--pw-space-10);
}
.pw-modal__footer--split { justify-content: space-between; }
.pw-modal__footer--stacked { flex-direction: column-reverse; align-items: stretch; }

/* =============================================================================
   SHEET  (bottom, mobile)  ·  DRAWER  (side)
   ============================================================================= */
.pw-sheet {
  position: fixed; inset-inline: 0; bottom: 0;
  z-index: var(--pw-z-sheet);
  display: flex; flex-direction: column;
  max-height: 92vh;
  padding-bottom: env(safe-area-inset-bottom, 0);
  border-radius: var(--pw-radius-3xl) var(--pw-radius-3xl) 0 0;
  background: var(--pw-bg-surface);
  box-shadow: var(--pw-shadow-2xl);
  transform: translateY(100%);
  transition: transform var(--pw-duration-slow) var(--pw-ease-out);
}
.pw-sheet.is-open { transform: none; }
.pw-sheet__handle {
  width: 44px; height: 5px;
  margin: var(--pw-space-4) auto var(--pw-space-2);
  border-radius: var(--pw-radius-pill);
  background: var(--pw-border-strong);
  flex: 0 0 auto;
  cursor: grab;
}
.pw-sheet__header { display: flex; align-items: center; gap: var(--pw-space-6); padding: var(--pw-space-5) var(--pw-space-8); }
.pw-sheet__body { flex: 1; overflow-y: auto; padding: var(--pw-space-4) var(--pw-space-8) var(--pw-space-10); }
/* Detents — collapsed peek, half, full */
.pw-sheet--peek { height: 180px; }
.pw-sheet--half { height: 52vh; }
.pw-sheet--full { height: 92vh; }

.pw-drawer {
  position: fixed; inset-block: 0; inset-inline-end: 0;
  z-index: var(--pw-z-sheet);
  display: flex; flex-direction: column;
  width: min(440px, 100vw);
  background: var(--pw-bg-surface);
  border-inline-start: 1px solid var(--pw-border-subtle);
  box-shadow: var(--pw-shadow-2xl);
  transform: translateX(100%);
  transition: transform var(--pw-duration-slow) var(--pw-ease-out);
}
[dir="rtl"] .pw-drawer { transform: translateX(-100%); }
.pw-drawer.is-open { transform: none; }
.pw-drawer--start { inset-inline-end: auto; inset-inline-start: 0; border-inline: 0; border-inline-end: 1px solid var(--pw-border-subtle); transform: translateX(-100%); }
[dir="rtl"] .pw-drawer--start { transform: translateX(100%); }
.pw-drawer__header {
  display: flex; align-items: center; gap: var(--pw-space-6);
  padding: var(--pw-space-8);
  border-bottom: 1px solid var(--pw-border-subtle);
}
/* A row in a drawer is a navigation item, not a settings row: it carries a
   label and nothing else, so it does not need the 52px a title-plus-
   description row does. Fully rounded, because the hover is a pill floating in
   a padded list rather than a band spanning a table. */
.pw-drawer .pw-row {
  min-height: 40px;
  padding-block: var(--pw-space-3);
  border-radius: var(--pw-radius-pill);
}

.pw-drawer__body { flex: 1; overflow-y: auto; padding: var(--pw-space-8); }
.pw-drawer__footer { padding: var(--pw-space-6) var(--pw-space-8); border-top: 1px solid var(--pw-border-subtle); }

/* =============================================================================
   ALERT  (inline, static)
   ============================================================================= */
.pw-alert {
  display: flex; align-items: flex-start; gap: var(--pw-space-5);
  padding: var(--pw-space-6);
  border: 1px solid var(--pw-border-default);
  border-radius: var(--pw-radius-lg);
  background: var(--pw-bg-subtle);
  color: var(--pw-fg-secondary);
  font-size: var(--pw-text-sm-size);
  line-height: var(--pw-text-sm-line);
}
.pw-alert__icon { flex: 0 0 auto; color: var(--pw-fg-quaternary); margin-top: 1px; }
.pw-alert__body { flex: 1; min-width: 0; }
.pw-alert__title { font-size: var(--pw-text-sm-size); font-weight: var(--pw-font-weight-bold); color: var(--pw-fg-primary); margin-bottom: 2px; }
.pw-alert__actions { display: flex; gap: var(--pw-space-4); margin-top: var(--pw-space-5); }
.pw-alert__close { margin-inline-start: auto; }

.pw-alert--info    { background: var(--pw-color-info-50);    border-color: var(--pw-color-brand-200); }
.pw-alert--info    .pw-alert__icon { color: var(--pw-color-info-600); }
.pw-alert--success { background: var(--pw-color-success-50);  border-color: var(--pw-color-success-150); }
.pw-alert--success .pw-alert__icon { color: var(--pw-color-success-650); }
.pw-alert--warning { background: var(--pw-color-warning-25);  border-color: var(--pw-color-warning-100); }
.pw-alert--warning .pw-alert__icon { color: var(--pw-color-warning-600); }
.pw-alert--danger  { background: var(--pw-color-danger-25);   border-color: var(--pw-color-danger-150); }
.pw-alert--danger  .pw-alert__icon { color: var(--pw-color-danger-600); }

html.dark .pw-alert--info, [data-pw-theme="dark"] .pw-alert--info { background: rgba(11,165,236,.12);  border-color: rgba(11,165,236,.28); }
html.dark .pw-alert--success, [data-pw-theme="dark"] .pw-alert--success { background: rgba(18,183,106,.12);  border-color: rgba(18,183,106,.28); }
html.dark .pw-alert--warning, [data-pw-theme="dark"] .pw-alert--warning { background: rgba(247,144,9,.12);   border-color: rgba(247,144,9,.28); }
html.dark .pw-alert--danger, [data-pw-theme="dark"] .pw-alert--danger { background: rgba(240,68,56,.12);   border-color: rgba(240,68,56,.28); }
html.dark .pw-alert--info    .pw-alert__icon, [data-pw-theme="dark"] .pw-alert--info    .pw-alert__icon { color: var(--pw-color-info-500); }
html.dark .pw-alert--success .pw-alert__icon, [data-pw-theme="dark"] .pw-alert--success .pw-alert__icon { color: var(--pw-color-success-400); }
html.dark .pw-alert--warning .pw-alert__icon, [data-pw-theme="dark"] .pw-alert--warning .pw-alert__icon { color: var(--pw-color-warning-400); }
html.dark .pw-alert--danger  .pw-alert__icon, [data-pw-theme="dark"] .pw-alert--danger  .pw-alert__icon { color: var(--pw-color-danger-350); }

/* Full-bleed page banner */
.pw-banner {
  display: flex; align-items: center; gap: var(--pw-space-6);
  padding: var(--pw-space-5) var(--pw-space-8);
  border-radius: 0;
  border-inline: 0; border-top: 0;
}

/* =============================================================================
   TOAST
   ============================================================================= */
/* Toasts STACK rather than list. Three messages in a column is a column of
   messages — a panel you did not ask for, sitting over the page. Piled, they
   are one object with a depth to it: the newest is readable and the ones behind
   it are visibly still there, 14px and 5% at a time. Hovering, or moving focus
   in, fans them out; leaving folds them back.

   The region is a block with absolutely positioned children, not a flex column.
   Cards of different heights cannot be piled by a layout that gives each one a
   row, and the fan-out has to be a TRANSFORM to animate at all: laying them out
   in flow and back is a reflow, and a reflow does not tween. The controller
   measures the heights and writes the offsets; the stylesheet decides what a
   fold and a fan look like. */
.pw-toast-region {
  position: fixed;
  z-index: var(--pw-z-toast);
  inset-inline-end: var(--pw-space-8);
  bottom: var(--pw-space-8);
  width: min(380px, calc(100vw - var(--pw-space-16)));
  pointer-events: none;
  transition: height var(--pw-duration-base) var(--pw-ease-out);
}
.pw-toast-region--top { bottom: auto; top: var(--pw-space-8); }
.pw-toast-region--center { inset-inline: 0; margin-inline: auto; }

.pw-toast {
  /* --i is the depth from the front, written by the controller. --y is where
     this card sits: its fold offset when piled, the sum of the heights in
     front of it when fanned. --in is the enter/leave nudge, kept separate so
     the two movements compose instead of overwriting each other. */
  --pw-toast-i: 0;
  --pw-toast-y: 0px;
  --pw-toast-in: 12px;
  position: absolute;
  inset-inline: 0;
  bottom: 0;
  /* Not a card: a TAB carrying the outcome, and a panel under it carrying the
     detail — one shape assembled from two boxes, joined by a fillet. The tab
     hugs the region's outer edge so the pile reads as a stack of labelled
     things rather than a stack of rectangles.

     The shadow is a filter rather than a box-shadow: `box-shadow` traces each
     border box, which would draw the tab's shadow across the top of the panel
     it is sitting on. `drop-shadow` traces the painted alpha, so the two boxes
     and the fillet between them cast one shadow, as one object. */
  display: flex; flex-direction: column; align-items: flex-end;
  filter: var(--pw-shadow-drop);
  pointer-events: auto;
  opacity: 0;
  transform-origin: bottom center;
  transform:
    translateY(calc(var(--pw-toast-y) + var(--pw-toast-in)))
    scale(calc(1 - var(--pw-toast-i) * 0.05));
  overflow: hidden;          /* a clamped card behind the pile must not spill */
  transition:
    opacity var(--pw-duration-base) var(--pw-ease-out),
    transform var(--pw-duration-base) var(--pw-ease-out),
    height var(--pw-duration-base) var(--pw-ease-out);
}
.pw-toast.is-open { opacity: 1; --pw-toast-in: 0px; }
.pw-toast.is-leaving { opacity: 0; --pw-toast-in: 6px; }
/* Fanned out, every card is its own size again. */
.pw-toast-region.is-expanded .pw-toast { --pw-toast-i: 0; }
/* Piled, only the front card and the two slivers behind it are worth drawing.
   A fourth card adds a line of shadow and nothing you can read. */
.pw-toast-region:not(.is-expanded) .pw-toast[data-pw-buried] {
  opacity: 0;
  pointer-events: none;
}
/* A pill is sized by its content, so it hangs from the trailing edge instead
   of filling the region's width. */
.pw-toast--pill { inset-inline-start: auto; }

/* -- The two boxes -----------------------------------------------------------
   The tab is the answer — an icon and a few words in the variant's own colour,
   which is the only place colour appears. The panel is the sentence under it,
   and it is quiet: a toast that shouts twice has said nothing twice.
   ---------------------------------------------------------------------------- */
/* `-raised`, not `-surface`. In light they are the same white; in dark the
   raised one is a step lighter than the page, and this shape has no border to
   fall back on — a fill-only surface separated by a shadow is separated by
   nothing at all once the page behind it is dark. */
.pw-toast__tab {
  position: relative;
  display: inline-flex; align-items: center; gap: var(--pw-space-4);
  max-width: 100%;
  padding: var(--pw-space-3) var(--pw-space-6);
  background: var(--pw-bg-surface-raised);
  border-start-start-radius: var(--pw-radius-lg);
  border-start-end-radius: var(--pw-radius-lg);
  font-size: var(--pw-text-xs-size); line-height: var(--pw-text-xs-line);
  font-weight: var(--pw-font-weight-bold);
  color: var(--pw-fg-primary);
}
.pw-toast__tab > .pw-icon,
.pw-toast__tab > .pw-spinner { flex: 0 0 auto; }
/* The joint. A square of the same fill sitting beside the tab's inner corner
   with a quarter-disc taken out of it: the arc runs from the tab's edge to the
   panel's, tangent to both, which is what makes the two boxes read as one
   poured shape rather than two stuck together. */
.pw-toast:has(.pw-toast__panel) .pw-toast__tab::after {
  content: "";
  position: absolute;
  inset-block-end: 0;
  inset-inline-end: 100%;
  width: var(--pw-radius-lg); height: var(--pw-radius-lg);
  background: radial-gradient(circle var(--pw-radius-lg) at 0 0,
              transparent var(--pw-radius-lg), var(--pw-bg-surface-raised) var(--pw-radius-lg));
}
[dir="rtl"] .pw-toast:has(.pw-toast__panel) .pw-toast__tab::after {
  background: radial-gradient(circle var(--pw-radius-lg) at 100% 0,
              transparent var(--pw-radius-lg), var(--pw-bg-surface-raised) var(--pw-radius-lg));
}
/* On its own it is a pill — nothing under it for the square corners to meet,
   and nothing hanging off it either, so it can clip its own countdown. The tab
   of a two-part toast must NOT clip: the fillet is drawn outside it. */
.pw-toast:not(:has(.pw-toast__panel)) .pw-toast__tab {
  border-radius: var(--pw-radius-pill);
  overflow: hidden;
}
/* A card behind the fold shows its tab and nothing else, so the tab is on its
   own again and has to look it: square bottom corners flowing into a panel that
   has been cropped away, plus a fillet joining it to nothing, is a shape with a
   piece missing. Both go, and come back when the pile fans out. */
.pw-toast-region:not(.is-expanded) .pw-toast:not([data-pw-front]) .pw-toast__tab {
  border-radius: var(--pw-radius-pill);
}
.pw-toast-region:not(.is-expanded) .pw-toast:not([data-pw-front]) .pw-toast__tab::after {
  opacity: 0;
}
.pw-toast__tab,
.pw-toast__tab::after {
  transition:
    border-radius var(--pw-duration-base) var(--pw-ease-out),
    opacity var(--pw-duration-base) var(--pw-ease-out);
}

.pw-toast__panel {
  position: relative;
  /* Clips the countdown to the panel's own corners. Safe here: nothing in a
     panel is meant to escape it — the fillet belongs to the tab. */
  overflow: hidden;
  width: 100%;
  display: flex; align-items: flex-start; gap: var(--pw-space-6);
  padding: var(--pw-space-6) var(--pw-space-6) var(--pw-space-7);
  background: var(--pw-bg-surface-raised);
  border-radius: var(--pw-radius-lg);
  border-start-end-radius: 0;
  font-size: var(--pw-text-sm-size); line-height: var(--pw-text-md-line);
  color: var(--pw-fg-secondary);
}
.pw-toast__time {
  flex: 0 0 auto;
  margin-inline-start: auto;
  font-size: var(--pw-text-xs-size);
  font-weight: var(--pw-font-weight-regular);
  color: var(--pw-fg-quaternary);
  font-variant-numeric: tabular-nums;
}
.pw-toast--success .pw-toast__tab { color: var(--pw-fg-success); }
.pw-toast--danger  .pw-toast__tab { color: var(--pw-fg-danger); }
.pw-toast--warning .pw-toast__tab { color: var(--pw-fg-warning); }
.pw-toast--info    .pw-toast__tab { color: var(--pw-fg-info); }
/* The specimen shows toasts in flow. Nothing is stacking them there, so the
   pile's geometry has to stay out of the way. */
.pw-toast[style*="static"] { transform: none; }
.pw-toast__icon { flex: 0 0 auto; }
/* The tab's words. It truncates rather than wrapping: a tab is a label, and a
   label that runs to two lines is a paragraph that has lost its panel. */
.pw-toast__label { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.pw-toast__text { min-width: 0; }
.pw-toast__body { flex: 1; min-width: 0; }
.pw-toast__action { margin-top: var(--pw-space-5); }
.pw-toast__action .pw-btn { padding-inline: 0; }
/* Compact dark "snackbar" used over the map canvas */
/* Compact dark "snackbar" used over the map canvas: both boxes and the fillet
   between them take the inverse fill, or the joint stays light and the shape
   comes apart at exactly the point that is meant to prove it is one. */
.pw-toast--inverse .pw-toast__tab,
.pw-toast--inverse .pw-toast__panel { background: var(--pw-bg-inverse); color: var(--pw-fg-inverse); }
.pw-toast--inverse .pw-toast__tab::after {
  background: radial-gradient(circle var(--pw-radius-lg) at 0 0,
              transparent var(--pw-radius-lg), var(--pw-bg-inverse) var(--pw-radius-lg));
}
.pw-toast--inverse .pw-toast__time { color: var(--pw-fg-inverse); opacity: .6; }

/* -- Pending, then the answer -------------------------------------------------
   Work that takes a moment gets ONE toast, not two. It arrives as a compact
   pill while the work runs and grows into the outcome in place.

   Two toasts — "Saving…" leaving, "Saved" arriving — tell the same story twice
   and read as two separate events; worse, the second one appears at a spot the
   eye has already finished with. One box that changes shape is the honest
   shape of one operation that changed state.
   ---------------------------------------------------------------------------- */
.pw-toast--pill {
  width: max-content;
  max-width: 100%;
  align-items: center;
  padding-block: var(--pw-space-4);
  border-radius: var(--pw-radius-pill);
}
.pw-toast--pill .pw-toast__body { flex: 0 1 auto; }

/* The morph, in order: the tab settles, then the panel unfolds from under it.
   Both animate a number the controller measured — the tab's width, the panel's
   height — because `auto` cannot be interpolated. Spring easing, because this
   is the one moment in the component where something physically changes size,
   and the panel waits out most of the tab's move before it starts. */
.pw-toast.is-morphing .pw-toast__tab {
  overflow: hidden;
  white-space: nowrap;
  transition:
    width var(--pw-duration-slow) var(--pw-ease-spring),
    border-radius var(--pw-duration-slow) var(--pw-ease-out);
}
.pw-toast.is-morphing .pw-toast__panel {
  overflow: hidden;
  transition:
    height var(--pw-duration-slow) var(--pw-ease-spring) var(--pw-duration-fast),
    opacity var(--pw-duration-base) var(--pw-ease-out) var(--pw-duration-fast);
}
.pw-toast.is-morphing .pw-toast__panel > * { animation: pw-toast-swap var(--pw-duration-base) var(--pw-ease-out) var(--pw-duration-base) both; }
@keyframes pw-toast-swap { from { opacity: 0; transform: translateY(3px); } }

/* Time left, as a hairline rather than a number. The BOX it sits in clips it —
   inheriting the corner radii cannot: a radius is scaled down to fit the box it
   is on, so 12px on a strip 2px tall resolves to about 2px and the ends run
   straight out past the corner they were meant to follow. */
.pw-toast__progress {
  position: absolute; inset-inline: 0; bottom: 0;
  z-index: 1;
  height: 2px;
  overflow: hidden;
  background: var(--pw-bg-muted);
}
.pw-toast__progress > i {
  display: block; height: 100%; width: 100%;
  transform-origin: left center;
  background: var(--pw-fg-quaternary);
  animation: pw-toast-countdown linear forwards;
}
[dir="rtl"] .pw-toast__progress > i { transform-origin: right center; }
@keyframes pw-toast-countdown { from { transform: scaleX(1); } to { transform: scaleX(0); } }
.pw-toast--success .pw-toast__progress > i { background: var(--pw-fg-success); }
.pw-toast--danger  .pw-toast__progress > i { background: var(--pw-fg-danger); }
.pw-toast--warning .pw-toast__progress > i { background: var(--pw-fg-warning); }
.pw-toast--info    .pw-toast__progress > i { background: var(--pw-fg-info); }
/* Reading is not idling: while the pointer is on the toast, or focus is inside
   it, the bar and the timer under it both stop. A toast that keeps counting
   down while someone is reading it is a bug, not a feature. */
.pw-toast.is-held .pw-toast__progress > i { animation-play-state: paused; }
/* Only the front card counts down where you can see it. A countdown drawn
   across a 14px sliver is a coloured line with nowhere to go — and the card it
   belongs to is not the one being read. */
.pw-toast-region:not(.is-expanded) .pw-toast:not([data-pw-front]) .pw-toast__progress { opacity: 0; }

@media (prefers-reduced-motion: reduce) {
  .pw-toast.is-morphing .pw-toast__tab,
  .pw-toast.is-morphing .pw-toast__panel { transition: none; }
  .pw-toast.is-morphing .pw-toast__panel > * { animation: none; }
  .pw-toast__progress > i { animation: none; transform: scaleX(1); }
}

/* =============================================================================
   COACH MARK  (product onboarding callout)
   A light card that shows the thing it announces. The cover is a crop of the
   feature itself on a soft mesh, fading into the card, so the eye reads the
   change before the words; the title and a sentence sit centred under it,
   and the actions close it. The tone tints the mesh, not the whole card:
   brand for something new, success for how data is handled, neutral for
   anything else.
   ============================================================================= */
.pw-coach {
  --pw-coach-a: color-mix(in srgb, var(--pw-color-brand-300) 70%, transparent);
  --pw-coach-b: color-mix(in srgb, var(--pw-color-purple-500) 30%, transparent);
  --pw-coach-c: color-mix(in srgb, var(--pw-color-warning-100) 90%, transparent);
  position: relative;
  width: 300px;
  padding: var(--pw-space-3);
  border-radius: var(--pw-radius-3xl);
  background: var(--pw-bg-surface-raised);
  color: var(--pw-fg-primary);
  box-shadow: 0 0 0 1px var(--pw-border-subtle), var(--pw-shadow-xl);
  text-align: center;
}
.pw-coach--success {
  --pw-coach-a: color-mix(in srgb, var(--pw-color-success-300) 70%, transparent);
  --pw-coach-b: color-mix(in srgb, var(--pw-color-brand-200) 60%, transparent);
  --pw-coach-c: color-mix(in srgb, var(--pw-color-success-100) 90%, transparent);
}
.pw-coach--neutral {
  --pw-coach-a: color-mix(in srgb, var(--pw-color-gray-200) 90%, transparent);
  --pw-coach-b: color-mix(in srgb, var(--pw-color-brand-100) 70%, transparent);
  --pw-coach-c: color-mix(in srgb, var(--pw-color-gray-100) 90%, transparent);
}
/* Caution: something is about to leave PrivacyWall's protection -- a site
   opened directly, seeing the visitor's IP. Not an error and not a warning
   dialog: the action still happens, and one press away. The mesh warms and
   the mark takes the warning ink, so it reads as "know this first" rather
   than as another new feature. */
.pw-coach--caution {
  --pw-coach-a: color-mix(in srgb, var(--pw-color-warning-400) 45%, transparent);
  --pw-coach-b: color-mix(in srgb, var(--pw-color-warning-100) 80%, transparent);
  --pw-coach-c: color-mix(in srgb, var(--pw-color-warning-50) 90%, transparent);
}
.pw-coach--caution .pw-coach__appicon { color: var(--pw-tint-warning-fg); }
.pw-coach__text strong { font-weight: var(--pw-font-weight-semibold); color: var(--pw-fg-primary); }

/* The cover. The crop runs off its bottom edge and the card's own surface
   rises over it, so the product fades in rather than stopping at a line. */
.pw-coach__cover {
  position: relative; height: 148px; overflow: hidden; border-radius: var(--pw-radius-2xl);
  background:
    radial-gradient(80% 90% at 8% 0%, var(--pw-coach-a), transparent 70%),
    radial-gradient(70% 80% at 100% 10%, var(--pw-coach-b), transparent 70%),
    radial-gradient(90% 70% at 60% 100%, var(--pw-coach-c), transparent 70%),
    var(--pw-bg-subtle);
}
.pw-coach__cover::after {
  content: ""; position: absolute; inset: auto 0 0; height: 56px; pointer-events: none;
  background: linear-gradient(to bottom, transparent, var(--pw-bg-surface-raised));
}
.pw-coach__cover--sm { height: 112px; }
/* The feature's own mark, overlapping the crop's corner. */
.pw-coach__appicon {
  position: absolute; z-index: 2; top: 10px; inset-inline-start: 10px;
  display: grid; place-items: center; width: 40px; height: 40px; border-radius: var(--pw-radius-lg);
  background: var(--pw-bg-surface-raised); color: var(--pw-fg-primary);
  box-shadow: 0 0 0 1px var(--pw-border-subtle), 0 6px 16px -6px color-mix(in srgb, var(--pw-color-gray-900) 30%, transparent);
}
.pw-coach__appicon .pw-icon { width: 20px; height: 20px; }
/* The mark sits over the window's corner; the window's own dots start clear of it. */
.pw-coach__appicon ~ .pw-coach__win .pw-coach__winbar { padding-inline-start: 28px; }

.pw-coach__eyebrow { display: flex; align-items: center; justify-content: center; gap: var(--pw-space-2); margin-top: var(--pw-space-5); font-size: var(--pw-text-xs-size); line-height: var(--pw-text-xs-line); font-weight: var(--pw-font-weight-medium); color: var(--pw-fg-tertiary); }
.pw-coach__title { display: flex; align-items: center; justify-content: center; gap: var(--pw-space-3); margin: var(--pw-space-2) var(--pw-space-4) 0; font-size: var(--pw-text-lg-size); line-height: var(--pw-text-lg-line); font-weight: var(--pw-font-weight-semibold); letter-spacing: -0.01em; }
.pw-coach__cover + .pw-coach__title { margin-top: var(--pw-space-5); }
.pw-coach__text { margin: var(--pw-space-2) var(--pw-space-5) 0; font-size: var(--pw-text-sm-size); line-height: var(--pw-text-sm-line); color: var(--pw-fg-secondary); text-wrap: pretty; }
.pw-coach__version { flex: none; padding: 1px var(--pw-space-3); border-radius: var(--pw-radius-pill); background: var(--pw-tint-brand-bg); color: var(--pw-tint-brand-fg); font-size: 11px; font-weight: var(--pw-font-weight-semibold); line-height: 16px; }
.pw-coach__actions { display: flex; align-items: center; justify-content: flex-end; gap: var(--pw-space-3); margin: var(--pw-space-6) var(--pw-space-2) var(--pw-space-2); }
.pw-coach__actions .pw-btn { border-radius: var(--pw-radius-pill); }
.pw-coach__dismiss { position: absolute; z-index: 2; top: var(--pw-space-4); inset-inline-end: var(--pw-space-4); background: color-mix(in srgb, var(--pw-bg-surface-raised) 80%, transparent); }

/* Compact: no cover, for a hint beside a control. The mark leads, the words
   follow, left-aligned because there is no picture to centre them under. */
.pw-coach--compact { width: 280px; padding: var(--pw-space-5); text-align: start; display: grid; grid-template-columns: auto 1fr; column-gap: var(--pw-space-4); }
.pw-coach--compact .pw-coach__appicon { position: static; width: 36px; height: 36px; background: radial-gradient(90% 90% at 0% 0%, var(--pw-coach-a), transparent), var(--pw-bg-subtle); box-shadow: none; }
.pw-coach--compact .pw-coach__title { justify-content: flex-start; margin: 0; font-size: var(--pw-text-sm-size); line-height: var(--pw-text-sm-line); }
.pw-coach--compact .pw-coach__text { margin: 2px 0 0; }
.pw-coach--compact .pw-coach__actions { grid-column: 1 / -1; justify-content: flex-end; margin: var(--pw-space-5) 0 0; }

/* The arrow is a rotated square in the card's surface, outlined on its two
   outer edges and clipped to the half that shows, so the card's hairline runs
   unbroken around the point. The rounded corner is the one pointing away. */
.pw-coach__arrow {
  position: absolute;
  width: 14px; height: 14px;
  background: var(--pw-bg-surface-raised);
  border: 1px solid var(--pw-border-subtle);
  border-radius: 4px 0 0 0;
  transform: rotate(45deg);
  clip-path: polygon(0 0, 100% 0, 0 100%);
}
.pw-coach__arrow[style*="top:-"]    { border-radius: 4px 0 0 0; clip-path: polygon(0 0, 100% 0, 0 100%); }
.pw-coach__arrow[style*="bottom:-"] { border-radius: 0 0 4px 0; clip-path: polygon(100% 0, 100% 100%, 0 100%); }
.pw-coach__arrow[style*="left:-"], .pw-coach__arrow[style*="inset-inline-start:-"] { border-radius: 0 0 0 4px; clip-path: polygon(0 0, 0 100%, 100% 100%); }
.pw-coach__arrow[style*="right:-"], .pw-coach__arrow[style*="inset-inline-end:-"]  { border-radius: 0 4px 0 0; clip-path: polygon(0 0, 100% 0, 100% 100%); }

/* WHAT'S NEW -- a feature that came back better. Its mark and version on the
   cover, then what changed as a list: three improvements run together in one
   sentence read as one vague claim. */
.pw-coach--whatsnew { width: 320px; }
/* Reasons that happen in order: numbered, left-aligned under a centred title. */
.pw-coach__list { list-style: none; counter-reset: pw-coach; margin: var(--pw-space-5) var(--pw-space-4) 0; padding: 0; display: flex; flex-direction: column; gap: var(--pw-space-4); text-align: start; }
.pw-coach__list li { counter-increment: pw-coach; display: flex; align-items: flex-start; gap: var(--pw-space-4); font-size: var(--pw-text-sm-size); line-height: var(--pw-text-sm-line); color: var(--pw-fg-secondary); }
.pw-coach__list li::before {
  content: counter(pw-coach); flex: none; display: grid; place-items: center; width: 20px; height: 20px;
  border-radius: var(--pw-radius-circle); background: var(--pw-tint-brand-bg); color: var(--pw-tint-brand-fg);
  font-size: 11px; font-weight: var(--pw-font-weight-semibold); font-variant-numeric: tabular-nums;
}
/* The browser's own search field, now PrivacyWall's. */
.pw-coach__omni { display: flex; align-items: center; gap: 6px; margin: 10px 12px 2px; padding: 6px 10px; border-radius: var(--pw-radius-pill); box-shadow: inset 0 0 0 1px var(--pw-border-default); font-size: 10px; line-height: 14px; color: var(--pw-fg-secondary); }
.pw-coach__omni .pw-logomark { width: 14px; height: 14px; flex: none; }
.pw-coach__appicon img { width: 22px; height: 22px; }
.pw-coach__badge--shield { inset-inline-end: 12px; top: 72px; }
.pw-coach__badge--shield .pw-icon { color: var(--pw-color-success-300); }
.pw-coach__features { list-style: none; margin: var(--pw-space-5) var(--pw-space-3) 0; padding: 0; display: flex; flex-direction: column; gap: var(--pw-space-5); text-align: start; }
.pw-coach__features li { display: flex; gap: var(--pw-space-4); }
.pw-coach__features li > .pw-icon { flex: none; margin-top: 2px; color: var(--pw-fg-brand); }
.pw-coach__features b { display: block; font-size: var(--pw-text-sm-size); line-height: var(--pw-text-sm-line); font-weight: var(--pw-font-weight-semibold); }
.pw-coach__features span { display: block; font-size: var(--pw-text-xs-size); line-height: var(--pw-text-xs-line); color: var(--pw-fg-tertiary); }

/* In steps: the pages share one grid cell, so the card is as tall as the
   tallest and never jumps. The footer is Back, the dots, then Next. */
.pw-coach__pages { display: grid; }
.pw-coach__page { grid-area: 1 / 1; display: flex; flex-direction: column; visibility: hidden; }
.pw-coach__page.is-current { visibility: visible; }
.pw-coach__actions--steps { display: grid; grid-template-columns: 1fr auto 1fr; }
/* Back keeps its column when it is hidden on the first step, so the dots
   stay centred and Next stays at the end. */
.pw-coach__actions--steps [data-pw-coach-back] { grid-column: 1; justify-self: start; }
.pw-coach__actions--steps .pw-coach__steps { grid-column: 2; grid-row: 1; }
.pw-coach__actions--steps [data-pw-coach-next] { grid-column: 3; grid-row: 1; justify-self: end; min-width: 72px; }
.pw-coach__steps { display: inline-flex; align-items: center; gap: 4px; }
.pw-coach__step { width: 6px; height: 6px; border-radius: var(--pw-radius-pill); background: var(--pw-border-strong); }
.pw-coach__step.is-current { background: var(--pw-fg-primary); }

/* The crops. A small window of the product: a bar, a page. Built from the
   system's surfaces so it follows the theme like the card around it. */
.pw-coach__win {
  position: absolute; inset-inline: 30px; top: 18px; height: 170px; overflow: hidden;
  border-radius: var(--pw-radius-xl); background: var(--pw-bg-surface-raised); color: var(--pw-fg-primary);
  box-shadow: 0 0 0 1px var(--pw-border-subtle), 0 12px 28px -10px color-mix(in srgb, var(--pw-color-gray-900) 30%, transparent);
  text-align: start;
}
/* The bar stays above the page, so a page that scrolls passes under it. */
.pw-coach__winbar { position: relative; z-index: 1; display: flex; align-items: center; gap: 4px; height: 24px; padding-inline: 10px; border-bottom: 1px solid var(--pw-border-faint); background: var(--pw-bg-surface-raised); }
.pw-coach__winbar > i { width: 6px; height: 6px; border-radius: var(--pw-radius-circle); background: var(--pw-border-default); }
.pw-coach__url { display: inline-flex; align-items: center; gap: 3px; margin-inline: auto; padding: 2px 8px; border-radius: var(--pw-radius-pill); background: var(--pw-bg-subtle); font-size: 9px; line-height: 12px; color: var(--pw-fg-tertiary); }
.pw-coach__url .pw-icon { width: 9px; height: 9px; }
.pw-coach__shot { display: block; width: 100%; height: 76px; object-fit: cover; }
.pw-coach__lines { display: flex; flex-direction: column; gap: 5px; padding: 10px 12px; }
.pw-coach__lines > i { height: 5px; border-radius: var(--pw-radius-pill); background: var(--pw-border-default); }
.pw-coach__lines > i:first-child { width: 62%; height: 7px; background: var(--pw-fg-secondary); }
.pw-coach__lines > i:nth-child(2) { width: 88%; }
.pw-coach__lines > i:nth-child(3) { width: 74%; }
.pw-coach__lines .pw-coach__link { width: 42%; height: 6px; background: var(--pw-fg-brand); }
.pw-coach__badge {
  position: absolute; z-index: 1; display: inline-flex; align-items: center; gap: 4px; padding: 3px 9px; border-radius: var(--pw-radius-pill);
  background: var(--pw-bg-inverse); color: var(--pw-fg-inverse); font-size: 10px; line-height: 14px; font-weight: var(--pw-font-weight-semibold);
  box-shadow: 0 6px 16px -6px color-mix(in srgb, var(--pw-color-gray-900) 50%, transparent);
}
.pw-coach__badge .pw-icon { width: 11px; height: 11px; }
/* Sharper: a loupe over the page shows the same photo, closer. */
.pw-coach__loupe {
  position: absolute; z-index: 1; inset-inline-end: 18px; top: 30px; width: 78px; height: 78px; border-radius: var(--pw-radius-circle);
  border: 3px solid var(--pw-bg-surface-raised); background-size: 320%; background-position: 58% 40%;
  box-shadow: 0 10px 24px -8px color-mix(in srgb, var(--pw-color-gray-900) 45%, transparent);
}
.pw-coach__loupe ~ .pw-coach__badge { inset-inline-end: 12px; top: 98px; }
/* Smoother: results in the preview, a speed readout over them. */
.pw-coach__rows { display: flex; flex-direction: column; gap: 8px; padding: 10px; }
.pw-coach__row { display: flex; align-items: center; gap: 8px; }
.pw-coach__row > img { flex: none; width: 32px; height: 32px; border-radius: var(--pw-radius-md); object-fit: cover; }
.pw-coach__row > span { flex: 1; display: flex; flex-direction: column; gap: 4px; }
.pw-coach__row > span > i { height: 5px; border-radius: var(--pw-radius-pill); background: var(--pw-border-default); }
.pw-coach__row > span > i:first-child { width: 70%; height: 6px; background: var(--pw-fg-secondary); }
.pw-coach__scroll { position: absolute; z-index: 1; inset-inline-end: 3px; top: 30px; width: 3px; height: 34px; border-radius: var(--pw-radius-pill); background: var(--pw-border-strong); }
.pw-coach__badge--speed { inset-inline-end: 12px; top: 10px; }
/* Follow links: the page you were on, and the one its link opened. */
.pw-coach__win--back { inset-inline: 24px 80px; top: 18px; }
.pw-coach__win--front { inset-inline: 96px 18px; top: 40px; }
.pw-coach__win--front .pw-coach__shot { height: 60px; }
.pw-coach__cursor { position: absolute; z-index: 1; width: 18px; height: 18px; inset-inline-start: 88px; top: 94px; filter: drop-shadow(0 2px 3px color-mix(in srgb, var(--pw-color-gray-900) 40%, transparent)); }
.pw-coach__cursor path { fill: var(--pw-fg-primary); stroke: var(--pw-bg-surface-raised); stroke-width: 1.5; stroke-linejoin: round; }
/* Filters, and a saved place: the chips and the card as they appear. */
.pw-coach__chips { position: absolute; inset-inline: 18px 52px; top: 18px; display: flex; flex-wrap: wrap; justify-content: center; gap: 6px; }
.pw-coach__chips > span { display: inline-flex; align-items: center; gap: 4px; padding: 5px 10px; border-radius: var(--pw-radius-pill); background: var(--pw-bg-surface-raised); box-shadow: 0 0 0 1px var(--pw-border-subtle); font-size: 11px; line-height: 14px; font-weight: var(--pw-font-weight-medium); color: var(--pw-fg-secondary); }
.pw-coach__chips > span.is-on { background: var(--pw-bg-inverse); color: var(--pw-fg-inverse); box-shadow: none; }
.pw-coach__chips .pw-icon { width: 12px; height: 12px; }
.pw-coach__place { position: absolute; inset-inline: 34px; top: 16px; display: flex; gap: 10px; padding: 8px; border-radius: var(--pw-radius-xl); background: var(--pw-bg-surface-raised); box-shadow: 0 0 0 1px var(--pw-border-subtle), 0 12px 28px -10px color-mix(in srgb, var(--pw-color-gray-900) 30%, transparent); text-align: start; }
.pw-coach__place > img { flex: none; width: 56px; height: 56px; border-radius: var(--pw-radius-lg); object-fit: cover; }
.pw-coach__place b { display: block; font-size: 12px; line-height: 16px; font-weight: var(--pw-font-weight-semibold); }
.pw-coach__place span { display: block; font-size: 10px; line-height: 14px; color: var(--pw-fg-tertiary); }
.pw-coach__place .pw-coach__saved { display: inline-flex; align-items: center; gap: 3px; margin-top: 4px; color: var(--pw-fg-success); font-weight: var(--pw-font-weight-semibold); }
.pw-coach__place .pw-coach__saved .pw-icon { width: 11px; height: 11px; }
.pw-coach__place + .pw-coach__place { top: 88px; inset-inline: 50px 18px; opacity: .7; }

/* =============================================================================
   TABS
   underline (SERP verticals) · pill · enclosed
   ============================================================================= */
.pw-tabs {
  display: flex; align-items: center; gap: var(--pw-space-6);
  border-bottom: 1px solid var(--pw-border-subtle);
  font-weight: var(--pw-font-weight-semibold);
  /* A row of tabs is wider than a phone, and a flex item defaults to
     `min-width: auto` — it refuses to shrink below its content and forces every
     ancestor out with it. The same fix `.pw-verticals` already carries, for the
     same reason: this row is MEANT to be narrower than what is in it. */
  min-width: 0;
  overflow-x: auto;
  scrollbar-width: none;
  -webkit-overflow-scrolling: touch;
}
.pw-tabs::-webkit-scrollbar { display: none; }
/* With the overflow controller running, the row never scrolls: what does not
   fit moves into the More menu instead, and the menu has to paint outside the
   row. Same escape `.pw-verticals__list` has. */
.pw-tabs[data-pw-overflow] { overflow: visible; }
.pw-tabs--no-rule { border-bottom: 0; }
.pw-tab {
  position: relative;
  display: inline-flex; align-items: center; gap: var(--pw-space-4);
  height: 40px;
  padding-inline: var(--pw-space-3);
  border: 0; background: none;
  color: var(--pw-fg-tertiary);
  font-size: var(--pw-text-sm-size);
  font-weight: var(--pw-font-weight-medium);
  white-space: nowrap;
  cursor: pointer;
  transition: color var(--pw-duration-fast) var(--pw-ease-out);
}
.pw-tab::after {
  content: ""; position: absolute; inset-inline: 0; bottom: -1px; height: 2px;
  border-radius: 2px 2px 0 0;
  background: var(--pw-fg-primary);
  transform: scaleX(0);
  transition: transform var(--pw-duration-base) var(--pw-ease-out);
}
/* The shared rule. `is-tracked` is added by PW.mount() once the indicator
   exists, and it stands the per-tab underline down — without JavaScript that
   one is still the whole selected state. */
.pw-tabs__indicator {
  position: absolute;
  bottom: -1px;
  inset-inline-start: 0;
  width: var(--pw-tab-w, 0);
  height: 2px;
  translate: var(--pw-tab-x, 0) 0;
  border-radius: 2px 2px 0 0;
  background: var(--pw-fg-primary);
  opacity: 0;
  pointer-events: none;
  transition:
    translate var(--pw-duration-base) var(--pw-ease-out),
    width var(--pw-duration-base) var(--pw-ease-out),
    opacity var(--pw-duration-fast) var(--pw-ease-out);
}
.pw-tabs.is-tracked { position: relative; }
.pw-tabs.is-tracked.has-current .pw-tabs__indicator { opacity: 1; }
.pw-tabs.is-tracked .pw-tab::after { display: none; }
@media (prefers-reduced-motion: reduce) {
  .pw-tabs__indicator { transition: opacity var(--pw-duration-fast) var(--pw-ease-out); }
}

.pw-tab:hover { color: var(--pw-fg-primary); }
.pw-tab[aria-selected="true"], .pw-tab.is-active { color: var(--pw-fg-primary); }
.pw-tab[aria-selected="true"]::after, .pw-tab.is-active::after { transform: scaleX(1); }
.pw-tab:disabled { color: var(--pw-fg-disabled); cursor: not-allowed; }
.pw-tab .pw-icon { color: currentColor; }
.pw-tab__count {
  margin-inline-start: var(--pw-space-1);
  padding-inline: var(--pw-space-3);
  border-radius: var(--pw-radius-pill);
  background: var(--pw-bg-muted);
  font-size: var(--pw-text-xs-size);
}

/* -- Pill -------------------------------------------------------------------
   Shares its geometry with the SERP vertical chip (`.pw-vertical`) through
   these two values, because the two rows sit one above the other on the
   results page and any drift between them is visible. They stay separate
   components for a reason the CSS cannot express — see the note on
   `.pw-verticals` in pw-search.css — but they must not be separate sizes.
   -------------------------------------------------------------------------- */
.pw-tabs { --pw-tab-pill-height: 36px; --pw-tab-pill-px: var(--pw-space-6); }
.pw-tabs--pill { border-bottom: 0; gap: var(--pw-space-2); }
.pw-tabs--pill .pw-tab {
  height: var(--pw-tab-pill-height);
  padding-inline: var(--pw-tab-pill-px);
  border-radius: var(--pw-radius-pill);
}
.pw-tabs--pill .pw-tab::after { display: none; }
.pw-tabs--pill .pw-tab:hover { background: var(--pw-bg-hover); }
/* Neutral, not brand. These tabs are a set of equal peers — All / Images /
   Videos, Outbound / Return — and choosing between peers is not the same act as
   the brand-tinted selection used where one option is the product's own
   recommendation. Blue here reads as an accent that has landed wherever the
   finger last was. The label carries `--pw-fg-primary` from the base tab, so
   the selected one is the darkest word in the row and the fill only says which
   shape it is in. */
.pw-tabs--pill .pw-tab[aria-selected="true"],
.pw-tabs--pill .pw-tab.is-active { background: var(--pw-bg-selected-neutral); }

/* The selected pill travels, for the same reason the segmented thumb does: a
   fill that appears on one tab while another's disappears is two things
   happening, and the eye is left to infer that they were the same thing. It is
   injected by PW.mount(), so the per-tab fill above is still the whole selected
   state without JavaScript, and `is-tracked` is what stands it down.

   Unlike the underline this needs all four numbers — a pill wraps the tab, so
   it has to know the tab's height and top as well as where it starts. */
.pw-tabs__pill {
  position: absolute;
  inset-block-start: 0;
  inset-inline-start: 0;
  width: var(--pw-tab-w, 0);
  height: var(--pw-tab-h, 0);
  translate: var(--pw-tab-x, 0) var(--pw-tab-y, 0);
  border-radius: var(--pw-radius-pill);
  background: var(--pw-bg-selected-neutral);
  opacity: 0;
  pointer-events: none;
  /* Behind the labels. The pill is appended last, so in DOM order it paints on
     top — and a filled pill over its own label hides the word it is selecting,
     which looks like the tab lost its text rather than gained a background.
     `position: relative` alone is not enough: it makes the tabs positioned but
     leaves them at `z-index: auto`, so they still lose to a later sibling. */
  z-index: 0;
  transition:
    translate var(--pw-duration-base) var(--pw-ease-out),
    width var(--pw-duration-base) var(--pw-ease-out),
    opacity var(--pw-duration-fast) var(--pw-ease-out);
}
.pw-tabs--pill.is-tracked.has-current .pw-tabs__pill { opacity: 1; }
/* The tabs sit above it and stop painting their own fill — the colour stays,
   because that is the label reading as selected rather than the shape. */
.pw-tabs--pill.is-tracked .pw-tab { position: relative; z-index: 1; }
.pw-tabs--pill.is-tracked .pw-tab[aria-selected="true"],
.pw-tabs--pill.is-tracked .pw-tab.is-active { background: transparent; }
@media (prefers-reduced-motion: reduce) {
  .pw-tabs__pill { transition: opacity var(--pw-duration-fast) var(--pw-ease-out); }
}

.pw-tabs--scroll { overflow-x: auto; scrollbar-width: none; }
.pw-tabs--scroll::-webkit-scrollbar { display: none; }
.pw-tabpanel:focus-visible { outline: 2px solid var(--pw-color-brand-600); outline-offset: 4px; }

/* A chip arriving in the row from the overflow menu flips into its seat. The
   seat is what changed — one chip left it and another took it — and a flip
   reads as "this became that" where a fade reads as "something appeared".

   `perspective()` is inside the transform rather than on the row: a
   `perspective` property on the list would give every chip a 3D rendering
   context for the whole of its life, which changes how they paint and what they
   stack against, to animate one of them for 180ms.

   `backface-visibility` matters even though the chip never passes 90°: without
   it some engines composite the half-turned face with its own reverse showing
   through, and the label ghosts. */
@keyframes pw-overflow-in {
  from { transform: perspective(600px) rotateY(-82deg); opacity: 0; }
  to   { transform: perspective(600px) rotateY(0deg);   opacity: 1; }
}
.pw-overflow-in {
  animation: pw-overflow-in var(--pw-duration-base) var(--pw-ease-out) both;
  transform-origin: 50% 50%;
  backface-visibility: hidden;
}
@media (prefers-reduced-motion: reduce) {
  .pw-overflow-in { animation: none; }
}

/* The rest of a trimmed list arriving. A short rise rather than a flip: these
   were always part of this list and are simply being shown, which is a
   different claim from the overflow chip's "this became that". */
@keyframes pw-more-in {
  from { opacity: 0; transform: translateY(-4px); }
  to   { opacity: 1; transform: none; }
}
.pw-more-in { animation: pw-more-in var(--pw-duration-base) var(--pw-ease-out) both; }
@media (prefers-reduced-motion: reduce) {
  .pw-more-in { animation: none; }
}

/* -- Overflow ---------------------------------------------------------------
   `data-pw-overflow` runs the same controller the SERP verticals use: what
   does not fit moves into a menu at the end of the row rather than scrolling
   out of sight, and the selected tab keeps its seat whatever else moves.
   The row must not scroll or wrap while it is measured.
   -------------------------------------------------------------------------- */
.pw-tabs[data-pw-overflow] { flex-wrap: nowrap; overflow: visible; min-width: 0; }
.pw-tabs__more { flex: none; }

/* -- Chip -------------------------------------------------------------------
   The SERP verticals row, as a tab variant.
   
   This began life as a separate component (`.pw-vertical`) because the search
   verticals are NAVIGATION — `<nav>` and `<a aria-current="page">`, each one a
   URL that loads a page — where `.pw-tabs` is a `role="tablist"` swapping
   panels in place. That difference is real and it is the one thing the two
   cannot share: different roles, different keyboard contract.
   
   Everything below the role, though, is presentation, and there was no reason
   for two copies of it. So the chip look, the topic hues, the overflow menu
   and the mobile label mode are defined once, here, for both names: use
   `.pw-tabs--chip` with `.pw-tab` for a tablist, `.pw-verticals` with
   `.pw-vertical` for a nav. The hue VALUES stay in pw-search.css — this
   component defines the knobs (`--pw-chip-*`), the product supplies the
   colours.
   -------------------------------------------------------------------------- */

/* The chip carries its state in the fill, so it takes neither of the underline
   tabs' marks: not the row's bottom rule, and not the `::after` under the
   selected tab. Left in, the row draws "you are here" twice in two different
   idioms — a filled pill AND a bar beneath it — and the rule cuts across every
   chip's baseline into the bargain.

   `.pw-vertical` never showed this: it is an `<a>` in a `<nav>` and was never
   a `.pw-tab`, so it inherited no underline to switch off. The bug appeared
   the moment the look was shared with the tablist, which is the honest cost of
   sharing: what one name never had, the other now has to opt out of.

   Gap matches the SERP row (8px) rather than the underline row's 12px —
   pills already carry their own edges, so they need less air between them. */
.pw-tabs--chip { border-bottom: 0; gap: var(--pw-space-4); }
.pw-tabs--chip .pw-tab::after { display: none; }

/* Each vertical is a filled chip, not bare text.
 *
 * A row of bare labels leaves the current one carrying the whole signal, and
 * on this page that signal is blue — the same blue as every result title
 * underneath. Giving every chip a resting fill turns the row into a track,
 * and the current chip then *lifts out* of it: white surface, real border,
 * small shadow. Shape does the work, so the state survives greyscale and
 * forced colours, and nothing has to compete with the results for blue. */
:is(.pw-vertical, .pw-tabs--chip .pw-tab) {
  /* The resting track and the hover tint are per-chip so a vertical can carry
     its own hue — see the topic block below. */
  --pw-chip-bg:           var(--pw-bg-subtle);
  --pw-chip-fg:           var(--pw-fg-secondary);
  --pw-chip-hover-bg:     var(--pw-bg-active);
  --pw-chip-hover-fg:     var(--pw-fg-primary);
  --pw-chip-hover-border: var(--pw-border-default);

  position: relative;
  display: inline-flex;
  align-items: center;
  gap: var(--pw-space-4);                        /* 8px */
  flex: none;
  height: 36px;
  padding-inline: var(--pw-space-6);             /* 12px */
  border: 1px solid transparent;
  border-radius: var(--pw-radius-pill);
  background: var(--pw-chip-bg);
  color: var(--pw-chip-fg);
  font: inherit;
  font-size: var(--pw-text-sm-size);
  line-height: var(--pw-text-sm-line);
  font-weight: var(--pw-font-weight-semibold);
  white-space: nowrap;
  text-decoration: none;
  cursor: pointer;
  user-select: none;
  transition: background var(--pw-duration-base) var(--pw-ease-out),
              border-color var(--pw-duration-base) var(--pw-ease-out),
              color var(--pw-duration-base) var(--pw-ease-out),
              box-shadow var(--pw-duration-base) var(--pw-ease-out),
              transform var(--pw-duration-base) var(--pw-ease-out);
}
:is(.pw-vertical, .pw-tabs--chip .pw-tab) .pw-icon { transition: transform var(--pw-duration-base) var(--pw-ease-out); }

/* `.is-hover` renders the hover treatment without a pointer — for documenting
   the state, and for a chip the app wants to pre-highlight. It resolves the
   same custom properties, so it themes and re-tints exactly like real hover
   instead of drifting into hard-coded colours. */
:is(.pw-vertical, .pw-tabs--chip .pw-tab):hover,
:is(.pw-vertical, .pw-tabs--chip .pw-tab).is-hover {
  background: var(--pw-chip-hover-bg);
  border-color: var(--pw-chip-hover-border);
  color: var(--pw-chip-hover-fg);
}
:is(.pw-vertical, .pw-tabs--chip .pw-tab):hover .pw-icon, :is(.pw-vertical, .pw-tabs--chip .pw-tab).is-hover .pw-icon { transform: translateY(-1px); }
:is(.pw-vertical, .pw-tabs--chip .pw-tab):active { transform: translateY(1px); }
:is(.pw-vertical, .pw-tabs--chip .pw-tab):focus-visible { outline: none; box-shadow: var(--pw-focus-ring); }

/* Current chip — its own colour, and lifted.
 *
 * "Current" is whichever word the row speaks: `aria-current="page"` for a nav
 * of links, `aria-selected="true"` for a tablist. Both are matched, because
 * the look is the same question — where am I — asked by two different ARIA
 * contracts, and a variant that only understood one of them would silently
 * render every tablist as though nothing were selected.
 *
 * The chip you are on wears its topic's hue: Images blue, Maps green, Videos
 * red. It is the one place the hue is more than decoration, because here it
 * is naming the thing you are actually looking at.
 *
 * Colour is still not doing this alone. The current chip also keeps a real
 * border and a shadow, so it lifts out of the track in greyscale, in forced
 * colours and on a bad monitor exactly as it does in colour. Hover, by
 * contrast, tints without lifting — that is what keeps the two apart.
 *
 * The generic white treatment below is the FALLBACK, for a vertical with no
 * hue of its own. "All" is the one that matters: it is not a topic, it is the
 * absence of one, so it has nothing to be coloured with. */
:is(.pw-vertical, .pw-tabs--chip .pw-tab):is([aria-current="page"], [aria-selected="true"]),
:is(.pw-vertical, .pw-tabs--chip .pw-tab):is(.is-current, .is-active) {
  background: var(--pw-chip-current-bg, var(--pw-bg-surface));
  border-color: var(--pw-chip-current-border, var(--pw-border-default));
  color: var(--pw-chip-current-fg, var(--pw-fg-primary));
  box-shadow: var(--pw-shadow-xs);
  font-weight: var(--pw-font-weight-bold);
}
:is(.pw-vertical, .pw-tabs--chip .pw-tab):is([aria-current="page"], [aria-selected="true"]) .pw-icon,
:is(.pw-vertical, .pw-tabs--chip .pw-tab):is(.is-current, .is-active) .pw-icon { color: inherit; transform: none; }

/* Hovering the chip you are already on must not pretend to go anywhere: same
   colours, only the shadow deepens. */
:is(.pw-vertical, .pw-tabs--chip .pw-tab):is([aria-current="page"], [aria-selected="true"]):hover,
:is(.pw-vertical, .pw-tabs--chip .pw-tab):is(.is-current, .is-active):hover,
:is(.pw-vertical, .pw-tabs--chip .pw-tab):is([aria-current="page"], [aria-selected="true"]).is-hover,
:is(.pw-vertical, .pw-tabs--chip .pw-tab):is(.is-current, .is-active).is-hover {
  background: var(--pw-chip-current-bg, var(--pw-bg-surface));
  border-color: var(--pw-chip-current-border, var(--pw-border-default));
  color: var(--pw-chip-current-fg, var(--pw-fg-primary));
  box-shadow: var(--pw-shadow-sm);
}
:is(.pw-vertical, .pw-tabs--chip .pw-tab):is([aria-current="page"], [aria-selected="true"]):hover .pw-icon,
:is(.pw-vertical, .pw-tabs--chip .pw-tab):is(.is-current, .is-active):hover .pw-icon { transform: none; }

/* Tools shares the chip's shape but toggles the filter bar. Open reads the
   same as current, because it is the same kind of "you are here". */
:is(.pw-vertical, .pw-tabs--chip .pw-tab)[aria-expanded="true"] {
  background: var(--pw-bg-surface);
  border-color: var(--pw-border-default);
  color: var(--pw-fg-primary);
  box-shadow: var(--pw-shadow-xs);
}

/* Labels only.
 *
 * On a phone the row has to hold six verticals in 375px, and an icon costs
 * about a third of a chip's width to say what the label beside it already
 * says. Below the breakpoint the icons go and the labels stay — dropping the
 * word instead would leave a row of glyphs nobody can name. */
:is(.pw-verticals--labels, .pw-tabs--chip.pw-tabs--labels) :is(.pw-vertical, .pw-tab) .pw-icon { display: none; }
:is(.pw-verticals--labels, .pw-tabs--chip.pw-tabs--labels) :is(.pw-vertical, .pw-tab) { gap: 0; }
/* Except where the icon IS the label. A chip with no text and no glyph is an
   empty pill — which is what the More trigger and a pinned Tools button became
   the first time this row was rendered in label-only mode. The media query
   below carried this exception; `--labels` did not, and the two have to agree
   because they express the same intent. */
:is(.pw-verticals--labels, .pw-tabs--chip.pw-tabs--labels) :is(.pw-vertical--icon, .pw-tab--icon) .pw-icon { display: block; }
@media (max-width: 768px) {
  :is(.pw-verticals, .pw-tabs--chip) :is(.pw-vertical, .pw-tab) .pw-icon { display: none; }
  :is(.pw-verticals, .pw-tabs--chip) :is(.pw-vertical, .pw-tab) { gap: 0; padding-inline: var(--pw-space-5); }
  /* The icon-only overflow trigger is the exception — it has no label to
     fall back on. */
  :is(.pw-verticals, .pw-tabs--chip) :is(.pw-vertical--icon, .pw-tab--icon) .pw-icon { display: block; }
}

/* Icon-only, for the overflow trigger and a pinned Tools button.
 *
 * 36px, not the icon button's 40px, because it has to match the chips beside
 * it — a control that breaks the row's height is not in the row. The GLYPH
 * size is not decided here though: that comes from `--pw-glyph`, next to the
 * icon button, so every label-less control in the system answers to one
 * rule. */
/* Written out rather than wrapped in `:is()`: `:is()` takes the specificity of
   its most specific argument, so `:is(.pw-vertical--icon, .pw-tab--icon)` is
   one class — and it loses to `.pw-tabs--chip .pw-tab`, which is two. The
   padding it was meant to clear stayed, and the glyph was squeezed into what
   was left. `.pw-vertical` is doubled up for the same reason: the modifier
   has to weigh as much as the rule it is overriding. */
.pw-vertical.pw-vertical--icon,
.pw-tabs--chip .pw-tab--icon { width: 36px; padding-inline: 0; justify-content: center; }

/* Placed after the mobile block on purpose: that block sets a padding on every
   chip in the row at the same specificity as this one, so on file order alone
   it would win and squeeze the glyph inside a fixed-width circle. */


/* Moved into the overflow menu, a chip is a menu row: full width, left
   aligned, no fill of its own. It keeps its icon — the menu has the room, and
   the icons are what make a long list scannable.

   This has to come AFTER the chip mechanics above. Both selectors resolve to
   the same specificity now that `:is()` carries the chip variant, so the file
   order is what decides which wins; the other way round, a chip in a menu
   would still be a chip. */
/* Inside the menu a vertical is a menu row, not a chip: full width, left
   aligned, no fill of its own. It keeps its icon here — the menu has the room,
   and the icons are what make a long list scannable. */
.pw-menu :is(.pw-vertical, .pw-tab) {
  display: flex;
  width: 100%;
  height: auto;
  min-height: 36px;
  padding: var(--pw-space-3) var(--pw-space-5);
  border: 0;
  border-radius: var(--pw-radius-sm);
  background: transparent;
  box-shadow: none;
  justify-content: flex-start;
}
.pw-menu :is(.pw-vertical, .pw-tab) .pw-icon { display: block; color: var(--pw-fg-quaternary); }
.pw-menu :is(.pw-vertical, .pw-tab):hover { background: var(--pw-bg-hover); border-color: transparent; box-shadow: none; }
.pw-menu :is(.pw-vertical, .pw-tab):hover .pw-icon { transform: none; }
.pw-menu :is(.pw-vertical, .pw-tab):is([aria-current="page"], [aria-selected="true"]) {
  background: var(--pw-bg-selected);
  color: var(--pw-fg-brand);
  box-shadow: none;
}
.pw-menu :is(.pw-vertical, .pw-tab):is([aria-current="page"], [aria-selected="true"]) .pw-icon { color: var(--pw-fg-brand); }

/* =============================================================================
   BREADCRUMB
   ============================================================================= */
.pw-breadcrumb { display: flex; align-items: center; gap: var(--pw-space-3); font-size: var(--pw-text-sm-size); color: var(--pw-fg-tertiary); min-width: 0; }
.pw-breadcrumb__item { display: inline-flex; align-items: center; gap: var(--pw-space-3); min-width: 0; }
.pw-breadcrumb__item > a:hover { color: var(--pw-fg-primary); text-decoration: underline; }
.pw-breadcrumb__sep { color: var(--pw-fg-disabled); }
.pw-breadcrumb__item:last-child { color: var(--pw-fg-secondary); }

/* =============================================================================
   PAGINATION
   ============================================================================= */
.pw-pagination { display: flex; align-items: center; justify-content: center; gap: var(--pw-space-4); }
.pw-pagination__page {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 36px; height: 36px; padding-inline: var(--pw-space-3);
  border: 0; border-radius: var(--pw-radius-pill);
  background: transparent; color: var(--pw-fg-secondary);
  font-size: var(--pw-text-sm-size); font-variant-numeric: tabular-nums; cursor: pointer;
  font-weight: var(--pw-font-weight-semibold);
}
.pw-pagination__page:hover { background: var(--pw-bg-hover); color: var(--pw-fg-primary); }
.pw-pagination__page[aria-current="page"] { background: var(--pw-bg-selected); color: var(--pw-fg-brand); font-weight: var(--pw-font-weight-semibold); }
.pw-pagination__ellipsis { color: var(--pw-fg-disabled); padding-inline: var(--pw-space-2); }
/* Prev / Next pair used at the foot of the image and video verticals */
.pw-pagination--prevnext { gap: var(--pw-space-6); }
.pw-pagination--prevnext .pw-btn { min-width: 156px; }

/* =============================================================================
   ACCORDION
   ============================================================================= */
/* No `overflow: hidden`: a select or date picker opened inside a panel has to
   be able to paint past the accordion's edge. The end items round themselves
   instead. */
.pw-accordion { border: 1px solid var(--pw-border-subtle); border-radius: var(--pw-radius-lg); background: var(--pw-bg-surface); }
.pw-accordion > :first-child, .pw-accordion > :first-child > .pw-accordion__trigger {
  border-start-start-radius: calc(var(--pw-radius-lg) - 1px);
  border-start-end-radius: calc(var(--pw-radius-lg) - 1px);
}
.pw-accordion > :last-child, .pw-accordion > :last-child > :last-child {
  border-end-start-radius: calc(var(--pw-radius-lg) - 1px);
  border-end-end-radius: calc(var(--pw-radius-lg) - 1px);
}
.pw-accordion:has(.pw-select.is-open), .pw-accordion:has([aria-expanded="true"] + .pw-filter-menu) { position: relative; z-index: 1; }
.pw-accordion__item + .pw-accordion__item { border-top: 1px solid var(--pw-border-subtle); }
.pw-accordion__trigger {
  display: flex; align-items: center; gap: var(--pw-space-6);
  width: 100%; padding: var(--pw-space-6) var(--pw-space-8);
  border: 0; background: transparent; color: var(--pw-fg-primary);
  font-size: var(--pw-text-sm-size); font-weight: var(--pw-font-weight-semibold);
  text-align: start; cursor: pointer;
}
.pw-accordion__trigger:hover { background: var(--pw-bg-hover); }
.pw-accordion__caret { margin-inline-start: auto; color: var(--pw-fg-quaternary); transition: transform var(--pw-duration-base) var(--pw-ease-out); }
.pw-accordion__trigger[aria-expanded="true"] .pw-accordion__caret { transform: rotate(180deg); }
.pw-accordion__panel {
  padding: 0 var(--pw-space-8) var(--pw-space-8);
  font-size: var(--pw-text-sm-size); line-height: var(--pw-text-md-line);
  color: var(--pw-fg-secondary);
  transition: height var(--pw-duration-base) var(--pw-ease-out);
}
/* Only while animating does the panel clip — the rest of the time it must not,
   or a select opened in the last row would be cut off. */
.pw-accordion__panel.is-animating { overflow: hidden; }
.pw-accordion__panel[hidden] { display: none; }

/* -- Flush rows --------------------------------------------------------------
   "Things to know", "People also ask": a list of questions on the page itself
   rather than a control panel sitting on it. So no outer border and no fill —
   only hairlines between the rows, which is the least a list needs to read as
   one. The caret becomes a filled disc because without a card edge there is
   nothing else saying the row can be opened.
   ---------------------------------------------------------------------------- */
.pw-accordion--flush { border: 0; border-radius: 0; background: none; }
.pw-accordion--flush .pw-accordion__item + .pw-accordion__item { border-top: 1px solid var(--pw-border-subtle); }
.pw-accordion--flush .pw-accordion__trigger {
  padding-inline: 0;
  padding-block: var(--pw-space-8);
  font-size: var(--pw-text-md-size);
  font-weight: var(--pw-font-weight-regular);
}
.pw-accordion--flush .pw-accordion__trigger:hover { background: transparent; }
.pw-accordion--flush .pw-accordion__trigger:hover .pw-accordion__title { text-decoration: underline; }
.pw-accordion--flush .pw-accordion__panel { padding-inline: 0; padding-block-end: var(--pw-space-8); }
/* A leading picture of the thing the row is about. Square and small: it is a
   hint at the answer, not the answer, and a larger one turns a list of
   questions into a list of cards. */
.pw-accordion__thumb {
  width: 44px; height: 44px; flex: 0 0 auto;
  border-radius: var(--pw-radius-md);
  background: var(--pw-bg-muted);
  object-fit: cover;
}
.pw-accordion__body { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.pw-accordion__title { color: var(--pw-fg-primary); }
.pw-accordion__sub { font-size: var(--pw-text-sm-size); color: var(--pw-fg-tertiary); }
/* The disc is the affordance, so it keeps its own hover rather than borrowing
   the row's — the row highlights its label, the disc highlights itself. */
.pw-accordion--flush .pw-accordion__caret {
  display: inline-flex; align-items: center; justify-content: center;
  width: 32px; height: 32px; flex: 0 0 auto;
  border-radius: var(--pw-radius-circle);
  background: var(--pw-bg-subtle);
  color: var(--pw-fg-secondary);
}
.pw-accordion--flush .pw-accordion__trigger:hover .pw-accordion__caret { background: var(--pw-bg-active); }

/* =============================================================================
   THEME PICKER — choose a theme by looking at it

   Three icon buttons in a segmented control name the themes; they do not show
   them. "Match system" is the hard case: there is no glyph for "whichever the
   OS is doing", and a monitor is a guess the user has to decode. So the control
   shows a miniature of each theme instead — a window with a card in it —
   and System shows both halves at once, which is the only honest picture of
   what it does.

   The swatch colours are deliberately LITERAL rather than tokens. A light
   swatch has to look light while the app is in dark mode, because it is a
   picture of a theme and not a surface in the current one. This is the one
   place in the system where hard-coded colour is correct, and the reason is
   worth stating so nobody "fixes" it into tokens later.
   ============================================================================= */
.pw-theme-picker { display: flex; gap: var(--pw-space-3); }
.pw-theme-opt {
  margin: 0;
  padding: 2px;
  border: 2px solid transparent;
  border-radius: 13px;
  background: none;
  cursor: pointer;
  transition: border-color var(--pw-duration-fast) var(--pw-ease-out);
}
/* Hover draws the ring the selected option already wears, in neutral. The
   option is a PICTURE of a theme, so moving its background or dimming it would
   change the thing being depicted — hover has to happen in the frame around the
   swatch, never inside it. That is also why the ring is the same 2px the
   pressed state uses: hovering previews the shape of choosing. */
.pw-theme-opt:hover { border-color: var(--pw-border-strong); }
.pw-theme-opt:focus-visible { outline: 2px solid var(--pw-color-brand-600); outline-offset: 2px; }
.pw-theme-opt[aria-pressed="true"] { border-color: var(--pw-color-brand-600); }

.pw-theme-swatch {
  display: block;
  position: relative;
  width: 54px; height: 34px;
  border-radius: 9px;
  overflow: hidden;
}
/* The card is anchored to the bottom trailing corner and bleeds off both edges,
   which is what makes 54×34 read as a window rather than a rounded rectangle. */
.pw-theme-swatch__card {
  position: absolute;
  inset-block-end: 0;
  inset-inline-end: 0;
  width: 38px; height: 21px;
  box-sizing: border-box;
  padding: 3px 6px;
  border-start-start-radius: 8px;
  border-block-start: 1px solid var(--pw-swatch-line);
  border-inline-start: 1px solid var(--pw-swatch-line);
  font-size: 11px;
  font-weight: var(--pw-font-weight-semibold);
}
.pw-theme-swatch--light { background: #e5e7e8; }
.pw-theme-swatch--light .pw-theme-swatch__card { background: #fff; color: #202124; --pw-swatch-line: #cdd0d4; }
.pw-theme-swatch--dark  { background: #414349; }
.pw-theme-swatch--dark .pw-theme-swatch__card { background: #202124; color: #f5f6f6; --pw-swatch-line: rgba(255, 255, 255, 0.16); }
/* System is both, split down the middle — not a third look, but the two it
   chooses between. */
.pw-theme-swatch--system { display: flex; }
.pw-theme-swatch__half { flex: 1; position: relative; }
.pw-theme-swatch__half .pw-theme-swatch__card { width: 23px; height: 21px; padding: 3px 5px; }
.pw-theme-swatch__half--light { background: #e5e7e8; }
.pw-theme-swatch__half--light .pw-theme-swatch__card { background: #fff; color: #202124; --pw-swatch-line: #cdd0d4; }
.pw-theme-swatch__half--dark { background: #414349; }
.pw-theme-swatch__half--dark .pw-theme-swatch__card { background: #202124; color: #f5f6f6; --pw-swatch-line: rgba(255, 255, 255, 0.16); }

/* =============================================================================
   PROGRESS  ·  SPINNER  ·  SKELETON
   ============================================================================= */
.pw-progress { height: 6px; border-radius: var(--pw-radius-pill); background: var(--pw-bg-strong); overflow: hidden; }
.pw-progress__bar {
  height: 100%; border-radius: inherit; background: var(--pw-action-bg);
  transition: width var(--pw-duration-slow) var(--pw-ease-out);
}
.pw-progress--indeterminate .pw-progress__bar {
  width: 40%; animation: pw-progress-slide 1.2s var(--pw-ease-in-out) infinite;
}
@keyframes pw-progress-slide {
  0%   { transform: translateX(-100%); }
  100% { transform: translateX(250%); }
}
.pw-progress--thin { height: 3px; }
/* Page-top loading bar */
.pw-progress--page { position: fixed; inset-inline: 0; top: 0; z-index: var(--pw-z-toast); height: 3px; border-radius: 0; background: transparent; }

.pw-spinner {
  width: 20px; height: 20px; flex: 0 0 auto;
  border: 2px solid var(--pw-border-default);
  border-top-color: var(--pw-action-bg);
  border-radius: var(--pw-radius-circle);
  animation: pw-spin 700ms linear infinite;
}
.pw-spinner--sm { width: 14px; height: 14px; border-width: 1.5px; }
.pw-spinner--lg { width: 28px; height: 28px; border-width: 3px; }

.pw-skeleton {
  border-radius: var(--pw-radius-sm);
  background: linear-gradient(90deg, var(--pw-skeleton-base) 25%, var(--pw-skeleton-sheen) 37%, var(--pw-skeleton-base) 63%);
  background-size: 400% 100%;
  animation: pw-shimmer 1.4s ease infinite;
}
@keyframes pw-shimmer { 0% { background-position: 100% 50%; } 100% { background-position: 0 50%; } }
.pw-skeleton--text { height: 12px; border-radius: var(--pw-radius-pill); }
.pw-skeleton--title { height: 18px; border-radius: var(--pw-radius-pill); }
.pw-skeleton--circle { border-radius: var(--pw-radius-circle); }
.pw-skeleton--block { height: 120px; border-radius: var(--pw-radius-lg); }

/* =============================================================================
   EMPTY  ·  ERROR STATE
   ============================================================================= */
.pw-empty {
  display: flex; flex-direction: column; align-items: center; text-align: center;
  gap: var(--pw-space-5);
  padding: var(--pw-space-24) var(--pw-space-8);
  max-width: 440px; margin-inline: auto;
}
.pw-empty__art {
  display: grid; place-items: center;
  width: 56px; height: 56px;
  border-radius: var(--pw-radius-circle);
  background: var(--pw-bg-muted);
  color: var(--pw-fg-quaternary);
}
.pw-empty__title { font-size: var(--pw-text-md-size); font-weight: var(--pw-font-weight-bold); letter-spacing: var(--pw-tracking-snug); }
.pw-empty__text  { font-size: var(--pw-text-sm-size); line-height: var(--pw-text-md-line); color: var(--pw-fg-tertiary); }
/* WRAPS. Two actions side by side are 300px of buttons, and an empty state is
   put in whatever box has nothing in it -- a 420px panel, a phone-width frame,
   the middle of a video still. Held on one line they simply leave the box, and
   the first thing to leave is the second action. */
.pw-empty__actions { display: flex; flex-wrap: wrap; justify-content: center; gap: var(--pw-space-4); margin-top: var(--pw-space-3); }
.pw-empty--inline { padding: var(--pw-space-12) var(--pw-space-8); }
/* Compact — an empty state inside a PANEL rather than on a page. The maps
   panels and the suggestion sheet all want the same thing: a tighter block and
   a title one step down, because 20px of heading in a 420px column reads as a
   page telling you it is broken rather than a list telling you it is empty.
   Eight places were setting `font-size:14px` on the title inline, which is a
   variant that had not been written down yet. */
.pw-empty--compact { padding: var(--pw-space-6) 0; gap: var(--pw-space-4); }
.pw-empty--compact .pw-empty__title {
  font-size: var(--pw-text-sm-size);
  line-height: var(--pw-text-sm-line);
}
.pw-empty--danger .pw-empty__art { background: var(--pw-color-danger-25); color: var(--pw-color-danger-600); }
html.dark .pw-empty--danger .pw-empty__art, [data-pw-theme="dark"] .pw-empty--danger .pw-empty__art { background: rgba(240,68,56,.14); color: var(--pw-color-danger-350); }

/* -- Source credit -----------------------------------------------------------
   Moved here from pw-widgets.css, where it could only be seen by pages that
   load the widgets. It is not a widget part: it is the line that says whose data
   this is, and the entity hero's fact card needs it exactly as much as the
   weather widget does — it just went unnoticed, because a mask with no rule
   behind it paints nothing and an unpainted mark looks like a space. Every
   specimen loads pw-components.css; only some load pw-widgets.css.
   ---------------------------------------------------------------------------- */
/* Apple Weather. WeatherKit's terms are that the trademark is shown and that it
   links to the list of the other sources the data came from, so the badge does
   the attributing and the foot's "Powered by" does the reading — every source
   line in the system opens the same way, trademark or not. The logo is a
   monochrome glyph painted through a mask, which is the one way a black mark
   follows the theme; see
   assets/brand/README.md. Reproduced exactly, never redrawn on our grid. */
.pw-wxsource {
  display: inline-flex; align-items: center; gap: 0.34em;
  color: var(--pw-fg-secondary); text-decoration: none;
  font-weight: var(--pw-font-weight-medium);
  transition: color var(--pw-duration-fast) var(--pw-ease-out);
}
.pw-wxsource:hover { color: var(--pw-fg-primary); }
.pw-wxsource__mark {
  width: 1em; height: 1.1em; flex: 0 0 auto;
  /* The apple sits a hair high against a cap-height word, as its leaf is drawn
     above the fruit and the eye reads the fruit as the mark. */
  margin-block-start: -0.06em;
  background: currentColor;
  -webkit-mask: url("../assets/brand/glyphs/apple.svg") center / contain no-repeat;
  mask: url("../assets/brand/glyphs/apple.svg") center / contain no-repeat;
}

/* =============================================================================
   TABLE
   ============================================================================= */
.pw-table-wrap { width: 100%; overflow-x: auto; border: 1px solid var(--pw-border-subtle); border-radius: var(--pw-radius-lg); background: var(--pw-bg-surface); }
.pw-table { width: 100%; border-collapse: collapse; font-size: var(--pw-text-sm-size); }
.pw-table thead th {
  position: sticky; top: 0;
  padding: var(--pw-space-5) var(--pw-space-6);
  border-bottom: 1px solid var(--pw-border-subtle);
  background: var(--pw-bg-subtle);
  color: var(--pw-fg-tertiary);
  font-weight: var(--pw-font-weight-semibold);
  text-align: start;
  white-space: nowrap;
}
.pw-table tbody td { padding: var(--pw-space-6); border-bottom: 1px solid var(--pw-border-subtle); color: var(--pw-fg-secondary); vertical-align: middle; }
.pw-table tbody tr:last-child td { border-bottom: 0; }
.pw-table tbody tr:hover td { background: var(--pw-bg-hover); }
.pw-table tbody tr.is-selected td { background: var(--pw-bg-selected); }
/* Only for figures the reader compares digit by digit — money, counts, raw
   measurements. NOT for text that happens to contain a number: "3 hr 5 min"
   and "143 kg" are read as labels, and right-aligning them leaves a ragged
   left edge with nothing to scan down. Those stay start-aligned. */
.pw-table__num { text-align: end; font-variant-numeric: tabular-nums; }
.pw-table__sort { display: inline-flex; align-items: center; gap: var(--pw-space-2); border: 0; background: none; color: inherit; font: inherit; cursor: pointer; }
.pw-table__sort .pw-icon { opacity: .4; }
.pw-table__sort[aria-sort] .pw-icon { opacity: 1; color: var(--pw-fg-brand); }

/* -- Variant: shopping accent -----------------------------------------------
   The Shopping vertical's Search action is amber in the approved designs. This
   is a named, vertical-scoped exception — do not use it elsewhere, and do not
   put it on the same screen as a blue primary. */
.pw-btn--accent-shop {
  background: var(--pw-accent-shop);
  color: var(--pw-accent-shop-fg);
  box-shadow: var(--pw-shadow-xs);
}
.pw-btn--accent-shop:hover  { background: var(--pw-accent-shop-hover); }
.pw-btn--accent-shop:active { background: var(--pw-accent-shop-active); box-shadow: none; }

/* ======================================================================== *
 * SUBPAGE — the screen a settings row opens.
 *
 * A row with a chevron promises somewhere to go, and everywhere it goes looks
 * like this: a bar that says what you are in and how to leave, and a ground
 * with one thing on it. The bar is the only chrome, because a screen that
 * edits ONE value should not also carry navigation, a search field and a
 * footer — the reader got here from a list and the way back is the way they
 * came.
 *
 * The title is in the bar rather than over the content: the back button and
 * the name of the place it returns FROM are the same sentence, and split
 * across two rows they stop being one.
 * ======================================================================== */
.pw-subpage {
  display: flex;
  flex-direction: column;
  min-height: 0;
  background: var(--pw-bg-subtle);
}
/* THE PADDING IS A PERCENTAGE, CAPPED. This screen is a page in the product and
   a 320px frame in the docs, so a media query would give the phone demo desktop
   padding; a container query would need `container-type` on the subpage, and
   that brings layout containment -- which makes it the containing block for the
   fixed-position layer an anchored menu turns into. A percentage resolves
   against the element's own inline size, which is the thing that actually
   changes, and needs neither. */
.pw-subpage__head {
  display: flex; align-items: center; gap: var(--pw-space-6);
  flex: 0 0 auto;
  padding: var(--pw-space-6) min(var(--pw-space-8), 5%);
  border-bottom: 1px solid var(--pw-border-faint);
  background: var(--pw-bg-surface);
}
.pw-subpage__title {
  margin: 0;
  font-size: var(--pw-text-lg-size);
  line-height: var(--pw-text-lg-line);
  font-weight: var(--pw-font-weight-semibold);
  color: var(--pw-fg-primary);
  /* A long place name shortens rather than pushing the action off the end. */
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* Whatever the screen keeps on the trailing edge -- usually nothing, sometimes
   the gear that got you into settings in the first place. */
.pw-subpage__end { margin-inline-start: auto; display: flex; align-items: center; gap: var(--pw-space-4); }
/* The content is CENTRED but not vertically: a form that floats in the middle
   of a tall window moves every time the window changes height, and the eye
   looks for the first thing under the bar. */
.pw-subpage__body {
  flex: 1 1 auto; min-height: 0; overflow-y: auto;
  display: flex; justify-content: center;
  padding: min(var(--pw-space-16), 8%) min(var(--pw-space-8), 5%) min(var(--pw-space-12), 6%);
}
.pw-subpage__body > * { width: 100%; max-width: 480px; }

/* ======================================================================== *
 * SETTINGS MENU — the gear panel shared by search and maps.
 *
 * A menu of *controls*, not of commands: each row carries a live value the
 * user can change in place. It is therefore not `role="menu"` — arrow-key
 * menu semantics would swallow the arrow keys the selects need. It is a
 * plain labelled group with a focus trap, and each control keeps its own
 * role. Rows come in two kinds: a setting row (icon · title · control) and
 * a link row (icon · title · chevron), which are visually the same height
 * so the panel scans as one list.
 * ======================================================================== */
/* The panel is the GROUND, not the surface. Each setting sits on its own white
   card, so the panel goes a step back to `--pw-bg-muted` and the cards come
   forward on it. That inversion is the whole structure: eight rows separated by
   hairlines on one white field read as a list you scan top to bottom, where
   eight cards read as eight independent things you can act on in any order —
   which is what a settings panel is. It also gives every row a real boundary,
   so a control on the trailing edge has something to sit against instead of
   floating in the middle of an undivided sheet. */
.pw-settings-menu {
  /* 400, not 360. The Appearance row carries three theme swatches, and at 360
     they leave the label 63px — enough to clip "Appearance" itself. The panel
     is sized by its widest row, and that row is now a picture rather than a
     word. */
  /* `100vw` is the SCREEN, and the menu is not on the screen — it is inside
     whatever is holding it, which on a phone has its own padding the viewport
     knows nothing about. So the viewport cap sets the ceiling and `100%` sets
     the real one. */
  width: min(400px, calc(100vw - var(--pw-space-16)));   /* 32px of gutter */
  max-width: 100%;
  padding: var(--pw-space-3);                            /* 6px */
  /* `--bg-subtle`, not `--bg-muted`. In light they are the same grey, but in
     dark `muted` is LIGHTER than `surface` — so a muted panel would sit in
     front of its own cards and the whole inversion would read backwards at
     night. `subtle` is the step behind `surface` in both themes, which is what
     "the ground the cards sit on" has to mean. */
  background: var(--pw-bg-subtle);
  border: 1px solid var(--pw-border-subtle);
  border-radius: var(--pw-radius-xl);
  box-shadow: var(--pw-shadow-xl);
}
.pw-settings-menu[hidden] { display: none; }

.pw-settings-menu__head { padding: var(--pw-space-6) var(--pw-space-6) var(--pw-space-4); }  /* 12 · 12 · 8 */
.pw-settings-menu__title {
  margin: 0;
  color: var(--pw-fg-primary);
  font-size: var(--pw-text-sm-size);
  line-height: var(--pw-text-sm-line);
  font-weight: var(--pw-font-weight-bold);
}
.pw-settings-menu__sub {
  margin: var(--pw-space-2) 0 0;
  color: var(--pw-fg-tertiary);
  font-size: var(--pw-text-xs-size);
  line-height: var(--pw-text-xs-line);
}

/* Cards need no rule between groups — the gap does it. A hairline across a
   field of cards draws a line between two things that are already separated,
   and it lands in the middle of the panel's own background rather than on any
   surface, which is where a stray line is most visible. */
.pw-settings-menu__group { display: flex; flex-direction: column; gap: var(--pw-space-2); }
.pw-settings-menu__group + .pw-settings-menu__group { margin-block-start: var(--pw-space-6); }

.pw-settings-row {
  display: flex;
  align-items: center;
  gap: var(--pw-space-6);                        /* 12px */
  width: 100%;
  min-height: 48px;
  padding: var(--pw-space-5) var(--pw-space-6);  /* 10 · 12 */
  /* No border. The card is already separated from the panel by being a lighter
     surface on a darker ground, and a hairline on top of that separates it a
     second time — nine of them stacked turns a quiet panel into a grid. The
     shadow is enough to lift the card; the contrast is what divides it. */
  border: 0;
  border-radius: var(--pw-radius-lg);
  background: var(--pw-bg-surface);
  box-shadow: var(--pw-shadow-xs);
  color: inherit;
  font: inherit;
  text-align: start;
  text-decoration: none;
  transition: box-shadow var(--pw-duration-fast) var(--pw-ease-out);
}
/* Only link rows are hoverable — a setting row's hit target is its control,
   so lighting up the whole row would promise a click that does nothing.

   The hover is a RING, not a fill. Once the rows are cards, moving their
   background moves the surface the content sits on: text, medallion and control
   all re-contrast at once and the row flickers as the pointer crosses it. A
   ring changes only the boundary, which is the part that says "this whole thing
   is one target" — exactly what a link row needs to say. It is drawn as a
   box-shadow composed with the card's own lift, so nothing shifts by a pixel. */
a.pw-settings-row:hover,
button.pw-settings-row:hover {
  box-shadow: 0 0 0 1px var(--pw-border-strong), var(--pw-shadow-xs);
}
a.pw-settings-row:focus-visible,
button.pw-settings-row:focus-visible { outline: none; box-shadow: var(--pw-focus-ring); }

/* The icon sits in a disc filled with the panel's ground, so it reads as a
   hole punched through the card rather than a glyph floating on it — the same
   trick the row's white against the panel's grey is doing one level up. */
.pw-settings-row__icon {
  flex: none;
  box-sizing: content-box;
  width: var(--pw-icon-md); height: var(--pw-icon-md);
  padding: var(--pw-space-4);
  border-radius: var(--pw-radius-circle);
  background: var(--pw-bg-subtle);
  color: var(--pw-fg-tertiary);
}
.pw-settings-row__body { flex: 1 1 auto; min-width: 0; }
.pw-settings-row__title {
  color: var(--pw-fg-primary);
  font-size: var(--pw-text-sm-size);
  line-height: var(--pw-text-sm-line);
  font-weight: var(--pw-font-weight-semibold);
}
.pw-settings-row__desc {
  margin-top: 1px;
  color: var(--pw-fg-tertiary);
  font-size: var(--pw-text-xs-size);
  line-height: var(--pw-text-xs-line);
}
.pw-settings-row__end { flex: none; display: flex; align-items: center; gap: var(--pw-space-8); }
.pw-settings-row__chevron { flex: none; color: var(--pw-fg-quaternary); }

/* The value picker inside a settings row is quiet until you touch it: no
   border, no shadow — the row already provides the boundary.
 *
 * These target the TRIGGER, not the `.pw-select` wrapper. The wrapper is a
 * bare positioning context: it carries no border, no background and no
 * radius, so styling it does nothing except paint a hard-edged rectangle
 * behind the pill on hover — and `aria-expanded` lives on the trigger too,
 * so the open state never matched at all. */
.pw-settings-row .pw-select__trigger {
  border-color: transparent;
  background: transparent;
  box-shadow: none;
}
/* Hover and open are different weights on purpose: hover is a hint that the
   control is live, open is a state you are in. Using the pressed token for
   both made a plain hover look like something had been activated. */
.pw-settings-row .pw-select__trigger:hover { background: var(--pw-bg-hover); }
.pw-settings-row .pw-select__trigger[aria-expanded="true"] { background: var(--pw-bg-active); }

/* ------------------------------------------------------------------------ *
 * DUAL RANGE SLIDER
 *
 * Two native range inputs stacked over one drawn track. Native gives us
 * keyboard, touch and screen-reader support for free; the visible track and
 * fill are ours, because a native track cannot show a *between* selection.
 * Each input keeps its own accessible name ("Earliest", "Latest") — one
 * shared label would leave a user unable to tell the thumbs apart.
 * ------------------------------------------------------------------------ */
.pw-range { position: relative; height: 24px; display: flex; align-items: center; }
.pw-range__track {
  position: absolute;
  inset-inline: 0;
  height: 4px;
  border-radius: var(--pw-radius-pill);
  background: var(--pw-bg-active);
}
.pw-range__fill {
  position: absolute;
  top: 0; bottom: 0;
  border-radius: var(--pw-radius-pill);
  background: var(--pw-action-bg);
}
/* The inputs are invisible but fully live: only their thumbs are painted,
   and pointer-events are off on the bar so the two do not block each other. */
.pw-range > input[type="range"] {
  position: absolute;
  inset-inline: 0;
  width: 100%;
  height: 24px;
  margin: 0;
  background: none;
  pointer-events: none;
  -webkit-appearance: none;
  appearance: none;
}
.pw-range > input[type="range"]::-webkit-slider-thumb {
  -webkit-appearance: none;
  appearance: none;
  pointer-events: auto;
  width: 18px; height: 18px;
  border: 2px solid var(--pw-action-bg);
  border-radius: var(--pw-radius-circle);
  background: var(--pw-bg-surface);
  box-shadow: var(--pw-shadow-sm);
  cursor: grab;
}
.pw-range > input[type="range"]::-moz-range-thumb {
  pointer-events: auto;
  width: 18px; height: 18px;
  border: 2px solid var(--pw-action-bg);
  border-radius: var(--pw-radius-circle);
  background: var(--pw-bg-surface);
  box-shadow: var(--pw-shadow-sm);
  cursor: grab;
}
.pw-range > input[type="range"]:active::-webkit-slider-thumb { cursor: grabbing; }
.pw-range > input[type="range"]:focus-visible::-webkit-slider-thumb { box-shadow: var(--pw-focus-ring); }
.pw-range > input[type="range"]:focus-visible::-moz-range-thumb  { box-shadow: var(--pw-focus-ring); }
.pw-range > input[type="range"]::-webkit-slider-runnable-track { background: none; }
.pw-range > input[type="range"]::-moz-range-track { background: none; }

/* A segmented control that fills the width it is given, splitting evenly. */
.pw-segmented--block,
.pw-seg-wide { display: flex; width: 100%; }
.pw-segmented--block > .pw-segmented__item,
.pw-seg-wide > .pw-segmented__item { flex: 1 1 0; justify-content: center; }


/* ======================================================================== *
 * LAYER MOTION
 *
 * Menus, popovers and the suggestion list use [hidden] as their source of
 * truth — assistive tech and find-in-page depend on it. The `is-open` class
 * carries the animation only, added a frame after `hidden` is cleared so
 * there is a start state to interpolate from.
 *
 * All of it is gated on [data-pw-layer=""] — the EMPTY value, which is what the
 * controller stamps when it is animating the layer in CSS. When the motion
 * layer is loaded it stamps [data-pw-layer="gsap"] instead and none of this
 * matches, so GSAP owns opacity and transform outright rather than writing
 * inline styles over a live transition and racing it. Same gate either way:
 * an unclaimed layer matches nothing here and is simply visible.
 *
 * All of it is gated on the attribute, which the JS controller stamps on
 * the element when it takes ownership. That matters: a layer rendered
 * without JS — a docs example, SSR before hydration — must never be left at
 * opacity 0 with nothing coming to turn it on. No controller, no animation,
 * and the layer is simply visible.
 *
 * Enter is slower than exit: appearing should feel deliberate, disappearing
 * should get out of the way.
 * ======================================================================== */
.pw-menu[data-pw-layer=""],
.pw-filter-menu[data-pw-layer=""],
.pw-suggest[data-pw-layer=""],
.pw-settings-menu[data-pw-layer=""],
.pw-popover[data-pw-layer=""] {
  opacity: 0;
  transform: translateY(-4px) scale(0.99);
  transform-origin: top center;
  transition: opacity var(--pw-duration-fast) var(--pw-ease-out),
              transform var(--pw-duration-fast) var(--pw-ease-out);
}
.pw-popover[data-pw-layer=""].is-open,
.pw-menu[data-pw-layer=""].is-open,
.pw-filter-menu[data-pw-layer=""].is-open,
.pw-suggest[data-pw-layer=""].is-open,
.pw-settings-menu[data-pw-layer=""].is-open {
  opacity: 1;
  transform: none;
  transition-duration: var(--pw-duration-base);
}

/* The suggestion list is welded to the field, so it may not slide — it would
   tear away from the seam. It grows down from the join instead. */
.pw-suggest[data-pw-layer=""] { transform: scaleY(0.96); }
.pw-suggest[data-pw-layer=""].is-open { transform: none; }

@media (prefers-reduced-motion: reduce) {
  .pw-menu[data-pw-layer=""], .pw-filter-menu[data-pw-layer=""],
  .pw-suggest[data-pw-layer=""], .pw-settings-menu[data-pw-layer=""],
  .pw-popover[data-pw-layer=""], .pw-accordion__panel {
    transition: none;
    transform: none;
  }
}


/* ======================================================================== *
 * BRAND MARK
 *
 * One asset, both themes. The shield keeps its three brand colours in every
 * context — those are the brand and they do not follow a theme — while the
 * fingerprint ridges and the "Privacy" half of the wordmark are drawn in
 * `currentColor`, so they land near-black on white and near-white on the dark
 * canvas. Shipping a second, hand-tinted file is how a wordmark quietly drifts
 * out of sync with its own mark.
 *
 *   <svg class="pw-logo" role="img" aria-label="PrivacyWall">
 *     <use href="#pw-logo"/>
 *   </svg>
 * ======================================================================== */
.pw-logo,
.pw-logomark {
  display: block;
  height: 38px;
  width: auto;
  color: var(--pw-logo-ink);
  flex: none;
}
.pw-logo    { aspect-ratio: 173 / 38; }
.pw-logomark{ aspect-ratio: 1 / 1; }

.pw-logo--sm    { height: 28px; }
.pw-logo--lg    { height: 48px; }
.pw-logomark--sm{ height: 24px; }
.pw-logomark--lg{ height: 48px; }

/* Locks the ink regardless of theme — for a mark placed on a fixed-colour
   surface: a coloured hero, a photo, a print asset. */
.pw-logo--on-dark, .pw-logomark--on-dark   { color: var(--pw-color-white); }
.pw-logo--on-light, .pw-logomark--on-light { color: #263947; }


/* A third-party mark used inline — the Chrome Web Store badge on "Get
   Secured", an app-store button.
 *
 * Referenced with <img>, not from the sprite. These marks carry their own
 * gradients and fixed colours: inlining one means rewriting every internal id
 * to avoid collisions, and a multi-gradient mark tends to survive that
 * *almost* intact — the shape still draws and only the fill disappears. An
 * <img> renders the file exactly as its owner published it, which is also the
 * right licensing posture. Sized like an icon so it sits on the same baseline;
 * it never takes currentColor and is never recoloured. */
.pw-brand-mark {
  display: inline-block;
  flex: 0 0 auto;
  height: var(--pw-icon-md);
  width: auto;
  vertical-align: -0.25em;
}
.pw-brand-mark--sm { height: var(--pw-icon-sm); }
.pw-brand-mark--lg { height: var(--pw-icon-lg); }

/* A mark that is one colour is a different object, and it takes the other
 * route. An <img> of a black glyph has a failure the coloured marks do not: it
 * stays black on a dark surface. So the file supplies the shape through a mask
 * and `currentColor` supplies the ink, which means the mark follows the theme
 * — and follows the button it sits in, going white inside a filled one without
 * a second file. The shape is still reproduced exactly, never redrawn on our
 * grid; see assets/brand/README.md for which folder a mark belongs in.
 *
 * Square rather than `width: auto`, because these are used in rows: a set of
 * glyphs on one optical box lines up, and their own aspect ratios do not. */
.pw-brand-glyph {
  display: inline-block;
  flex: 0 0 auto;
  width: var(--pw-icon-md); height: var(--pw-icon-md);
  background: currentColor;
  -webkit-mask: var(--pw-brand-glyph) center / contain no-repeat;
  mask: var(--pw-brand-glyph) center / contain no-repeat;
}
.pw-brand-glyph--sm { width: var(--pw-icon-sm); height: var(--pw-icon-sm); }
.pw-brand-glyph--lg { width: var(--pw-icon-lg); height: var(--pw-icon-lg); }

/* A coloured mark inside a button wants the same square, for the same reason:
   Google's G is 256×262 and Discord's is 256×199, so at a shared height they
   are different widths and the labels beside them start at different places. */
.pw-brand-mark--box {
  width: var(--pw-icon-md); height: var(--pw-icon-md);
  object-fit: contain; vertical-align: middle;
}
.pw-brand-mark--sm.pw-brand-mark--box { width: var(--pw-icon-sm); height: var(--pw-icon-sm); }
.pw-brand-mark--lg.pw-brand-mark--box { width: var(--pw-icon-lg); height: var(--pw-icon-lg); }


/* Our own marks that carry fixed colours follow the same route, for the same
   reason rather than the licensing one: the maps pin is a three-stop gradient,
   so it cannot be a `currentColor` sprite symbol and it must not be flattened
   into one. It keeps its 16:22 aspect — height drives, width follows. */
.pw-mark-pin { height: 22px; width: auto; flex: 0 0 auto; }
.pw-search--sm .pw-mark-pin { height: 20px; }
.pw-search--xs .pw-mark-pin { height: 18px; }

/* =============================================================================
   COLOUR PALETTE  ·  .pw-palette
   Nineteen swatches on a frosted disc: twelve full colours on the ring, six
   soft tints overlapping them like petals, white on top at the centre. Each
   swatch is a small lit object -- a highlight on its top edge, a shade under
   it, a soft shadow on what it rests on -- and a thin rainbow rim glows round
   the disc. It is a radiogroup -- one tab stop, arrows move -- filled in by
   the script.
   Sizes    sm (168) · md (224, default) · lg (288)
   States   rest · hover · focus · checked · disabled
   ============================================================================= */
.pw-palette {
  --pw-palette-size: 224px;
  --pw-palette-light: var(--pw-color-white);
  --pw-palette-shade: var(--pw-fg-primary);
  position: relative; isolation: isolate;
  flex: none;
  width: var(--pw-palette-size); max-width: 100%;
  aspect-ratio: 1;
  border-radius: var(--pw-radius-circle);
  user-select: none;
}
/* The disc: a pale frosted face, a little darker toward the rim, inset so the
   open wheel has room to spread past it. */
.pw-palette::after {
  content: "";
  position: absolute; inset: 10%; z-index: 0;
  border-radius: inherit;
  pointer-events: none;
  background: radial-gradient(closest-side,
    color-mix(in srgb, var(--pw-bg-surface) 92%, var(--pw-palette-shade)) 70%,
    color-mix(in srgb, var(--pw-bg-surface) 86%, var(--pw-palette-shade)) 100%);
  box-shadow:
    inset 0 0 0 calc(var(--pw-palette-size) * 0.028) color-mix(in srgb, var(--pw-palette-light) 55%, transparent),
    0 calc(var(--pw-palette-size) * 0.04) calc(var(--pw-palette-size) * 0.12) color-mix(in srgb, var(--pw-palette-shade) 10%, transparent);
}
.pw-palette--sm { --pw-palette-size: 168px; }
.pw-palette--lg { --pw-palette-size: 288px; }
/* The rim: the rainbow as a hairline round the disc, and the same line
   blurred behind it so it glows. A mask reads only alpha; any opaque colour
   will do, so a token it is. */
.pw-palette::before,
.pw-palette__glow {
  content: "";
  position: absolute; inset: 10%;
  border-radius: var(--pw-radius-circle);
  background: conic-gradient(from 0deg, var(--pw-palette-hues));
  -webkit-mask: radial-gradient(closest-side, transparent calc(100% - 2px), var(--pw-fg-primary) calc(100% - 1.5px));
          mask: radial-gradient(closest-side, transparent calc(100% - 2px), var(--pw-fg-primary) calc(100% - 1.5px));
  pointer-events: none;
}
.pw-palette::before { z-index: 5; opacity: .9; }
.pw-palette__glow {
  z-index: -1;
  -webkit-mask: radial-gradient(closest-side, transparent 88%, var(--pw-fg-primary) 97%);
          mask: radial-gradient(closest-side, transparent 88%, var(--pw-fg-primary) 97%);
  filter: blur(calc(var(--pw-palette-size) * 0.02));
  opacity: .7;
}
.pw-palette__swatch {
  --pw-swatch: var(--pw-color-white);
  position: absolute; z-index: 1;
  width: 19%; aspect-ratio: 1;
  /* Centred on its point by margins -- half its width, and a margin's
     percentage is of the wheel's width either way. Not `translate`: GSAP
     owns `transform` for the hover and clears `translate` when it does. */
  margin: -9.5% 0 0 -9.5%; padding: 0;
  border: 0; border-radius: var(--pw-radius-circle);
  /* Lit from above: a sheen over the top half on the colour itself. */
  background:
    linear-gradient(180deg, color-mix(in srgb, var(--pw-palette-light) 12%, transparent), transparent 50%),
    var(--pw-swatch);
  box-shadow:
    inset 0 1px 0 color-mix(in srgb, var(--pw-palette-light) 55%, transparent),
    inset 0 -2px 3px color-mix(in srgb, var(--pw-palette-shade) 8%, transparent),
    0 1px 1px color-mix(in srgb, var(--pw-palette-shade) 10%, transparent),
    0 3px 6px color-mix(in srgb, var(--pw-palette-shade) 9%, transparent),
    0 8px 16px color-mix(in srgb, var(--pw-palette-shade) 8%, transparent);
  cursor: pointer;
  /* Its own GPU layer: the hover scales and moves it every frame, and a
     layered shadow repainted each time is what makes that feel heavy. */
  will-change: transform;
  transition: box-shadow var(--pw-duration-fast) var(--pw-ease-out);
}
/* The tints rest on the ring, white rests on the tints. */
.pw-palette__swatch[data-ring="1"] { z-index: 2; }
.pw-palette__swatch[data-ring="0"] { z-index: 3; }
.pw-palette__swatch:focus-visible { outline: 2px solid var(--pw-focus-ring-color); outline-offset: 2px; }
/* Checked: a gap of the page's colour and a ring, so it reads on any swatch --
   white included -- in its own layer like everything else. */
.pw-palette__swatch[aria-checked="true"] {
  box-shadow:
    inset 0 1px 0 color-mix(in srgb, var(--pw-palette-light) 55%, transparent),
    0 0 0 3px var(--pw-bg-surface),
    0 0 0 5px var(--pw-fg-primary),
    0 10px 20px color-mix(in srgb, var(--pw-palette-shade) 16%, transparent);
}
.pw-palette[aria-disabled="true"] { opacity: .5; }
.pw-palette[aria-disabled="true"] .pw-palette__swatch { cursor: not-allowed; }
@media (prefers-reduced-motion: reduce) {
  .pw-palette__swatch { transition: none; }
}

/* =============================================================================
   ARC CAROUSEL  ·  .pw-arc
   Cards on a wheel that turns around a point below them: the front card full
   size, each step away leaning 18° further, smaller and fainter. The script
   lays every card out from one number, the wheel's position, which GSAP
   tweens; a drag moves the same number under the finger.
   Parts    stage · card (img) · foot (prev · caption · next) · explore
   ============================================================================= */
.pw-arc {
  --pw-arc-w: 140px;
  --pw-arc-h: 176px;
  display: flex; flex-direction: column; align-items: center; gap: var(--pw-space-6);
  width: 100%; outline: none;
  user-select: none;
}
.pw-arc--lg { --pw-arc-w: 176px; --pw-arc-h: 220px; }
.pw-arc:is(:focus-visible, .is-focus) .pw-arc__card[aria-current="true"] { outline: 2px solid var(--pw-focus-ring-color); outline-offset: 3px; }
.pw-arc__stage {
  position: relative; width: 100%;
  height: calc(var(--pw-arc-h) * 1.5);
  overflow: hidden;
  touch-action: pan-y; cursor: grab;
  /* The wheel fades out at its sides rather than being cut by them. */
  -webkit-mask: linear-gradient(90deg, transparent, var(--pw-fg-primary) 14%, var(--pw-fg-primary) 86%, transparent);
          mask: linear-gradient(90deg, transparent, var(--pw-fg-primary) 14%, var(--pw-fg-primary) 86%, transparent);
}
.pw-arc__stage:active { cursor: grabbing; }
.pw-arc__card {
  /* Each card starts AT the pivot and is carried out to the arc by the
     script's transform -- turn, then out along the radius, then its own
     scale -- so shrinking a card never drags it off the curve. */
  --pw-arc-r: calc(var(--pw-arc-w) * 2.43);
  position: absolute; left: calc(50% - var(--pw-arc-w) / 2);
  top: calc(var(--pw-space-6) + var(--pw-arc-r));
  width: var(--pw-arc-w); height: var(--pw-arc-h); padding: 0;
  border: 0; border-radius: var(--pw-radius-xl); overflow: hidden;
  background: var(--pw-bg-muted);
  box-shadow: 0 1px 2px color-mix(in srgb, var(--pw-fg-primary) 8%, transparent), 0 10px 24px -6px color-mix(in srgb, var(--pw-fg-primary) 22%, transparent);
  cursor: pointer; will-change: transform, opacity;
}
.pw-arc__card img { display: block; width: 100%; height: 100%; object-fit: cover; pointer-events: none; -webkit-user-drag: none; }
.pw-arc__card[aria-current="true"] { cursor: default; }
.pw-arc__foot { display: flex; align-items: center; justify-content: center; gap: var(--pw-space-8); width: 100%; max-width: 420px; }
.pw-arc__caption { flex: 1; min-width: 0; text-align: center; }
.pw-arc__title { margin: 0; font-size: var(--pw-text-lg-size); font-weight: var(--pw-font-weight-bold); color: var(--pw-fg-primary); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.pw-arc__place { display: inline-flex; align-items: center; gap: var(--pw-space-2); margin: var(--pw-space-1) 0 0; font-size: var(--pw-text-sm-size); color: var(--pw-fg-tertiary); }
.pw-arc__place .pw-icon { color: var(--pw-fg-quaternary); }
.pw-arc__place-text { white-space: nowrap; }

/* =============================================================================
   CONVERSATION  ·  .pw-convo
   Two sides of a chat in bubbles: the reader's on the right in the brand
   colour, the other side on the left in a muted fill. A run from one side is
   grouped -- the corners between its bubbles tuck in. A bubble with a time
   shows it when pressed. Typing is a bubble of its own, then the answer.
   ============================================================================= */
.pw-convo { display: flex; flex-direction: column; gap: 2px; width: 100%; max-width: 480px; }
.pw-convo__row { display: flex; flex-direction: column; align-items: flex-start; }
.pw-convo__row[data-from="me"] { align-items: flex-end; }
.pw-convo__row.is-first { margin-block-start: var(--pw-space-5); }
.pw-convo__row:first-child { margin-block-start: 0; }
.pw-convo__bubble {
  --r: 18px; --t: 6px;
  display: inline-flex; align-items: center; gap: var(--pw-space-3);
  max-width: 78%; padding: var(--pw-space-4) var(--pw-space-6);
  border-radius: var(--r);
  /* A step darker than the muted ground, so theirs reads as a bubble on a
     page, a card or a panel alike. */
  background: var(--pw-bg-strong); color: var(--pw-fg-primary);
  font-size: var(--pw-text-sm-size); line-height: var(--pw-text-sm-line);
  overflow-wrap: anywhere;
}
.pw-convo__row[data-from="me"] .pw-convo__bubble { background: var(--pw-action-bg); color: var(--pw-action-fg); }
/* Tucked corners: on the reader's side for theirs, on the other side for mine. */
.pw-convo__row[data-from="them"]:not(.is-first) .pw-convo__bubble { border-start-start-radius: var(--t); }
.pw-convo__row[data-from="them"]:not(.is-last) .pw-convo__bubble { border-end-start-radius: var(--t); }
.pw-convo__row[data-from="me"]:not(.is-first) .pw-convo__bubble { border-start-end-radius: var(--t); }
.pw-convo__row[data-from="me"]:not(.is-last) .pw-convo__bubble { border-end-end-radius: var(--t); }
.pw-convo__bubble[data-time] { cursor: pointer; }
.pw-convo__bubble[data-time]:focus-visible { outline: 2px solid var(--pw-focus-ring-color); outline-offset: 2px; }
.pw-convo__bubble--status { background: transparent; box-shadow: inset 0 0 0 1px var(--pw-border-subtle); color: var(--pw-fg-secondary); }
.pw-convo__bubble--status .pw-icon { flex: none; color: var(--pw-tint-warning-fg); }
.pw-convo__bubble--typing { color: var(--pw-fg-tertiary); }
.pw-convo__dots { display: inline-flex; gap: 3px; }
.pw-convo__dots i { width: 4px; height: 4px; border-radius: var(--pw-radius-circle); background: currentColor; opacity: .45; }
.pw-convo__time { padding: var(--pw-space-2) var(--pw-space-3) 0; overflow: hidden; font-size: var(--pw-text-xs-size); color: var(--pw-fg-tertiary); font-variant-numeric: tabular-nums; }

/* =============================================================================
   CRM TABLE  ·  .pw-crm
   Accounts in a table that works like a tool: select, sort, resize, a pinned
   first column, win odds and activity drawn in the row, and a footer that
   answers "how much" for any column. Built from the system's checkbox, tag
   and avatar; the script reads everything from the markup.
   ============================================================================= */
.pw-crm {
  position: relative;
  border: 1px solid var(--pw-border-subtle); border-radius: var(--pw-radius-xl);
  background: var(--pw-bg-surface); overflow: hidden;
  font-size: var(--pw-text-sm-size);
}
.pw-crm__scroll { max-height: 520px; overflow: auto; overscroll-behavior: contain; }
.pw-crm__table { width: max-content; min-width: 100%; border-collapse: separate; border-spacing: 0; table-layout: fixed; }
.pw-crm__table th, .pw-crm__table td {
  height: 44px; padding: 0 var(--pw-space-6);
  border-block-end: 1px solid var(--pw-border-faint);
  background: var(--pw-bg-surface);
  text-align: start; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  color: var(--pw-fg-secondary);
}
.pw-crm__table th + th, .pw-crm__table td + td { border-inline-start: 1px solid var(--pw-border-faint); }
/* The head stays on top while the rows scroll under it. */
.pw-crm__table thead th {
  position: sticky; top: 0; z-index: 3;
  height: 40px; background: var(--pw-bg-subtle);
  font-weight: var(--pw-font-weight-medium); color: var(--pw-fg-tertiary);
}
.pw-crm.is-scrolled-y thead th { box-shadow: 0 1px 0 var(--pw-border-subtle); }
.pw-crm__th {
  display: inline-flex; align-items: center; gap: var(--pw-space-3); max-width: 100%;
  padding: 0; border: 0; background: none; color: inherit; font: inherit; cursor: pointer;
}
.pw-crm__th .pw-icon { flex: none; color: var(--pw-fg-quaternary); }
.pw-crm__th:hover { color: var(--pw-fg-primary); }
.pw-crm__sort { opacity: 0; transition: opacity var(--pw-duration-fast) var(--pw-ease-out), transform var(--pw-duration-fast) var(--pw-ease-out); }
th[aria-sort="ascending"] .pw-crm__sort, th[aria-sort="descending"] .pw-crm__sort { opacity: 1; }
th[aria-sort="descending"] .pw-crm__sort { transform: rotate(180deg); }
th[aria-sort="ascending"] .pw-crm__th, th[aria-sort="descending"] .pw-crm__th { color: var(--pw-fg-primary); }
/* Resize: a grip on the header's trailing edge. */
.pw-crm__table thead th { overflow: visible; }
.pw-crm__resize { position: absolute; inset-block: 0; inset-inline-end: -3px; z-index: 1; width: 6px; cursor: col-resize; }
.pw-crm__resize::after { content: ""; position: absolute; inset-block: 25%; inset-inline-start: 2px; width: 2px; border-radius: 1px; background: var(--pw-action-bg); opacity: 0; transition: opacity var(--pw-duration-fast) var(--pw-ease-out); }
.pw-crm__resize:hover::after, .pw-crm.is-resizing .pw-crm__resize:active::after { opacity: 1; }
.pw-crm.is-resizing { cursor: col-resize; user-select: none; }
/* Pinned: sticks to the start; the last pinned column casts a shadow once the
   table has scrolled under it. */
.pw-crm__table .is-pin { position: sticky; z-index: 2; }
.pw-crm__table thead .is-pin { z-index: 4; }
.pw-crm__table .is-pin-last { transition: box-shadow var(--pw-duration-fast) var(--pw-ease-out); }
.pw-crm.is-scrolled .is-pin-last { box-shadow: 6px 0 10px -6px color-mix(in srgb, var(--pw-fg-primary) 22%, transparent); }
[dir="rtl"] .pw-crm.is-scrolled .is-pin-last { box-shadow: -6px 0 10px -6px color-mix(in srgb, var(--pw-fg-primary) 22%, transparent); }
/* Rows */
.pw-crm__table tbody tr:hover td { background: var(--pw-bg-subtle); }
.pw-crm__table tbody tr.is-selected td { background: color-mix(in srgb, var(--pw-action-bg) 7%, var(--pw-bg-surface)); }
.pw-crm__table tbody tr:last-child td { border-block-end: 0; }
.pw-crm__select { width: 44px; padding: 0 !important; text-align: center !important; }
.pw-crm__select .pw-choice { justify-content: center; }
.pw-crm__company { display: inline-flex; align-items: center; gap: var(--pw-space-4); max-width: 100%; color: var(--pw-fg-primary); font-weight: var(--pw-font-weight-medium); }
.pw-crm__logo {
  flex: none; display: grid; place-items: center; width: 22px; height: 22px;
  border-radius: var(--pw-radius-sm); background: var(--pw-bg-inverse); color: var(--pw-fg-inverse);
  font-size: 11px; font-weight: var(--pw-font-weight-bold);
}
img.pw-crm__logo { display: block; object-fit: cover; background: none; }
.pw-crm__tags { display: inline-flex; align-items: center; gap: var(--pw-space-2); }
.pw-crm__owner { display: inline-flex; align-items: center; gap: var(--pw-space-3); color: var(--pw-fg-primary); }
.pw-crm__num { text-align: end !important; font-variant-numeric: tabular-nums; color: var(--pw-fg-primary) !important; }
.pw-crm__money { display: inline-flex; gap: var(--pw-space-2); }
.pw-crm__money > span:first-child { color: var(--pw-fg-quaternary); }
/* Win odds: the number, then five steps filled in the tone the number earns. */
.pw-crm__win { display: inline-flex; align-items: center; gap: var(--pw-space-4); font-variant-numeric: tabular-nums; color: var(--pw-fg-primary); }
.pw-crm__bars { display: inline-flex; gap: 2px; }
.pw-crm__bars i { width: 4px; height: 12px; border-radius: 1px; background: var(--pw-bg-strong); }
.pw-crm__win[data-tone="good"] .pw-crm__bars i.is-on { background: var(--pw-fg-success); }
.pw-crm__win[data-tone="fair"] .pw-crm__bars i.is-on { background: var(--pw-tint-warning-fg); }
.pw-crm__win[data-tone="risk"] .pw-crm__bars i.is-on { background: var(--pw-fg-danger); }
.pw-crm__trend { display: inline-flex; vertical-align: middle; }
.pw-crm__trend[data-dir="up"] { color: var(--pw-fg-success); }
.pw-crm__trend[data-dir="down"] { color: var(--pw-fg-danger); }
.pw-crm__date { display: inline-flex; align-items: center; gap: var(--pw-space-3); }
.pw-crm__date .pw-icon { color: var(--pw-fg-quaternary); }
.pw-crm__act { text-align: center !important; }
/* Footer: sticky at the bottom; each cell a quiet button that becomes an answer. */
.pw-crm__table tfoot td { position: sticky; bottom: 0; z-index: 3; height: 40px; border-block-start: 1px solid var(--pw-border-subtle); border-block-end: 0; background: var(--pw-bg-subtle); }
.pw-crm__table tfoot .is-pin { z-index: 4; }
.pw-crm__foot-count { color: var(--pw-fg-tertiary); font-variant-numeric: tabular-nums; }
.pw-crm.has-selection .pw-crm__foot-count { color: var(--pw-fg-brand); font-weight: var(--pw-font-weight-medium); }
.pw-crm__calc {
  display: inline-flex; align-items: baseline; gap: var(--pw-space-2); max-width: 100%;
  padding: var(--pw-space-1) var(--pw-space-3); margin-inline-start: calc(var(--pw-space-3) * -1);
  border: 0; border-radius: var(--pw-radius-md); background: none;
  color: var(--pw-fg-secondary); font: inherit; cursor: pointer;
}
.pw-crm__calc:hover { background: var(--pw-bg-hover); }
.pw-crm__calc-hint { color: var(--pw-fg-quaternary); opacity: 0; transition: opacity var(--pw-duration-fast) var(--pw-ease-out); }
.pw-crm__table tfoot tr:hover .pw-crm__calc-hint, .pw-crm__calc:focus-visible .pw-crm__calc-hint { opacity: 1; }
.pw-crm__calc-kind { color: var(--pw-fg-tertiary); font-size: var(--pw-text-xs-size); }
.pw-crm__calc-val { color: var(--pw-fg-primary); font-weight: var(--pw-font-weight-semibold); font-variant-numeric: tabular-nums; }
.pw-crm__menu { min-width: 180px; }

/* =============================================================================
   BROWSER TABS — one thing, in several places, as a browser window.
   -----------------------------------------------------------------------------
   The strip is the top of the window and the panel is the page: the selected tab is the same surface as the panel and
   joins it with a curved foot on each side, the way a browser tab does. Use it when a set of tabs are the same kind of
   thing (one app on six platforms, one product in four editions) and each deserves a full panel, not when the tabs
   only filter a list (that is .pw-tabs).

     .pw-browser
       .pw-browser__tabs   role="tablist"
         .pw-browser__tab  role="tab"   (.pw-browser__mark for a small picture before the label)
       .pw-browser__panel  role="tabpanel"   one per tab

   PW.mount() wires the roles, the arrow keys and the swap. Every panel shares one grid cell, so the fade between two of
   them happens in place and the window never changes height.
   ============================================================================= */
.pw-browser {
  --pw-browser-r: var(--pw-radius-3xl);        /* the window's corner */
  --pw-browser-foot: 16px;                     /* the curve where the selected tab meets the panel */
  display: grid; grid-template-columns: minmax(0, 1fr);
  border-radius: var(--pw-browser-r);
  background: color-mix(in srgb, var(--pw-bg-subtle), var(--pw-fg-primary) 5%);   /* the strip */
  border: 1px solid var(--pw-border-default);
  overflow: hidden;
}
/* The page's surface is a layer of its own under every panel. If each panel carried its own background, a panel that is
   fading would turn see-through and show the strip's gray behind it. */
.pw-browser::before {
  content: ""; grid-row: 2; grid-column: 1;
  background: var(--pw-bg-surface);
  border-radius: var(--pw-browser-r);
}
.pw-browser:has(.pw-browser__tab:first-child[aria-selected="true"])::before { border-top-left-radius: 0; }

.pw-browser__tabs {
  grid-row: 1; grid-column: 1;
  display: flex; align-items: flex-end; gap: 0;
  min-width: 0;
  padding: var(--pw-space-5) var(--pw-space-6) 0 0;
  overflow-x: auto; overflow-y: hidden; scrollbar-width: none;
}
.pw-browser__tabs::-webkit-scrollbar { display: none; }

.pw-browser__tab {
  position: relative; flex: none;
  display: inline-flex; align-items: center; gap: var(--pw-space-4);
  height: 46px; padding-inline: var(--pw-space-10);
  border: 0; border-radius: 14px 14px 0 0; background: none;
  color: var(--pw-fg-secondary);
  font: inherit; font-size: var(--pw-text-sm-size); font-weight: var(--pw-font-weight-semibold);
  white-space: nowrap; cursor: pointer;
  /* Colour only: the feet appear at once, so the fill has to as well. */
  transition: color var(--pw-duration-fast) var(--pw-ease-out);
}
.pw-browser__tab:hover:not([aria-selected="true"]) {
  background: color-mix(in srgb, var(--pw-bg-surface) 55%, transparent);
  color: var(--pw-fg-primary);
}
.pw-browser__tab[aria-selected="true"] { background: var(--pw-bg-surface); color: var(--pw-fg-primary); z-index: 2; }
.pw-browser__tab:focus-visible { outline: 2px solid var(--pw-border-brand); outline-offset: -3px; }
.pw-browser__mark { width: 22px; height: 22px; border-radius: 6px; flex: none; }

/* The feet: a quarter-circle cut out of a square of the surface colour, one each side of the selected tab. */
.pw-browser__tab[aria-selected="true"]::before,
.pw-browser__tab[aria-selected="true"]::after {
  content: ""; position: absolute; bottom: 0;
  width: var(--pw-browser-foot); height: var(--pw-browser-foot);
}
.pw-browser__tab[aria-selected="true"]::before {
  left: calc(var(--pw-browser-foot) * -1);
  background: radial-gradient(circle at 0 0, transparent var(--pw-browser-foot), var(--pw-bg-surface) calc(var(--pw-browser-foot) + 0.5px));
}
.pw-browser__tab[aria-selected="true"]::after {
  right: calc(var(--pw-browser-foot) * -1);
  background: radial-gradient(circle at 100% 0, transparent var(--pw-browser-foot), var(--pw-bg-surface) calc(var(--pw-browser-foot) + 0.5px));
}
/* The first tab sits flush with the window's edge, so it has no left foot. */
.pw-browser__tab:first-child::before { display: none; }

.pw-browser__panel {
  grid-row: 2; grid-column: 1; position: relative;
  min-height: var(--pw-browser-min, 0);
  padding: var(--pw-browser-pad, var(--pw-space-20));
  background: transparent;
}
.pw-browser__panel[hidden] { display: none; }

@media (max-width: 900px) {
  .pw-browser { --pw-browser-r: var(--pw-radius-2xl); }
  .pw-browser__tab { padding-inline: var(--pw-space-6); }
}
