/* =============================================================
   bidmy.app
   Apple HIG type scale and 4pt grid, Shadcn flavoured components,
   built by hand. Dark by default, light through a body class.
   ============================================================= */

/* -------------------------------------------------------------
   1. Tokens
   ------------------------------------------------------------- */

:root {
  /* Colour, dark */
  --bg: #000000;
  --surface: #1C1C1E;
  --surface-2: #2C2C2E;
  --surface-3: #3A3A3C;
  /* The footer sits on its own plane, just off the page behind it. */
  --surface-sunk: #0D0D0F;
  --blue-tint: rgba(10, 132, 255, 0.12);
  --separator: rgba(84, 84, 88, 0.65);
  --label: rgba(255, 255, 255, 0.92);
  --label-2: rgba(235, 235, 245, 0.60);
  --label-3: rgba(235, 235, 245, 0.30);
  --blue: #0A84FF;
  --green: #30D158;
  --orange: #FF9F0A;
  --red: #FF453A;

  --material: rgba(28, 28, 30, 0.72);
  --material-strong: rgba(0, 0, 0, 0.78);
  --on-accent: #FFFFFF;
  --shadow-1: 0 1px 2px rgba(0, 0, 0, 0.40);
  --shadow-2: 0 8px 24px rgba(0, 0, 0, 0.45);
  --shadow-3: 0 24px 64px rgba(0, 0, 0, 0.55);
  --ring: rgba(10, 132, 255, 0.45);
  --accent-soft: rgba(10, 132, 255, 0.16);
  --elevated: #2C2C2E;

  /* Soft fills behind a coloured icon. */
  --tint-green: rgba(48, 209, 88, 0.16);
  --tint-orange: rgba(255, 159, 10, 0.16);
  --tint-blue: rgba(10, 132, 255, 0.16);

  /* Medal tones, overridden in light mode where bright gold washes out. */
  --medal-gold: #FFB340;
  --medal-silver: #B8BCC4;
  --medal-bronze: #C98B5E;

  /* Type scale */
  --fs-caption2: 11px;
  --fs-caption1: 12px;
  --fs-footnote: 13px;
  --fs-body: 15px;
  --fs-headline: 17px;
  --fs-title3: 22px;
  --fs-title2: 28px;
  --fs-title1: 34px;

  /* Spacing, 4pt grid */
  --sp-1: 4px;
  --sp-2: 8px;
  --sp-3: 12px;
  --sp-4: 16px;
  --sp-5: 20px;
  --sp-6: 24px;
  --sp-7: 32px;
  --sp-8: 44px;
  --sp-9: 64px;

  /* Radius */
  --r-sm: 8px;
  --r-md: 12px;
  --r-lg: 16px;
  --r-pill: 980px;

  /* Metrics */
  --control-h: 44px;
  --row-h: 64px;
  --page-pad: 20px;
  --max-w: 1360px;

  /* Motion */
  --enter: 200ms cubic-bezier(0.22, 0.61, 0.36, 1);
  --state: 150ms cubic-bezier(0.22, 0.61, 0.36, 1);

  --font-sans: "Geist", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
    "Helvetica Neue", Arial, sans-serif;
  --font-mono: "Geist Mono", ui-monospace, SFMono-Regular, "SF Mono", Menlo,
    Consolas, "Liberation Mono", monospace;
}

/*
 * Light mode follows the grouped-background idea rather than a flat sheet
 * of white: the page is a soft grey and the cards sitting on it are the
 * white ones. The previous palette had it the other way round, with a
 * pure white page and grey cards, which is why every surface read as a
 * dent instead of a card. Shadows do the lifting; borders stay faint.
 */
body.light {
  /*
   * Dimmed on purpose. A pure white page, or anything close to it, is
   * uncomfortable to sit in front of for long. The page is a settled
   * grey, the cards are an off white that still reads as raised against
   * it, and nothing on screen is #FFFFFF.
   */
  --bg: #E6E6EB;
  --surface: #F7F7FA;
  --surface-2: #EDEDF2;
  --surface-3: #DEDEE5;
  --surface-sunk: #DFDFE6;
  --blue-tint: rgba(0, 100, 210, 0.08);
  --elevated: #FBFBFD;
  --separator: rgba(60, 60, 67, 0.18);
  --label: rgba(0, 0, 0, 0.84);
  /* Alphas chosen so secondary text clears 4.5:1 and tertiary clears
     3:1 against the page, rather than fading into it. */
  --label-2: rgba(60, 60, 67, 0.75);
  --label-3: rgba(60, 60, 67, 0.58);
  --blue: #0064D2;
  --green: #1F7A36;
  --orange: #A34800;
  --red: #C60013;

  --tint-green: rgba(31, 122, 54, 0.13);
  --tint-orange: rgba(163, 72, 0, 0.13);
  --tint-blue: rgba(0, 100, 210, 0.12);

  --accent-soft: rgba(0, 100, 210, 0.11);
  --material: rgba(247, 247, 250, 0.80);
  --material-strong: rgba(249, 249, 252, 0.94);

  /* Two layers each: a tight contact shadow and a soft ambient one. */
  --shadow-1: 0 1px 1px rgba(0, 0, 0, 0.04), 0 1px 3px rgba(0, 0, 0, 0.05);
  --shadow-2: 0 1px 2px rgba(0, 0, 0, 0.04), 0 8px 24px rgba(0, 0, 0, 0.07);
  --shadow-3: 0 2px 6px rgba(0, 0, 0, 0.05), 0 24px 56px rgba(0, 0, 0, 0.12);
  --ring: rgba(0, 113, 227, 0.28);

  --medal-gold: #B26A00;
  --medal-silver: #6E7178;
  --medal-bronze: #9A5B32;
}

@media (min-width: 768px) {
  :root {
    --page-pad: 24px;
  }
}

@media (min-width: 1200px) {
  :root {
    --page-pad: 40px;
  }
}

/* -------------------------------------------------------------
   2. Reset and base
   ------------------------------------------------------------- */

*,
*::before,
*::after {
  box-sizing: border-box;
}

html {
  /* Text inflation is pinned in the cross browser block below. */
  scroll-behavior: smooth;

  /* The bar is sticky and 64px tall, so anything jumped to lands
     underneath it. Padding the scroll container fixes every anchor on
     the site at once: a search result, a listing link, a heading in
     the written pages, and anything added later. The extra twenty is
     so the target clears the bar rather than touching it. */
  scroll-padding-top: 84px;
}

body {
  margin: 0;
  background: var(--bg);
  color: var(--label);
  font-family: var(--font-sans);
  font-size: var(--fs-body);
  font-weight: 400;
  line-height: 1.47;
  letter-spacing: 0;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  min-height: 100vh;
}

h1,
h2,
h3,
h4 {
  margin: 0;
  font-weight: 600;
  line-height: 1.2;
  letter-spacing: -0.02em;
}

h1 {
  font-size: var(--fs-title1);
  font-weight: 700;
}

h2 {
  font-size: var(--fs-title2);
}

h3 {
  font-size: var(--fs-title3);
}

h4 {
  font-size: var(--fs-headline);
  letter-spacing: 0;
}

p {
  margin: 0 0 var(--sp-4);
}

p:last-child {
  margin-bottom: 0;
}

a {
  color: var(--blue);
  text-decoration: none;
  transition: color var(--state), opacity var(--state);
}

a:hover {
  opacity: 0.82;
}

img {
  max-width: 100%;
  display: block;
}

svg {
  display: block;
}

ul,
ol {
  margin: 0 0 var(--sp-4);
  padding-left: var(--sp-5);
}

li {
  margin-bottom: var(--sp-2);
}

hr {
  border: 0;
  border-top: 1px solid var(--separator);
  margin: var(--sp-6) 0;
}

:focus-visible {
  outline: 2px solid var(--blue);
  outline-offset: 2px;
  border-radius: var(--r-sm);
}

::selection {
  background: var(--blue);
  color: #fff;
}

[hidden] {
  display: none !important;
}

.mono {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum" 1;
}

.skip-link {
  position: absolute;
  left: -9999px;
  top: 0;
  background: var(--blue);
  color: #fff;
  padding: var(--sp-3) var(--sp-4);
  border-radius: 0 0 var(--r-md) 0;
  z-index: 200;
}

.skip-link:focus {
  left: 0;
}

.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

/* -------------------------------------------------------------
   3. Layout
   ------------------------------------------------------------- */

.main {
  max-width: var(--max-w);
  margin: 0 auto;
  padding: 0 var(--page-pad) var(--sp-9);
}

.section {
  margin-top: var(--sp-7);
}

.section-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--sp-4);
  margin-bottom: var(--sp-4);
}

.section-title {
  font-size: var(--fs-title3);
  font-weight: 600;
  letter-spacing: -0.02em;
}

.section-sub {
  font-size: var(--fs-footnote);
  color: var(--label-2);
}

.page-head {
  padding: var(--sp-7) 0 var(--sp-5);
  //border-bottom: 1px solid var(--separator);
  margin-bottom: var(--sp-6);
}

.page-head h1 {
  font-size: var(--fs-title2);
  margin-bottom: var(--sp-2);
}

.page-head p {
  color: var(--label-2);
  font-size: var(--fs-body);
  max-width: 62ch;
}

.page-head-center {
  text-align: center;
}

.page-head-center p {
  margin-left: auto;
  margin-right: auto;
}

/* -------------------------------------------------------------
   4. Top bar
   ------------------------------------------------------------- */

/* At rest the bar is part of the page: no fill, no rule, nothing
   cutting across the hero backdrop behind it. It becomes a surface
   only once there is content passing underneath. */
.topbar {
  position: sticky;
  top: 0;
  z-index: 90;
  background: transparent;
  border-bottom: 1px solid transparent;
  transition: background var(--enter), border-color var(--enter),
    box-shadow var(--enter), backdrop-filter var(--enter);
}

@supports (backdrop-filter: blur(20px)) or (-webkit-backdrop-filter: blur(20px)) {
  .topbar.is-scrolled {
    -webkit-backdrop-filter: blur(20px) saturate(180%);
    backdrop-filter: blur(20px) saturate(180%);
  }
}

/* The bar lifts off the page once it stops sitting at the top. */
.topbar.is-scrolled {
  background: var(--material);
  border-bottom-color: var(--separator);
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.28);
}

body.light .topbar.is-scrolled {
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.06);
}

.topbar-inner {
  max-width: var(--max-w);
  margin: 0 auto;
  padding: 0 var(--page-pad);
  height: 64px;
  display: flex;
  align-items: center;
  gap: var(--sp-4);
}

/* A fixed size rather than a token. The wordmark is hidden below
   680px, so it only ever renders at widths where the fluid type
   scale does not apply, and pinning it keeps the lockup identical
   in the bar and in the admin bar. */
.brand {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  color: var(--label);
  font-weight: 700;
  font-size: 20px;
  letter-spacing: -0.035em;
  flex: 0 0 auto;
}

.brand:hover {
  opacity: 1;
}

/* No fill behind it. The mark used to be a one pixel placeholder and
   the grey square was all there was to see; a logo with its own
   transparent background would just sit on a grey tile. contain
   rather than cover, so nothing is cropped if the artwork is not
   perfectly square. */
.brand-mark {
  width: 33px;
  height: 33px;
  border-radius: 10px;
  object-fit: contain;
}

.brand-dot {
  color: var(--blue);
}

.topbar-actions {
  margin-left: auto;
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  flex: 0 0 auto;
}

/* Search, shaped like the field it opens */

.topbar-search {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  height: 36px;
  min-width: 210px;
  padding: 0 6px 0 var(--sp-3);
  border: 1px solid var(--separator);
  border-radius: var(--r-sm);
  background: var(--surface);
  color: var(--label-3);
  font-family: inherit;
  font-size: var(--fs-footnote);
  cursor: pointer;
  transition: border-color var(--state), background var(--state), color var(--state);
}

.topbar-search:hover {
  border-color: var(--label-3);
  background: var(--surface-2);
  color: var(--label-2);
}

.topbar-search-icon {
  display: inline-flex;
  flex: 0 0 auto;
}

.topbar-search-label {
  flex: 1 1 auto;
  text-align: left;
}

.topbar-kbd {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 22px;
  height: 22px;
  padding: 0 5px;
  border: 1px solid var(--separator);
  border-radius: 5px;
  background: var(--bg);
  color: var(--label-3);
  font-family: var(--font-mono);
  font-size: var(--fs-caption2);
  line-height: 1;
}

body.light .topbar-kbd {
  background: var(--elevated);
}

.icon-button {
  width: 36px;
  height: 36px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: 0;
  background: transparent;
  color: var(--label-2);
  border-radius: var(--r-sm);
  cursor: pointer;
  transition: background var(--state), color var(--state);
}

.icon-button:hover {
  background: var(--surface-2);
  color: var(--label);
}

/* The live price of the top spot */

/* Categories, as the one filled control in the bar. */
.topbar-cta {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  height: 36px;
  padding: 0 var(--sp-4);
  border-radius: var(--r-sm);
  background: var(--blue);
  color: #fff;
  font-size: var(--fs-footnote);
  font-weight: 600;
  white-space: nowrap;
  transition: filter var(--state), transform var(--state);
}

.topbar-cta:hover {
  opacity: 1;
  filter: brightness(1.08);
}

.topbar-cta:active {
  transform: scale(0.98);
}

.topbar-cta .icon {
  width: 16px;
  height: 16px;
}

/* On the categories page itself the button reads as the current place
   rather than an invitation to go there. */
.topbar-cta.is-active {
  filter: brightness(0.92);
}

/* Narrower screens shed the labels before they shed the controls. */
@media (max-width: 1100px) {
  .topbar-search {
    min-width: 0;
    padding: 0 var(--sp-2);
  }

  .topbar-search-label,
  .topbar-kbd {
    display: none;
  }

  .topbar-search {
    width: 36px;
    justify-content: center;
  }
}

@media (max-width: 860px) {
  .topbar-inner {
    gap: var(--sp-2);
  }
}

/* The button label goes before the wordmark does.

   The bar needs roughly 382px to hold the logo, the name, the search
   button, the theme toggle and the Categories button, so at 360px it
   is about 22px short and something has to give. It used to be the
   name, which was backwards: a blue square with no word beside it
   does not tell anyone whose site this is, while a grid icon on a
   blue button is read as a menu without help.

   Dropping the label frees about 76px, well past the 22px needed. */
@media (max-width: 680px) {
  .topbar-cta {
    padding: 0 var(--sp-3);
  }
}

@media (max-width: 400px) {
  .topbar-inner {
    gap: 6px;
  }

  .topbar-cta {
    padding: 0 10px;
    font-size: var(--fs-caption1);
    gap: 6px;
  }

  .topbar-cta .icon {
    width: 15px;
    height: 15px;
  }
}

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

body.light .theme-icon-dark {
  display: none;
}

body.light .theme-icon-light {
  display: block;
}

/* -------------------------------------------------------------
   5. Buttons
   ------------------------------------------------------------- */

.button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--sp-2);
  height: var(--control-h);
  padding: 0 var(--sp-5);
  border: 1px solid transparent;
  border-radius: var(--r-md);
  background: var(--blue);
  color: var(--on-accent);
  font-family: inherit;
  font-size: var(--fs-body);
  font-weight: 500;
  line-height: 1;
  white-space: nowrap;
  cursor: pointer;
  transition: background var(--state), border-color var(--state),
    color var(--state), transform var(--state), opacity var(--state);
}

.button:hover {
  filter: brightness(1.08);
}

.button:active {
  transform: scale(0.98);
}

.button:disabled,
.button[aria-disabled="true"] {
  opacity: 0.4;
  cursor: not-allowed;
  filter: none;
  transform: none;
}

.button-secondary {
  background: var(--surface-2);
  color: var(--label);
  border-color: var(--separator);
}

.button-secondary:hover {
  background: var(--surface-3);
  filter: none;
}

.button-ghost {
  background: transparent;
  color: var(--label-2);
}

.button-ghost:hover {
  background: var(--surface);
  color: var(--label);
  filter: none;
}

.button-danger {
  background: var(--red);
}

.button-small {
  height: 34px;
  padding: 0 var(--sp-3);
  font-size: var(--fs-footnote);
  border-radius: var(--r-sm);
}

.button-pill {
  border-radius: var(--r-pill);
}

.button-block {
  width: 100%;
}

.button .icon {
  width: 18px;
  height: 18px;
}

/* The row claim action.
   This repeats on every row, so it stays quiet at rest and only commits
   to colour on hover. Same shape as the podium button, a size down. */

.row-claim {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  height: 36px;
  padding: 0 5px 0 var(--sp-3);
  border: 1px solid var(--separator);
  border-radius: var(--r-pill);
  background: transparent;
  color: var(--label-2);
  font-family: inherit;
  font-size: var(--fs-caption1);
  cursor: pointer;
  white-space: nowrap;
  transition: border-color var(--state), background var(--state),
    color var(--state), transform var(--state);
}

.row-claim-label {
  font-weight: 600;
  letter-spacing: -0.01em;
}

.row-claim-price {
  display: inline-flex;
  align-items: center;
  height: 26px;
  padding: 0 10px;
  border-radius: var(--r-pill);
  background: var(--surface-2);
  color: var(--label);
  font-size: var(--fs-footnote);
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.01em;
  transition: background var(--state), color var(--state);
}

/* Hovering anywhere on the row wakes the button up. */
.board-row:hover .row-claim {
  border-color: var(--label-3);
  color: var(--label);
}

/* Hovering the button itself commits. */
.row-claim:hover {
  border-color: var(--blue);
  background: rgba(10, 132, 255, 0.1);
  color: var(--label);
  transform: translateY(-1px);
}

.row-claim:hover .row-claim-price {
  background: var(--blue);
  color: #fff;
}

.row-claim:active {
  transform: scale(0.98);
}

/* -------------------------------------------------------------
   6. Inputs
   ------------------------------------------------------------- */

.input,
.select,
.textarea {
  width: 100%;
  height: var(--control-h);
  padding: 0 var(--sp-3);
  background: var(--surface);
  border: 1px solid var(--separator);
  border-radius: var(--r-sm);
  color: var(--label);
  font-family: inherit;
  font-size: var(--fs-body);
  transition: border-color var(--state), box-shadow var(--state), background var(--state);
  -webkit-appearance: none;
  appearance: none;
}

.textarea {
  height: auto;
  min-height: 76px;
  padding: var(--sp-3);
  line-height: 1.5;
  resize: vertical;
}

.input::placeholder,
.textarea::placeholder {
  color: var(--label-3);
}

.input:focus,
.select:focus,
.textarea:focus {
  outline: none;
  border-color: var(--blue);
  box-shadow: 0 0 0 3px var(--ring);
}

.input:disabled,
.select:disabled,
.textarea:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

.input.is-invalid,
.textarea.is-invalid {
  border-color: var(--red);
}

/* Select with a real chevron rather than a CSS triangle */

.select-wrap {
  position: relative;
}

.select {
  padding-right: 42px;
  cursor: pointer;
}

.select-wrap .select-arrow {
  position: absolute;
  right: var(--sp-3);
  top: 50%;
  transform: translateY(-50%);
  color: var(--label-2);
  pointer-events: none;
}

.field {
  margin-bottom: var(--sp-5);
}

.field:last-child {
  margin-bottom: 0;
}

.field-label {
  display: block;
  font-size: var(--fs-caption1);
  font-weight: 500;
  color: var(--label-2);
  letter-spacing: 0.01em;
  margin-bottom: var(--sp-2);
}

.field-hint {
  font-size: var(--fs-caption1);
  color: var(--label-2);
  line-height: 1.5;
  margin: var(--sp-3) 0 0;
}

.field-error {
  font-size: var(--fs-caption1);
  color: var(--red);
  margin: var(--sp-2) 0 0;
  min-height: 16px;
}

/* Amount input with a leading dollar sign */

.amount-field {
  position: relative;
}

.amount-field .amount-prefix {
  position: absolute;
  left: var(--sp-4);
  top: 50%;
  transform: translateY(-50%);
  font-size: var(--fs-title3);
  font-weight: 500;
  color: var(--label-3);
  pointer-events: none;
  line-height: 1;
}

.amount-input {
  height: 56px;
  padding-left: 38px;
  font-size: var(--fs-title3);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.01em;
}

/* Checkbox */
.checkbox {
  display: flex;
  align-items: flex-start;
  gap: var(--sp-3);
  cursor: pointer;
  font-size: var(--fs-footnote);
  color: var(--label-2);
  line-height: 1.45;
}

.checkbox input {
  position: absolute;
  opacity: 0;
  width: 0;
  height: 0;
}

.checkbox-box {
  flex: 0 0 auto;
  width: 20px;
  height: 20px;
  margin-top: 1px;
  border: 1px solid var(--label-3);
  border-radius: 6px;
  background: var(--surface);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  transition: background var(--state), border-color var(--state);
}

.checkbox-box::after {
  content: "";
  width: 10px;
  height: 6px;
  border-left: 2px solid #fff;
  border-bottom: 2px solid #fff;
  transform: rotate(-45deg) scale(0.6);
  opacity: 0;
  transition: opacity var(--state), transform var(--state);
  margin-top: -2px;
}

.checkbox input:checked+.checkbox-box {
  background: var(--blue);
  border-color: var(--blue);
}

.checkbox input:checked+.checkbox-box::after {
  opacity: 1;
  transform: rotate(-45deg) scale(1);
}

.checkbox input:focus-visible+.checkbox-box {
  box-shadow: 0 0 0 3px var(--ring);
}


.segmented-item {
  border: 0;
  background: transparent;
  color: var(--label-2);
  font-family: inherit;
  font-size: var(--fs-footnote);
  font-weight: 500;
  padding: var(--sp-2) var(--sp-4);
  height: 34px;
  border-radius: 9px;
  cursor: pointer;
  transition: background var(--state), color var(--state);
}

.segmented-item[aria-current="page"] {
  background: var(--surface-3);
  color: var(--label);
  box-shadow: var(--shadow-1);
}

.tabs {
  display: flex;
  gap: var(--sp-1);
  padding: 4px;
  background: var(--surface);
  border: 1px solid var(--separator);
  border-radius: var(--r-md);
  margin-bottom: var(--sp-4);
}

.tab {
  flex: 1;
  border: 0;
  background: transparent;
  color: var(--label-2);
  font-family: inherit;
  font-size: var(--fs-footnote);
  font-weight: 500;
  height: 38px;
  border-radius: 9px;
  cursor: pointer;
  transition: background var(--state), color var(--state);
}

.tab:hover {
  color: var(--label);
}

.tab[aria-selected="true"] {
  background: var(--surface-3);
  color: var(--label);
  box-shadow: var(--shadow-1);
}

.tab-panel[hidden] {
  display: none;
}

/* -------------------------------------------------------------
   8. Badges
   ------------------------------------------------------------- */

.badge {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-1);
  height: 22px;
  padding: 0 var(--sp-2);
  border-radius: 6px;
  background: var(--surface-2);
  color: var(--label-2);
  font-size: var(--fs-caption2);
  font-weight: 500;
  letter-spacing: 0;
  white-space: nowrap;
}

.badge-blue {
  background: rgba(10, 132, 255, 0.16);
  color: var(--blue);
}

.badge-green {
  background: rgba(48, 209, 88, 0.16);
  color: var(--green);
}

.badge-orange {
  background: rgba(255, 159, 10, 0.16);
  color: var(--orange);
}

.badge-red {
  background: rgba(255, 69, 58, 0.16);
  color: var(--red);
}

/* -------------------------------------------------------------
   9. Cards
   ------------------------------------------------------------- */

.card {
  background: var(--material);
  border: 1px solid var(--separator);
  border-radius: var(--r-lg);
  padding: var(--sp-5);
}

@supports (backdrop-filter: blur(20px)) or (-webkit-backdrop-filter: blur(20px)) {
  .card {
    -webkit-backdrop-filter: blur(20px) saturate(180%);
    backdrop-filter: blur(20px) saturate(180%);
  }
}

@supports not ((backdrop-filter: blur(20px)) or (-webkit-backdrop-filter: blur(20px))) {
  .card {
    background: var(--surface);
  }
}

/* -------------------------------------------------------------
   10. Hero
   ------------------------------------------------------------- */

.hero {
  padding: var(--sp-9) 0 var(--sp-7);
  text-align: center;
}


.hero h1 {
  font-size: clamp(28px, 6vw, var(--fs-title1));
  font-weight: 700;
  letter-spacing: -0.02em;
  margin-bottom: var(--sp-3);
}

.hero-price {
  font-family: var(--font-mono);
  font-weight: 600;
  color: var(--green);
  font-variant-numeric: tabular-nums;
}

.hero-sub {
  font-size: var(--fs-headline);
  color: var(--label-2);
  max-width: 56ch;
  margin: 0 auto var(--sp-6);
  letter-spacing: 0;
  font-weight: 400;
}

/* The claim field: one rounded shell holding an icon, the input and the
   submit button, so it reads as a single control. */

.claim-form {
  max-width: 620px;
  margin: 0 auto;
}

.claim-field {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  padding: 7px 7px 7px var(--sp-3);
  background: var(--surface);
  border: 1px solid var(--surface-3);
  border-radius: 18px;
  box-shadow: var(--shadow-1);
  transition: border-color var(--state), box-shadow var(--state);
}

.claim-field:focus-within {
  border-color: var(--label-3);
  box-shadow: 0 0 0 4px var(--ring);
}

.claim-form.is-targeted .claim-field {
  border-color: var(--blue);
  box-shadow: 0 0 0 4px var(--ring);
}

/* No plate behind the marks, they carry themselves. */
.claim-field-icon {
  flex: 0 0 auto;
  width: 26px;
  height: 26px;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--label-2);
}

/* The accepted listing types, cycling in place.
   Seven items, each visible for its share of one 10.5s loop. */

.type-roll {
  position: relative;
  width: 24px;
  height: 24px;
  overflow: hidden;
}

.type-roll-item {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  opacity: 0;
  animation: type-roll 10.5s linear infinite;
}

.type-roll-item img {
  width: 24px;
  height: 24px;
  object-fit: contain;
}

@keyframes type-roll {
  0% {
    opacity: 0;
    transform: translateY(70%) scale(0.8);
  }

  3% {
    opacity: 1;
    transform: translateY(0) scale(1);
  }

  11.43% {
    opacity: 1;
    transform: translateY(0) scale(1);
  }

  14.29% {
    opacity: 0;
    transform: translateY(-70%) scale(0.8);
  }

  100% {
    opacity: 0;
    transform: translateY(-70%) scale(0.8);
  }
}

/* No motion: show the first mark and hold it. */
@media (prefers-reduced-motion: reduce) {
  .type-roll-item {
    animation: none;
    opacity: 0;
    transform: none;
  }

  .type-roll-item:first-child {
    opacity: 1;
  }
}

.claim-input {
  flex: 1 1 auto;
  min-width: 0;
  height: 40px;
  padding: 0 var(--sp-1);
  border: 0;
  background: transparent;
  color: var(--label);
  font-family: inherit;
  font-size: var(--fs-headline);
  font-weight: 400;
}

.claim-input::placeholder {
  color: var(--label-3);
}

.claim-input:focus {
  outline: none;
}

.claim-button {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  height: 40px;
  padding: 0 var(--sp-4);
  border: 0;
  border-radius: 12px;
  background: var(--label);
  color: var(--bg);
  font-family: inherit;
  font-size: var(--fs-body);
  font-weight: 500;
  white-space: nowrap;
  cursor: pointer;
  transition: opacity var(--state), transform var(--state);
}

.claim-button:hover {
  opacity: 0.88;
}

.claim-button:active {
  transform: scale(0.98);
}

.claim-button .icon {
  width: 16px;
  height: 16px;
}

.hero-error {
  font-size: var(--fs-footnote);
  color: var(--red);
  min-height: 18px;
  margin: var(--sp-2) 0 0;
}

.hero-note {
  font-size: var(--fs-footnote);
  color: var(--blue);
  margin: 0 0 var(--sp-6);
}

/* The three terms.
   No container, no border: these are facts, not a control, and the only
   pill in the hero should be the board toggle underneath. A bordered box
   here read as a second control and made the toggle look misaligned. */

.hero-facts {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--sp-3) var(--sp-5);
  margin: 0 0 var(--sp-7);
  padding: 0;
  list-style: none;
}

.hero-fact {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 9px;
  margin: 0;
  font-size: var(--fs-footnote);
  color: var(--label-2);
  white-space: nowrap;
}

/* The tinted chips give each fact its own edge, so no dot or rule is
   needed between them and nothing competes with the board toggle. */
.hero-fact-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 28px;
  height: 28px;
  border-radius: 50%;
  flex: 0 0 auto;
}

.hero-fact-icon .icon {
  width: 15px;
  height: 15px;
}

.hero-fact:nth-child(1) .hero-fact-icon {
  color: var(--green);
  background: var(--tint-green);
}

.hero-fact:nth-child(2) .hero-fact-icon {
  color: var(--orange);
  background: var(--tint-orange);
}

.hero-fact:nth-child(3) .hero-fact-icon {
  color: var(--blue);
  background: var(--tint-blue);
}

.hero-fact-text strong {

  color: var(--label);
  font-weight: 600;
  letter-spacing: -0.01em;
}

/* The board toggle is centred explicitly rather than relying on the
   hero's inherited text-align. */
.hero-toggle {
  display: flex;
  justify-content: center;
  width: 100%;
}

/* -------------------------------------------------------------
   10b. Board switch
   ------------------------------------------------------------- */

.board-switch {
  display: inline-flex;
  gap: var(--sp-1);
  padding: var(--sp-1);
  background: var(--surface);
  border: 1px solid var(--separator);
  border-radius: var(--r-lg);
}

.board-switch-item {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  padding: var(--sp-2) var(--sp-4);
  border-radius: var(--r-md);
  color: var(--label-2);
  text-align: left;
  transition: background var(--state), color var(--state);
}

.board-switch-item:hover {
  background: var(--surface-2);
  color: var(--label);
  opacity: 1;
}

.board-switch-item[aria-current="page"] {
  background: var(--surface-3);
  color: var(--label);
  box-shadow: var(--shadow-1);
}

/* The icon matches the combined height of the title and its subtitle. */
.board-switch-icon {
  flex: 0 0 auto;
  width: 38px;
  height: 38px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: 11px;
  background: var(--surface-2);
  color: var(--label-2);
  transition: background var(--state), color var(--state);
}

.board-switch-icon .icon {
  width: 22px;
  height: 22px;
}

.board-switch-item[aria-current="page"] .board-switch-icon {
  background: var(--accent-soft);
  color: var(--blue);
}

.board-switch-text {
  display: flex;
  flex-direction: column;
  line-height: 1.25;
}

.board-switch-title {
  font-size: var(--fs-footnote);
  font-weight: 600;
}

.board-switch-sub {
  font-size: var(--fs-caption2);
  color: var(--label-3);
}

/* -------------------------------------------------------------
   11. Listing icon and type badge
   ------------------------------------------------------------- */

.listing-icon {
  position: relative;
  flex: 0 0 auto;
  width: var(--icon-size, 75px);
  height: var(--icon-size, 75px);
  border-radius: 18px;
  background: var(--surface-2);
  border: 1px solid var(--separator);
  overflow: visible;
}

.listing-icon-img {
  width: 100%;
  height: 100%;
  border-radius: inherit;
  object-fit: cover;
  background: var(--surface-2);
}

.listing-icon-placeholder {
  width: 100%;
  height: 100%;
  border-radius: inherit;
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: var(--font-mono);
  font-size: var(--fs-headline);
  font-weight: 600;
  color: var(--label-2);
  background: var(--surface-2);
}

/* The store mark sits directly on the corner of the icon. No plate,
   no ring: a drop shadow is enough to lift it off whatever is behind. */
.type-badge {
  position: absolute;
  right: -6px;
  bottom: -6px;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--label-2);
  filter: drop-shadow(0 1px 3px rgba(0, 0, 0, 0.55));
}

body.light .type-badge {
  filter: drop-shadow(0 1px 3px rgba(0, 0, 0, 0.22));
}

.type-badge svg,
.type-badge img {
  width: 20px;
  height: 20px;
}

/* Brand marks are referenced as images, so they keep their own colour. */
.type-icon-img {
  object-fit: contain;
}

.type-badge-fallback {
  font-family: var(--font-mono);
  font-size: 9px;
  font-weight: 600;
  color: var(--label-2);
  line-height: 1;
}

/* -------------------------------------------------------------
   12. Podium
   ------------------------------------------------------------- */

.podium {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--sp-4);
  margin-bottom: var(--sp-6);
}

@media (min-width: 900px) {
  .podium {
    grid-template-columns: 1fr 1.18fr 1fr;
    align-items: stretch;
  }

  /* Each medal owns its column rather than being sorted into the next
     free one. With order alone, a category holding a single listing put
     its winner in the left column; now first place is always in the
     middle, second on the left and third on the right, and a rank
     nobody holds yet simply leaves its column empty. */
  .podium-gold {
    grid-column: 2;
    grid-row: 1;
  }

  .podium-silver {
    grid-column: 1;
    grid-row: 1;
  }

  .podium-bronze {
    grid-column: 3;
    grid-row: 1;
  }

  /* The winner stands proud of the other two. */
  .podium-silver,
  .podium-bronze {
    margin-top: var(--sp-6);
  }
}

.podium-card {
  position: relative;
  display: flex;
  flex-direction: column;
  padding: 0;
  background: var(--surface);
  border: 1px solid var(--separator);
  border-radius: 20px;
  overflow: hidden;
  transition: transform var(--enter), border-color var(--state), box-shadow var(--state);
}

/* The medal wash across the top of each card. */
.podium-card::before {
  content: "";
  position: absolute;
  inset: 0 0 auto 0;
  height: 96px;
  background: linear-gradient(180deg, var(--medal-glow) 0%, transparent 100%);
  pointer-events: none;
}

.podium-gold {
  --medal: var(--medal-gold);
  --medal-glow: rgba(255, 179, 64, 0.16);
  border-color: rgba(255, 179, 64, 0.34);
}

.podium-silver {
  --medal: var(--medal-silver);
  --medal-glow: rgba(184, 188, 196, 0.12);
}

.podium-bronze {
  --medal: var(--medal-bronze);
  --medal-glow: rgba(201, 139, 94, 0.12);
}

.podium-card:hover {
  transform: translateY(-3px);
  border-color: var(--medal);
  box-shadow: var(--shadow-2);
}

.podium-top {
  box-shadow: var(--shadow-2);
}

/* The rank, set large behind the content as a gradient watermark.
   letter-spacing is left alone here: a negative value eats into the
   glyph's own advance width, which is what clipped the 2 against the
   right edge. The padding gives the sidebearing somewhere to live. */

.podium-numeral {
  position: absolute;
  right: 18px;
  top: 20px;
  z-index: 0;
  padding-right: 0.06em;
  font-size: 168px;
  font-weight: 800;
  line-height: 0.86;
  letter-spacing: 0;
  pointer-events: none;
  user-select: none;
  background-image: linear-gradient(170deg, var(--medal) 0%, var(--medal) 28%, transparent 84%);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  -webkit-text-fill-color: transparent;
  opacity: 0.5;
}

.podium-top .podium-numeral {
  font-size: 204px;
  top: 16px;
  opacity: 0.62;
}

/* Browsers without background-clip on text still get a readable numeral. */
@supports not ((-webkit-background-clip: text) or (background-clip: text)) {
  .podium-numeral {
    color: var(--medal);
    -webkit-text-fill-color: currentColor;
    opacity: 0.18;
  }
}

@media (max-width: 900px) {
  .podium-numeral {
    font-size: 138px;
    top: 18px;
  }

  .podium-top .podium-numeral {
    font-size: 152px;
    top: 16px;
  }
}

/* The amount, top left */

.podium-amount {
  font-family: var(--font-mono);
  font-size: var(--fs-title3);
  font-weight: 700;
  color: var(--label);
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.02em;
}

.podium-top .podium-amount {
  font-size: var(--fs-title2);
  color: var(--medal);
}

.podium-amount-label {
  font-family: var(--font-sans);
  font-size: var(--fs-caption2);
  font-weight: 400;
  color: var(--label-3);
  letter-spacing: 0;
  margin-top: 3px;
}

/* Body, centred */

.podium-body {
  position: relative;
  z-index: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: var(--sp-3);
  padding: var(--sp-5) var(--sp-5) var(--sp-4);
  flex: 1 1 auto;
}

.podium-body .listing-icon {
  border-radius: 16px;
  box-shadow: var(--shadow-2);
}

.podium-title {
  display: block;
  font-size: var(--fs-headline);
  font-weight: 600;
  color: var(--label);
  letter-spacing: 0;
  line-height: 1.35;
}

/* No underline on hover. The trailing mark carries the affordance. */
a.podium-title:hover {
  opacity: 1;
  text-decoration: none;
  color: var(--label);
}

.podium-title-icon {
  display: inline-flex;
  align-items: center;
  margin-left: 7px;
  vertical-align: -2px;
  color: var(--label-3);
  transition: color var(--state), transform var(--state);
}

.podium-title-icon .icon {
  width: 14px;
  height: 14px;
}

a.podium-title:hover .podium-title-icon {
  color: var(--medal);
  transform: translate(1px, -1px);
}

.podium-top .podium-title {
  font-size: var(--fs-title3);
  letter-spacing: -0.02em;
}

.podium-desc {
  font-size: var(--fs-footnote);
  color: var(--label-2);
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  margin: 0;
}

/* Straight after the description, not pushed to the bottom of the card.
   Pinned low, it left a hole under every short description; the spare
   height of a card now sits above the footer instead, where it reads as
   the card's own breathing room rather than a gap in the listing. */
.podium-tags {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--sp-2);
  margin-top: 0;
  padding-top: var(--sp-1);
}

.chip {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  height: 24px;
  padding: 0 var(--sp-2);
  border-radius: 7px;
  background: var(--surface-2);
  border: 1px solid transparent;
  color: var(--label-2);
  font-size: var(--fs-caption2);
  font-weight: 500;
  white-space: nowrap;
}

a.chip:hover {
  color: var(--label);
  border-color: var(--separator);
  opacity: 1;
}

.chip img {
  width: 12px;
  height: 12px;
  object-fit: contain;
}

/* Action, centred, sitting on the card itself with no bar behind it */

.podium-button {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  height: 40px;
  padding: 0 var(--sp-2) 0 var(--sp-4);
  border: 1px solid var(--separator);
  border-radius: var(--r-pill);
  background: var(--surface-2);
  color: var(--label);
  font-family: inherit;
  font-size: var(--fs-footnote);
  font-weight: 500;
  cursor: pointer;
  transition: background var(--state), border-color var(--state),
    color var(--state), transform var(--state);
}

.podium-button:hover {
  background: var(--surface-3);
  border-color: var(--medal);
  transform: translateY(-1px);
}

.podium-button:active {
  transform: scale(0.98);
}

/* The price rides in its own pill on the right of the button. */
.podium-button-price {
  display: inline-flex;
  align-items: center;
  height: 28px;
  padding: 0 var(--sp-3);
  border-radius: var(--r-pill);
  background: var(--bg);
  color: var(--label);
  font-size: var(--fs-footnote);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}

body.light .podium-button-price {
  background: var(--elevated);
}

/* Number one gets the solid medal treatment. */
.podium-top .podium-button {
  background: var(--medal);
  border-color: var(--medal);
  color: #1a1206;
  font-weight: 600;
}

.podium-top .podium-button:hover {
  filter: brightness(1.06);
  background: var(--medal);
}

.podium-top .podium-button-price {
  background: rgba(0, 0, 0, 0.16);
  color: #1a1206;
}

/* --medal is darkened in light mode so gold text can be read against a
   pale page. This button wants the reverse: it fills with the colour
   and puts dark ink on top, and the darkened gold left that at 4.4:1.
   A brighter fill of its own takes it to 7.8:1 and looks more like
   gold than the darkened tone did. */
body.light .podium-top .podium-button {
  background: #F0A020;
  border-color: #E09410;
  color: #2A1B00;
}

body.light .podium-top .podium-button:hover {
  background: #E89A10;
}

body.light .podium-top .podium-button-price {
  background: rgba(0, 0, 0, 0.18);
  color: #2A1B00;
}

/* -------------------------------------------------------------
   13. Board list
   ------------------------------------------------------------- */

.board-list {
  border: 1px solid var(--separator);
  border-radius: var(--r-lg);
  overflow: hidden;
  background: var(--surface);
}

body.light .board-list {
  background: var(--surface);
}

.board-row {
  display: flex;
  align-items: center;
  gap: var(--sp-4);
  min-height: 104px;
  padding: var(--sp-4);
  border-bottom: 1px solid var(--separator);
  transition: background var(--state);
}

.board-row:last-child {
  border-bottom: 0;
}

.board-row:hover {
  background: var(--surface-2);
}

.board-row:target {
  background: rgba(10, 132, 255, 0.12);
}

.row-rank {
  flex: 0 0 auto;
  min-width: 48px;
  font-family: var(--font-mono);
  font-size: var(--fs-headline);
  font-weight: 700;
  color: var(--label);
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.01em;
  text-align: right;
}

.row-rank-hash {
  color: var(--label-3);
  font-weight: 500;
  margin-right: 4px;
}

.row-main {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.row-title {
  font-size: var(--fs-body);
  font-weight: 500;
  color: var(--label);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

a.row-title {
  color: var(--label);
}

a.row-title:hover {
  opacity: 1;
  text-decoration: underline;
}

.row-desc {
  font-size: var(--fs-footnote);
  color: var(--label-2);
  line-height: 1.4;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.row-meta {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 6px;
  margin-top: 4px;
  min-width: 0;
  max-width: 100%;
}

/* The category as a boxed tag, so it reads apart from the description
   sitting directly above it. The listing type is not repeated here: the
   store mark already sits on the corner of the icon. */
.row-tag {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  height: 24px;
  padding: 0 10px;
  border-radius: 7px;
  font-size: var(--fs-caption2);
  font-weight: 500;
  line-height: 1;
  white-space: nowrap;
}

/* No display or line-height of its own. `.row-tag` is a flex box that
   centres its content, and overriding either put the text back at the
   mercy of a line height that had to be kept in step with the badge
   height by hand. It was not: the badge is shorter on a phone, so a
   22px line in an 18px content box pushed the text off centre. */
.row-tag-category {
  max-width: 100%;
  overflow: hidden;
  background: transparent;
  border: 1px solid var(--separator);
  color: var(--label-2);
}

/* The label truncates in its own box, because text-overflow needs a
   block box and does not apply to a flex container. */
.row-tag-category>span {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

a.row-tag-category:hover {
  color: var(--blue);
  border-color: var(--blue);
  opacity: 1;
}

/* Price above, action below, pinned to the right of the row. */
.row-side {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  gap: var(--sp-4);
}

.row-amount {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
}

.row-amount-value {
  font-size: var(--fs-title3);
  font-weight: 700;
  color: var(--label);
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.02em;
}

/* The claim action is the point of a row, so it is never hidden. On
   narrow screens it moves to a second line rather than disappearing. */
.row-action {
  flex: 0 0 auto;
  display: block;
}

.row-desc-mobile {
  display: block;
}

/* -------------------------------------------------------------
   14. Empty states
   ------------------------------------------------------------- */

.empty-state {
  text-align: center;
  padding: var(--sp-9) var(--sp-5);
  border: 1px dashed var(--separator);
  border-radius: var(--r-lg);
  color: var(--label-2);
}

.empty-state .icon {
  width: 32px;
  height: 32px;
  margin: 0 auto var(--sp-4);
  color: var(--label-3);
}

.empty-state h3 {
  font-size: var(--fs-title3);
  color: var(--label);
  margin-bottom: var(--sp-2);
}

.empty-state p {
  font-size: var(--fs-body);
  max-width: 48ch;
  margin: 0 auto var(--sp-5);
}

/* -------------------------------------------------------------
   15. Pagination
   ------------------------------------------------------------- */

.pagination {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--sp-1);
  margin-top: var(--sp-6);
  flex-wrap: wrap;
}

.page-link {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--sp-1);
  min-width: 38px;
  height: 38px;
  padding: 0 var(--sp-3);
  border: 1px solid var(--separator);
  border-radius: var(--r-sm);
  background: var(--surface);
  color: var(--label-2);
  font-family: var(--font-mono);
  font-size: var(--fs-footnote);
  font-variant-numeric: tabular-nums;
  transition: background var(--state), color var(--state), border-color var(--state);
}

.page-link:hover {
  background: var(--surface-2);
  color: var(--label);
  opacity: 1;
}

.page-link.is-current {
  background: var(--blue);
  border-color: var(--blue);
  color: #fff;
  font-weight: 600;
}

.page-link.is-disabled {
  opacity: 0.35;
  pointer-events: none;
}

.page-link .icon {
  width: 16px;
  height: 16px;
}

.page-ellipsis {
  color: var(--label-3);
  padding: 0 var(--sp-1);
  font-family: var(--font-mono);
}

/* -------------------------------------------------------------
   16. Modal
   ------------------------------------------------------------- */

/* Anchored to the visual viewport rather than the window. When a phone
   keyboard opens it covers the bottom of the window without resizing
   the layout, so a panel centred in the window lands behind it. --vvh
   and --vvt are published by the script and describe the part still
   on screen; the fallbacks are the plain window for anything without
   the API and for the first paint. */
.modal-root {
  position: fixed;
  top: var(--vvt, 0px);
  left: 0;
  width: 100%;
  height: var(--vvh, 100%);
  z-index: 120;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--sp-4);
}

/* The backdrop covers the whole window, not just the band the panel
   sits in, or the page shows through beside the keyboard. Fixed, so it
   is placed against the viewport rather than the offset root above. */
.modal-backdrop {
  position: fixed;
  inset: 0;
  height: 100vh;
  height: 100dvh;
}

/* With the keyboard up there is little height left, so the panel stops
   trying to centre itself and sits at the top of what is visible. */
body.kb-open .modal-root {
  align-items: flex-start;
}

.modal-backdrop {
  background: rgba(0, 0, 0, 0.6);
  opacity: 0;
  transition: opacity var(--enter);
}

.modal-root.is-open .modal-backdrop {
  opacity: 1;
}

.modal-panel {
  position: relative;
  width: 100%;
  max-width: 540px;
  max-height: calc(var(--vvh, 100vh) - var(--sp-7));
  overflow-y: auto;
  overscroll-behavior: contain;
  background: var(--material-strong);
  border: 1px solid var(--separator);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-3);
  padding: var(--sp-5);
  opacity: 0;
  transform: scale(0.96);
  transition: opacity var(--enter), transform var(--enter);
}

@supports (backdrop-filter: blur(20px)) or (-webkit-backdrop-filter: blur(20px)) {
  .modal-panel {
    -webkit-backdrop-filter: blur(20px) saturate(180%);
    backdrop-filter: blur(20px) saturate(180%);
  }
}

@supports not ((backdrop-filter: blur(20px)) or (-webkit-backdrop-filter: blur(20px))) {
  .modal-panel {
    background: var(--surface);
  }
}

.modal-root.is-open .modal-panel {
  opacity: 1;
  transform: scale(1);
}

.modal-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--sp-3);
  margin-bottom: var(--sp-4);
}

.modal-title {
  font-size: var(--fs-title3);
  font-weight: 600;
  letter-spacing: -0.02em;
}

.modal-sub {
  font-size: var(--fs-footnote);
  color: var(--label-2);
  margin-top: var(--sp-1);
}

.modal-foot {
  display: flex;
  gap: var(--sp-2);
  margin-top: var(--sp-5);
}

.modal-foot .button {
  flex: 1;
}

/* Search sits high rather than centred, which is where a search panel
   is expected. The offset is a share of the visible band, not of the
   window: with a keyboard up, 8vh of the full window would push the
   panel down behind it. */
.modal-panel-search {
  max-width: 660px;
  align-self: flex-start;
  margin-top: calc(var(--vvh, 100vh) * 0.08);
  padding: var(--sp-5) var(--sp-5) var(--sp-4);
}

/* With the keyboard open every pixel counts, so the offset goes. */
body.kb-open .modal-panel-search {
  margin-top: var(--sp-2);
}

/* The listing exactly as it will appear on the board */

.listing-preview {
  display: flex;
  align-items: flex-start;
  gap: var(--sp-4);
  padding: var(--sp-4);
  background: var(--surface);
  border: 1px solid var(--separator);
  border-radius: var(--r-md);
}

.listing-preview .listing-icon {
  width: 75px;
  height: 75px;
  border-radius: 18px;
}

.listing-preview-body {
  flex: 1 1 auto;
  min-width: 0;
}

.listing-preview-title {
  font-size: var(--fs-headline);
  font-weight: 600;
  color: var(--label);
  line-height: 1.3;
  margin-bottom: 3px;
  overflow: hidden;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
}

.listing-preview-desc {
  font-size: var(--fs-footnote);
  color: var(--label-2);
  line-height: 1.45;
  margin-bottom: var(--sp-2);
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.listing-preview-desc.is-empty {
  color: var(--label-3);
  font-style: italic;
}

.listing-preview-url {
  font-size: var(--fs-caption2);
  color: var(--label-3);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* With no heading above it, the preview card carries the top of the
   modal on its own. */
.modal-head-bare {
  margin-bottom: var(--sp-2);
  justify-content: flex-end;
}

#preview-content>.listing-preview {
  margin-bottom: var(--sp-5);
}

.preview-note {
  display: flex;
  align-items: flex-start;
  gap: var(--sp-2);
  padding: var(--sp-3) var(--sp-4);
  border-radius: var(--r-sm);
  background: rgba(255, 159, 10, 0.12);
  color: var(--orange);
  font-size: var(--fs-footnote);
  line-height: 1.45;
  margin-bottom: var(--sp-5);
}

.preview-note .icon {
  width: 16px;
  height: 16px;
  flex: 0 0 auto;
  margin-top: 2px;
}

/* Rank and price readout */

.rank-readout {
  display: flex;
  align-items: center;
  gap: var(--sp-4);
  padding: var(--sp-4) var(--sp-5);
  background: var(--surface);
  border: 1px solid var(--separator);
  border-radius: var(--r-md);
}

.rank-readout-cell {
  flex: 1 1 0;
  min-width: 0;
}

.rank-readout-cell-end {
  text-align: right;
}

.rank-readout-divider {
  width: 1px;
  align-self: stretch;
  background: var(--separator);
  flex: 0 0 auto;
}

.rank-readout-label {
  font-size: var(--fs-caption1);
  color: var(--label-2);
  margin-bottom: 3px;
}

.rank-readout-value {
  font-family: var(--font-mono);
  font-size: var(--fs-title2);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.02em;
  line-height: 1.1;
}

.rank-readout-value.is-top {
  color: var(--orange);
}

.modal-fineprint {
  font-size: var(--fs-caption1);
  color: var(--label-2);
  line-height: 1.55;
  margin: var(--sp-5) 0 0;
  padding-top: var(--sp-5);
  border-top: 1px solid var(--separator);
}

.modal-agree {
  margin-top: var(--sp-4);
}

/* -------------------------------------------------------------
   17. Skeleton loader
   ------------------------------------------------------------- */

.skeleton {
  background: linear-gradient(90deg, var(--surface-2) 25%, var(--surface-3) 37%, var(--surface-2) 63%);
  background-size: 400% 100%;
  animation: skeleton-shimmer 1.4s ease-in-out infinite;
  border-radius: var(--r-sm);
}

@keyframes skeleton-shimmer {
  0% {
    background-position: 100% 50%;
  }

  100% {
    background-position: 0 50%;
  }
}

.skeleton-line {
  height: 12px;
  margin-bottom: var(--sp-2);
}

.skeleton-line.w-40 {
  width: 40%;
}

.skeleton-line.w-60 {
  width: 60%;
}

.skeleton-line.w-90 {
  width: 90%;
}

.skeleton-box {
  width: 44px;
  height: 44px;
  border-radius: var(--r-md);
}

/* -------------------------------------------------------------
   18. Toast
   ------------------------------------------------------------- */

.toast-stack {
  position: fixed;
  left: 50%;
  bottom: var(--sp-6);
  transform: translateX(-50%);
  z-index: 200;
  display: flex;
  flex-direction: column;
  gap: var(--sp-2);
  width: calc(100% - var(--sp-7));
  max-width: 420px;
  pointer-events: none;
}

.toast {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  padding: var(--sp-3) var(--sp-4);
  background: var(--material-strong);
  border: 1px solid var(--separator);
  border-radius: var(--r-md);
  box-shadow: var(--shadow-2);
  color: var(--label);
  font-size: var(--fs-footnote);
  pointer-events: auto;
  opacity: 0;
  transform: translateY(8px);
  transition: opacity var(--enter), transform var(--enter);
}

@supports (backdrop-filter: blur(20px)) or (-webkit-backdrop-filter: blur(20px)) {
  .toast {
    -webkit-backdrop-filter: blur(20px) saturate(180%);
    backdrop-filter: blur(20px) saturate(180%);
  }
}

.toast.is-visible {
  opacity: 1;
  transform: translateY(0);
}

.toast .icon {
  width: 18px;
  height: 18px;
  flex: 0 0 auto;
}

.toast-error .icon {
  color: var(--red);
}

.toast-success .icon {
  color: var(--green);
}

/* -------------------------------------------------------------
   19. Search results
   ------------------------------------------------------------- */

.search-field {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  padding: 0 var(--sp-1) var(--sp-4);
  border-bottom: 1px solid var(--separator);
}

.search-field-icon {
  color: var(--label-3);
  flex: 0 0 auto;
  display: inline-flex;
}

.search-input {
  flex: 1 1 auto;
  min-width: 0;
  height: 28px;
  border: 0;
  background: transparent;
  color: var(--label);
  font-family: inherit;
  font-size: var(--fs-title3);
  font-weight: 500;
  letter-spacing: -0.01em;
  padding: 0;
}

.search-input::placeholder {
  color: var(--label-3);
  font-weight: 400;
}

.search-input:focus {
  outline: none;
  box-shadow: none;
}

.search-input::-webkit-search-cancel-button {
  display: none;
}

.search-esc {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  height: 22px;
  padding: 0 7px;
  border: 1px solid var(--separator);
  border-radius: 5px;
  color: var(--label-3);
  font-family: var(--font-mono);
  font-size: var(--fs-caption2);
  line-height: 1;
}

.search-close {
  flex: 0 0 auto;
  width: 30px;
  height: 30px;
}

.search-status {
  font-size: var(--fs-caption2);
  color: var(--label-3);
  letter-spacing: 0.02em;
  text-transform: uppercase;
  margin: var(--sp-3) var(--sp-1) var(--sp-1);
}

.search-status[hidden] {
  display: none;
}

.search-results {
  max-height: 56vh;
  overflow-y: auto;
  overscroll-behavior: contain;
  margin: 0 calc(var(--sp-2) * -1);
  padding: 0 var(--sp-2) var(--sp-1);
}

.search-hint {
  font-size: var(--fs-footnote);
  color: var(--label-3);
  padding: var(--sp-7) var(--sp-4);
  text-align: center;
  margin: 0;
}

.search-empty {
  padding: var(--sp-7) var(--sp-4);
  text-align: center;
}

.search-empty-title {
  font-size: var(--fs-body);
  font-weight: 500;
  color: var(--label);
  margin: 0 0 var(--sp-1);
}

.search-empty-hint {
  font-size: var(--fs-footnote);
  color: var(--label-3);
  margin: 0;
}

/* One row: icon, a text column that truncates, and a fixed figures
   column. The grid is what keeps the price from ever colliding with a
   long title. */
.search-result {
  display: grid;
  grid-template-columns: 44px minmax(0, 1fr) auto;
  align-items: center;
  gap: var(--sp-3);
  padding: 10px var(--sp-3);
  border-radius: var(--r-md);
  color: var(--label);
  transition: background var(--state);
}

.search-result:hover,
.search-result:focus-visible {
  background: var(--surface-2);
  opacity: 1;
}

.search-result .listing-icon {
  width: 44px;
  height: 44px;
  border-radius: 12px;
}

.search-result .type-badge {
  right: -4px;
  bottom: -4px;
}

.search-result .type-badge svg,
.search-result .type-badge img {
  width: 15px;
  height: 15px;
}

.search-result-main {
  min-width: 0;
}

.search-result-title {
  font-size: var(--fs-body);
  font-weight: 500;
  line-height: 1.35;
  color: var(--label);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.search-result-meta {
  display: flex;
  align-items: center;
  gap: 7px;
  margin-top: 2px;
  min-width: 0;
  font-size: var(--fs-caption1);
  color: var(--label-2);
}

.search-result-cat {
  flex: 0 0 auto;
  max-width: 45%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.search-result-dot {
  flex: 0 0 auto;
  width: 3px;
  height: 3px;
  border-radius: 50%;
  background: var(--label-3);
}

.search-result-desc {
  flex: 1 1 auto;
  min-width: 0;
  color: var(--label-3);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.search-result-figures {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  justify-content: center;
  gap: 1px;
  min-width: 62px;
}

.search-result-amount {
  font-size: var(--fs-body);
  font-weight: 700;
  color: var(--label);
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.01em;
}

.search-result-rank {
  font-size: var(--fs-caption2);
  color: var(--label-3);
  font-variant-numeric: tabular-nums;
}

@media (max-width: 560px) {
  .search-result {
    grid-template-columns: 40px minmax(0, 1fr) auto;
    gap: var(--sp-2);
  }

  .search-result .listing-icon {
    width: 40px;
    height: 40px;
  }

  .search-result-desc {
    display: none;
  }

  .search-result-dot {
    display: none;
  }

  .search-result-cat {
    max-width: 100%;
  }

  .search-esc {
    display: none;
  }

  .search-input {
    font-size: var(--fs-headline);
  }
}

/* -------------------------------------------------------------
   21. Content pages
   ------------------------------------------------------------- */

/* Content pages run the full content width. Long paragraphs stay
   readable because the body text is capped, not the page. */

.prose {
  width: 100%;
  font-size: var(--fs-body);
  color: var(--label-2);
  padding-bottom: var(--sp-7);
}

.prose>p,
.prose>ul,
.prose>ol {
  max-width: 78ch;
}

.prose h2 {
  font-size: var(--fs-title2);
  color: var(--label);
  margin: var(--sp-9) 0 var(--sp-4);
  padding-bottom: var(--sp-3);
  border-bottom: 1px solid var(--separator);
}

.prose h2:first-child {
  margin-top: 0;
}

.prose h3 {
  font-size: var(--fs-headline);
  color: var(--label);
  letter-spacing: 0;
  margin: var(--sp-6) 0 var(--sp-2);
}

.prose p {
  margin-bottom: var(--sp-4);
  line-height: 1.62;
}

.prose ul {
  margin-bottom: var(--sp-4);
  padding-left: var(--sp-5);
}

.prose li {
  margin-bottom: var(--sp-2);
  line-height: 1.6;
}

.prose strong {
  color: var(--label);
  font-weight: 600;
}

.prose-updated {
  font-size: var(--fs-caption1);
  color: var(--label-3);
  margin-bottom: var(--sp-6);
}

/* Two column grid for the shorter rule blocks on wide screens */
.prose-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--sp-4);
  margin-bottom: var(--sp-5);
}

@media (min-width: 860px) {
  .prose-grid {
    grid-template-columns: 1fr 1fr;
    gap: var(--sp-5);
  }
}

.prose-card {
  padding: var(--sp-5);
  background: var(--surface);
  border: 1px solid var(--separator);
  border-radius: var(--r-lg);
}

.prose-card h3 {
  margin-top: 0;
}

.prose-card p:last-child {
  margin-bottom: 0;
}

.prose-card>p {
  max-width: none;
}

.callout {
  padding: var(--sp-4) var(--sp-5);
  background: var(--surface);
  border: 1px solid var(--separator);
  border-left: 3px solid var(--blue);
  border-radius: var(--r-md);
  margin: var(--sp-5) 0;
  color: var(--label);
  font-size: var(--fs-footnote);
  line-height: 1.6;
  max-width: 78ch;
}

/* Price examples table on the rules page */
.rules-table {
  width: 100%;
  border-collapse: collapse;
  margin: var(--sp-4) 0 var(--sp-5);
  font-size: var(--fs-footnote);
}

.rules-table th,
.rules-table td {
  text-align: left;
  padding: var(--sp-3) var(--sp-4);
  border-bottom: 1px solid var(--separator);
}

.rules-table thead th {
  font-size: var(--fs-caption1);
  font-weight: 500;
  color: var(--label-2);
  background: var(--surface);
  white-space: nowrap;
}

.rules-table tbody tr:last-child td {
  border-bottom: 0;
}

.rules-table td:last-child {
  color: var(--label);
  font-weight: 500;
  white-space: nowrap;
}

.rules-table-wrap {
  border: 1px solid var(--separator);
  border-radius: var(--r-md);
  overflow-x: auto;
}

/* -------------------------------------------------------------
   22. Success page
   ------------------------------------------------------------- */

.success-wrap {
  max-width: 520px;
  margin: var(--sp-9) auto;
  text-align: center;
}

.success-mark {
  width: 64px;
  height: 64px;
  margin: 0 auto var(--sp-5);
  border-radius: 50%;
  background: rgba(48, 209, 88, 0.16);
  color: var(--green);
  display: flex;
  align-items: center;
  justify-content: center;
}

.success-mark .icon {
  width: 32px;
  height: 32px;
}

.success-pending .success-mark {
  background: rgba(255, 159, 10, 0.16);
  color: var(--orange);
}

.success-detail {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: var(--sp-3) 0;
  border-bottom: 1px solid var(--separator);
  font-size: var(--fs-footnote);
}

.success-detail:last-child {
  border-bottom: 0;
}

.success-detail dt {
  color: var(--label-2);
}

.success-detail dd {
  margin: 0;
  font-family: var(--font-mono);
  font-weight: 500;
  font-variant-numeric: tabular-nums;
}

/* -------------------------------------------------------------
   23. Admin
   ------------------------------------------------------------- */

.admin-body {
  background: var(--bg);
}

.admin-shell {
  max-width: 1480px;
  margin: 0 auto;
  padding: 0 var(--page-pad) var(--sp-9);
}

.admin-bar {
  position: sticky;
  top: 0;
  z-index: 80;
  background: var(--material);
  border-bottom: 1px solid var(--separator);
  margin-bottom: var(--sp-6);
}

@supports (backdrop-filter: blur(20px)) {
  .admin-bar {
    -webkit-backdrop-filter: blur(20px) saturate(180%);
    backdrop-filter: blur(20px) saturate(180%);
  }
}

.admin-bar-inner {
  max-width: 1480px;
  margin: 0 auto;
  padding: 0 var(--page-pad);
  height: 56px;
  display: flex;
  align-items: center;
  gap: var(--sp-4);
}

.admin-nav {
  display: flex;
  gap: var(--sp-1);
  overflow-x: auto;
}

.admin-nav a {
  color: var(--label-2);
  font-size: var(--fs-footnote);
  font-weight: 500;
  padding: var(--sp-2) var(--sp-3);
  border-radius: var(--r-sm);
  white-space: nowrap;
}

.admin-nav a:hover {
  background: var(--surface);
  color: var(--label);
  opacity: 1;
}

.admin-nav a.is-active {
  background: var(--surface-2);
  color: var(--label);
}

/* =============================================================
   Dashboard
   ============================================================= */

/* ---- Toolbar ---- */

.dash-toolbar {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--sp-2);
  padding: var(--sp-5) 0 var(--sp-4);
}

.dash-chip,
.dash-range,
.dash-icon-button {
  display: inline-flex;
  align-items: center;
  height: 34px;
  border: 1px solid var(--separator);
  border-radius: var(--r-sm);
  background: var(--surface);
}

.dash-chip {
  gap: 7px;
  padding: 0 var(--sp-3);
  color: var(--label);
  font-size: var(--fs-footnote);
  font-weight: 600;
}

.dash-chip .icon {
  color: var(--label-3);
}

.dash-range {
  padding: 0;
  overflow: hidden;
}

.dash-range-current {
  padding: 0 var(--sp-3);
  font-size: var(--fs-footnote);
  font-weight: 500;
  color: var(--label);
  white-space: nowrap;
}

.dash-range-step {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  color: var(--label-2);
  transition: background var(--state), color var(--state);
}

.dash-range-step:hover {
  background: var(--surface-2);
  color: var(--label);
  opacity: 1;
}

.dash-range-step.is-disabled {
  opacity: 0.3;
  pointer-events: none;
}

.dash-icon-button {
  justify-content: center;
  width: 34px;
  color: var(--label-2);
  transition: background var(--state), color var(--state);
}

.dash-icon-button:hover {
  background: var(--surface-2);
  color: var(--label);
  opacity: 1;
}

.dash-toolbar-right {
  margin-left: auto;
  display: flex;
  align-items: center;
  gap: var(--sp-2);
}

.pill {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  height: 34px;
  padding: 0 var(--sp-3);
  border-radius: var(--r-sm);
  border: 1px solid var(--separator);
  font-size: var(--fs-caption1);
  font-weight: 500;
  text-transform: capitalize;
}

.pill-dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: currentColor;
}

.pill-green {
  color: var(--green);
  background: rgba(48, 209, 88, 0.10);
  border-color: rgba(48, 209, 88, 0.30);
}

.pill-orange {
  color: var(--orange);
  background: rgba(255, 159, 10, 0.10);
  border-color: rgba(255, 159, 10, 0.30);
}

/* ---- Headline strip ----
   One card, cells divided by hairlines, rather than a row of boxes. */

.metric-strip {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  background: var(--surface);
  border: 1px solid var(--separator);
  border-radius: var(--r-lg);
  overflow: hidden;
  margin-bottom: var(--sp-3);
}

.metric {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: var(--sp-4) var(--sp-5);
  border-left: 1px solid var(--separator);
}

.metric:first-child {
  border-left: 0;
}

.metric-label {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: var(--fs-caption1);
  color: var(--label-2);
  font-weight: 500;
  white-space: nowrap;
}

.metric-value {
  font-size: 26px;
  font-weight: 700;
  color: var(--label);
  letter-spacing: -0.03em;
  line-height: 1.05;
  font-variant-numeric: tabular-nums;
}

.metric-delta {
  display: inline-flex;
  align-items: center;
  gap: 3px;
  font-size: var(--fs-caption2);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}

.metric-delta .icon {
  width: 12px;
  height: 12px;
}

.metric-delta.is-up {
  color: var(--green);
}

.metric-delta.is-down {
  color: var(--red);
}

.metric-delta.is-flat,
.metric-delta.is-quiet {
  color: var(--label-3);
  font-weight: 400;
}

.metric-delta.is-new {
  color: var(--blue);
}

.metric-live-dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--green);
  box-shadow: 0 0 0 3px rgba(48, 209, 88, 0.2);
}

/* ---- Chart ---- */

.dash-chart-card {
  padding: var(--sp-5) var(--sp-5) var(--sp-4);
  background: var(--surface);
  border: 1px solid var(--separator);
  border-radius: var(--r-lg);
  margin-bottom: var(--sp-3);
}

.dash-chart {
  display: flex;
  align-items: stretch;
  gap: var(--sp-3);
  height: 260px;
}

.dash-axis {
  flex: 0 0 auto;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  font-family: var(--font-mono);
  font-size: var(--fs-caption2);
  color: var(--label-3);
  font-variant-numeric: tabular-nums;
}

.dash-axis-left {
  width: 44px;
  align-items: flex-end;
}

.dash-axis-right {
  width: 52px;
  align-items: flex-start;
}

.dash-axis span {
  transform: translateY(-50%);
}

.dash-axis span:first-child {
  transform: translateY(0);
}

.dash-axis span:last-child {
  transform: translateY(-100%);
}

.dash-plot {
  position: relative;
  flex: 1 1 auto;
  min-width: 0;
}

/* Bars, one slot a day */
.dash-bars {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: flex-end;
}

.dash-bar-slot {
  flex: 1 1 0;
  height: 100%;
  display: flex;
  align-items: flex-end;
  justify-content: center;
  padding: 0 12%;
}

.dash-bar {
  display: block;
  width: 100%;
  min-height: 2px;
  border-radius: 3px 3px 0 0;
  background: var(--surface-3);
  transition: background var(--state);
}

.dash-bar.is-partial {
  background: repeating-linear-gradient(135deg,
      var(--surface-3) 0 4px,
      transparent 4px 8px);
  border: 1px solid var(--surface-3);
  border-bottom: 0;
}

.dash-svg {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  overflow: visible;
  pointer-events: none;
}

.dash-grid {
  stroke: var(--separator);
  stroke-width: 1;
  stroke-dasharray: 2 6;
  opacity: 0.6;
}

.dash-line {
  fill: none;
  stroke: var(--label);
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.dash-hotspots {
  position: absolute;
  inset: 0;
  display: flex;
}

.dash-hotspot {
  flex: 1 1 0;
  border: 0;
  padding: 0;
  background: transparent;
  cursor: crosshair;
}

.dash-hotspot:hover~.dash-hotspot,
.dash-hotspot:hover {
  background: rgba(255, 255, 255, 0.02);
}

.dash-hotspot:hover~.dash-hotspot {
  background: transparent;
}

.dash-guide {
  position: absolute;
  top: 0;
  bottom: 0;
  width: 1px;
  background: var(--label-3);
  pointer-events: none;
  transform: translateX(-0.5px);
}

.dash-dot {
  position: absolute;
  width: 10px;
  height: 10px;
  margin: -5px 0 0 -5px;
  border-radius: 50%;
  background: var(--label);
  box-shadow: 0 0 0 3px var(--surface);
  pointer-events: none;
}

.dash-tip {
  position: absolute;
  top: 0;
  transform: translate(-50%, -110%);
  display: flex;
  flex-direction: column;
  gap: 3px;
  padding: var(--sp-2) var(--sp-3);
  background: var(--material-strong);
  border: 1px solid var(--separator);
  border-radius: var(--r-sm);
  box-shadow: var(--shadow-2);
  pointer-events: none;
  white-space: nowrap;
  z-index: 3;
}

@supports (backdrop-filter: blur(20px)) {
  .dash-tip {
    -webkit-backdrop-filter: blur(20px) saturate(180%);
    backdrop-filter: blur(20px) saturate(180%);
  }
}

.dash-tip.is-start {
  transform: translate(0, -110%);
}

.dash-tip.is-end {
  transform: translate(-100%, -110%);
}

.dash-tip-label {
  font-size: var(--fs-caption2);
  color: var(--label-3);
}

.dash-tip-row {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  justify-content: space-between;
  font-size: var(--fs-caption1);
  color: var(--label-2);
}

.dash-tip-row b {
  font-family: var(--font-mono);
  font-weight: 700;
  color: var(--label);
  font-variant-numeric: tabular-nums;
}

.dash-tip-key {
  display: inline-flex;
  align-items: center;
  gap: 6px;
}

.dash-xaxis {
  position: relative;
  height: 18px;
  margin: var(--sp-3) 52px 0 calc(44px + var(--sp-3));
}

.dash-xaxis span {
  position: absolute;
  top: 0;
  transform: translateX(-50%);
  font-size: var(--fs-caption2);
  color: var(--label-3);
  white-space: nowrap;
}

.dash-legend {
  display: flex;
  gap: var(--sp-5);
  margin-top: var(--sp-3);
  padding-top: var(--sp-3);
  border-top: 1px solid var(--separator);
}

.dash-legend-item {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  font-size: var(--fs-caption1);
  color: var(--label-2);
}

.dash-legend-key {
  width: 12px;
  height: 3px;
  border-radius: 2px;
  flex: 0 0 auto;
}

.key-bar {
  background: var(--surface-3);
  height: 10px;
  width: 8px;
  border-radius: 2px;
}

.key-line {
  background: var(--label);
}

/* ---- Panels ---- */

.dash-panels {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--sp-3);
  margin-bottom: var(--sp-4);
}

@media (min-width: 1000px) {
  .dash-panels {
    grid-template-columns: 1fr 1fr;
  }
}

.panel {
  min-width: 0;
  padding: var(--sp-4);
  background: var(--surface);
  border: 1px solid var(--separator);
  border-radius: var(--r-lg);
}

.panel-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-3);
  margin-bottom: var(--sp-3);
}

.panel-title {
  font-size: var(--fs-headline);
  font-weight: 600;
  letter-spacing: -0.01em;
}

.panel-sub {
  font-size: var(--fs-caption1);
  color: var(--label-2);
  margin: 3px 0 0;
}

.panel-metric {
  font-size: var(--fs-caption1);
  color: var(--label-3);
  white-space: nowrap;
}

.panel-link {
  font-size: var(--fs-caption1);
  color: var(--label-2);
  white-space: nowrap;
}

.panel-link:hover {
  color: var(--blue);
  opacity: 1;
}

.tabset {
  display: inline-flex;
  gap: 2px;
  padding: 3px;
  background: var(--bg);
  border: 1px solid var(--separator);
  border-radius: var(--r-sm);
}

body.light .tabset {
  background: var(--surface-2);
}

.tabset-item {
  border: 0;
  background: transparent;
  color: var(--label-2);
  font-family: inherit;
  font-size: var(--fs-caption1);
  font-weight: 500;
  height: 26px;
  padding: 0 10px;
  border-radius: 6px;
  cursor: pointer;
  white-space: nowrap;
  transition: background var(--state), color var(--state);
}

.tabset-item:hover {
  color: var(--label);
}

.tabset-item[aria-selected="true"] {
  background: var(--surface-3);
  color: var(--label);
}

/* ---- Proportional bar rows ---- */

.barrow-list {
  list-style: none;
  margin: 0;
  padding: 0;
}

.barrow {
  position: relative;
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  margin: 0 0 3px;
  padding: 8px 10px;
  border-radius: 6px;
  overflow: hidden;
  font-size: var(--fs-footnote);
  min-height: 34px;
}

/* The bar is the row's own background, filled to its share. */
.barrow-fill {
  position: absolute;
  left: 0;
  top: 0;
  bottom: 0;
  background: rgba(10, 132, 255, 0.28);
  border-radius: 6px;
  transition: background var(--state);
}

.barrow:hover .barrow-fill {
  background: rgba(10, 132, 255, 0.42);
}

body.light .barrow-fill {
  background: rgba(10, 132, 255, 0.20);
}

body.light .barrow:hover .barrow-fill {
  background: rgba(10, 132, 255, 0.32);
}

.barrow-label {
  position: relative;
  z-index: 1;
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  color: var(--label);
}

.barrow-icon {
  display: inline-flex;
  align-items: center;
  flex: 0 0 auto;
}

.barrow-icon img {
  width: 14px;
  height: 14px;
  object-fit: contain;
}

.barrow-text {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.barrow-when {
  flex: 0 0 auto;
  font-size: var(--fs-caption2);
  color: var(--label-3);
}

.barrow-value {
  position: relative;
  z-index: 1;
  flex: 0 0 auto;
  font-weight: 700;
  color: var(--label);
  font-variant-numeric: tabular-nums;
}

.barrow-empty {
  margin: 0;
  padding: var(--sp-6) 0;
  text-align: center;
  font-size: var(--fs-footnote);
  color: var(--label-3);
}

/* Status rows carry their own colour. */
.barrow-list-status .seg-green .barrow-fill {
  background: rgba(48, 209, 88, 0.30);
}

.barrow-list-status .seg-orange .barrow-fill {
  background: rgba(255, 159, 10, 0.30);
}

.barrow-list-status .seg-red .barrow-fill {
  background: rgba(255, 69, 58, 0.30);
}

.barrow-list-status .seg-grey .barrow-fill {
  background: var(--surface-3);
}

.barrow-total {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-3);
  margin: var(--sp-3) 0 0;
  padding-top: var(--sp-3);
  border-top: 1px solid var(--separator);
  font-size: var(--fs-caption1);
  color: var(--label-2);
}

.barrow-total .mono {
  color: var(--label);
  font-weight: 600;
}

.dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  flex: 0 0 auto;
  display: inline-block;
}

.dot-green {
  background: var(--green);
}

.dot-orange {
  background: var(--orange);
}

.dot-red {
  background: var(--red);
}

.dash-foot {
  font-size: var(--fs-caption1);
  color: var(--label-3);
  margin: 0 0 var(--sp-6);
}

@media (max-width: 700px) {
  .dash-axis-left {
    width: 34px;
  }

  .dash-axis-right {
    width: 40px;
  }

  .dash-xaxis {
    margin-left: calc(34px + var(--sp-3));
    margin-right: 40px;
  }

  .metric-value {
    font-size: 22px;
  }

  .barrow-when {
    display: none;
  }
}

.stat-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  gap: var(--sp-3);
  margin-bottom: var(--sp-6);
}

.stat-card {
  padding: var(--sp-4);
  background: var(--surface);
  border: 1px solid var(--separator);
  border-radius: var(--r-md);
}

.stat-label {
  font-size: var(--fs-caption1);
  color: var(--label-2);
  margin-bottom: var(--sp-1);
}

.stat-value {
  font-family: var(--font-mono);
  font-size: var(--fs-title2);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.02em;
}

.stat-value.small {
  font-size: var(--fs-title3);
}

.table-wrap {
  border: 1px solid var(--separator);
  border-radius: var(--r-md);
  overflow-x: auto;
  background: var(--surface);
}

.table {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--fs-footnote);
  min-width: 760px;
}

.table th,
.table td {
  padding: var(--sp-3);
  text-align: left;
  border-bottom: 1px solid var(--separator);
  vertical-align: middle;
}

.table th {
  font-size: var(--fs-caption1);
  font-weight: 500;
  color: var(--label-2);
  white-space: nowrap;
  background: var(--surface-2);
  position: sticky;
  top: 0;
}

.table th a {
  color: inherit;
  display: inline-flex;
  align-items: center;
  gap: 4px;
}

.table th a:hover {
  color: var(--label);
}

.table tbody tr:hover {
  background: var(--surface-2);
}

.table tbody tr:last-child td {
  border-bottom: 0;
}

.table .num {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  text-align: right;
  white-space: nowrap;
}

.table .truncate {
  max-width: 260px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.toolbar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-2);
  margin-bottom: var(--sp-4);
}

.toolbar .input,
.toolbar .select {
  width: auto;
  min-width: 160px;
  height: 38px;
}

.login-wrap {
  max-width: 380px;
  margin: 12vh auto;
  padding: var(--sp-6);
  background: var(--surface);
  border: 1px solid var(--separator);
  border-radius: var(--r-lg);
}

.alert {
  padding: var(--sp-3) var(--sp-4);
  border-radius: var(--r-sm);
  font-size: var(--fs-footnote);
  margin-bottom: var(--sp-4);
}

.alert-error {
  background: rgba(255, 69, 58, 0.14);
  color: var(--red);
}

.alert-success {
  background: rgba(48, 209, 88, 0.14);
  color: var(--green);
}

.alert-info {
  background: rgba(10, 132, 255, 0.14);
  color: var(--blue);
}

/* Revenue chart, pure CSS bars */
.chart {
  display: flex;
  align-items: flex-end;
  gap: 3px;
  height: 160px;
  padding: var(--sp-4);
  background: var(--surface);
  border: 1px solid var(--separator);
  border-radius: var(--r-md);
  margin-bottom: var(--sp-6);
}

.chart-bar {
  flex: 1 1 0;
  min-width: 4px;
  background: var(--blue);
  border-radius: 3px 3px 0 0;
  min-height: 2px;
  opacity: 0.85;
  transition: opacity var(--state);
}

.chart-bar:hover {
  opacity: 1;
}

/* -------------------------------------------------------------
   24. Motion preference
   ------------------------------------------------------------- */

@media (prefers-reduced-motion: reduce) {
  html {
    scroll-behavior: auto;
  }

  *,
  *::before,
  *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
    transition-delay: 0 !important;
  }

  .modal-panel,
  .podium-card,
  .cat-card,
  .toast,
  .button {
    transform: none !important;
  }

  .modal-root.is-open .modal-panel,
  .modal-root.is-open .modal-backdrop,
  .toast.is-visible {
    opacity: 1;
  }

  .skeleton {
    animation: none;
    background: var(--surface-2);
  }
}



/* The bar under the cursor lifts to match the tooltip. */
.dash-bar.is-active {
  background: var(--blue);
}

.dash-bar.is-partial.is-active {
  background: repeating-linear-gradient(135deg, var(--blue) 0 4px, transparent 4px 8px);
  border-color: var(--blue);
}

/* =============================================================
   Admin: filters, tables and settings
   ============================================================= */

/* ---- Filter bar ---- */

.filter-form {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--sp-2);
}

.filter-search {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  height: 34px;
  padding: 0 var(--sp-3);
  background: var(--surface);
  border: 1px solid var(--separator);
  border-radius: var(--r-sm);
  color: var(--label-3);
  min-width: 230px;
  transition: border-color var(--state), box-shadow var(--state);
}

.filter-search:focus-within {
  border-color: var(--blue);
  box-shadow: 0 0 0 3px var(--ring);
}

.filter-search input {
  flex: 1 1 auto;
  min-width: 0;
  border: 0;
  background: transparent;
  color: var(--label);
  font-family: inherit;
  font-size: var(--fs-footnote);
  padding: 0;
}

.filter-search input:focus {
  outline: none;
}

.filter-search input::placeholder {
  color: var(--label-3);
}

.filter-search input::-webkit-search-cancel-button {
  display: none;
}

.filter-date {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  font-size: var(--fs-caption1);
  color: var(--label-2);
}

.select-sm,
.input-sm {
  height: 34px;
  font-size: var(--fs-footnote);
}

.select-wrap-sm .select-arrow {
  right: var(--sp-2);
}

.select-sm {
  padding-right: 34px;
}

.input-sm {
  width: auto;
  min-width: 132px;
}

.quick-filters {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--sp-2);
  margin-bottom: var(--sp-3);
}

.quick-filters-label {
  font-size: var(--fs-caption2);
  color: var(--label-3);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  margin-right: var(--sp-1);
}

.quick-chip {
  display: inline-flex;
  align-items: center;
  height: 28px;
  padding: 0 var(--sp-3);
  border: 1px solid var(--separator);
  border-radius: var(--r-pill);
  background: var(--surface);
  color: var(--label-2);
  font-size: var(--fs-caption1);
  transition: background var(--state), color var(--state), border-color var(--state);
}

.quick-chip:hover {
  background: var(--surface-2);
  color: var(--label);
  opacity: 1;
}

.quick-chip.is-on {
  background: var(--surface-3);
  border-color: var(--label-3);
  color: var(--label);
  font-weight: 500;
}

/* ---- Tables ---- */

.panel-flush {
  padding: 0;
  overflow: hidden;
}

.panel-flush .table-wrap {
  border: 0;
  border-radius: 0;
  background: transparent;
}

.table {
  min-width: 900px;
}

.table th {
  padding: var(--sp-3) var(--sp-4);
  background: var(--bg);
  border-bottom: 1px solid var(--separator);
  font-size: var(--fs-caption2);
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--label-3);
  z-index: 2;
}

body.light .table th {
  background: var(--surface-2);
}

.table td {
  padding: var(--sp-3) var(--sp-4);
  border-bottom: 1px solid var(--separator);
  vertical-align: middle;
}

.table tbody tr {
  transition: background var(--state);
}

.table tbody tr:hover {
  background: var(--surface-2);
}

.th-sort {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  color: inherit;
}

.th-sort .icon {
  opacity: 0.4;
}

.th-sort:hover {
  color: var(--label);
  opacity: 1;
}

.th-sort:hover .icon {
  opacity: 0.8;
}

.th-sort.is-active {
  color: var(--label);
}

.th-sort.is-active .icon {
  opacity: 1;
  color: var(--blue);
}

.col-rank {
  width: 68px;
}

.col-id {
  width: 60px;
}

.col-actions {
  width: 52px;
  text-align: right;
}

.table .muted {
  color: var(--label-3);
}

.table .tiny {
  font-size: var(--fs-caption2);
}

.table .nowrap {
  white-space: nowrap;
}

.cell-listing {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  min-width: 0;
}

.cell-listing .listing-icon {
  width: 34px;
  height: 34px;
  border-radius: 10px;
}

.cell-listing .type-badge {
  right: -4px;
  bottom: -4px;
}

.cell-listing .type-badge svg,
.cell-listing .type-badge img {
  width: 13px;
  height: 13px;
}

.cell-listing-text {
  display: flex;
  flex-direction: column;
  min-width: 0;
}

.cell-title {
  font-weight: 500;
  color: var(--label);
  max-width: 320px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.cell-url {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  font-size: var(--fs-caption2);
  color: var(--label-3);
  max-width: 320px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  margin-top: 1px;
}

.cell-url:hover {
  color: var(--blue);
  opacity: 1;
}

.cell-chip {
  display: inline-flex;
  align-items: center;
  height: 24px;
  padding: 0 10px;
  border: 1px solid var(--separator);
  border-radius: 7px;
  font-size: var(--fs-caption2);
  color: var(--label-2);
  white-space: nowrap;
}

.cell-amount {
  font-family: var(--font-mono);
  font-weight: 700;
  color: var(--label);
  font-variant-numeric: tabular-nums;
}

.state {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: var(--fs-caption1);
  white-space: nowrap;
  text-transform: capitalize;
}

.state-live {
  color: var(--green);
}

.state-hidden {
  color: var(--red);
}

.state-expired {
  color: var(--orange);
}

.state-unpaid {
  color: var(--label-3);
}

.state-unpaid .dot {
  background: var(--label-3);
}

.table-empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--sp-3);
  padding: var(--sp-9) var(--sp-4);
  color: var(--label-3);
  text-align: center;
}

.table-empty-title {
  margin: 0;
  font-size: var(--fs-body);
  color: var(--label);
  font-weight: 500;
}

/* ---- Modal extras ---- */

.field-row {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--sp-4);
  margin-bottom: var(--sp-5);
}

@media (min-width: 520px) {
  .field-row {
    grid-template-columns: 1fr 1fr;
  }
}

.field-row .field {
  margin-bottom: 0;
}

.toggle-field {
  margin-top: 10px;
}

.modal-danger {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-4);
  flex-wrap: wrap;
  margin-top: var(--sp-5);
  padding-top: var(--sp-5);
  border-top: 1px solid var(--separator);
}

.modal-danger strong {
  font-size: var(--fs-footnote);
  color: var(--label);
}

.modal-danger p {
  margin: 3px 0 0;
  font-size: var(--fs-caption1);
  color: var(--label-3);
  max-width: 44ch;
}

/* ---- Settings ---- */

.settings-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--sp-3);
  margin-bottom: var(--sp-4);
}

@media (min-width: 1000px) {
  .settings-grid {
    grid-template-columns: 1fr 1fr;
  }

  .settings-wide {
    grid-column: 1 / -1;
  }
}

.settings-panel {
  padding: var(--sp-5);
}

.settings-head {
  display: flex;
  align-items: flex-start;
  gap: var(--sp-3);
  margin-bottom: var(--sp-5);
}

.settings-icon {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 34px;
  height: 34px;
  border-radius: 10px;
  background: var(--surface-2);
  color: var(--label-2);
}

.kv {
  margin: 0 0 var(--sp-4);
}

.kv-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-4);
  padding: 9px 0;
  border-bottom: 1px solid var(--separator);
}

.kv-row:last-child {
  border-bottom: 0;
}

.kv dt {
  font-size: var(--fs-footnote);
  color: var(--label-2);
  flex: 0 0 auto;
}

.kv dd {
  margin: 0;
  color: var(--label);
  text-align: right;
  min-width: 0;
}

.kv-wrap {
  word-break: break-all;
}

.textarea-mono {
  font-family: var(--font-mono);
  font-size: var(--fs-footnote);
  line-height: 1.6;
}

.settings-stats {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
  gap: var(--sp-3);
  margin-bottom: var(--sp-4);
}

.settings-stat {
  padding: var(--sp-3) var(--sp-4);
  background: var(--bg);
  border: 1px solid var(--separator);
  border-radius: var(--r-md);
}

body.light .settings-stat {
  background: var(--surface-2);
}

.settings-stat.is-warn {
  border-color: rgba(255, 159, 10, 0.4);
}

.settings-stat.is-warn .settings-stat-value {
  color: var(--orange);
}

.settings-stat-value {
  display: block;
  font-size: var(--fs-title3);
  font-weight: 700;
  color: var(--label);
  letter-spacing: -0.02em;
  font-variant-numeric: tabular-nums;
}

.settings-stat-label {
  display: block;
  font-size: var(--fs-caption2);
  color: var(--label-3);
  margin-top: 2px;
}

.metric-slash {
  color: var(--label-3);
  margin: 0 3px;
  font-weight: 400;
}

/* =============================================================
   Cross browser normalisation
   -------------------------------------------------------------
   Chrome and Firefox on a phone disagree by default about text
   inflation, form control metrics and focus artefacts. Every
   difference is pinned here so the two render the same page.
   ============================================================= */

html {
  /* Chrome on Android inflates text on pages it judges non responsive.
     Firefox and Safari have their own versions. All three are named so
     no engine grows the type on its own. */
  -webkit-text-size-adjust: 100%;
  -moz-text-size-adjust: 100%;
  text-size-adjust: 100%;
}

/* Firefox draws an inner dotted border inside a focused button and adds
   its own padding to form controls. Neither exists in Chrome. */
button::-moz-focus-inner,
input::-moz-focus-inner {
  border: 0;
  padding: 0;
}

/* Firefox applies a minimum width to form elements. */
input,
select,
textarea,
button {
  min-width: 0;
}

/* Chrome and Firefox pick different defaults for a control's font. */
input,
select,
textarea,
button {
  font: inherit;
  letter-spacing: inherit;
}

/* Firefox pads select differently and shows its own arrow. */
select {
  -moz-appearance: none;
  -webkit-appearance: none;
  appearance: none;
  text-indent: 0.01px;
}

/* Firefox renders a number spinner and a search clear button that
   Chrome styles differently. Both are removed everywhere. */
input[type="number"] {
  -moz-appearance: textfield;
  appearance: textfield;
}

input[type="number"]::-webkit-outer-spin-button,
input[type="number"]::-webkit-inner-spin-button {
  -webkit-appearance: none;
  margin: 0;
}

/* Firefox and Chrome disagree on the baseline of an inline SVG unless
   its display is set explicitly. */
svg {
  display: block;
  flex: none;
}

/* A line clamped block needs the webkit box in both engines. Firefox
   supports the prefixed properties, so one declaration serves both. */
.podium-desc,
.podium-title,
.listing-preview-title,
.listing-preview-desc,
.search-result-title {
  -webkit-box-orient: vertical;
}

/* =============================================================
   Responsive
   -------------------------------------------------------------
   The hero field, the terms row, the board toggle and the listing
   row are all built to fit a 360px screen, so none of them is ever
   stacked or hidden by a breakpoint. That is what makes the two
   browsers agree: there is no width at which one shows a different
   layout from the other.
   ============================================================= */

html,
body {
  max-width: 100%;
}

.main,
.topbar-inner,
.footer-inner,
.admin-shell,
.admin-bar-inner {
  min-width: 0;
}

.cell-url,
.preview-url,
.listing-preview-url,
.kv-wrap {
  overflow-wrap: anywhere;
}

.board-row:focus {
  outline: none;
}

/* -------------------------------------------------------------
   The hero field: one line, always
   ------------------------------------------------------------- */

@media (max-width: 620px) {
  .claim-field {
    gap: 6px;
    padding: 6px 6px 6px 10px;
    border-radius: 15px;
  }

  .claim-field-icon {
    width: 22px;
    height: 22px;
  }

  .type-roll {
    width: 20px;
    height: 20px;
  }

  .type-roll-item img {
    width: 20px;
    height: 20px;
  }

  .claim-input {
    font-size: clamp(13px, 4.071vw, 16px);
    height: 38px;
  }

  .claim-button {
    height: 38px;
    padding: 0 var(--sp-3);
    font-size: var(--fs-footnote);
    border-radius: 11px;
  }
}

@media (max-width: 400px) {
  .claim-field {
    padding-left: 8px;
    gap: 5px;
  }

  .claim-button {
    padding: 0 10px;
    font-size: var(--fs-caption1);
  }
}

/* -------------------------------------------------------------
   The three terms: one line, always
   ------------------------------------------------------------- */

@media (max-width: 720px) {
  .hero-facts {
    flex-wrap: nowrap;
    gap: var(--sp-3);
    margin-bottom: var(--sp-6);
  }

  .hero-fact {
    gap: 6px;
    font-size: var(--fs-caption1);
    min-width: 0;
  }

  .hero-fact-icon {
    width: 22px;
    height: 22px;
  }

  .hero-fact-icon .icon {
    width: 13px;
    height: 13px;
  }
}

@media (max-width: 420px) {
  .hero-facts {
    gap: 9px;
  }

  .hero-fact {
    gap: 5px;
    font-size: var(--fs-caption2);
  }

  .hero-fact-icon {
    width: 19px;
    height: 19px;
  }

  .hero-fact-icon .icon {
    width: 11px;
    height: 11px;
  }
}

@media (max-width: 340px) {
  .hero-facts {
    gap: 7px;
  }

  .hero-fact-icon {
    display: none;
  }
}

/* -------------------------------------------------------------
   The board toggle: both lines of text, always
   ------------------------------------------------------------- */

@media (max-width: 620px) {
  .board-switch {
    width: 100%;
    max-width: 380px;
  }

  .board-switch-item {
    flex: 1 1 0;
    min-width: 0;
    gap: var(--sp-2);
    padding: var(--sp-2) var(--sp-3);
  }

  .board-switch-icon {
    width: 32px;
    height: 32px;
    border-radius: 9px;
  }

  .board-switch-icon .icon {
    width: 18px;
    height: 18px;
  }

  .board-switch-text {
    min-width: 0;
  }

  .board-switch-title {
    font-size: var(--fs-caption1);
  }

  .board-switch-sub {
    font-size: clamp(8.5px, 2.545vw, 10px);
    line-height: 1.3;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
}

@media (max-width: 380px) {
  .board-switch-item {
    padding: var(--sp-2);
    gap: 7px;
  }

  .board-switch-icon {
    width: 28px;
    height: 28px;
  }

  .board-switch-icon .icon {
    width: 16px;
    height: 16px;
  }
}

/* -------------------------------------------------------------
   Podium
   ------------------------------------------------------------- */

/* 899.98px, not 900px: the three column layout starts at min-width 900px,
   and at exactly 900 both queries matched, which put the single column
   ordering onto a three column grid. */
@media (max-width: 899.98px) {

  .podium-silver,
  .podium-bronze {
    margin-top: 0;
  }

  /* One column: back to natural placement, first place on top. */
  .podium-gold,
  .podium-silver,
  .podium-bronze {
    grid-column: auto;
    grid-row: auto;
  }

  .podium-gold {
    order: 1;
  }

  .podium-silver {
    order: 2;
  }

  .podium-bronze {
    order: 3;
  }
}

@media (max-width: 640px) {
  .podium {
    gap: var(--sp-3);
  }

  /* The numeral is sized from the viewport so it lands in the same
     place whatever the browser reports for a text size. */
  .podium-numeral {
    font-size: clamp(96px, 30vw, 132px);
    top: 14px;
    right: 14px;
  }

  .podium-top .podium-numeral {
    font-size: clamp(104px, 33vw, 146px);
    top: 12px;
  }

  .podium-amount {
    font-size: var(--fs-title3);
  }

  .podium-top .podium-amount {
    font-size: clamp(20px, 6.616vw, 26px);
  }

  .podium-body {
    padding: var(--sp-4);
    gap: 10px;
  }

  .podium-body .listing-icon {
    --icon-size: 64px;
  }

  .podium-top .podium-body .listing-icon {
    --icon-size: 72px;
  }

  .podium-title {
    font-size: var(--fs-body);
  }

  .podium-top .podium-title {
    font-size: var(--fs-headline);
  }

  .podium-desc {
    font-size: var(--fs-caption1);
  }

  .podium-button {
    height: 36px;
    font-size: var(--fs-caption1);
  }

  .podium-button-price {
    height: 24px;
    font-size: var(--fs-caption1);
  }
}

/* -------------------------------------------------------------
   Listing rows
   -------------------------------------------------------------
   Rank and icon on the left, the text column in the middle with its
   description intact, and a right column carrying the price at the
   top and the claim action at the bottom.
   ------------------------------------------------------------- */

@media (max-width: 760px) {

  /* Four columns on one row. The side column stretches to the row's
     height and pushes the price to its top and the action to its
     bottom. Flex and stretch behave identically in both engines, which
     display:contents historically has not. */
  .board-row {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: var(--sp-3);
    min-height: 0;
  }

  .board-row .row-rank {
    flex: 0 0 auto;
  }

  .board-row .listing-icon {
    --icon-size: 52px;
  }

  .board-row .row-main {
    flex: 1 1 auto;
    min-width: 0;
  }

  .board-row .row-side {
    flex: 0 0 auto;
    align-self: stretch;
    flex-direction: column;
    align-items: flex-end;
    justify-content: space-between;
    gap: 6px;
    min-height: 58px;
  }

  .board-row .row-amount {
    align-items: flex-start;
  }

  .board-row .row-action {
    display: block;
  }

  .row-rank {
    min-width: 0;
    font-size: var(--fs-footnote);
  }

  .row-rank-hash {
    margin-right: 2px;
  }

  .row-title {
    font-size: var(--fs-footnote);
    line-height: 1.35;
  }

  .row-desc {
    font-size: var(--fs-caption2);
    line-height: 1.35;
  }

  .row-meta {
    margin-top: 3px;
    gap: 5px;
  }

  .row-tag {
    height: 20px;
    padding: 0 7px;
    font-size: clamp(8.5px, 2.545vw, 10px);
  }

  .row-amount-value {
    font-size: var(--fs-headline);
  }

  .type-badge svg,
  .type-badge img {
    width: 16px;
    height: 16px;
  }

  .type-badge {
    right: -5px;
    bottom: -5px;
  }

  /* A smaller, quieter claim control. */
  .row-claim {
    height: 28px;
    gap: 5px;
    padding: 0 3px 0 9px;
    font-size: clamp(8.5px, 2.545vw, 10px);
  }

  .row-claim-price {
    height: 22px;
    padding: 0 7px;
    font-size: var(--fs-caption2);
  }
}

/* Below this the rank number leaves the button. The row already shows
   the rank on its left, so "Claim $395" loses nothing and buys back the
   width the title needs. */
@media (max-width: 440px) {
  .row-claim-rank {
    display: none;
  }
}

@media (max-width: 400px) {
  .board-row {
    gap: 8px;
    padding: 10px var(--sp-2);
  }

  .board-row .listing-icon {
    --icon-size: 46px;
  }

  .board-row .row-side {
    min-height: 52px;
  }

  .row-rank {
    font-size: var(--fs-caption1);
  }

  .row-amount-value {
    font-size: var(--fs-body);
  }

  .row-claim {
    padding: 0 3px 0 7px;
    font-size: clamp(8.5px, 2.545vw, 10px);
  }

  .row-claim-price {
    padding: 0 7px;
  }
}

/* -------------------------------------------------------------
   Everything else on a phone
   ------------------------------------------------------------- */

@media (max-width: 640px) {
  .hero {
    padding: var(--sp-6) 0 var(--sp-5);
  }

  .hero h1 {
    font-size: clamp(24px, 7.5vw, 32px);
  }

  .hero-eyebrow {
    margin-bottom: var(--sp-4);
  }

  .hero-sub {
    font-size: var(--fs-footnote);
    margin-bottom: var(--sp-5);
  }

  .hero-note {
    font-size: var(--fs-caption1);
    margin-bottom: var(--sp-5);
  }

  .section {
    margin-top: var(--sp-6);
  }

  .section-head {
    flex-direction: column;
    align-items: flex-start;
    gap: var(--sp-1);
  }

  .page-head {
    padding: var(--sp-5) 0 var(--sp-4);
  }

  .page-head h1 {
    font-size: var(--fs-title3);
  }

  .pagination {
    gap: 5px;
  }

  .page-link {
    min-width: 40px;
    height: 40px;
    padding: 0 var(--sp-2);
  }

  .page-link span {
    display: none;
  }

  .page-link .icon {
    width: 18px;
    height: 18px;
  }

  .prose h2 {
    font-size: var(--fs-title3);
    margin-top: var(--sp-7);
  }

  .prose-card {
    padding: var(--sp-4);
  }

  .site-footer {
    margin-top: var(--sp-7);
  }
}

/* -------------------------------------------------------------
   Modals become sheets
   ------------------------------------------------------------- */

@media (max-width: 640px) {

  /* Centred, the same as on a desktop. These used to be sheets pinned
     to the bottom edge, which is precisely where the keyboard opens. */
  .modal-root {
    padding: var(--sp-3);
  }

  .modal-panel {
    max-width: 100%;
    width: 100%;
    max-height: calc(var(--vvh, 100vh) - var(--sp-5));
    border-radius: 18px;
    padding: var(--sp-4);
    padding-bottom: calc(var(--sp-4) + env(safe-area-inset-bottom, 0px));
  }

  .modal-panel-search {
    margin-top: 0;
    max-height: calc(var(--vvh, 100vh) - var(--sp-5));
  }

  .field-row {
    gap: var(--sp-4);
  }

  .rank-readout {
    padding: var(--sp-4);
  }

  .rank-readout-value {
    font-size: var(--fs-title3);
  }

  .listing-preview {
    gap: var(--sp-3);
    padding: var(--sp-3);
  }

  .listing-preview .listing-icon {
    --icon-size: 56px;
  }
}

@media (prefers-reduced-motion: reduce) {
  .modal-panel {
    transform: none !important;
  }
}

/* -------------------------------------------------------------
   Admin on a phone
   ------------------------------------------------------------- */

@media (max-width: 767px) {
  .dash-toolbar {
    gap: var(--sp-2);
    padding: var(--sp-4) 0 var(--sp-3);
  }

  .dash-toolbar-right {
    margin-left: 0;
    width: 100%;
  }

  .filter-form {
    width: 100%;
  }

  .filter-search {
    min-width: 0;
    flex: 1 1 100%;
  }

  .select-wrap-sm {
    flex: 1 1 44%;
  }

  .select-sm {
    width: 100%;
  }

  .filter-date {
    flex: 1 1 44%;
  }

  .input-sm {
    width: 100%;
    min-width: 0;
  }

  .metric {
    padding: var(--sp-3) var(--sp-4);
  }

  .metric-value {
    font-size: 20px;
  }

  .dash-chart {
    height: 200px;
  }

  .dash-chart-card {
    padding: var(--sp-4) var(--sp-3) var(--sp-3);
  }

  .dash-legend {
    gap: var(--sp-4);
  }

  .settings-panel {
    padding: var(--sp-4);
  }

  .settings-head {
    margin-bottom: var(--sp-4);
  }

  .modal-danger {
    flex-direction: column;
    align-items: flex-start;
  }

  .input,
  .textarea,
  .select,
  .search-input,
  .filter-search input {
    font-size: clamp(13px, 4.071vw, 16px);
  }

  .amount-input {
    font-size: var(--fs-title3);
  }
}

/* -------------------------------------------------------------
   Touch
   ------------------------------------------------------------- */

@media (hover: none) {

  /* Hover states are meaningless on touch and can stick after a tap. */
  .board-row:hover {
    background: transparent;
  }

  .board-row:hover .row-claim {
    border-color: var(--separator);
    color: var(--label-2);
  }

  .podium-card:hover {
    transform: none;
    box-shadow: var(--shadow-2);
  }

  .row-claim:hover {
    transform: none;
  }

  .icon-button {
    width: 42px;
    height: 42px;
  }

  .footer-col a {
    padding: var(--sp-1) 0;
    display: inline-block;
  }
}

/* =============================================================
   Hero backdrop
   -------------------------------------------------------------
   A glow, a drifting particle field and low contrast marks of the
   things people list here. Only transform and opacity are animated,
   so the whole thing runs on the compositor and never triggers
   layout or paint while it moves.
   ============================================================= */

:root {
  --hero-ink: rgba(255, 255, 255, 0.14);
  --hero-dot: rgba(255, 255, 255, 0.42);
  --hero-glow-a: rgba(10, 132, 255, 0.15);
  --hero-glow-b: rgba(255, 255, 255, 0.035);
}

body.light {
  /* On a light page the marks are ink rather than light, and the glow
     is cooled right down so it reads as a tint, not a hotspot. */
  --hero-ink: rgba(20, 24, 40, 0.13);
  --hero-dot: rgba(20, 24, 40, 0.30);
  --hero-glow-a: rgba(0, 113, 227, 0.13);
  --hero-glow-b: rgba(255, 255, 255, 0.60);
}

/* The hero owns its own stacking context, so the backdrop can sit at
   a negative index without falling behind the page itself. */
.hero {
  position: relative;
  isolation: isolate;
}

.hero-canvas {
  position: absolute;
  z-index: -1;
  /* Reaches up behind the transparent bar and well past the bottom of
     the hero, so the glow has somewhere to fade out into, and spans
     the real viewport width. --vw is measured from clientWidth by
     the script, because 100vw counts the scrollbar and would add a
     horizontal one. It falls back to the content width until the
     script runs, which is narrower but never broken. */
  top: -120px;
  /* Far enough to run the full height of the rank blocks, so the
     light carries through them rather than stopping at the hero. */
  bottom: clamp(-620px, -34vw, -300px);
  left: 50%;
  width: var(--vw, 100%);
  transform: translateX(-50%);
  pointer-events: none;
  overflow: hidden;
  contain: layout paint style;

  /* The fade is what stops it reading as a pasted rectangle. */
  -webkit-mask-image: linear-gradient(180deg, transparent 0%, #000 10%, #000 62%, transparent 100%);
  mask-image: linear-gradient(180deg, transparent 0%, #000 10%, #000 62%, transparent 100%);
}

/* ---- Glow ---- */

/* A wide ellipse filling the canvas, not a circle sitting in the
   middle of it. The radii are percentages of the element, so the
   wash spans the full width at any screen size instead of topping
   out at a fixed diameter. */
.hero-glow {
  position: absolute;
  inset: 0;
  background:
    radial-gradient(ellipse 125% 82% at 50% 10%, var(--hero-glow-a) 0%, transparent 74%),
    radial-gradient(ellipse 72% 46% at 50% 2%, var(--hero-glow-b) 0%, transparent 78%);
  animation: hero-breathe 16s ease-in-out infinite;
}

@keyframes hero-breathe {

  0%,
  100% {
    opacity: 0.9;
    transform: scale(1);
  }

  50% {
    opacity: 1;
    transform: scale(1.04);
  }
}

/* ---- Particles ---- */

/* The marks and dots stay in the hero. Only the light runs on into
   the rank blocks below, so the field keeps its density instead of
   thinning out across twice the height. */
.hero-dots,
.hero-marks {
  position: absolute;
  inset: 0 0 38% 0;
  display: block;
}

.hero-dots>span {
  position: absolute;
  left: var(--x);
  top: var(--y);
  width: var(--size);
  height: var(--size);
  border-radius: 50%;
  background: var(--hero-dot);
  opacity: 0;
  animation: hero-twinkle var(--dur) ease-in-out var(--delay) infinite;
}

@keyframes hero-twinkle {

  0%,
  100% {
    opacity: 0;
    transform: translate3d(0, 6px, 0);
  }

  50% {
    opacity: var(--dim);
    transform: translate3d(0, -6px, 0);
  }
}

/* ---- Marks ---- */

/* Each mark carries its own path as custom properties, so one
   keyframe rule gives forty different routes. The old pair of shared
   keyframes moved every icon straight up and back down, which read as
   two groups bobbing rather than a field drifting.

   The fade runs on its own clock, at a length unrelated to the drift,
   so a mark is rarely at the same point of both cycles twice. */
.hero-mark {
  position: absolute;
  left: var(--x);
  top: var(--y);
  color: var(--hero-ink);
  will-change: transform;
  animation:
    hero-float var(--dur) ease-in-out var(--delay) infinite,
    hero-fade var(--fade) ease-in-out var(--delay) infinite;
}

.hero-mark-icon {
  display: block;
  stroke-width: 1.6;
}

/* A four point wander. The offsets are per mark, so the path is a
   different loop for every icon. translate3d keeps it on the
   compositor, and nothing here touches layout. */
@keyframes hero-float {
  0% {
    transform: translate3d(-50%, -50%, 0) rotate(var(--rot));
  }

  25% {
    transform: translate3d(calc(-50% + var(--ax)), calc(-50% + var(--ay)), 0) rotate(calc(var(--rot) + var(--spin)));
  }

  50% {
    transform: translate3d(calc(-50% + var(--bx)), calc(-50% + var(--by)), 0) rotate(var(--rot));
  }

  75% {
    transform: translate3d(calc(-50% - var(--ax)), calc(-50% + var(--ay)), 0) rotate(calc(var(--rot) - var(--spin)));
  }

  100% {
    transform: translate3d(-50%, -50%, 0) rotate(var(--rot));
  }
}

/* A slow breath, so marks surface and recede instead of all sitting
   at one weight. */
@keyframes hero-fade {

  0%,
  100% {
    opacity: 0.55;
  }

  50% {
    opacity: 1;
  }
}


/* ---- Smaller screens ----
   The marks thin out and pull toward the edges, because there is far
   less room either side of the headline on a phone. */

@media (max-width: 860px) {
  .hero-mark-icon {
    transform: scale(0.8);
  }
}

@media (max-width: 640px) {
  .hero-canvas {
    top: calc(var(--sp-4) * -1);
    bottom: -300px;
  }

  .hero-glow {
    background:
      radial-gradient(ellipse 165% 72% at 50% 6%, var(--hero-glow-a) 0%, transparent 76%),
      radial-gradient(ellipse 105% 42% at 50% 0%, var(--hero-glow-b) 0%, transparent 82%);
  }

  .hero-dots,
  .hero-marks {
    inset: 0 0 30% 0;
  }

  /* Forty marks in the width of a phone is a texture, not a field.
     Every third one goes, which thins it evenly rather than leaving a
     bare patch, and the ones that would land under the headline go
     with them. */
  .hero-mark:nth-child(3n) {
    display: none;
  }

  .hero-mark:nth-child(13),
  .hero-mark:nth-child(14),
  .hero-mark:nth-child(17),
  .hero-mark:nth-child(19),
  .hero-mark:nth-child(20),
  .hero-mark:nth-child(22) {
    display: none;
  }

  .hero-mark-icon {
    transform: scale(0.66);
  }

  .hero-dots>span:nth-child(2n) {
    display: none;
  }
}

/* ---- Motion preference ----
   The composition stays, the movement stops. */

@media (prefers-reduced-motion: reduce) {

  .hero-glow,
  .hero-mark,
  .hero-dots>span {
    animation: none;
  }

  .hero-glow {
    transform: none;
    opacity: 0.95;
  }

  .hero-mark {
    transform: translate3d(-50%, -50%, 0) rotate(var(--rot));
  }

  .hero-dots>span {
    opacity: var(--dim);
  }
}

/* =============================================================
   Fluid type on small screens
   -------------------------------------------------------------
   Chrome and Firefox on the same phone report different CSS
   viewport widths, because they resolve the device pixel ratio
   differently. On a 1080px screen Firefox lays out at about 393px
   and Chrome at about 322px. Both render a 15px font as exactly
   15px, but that is 3.8% of the screen in Firefox and 4.7% in
   Chrome, which is why Chrome looks larger and tighter.

   No amount of text-size-adjust fixes that, because nothing is
   being adjusted: the type is already the size it was asked to be.
   The layout has to stop being measured in fixed pixels.

   Every value below is calibrated to resolve to its current pixel
   size at 393px, so Firefox renders exactly as it does now, and
   scales proportionally beneath that so Chrome matches it.
   ============================================================= */

@media (max-width: 520px) {
  :root {
    --fs-caption2: clamp(9px, 2.799vw, 11px);
    --fs-caption1: clamp(10px, 3.053vw, 12px);
    --fs-footnote: clamp(10.5px, 3.308vw, 13px);
    --fs-body: clamp(12px, 3.817vw, 15px);
    --fs-headline: clamp(13.5px, 4.326vw, 17px);
    --fs-title3: clamp(17px, 5.598vw, 22px);
    --fs-title2: clamp(21px, 7.125vw, 28px);
    --fs-title1: clamp(25px, 8.651vw, 34px);

    /* The gutter scales with the type, so the text column keeps the
       same proportion of the screen in both browsers. */
    --page-pad: clamp(13px, 5.089vw, 20px);

    /* The rhythm scales too, otherwise the smaller type would sit in
       spacing built for the larger. */
    --sp-3: clamp(9px, 3.053vw, 12px);
    --sp-4: clamp(12px, 4.071vw, 16px);
    --sp-5: clamp(15px, 5.089vw, 20px);
    --sp-6: clamp(18px, 6.107vw, 24px);
    --sp-7: clamp(23px, 8.142vw, 32px);
    --sp-9: clamp(40px, 16.285vw, 64px);
  }

}

/* -------------------------------------------------------------
   iOS Safari zooms the page when a form field smaller than 16px
   takes focus. No other engine does. -webkit-touch-callout is
   iOS only, so the fixed size is restored just for the browser
   that needs it, and Android keeps scaling with the rest of the
   type. Otherwise one fixed 16px field would sit in a layout
   that had scaled down around it.
   ------------------------------------------------------------- */

@supports (-webkit-touch-callout: none) {
  @media (max-width: 767px) {

    .input,
    .textarea,
    .select,
    .claim-input,
    .search-input,
    .filter-search input {
      font-size: 16px;
    }
  }
}


/* -------------------------------------------------------------
   Footer
   -------------------------------------------------------------
   Three link columns beside a short statement of what the site is,
   then a thin base strip. The old footer was a single row of bare
   links with a disclaimer hanging off the end, which read as an
   afterthought against the rest of the page.
   ------------------------------------------------------------- */

.site-footer {
  margin-top: var(--sp-9);
  border-top: 1px solid var(--separator);
  background: var(--surface-sunk, var(--surface));
}

.footer-inner {
  max-width: var(--max-w);
  margin: 0 auto;
  padding: var(--sp-8) var(--page-pad) var(--sp-7);
  display: grid;
  gap: var(--sp-7);
  grid-template-columns: 1fr;
}

/* ---- The statement ---- */

.footer-lead {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--sp-3);
  max-width: 380px;
}

/* This one does render on a phone, so it scales rather than being
   pinned, and tops out where the bar version sits. */
.footer-brand {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  font-size: clamp(18px, 5.1vw, 20px);
  font-weight: 700;
  letter-spacing: -0.035em;
  color: var(--label);
}

.footer-brand:hover {
  opacity: 1;
}

.footer-brand .brand-mark {
  width: clamp(30px, 8.4vw, 33px);
  height: clamp(30px, 8.4vw, 33px);
  border-radius: 10px;
}

.footer-brand .brand-dot {
  color: var(--blue);
}

.footer-blurb {
  margin: 0;
  font-size: var(--fs-footnote);
  line-height: 1.55;
  color: var(--label-2);
  text-wrap: pretty;
}

/* ---- The link columns ---- */

.footer-nav {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(128px, 1fr));
  gap: var(--sp-6) var(--sp-4);
}

.footer-col {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 10px;
  min-width: 0;
}

.footer-col-title {
  margin: 0 0 2px;
  font-size: var(--fs-caption2);
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--label-3);
}

.footer-col a {
  max-width: 100%;
  font-size: var(--fs-footnote);
  color: var(--label-2);
  overflow: hidden;
  text-overflow: ellipsis;
  transition: color var(--state);
}

.footer-col a:hover {
  color: var(--label);
  opacity: 1;
}

/* ---- The base line ----
   No rule above it. A second border cut the footer into two blocks
   that looked like separate sections of the page. */

.footer-base-inner {
  max-width: var(--max-w);
  margin: 0 auto;
  padding: 0 var(--page-pad) var(--sp-6);
  text-align: center;
  font-size: var(--fs-caption1);
  color: var(--label-3);
}

/* ---- Wider screens ---- */

@media (min-width: 720px) {
  .footer-inner {
    grid-template-columns: minmax(0, 1fr) minmax(0, 1.15fr);
    gap: var(--sp-9);
    align-items: start;
  }
}

@media (max-width: 520px) {
  .footer-inner {
    padding: var(--sp-7) var(--page-pad) var(--sp-6);
    gap: var(--sp-6);
  }

  .footer-lead {
    max-width: none;
  }

  /* Three columns would each be too narrow to read at this width, so
     they become two and the contact column runs full width under them. */
  .footer-nav {
    grid-template-columns: 1fr 1fr;
    gap: var(--sp-5) var(--sp-4);
  }

  .footer-col:last-child {
    grid-column: 1 / -1;
  }

  .footer-base-inner {
    padding: 0 var(--page-pad) var(--sp-5);
  }
}

/* -------------------------------------------------------------
   Categories
   -------------------------------------------------------------
   The old page was a flat alphabetical grid of centred cards, which
   gave no sense of where the competition actually is. The cards are
   left aligned now so the names form a single scan line, they carry
   the money as well as the count, and the page opens with the totals
   and a filter because there are close to thirty of them.
   ------------------------------------------------------------- */

.cat-head {
  padding: var(--sp-7) 0 var(--sp-5);
}

.cat-head h1 {
  font-size: var(--fs-title1);
  letter-spacing: -0.03em;
  margin-bottom: var(--sp-2);
}

.cat-head>p {
  margin: 0;
  max-width: 60ch;
  font-size: var(--fs-body);
  line-height: 1.55;
  color: var(--label-2);
  text-wrap: pretty;
}

/* ---- Filter and sort ---- */

.cat-toolbar {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  margin: var(--sp-6) 0 var(--sp-3);
}

.cat-filter {
  position: relative;
  flex: 1 1 auto;
  min-width: 0;
}

.cat-filter-icon {
  position: absolute;
  left: 12px;
  top: 50%;
  transform: translateY(-50%);
  display: flex;
  color: var(--label-3);
  pointer-events: none;
}

.cat-filter-input {
  width: 100%;
  height: 40px;
  padding: 0 var(--sp-4) 0 38px;
  background: var(--surface);
  border: 1px solid var(--separator);
  border-radius: 999px;
  color: var(--label);
  font-size: var(--fs-footnote);
  font-family: inherit;
  transition: border-color var(--state), background var(--state);
}

.cat-filter-input::placeholder {
  color: var(--label-3);
}

.cat-filter-input:focus {
  outline: none;
  border-color: var(--blue);
  background: var(--surface-2);
}

/* The browser's own clear button sits badly inside a pill. */
.cat-filter-input::-webkit-search-decoration,
.cat-filter-input::-webkit-search-cancel-button {
  -webkit-appearance: none;
}

.cat-sort {
  display: flex;
  flex: 0 0 auto;
  padding: 3px;
  gap: 2px;
  background: var(--surface);
  border: 1px solid var(--separator);
  border-radius: 999px;
}

.cat-sort-button {
  height: 32px;
  padding: 0 14px;
  border: 0;
  border-radius: 999px;
  background: transparent;
  color: var(--label-2);
  font-family: inherit;
  font-size: var(--fs-caption1);
  font-weight: 600;
  cursor: pointer;
  white-space: nowrap;
  transition: background var(--state), color var(--state);
}

.cat-sort-button:hover {
  color: var(--label);
}

.cat-sort-button.is-on {
  background: var(--surface-3);
  color: var(--label);
}

.cat-count {
  margin: 0 0 var(--sp-4);
  font-size: var(--fs-caption1);
  color: var(--label-3);
}

/* ---- The grid ---- */

.cat-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(232px, 1fr));
  gap: var(--sp-3);
}

.cat-card {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: var(--sp-2);
  padding: var(--sp-4);
  background: var(--surface);
  border: 1px solid var(--separator);
  border-radius: var(--r-lg);
  color: var(--label);
  overflow: hidden;
  transition: background var(--state), border-color var(--state), transform var(--state);
}

.cat-card:hover {
  opacity: 1;
  background: var(--surface-2);
  border-color: var(--blue);
  transform: translateY(-2px);
}

.cat-card-top {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-3);
}

.cat-card-icon {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 38px;
  height: 38px;
  border-radius: 11px;
  background: var(--blue-tint);
  color: var(--blue);
  transition: background var(--state);
}

.cat-card:hover .cat-card-icon {
  background: var(--blue);
  color: #FFFFFF;
}

/* The count is the headline number on the card, so it is set as one. */
.cat-card-count {
  font-size: var(--fs-title3);
  font-weight: 600;
  letter-spacing: -0.03em;
  color: var(--label);
  font-variant-numeric: tabular-nums;
  line-height: 1;
}

.cat-card-name {
  font-size: var(--fs-body);
  font-weight: 600;
  line-height: 1.3;
  letter-spacing: -0.01em;
  text-wrap: balance;
}

.cat-card-meta {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 6px;
  margin-top: auto;
  padding-top: var(--sp-2);
  font-size: var(--fs-caption1);
  color: var(--label-2);
}

.cat-card-meta b {
  font-weight: 600;
  color: var(--label);
  font-variant-numeric: tabular-nums;
}

.cat-card-sep {
  color: var(--label-3);
}

/* The filter hides cards with this rather than the hidden attribute.
   .cat-card sets display:flex, and an author declaration outranks the
   browsers own [hidden] { display: none }, so the attribute on its own
   would hide nothing at all. */
.cat-card.is-filtered {
  display: none;
}

/* ---- The activity bar ---- */

.cat-card-bar {
  display: block;
  height: 3px;
  margin-top: var(--sp-3);
  border-radius: 999px;
  background: var(--surface-3);
  overflow: hidden;
}

.cat-card-bar-fill {
  display: block;
  height: 100%;
  border-radius: inherit;
  background: var(--blue);
  opacity: 0.75;
  transition: opacity var(--state);
}

.cat-card:hover .cat-card-bar-fill {
  opacity: 1;
}

/* ---- Categories nobody has claimed ---- */

.cat-card.is-open {
  border-style: dashed;
  background: transparent;
}

.cat-card.is-open .cat-card-count {
  color: var(--label-3);
}

.cat-card.is-open .cat-card-icon {
  background: var(--surface-2);
  color: var(--label-3);
}

.cat-card.is-open .cat-card-bar {
  display: none;
}

.cat-card-open {
  color: var(--label-3);
}

.cat-card.is-open:hover {
  border-style: solid;
  border-color: var(--green);
  background: var(--surface);
}

.cat-card.is-open:hover .cat-card-icon {
  background: var(--green);
  color: #FFFFFF;
}

.cat-card.is-open:hover .cat-card-open {
  color: var(--green);
}

.cat-empty {
  padding: var(--sp-7) 0;
  text-align: center;
  color: var(--label-3);
  font-size: var(--fs-body);
}

/* ---- Narrow screens ---- */

@media (max-width: 620px) {
  .cat-head {
    padding: var(--sp-6) 0 var(--sp-4);
  }

  /* The filter and the sort will not both fit on one line here. */
  .cat-toolbar {
    flex-direction: column;
    align-items: stretch;
  }

  .cat-sort {
    align-self: flex-start;
  }

  .cat-grid {
    grid-template-columns: repeat(auto-fill, minmax(158px, 1fr));
    gap: var(--sp-2);
  }

  .cat-card {
    padding: var(--sp-3);
  }

  .cat-card-icon {
    width: 34px;
    height: 34px;
    border-radius: 10px;
  }
}

/* -------------------------------------------------------------
   Render containment
   -------------------------------------------------------------
   A board page carries fifty rows, most of them below the fold. Each
   one is a grid holding an image, three text blocks and two controls,
   and the browser lays every one of them out before the first paint
   even though only a handful are visible.

   content-visibility lets it skip the work for rows that are off
   screen and do it as they approach. contain-intrinsic-size gives the
   skipped rows a placeholder height so the scrollbar stays honest and
   nothing jumps as they are rendered.

   The height is the row's real one, so the estimate is exact and there
   is no shift at all. Only rows are treated this way: the podium and
   the hero are on screen immediately and would gain nothing.
   ------------------------------------------------------------- */

@supports (content-visibility: auto) {

  /* 104px is the rows own min-height, so the placeholder matches what
     the row actually becomes and the scrollbar does not move as rows
     are rendered. The auto keyword makes the browser remember the real
     height once it has measured one, so this figure only has to carry
     the first layout. */
  .board-row {
    content-visibility: auto;
    contain-intrinsic-size: auto 104px;
  }

  /* Below 760px the row drops its min-height and is sized by content. */
  @media (max-width: 760px) {
    .board-row {
      contain-intrinsic-size: auto 112px;
    }
  }
}

/* The category cards are cheap on their own but there are close to
   thirty of them, and the ones past the fold cost the same as the rest. */
@supports (content-visibility: auto) {
  .cat-card {
    content-visibility: auto;
    contain-intrinsic-size: auto 148px;
  }
}

/* -------------------------------------------------------------
   Taking the top step
   -------------------------------------------------------------
   The three cards arrive as a podium rather than as a list. Second
   and third rise and settle, then the winner comes up last and
   climbs past them, which is the one moment of the sequence the eye
   is meant to land on.

   Afterwards nothing on the card moves. The only continuing effect
   is inside the giant numeral, where the gold catches the light every
   few seconds. It is the medal that glints, not a box glowing.

   Everything here animates transform, opacity or a background
   position on a single element, so no layout is ever recalculated.
   ------------------------------------------------------------- */

@keyframes podium-step {
  from {
    opacity: 0;
    transform: translateY(22px);
  }

  to {
    opacity: 1;
    transform: none;
  }
}

/* The winner overshoots on the way up, the way something does when it
   steps onto a higher stair and settles. */
@keyframes podium-climb {
  0% {
    opacity: 0;
    transform: translateY(40px) scale(0.96);
  }

  55% {
    opacity: 1;
    transform: translateY(-10px) scale(1.02);
  }

  78% {
    transform: translateY(2px) scale(0.998);
  }

  100% {
    opacity: 1;
    transform: none;
  }
}

/* backwards, never forwards. A forwards animation outranks normal
   declarations for the life of the page, which would leave the hover
   lift permanently unable to apply. */
.podium-card {
  animation: podium-step 0.55s cubic-bezier(0.22, 1, 0.36, 1) backwards;
}

.podium-silver {
  animation-delay: 0.05s;
}

.podium-bronze {
  animation-delay: 0.12s;
}

.podium-top {
  animation: podium-climb 0.95s cubic-bezier(0.34, 1.4, 0.5, 1) 0.3s backwards;
}

/* Opened at a particular listing, so the entrance is skipped. It ends
   at 1.25s, later than any scroll correction can run, and it moves the
   card forty pixels on the way. A card scrolled into place would slide
   back under the bar once it finished. */
body.is-anchored .podium-card,
body.is-anchored .podium-top {
  animation: none;
}

/* ---- The gold catches the light ----

   The numeral is painted by clipping a gradient to the glyph. A second
   gradient rides on top of that one and is swept across, so a highlight
   travels through the figure and then rests. The long tail on the
   keyframes is the rest: it glints, it does not shimmer constantly.

   Only the top card does this. Silver and bronze stay still, which is
   what makes the gold read as the winner. */

@keyframes podium-glint {
  0% {
    background-position: 190% 0, 0 0;
  }

  30% {
    background-position: -110% 0, 0 0;
  }

  100% {
    background-position: -110% 0, 0 0;
  }
}

@supports ((-webkit-background-clip: text) or (background-clip: text)) {
  .podium-top .podium-numeral {
    background-image:
      linear-gradient(100deg,
        transparent 38%,
        rgba(255, 255, 255, 0.85) 50%,
        transparent 62%),
      linear-gradient(170deg, var(--medal) 0%, var(--medal) 28%, transparent 84%);
    background-size: 240% 100%, 100% 100%;
    background-position: 190% 0, 0 0;
    background-repeat: no-repeat;
    animation: podium-glint 6.5s ease-in-out 1.15s infinite;
  }

  /* On the light theme a white highlight would disappear into the page,
     so the glint darkens the gold instead of brightening it. */
  body.light .podium-top .podium-numeral {
    background-image:
      linear-gradient(100deg,
        transparent 38%,
        rgba(120, 72, 0, 0.55) 50%,
        transparent 62%),
      linear-gradient(170deg, var(--medal) 0%, var(--medal) 28%, transparent 84%);
  }
}

/* ---- The amount counts up ----

   The figure is what the rank cost, so it is worth a beat of attention.
   The script fills it in; this only stops the digits jittering as the
   width changes, which tabular figures already handle, and holds the
   starting state so there is no flash of the final number first. */

.podium-top .podium-amount[data-count] {
  font-variant-numeric: tabular-nums;
}

/* -------------------------------------------------------------
   Nothing moves for a reader who asked for that. The cards are
   simply there, which is all the animation was ever saying.
   ------------------------------------------------------------- */

@media (prefers-reduced-motion: reduce) {

  .podium-card,
  .podium-top {
    opacity: 1;
    animation: none;
  }

  .podium-top .podium-numeral {
    animation: none;
    background-image: linear-gradient(170deg, var(--medal) 0%, var(--medal) 28%, transparent 84%);
    background-size: 100% 100%;
    background-position: 0 0;
  }
}


/* -------------------------------------------------------------
   Podium card layout
   -------------------------------------------------------------
   A badge in the top corner, the listing in the middle, and a footer
   that carries the amount on the left with the claim button on the
   right. The amount used to sit at the top and the button was centred
   underneath, which left the corners empty and the two numbers on the
   card far apart from each other.
   ------------------------------------------------------------- */

.podium-head {
  position: relative;
  z-index: 2;
  display: flex;
  padding: var(--sp-4) var(--sp-4) 0;
}

/* ---- The footer ---- */

.podium-foot {
  position: relative;
  z-index: 2;
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: var(--sp-3);
  margin-top: auto;
  padding: var(--sp-4) var(--sp-4) var(--sp-4);
}

.podium-price {
  position: relative;
  z-index: 2;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 1px;
  min-width: 0;
  padding: 0;
  line-height: 1.1;
}

/* The button sits on the right and never shrinks, so a long amount
   label wraps or clips rather than squeezing the action. */
.podium-foot .podium-button {
  flex: 0 0 auto;
}

/* The button never shrinks, so the label is what gives way on a narrow
   card. Better a trimmed caption than an action pushed out of reach. */
.podium-foot .podium-amount-label {
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* ---- Narrow screens ----
   The footer stays a row down to the smallest width. Both halves are
   short, and stacking them would push the card taller for no gain. */

@media (max-width: 520px) {
  .podium-head {
    padding: var(--sp-3) var(--sp-3) 0;
  }

  .podium-foot {
    padding: var(--sp-3);
    gap: var(--sp-2);
  }
}


/* -------------------------------------------------------------
   The medal, restrained
   -------------------------------------------------------------
   The first version was doing too much: primary coloured ribbons, a
   four stop gradient with a bright band through it, an inset bevel
   with a white highlight and a dark underside, and a text shadow on
   the numeral. Stacked together those read as a sticker rather than
   as metal.

   This one keeps the object and drops the effects. Two tones per
   metal instead of four, a hairline rim instead of a bevel, a ribbon
   in the same family as its disc rather than a primary colour, and no
   text shadow. Monochrome per medal is what makes it look struck.
   ------------------------------------------------------------- */

.podium-medal {
  position: relative;
  display: block;
  width: 34px;
  height: 46px;
  flex: 0 0 auto;
}

/* ---- The ribbon ----
   Narrow, upright, and only slightly splayed. The earlier pair leaned
   at eleven degrees and read as a bow tie. */

.podium-medal-ribbon {
  position: absolute;
  top: 0;
  left: 50%;
  width: 26px;
  height: 26px;
  transform: translateX(-50%);
}

.podium-medal-ribbon::before,
.podium-medal-ribbon::after {
  content: "";
  position: absolute;
  top: 0;
  width: 9px;
  height: 100%;
  background: var(--ribbon);
  border-radius: 1px 1px 0 0;
}

.podium-medal-ribbon::before {
  left: 0;
  transform: skewX(7deg);
  transform-origin: top left;
}

.podium-medal-ribbon::after {
  right: 0;
  transform: skewX(-7deg);
  transform-origin: top right;
  background: var(--ribbon-back);
}

/* ---- The disc ----
   A single soft sweep from the upper left, a hairline rim, and one
   quiet shadow. Nothing is trying to look glossy. */

.podium-medal-disc {
  position: absolute;
  bottom: 0;
  left: 50%;
  z-index: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  transform: translateX(-50%);
  border-radius: 50%;
  background: linear-gradient(150deg, var(--disc-a) 0%, var(--disc-b) 100%);
  box-shadow:
    inset 0 0 0 1px var(--disc-rim),
    0 1px 3px rgba(0, 0, 0, 0.22);
  font-family: var(--font-mono);
  font-size: 14px;
  font-weight: 600;
  line-height: 1;
  color: var(--disc-ink);
  letter-spacing: -0.02em;
}

/* ---- The three metals ----
   Two tones each. The same values in both themes, because a medal is
   the same object whichever theme it sits in. */

.podium-gold {
  --ribbon: #A8813A;
  --ribbon-back: #8A6929;
  --disc-a: #F3D18C;
  --disc-b: #C99A3C;
  --disc-rim: rgba(120, 86, 24, 0.40);
  --disc-ink: #402C06;
}

.podium-silver {
  --ribbon: #878E99;
  --ribbon-back: #6E747E;
  --disc-a: #E9ECF0;
  --disc-b: #A9AFB9;
  --disc-rim: rgba(84, 91, 102, 0.36);
  --disc-ink: #383C44;
}

.podium-bronze {
  --ribbon: #96694A;
  --ribbon-back: #7A543A;
  --disc-a: #E2B896;
  --disc-b: #B67E52;
  --disc-rim: rgba(110, 66, 34, 0.40);
  --disc-ink: #2E1B09;
}

/* The page behind it is pale, so the cast shadow lightens rather than
   sitting under the disc as a dark smudge. */
body.light .podium-medal-disc {
  box-shadow:
    inset 0 0 0 1px var(--disc-rim),
    0 1px 3px rgba(0, 0, 0, 0.14);
}

/* ---- Narrow screens ---- */

@media (max-width: 520px) {
  .podium-medal {
    width: 28px;
    height: 38px;
  }

  .podium-medal-ribbon {
    width: 21px;
    height: 21px;
  }

  .podium-medal-ribbon::before,
  .podium-medal-ribbon::after {
    width: 7px;
  }

  .podium-medal-disc {
    width: 26px;
    height: 26px;
    font-size: 12px;
  }
}

/* -------------------------------------------------------------
   Hero status chip
   -------------------------------------------------------------
   Replaces the flat pill with a trophy in it. That icon said nothing
   the heading did not already say, and the pill looked the same on
   both boards even though the two mean different things: Today is a
   window that is open right now, All time is a settled total.

   The chip carries a state dot, the state, and what the state means.
   The dot beats on Today and rests on All time.

   The rim is a gradient rather than a flat line. Two backgrounds do
   it: one clipped to the padding box for the fill, one to the border
   box for the rim, with the border itself left transparent. That
   gives a hairline that is lit at one corner and fades at the other,
   which is what stops it reading as a plain outlined box.
   ------------------------------------------------------------- */

.hero-eyebrow {
  --chip: var(--orange);

  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 9px;
  height: 30px;
  padding: 0 13px;
  margin-bottom: var(--sp-5);
  border: 1px solid transparent;
  border-radius: 999px;

  background:
    linear-gradient(var(--surface), var(--surface)) padding-box,
    linear-gradient(120deg,
      color-mix(in srgb, var(--chip) 62%, transparent) 0%,
      var(--separator) 42%,
      var(--separator) 62%,
      color-mix(in srgb, var(--chip) 34%, transparent) 100%) border-box;

  font-size: var(--fs-caption1);
  font-weight: 500;
  line-height: 1;
  color: var(--label-2);
  white-space: nowrap;
}

/* Live is green because the window is open. */
.hero-eyebrow.is-live {
  --chip: var(--green);
}

/* Without color-mix the rim falls back to the ordinary separator, which
   is the old look rather than a broken one. */
@supports not (color: color-mix(in srgb, red 50%, transparent)) {
  .hero-eyebrow {
    background: var(--surface);
    border-color: var(--separator);
  }
}

/* It sits over the moving hero backdrop, so it is glass rather than a
   solid patch cut out of it. */
@supports (backdrop-filter: blur(12px)) or (-webkit-backdrop-filter: blur(12px)) {
  .hero-eyebrow {
    -webkit-backdrop-filter: blur(12px) saturate(160%);
    backdrop-filter: blur(12px) saturate(160%);
  }
}

/* ---- The dot ---- */

.hero-eyebrow-dot {
  position: relative;
  flex: 0 0 auto;
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--chip);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--chip) 18%, transparent);
}

/* The ring that beats outward. A separate layer, so the dot itself
   stays crisp while only the ring is scaled. */
.hero-eyebrow.is-live .hero-eyebrow-dot::after {
  content: "";
  position: absolute;
  inset: -3px;
  border-radius: 50%;
  border: 1px solid var(--chip);
  animation: chip-beat 2.4s ease-out infinite;
}

@keyframes chip-beat {
  0% {
    opacity: 0.7;
    transform: scale(0.7);
  }

  70% {
    opacity: 0;
    transform: scale(2.1);
  }

  100% {
    opacity: 0;
    transform: scale(2.1);
  }
}

/* ---- The words ---- */

.hero-eyebrow-label {
  color: var(--chip);
  font-weight: 600;
  letter-spacing: 0.02em;
}

.hero-eyebrow-note {
  color: var(--label-2);
}

/* A hairline between the parts, rather than a middle dot that would
   sit on the text baseline and look like punctuation. */
.hero-eyebrow-rule {
  flex: 0 0 auto;
  width: 1px;
  height: 11px;
  background: var(--separator);
}

/* ---- Narrow screens ----
   The explanation goes first, because the state and the scope are the
   parts that actually differ between the two boards. */

@media (max-width: 480px) {
  .hero-eyebrow {
    height: 27px;
    gap: 7px;
    padding: 0 11px;
  }
}

/* Below this the bar cannot hold the name and the button label at
   once, so the label goes. The icon carries it. */
@media (max-width: 430px) {
  .topbar-cta-label {
    display: none;
  }

  .topbar-cta {
    padding: 0 var(--sp-3);
  }
}

/* The chip still says which board this is; it just stops beating. */
@media (prefers-reduced-motion: reduce) {
  .hero-eyebrow.is-live .hero-eyebrow-dot::after {
    animation: none;
    opacity: 0.5;
  }
}

/* -------------------------------------------------------------
   Written pages
   -------------------------------------------------------------
   Rules, Terms and Privacy share one layout. They had drifted apart:
   one numbered its sections by hand and the others did not, the date
   sat loose above the first heading, and a page of a dozen sections
   gave no sense of its own shape.

   A banner across the top, a contents list that sticks beside the
   text on a wide screen, and sections numbered by the stylesheet so
   the three pages agree without anyone maintaining the numbers.
   ------------------------------------------------------------- */

.doc-head {
  padding: var(--sp-8) 0 var(--sp-7);
  margin-bottom: var(--sp-7);
  border-bottom: 1px solid var(--separator);
}

.doc-head-inner {
  max-width: 62ch;
}

.doc-head h1 {
  margin: 0 0 var(--sp-3);
  font-size: var(--fs-title1);
  letter-spacing: -0.03em;
  line-height: 1.1;
}

.doc-intro {
  margin: 0;
  font-size: var(--fs-headline);
  line-height: 1.55;
  color: var(--label-2);
  text-wrap: pretty;
}

.doc-updated {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  margin: var(--sp-5) 0 0;
  padding: 5px 11px 5px 9px;
  border: 1px solid var(--separator);
  border-radius: 999px;
  font-size: var(--fs-caption1);
  color: var(--label-3);
}

.doc-updated .icon {
  width: 14px;
  height: 14px;
}

/* ---- The two columns ---- */

.doc {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--sp-7);
  align-items: start;
  padding-bottom: var(--sp-8);
}

/* ---- Contents ---- */

.doc-nav {
  min-width: 0;
}

.doc-nav-title {
  margin: 0 0 var(--sp-3);
  font-size: var(--fs-caption2);
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--label-3);
}

.doc-nav-list {
  counter-reset: doc-nav;
  margin: 0;
  padding: 0;
  list-style: none;
  border-left: 1px solid var(--separator);
}

.doc-nav-list li {
  counter-increment: doc-nav;
  margin: 0;
}

.doc-nav-list a {
  display: block;
  position: relative;
  padding: 7px 0 7px var(--sp-4);
  margin-left: -1px;
  border-left: 1px solid transparent;
  font-size: var(--fs-footnote);
  line-height: 1.35;
  color: var(--label-2);
  transition: color var(--state), border-color var(--state);
}

.doc-nav-list a::before {
  content: counter(doc-nav) ".";
  margin-right: 7px;
  color: var(--label-3);
  font-variant-numeric: tabular-nums;
}

.doc-nav-list a:hover {
  opacity: 1;
  color: var(--label);
  border-left-color: var(--blue);
}

.doc-nav-list a:hover::before {
  color: var(--blue);
}

/* ---- The text ---- */

.doc-body {
  min-width: 0;
}

/* No number beside the heading. It sat in its own box with a rule
   under it and read as a broken link, and the contents list already
   numbers the sections for anyone who wants to count them. */
.doc-body h2 {
  /* Offset handled by scroll-padding-top on the root. */
}

/* ---- Wide screens ---- */

@media (min-width: 900px) {
  .doc {
    grid-template-columns: 208px minmax(0, 1fr);
    gap: var(--sp-8);
  }

  /* Follows the reader down the page. The offset clears the bar. */
  .doc-nav {
    position: sticky;
    top: 84px;
    max-height: calc(100vh - 110px);
    overflow-y: auto;
    overscroll-behavior: contain;
  }
}

/* ---- Narrow screens ----
   The contents list becomes a block above the text rather than a
   column beside it, and stops sticking, because a sticky panel on a
   short screen takes room the text needs. */

@media (max-width: 899px) {
  .doc-nav {
    padding: var(--sp-4);
    background: var(--surface);
    border: 1px solid var(--separator);
    border-radius: var(--r-lg);
  }

  .doc-nav-list {
    columns: 2;
    column-gap: var(--sp-5);
    border-left: 0;
  }

  .doc-nav-list a {
    padding: 5px 0;
    margin-left: 0;
    border-left: 0;
    break-inside: avoid;
  }
}

@media (max-width: 560px) {
  .doc-head {
    padding: var(--sp-6) 0 var(--sp-5);
    margin-bottom: var(--sp-5);
  }

  .doc-nav-list {
    columns: 1;
  }

  .doc-body h2 {
    gap: var(--sp-2);
  }

  .doc-body h2::before {
    min-width: 22px;
  }
}




/* -------------------------------------------------------------
   How it works
   -------------------------------------------------------------
   Steps on the left, a drawing of the rule on the right.

   The right hand side used to be a ladder of what ranks 1, 3, 10,
   25 and 50 currently cost, read live from the board. That put the
   whole price curve of the market on a public page. The board
   already prints a price beside each row it lists, which is as much
   as a visitor needs; it does not need the shape of the whole thing
   summarised above it.

   What replaced it carries no figures at all. Four bars stand for
   four listings, a fifth arrives at the bottom, and it climbs to the
   top while the others step down to make room. That is the entire
   mechanic, and it gives nothing away.
   ------------------------------------------------------------- */

.how {
  margin-top: var(--sp-9);
  padding-top: var(--sp-8);
  border-top: 1px solid var(--separator);
}

.how-head {
  max-width: 56ch;
  margin-bottom: var(--sp-6);
}

.how-head h2 {
  margin: 0 0 var(--sp-2);
  font-size: var(--fs-title2);
  letter-spacing: -0.03em;
}

.how-head p {
  margin: 0;
  font-size: var(--fs-body);
  color: var(--label-2);
}

.how-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--sp-6);
  align-items: start;
}

/* ---- The steps ---- */

.how-steps {
  margin: 0;
  padding: 0;
  list-style: none;
}

.how-step {
  display: flex;
  gap: var(--sp-4);
  padding: var(--sp-4) 0;
  border-top: 1px solid var(--separator);
}

.how-step:first-child {
  border-top: 0;
  padding-top: 0;
}

.how-step-n {
  flex: 0 0 auto;
  padding-top: 2px;
  font-family: var(--font-mono);
  font-size: var(--fs-caption1);
  font-weight: 600;
  letter-spacing: 0.06em;
  color: var(--label-3);
  font-variant-numeric: tabular-nums;
}

.how-step-text {
  min-width: 0;
}

.how-step h3 {
  margin: 0 0 5px;
  font-size: var(--fs-headline);
  letter-spacing: -0.01em;
}

.how-step p {
  margin: 0;
  font-size: var(--fs-footnote);
  line-height: 1.6;
  color: var(--label-2);
  text-wrap: pretty;
}

/* ---- The drawing ----
   Five rows on a fixed step, so the climb is a whole number of rows
   and nothing lands between two of them. */

.how-demo {
  --row-h: 34px;
  --row-gap: 10px;
  --step: calc(var(--row-h) + var(--row-gap));

  margin: 0;
  padding: var(--sp-5);
  background: var(--surface);
  border: 1px solid var(--separator);
  border-radius: var(--r-lg);
}

/* The frame clips, and the top row sits exactly on its top edge. On a
   screen with a fractional pixel ratio the moving bar lands a sub pixel
   above that edge and its top border was clipped away. Every row is
   inset by --inset, and the frame is that much taller at both ends, so
   no border ever touches the clipping edge. */
.how-demo-frame {
  --inset: 3px;

  position: relative;
  height: calc(var(--step) * 5 - var(--row-gap) + var(--inset) * 2);
  overflow: hidden;
}

.how-demo-row {
  position: absolute;
  left: 0;
  right: 0;
  margin-top: var(--inset);
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  height: var(--row-h);
  will-change: transform;
}

/* The four that are already there. Each sits one step lower than the
   last and their bars shorten, so the stack reads as a ranking without
   a single number on it. */
.how-demo-row-1 {
  top: 0;
}

.how-demo-row-2 {
  top: var(--step);
}

.how-demo-row-3 {
  top: calc(var(--step) * 2);
}

.how-demo-row-4 {
  top: calc(var(--step) * 3);
}

.how-demo-row-you {
  top: calc(var(--step) * 4);
}

.how-demo-rank {
  flex: 0 0 auto;
  width: 22px;
  height: 6px;
  border-radius: 999px;
  background: var(--surface-3);
}

.how-demo-bar {
  height: 100%;
  border-radius: 8px;
  background: var(--surface-2);
  border: 1px solid var(--separator);
}

.how-demo-row-1 .how-demo-bar {
  width: 78%;
}

.how-demo-row-2 .how-demo-bar {
  width: 64%;
}

.how-demo-row-3 .how-demo-bar {
  width: 52%;
}

.how-demo-row-4 .how-demo-bar {
  width: 41%;
}

/* The arriving listing. Blue, so the eye follows it and nothing else. */
.how-demo-row-you .how-demo-bar {
  width: 30%;
  background: var(--blue-tint);
  border-color: var(--blue);
}

.how-demo-row-you .how-demo-rank {
  background: var(--blue);
}

.how-demo-tag {
  flex: 0 0 auto;
  font-size: var(--fs-caption2);
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--blue);
}

/* ---- The climb ----
   One loop. The newcomer rises four rows while its bar grows past the
   others, and the four above it each step down one row to make room.
   Both halves share a timeline so nothing ever overlaps.

   Only transform is animated, apart from the width on the one bar that
   is meant to be growing, so the compositor does nearly all of it. */

@keyframes how-climb {

  0%,
  12% {
    transform: translateY(0);
  }

  46%,
  100% {
    transform: translateY(calc(var(--step) * -4));
  }
}

@keyframes how-grow {

  0%,
  12% {
    width: 30%;
  }

  46%,
  100% {
    width: 88%;
  }
}

@keyframes how-shift {

  0%,
  12% {
    transform: translateY(0);
  }

  46%,
  100% {
    transform: translateY(var(--step));
  }
}

.how-demo-row-you {
  animation: how-climb 7s cubic-bezier(0.65, 0, 0.35, 1) infinite;
}

.how-demo-row-you .how-demo-bar {
  animation: how-grow 7s cubic-bezier(0.65, 0, 0.35, 1) infinite;
}

.how-demo-row-1,
.how-demo-row-2,
.how-demo-row-3,
.how-demo-row-4 {
  animation: how-shift 7s cubic-bezier(0.65, 0, 0.35, 1) infinite;
}

.how-demo-caption {
  margin: var(--sp-4) 0 0;
  padding-top: var(--sp-3);
  border-top: 1px solid var(--separator);
  font-size: var(--fs-caption1);
  line-height: 1.55;
  color: var(--label-2);
}

/* Held at the end of the loop rather than the start: the point of the
   drawing is where the listing ends up, not where it began. */
@media (prefers-reduced-motion: reduce) {

  .how-demo-row-you,
  .how-demo-row-you .how-demo-bar,
  .how-demo-row-1,
  .how-demo-row-2,
  .how-demo-row-3,
  .how-demo-row-4 {
    animation: none;
  }

  .how-demo-row-you {
    transform: translateY(calc(var(--step) * -4));
  }

  .how-demo-row-you .how-demo-bar {
    width: 88%;
  }

  .how-demo-row-1,
  .how-demo-row-2,
  .how-demo-row-3,
  .how-demo-row-4 {
    transform: translateY(var(--step));
  }
}

/* ---- The closing row ---- */

.how-foot {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-4);
  margin-top: var(--sp-6);
  padding-top: var(--sp-5);
  border-top: 1px solid var(--separator);
}

.how-works {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--sp-3);
  min-width: 0;
}

.how-works-title {
  font-size: var(--fs-caption2);
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--label-3);
}

.how-works-list {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-3);
  margin: 0;
  padding: 0;
  list-style: none;
}

/* Marks only, no labels. Seven store names in a row was a sentence
   nobody reads; the logos are recognised without them, and each still
   carries its name for anyone who cannot see it. */
.how-works-list .type-icon-img {
  width: 24px;
  height: 24px;
  display: block;
  opacity: 0.85;
  transition: opacity var(--state), transform var(--state);
}

.how-works-list li:hover .type-icon-img {
  opacity: 1;
  transform: translateY(-2px);
}

.how-cta {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  flex: 0 0 auto;
  height: 44px;
  padding: 0 var(--sp-5);
  border-radius: 999px;
  background: var(--blue);
  color: #FFFFFF;
  font-size: var(--fs-footnote);
  font-weight: 600;
  white-space: nowrap;
  transition: filter var(--state), transform var(--state);
}

.how-cta:hover {
  opacity: 1;
  filter: brightness(1.08);
  transform: translateY(-1px);
}

.how-cta .icon {
  width: 16px;
  height: 16px;
}

.how-note {
  max-width: 66ch;
  margin: var(--sp-4) 0 0;
  font-size: var(--fs-caption2);
  line-height: 1.5;
  color: var(--label-3);
}

/* ---- Wide screens ---- */

@media (min-width: 900px) {
  .how-grid {
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    gap: var(--sp-8);
  }
}

/* ---- Narrow screens ---- */

@media (max-width: 899px) {
  .how {
    margin-top: var(--sp-7);
    padding-top: var(--sp-6);
  }

  .how-demo {
    padding: var(--sp-4);
    --row-h: 30px;
    --row-gap: 9px;
  }
}

@media (max-width: 520px) {
  .how-foot {
    gap: var(--sp-4);
  }

  .how-cta {
    width: 100%;
    justify-content: center;
  }

  .how-demo-tag {
    display: none;
  }
}

@media (prefers-reduced-motion: reduce) {

  .how-cta:hover,
  .how-works-list li:hover .type-icon-img {
    transform: none;
  }
}
/* -------------------------------------------------------------
   Error page
   -------------------------------------------------------------
   Shares the bar and the footer with the rest of the site, so a
   wrong address still leaves the reader somewhere they recognise
   rather than on a bare server page.
   ------------------------------------------------------------- */

.errorpage {
  max-width: 46ch;
  margin: 0 auto;
  padding: var(--sp-9) 0 var(--sp-8);
  text-align: center;
}

/* The number, set large and faint behind the heading. The podium and
   the steps use the same device, so the page still looks like part of
   this site rather than a generic error screen. */
.errorpage-code {
  margin: 0 0 calc(var(--sp-5) * -1);
  font-family: var(--font-mono);
  font-size: clamp(96px, 26vw, 168px);
  font-weight: 700;
  line-height: 1;
  letter-spacing: -0.05em;
  color: var(--label);
  opacity: 0.07;
  user-select: none;
  pointer-events: none;
}

.errorpage h1 {
  position: relative;
  margin: 0 0 var(--sp-3);
  font-size: var(--fs-title2);
  letter-spacing: -0.03em;
}

.errorpage-text {
  margin: 0 0 var(--sp-6);
  font-size: var(--fs-body);
  line-height: 1.6;
  color: var(--label-2);
  text-wrap: pretty;
}

.errorpage-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: var(--sp-3);
}

.errorpage-aside {
  margin: var(--sp-7) 0 0;
  padding-top: var(--sp-5);
  border-top: 1px solid var(--separator);
  font-size: var(--fs-footnote);
  line-height: 1.65;
  color: var(--label-3);
  text-wrap: pretty;
}

.errorpage-aside a {
  color: var(--blue);
}

.errorpage-aside .topbar-kbd {
  display: inline-flex;
  vertical-align: baseline;
  margin: 0 2px;
}

@media (max-width: 520px) {
  .errorpage { padding: var(--sp-7) 0 var(--sp-6); }
  .errorpage-actions { flex-direction: column; align-items: stretch; }
  .errorpage-actions .button { width: 100%; justify-content: center; }
}

/* -------------------------------------------------------------
   Click counts, visitor stats and the launch badge
   ------------------------------------------------------------- */

/* ---- Click counts ----
   A count is a figure about how a listing is doing, so it sits with
   the price, not among the title, description and category. The price
   stays the loudest thing in either place; the count is the quieter
   second figure beside it. */

.clicks-num {
  font-weight: 600;
  color: var(--label);
  font-variant-numeric: tabular-nums;
}

.clicks-word {
  font-weight: 400;
  color: var(--label-3);
}

/* Podium footer: the two figures as a pair, each a value over the same
   caption style, divided by a hairline, bottoms aligned so the captions
   share a baseline. The button keeps its place on the right. */

.podium-stats {
  display: flex;
  align-items: flex-end;
  flex: 1 1 auto;
  min-width: 0;
}

.podium-stats .podium-price {
  flex: 0 1 auto;
}

.podium-clicks {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  flex: 0 0 auto;
  gap: 1px;
  margin-left: var(--sp-4);
  padding-left: var(--sp-4);
  border-left: 1px solid var(--separator);
  line-height: 1.1;
}

/* Smaller than the price above all, and one weight lighter, so the
   eye still lands on what was paid first. */
.podium-clicks-value {
  font-size: var(--fs-headline);
  font-weight: 600;
  color: var(--label);
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.01em;
}

.podium-clicks .podium-amount-label {
  white-space: nowrap;
}

@media (max-width: 520px) {
  .podium-clicks {
    margin-left: var(--sp-3);
    padding-left: var(--sp-3);
  }

  .podium-clicks-value {
    font-size: var(--fs-body);
  }
}

/* Rows, wider screens: the podium footer's pairing again. The price,
   a hairline, then the count over its caption in a column of fixed
   width, so the counts line up down the board the way figures in a
   table do, and the count never reads as part of the price. */

.row-clicks {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  flex: 0 0 auto;
  gap: 1px;
  min-width: 64px;
  padding-left: var(--sp-4);
  border-left: 1px solid var(--separator);
  line-height: 1.1;
  white-space: nowrap;
}

.row-clicks .clicks-num {
  font-size: var(--fs-body);
}

.row-clicks .clicks-word {
  font-size: var(--fs-caption2);
}

.row-meta-clicks {
  display: none;
}

/* The category tag gives way before anything else on the line does. */
.row-meta {
  flex-wrap: nowrap;
}

.row-meta .row-tag-category {
  min-width: 0;
  flex: 0 1 auto;
}

/* Rows, phones: the side column is already price over action, and a
   third figure there would crowd both. The count follows the category
   as plain metadata instead, after a dot, and never truncates. */
@media (max-width: 760px) {
  .row-clicks {
    display: none;
  }

  .row-meta-clicks {
    display: inline-flex;
    align-items: center;
    flex: 0 0 auto;
    gap: 3px;
    font-size: var(--fs-caption2);
    color: var(--label-3);
    white-space: nowrap;
  }

  .row-meta-clicks::before {
    content: "·";
    margin-right: 2px;
    color: var(--label-3);
  }

  .row-meta-clicks .clicks-num {
    color: var(--label-2);
  }
}

/* ---- Hero visitor stats ----
   Reuses the status chip. The numbers are tabular so the chip does not
   shift width by a pixel every time a count ticks over. */

.hero-stats [data-stat] {
  display: inline-block;
  border-radius: 4px;
  font-variant-numeric: tabular-nums;
}

/* A new visitor arriving lights the total for a moment, so the reader
   can see the count is live rather than a number printed once. */
@keyframes stat-bump {
  0%   { background: color-mix(in srgb, var(--green) 28%, transparent); }
  100% { background: transparent; }
}

.hero-stats [data-stat].is-bumped {
  animation: stat-bump 1.6s ease-out;
}

@media (prefers-reduced-motion: reduce) {
  .hero-stats [data-stat].is-bumped { animation: none; }
}

/* ---- Launch pages (Product Hunt, PeerPush) ----
   Built from what the page already uses: the flat card of the how it
   works drawing, a headline set like a step title, secondary text in
   the secondary label colour. No accent colour of its own; the badges
   bring their platforms' colours, and that is plenty. */

.launch {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-5);
  margin-top: var(--sp-8);
  padding: var(--sp-5) var(--sp-6);
  background: var(--surface);
  border: 1px solid var(--separator);
  border-radius: var(--r-lg);
}

.launch-text {
  min-width: 0;
}

.launch h2 {
  margin: 0 0 5px;
  font-size: var(--fs-headline);
  letter-spacing: -0.01em;
}

.launch p {
  margin: 0;
  font-size: var(--fs-footnote);
  line-height: 1.6;
  color: var(--label-2);
  text-wrap: pretty;
}

.launch-badges {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-3);
  flex: 0 0 auto;
  margin: 0;
  padding: 0;
  list-style: none;
}

.launch-badge {
  display: block;
  line-height: 0;
  border-radius: 10px;
}

/* Every badge at the same height, width following its own shape:
   Product Hunt's is 250 by 54, PeerPush's 460 by 130. */
.launch-badge img {
  width: auto;
  height: 54px;
  max-width: 100%;
}

/* The how it works section opens with its own rule and spacing, so it
   closes up behind the card instead of doubling it. */
.launch + .how {
  margin-top: var(--sp-8);
}

/* Two badges need more room beside the sentence than one did, so they
   move under it from tablet width down. */
@media (max-width: 900px) {
  .launch {
    flex-direction: column;
    align-items: flex-start;
    gap: var(--sp-4);
  }
}

/* Phones: the pair is wider than the card, so they stack, left aligned
   with the sentence, rather than shrinking until neither can be read. */
@media (max-width: 760px) {
  .launch {
    margin-top: var(--sp-6);
    padding: var(--sp-5);
  }

  .launch + .how { margin-top: var(--sp-6); }
}

/* -------------------------------------------------------------
   Category picker
   -------------------------------------------------------------
   The claim modal's category field, drawn as a listbox so each
   category carries its icon. The closed field is the ordinary
   .select with the chosen icon in front of the name; the list is a
   popover of the same material as the modal's controls. Icons are
   neutral grey and only the chosen one turns blue, so the list reads
   as names first and the colour marks the answer.
   ------------------------------------------------------------- */

.cat-picker-button {
  display: flex;
  align-items: center;
  gap: 10px;
  min-width: 0;
  text-align: left;
  user-select: none;
}

.cat-picker-button.is-disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

.cat-picker.is-open .cat-picker-button {
  border-color: var(--blue);
  box-shadow: 0 0 0 3px var(--ring);
}

.cat-picker-button .cat-picker-icon {
  color: var(--blue);
}

.cat-picker-value {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.cat-picker-icon {
  display: inline-flex;
  flex: 0 0 auto;
  align-items: center;
  justify-content: center;
  width: 18px;
  height: 18px;
  color: var(--label-2);
}

.cat-picker-icon .icon {
  width: 16px;
  height: 16px;
}

/* The chevron turns over while the list is open. */
.cat-picker .select-arrow .icon {
  transition: transform var(--state);
}

.cat-picker.is-open .select-arrow .icon {
  transform: rotate(180deg);
}

.cat-picker-list {
  position: absolute;
  z-index: 30;
  left: 0;
  right: 0;
  top: calc(100% + 6px);
  max-height: 296px;
  margin: 0;
  padding: var(--sp-1);
  overflow-y: auto;
  overscroll-behavior: contain;
  list-style: none;
  background: var(--elevated);
  border: 1px solid var(--separator);
  border-radius: var(--r-md);
  box-shadow: var(--shadow-3);
}

.cat-picker.opens-up .cat-picker-list {
  top: auto;
  bottom: calc(100% + 6px);
}

.cat-picker-option {
  display: flex;
  align-items: center;
  gap: 10px;
  min-height: 38px;
  padding: 0 10px;
  border-radius: var(--r-sm);
  color: var(--label);
  font-size: var(--fs-footnote);
  cursor: pointer;
}

.cat-picker-name {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* A tint of the text colour rather than a surface token: in dark mode
   --surface-2 is the very colour of the list, so the highlight vanished. */
.cat-picker-option.is-active {
  background: color-mix(in srgb, var(--label) 8%, transparent);
}

.cat-picker-option.is-selected {
  font-weight: 500;
}

.cat-picker-option.is-selected .cat-picker-icon {
  color: var(--blue);
}

/* A tick beside the chosen category, invisible on the rest so every
   name lines up to the same width. */
.cat-picker-check {
  display: inline-flex;
  flex: 0 0 auto;
  color: var(--blue);
  visibility: hidden;
}

.cat-picker-option.is-selected .cat-picker-check {
  visibility: visible;
}

@media (hover: none) {
  .cat-picker-option {
    min-height: 44px;
  }
}

/* -------------------------------------------------------------
   Admin: Original / All
   -------------------------------------------------------------
   A two way segmented switch in the admin bar. The chosen half is a
   raised pill on a sunken track, the same idea as the board switch on
   the public page. "Including manual payments" is repeated on pages
   whose numbers change, so a screenshot of the dashboard never passes
   off manual money as real revenue.
   ------------------------------------------------------------- */

.money-view {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  margin: 0;
}

.money-view-label {
  font-size: var(--fs-caption2);
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--label-3);
}

.money-view-switch {
  display: inline-flex;
  padding: 2px;
  border-radius: var(--r-pill);
  background: var(--surface-2);
  border: 1px solid var(--separator);
}

.money-view-option {
  height: 26px;
  padding: 0 var(--sp-3);
  border: 0;
  border-radius: var(--r-pill);
  background: transparent;
  color: var(--label-2);
  font-family: inherit;
  font-size: var(--fs-caption1);
  font-weight: 500;
  cursor: pointer;
  transition: background var(--state), color var(--state);
}

.money-view-option:hover {
  color: var(--label);
}

.money-view-option.is-on {
  background: var(--elevated);
  color: var(--label);
  box-shadow: var(--shadow-1);
  cursor: default;
}

.view-note {
  display: inline-flex;
  align-items: center;
  height: 26px;
  padding: 0 10px;
  border-radius: var(--r-pill);
  background: var(--tint-orange);
  color: var(--orange);
  font-size: var(--fs-caption1);
  font-weight: 500;
  white-space: nowrap;
}

/* Narrow admin bar: the label goes, the switch stays. */
@media (max-width: 860px) {
  .money-view-label {
    display: none;
  }
}

/* -------------------------------------------------------------
   Admin: payment id with a copy button
   -------------------------------------------------------------
   The id is set in full and trimmed by the column, not by PHP, so a
   wide screen shows all of it; the button copies the whole value
   whatever is visible. After a copy the icon becomes a green tick.
   ------------------------------------------------------------- */

.payid {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  max-width: 100%;
  min-width: 0;
  vertical-align: middle;
}

.payid-text {
  min-width: 0;
  max-width: 190px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: var(--fs-caption1);
  color: var(--label-2);
  user-select: all;
}

.payid-kind {
  flex: 0 0 auto;
  padding: 1px 6px;
  border-radius: 5px;
  background: var(--surface-2);
  color: var(--label-3);
  font-size: var(--fs-caption2);
}

.copy-button {
  position: relative;
  display: inline-grid;
  place-items: center;
  flex: 0 0 auto;
  width: 26px;
  height: 26px;
  padding: 0;
  border: 1px solid transparent;
  border-radius: 7px;
  background: transparent;
  color: var(--label-3);
  cursor: pointer;
  transition: background var(--state), color var(--state), border-color var(--state);
}

.copy-button:hover {
  background: var(--surface-2);
  border-color: var(--separator);
  color: var(--label);
}

.copy-button > span {
  grid-area: 1 / 1;
  display: inline-flex;
  transition: opacity var(--state), transform var(--state);
}

.copy-icon-done {
  opacity: 0;
  transform: scale(0.6);
  color: var(--green);
}

.copy-button.is-copied .copy-icon-idle {
  opacity: 0;
  transform: scale(0.6);
}

.copy-button.is-copied .copy-icon-done {
  opacity: 1;
  transform: none;
}

.copy-button.is-failed {
  color: var(--red);
}

/* -------------------------------------------------------------
   Admin: manual listings and payments
   ------------------------------------------------------------- */

/* The small label that marks anything added by hand, in the form and
   in the payments table alike, so a manual entry is never mistaken for
   money that came through Dodo. */
.manual-tag {
  display: inline-flex;
  align-items: center;
  height: 20px;
  padding: 0 7px;
  border-radius: 6px;
  background: color-mix(in srgb, var(--orange) 16%, transparent);
  color: var(--orange);
  font-size: var(--fs-caption2);
  font-weight: 600;
  letter-spacing: 0.02em;
  white-space: nowrap;
}

@supports not (color: color-mix(in srgb, red 50%, transparent)) {
  .manual-tag { background: var(--surface-2); }
}

.field-optional {
  margin-left: 4px;
  font-weight: 400;
  color: var(--label-3);
}

/* A dollar sign inside the amount field, so the number is read as money. */
.input-money {
  position: relative;
}

.input-money > span {
  position: absolute;
  left: 12px;
  top: 50%;
  transform: translateY(-50%);
  color: var(--label-3);
  font-size: var(--fs-body);
  pointer-events: none;
}

.input-money .input {
  padding-left: 26px;
}

/* ---- The payment block in the Add listing form ---- */

.manual-block {
  margin: var(--sp-4) 0;
  padding: var(--sp-4);
  border: 1px solid var(--separator);
  border-radius: var(--r-md);
  background: var(--surface-2);
}

.manual-block .field-row:last-child .field {
  margin-bottom: 0;
}

.manual-pay-head {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  margin-bottom: var(--sp-3);
}

.manual-pay-head strong {
  font-size: var(--fs-footnote);
  color: var(--label);
}

.manual-more {
  margin: var(--sp-2) 0 var(--sp-3);
}

.manual-more summary {
  cursor: pointer;
  font-size: var(--fs-caption1);
  color: var(--blue);
  padding: var(--sp-2) 0;
}

.manual-more[open] summary {
  margin-bottom: var(--sp-3);
}

.manual-visible {
  margin: var(--sp-2) 0 var(--sp-4);
}

/* ---- The payment form inside the Edit dialog ---- */

.manual-pay {
  margin-top: var(--sp-5);
  padding-top: var(--sp-5);
  border-top: 1px solid var(--separator);
}

.manual-pay-help {
  margin: calc(var(--sp-2) * -1) 0 var(--sp-4);
  font-size: var(--fs-caption1);
  color: var(--label-2);
}

.manual-pay-foot {
  display: flex;
  justify-content: flex-end;
}

/* ---- Payments table ---- */

.cell-manual {
  white-space: nowrap;
}

.manual-note {
  display: inline-block;
  max-width: 180px;
  margin-left: 6px;
  vertical-align: middle;
  overflow: hidden;
  text-overflow: ellipsis;
  font-size: var(--fs-caption1);
  color: var(--label-2);
}

/* The toolbar button sits at the far end, away from the filters. */
.dash-toolbar .create-listing {
  display: inline-flex;
  align-items: center;
  gap: 6px;
}
