/* TicketHub live listings — styles. Locked system: web/DESIGN.md ("Dark sports app", 2026-09-27).
   Every colour and every font family goes through the tokens on :root (hex, from the two approved
   artboards) — never a raw value below. One dark theme. Layouts:
     ≥ 1400px   the artboard grid: Posted · Type · Match · Date·Venue · Qty · Seats · Price · Seller · Notes
     1024–1399  the same grid, Posted under Type; the Notes column from 1200 (below, notes stay in the opened row)
     768–1023   a two-column board of listing cards
     < 768      one column of cards (the phone artboard)
   The same 11 cells of a listing row are placed by grid areas on desktop and by a wrapping flex
   card below 1024px — the DOM never changes with the width. */

/* ---------- fonts (self-hosted woff2, see fonts/SOURCE.txt; CSP stays 'self') ---------- */

@font-face {
  font-family: "Archivo";
  font-style: normal;
  font-display: swap;
  font-weight: 100 900;
  font-stretch: 62% 125%;
  src: url(fonts/archivo-latin-wdth-normal.woff2) format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Archivo";
  font-style: normal;
  font-display: swap;
  font-weight: 100 900;
  font-stretch: 62% 125%;
  src: url(fonts/archivo-latin-ext-wdth-normal.woff2) format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: "Geist Mono";
  font-style: normal;
  font-display: swap;
  font-weight: 100 900;
  src: url(fonts/geist-mono-latin-wght-normal.woff2) format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Geist Mono";
  font-style: normal;
  font-display: swap;
  font-weight: 100 900;
  src: url(fonts/geist-mono-latin-ext-wght-normal.woff2) format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* ---------- tokens ---------- */

:root {
  color-scheme: dark;
  /* ground and surfaces (near-black, one step at a time) */
  --color-bg:          #0A0B0D;
  --color-surface:     #0F1116; /* pending row, hovered / opened row, its detail, status strip */
  --color-surface-2:   #111318; /* cards: phone listing cards, admin cards, the sign-in card */
  --color-field:       #14161B; /* inputs, selects, the segmented filter */
  --color-raised:      #1A1D24; /* count pills, the event disc, hover on a card */
  --color-skeleton:    #1D2129;
  /* rules */
  --color-line:        #1B1E25;
  --color-line-soft:   #16191F; /* between table rows */
  --color-line-card:   #1C1F27; /* card edges */
  --color-line-field:  #20242C;
  --color-line-strong: #2A2F39; /* button and chip outlines */
  --color-line-input:  #5F6675; /* an editor field's edge and an off switch's ring: ≥ 3:1 on the grounds they sit on */
  --color-link-line:   #3A404C; /* the seller link's underline */
  --color-sep:         #4A505C; /* the "·" between inline facts (decoration) */
  /* text */
  --color-ink:    #F4F6F8;
  --color-ink-2:  #DDE2EA;
  --color-muted:  #A5ACB8;
  --color-subtle: #8A919E;
  --color-faint:  #7E8694;
  /* the one brand accent, electric blue: live states, focus, the active filter, links, the Admin badge */
  --color-accent:           #2F80FF;
  --color-accent-hover:     #5696FF;
  --color-accent-ink:       #050A14;
  --color-accent-text:      #7DB3FF;
  --color-accent-soft:      #0F1A2E;
  --color-accent-line:      #1D3257;
  --color-accent-tint:      #0F2344;
  --color-accent-tint-line: #1D3A6B;
  --color-accent-tint-ink:  #CFE0FF;
  --color-flash:            #0E1B33; /* a price that changed across reposts */
  --color-flash-ink:        #8DBDFF;
  --color-focus:            #2F80FF;
  /* offer / request — owner change 2026-09-27: WTS green, WTB red. The green is the lighter of the
     two (colour-blind readers get a lightness step too), and the words stay on every chip. */
  --color-wts:    #4ADE80;
  --color-wts-bg: #0C2A18;
  --color-wtb:    #FF5A67;
  --color-wtb-bg: #3A1219;
  /* WhatsApp's brand green - only the chat glyph beside a seller (owner request 2026-10-04); never a
     WTS / WTB or state colour. 9.6:1 on --color-bg. */
  --color-whatsapp: #25D366;
  /* states — never the WTS / WTB colours; errors also carry an icon and words */
  --color-ok:          #45D3C2;
  --color-ok-line:     #174A45;
  --color-warn:        #F5C451;
  --color-warn-line:   #4A3B14;
  --color-danger:      #FF8A3D;
  --color-danger-line: #52301A;
  --color-pending:     #A5ACB8;
  /* crests, account, charts */
  --color-crest-disc: #EEF1F5; /* behind a dark crest only */
  --color-knob:       #FFFFFF; /* a switch's knob */
  --color-bar:        #6B7382; /* chart bars (≥ 3:1 on the cards) */
  /* the backdrop (owner 2026-10-13: "in the background of the dark page show a football stadium") — the night
     tones of brand/stadium-night.svg, lifted here so the palette stays in one place (an image cannot read a
     custom property: the file repeats them, and test/backdrop.test.mjs compares the two). Never a UI colour.
     Each is chosen so that, laid over the ground at --stadium-strength, the faintest text still has 4.5:1 and
     a field's edge 3:1 on the brightest of them */
  --stadium-sky:     #0C1236; /* the haze of the lights over the far roof */
  --stadium-shade:   #000000; /* under the roofs, the walls behind the top rows */
  --stadium-stand:   #0D1333; /* the lower tiers */
  --stadium-stand-2: #0F183F; /* the upper tiers */
  --stadium-line:    #202C62; /* the roof's edge, the balconies, the rows, the pylons */
  --stadium-pitch:   #002206; /* the grass */
  --stadium-pitch-2: #002707; /* … and its lighter mown stripes */
  --stadium-mark:    #213624; /* the pitch markings, the goal */
  --stadium-lamp:    #12229C; /* the floodlights' lamps */
  --stadium-glow:    #0D1879; /* the glow around them */
  --stadium-strength: 0.4;    /* behind the feed, the admin panel, an event page: text stands right on the picture */
  --stadium-strength-gate: 0.7; /* behind the sign-in screen: only the card stands on it */
  /* "My Listing" (owner 2026-10-13: "a listing the user posted — highlight the WHOLE ROW in yellow, in addition
     to the label"): the viewer's own row / card — a yellow ground, a full yellow ring, a yellow chip. A family
     of its own: a lemon yellow, never --color-warn (that amber is a STATE; an own row is nobody's warning),
     and never a control's colour. The two grounds are as yellow as the page's FAINTEST text allows: every text
     token keeps 4.5:1 on both (test/home-style.test.mjs does the sum) */
  --mine:          #FFE83F; /* the chip's word; the ring of an own row under the pointer */
  --mine-line:     #A39100; /* the ring of an own row / card (6:1 on the ground: a line nobody misses) */
  --mine-bg:       #1E1B00; /* the row's / the card's ground */
  --mine-bg-hover: #211E00; /* … under the pointer or the keyboard */
  --mine-chip:     #383200; /* the chip's fill */

  /* type: Archivo (variable width + weight) for everything, Geist Mono for times, dates, seats */
  --font-sans: "Archivo", system-ui, -apple-system, "Segoe UI", Roboto, "Noto Sans", "Noto Sans Hebrew", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Noto Color Emoji";
  --font-mono: "Geist Mono", ui-monospace, "SF Mono", Menlo, Consolas, "Noto Sans Hebrew", monospace;
  --wdth-display: 118%; /* the page title */
  --wdth-strong: 110%;  /* team names, day labels */
  --wdth-num: 108%;     /* prices, quantities */
  --wdth-counter: 112%; /* today's counters, stat tiles, the sign-in title */
  --wdth-tight: 88%;    /* a team name on a narrow match card (upgrade 2026-10-11): the word fits its column */
  --text-2xs: 0.6875rem; /* 11 */
  --text-xs: 0.75rem;    /* 12 */
  --text-xs-plus: 0.78125rem; /* 12.5 — venue, event kind, seat values */
  --text-sm: 0.8125rem;  /* 13 */
  --text-md: 0.875rem;   /* 14 — body */
  --text-lg-minus: 0.96875rem; /* 15.5 — team names on cards */
  --text-lg: 1rem;       /* 16 — team names */
  --text-xl: 1.125rem;   /* 18 — quantity */
  --text-2xl: 1.375rem;  /* 22 — price */
  --text-3xl: 1.75rem;   /* 28 — counters */
  --text-4xl: 2.125rem;  /* 34 — page title */

  /* spacing (4-pt, with the artboards' 6 / 10 / 14) */
  --space-1: 0.125rem;
  --space-2: 0.25rem;
  --space-3: 0.375rem;
  --space-4: 0.5rem;
  --space-5: 0.625rem;
  --space-6: 0.75rem;
  --space-7: 0.875rem;
  --space-8: 1rem;
  --space-9: 1.25rem;
  --space-10: 1.5rem;
  --space-11: 2rem;
  --space-12: 3rem;
  --gutter: 2rem;
  --page-max: 100rem; /* main's width cap; the header and the demo banner line up with it */
  /* the inline padding that puts full-bleed bars' content on main's edges (100% = the bar's width) */
  --page-pad: max(var(--gutter), calc((100% - var(--page-max)) / 2 + var(--gutter)));
  --header-h: 0px; /* set by app.js from the header's real height */
  --thead-h: 0px;  /* set by app.js: the sticky table header's height (desktop only) */
  /* rhythm (owner 2026-10-13: "bigger spacing between the rows" / "… between the data in the top part of the
     page"): the air of a row, of a card and between the bands above the table — every rule takes these, on
     the scale above (DESIGN.md "Rhythm 2026-10-13") */
  --band-gap: var(--space-9);       /* 20 — between the bands above the table: status · hot rail · filters · tabs (were 8–10) */
  --band-gap-tight: var(--space-6); /* 12 — inside one band: the filter row · the leagues (were 6–8) */
  --item-gap: var(--space-5);       /* 10 — between the chips / pills of a rail (was 8) */
  --row-pad: var(--space-6);        /* 12 — a desktop row's air above and below (was 6) */
  --row-min: 5.25rem;               /* 84 — a desktop listing row at least (was 68) */
  --row-pad-top: var(--space-8);    /* 16 — a top-aligned row: Skipped (was 12) */
  --cell-pad: var(--space-7);       /* 14 — a cell of the event page's table (was 10) */
  --card-pad-top: var(--space-7);   /* 14 — a listing card, above its first line (was 10) */
  --card-pad-bottom: var(--space-8);/* 16 — … and under its last (was 12) */
  --card-line: var(--space-5);      /* 10 — between a card's lines (was 8) */
  --card-gap: var(--space-6);       /* 12 — between cards in one column (was 8); the two-column board: 16 */
  /* upgrade 2026-10-11: the Matches view, the rails, the row's type notch */
  --card-min: 21rem;    /* a match card's narrowest; the Matches grid fills each row with as many as fit */
  --rail-label: 8.5rem; /* the label column of the rails (desktop) */
  --chip-h: 2.25rem;    /* a league chip (44px on phones) */
  --notch-w: 3px;       /* the WTS / WTB notch on a row's leading edge */

  --radius-xs: 5px;
  --radius-chip: 6px;
  --radius-sm: 8px;
  --radius-seg: 9px;
  --radius-btn: 10px;
  --radius-control: 12px;
  --radius-card: 16px;
  --radius-panel: 20px;
  --radius-pill: 999px;

  --ease-out: cubic-bezier(0.16, 1, 0.3, 1);
  --ease-in-out: cubic-bezier(0.65, 0, 0.35, 1);
  --dur-micro: 120ms;
  --dur-short: 220ms;
  --dur-flash: 1400ms;
  --dur-pulse: 1600ms;
  --dur-spin: 900ms;

  /* icons drawn as CSS masks (in currentColor), so a row never holds an element the message did not ask for */
  --icon-repost: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M17 2l3 3-3 3'/%3E%3Cpath d='M4 11V9a4 4 0 0 1 4-4h12'/%3E%3Cpath d='M7 22l-3-3 3-3'/%3E%3Cpath d='M20 13v2a4 4 0 0 1-4 4H4'/%3E%3C/svg%3E");
  --icon-down: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M12 5v14'/%3E%3Cpath d='M6 13l6 6 6-6'/%3E%3C/svg%3E");
  --icon-up: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M12 19V5'/%3E%3Cpath d='M6 11l6-6 6 6'/%3E%3C/svg%3E");
  --icon-calendar: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='4' y='5' width='16' height='15' rx='2'/%3E%3Cpath d='M4 10h16'/%3E%3Cpath d='M9 3v4'/%3E%3Cpath d='M15 3v4'/%3E%3C/svg%3E");
  --icon-shield: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linejoin='round'%3E%3Cpath d='M12 3l7 3v5.5c0 4.2-2.9 7.9-7 9.5-4.1-1.6-7-5.3-7-9.5V6l7-3z'/%3E%3C/svg%3E");
  --icon-chevron: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E");
  --icon-check: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M5 12l4 4 10-10'/%3E%3C/svg%3E");
  --icon-flag: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.9' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M5 21V4'/%3E%3Cpath d='M5 4h11l-2 4 2 4H5'/%3E%3C/svg%3E");
  --icon-refresh: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.9' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M20 11a8 8 0 0 0-14.3-4.9L4 8'/%3E%3Cpath d='M4 4v4h4'/%3E%3Cpath d='M4 13a8 8 0 0 0 14.3 4.9L20 16'/%3E%3Cpath d='M20 20v-4h-4'/%3E%3C/svg%3E");
  --icon-edit: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.9' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M4 20h4L19 9l-4-4L4 16v4z'/%3E%3Cpath d='M13.5 6.5l4 4'/%3E%3C/svg%3E");
  --icon-alert: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='9'/%3E%3Cpath d='M12 7.5v5.5'/%3E%3Cpath d='M12 16.5v.01'/%3E%3C/svg%3E");
  --icon-logout: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.9' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4'/%3E%3Cpath d='M16 17l5-5-5-5'/%3E%3Cpath d='M21 12H9'/%3E%3C/svg%3E");
  /* the heat index (SPEC §9): a filled flame — the Demand gauge and the 📊 button of an event "On fire" */
  --icon-flame: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill='%23000' d='M12.6 2c.5 3.3-.9 5.3-2.6 7.1C8.2 11 6 13.1 6 16.2A6 6 0 0 0 12 22a6 6 0 0 0 6-5.9c0-2.6-1.3-4.6-2.8-6.2-.3 1.5-1.1 2.6-2.4 3.2.5-3.9-.1-8.4-.2-11.1z'/%3E%3C/svg%3E");
  --icon-info: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='9'/%3E%3Cpath d='M12 11v5.5'/%3E%3Cpath d='M12 7.5v.01'/%3E%3C/svg%3E");
  /* upgrade 2026-10-11: the "clear" cross of a picked event's rail chip */
  --icon-close: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2.4' stroke-linecap='round'%3E%3Cpath d='M6 6l12 12'/%3E%3Cpath d='M18 6L6 18'/%3E%3C/svg%3E");
}

/* ---------- base ---------- */

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

html {
  -webkit-text-size-adjust: 100%;
  overflow-x: clip;
  background-color: var(--color-bg);
  /* focus scrolled into view never lands under the sticky header (+ the sticky table header) */
  scroll-padding-top: calc(var(--header-h) + var(--thead-h) + var(--space-4));
}

body {
  margin: 0;
  overflow-x: clip;
  min-height: 100dvh;
  display: flex;
  flex-direction: column;
  isolation: isolate; /* body::before (the backdrop, z-index -1) stays above this ground and under the content */
  color: var(--color-ink);
  background-color: var(--color-bg);
  font: 400 var(--text-md)/1.45 var(--font-sans);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

/* The backdrop (owner 2026-10-13): a football ground at night — original artwork, brand/stadium-night.svg —
   FIXED behind the whole page, the sign-in screen too. A layer of its own, never background-attachment: fixed
   (that repaints the page on every scroll): it is painted once and only composited after that. 100lvh: a
   phone's toolbar sliding away does not resize the picture. `cover` + centre: the picture's middle third is a
   whole ground by itself — what a phone held upright shows. Quiet by design: at --stadium-strength the
   brightest line of it is 1.13:1 on the ground, so every text colour keeps its AA contrast where it stands
   right on the page (test/backdrop.test.mjs does the sum); behind the sign-in screen, where only the card
   stands on it, it is shown stronger. Not animated, no filter; gone in print, in forced colours and for a
   reader who asked for more contrast. Decoration: a pseudo-element, nothing for a screen reader to meet. */
body::before {
  content: ""; position: fixed; z-index: -1; top: 0; left: 0; width: 100%; height: 100vh; height: 100lvh;
  background: url(brand/stadium-night.svg) center / cover no-repeat;
  opacity: var(--stadium-strength);
  pointer-events: none;
}
body.gate-on::before { opacity: var(--stadium-strength-gate); }
/* … but not on a phone: the sign-in card covers the picture there and only a 16px sliver shows either side of
   it — at the stronger strength those slivers read as striped page edges, not as a ground (review 2026-10-13) */
@media (max-width: 767.98px) {
  body.gate-on::before { opacity: var(--stadium-strength); }
}
@media print {
  body::before { display: none; }
}
@media (forced-colors: active), (prefers-contrast: more) {
  body::before { display: none; }
}

.num, .count, .digits { font-variant-numeric: tabular-nums; }
.muted { color: var(--color-muted); }
.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;
}
[hidden] { display: none !important; }

button, input, select, textarea { font: inherit; color: inherit; }
h1, h2, h3, h4 { font-style: normal; }
a { color: var(--color-ink); }
a:hover { color: var(--color-accent-text); }

:focus-visible {
  outline: 2px solid var(--color-focus);
  outline-offset: 2px;
}

/* an icon mask: the element sizes it, currentColor paints it */
.tag-reposts::before, .price-hist::before, .ev-icon::before, .crest-none::before, .select::after,
.btn.has-icon::before, .status-warn::before, .feed-error::before, .action-msg.is-error::before,
.av-note.is-error::before, .demo-banner p::before {
  content: "";
  flex: none;
  display: inline-block;
  background-color: currentColor;
  -webkit-mask: var(--icon-info) center / contain no-repeat;
  mask: var(--icon-info) center / contain no-repeat;
}

/* ---------- buttons (all eight states) ---------- */

.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--space-4);
  min-height: 2.5rem; padding: 0 var(--space-8);
  border: 1px solid var(--color-line-strong); border-radius: var(--radius-btn);
  background: var(--color-surface-2); color: var(--color-ink);
  font: 600 var(--text-md)/1 var(--font-sans);
  white-space: nowrap; cursor: pointer;
  transition: background-color var(--dur-micro) var(--ease-out), border-color var(--dur-micro) var(--ease-out), transform var(--dur-micro) var(--ease-out);
}
.btn:hover { background: var(--color-raised); border-color: var(--color-link-line); }
.btn:active { transform: translateY(1px); }
.btn:disabled, .btn[aria-disabled="true"] { opacity: 0.5; cursor: not-allowed; transform: none; }
.btn[aria-busy="true"] { cursor: progress; opacity: 0.75; }
.btn[data-state="error"] { border-color: var(--color-danger); color: var(--color-danger); }
.btn[data-state="success"] { border-color: var(--color-ok); color: var(--color-ok); }
.btn-primary { background: var(--color-accent); border-color: var(--color-accent); color: var(--color-accent-ink); font-weight: 700; }
.btn-primary:hover { background: var(--color-accent-hover); border-color: var(--color-accent-hover); }
.btn-secondary { background: var(--color-surface-2); }
.btn-sm { min-height: 2.25rem; padding: 0 var(--space-6); font-size: var(--text-sm); border-radius: var(--radius-sm); }
.btn.has-icon::before { width: 1rem; height: 1rem; }
.btn.icon-flag::before { -webkit-mask-image: var(--icon-flag); mask-image: var(--icon-flag); }
.btn.icon-refresh::before { -webkit-mask-image: var(--icon-refresh); mask-image: var(--icon-refresh); }
.btn.icon-edit::before { -webkit-mask-image: var(--icon-edit); mask-image: var(--icon-edit); }

.btn-link, .btn.btn-link {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--space-2);
  min-height: 0; padding: var(--space-2) var(--space-1);
  background: none; border: 0; border-radius: var(--radius-xs);
  color: var(--color-ink-2); font: 600 var(--text-md)/1.2 var(--font-sans);
  white-space: nowrap; cursor: pointer;
}
.btn-link:hover { color: var(--color-accent-text); text-decoration: underline; text-underline-offset: 4px; }
.btn-link:active { opacity: 0.75; }
.btn-link:disabled { opacity: 0.5; cursor: not-allowed; text-decoration: none; }
.btn-link.is-danger { color: var(--color-ink-2); }
.btn-link.is-danger:hover { color: var(--color-danger); }

/* ---------- the header ---------- */

.topbar {
  position: sticky; top: 0; z-index: 10;
  display: flex; align-items: center; gap: var(--space-9);
  min-height: 4rem;
  padding: var(--space-2) max(var(--page-pad), env(safe-area-inset-right)) var(--space-2) max(var(--page-pad), env(safe-area-inset-left));
  background: var(--color-bg);
  border-bottom: 1px solid var(--color-line);
}
.tb-brand { display: flex; align-items: center; gap: var(--space-9); min-width: 0; flex: 1 1 auto; }
/* the page's h1: the logo (its alt) + " live listings" read, not shown */
.brand { display: flex; align-items: center; flex: none; margin: 0; font: inherit; }
/* the logo is the way home (owner 2026-10-13): a link the size of the tile — the page's own focus ring, a dim
   under the pointer and the press (no motion) */
.brand-link { display: block; position: relative; border-radius: var(--radius-btn); }
.brand-link:hover { opacity: 0.85; }
.brand-link:active { opacity: 0.7; }
/* the original app logo: a square tile with its own rounded corners (transparent outside them) */
.brand-slot { display: block; height: 40px; }
.brand-logo { display: block; width: 40px; height: 40px; }
.tb-divider { flex: none; width: 1px; height: 24px; background: var(--color-line-strong); }

/* the LIVE pill: blue while the runner and WhatsApp are fresh, then its state in words */
.live {
  display: inline-flex; align-items: center; gap: var(--space-5);
  height: 30px; min-width: 0; padding: 0 var(--space-6) 0 11px;
  border-radius: var(--radius-pill);
  background: var(--color-surface); border: 1px solid var(--color-line-strong);
  white-space: nowrap; overflow: hidden; /* squeezed, it clips inside its pill — never over the header */
}
.led { flex: none; width: 8px; height: 8px; border-radius: 50%; background: var(--color-faint); }
.live-word { font: 800 var(--text-xs)/1 var(--font-sans); letter-spacing: 0.1em; text-transform: uppercase; color: var(--color-ink); }
.live-last { min-width: 0; overflow: hidden; text-overflow: ellipsis; font-size: var(--text-sm); color: var(--color-muted); }
.live-last-value { color: var(--color-ink); font-weight: 600; }
.live[data-level="live"] { background: var(--color-accent-soft); border-color: var(--color-accent-line); }
.live[data-level="live"] .led { background: var(--color-accent); animation: pulse var(--dur-pulse) var(--ease-in-out) infinite; }
.live[data-level="warn"] { border-color: var(--color-warn-line); }
.live[data-level="warn"] .led { background: var(--color-warn); }
.live[data-level="down"] { border-color: var(--color-danger-line); }
.live[data-level="down"] .led { background: var(--color-danger); }
.live[data-level="down"] .live-word { color: var(--color-danger); }
body:not(.app-on) .live, body:not(.app-on) .tb-divider { visibility: hidden; } /* nothing to report before sign-in */

.sb-side { display: flex; align-items: center; gap: var(--space-8); min-width: 0; flex: none; }
.zone { display: inline-flex; align-items: center; gap: var(--space-4); font-size: var(--text-sm); color: var(--color-muted); white-space: nowrap; }
.zone-glyph { width: 16px; height: 16px; flex: none; }
.zone-clock { font-family: var(--font-mono); color: var(--color-ink); }
.zone-clock:empty { display: none; }
.zone:has(.zone-clock:empty) { display: none; } /* no "Israel time" without its time while loading */
.user { display: flex; align-items: center; gap: var(--space-6); min-width: 0; }
/* The guest view (app.js GUEST_OPTION, owner 2026-10-12): the quiet "Guest view" mark beside the LIVE pill while
   the feed is shown to a guest — outlined and muted like a row tag, no colour of its own — and the header's
   "Sign in" for a signed-out guest: a link button (44px on phones) */
.guest-tag {
  display: inline-flex; align-items: center; flex: none; height: 24px; padding: 0 var(--space-5);
  border-radius: var(--radius-pill); border: 1px solid var(--color-line-strong);
  font: 700 var(--text-2xs)/1 var(--font-sans); letter-spacing: 0.08em; text-transform: uppercase;
  color: var(--color-muted); white-space: nowrap;
}
.btn-signin { font-size: var(--text-sm); }

.btn-admin {
  position: relative;
  display: inline-flex; align-items: center; gap: var(--space-4);
  height: 36px; padding: 0 var(--space-4) 0 var(--space-6);
  border: 1px solid var(--color-line-strong); border-radius: var(--radius-btn);
  background: var(--color-surface-2); color: var(--color-ink);
  font: 600 var(--text-md)/1 var(--font-sans);
  white-space: nowrap; cursor: pointer;
  transition: background-color var(--dur-micro) var(--ease-out), transform var(--dur-micro) var(--ease-out);
}
.btn-admin:hover { background: var(--color-raised); }
.btn-admin:active { transform: translateY(1px); }
.btn-admin:disabled { opacity: 0.5; cursor: not-allowed; }
.btn-admin[aria-expanded="true"] { background: var(--color-accent-tint); border-color: var(--color-accent-tint-line); color: var(--color-accent-tint-ink); }
.admin-glyph { width: 16px; height: 16px; flex: none; }
/* the open-flags badge: the brand blue (DESIGN.md accent discipline) */
.admin-badge {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 20px; height: 20px; padding: 0 var(--space-3);
  border-radius: var(--radius-pill);
  background: var(--color-accent); color: var(--color-accent-ink);
  font: 800 var(--text-2xs)/1 var(--font-sans);
}

/* ---------- demo banner + loading bar ---------- */

.demo-banner {
  padding: var(--space-4) var(--page-pad);
  background: var(--color-surface); color: var(--color-muted);
  border-bottom: 1px solid var(--color-line);
  font-size: var(--text-sm);
}
.demo-banner p { margin: 0; max-width: 90ch; }
.demo-banner p::before { width: 14px; height: 14px; margin-right: var(--space-4); vertical-align: -2px; color: var(--color-warn); }
.demo-banner strong { color: var(--color-warn); font-weight: 700; }
.demo-banner a { color: var(--color-ink); font-weight: 600; text-underline-offset: 3px; }

.progress { padding: 0; }
.progress-track { height: 2px; background: var(--color-line); overflow: hidden; }
.progress-fill {
  height: 100%; width: 100%;
  background: var(--color-accent);
  transform-origin: left center; transform: scaleX(0.06);
  transition: transform var(--dur-short) var(--ease-out);
}
.progress:not(.done):not(.failed) .progress-fill { animation: loading-pulse var(--dur-pulse) var(--ease-in-out) infinite; }
.progress.done .progress-fill { background: var(--color-ok); }
.progress.failed .progress-fill { background: var(--color-danger); }
.progress-text { padding: var(--space-2) var(--page-pad) var(--space-4); font-size: var(--text-xs); color: var(--color-muted); }

/* ---------- layout ---------- */

main {
  flex: 1;
  width: 100%;
  max-width: var(--page-max);
  margin: 0 auto;
  padding: 0 max(var(--gutter), env(safe-area-inset-right)) var(--space-11) max(var(--gutter), env(safe-area-inset-left));
}

/* ---------- the title + today's counters ---------- */

.hero {
  display: flex; flex-wrap: wrap; align-items: flex-end; justify-content: space-between; gap: var(--space-8) var(--space-11);
  /* (2026-10-13: the status strip right under it belongs to the same group — "today" — so what stands between
     them is the gap INSIDE a band, not a loose 16px) */
  padding: var(--space-9) 0 var(--band-gap-tight);
}
body:not(.app-on) .hero { display: none; } /* before sign-in the card is the page */
.hero-text { flex: 1 1 14.5rem; min-width: 0; } /* gives way (its line wraps) before the counters drop under it */
.page-title {
  margin: 0;
  font: 800 var(--text-4xl)/1.12 var(--font-sans); font-stretch: var(--wdth-display);
  letter-spacing: -0.02em;
}
/* (balance: where the sentence must wrap — beside the counters on a small desktop — its lines are even; a last
   line is never one lone word) */
.page-sub { margin: var(--space-3) 0 0; font-size: var(--text-md); line-height: 1.43; color: var(--color-muted); text-wrap: balance; }

.sb-counters { display: flex; flex-wrap: wrap; align-items: flex-start; position: relative; }
/* "Today" over the counters (and, where the header has no room for it, the zone) */
.counters-cap { flex: 0 0 100%; margin: 0 0 var(--space-4); padding-left: var(--space-10); font-size: var(--text-xs); font-weight: 800; color: var(--color-ink); }
.cap-zone { display: none; font-weight: 400; color: var(--color-subtle); }
.counters { display: flex; margin: 0; }
.counter {
  display: flex; flex-direction: column-reverse; gap: var(--space-4);
  padding: 0 var(--space-10); border-left: 1px solid var(--color-line);
  min-width: 0;
}
.counter dt { display: flex; align-items: center; gap: var(--space-3); font-size: var(--text-xs); color: var(--color-muted); white-space: nowrap; }
.counter dd { margin: 0; }
.digits { font: 800 var(--text-3xl)/1 var(--font-sans); font-stretch: var(--wdth-counter); color: var(--color-ink); }
.counter-wts .digits { color: var(--color-wts); }
.counter-wtb .digits { color: var(--color-wtb); }
.split-group { display: grid; gap: var(--space-4); }
.split .split-wts, .split .split-wtb { display: block; min-width: 0; height: 100%; }
.split {
  display: flex; gap: 2px; height: 4px; margin-left: var(--space-10);
  border-radius: var(--radius-pill); overflow: hidden; background: var(--color-line);
}
.split-wts { flex: var(--wts, 0) 1 0; background: var(--color-wts); }
.split-wtb { flex: var(--wtb, 0) 1 0; background: var(--color-wtb); }
/* desktop (rhythm 2026-10-13): "Today" stands in FRONT of its numbers, on the digits' own line — the row's
   label, not a line of its own over it (25px the bands under it now breathe with); from 1366px, where the
   title leaves the room, each number gets more air around it (it was from 1200px: on a 1280px laptop the
   wider numbers pushed the title's one sentence onto a second line, with one word on it) */
@media (min-width: 1024px) {
  .counters-cap { flex: none; margin: 0; padding: 0 var(--space-10) 0 0; line-height: var(--text-3xl); }
}
@media (min-width: 1366px) {
  .counters-cap { padding-right: var(--space-11); }
  .counter { padding: 0 var(--space-11); }
  .split { margin-left: var(--space-11); }
}

/* the WTS / WTB key dot (filters, counters) */
.key { flex: none; display: inline-block; width: 6px; height: 6px; border-radius: 50%; }
.key-wts { background: var(--color-wts); }
.key-wtb { background: var(--color-wtb); }

/* ---------- the sign-in card ---------- */

.gate { display: flex; justify-content: center; padding: 10vh 0 var(--space-12); }
.card {
  width: 100%; max-width: 26rem;
  background: var(--color-surface-2);
  border: 1px solid var(--color-line-card);
  border-radius: var(--radius-panel);
  overflow: hidden;
}
.card-brand {
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: var(--space-6);
  padding: var(--space-11) var(--space-10) var(--space-10);
  border-bottom: 1px solid var(--color-line);
}
.card-brand-slot { display: flex; align-items: center; justify-content: center; max-width: 100%; }
.card-logo { display: block; width: 88px; height: 88px; max-width: 100%; }
.card-kicker { font: 700 var(--text-xs)/1 var(--font-sans); letter-spacing: 0.12em; text-transform: uppercase; color: var(--color-muted); }
.card-body { padding: var(--space-10); }
.card h2 { margin: 0 0 var(--space-4); font: 800 var(--text-2xl)/1.2 var(--font-sans); font-stretch: var(--wdth-counter); letter-spacing: -0.01em; overflow-wrap: anywhere; }
.card p { margin: 0 0 var(--space-4); color: var(--color-muted); overflow-wrap: anywhere; }
.card-actions { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-6); margin-top: var(--space-10); }
/* the slot keeps its line while empty, so a message appearing never moves the card */
.card-error { margin: var(--space-6) 0 0; min-height: 1.45em; min-height: 1lh; color: var(--color-danger); font-size: var(--text-md); }

/* ---------- status strip ---------- */

.status {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: var(--space-2) var(--space-8);
  padding: var(--space-5) var(--space-8); margin-bottom: var(--band-gap);
  background: var(--color-surface); border: 1px solid var(--color-line); border-radius: var(--radius-control);
  font-size: var(--text-sm);
}
.status-line { display: flex; align-items: center; gap: var(--space-4); flex-wrap: wrap; min-width: 0; }
.status-head { font-weight: 600; color: var(--color-ink); }
.status-head.muted { color: var(--color-muted); font-weight: 500; }
.status[data-level="down"] .status-head { color: var(--color-danger); }
.status-info, .status-today { color: var(--color-muted); font-size: var(--text-xs); }
.status-today { font-variant-numeric: tabular-nums; }
.status-today b { color: var(--color-ink); font-weight: 600; }
.status-warn {
  flex-basis: 100%;
  display: flex; gap: var(--space-4); align-items: flex-start;
  color: var(--color-warn);
}
.status-warn::before { width: 1rem; height: 1rem; margin-top: var(--space-1); -webkit-mask-image: var(--icon-alert); mask-image: var(--icon-alert); }
.status-warn > span:last-child { max-width: 75ch; }
.warn-icon { display: none; } /* the icon is the ::before mask; the "!" stays out of the reading order */
.dot { width: 8px; height: 8px; border-radius: 50%; flex: none; background: var(--color-faint); }
.dot-live { background: var(--color-accent); }
.dot-warn { background: var(--color-warn); }
.dot-down { background: var(--color-danger); }

/* ---------- filters ---------- */

.filters { display: flex; flex-wrap: wrap; align-items: center; gap: var(--band-gap-tight) var(--space-6); margin-bottom: var(--band-gap); }
/* one row on wide screens: the rail's children join the form's row, in DOM order (search, "My listings",
   the window, the switches) — never reordered, so Tab follows the eye. (All / WTS / WTB is in the view bar
   since 2026-10-13, the currency select is gone: the search takes the room they left — below) */
.filter-rail { display: contents; }
.filters-gap { flex: 1 1 0; }
/* the two switches are ONE item of the row (review 2026-10-12): when the row is too narrow for both they
   move to the next line TOGETHER, at its right end — never one left alone at the left edge of a line of
   its own while the other stays right-aligned above it (with "My listings" in the row that happened at
   1208–1352px, i.e. on a 1280px laptop) */
.checks { display: inline-flex; flex: none; align-items: center; gap: var(--space-6); margin-left: auto; }

/* the page's first control: it grows into the row (to 36rem — a line of search, not a banner), so the band is
   never half empty between the filters and the two switches at its end. Its grow factor is 100 to the
   spacer's 1: the field takes the row's free room FIRST, and only what is left stands before the switches */
.search { position: relative; flex: 100 1 21.25rem; min-width: 12rem; max-width: 36rem; }
.search-icon {
  position: absolute; left: 13px; top: 50%; width: 18px; height: 18px;
  transform: translateY(-50%); color: var(--color-subtle); pointer-events: none;
}
.search input, .select select {
  width: 100%; height: 2.5rem; padding: 0 var(--space-7);
  background: var(--color-field); border: 1px solid var(--color-line-field); border-radius: var(--radius-control);
  color: var(--color-ink); font-size: var(--text-md);
}
.search input { padding-left: 2.5rem; }
.search input::placeholder { color: var(--color-subtle); }
/* an empty search field keeps no room for its clear button (it shows once something is typed): the hint has
   the whole field — at the field's narrowest that room is what kept the hint's last word whole */
.search input:placeholder-shown::-webkit-search-cancel-button { display: none; }
.search input:hover, .select select:hover { border-color: var(--color-line-strong); }
.search input:disabled, .select select:disabled { opacity: 0.55; cursor: not-allowed; }
.select { position: relative; display: block; flex: none; }
.select select { appearance: none; -webkit-appearance: none; width: auto; padding-right: 2.25rem; font-weight: 500; cursor: pointer; }
.select::after {
  position: absolute; right: 13px; top: 50%; width: 16px; height: 16px; margin-top: -8px;
  color: var(--color-muted); pointer-events: none;
  -webkit-mask-image: var(--icon-chevron); mask-image: var(--icon-chevron);
}
.select option { background: var(--color-field); color: var(--color-ink); }

.seg {
  display: inline-flex; gap: 2px; padding: 3px; flex: none;
  border-radius: var(--radius-control); background: var(--color-field); border: 1px solid var(--color-line-field);
}
.seg button {
  display: inline-flex; align-items: center; gap: var(--space-4);
  height: 34px; padding: 0 var(--space-7); border: 0; border-radius: var(--radius-seg);
  background: none; color: var(--color-ink-2); cursor: pointer; white-space: nowrap;
  font: 600 var(--text-md)/1 var(--font-sans);
}
.seg button:hover { background: var(--color-raised); color: var(--color-ink); }
.seg button:active { transform: translateY(1px); }
.seg button:disabled { opacity: 0.5; cursor: not-allowed; }
.seg button[aria-pressed="true"] { background: var(--color-accent); color: var(--color-accent-ink); font-weight: 700; }
/* the key dot keeps its colour on the blue pill, ringed so it reads there */
.seg button[aria-pressed="true"] .key { box-shadow: 0 0 0 1.5px var(--color-accent-ink); }

/* a switch: the checkbox keeps its semantics (role="switch"), drawn as a track and a knob */
.check {
  display: inline-flex; align-items: center; gap: var(--space-5); flex: none;
  height: 2.5rem; padding: 0 var(--space-2);
  color: var(--color-ink-2); cursor: pointer; user-select: none; white-space: nowrap;
  font-size: var(--text-md);
}
.check:hover { color: var(--color-ink); }
.check input, .switch input {
  appearance: none; -webkit-appearance: none; flex: none; margin: 0;
  width: 32px; height: 18px; border-radius: var(--radius-pill); cursor: pointer;
  background: radial-gradient(circle at 9px 50%, var(--color-knob) 0 6.5px, transparent 7px) no-repeat, var(--color-line-strong);
  box-shadow: inset 0 0 0 1px var(--color-line-input); /* an off track still has a 3:1 edge */
}
.check input:checked, .switch input:checked {
  background: radial-gradient(circle at calc(100% - 9px) 50%, var(--color-knob) 0 6.5px, transparent 7px) no-repeat, var(--color-accent);
  box-shadow: none;
}
.check input:disabled, .switch input:disabled { opacity: 0.5; cursor: not-allowed; }

/* ---------- view bar (Listings / Matches / Skipped · All / WTS / WTB · the summary) ---------- */

.viewbar {
  display: flex; flex-wrap: wrap; align-items: flex-end; justify-content: space-between; gap: var(--space-2) var(--space-8);
  border-bottom: 1px solid var(--color-line);
}
.tabs { display: flex; gap: var(--space-9); }
.tabs button {
  display: inline-flex; align-items: center; gap: var(--space-4);
  height: 2.75rem; padding: 0 var(--space-1); margin-bottom: -1px;
  border: 0; border-bottom: 2px solid transparent; background: none; cursor: pointer;
  font: 700 var(--text-md)/1 var(--font-sans); color: var(--color-muted); white-space: nowrap;
}
.tabs button:hover { color: var(--color-ink); }
.tabs button:active { transform: translateY(1px); }
.tabs button:disabled { opacity: 0.5; cursor: not-allowed; }
.tabs button[aria-selected="true"] { color: var(--color-ink); border-bottom-color: var(--color-accent); }
.count {
  display: inline-block; min-width: 1.25rem; padding: 0 var(--space-3); border-radius: var(--radius-pill);
  font: 700 var(--text-2xs)/1.25rem var(--font-sans); text-align: center;
  background: var(--color-raised); color: var(--color-ink-2);
}
.summary { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-4); padding: 0 0 var(--space-4); font-size: var(--text-sm); color: var(--color-muted); }
/* All / WTS / WTB (owner 2026-10-13: "show the WTS/WTB filter lower, above the listings table"): in the bar
   right above the table — which view, then which type, then the summary that says what is left (it keeps the
   far end: the control's auto margin); 4px clear of the bar's hairline. On a desktop it is a size down (with
   the 40px tabs, further down: the bar is no taller than its tabs); a tablet keeps the system's 34px, a phone
   44px on a row of its own. Dimmed (and inert — app.js) in the Skipped view, where a message has no type:
   like the rails, never pressable to no effect.
   THE BAR'S HEIGHT NEVER DEPENDS ON WHAT THE SUMMARY SAYS (review 2026-10-13: beside the control the summary
   fitted in some states and not in others, so the whole table / board jumped 31–37px when a filter was
   pressed — and by itself whenever a message was being classified). Each width has ONE layout, by rule:
   a desktop — one line, the summary wrapping inside its own box at the far end; a tablet — the summary on a
   line of its own; a phone — a line of its own, shown when it says more than the tab's pill (further down) */
.type-seg { margin: 0 auto var(--space-2) var(--space-4); }
.type-seg.is-off { opacity: 0.5; }

.feed-error {
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-4) var(--space-6);
  margin-top: var(--space-6); padding: var(--space-4) var(--space-6); border-radius: var(--radius-control);
  border: 1px solid var(--color-danger-line); color: var(--color-danger); background: var(--color-surface);
}
.feed-error::before { width: 1rem; height: 1rem; -webkit-mask-image: var(--icon-alert); mask-image: var(--icon-alert); }
.feed-error > span { flex: 1 1 16rem; }

/* ---------- the feed: shared cell contents ---------- */

.feed { display: block; width: 100%; border-collapse: separate; }
.feed > thead, .feed > tbody { display: block; }
/* every column header is ONE line (owner 2026-10-11): a label never wraps; the Posted header's zone is
   its tooltip (app.js renderZone) and the top bar's clock */
.feed thead th {
  text-align: left; min-width: 0; padding: 0; white-space: nowrap;
  font: 700 var(--text-2xs)/1.3 var(--font-sans); letter-spacing: 0.08em; text-transform: uppercase;
  color: var(--color-faint);
}
.feed td { min-width: 0; padding: 0; text-align: left; }

.feed tr.row {
  position: relative;
  isolation: isolate; /* the arrival tint (::after, z-index -1) paints above the row's own background */
  cursor: pointer;
}
/* the arrival: the ROW slides 6px down into place and a blue tint fades out once (never its cells) */
.feed tr.row::after {
  content: ""; position: absolute; inset: 0; z-index: -1;
  border-radius: inherit; background: var(--color-accent-soft);
  opacity: 0; pointer-events: none;
}
.feed tr.row.is-new { animation: row-in var(--dur-short) var(--ease-out) backwards; }
.feed tr.row.is-new::after { animation: arrive var(--dur-flash) var(--ease-out) both; }
.tab-hidden .feed tr.row.is-new, .tab-hidden .feed tr.row.is-new::after { animation-play-state: paused; }

/* Posted */
.when { display: flex; align-items: baseline; gap: var(--space-2); flex-wrap: wrap; }
.clock { font: 400 var(--text-sm)/1.2 var(--font-mono); color: var(--color-muted); }
.clock-date { font: 400 var(--text-xs)/1.2 var(--font-mono); color: var(--color-faint); }
.rel { display: flex; flex-wrap: wrap; gap: var(--space-2); align-items: center; margin-top: var(--space-2); font-size: var(--text-xs); color: var(--color-faint); }

/* chips: WTS / WTB, status words, small tags */
.badge {
  display: inline-flex; align-items: center; height: 22px; padding: 0 var(--space-4);
  border-radius: var(--radius-chip);
  font: 800 var(--text-xs)/1 var(--font-sans); letter-spacing: 0.06em; text-transform: uppercase; white-space: nowrap;
}
.badge.wts { background: var(--color-wts-bg); color: var(--color-wts); }
.badge.wtb { background: var(--color-wtb-bg); color: var(--color-wtb); }
/* "…" while the type is not known yet: a small blue spinner (the word stays for screen readers) */
.badge.pending {
  width: 18px; height: 18px; padding: 0; font-size: 0; letter-spacing: 0;
  border-radius: 50%; border: 2.5px solid var(--color-line-strong); border-top-color: var(--color-accent);
  animation: spin var(--dur-spin) linear infinite;
}
.feed tr.row.is-stale .badge.pending { animation: none; border-top-color: var(--color-warn); }
.badge.st-muted { background: var(--color-raised); color: var(--color-ink-2); }
.badge.st-error { background: none; color: var(--color-danger); box-shadow: inset 0 0 0 1px var(--color-danger-line); }
.badge.st-refused { background: none; color: var(--color-warn); box-shadow: inset 0 0 0 1px var(--color-warn-line); }

.tag {
  display: inline-flex; align-items: center; gap: 3px; height: 20px; padding: 0 var(--space-3);
  border-radius: var(--radius-chip); border: 1px solid var(--color-line-strong);
  font: 700 var(--text-2xs)/1 var(--font-sans); color: var(--color-muted); white-space: nowrap;
}
.tag-reposts { color: var(--color-ink-2); font-variant-numeric: tabular-nums; }
.tag-reposts::before { width: 11px; height: 11px; -webkit-mask-image: var(--icon-repost); mask-image: var(--icon-repost); }
.tag-mock { color: var(--color-warn); border-color: var(--color-warn-line); }
.tag-stale { color: var(--color-warn); border-color: var(--color-warn-line); white-space: normal; height: auto; min-height: 20px; padding-block: var(--space-1); }
.tag-updating { color: var(--color-muted); }
.tag-fix { color: var(--color-ok); border-color: var(--color-ok-line); }
.tag-flag { color: var(--color-warn); border-color: var(--color-warn-line); }

/* Type */
.feed td.c-type { display: flex; flex-direction: column; align-items: flex-start; gap: var(--space-3); }
/* the type notch (upgrade 2026-10-11): the row's WTS green / WTB red as a short rounded tab on its leading
   edge — never a stripe down the row's height, never a state; the chip beside it says the word. It is the
   Type cell's ::before, placed against the row (the row's own pseudo-elements are the card's line break and
   the arrival tint). Under the pointer or the keyboard focus it lengthens a little (transform only) */
.feed tr.row.listing > td.c-type::before {
  content: ""; position: absolute; left: 0; top: 50%; width: var(--notch-w); height: 1.75rem; margin-top: -0.875rem;
  border-radius: 0 var(--notch-w) var(--notch-w) 0; background: var(--color-line-strong);
  transition: transform var(--dur-micro) var(--ease-out); pointer-events: none;
}
.feed tr.row.t-wts > td.c-type::before { background: var(--color-wts); }
.feed tr.row.t-wtb > td.c-type::before { background: var(--color-wtb); }
.feed tr.row.listing:hover > td.c-type::before, .feed tr.row.listing:focus-visible > td.c-type::before { transform: scaleY(1.3); }
.feed tr.row.is-updating > td.c-type::before { opacity: 0.5; }

/* Match: two stacked team lines, each led by its crest (the hero of the row) */
.ev-main { min-width: 0; color: var(--color-ink); }
.ev-match { display: flex; flex-direction: column; gap: var(--space-2); }
.ev-match .vs {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}
/* a long name wraps to a second line next to its crest (top-aligned: the first line stays centred
   on the crest) — never cut; a third line is clamped, with the whole name in the title */
.team, .ev-event { display: flex; align-items: flex-start; gap: var(--space-5); min-width: 0; min-height: 28px; }
.team-name, .ev-name {
  min-width: 0; padding-top: var(--space-2);
  /* a word too long for the line is hyphenated at a real break (long words only), else broken */
  overflow-wrap: break-word; -webkit-hyphens: auto; hyphens: auto; hyphenate-limit-chars: 10 4 4;
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; line-clamp: 2; overflow: hidden;
  font: 700 var(--text-lg)/20px var(--font-sans); font-stretch: var(--wdth-strong); letter-spacing: -0.005em;
}
.ev-main.muted .ev-name { color: var(--color-muted); }
.crest { flex: none; display: block; width: 28px; height: 28px; object-fit: contain; }
/* a dark crest (measured by view.js) sits on a light disc so it reads on the near-black ground */
.crest.is-dark { padding: 3px; border-radius: 50%; background: var(--color-crest-disc); }
.crest-none, .ev-icon {
  position: relative; flex: none; display: inline-flex; align-items: center; justify-content: center;
  width: 28px; height: 28px; border-radius: 50%; background: var(--color-raised); color: var(--color-faint);
}
.crest-none::before { width: 14px; height: 14px; -webkit-mask-image: var(--icon-shield); mask-image: var(--icon-shield); }
.ev-icon { color: var(--color-muted); }
.ev-icon::before { width: 15px; height: 15px; -webkit-mask-image: var(--icon-calendar); mask-image: var(--icon-calendar); }
.ev-kind { display: flex; align-items: center; height: 28px; margin-top: var(--space-2); padding-left: 38px; font-size: var(--text-xs-plus); color: var(--color-subtle); }
.row-state { display: flex; flex-wrap: wrap; gap: var(--space-2); margin-top: var(--space-3); }
/* the league (SPEC §8.6): a 16px icon + the competition's name under the team names (lined up with
   them), a dead match's "postponed" tag beside it; the name wraps, never cut. A mark too dark for the
   ground sits on the crest disc (leagues.js "disc"); a missing icon leaves the name alone. */
.ev-league {
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2) var(--space-3);
  min-height: 20px; margin-top: var(--space-2); padding-left: 38px;
  font-size: var(--text-xs-plus); line-height: 1.3; color: var(--color-muted);
}
.league-icon { flex: none; display: block; width: 16px; height: 16px; object-fit: contain; }
/* a light rounded TILE, not a circle (owner 2026-09-27): a circle clipped the wordmarks at its corners
   ("LALIGA", "BUNDESLIGA"); the square keeps the whole mark inside, and tells a league from a team crest */
.league-icon.is-disc { box-sizing: content-box; padding: 3px; border-radius: var(--radius-xs); background: var(--color-crest-disc); }
.league-name { min-width: 0; overflow-wrap: anywhere; }
.tag.fx-dead { color: var(--color-warn); border-color: var(--color-warn-line); }
/* the event date's fixture tag: "auto" (filled from the league's list), "check date", or a SCHEDULED
   match's "likely 17/10/2026" + "not confirmed" — the line wraps between its parts, never in the date */
.date-text.has-mark { white-space: normal; }
.date-val { white-space: nowrap; }
.date-q { font: 600 var(--text-2xs)/1 var(--font-sans); letter-spacing: 0.04em; color: var(--color-subtle); }
.date-text.is-likely .date-val { color: var(--color-muted); }
.tag.date-mark { height: 18px; margin-left: var(--space-1); vertical-align: 1px; color: var(--color-muted); }
.tag.date-mark.is-check { color: var(--color-warn); border-color: var(--color-warn-line); }

/* the 📊 event-page button (SPEC §7.6). A row has two copies and shows one, so the button never
   takes width from the team names (review 2026-09-27: beside them it cost 44px — at 320px 25 names
   wrapped instead of 4, "Paris Saint-Ger…", "Marse / ille"; at 1024px 6 instead of 1):
     ≥ 1400px  right after the longer team name (the Match cell: it reads as part of the match,
               and the wide Match track has the room)
     < 1400px  on the event date's line (the Event date cell): after the date on desktop, at the end
               of the card's date · venue line (before "Qty") on tablets and phones
   Never near the price or the seller. The glyph is the Monitor's Market icon: an owner-requested
   exception to "no emoji as icons" (DESIGN.md), drawn from the aria-hidden span's data-glyph so the
   cells keep their own text. */
@media (min-width: 1400px) {
  .feed tr.row.listing > td.c-event.has-evbtn {
    display: grid; grid-template-columns: minmax(0, max-content) auto; justify-content: start;
    column-gap: var(--space-6); row-gap: 0; align-items: start;
  }
  .feed td.c-event.has-evbtn > .ev-main, .feed td.c-event.has-evbtn > .ev-kind, .feed td.c-event.has-evbtn > .row-state { grid-column: 1; min-width: 0; }
  .feed td.c-event.has-evbtn > .ev-btn { grid-column: 2; grid-row: 1 / span 3; align-self: center; }
  .feed td.c-event.has-evbtn > .ev-league { grid-column: 1; min-width: 0; }
  .ev-btn.ev-btn-date { display: none; }
}
@media (max-width: 1399.98px) {
  .ev-btn.ev-btn-match { display: none; }
}
@media (min-width: 1024px) and (max-width: 1399.98px) {
  /* the date, the button right after it; the venue under both (the cell's whole width) — and under that
     the fixture's stadium when a match's own event name already took the venue line (each a row of its
     own). Three tracks:
       1  the date. It may shrink (minmax(0, …)): a date with its "auto" / "check date" tag is wider than
          the cell leaves beside the button, so the tag wraps under the date (DESIGN.md: the line wraps
          between its parts) — as max-content it pushed the button over the Qty number (found 2026-10-11).
          A date written in words ("next Saturday") wraps between its words too; DD/MM/YYYY has no break
       2  the button: a FIXED track — its 32px and the gap before it (the button sits at the track's end)
       3  a flexible filler
     The lines under the date span all three, and an item that spans a flexible track gives its width to
     that track alone (CSS Grid §11.5) — so no venue, however long, resizes the date's or the button's
     track. With an `auto` button track a long line grew it and squeezed the date's to 49px: the button
     sat on the year ("21/10/20" under 📊 — review 2026-10-11; the fixture's stadium made it common) */
  .feed tr.row.listing > td.c-date.has-evbtn { display: grid; grid-template-columns: minmax(0, max-content) calc(2rem + var(--space-5)) minmax(0, 1fr); align-items: center; column-gap: 0; }
  .feed td.c-date.has-evbtn > .date-text { grid-column: 1; grid-row: 1; min-width: 0; white-space: normal; }
  .feed td.c-date.has-evbtn > .ev-btn-date { grid-column: 2; grid-row: 1; justify-self: end; }
  .feed td.c-date.has-evbtn > .ev-sub { grid-column: 1 / -1; }
}
@media (max-width: 1023.98px) {
  /* the card's date line holds the 32px button: its words sit on the line's middle, like "Qty" */
  .feed tr.row.listing > td.c-date.has-evbtn { align-items: center; }
  .feed tr.row.listing > td.c-date.has-evbtn > .ev-btn-date { margin-left: auto; }
}
.ev-btn {
  position: relative; flex: none;
  display: inline-flex; align-items: center; justify-content: center;
  width: 2rem; height: 2rem; padding: 0; margin: 0;
  border: 1px solid var(--color-line-strong); border-radius: var(--radius-sm);
  background: var(--color-surface-2); color: var(--color-ink);
  font: 400 var(--text-lg)/1 var(--font-sans);
  cursor: pointer;
  transition: background-color var(--dur-micro) var(--ease-out), border-color var(--dur-micro) var(--ease-out), transform var(--dur-micro) var(--ease-out);
}
.ev-btn:hover { background: var(--color-accent-soft); border-color: var(--color-accent-line); }
.ev-btn:active { transform: translateY(1px); }
.ev-btn-glyph { display: block; line-height: 1; pointer-events: none; }
.ev-btn-glyph::before { content: attr(data-glyph); }
/* an event "On fire" among the upcoming events' WTB requests (heat.js feedFlame): a small WTB-red flame
   on the button's corner — aria-hidden decoration; the button's name and tooltip say it in words */
.ev-btn.is-hot::after {
  content: ""; position: absolute; top: -7px; right: -6px; width: 14px; height: 14px; pointer-events: none;
  background-color: var(--color-wtb);
  -webkit-mask: var(--icon-flame) center / contain no-repeat; mask: var(--icon-flame) center / contain no-repeat;
}

/* Date · Venue */
.date-text { display: block; font: 400 var(--text-sm)/1.3 var(--font-mono); color: var(--color-ink-2); white-space: nowrap; }
.feed td.c-date.is-empty .date-text { color: var(--color-faint); }
/* competition · venue: wraps under the date, never cut (buyers need the venue); its own title */
.ev-sub { display: block; margin-top: var(--space-2); overflow-wrap: anywhere; font-size: var(--text-xs-plus); line-height: 1.35; color: var(--color-muted); }
/* the fixture's stadium, shown when the message stated no place (owner 2026-10-11): the venue line's own
   quiet style, then "auto" — the word of a filled date's tag — as a small plain qualifier (like the
   date's "likely"): fixture data, not the seller's words. It flows after the name as a word */
.venue-mark { font: 600 var(--text-2xs)/1 var(--font-sans); letter-spacing: 0.04em; color: var(--color-subtle); white-space: nowrap; }

/* Qty */
.feed td.c-qty { font: 700 var(--text-xl)/1 var(--font-sans); font-stretch: var(--wdth-num); }

/* Seats: the category, then "Block 404  Row 15" in small mono */
/* a seat fact the listing does not state is left out — no "Block —" / "Row —" placeholder, no label
   (owner 2026-10-11; view.js builds that cell empty; a listing with no seat facts keeps one "—" in the
   category cell). At every width: it must outrank the card-cell rule */
.feed tr.row.listing > td.c-seats:empty { display: none; }
/* the category wraps (two lines at most, the whole text in its title) — never cut on one line */
.feed td.c-cat { font-size: var(--text-md); font-weight: 600; line-height: 1.3; color: var(--color-ink); overflow-wrap: anywhere; }
.feed td.c-cat .seat-value { display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; line-clamp: 2; overflow: hidden; }
.feed td.c-block, .feed td.c-row { display: flex; align-items: baseline; gap: 5px; white-space: nowrap; font-size: var(--text-xs); color: var(--color-subtle); }
.feed td.c-block::before, .feed td.c-row::before { content: attr(data-label); }
.feed td.c-block .seat-value, .feed td.c-row .seat-value { min-width: 0; overflow: hidden; text-overflow: ellipsis; font: 400 var(--text-xs-plus)/1.2 var(--font-mono); color: var(--color-ink-2); }
.feed td.c-seats.is-empty .seat-value { color: var(--color-faint); }
.feed td.c-cat.is-empty { color: var(--color-faint); font-weight: 400; }

/* Price: the big number, the qualifier / unit small, the per-ticket part and the history in mono
   under it. The history is the cell's second child (view.js): under the price box on desktop, on
   the seats line on cards. */
.feed td.c-price { text-align: right; }
.price-box { display: flex; flex-direction: column; align-items: flex-end; gap: var(--space-1); max-width: 100%; }
.price-line { display: inline-flex; align-items: baseline; justify-content: flex-end; flex-wrap: wrap; gap: 0 5px; }
.price-main { font: 800 var(--text-2xl)/1.1 var(--font-sans); font-stretch: var(--wdth-num); letter-spacing: -0.01em; color: var(--color-ink); white-space: nowrap; }
.price-main.is-none { color: var(--color-faint); font-weight: 600; }
.price-q { font-size: var(--text-xs); font-weight: 700; color: var(--color-ink-2); white-space: nowrap; }
.feed tr.t-wts .price-q { color: var(--color-wts); }
.feed tr.t-wtb .price-q { color: var(--color-wtb); }
.price-unit, .price-basis { font-size: var(--text-xs); color: var(--color-muted); white-space: nowrap; }
.price-sub { font-size: var(--text-xs); color: var(--color-muted); }
/* "£175 → £160 → £150": unbreakable steps that wrap BETWEEN prices — never cut, the newest shows */
.price-hist {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: flex-end; gap: 0 var(--space-2);
  max-width: 100%; font: 400 var(--text-2xs)/1.35 var(--font-mono); color: var(--color-muted);
}
.ph-step { white-space: nowrap; }
.price-hist::before { width: 11px; height: 11px; color: var(--color-accent-text); -webkit-mask-image: var(--icon-repost); mask-image: var(--icon-repost); }
.price-hist.is-down::before { -webkit-mask-image: var(--icon-down); mask-image: var(--icon-down); }
.price-hist.is-up::before { -webkit-mask-image: var(--icon-up); mask-image: var(--icon-up); }
/* a price that moved across the reposts: the live-state blue (the box: the cell on desktop, the
   price box on cards) */
.feed td.c-price.has-history .price-main { color: var(--color-flash-ink); }

/* Seller (owner 2026-10-11: identified by PHONE NUMBER): the WhatsApp glyph, then the number underlined,
   the seller's own WhatsApp name as a quiet second line (styles further down: "Identity") */
/* a seller's name (or "+<digits>") wraps, never cut — it is how a buyer finds the seller */
.seller-name { font-weight: 600; color: var(--color-ink-2); overflow-wrap: anywhere; }
.feed td.c-seller .seller-name { display: block; }
.seller-link {
  display: inline-flex; align-items: center; gap: var(--space-4); max-width: 100%;
  min-height: 32px; padding: 0 var(--space-5) 0 var(--space-4); margin-left: calc(-1 * var(--space-4));
  border-radius: var(--radius-sm);
  color: var(--color-ink); font-weight: 600;
  text-decoration: none; /* the underline is the number's own (.seller-text): the name line under it stays plain */
}
.seller-link .seller-text { text-decoration: underline; text-decoration-color: var(--color-link-line); text-underline-offset: 4px; }
.seller-link:hover { color: var(--color-accent-text); }
.seller-link:hover .seller-text { text-decoration-color: currentColor; }
.seller-link:active { opacity: 0.75; }
.seller-text { min-width: 0; overflow-wrap: anywhere; line-height: 1.3; }
/* a "+<digits>" label keeps its digits together (the Seller track is sized to hold one) */
.seller-text.is-number, .seller-name.is-number { white-space: nowrap; overflow-wrap: normal; }
/* The number, and under it the seller's own WhatsApp name (owner 2026-10-11; it was the other way round) */
.seller-lines { display: inline-flex; flex-direction: column; min-width: 0; }
/* the WhatsApp glyph in WhatsApp's own green (owner request 2026-10-04), hover / focus included */
.wa-glyph { width: 16px; height: 16px; flex: none; color: var(--color-whatsapp); }

/* Notes */
.feed td.c-notes { font-size: var(--text-sm); line-height: 1.38; color: var(--color-muted); }
.notes-text {
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; line-clamp: 2; overflow: hidden;
  overflow-wrap: anywhere;
}

/* ---------- pending ("classifying…") rows ----------
   The runner writes a pending record the moment it accepts a message; the row shows the raw
   text until the classified listings replace it (a few seconds). */
.pending-state { font: 700 var(--text-lg)/1.25 var(--font-sans); font-stretch: var(--wdth-strong); color: var(--color-ink-2); }
.pending-state::first-letter { text-transform: uppercase; }
/* the caption is generated content on an EMPTY aria-hidden span (view.js): shown, never read — the
   row's title already says it — and never part of the row's text */
.pending-note { display: block; }
.pending-note::before {
  content: "Just arrived · the listings appear here in a few seconds";
  display: block; margin-top: var(--space-1);
  font: 400 var(--text-xs-plus)/1.3 var(--font-sans); font-stretch: normal; color: var(--color-subtle);
}
.pending-state.is-stale { font-weight: 600; font-size: var(--text-md); color: var(--color-warn); }
/* contain: inline-size — the preview spans Event date..Row and must never SIZE those columns */
.pending-text {
  contain: inline-size;
  white-space: pre-line; overflow-wrap: anywhere;
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; line-clamp: 2; overflow: hidden;
  font-size: var(--text-sm); color: var(--color-muted);
}
.classifying { white-space: nowrap; }
.dots span { animation: dot var(--dur-pulse) var(--ease-in-out) infinite both; }
.dots span:nth-child(2) { animation-delay: 0.2s; }
.dots span:nth-child(3) { animation-delay: 0.4s; }
/* the stub of a row still being classified: a skeleton where its price will be */
.feed td.c-price.is-blank::before {
  content: ""; display: inline-block; width: 76px; height: 16px; border-radius: var(--radius-xs);
  background: var(--color-skeleton); animation: skeleton 1.4s var(--ease-in-out) infinite;
}
/* an edited message's previous listing, while it is re-classified: its facts in muted ink */
.feed tr.row.is-updating .c-price, .feed tr.row.is-updating .c-seats, .feed tr.row.is-updating .c-qty, .feed tr.row.is-updating .date-text { color: var(--color-muted); }
.feed tr.row.is-updating .price-main, .feed tr.row.is-updating .seat-value { color: var(--color-muted); }

/* ---------- day dividers ---------- */
.feed tr.day-sep { display: block; }
.feed tr.day-sep > th {
  display: flex; align-items: center; gap: var(--space-5);
  height: 36px; padding: 0 var(--space-6);
  font: 400 var(--text-sm)/1 var(--font-sans); letter-spacing: 0; text-transform: none; color: var(--color-muted); text-align: left;
}
.sep-label { flex: none; white-space: nowrap; }
.sep-day { font-weight: 800; font-stretch: var(--wdth-strong); letter-spacing: 0.02em; color: var(--color-ink); }
.sep-date { color: var(--color-muted); }
/* the rule is the aria-hidden .sep-line's ::before; "Newest first" (the first divider only) is its
   ::after — generated content inside an aria-hidden element, so the header's name stays its label */
.sep-line { flex: 1 1 0; display: flex; align-items: center; gap: var(--space-5); min-width: 0; }
.sep-line::before { content: ""; flex: 1 1 0; height: 1px; background: var(--color-line); }
.feed tbody tr.day-sep:first-child .sep-line::after { content: "Newest first"; flex: none; font-size: var(--text-xs); color: var(--color-faint); }

/* ---------- the opened row: its original message ---------- */
.feed tr.detail { display: block; }
.feed tr.detail > td { display: block; padding: 0; }
.detail-inner {
  padding: var(--space-8) var(--space-9) var(--space-9);
  background: var(--color-surface);
  border: 1px solid var(--color-line); border-top: 0;
  border-radius: 0 0 var(--radius-control) var(--radius-control);
}
.detail-meta { font-size: var(--text-xs); color: var(--color-muted); margin-bottom: var(--space-4); }
.detail-notes { margin-bottom: var(--space-4); color: var(--color-ink-2); }
.detail-notes .muted { color: var(--color-muted); font-weight: 600; }
.msg-text { white-space: pre-wrap; overflow-wrap: anywhere; max-width: 72ch; color: var(--color-ink); }
/* every post of a merged offer, newest first */
.reposts { list-style: none; margin: var(--space-6) 0 0; padding: 0; display: grid; gap: var(--space-3); }
.repost {
  display: grid; grid-template-columns: minmax(0, 15rem) minmax(0, 1fr);
  background: var(--color-surface-2); border: 1px solid var(--color-line-card); border-radius: var(--radius-btn);
}
.repost.is-latest { border-color: var(--color-accent-line); }
.repost-meta {
  padding: var(--space-4) var(--space-6);
  font: 400 var(--text-xs)/1.4 var(--font-mono); color: var(--color-muted);
  border-right: 1px solid var(--color-line);
}
.repost .msg-text { padding: var(--space-4) var(--space-6); }

/* actions under an opened row (admin-panel.js) */
.row-actions {
  display: grid; gap: var(--space-4);
  margin-top: var(--space-6); padding-top: var(--space-6);
  border-top: 1px solid var(--color-line);
}
.repost .row-actions { grid-column: 1 / -1; margin: 0 var(--space-6) var(--space-4); padding-top: var(--space-4); }
.detail-actions .row-actions { margin-top: 0; padding-top: 0; border-top: 0; }
.row-actions-line { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-4); }
.report-form { display: grid; gap: var(--space-3); max-width: 36rem; }
.action-msg { display: inline-flex; align-items: flex-start; gap: var(--space-3); font-size: var(--text-md); color: var(--color-ink-2); }
.action-msg.is-ok { color: var(--color-ok); }
.action-msg.is-warn { color: var(--color-warn); }
.action-msg.is-error { color: var(--color-danger); }
.action-msg.is-error::before { width: 1rem; height: 1rem; margin-top: 2px; -webkit-mask-image: var(--icon-alert); mask-image: var(--icon-alert); }

/* ---------- skipped view ---------- */
.feed td.c-status { white-space: nowrap; }
.c-msg .clamp {
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 3; line-clamp: 3; overflow: hidden;
}
.feed tr.skipped.is-open .clamp { display: block; -webkit-line-clamp: unset; line-clamp: none; }
.feed td.c-reason { color: var(--color-muted); font-size: var(--text-sm); overflow-wrap: anywhere; }

.empty { padding: var(--space-12) var(--space-8); text-align: center; color: var(--color-muted); }
.empty p { margin: 0 0 var(--space-6); }
.more {
  display: block; width: 100%; margin-top: var(--space-6); min-height: 2.75rem; padding: var(--space-6);
  background: var(--color-surface); border: 1px solid var(--color-line-strong); border-radius: var(--radius-control);
  color: var(--color-ink-2); font: 600 var(--text-md)/1.2 var(--font-sans);
  white-space: nowrap; cursor: pointer;
}
.more:hover { color: var(--color-ink); background: var(--color-raised); }
.more:active { transform: translateY(1px); }
.more:disabled { opacity: 0.5; cursor: not-allowed; }

.foot { padding: var(--space-8) var(--gutter) var(--space-10); text-align: center; font-size: var(--text-xs); color: var(--color-faint); }
/* behind the sign-in screen the backdrop is shown stronger: the footer's words go one step up the text scale (4.5:1 there too) */
body.gate-on .foot { color: var(--color-muted); }

/* ==========================================================================
   Desktop (≥ 1024px): a table whose rows are grids, with the artboard's columns
   ========================================================================== */
@media (min-width: 1024px) {
  .feed > thead {
    position: sticky; top: var(--header-h); z-index: 2;
    background: var(--color-bg);
  }
  .feed thead tr, .feed tr.row {
    display: grid;
    grid-template-columns: var(--cols);
    column-gap: 12px;
    padding: 0 var(--space-6);
  }
  .feed thead tr { align-items: end; padding-top: var(--space-4); padding-bottom: var(--space-4); border-bottom: 1px solid var(--color-line); }
  .feed thead th.c-price { text-align: right; padding-right: var(--space-5); }
  /* ONE header line (owner 2026-10-11; it was "Category" over "Block  Row", and "Posted" over its zone).
     The seats cell stacks the category over block + row, so its header is one label across both tracks —
     "Cat · Block · Row" (th.c-cat); the Block and Row headers are read, not shown */
  .listings-table thead th.c-block, .listings-table thead th.c-row {
    position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0);
  }

  /* Posted · Type · Match · Date·Venue · Qty · Seats (category over block + row) · Price · Seller · Notes
     (the Seller track fits a "+<digits>" number on one line; the Match track gives the room) */
  .listings-table {
    --cols: 4.25rem 4.25rem minmax(0, 1fr) 10.5rem 2.5rem 4rem 3.75rem 10.25rem 10.5rem 9rem;
    --areas-head: "posted type event date qty cat cat price seller notes";
    --areas-row: "posted type event date qty cat cat price seller notes" "posted type event date qty block row price seller notes";
    --areas-pending: "posted type event msg msg msg msg price seller notes";
  }
  .listings-table thead tr { grid-template-areas: var(--areas-head); }
  .feed tr.row.listing { grid-template-areas: var(--areas-row); }
  .feed tr.row.pending { grid-template-areas: var(--areas-pending); }
  .feed .c-posted { grid-area: posted; }
  .feed .c-type { grid-area: type; }
  .feed .c-event { grid-area: event; }
  .feed .c-date { grid-area: date; }
  .feed .c-qty { grid-area: qty; }
  .feed .c-cat { grid-area: cat; align-self: end; }
  .feed .c-block { grid-area: block; align-self: start; }
  .feed .c-row { grid-area: row; align-self: start; }
  /* what is left of the seats cell takes the room of what the listing does not state (view.js
     seatCells): the category without a block and a row sits on the row's middle (is-solo); a block
     without a row, or a row without a block, takes the whole seats line (is-wide); a block / row line
     without a category above it sits on the row's middle (is-mid) */
  .feed td.c-cat.is-solo { grid-row-end: block-end; align-self: center; }
  .feed td.c-block.is-wide, .feed td.c-row.is-wide { grid-column: block-start / row-end; }
  .feed td.c-block.is-mid, .feed td.c-row.is-mid { grid-row-start: cat-start; align-self: center; }
  .feed .c-pending-msg { grid-area: msg; }
  .feed .c-price { grid-area: price; }
  .feed .c-seller { grid-area: seller; }
  .feed .c-notes { grid-area: notes; }
  .feed .c-status { grid-area: status; }
  .feed .c-msg { grid-area: msg; }
  .feed .c-reason { grid-area: reason; }
  .skipped-table {
    --cols: 5.25rem 7.5rem minmax(0, 1fr) minmax(0, 3fr) minmax(0, 1.4fr);
  }
  .skipped-table thead tr, .feed tr.row.skipped { grid-template-areas: "posted status seller msg reason"; }

  .feed tr.row {
    align-items: center; align-content: center; row-gap: var(--space-2);
    min-height: var(--row-min); padding-top: var(--row-pad); padding-bottom: var(--row-pad);
    border-bottom: 1px solid var(--color-line-soft);
  }
  .feed tr.row:hover { background: var(--color-surface); border-radius: var(--radius-control); border-bottom-color: transparent; }
  .feed tr.row:focus-visible { outline-offset: -2px; border-radius: var(--radius-control); }
  .feed tr.row.is-open { background: var(--color-surface); border: 1px solid var(--color-line); border-bottom-color: transparent; border-radius: var(--radius-control) var(--radius-control) 0 0; }
  .feed tr.row.pending {
    min-height: 4.25rem; margin: var(--space-3) 0;
    background: var(--color-surface); border: 1px solid var(--color-line-card); border-radius: var(--radius-control);
  }
  .feed tr.row.pending.is-open { border-bottom-color: transparent; border-radius: var(--radius-control) var(--radius-control) 0 0; margin-bottom: 0; }
  .feed tr.row.skipped { align-items: start; min-height: 0; padding-top: var(--row-pad-top); padding-bottom: var(--row-pad-top); }
  .feed tr.row.skipped > td.c-posted .when { padding-top: 2px; }
  /* the sender link keeps its 32px target, its text on the row's first line */
  .feed tr.row.skipped .seller-link { margin-block: calc(-1 * var(--space-3)); }
  /* the price cell is the box: it hugs the price and its history, right-aligned; blue when the
     price moved across reposts */
  .feed td.c-price {
    display: flex; flex-direction: column; align-items: flex-end; gap: var(--space-2);
    justify-self: end; max-width: 100%;
    padding: var(--space-3) var(--space-5); border-radius: var(--radius-btn);
  }
  .feed td.c-price.has-history { background: var(--color-flash); }
}

/* medium desktop: Posted tucks under Type; the Notes column from 1200px (below that, notes stay in
   the opened row) */
@media (min-width: 1024px) and (max-width: 1399.98px) {
  :root { --gutter: 1.5rem; --band-gap: var(--space-8); } /* (rhythm 2026-10-13: the medium desktop's rows are taller and its filters wrap — its bands stand 16px apart, 24 in a tall window) */
  .listings-table {
    --cols: 4.5rem minmax(0, 1fr) 8.75rem 2.25rem 3.75rem 3.5rem 8.75rem 9.75rem;
    --areas-head: "type event date qty cat cat price seller";
    --areas-row: "type event date qty cat cat price seller" "posted event date qty block row price seller";
    --areas-pending: "type event msg msg msg msg price seller" "posted event msg msg msg msg price seller";
  }
  .feed .c-notes { display: none; }
  .feed tr.row.listing > td.c-type { align-self: end; }
  .feed tr.row.listing > td.c-posted { align-self: start; }
  .feed tr.row.pending > td.c-type { align-self: end; }
  .feed tr.row.pending > td.c-posted { align-self: start; }
  /* one header line: the first column (the type chip over the time) is headed "Type"; "Posted" is
     read, not shown here (the time under the chip says it; the top bar's clock names the zone) */
  .listings-table thead th.c-posted { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); }
}
/* 1200–1399 (laptops): room for the Notes column again — taken from the Match track */
@media (min-width: 1200px) and (max-width: 1399.98px) {
  .listings-table {
    --cols: 4.5rem minmax(0, 1fr) 8.75rem 2.25rem 3.75rem 3.5rem 8.75rem 9.75rem 8rem;
    --areas-head: "type event date qty cat cat price seller notes";
    --areas-row: "type event date qty cat cat price seller notes" "posted event date qty block row price seller notes";
    --areas-pending: "type event msg msg msg msg price seller notes" "posted event msg msg msg msg price seller notes";
  }
  .feed .c-notes { display: block; }
}

/* ==========================================================================
   Below 1024px: every row is a card (the phone artboard). One wrapping flex line per fact:
     1  type · posted ·············· seller       (one line: the time never stacks)
     2  the match (two crest lines) ··· price
     3  date · venue ··················· Qty
     4  category · Block · Row · price history
     5  notes
   A zero-height ::before ends line 1; after that the last item of each line fills it, so the
   match, the date and the notes always start a line of their own. The price cell dissolves
   (display: contents) so its box sits on line 2 and its history on line 4.
   ========================================================================== */
@media (max-width: 1023.98px) {
  :root { --gutter: 1.5rem; }
  /* a narrower header: "Last activity" is read, not shown */
  .live-last-label { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
  .feed { --card-price-w: 9rem; } /* the widest a card's price box gets; the match takes the rest */
  .feed > thead {
    position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0);
  }
  .feed > tbody { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--card-gap); margin-top: var(--space-6); }
  .feed tr.day-sep, .feed tr.detail { grid-column: 1 / -1; }
  .feed tr.day-sep > th { height: 34px; padding: 0 var(--space-1); }
  .feed tbody tr.day-sep:first-child { margin-top: calc(-1 * var(--space-2)); }

  .feed tr.row {
    display: flex; flex-wrap: wrap; align-items: center; align-content: flex-start; column-gap: var(--space-3);
    padding: var(--card-pad-top) var(--space-7) var(--card-pad-bottom);
    border-radius: var(--radius-card);
    background: var(--color-surface-2); border: 1px solid var(--color-line-card);
  }
  .feed tr.row:hover { background: var(--color-field); }
  /* the type notch sits on the card's first line (beside the chip), over the card's edge */
  .feed tr.row.listing > td.c-type::before { left: -1px; top: calc(var(--card-pad-top) + var(--space-1)); height: 1.375rem; margin-top: 0; }
  .feed tr.row.is-open { border-color: var(--color-line-strong); }
  .feed tr.row:focus-visible { outline-offset: -2px; }
  .feed tr.row > td { display: block; min-width: 0; padding: 0; }

  /* line 1 — always ONE line (the artboard's): the chip (+ ×N), the time, the seller. The Posted
     cell dissolves (display: contents) so its parts are placed one by one: the time sits on line 1
     and never wraps, the row's small tags (edited, corrected, flagged, reported) go to the end of
     the card, and "5m ago" is left to the opened row, as on the phone artboard. The seller takes
     what the chip and the time leave (its basis is 0, so it can never push line 1 into two); a long
     name wraps inside its box — never cut. */
  .feed tr.row.listing > td.c-type { order: 1; flex: none; flex-direction: row; align-items: center; gap: var(--space-3); display: flex; }
  .feed tr.row.listing > td.c-posted, .feed tr.row.pending > td.c-posted { display: contents; }
  .feed tr.row.listing > td.c-posted .rel, .feed tr.row.pending > td.c-posted .rel { display: contents; }
  .feed tr.row.listing > td.c-posted .rel > [data-ts], .feed tr.row.pending > td.c-posted .rel > [data-ts] { display: none; }
  .feed tr.row.listing > td.c-posted .when { order: 2; flex: none; flex-wrap: nowrap; white-space: nowrap; }
  /* min-width auto: a name can shrink to a letter (it wraps), a "+<digits>" label never below its
     digits — on the narrowest card with a ×N chip and a date it takes a line of its own instead */
  .feed tr.row.listing > td.c-seller { order: 3; flex: 1 1 0%; min-width: auto; display: flex; justify-content: flex-end; text-align: right; }
  .feed tr.row.listing::before { content: ""; order: 4; flex: 0 0 100%; height: 0; } /* ends line 1 */
  /* line 2 — the match's basis is wider than line 3's date can join, and leaves exactly the price
     box's widest */
  .feed tr.row.listing > td.c-event { order: 5; flex: 1 1 calc(100% - var(--card-price-w) - var(--space-3)); margin-top: var(--card-line); }
  .feed tr.row.listing > td.c-price { display: contents; }
  .feed tr.row.listing .price-box {
    order: 6; flex: none; max-width: var(--card-price-w); margin-top: var(--card-line); align-self: center;
    /* the tint's padding hangs past the card's text edge; a relative offset, so it never narrows the price */
    position: relative; left: var(--space-4); padding: var(--space-2) var(--space-4); border-radius: var(--radius-btn);
  }
  .feed tr.row.listing > td.c-price.has-history .price-box { background: var(--color-flash); }
  /* "each" goes under the number on every card (the artboard's sub line), never by accident; the
     line is plain inline text, so the box is only as wide as the number */
  .feed tr.row.listing .price-line { display: block; text-align: right; }
  .feed tr.row.listing .price-unit { display: block; }
  /* line 3 */
  .feed tr.row.listing > td.c-date { order: 7; flex: 1 1 calc(100% - 4.5rem - var(--space-3)); margin-top: var(--card-line); display: flex; align-items: baseline; gap: var(--space-3); }
  .feed tr.row.listing > td.c-qty { order: 8; flex: 0 0 4.5rem; margin-top: var(--card-line); text-align: right; }
  /* line 4 — the seats, then a merged offer's price history (it wraps to a line of its own when
     the seats leave no room) */
  .feed tr.row.listing > td.c-cat { order: 9; flex: 0 1 auto; margin: var(--space-3) var(--space-5) 0 0; }
  .feed tr.row.listing > td.c-block { order: 10; flex: none; display: flex; margin: var(--space-3) var(--space-5) 0 0; }
  .feed tr.row.listing > td.c-row { order: 11; flex: none; display: flex; margin: var(--space-3) var(--space-5) 0 0; }
  .feed tr.row.listing > td.c-seats.is-empty { display: none; }
  .feed tr.row.listing .price-hist { order: 12; flex: 0 1 auto; min-width: 0; justify-content: flex-start; margin-top: var(--space-3); }
  /* line 5 */
  .feed tr.row.listing > td.c-notes { order: 13; flex: 1 1 100%; margin-top: var(--space-3); }
  .feed tr.row.listing > td.c-notes:empty { display: none; }
  .feed tr.row.listing .notes-text { display: block; -webkit-line-clamp: unset; line-clamp: none; }
  /* the row's tags close the card (on the seats line when it has room) */
  .feed tr.row.listing > td.c-posted .tag { order: 14; flex: none; margin: var(--space-3) var(--space-3) 0 0; }

  .feed tr.row .rel { margin-top: 0; }
  .feed tr.row .clock { font-size: var(--text-xs); }
  .team, .ev-event { min-height: 24px; gap: 9px; }
  .ev-match { gap: var(--space-1); }
  .crest, .crest-none, .ev-icon { width: 24px; height: 24px; }
  .crest-none::before { width: 12px; height: 12px; }
  .ev-icon::before { width: 13px; height: 13px; }
  .team-name, .ev-name { font-size: var(--text-lg-minus); padding-top: var(--space-1); }
  .ev-kind { height: 24px; margin-top: var(--space-1); padding-left: 33px; }
  .ev-league { margin-top: var(--space-2); padding-left: 33px; }
  .date-text { display: inline; flex: none; white-space: nowrap; font-size: var(--text-xs); }
  .date-text.has-mark { flex: 0 1 auto; min-width: 0; white-space: normal; }
  /* the venue wraps beside the date, never cut */
  .ev-sub { display: inline; flex: 1 1 auto; margin: 0; min-width: 0; font-size: var(--text-sm); }
  .ev-sub::before { content: "·"; margin-right: var(--space-3); color: var(--color-sep); }
  /* the fixture's stadium (owner 2026-10-11) takes a line of its own under the date line (beside a
     tagged date it had 64px at 320px) — and so does a match's own event name in such a cell: the date
     line is the date, its tag and the 📊 button and nothing else, then one whole line per fact, in the
     DOM's order (view.js: date · button · event name · stadium). Each starts a line, so it has no "·".
     Only those lines wrap: the date leaves the button its 32px + the gap (a long "likely … not
     confirmed" wraps inside its own box), so the button never drops to a line of its own and no line
     starts with a "·" (review 2026-10-11) */
  .feed tr.row.listing > td.c-date.has-fxvenue { flex-wrap: wrap; row-gap: var(--space-1); }
  .feed tr.row.listing > td.c-date.has-fxvenue.has-evbtn > .date-text { flex: 0 1 auto; min-width: 0; max-width: calc(100% - 2rem - var(--space-3)); white-space: normal; }
  .feed tr.row.listing > td.c-date.has-fxvenue > .ev-sub { flex: 1 1 100%; }
  .feed tr.row.listing > td.c-date.has-fxvenue > .ev-sub::before { content: none; }
  .feed td.c-qty { font: 600 var(--text-sm)/1.3 var(--font-sans); color: var(--color-ink-2); }
  .feed td.c-qty::before { content: attr(data-label) " "; font-weight: 400; color: var(--color-subtle); }
  .feed td.c-cat { font-size: var(--text-sm); }
  .feed td.c-block, .feed td.c-row { font-size: var(--text-sm); }
  .feed td.c-block .seat-value, .feed td.c-row .seat-value { font-size: var(--text-xs); }
  .feed tr.row.listing > td.c-notes { font-size: var(--text-sm); }

  /* the seller link keeps a 44px target on the card's first line without making it taller (its
     12px block padding is cancelled by the margins); a two-line name grows the line instead of
     spilling over it */
  .seller-link { min-height: 44px; gap: var(--space-3); margin: calc(-1 * var(--space-6)) calc(-1 * var(--space-2)) calc(-1 * var(--space-6)) 0; padding: var(--space-6) var(--space-2); font-size: var(--text-sm); }
  .feed td.c-seller .seller-name { font-size: var(--text-sm); }

  /* pending card: spinner · "Classifying…" · time ··· seller, then the message's first lines */
  .feed tr.row.pending > td.c-type { order: 1; flex: none; }
  .feed tr.row.pending > td.c-event { order: 2; flex: 0 1 auto; min-width: 0; }
  .feed tr.row.pending > td.c-posted .when { order: 3; flex: none; flex-wrap: nowrap; white-space: nowrap; }
  .feed tr.row.pending > td.c-seller { order: 4; flex: 1 1 0%; min-width: auto; display: flex; justify-content: flex-end; text-align: right; }
  .feed tr.row.pending > td.c-pending-msg { order: 5; flex: 1 1 100%; margin-top: var(--card-line); padding-left: 24px; }
  .feed tr.row.pending > td.c-posted .tag { order: 6; flex: none; margin: var(--space-2) var(--space-3) 0 0; }
  /* must outrank `.feed tr.row > td { display: block }` above */
  .feed tr.row.pending > td.c-notes, .feed tr.row.pending > td.c-price { display: none; }
  .feed tr.row.pending { background: var(--color-surface); }
  .feed tr.row.pending .badge.pending { width: 16px; height: 16px; }
  .feed tr.row.pending .pending-state { font-size: var(--text-md); }
  .feed tr.row.pending .pending-note { display: none; }

  /* skipped card: status · sender ··· time, then the message, then why */
  .feed tr.row.skipped > td.c-status { order: 1; flex: none; }
  .feed tr.row.skipped > td.c-seller { order: 2; flex: 1 1 0%; font-weight: 600; overflow: hidden; }
  .feed tr.row.skipped > td.c-posted { order: 3; flex: none; text-align: right; }
  .feed tr.row.skipped > td.c-posted .when, .feed tr.row.skipped > td.c-posted .rel { justify-content: flex-end; }
  .feed tr.row.skipped > td.c-msg { order: 4; flex: 1 1 100%; margin-top: var(--card-line); }
  .feed tr.row.skipped > td.c-reason { order: 5; flex: 1 1 100%; margin-top: var(--space-3); }
  .feed tr.row.skipped .seller-link { margin-left: 0; }

  .detail-inner { border: 1px solid var(--color-line-card); border-radius: var(--radius-card); padding: var(--space-7) var(--space-7) var(--space-8); background: var(--color-surface-2); }
  .repost { grid-template-columns: minmax(0, 1fr); }
  .repost-meta { border-right: 0; border-bottom: 1px solid var(--color-line); }

  /* an opened card continues into its message: one panel, the gap between them cancelled */
  .feed tr.row.is-open { border-bottom-left-radius: 0; border-bottom-right-radius: 0; border-bottom-color: transparent; }
  .feed tr.detail { margin-top: calc(-1 * var(--card-gap)); }
  .feed tr.row.is-open + tr.detail .detail-inner { border-color: var(--color-line-strong); border-top: 0; border-radius: 0 0 var(--radius-card) var(--radius-card); }
}

/* tablets (768–1023px): a two-column board of the same cards — never a squeezed table. Cards keep
   their own height (a short one never shows an empty slab); an opened card takes both columns, so
   its message sits right under it */
@media (min-width: 768px) and (max-width: 1023.98px) {
  /* the header keeps the account's email (shortened; the whole one in its title): the Admin button
     shows its shield + count only (the word is read, not shown) to make the room */
  .tb-brand { gap: var(--space-6); }
  .tb-divider { display: none; }
  .admin-word { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
  .btn-admin { padding: 0 var(--space-4); }
  .sb-side { gap: var(--space-6); }
  .feed > tbody { grid-template-columns: repeat(2, minmax(0, 1fr)); column-gap: var(--card-gap); row-gap: var(--card-gap); align-items: start; }
  .feed, .evp-table { --card-gap: var(--space-8); }
  .feed tr.row.is-open { grid-column: 1 / -1; }
  /* the view bar: the tabs, and All / WTS / WTB at the far end of their line; the summary ALWAYS on the line
     under them (never beside the control in one state and under it in the next) */
  .type-seg { margin-left: auto; margin-right: 0; }
  .viewbar .summary { flex: 1 1 100%; }
  .viewbar .summary .btn-link { margin-block: calc(-1 * var(--space-2)); } /* "Clear filters" never makes the line taller */
}

/* ==========================================================================
   Phones (< 768px)
   ========================================================================== */
@media (max-width: 767.98px) {
  /* (--band-gap: 12px here — review 2026-10-13: at 16 the bands above the cards, with All / WTS / WTB now a row
     of its own, left a phone's first screen without a whole listing; 12 is still more than the 8–10 it was) */
  :root { --gutter: 1rem; --band-gap: var(--space-6); }
  /* touch targets: every button and link button is at least 44 × 44 on a phone (a link button
     keeps its look — its box grows, not its text); these come before the header's own buttons,
     which set their sizes below */
  .btn { min-height: 44px; }
  .btn-link, .btn.btn-link { min-height: 44px; min-width: 44px; }
  /* the header: logo · LIVE pill ····· Admin · Sign out (one 52px row, stays on top) */
  .topbar { min-height: 52px; gap: var(--space-5); padding-top: 0; padding-bottom: 0; padding-right: max(var(--space-2), env(safe-area-inset-right)); }
  .tb-brand { gap: var(--space-5); }
  .brand-slot { height: 36px; }
  .brand-logo { width: 36px; height: 36px; }
  .brand-link::after { content: ""; position: absolute; inset: calc(-1 * var(--space-2)); } /* the way home: 36px to see, 44px to touch */
  .tb-divider, .zone { display: none; }
  .live { height: 26px; gap: var(--space-3); padding: 0 var(--space-5) 0 9px; }
  .led { width: 7px; height: 7px; }
  .live-word { font-size: var(--text-2xs); }
  .live-last { font-size: var(--text-xs); }
  body:not(.app-on) .live { display: none; }
  .sb-side { gap: 0; }
  .user { gap: 0; }
  .btn-admin { width: 44px; height: 44px; padding: 0; justify-content: center; border: 0; background: none; border-radius: var(--radius-control); color: var(--color-ink-2); }
  .btn-admin .admin-glyph { width: 20px; height: 20px; }
  .admin-word { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
  .admin-badge { position: absolute; top: 5px; right: 4px; min-width: 18px; height: 18px; padding: 0 var(--space-2); font-size: var(--text-2xs); box-shadow: 0 0 0 2px var(--color-bg); }
  .guest-tag { height: 22px; padding: 0 var(--space-4); } /* the guest view: a step under the phone's 26px LIVE pill */

  .demo-banner { font-size: var(--text-xs); }

  /* the title is read, not shown; the counters become one compact row with the zone over it */
  .hero { display: block; padding: var(--space-8) 0 var(--space-6); }
  .page-title {
    position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
    overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
  }
  .page-sub { display: none; }
  .sb-counters { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--space-3) 0; }
  .counters-cap { grid-column: 1 / -1; margin: 0 0 var(--space-3); padding-left: 0; }
  .cap-zone { display: inline; } /* the header has no room for the zone on a phone */
  .counters { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .counter { padding: 0 var(--space-4); gap: var(--space-3); }
  .counters > .counter:first-child { border-left: 0; padding-left: 0; }
  .counter dt { font-size: var(--text-2xs); gap: var(--space-2); }
  .dt-long { display: none; }
  .digits { font-size: var(--text-2xl); }
  .split-group { gap: var(--space-4); }
  .split { margin-left: var(--space-4); height: 3px; }

  .status { padding: var(--space-5) var(--space-7); }

  /* search on its own row; the rest in a rail that scrolls sideways on its own */
  .filters { gap: var(--space-5); }
  .search { flex: 1 1 100%; min-width: 0; max-width: none; }
  .search input, .select select { height: 44px; font-size: var(--text-lg); } /* iOS zooms into inputs under 16px */
  .search input { padding-left: 42px; }
  .search-icon { left: 14px; }
  /* the rail scrolls (so it clips): 4px of padding on every side — cancelled by its margins —
     keeps a control's focus ring (2px + 2px offset) whole */
  .filter-rail {
    display: flex; flex: 1 1 100%; gap: var(--item-gap); min-width: 0;
    margin: calc(-1 * var(--space-2)) calc(-1 * var(--gutter)); padding: var(--space-2) var(--gutter);
    overflow-x: auto; overscroll-behavior-x: contain; scrollbar-width: none;
  }
  .filter-rail::-webkit-scrollbar { display: none; }
  .filters-gap { display: none; }
  .checks { display: contents; } /* in the scrolling rail the two chips are items of the rail itself, as before */
  .seg { padding: 0; border: 0; gap: 0; box-shadow: inset 0 0 0 1px var(--color-line-field); }
  .seg button { height: 44px; min-width: 44px; justify-content: center; padding: 0 var(--space-6); border-radius: var(--radius-control); }
  .select select { padding-right: 2rem; }
  .select::after { right: 12px; width: 14px; height: 14px; margin-top: -7px; }
  /* a switch becomes a chip: blue with a check when on */
  .check {
    height: 44px; padding: 0 var(--space-7) 0 11px; gap: var(--space-3);
    border-radius: var(--radius-control); border: 1px solid var(--color-line-field); background: var(--color-field);
    font-weight: 600; color: var(--color-ink-2);
  }
  .check input { width: 16px; height: 16px; border-radius: var(--radius-xs); background: none; box-shadow: inset 0 0 0 1.5px var(--color-faint); }
  .check input:checked {
    background: var(--color-accent-text); box-shadow: none; border-radius: 0;
    -webkit-mask: var(--icon-check) center / contain no-repeat; mask: var(--icon-check) center / contain no-repeat;
  }
  .filter-rail .check:has(input:checked) { background: var(--color-accent-tint); border-color: var(--color-accent-tint-line); color: var(--color-accent-tint-ink); }
  /* the masked check clips its own outline, so the chip carries the focus ring */
  .check:has(input:focus-visible) { outline: 2px solid var(--color-focus); outline-offset: 2px; }
  .check input:focus-visible { outline: none; }

  .viewbar { gap: 0 var(--space-6); }
  .tabs button { height: 44px; }
  /* All / WTS / WTB: a row of its own under the tabs — three equal 44px thumbs across the screen — right
     above the first card. The summary line stands between them only when it says more than the page already
     shows: a filter is on (what is left, and "Clear filters"). The plain count ("52 listings · the last 3
     days" — the tab's pill and the window select say it) is READ, not shown (app.js sets .is-plain): the
     listings keep the line (review 2026-10-13: the first card had sunk 124px under where the live page had it) */
  .type-seg { flex: 1 1 100%; margin: var(--space-3) 0 var(--space-4); }
  .type-seg button { flex: 1 1 0; }
  .summary { font-size: var(--text-xs); padding-bottom: var(--space-5); }
  .viewbar .summary.is-plain {
    position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
    overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
  }
  /* "Clear filters": a 44px target without making the summary line taller */
  .summary .btn-link { margin-block: calc(-1 * var(--space-5)); }

  /* one column of cards (an opened card continues into its message: the rule above) */
  .detail-inner { padding: var(--space-5) var(--space-7) var(--space-8); }
  .btn-sm { min-height: 44px; }
  .row-actions-line .btn { flex: 1 1 auto; }
}

/* small phones (< 375px): the logo tile is a little smaller (whole, never cropped), and the LIVE pill keeps ONE of its two
   texts — while live the blue dot + "12s ago" (the word "Live" is read, not shown); in any other
   state the word (Down, Offline, Paused, …) and the time is read, not shown: never colour alone */
@media (max-width: 374.98px) {
  .brand-slot { width: 32px; height: 32px; }
  .brand-logo { width: 32px; height: 32px; object-fit: contain; }
  .brand-link::after { inset: calc(-1 * var(--space-3)); } /* 32px + 6 on each side */
  .live[data-level="live"] .live-word,
  .live[data-level="warn"] .live-last, .live[data-level="down"] .live-last, .live[data-level="offline"] .live-last,
  .live[data-level="paused"] .live-last, .live[data-level="unknown"] .live-last {
    position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
    overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
  }
  .seg button { padding: 0 var(--space-5); }
  /* the narrowest cards: team names get a third line before anything is clamped */
  .team-name, .ev-name { -webkit-line-clamp: 3; line-clamp: 3; }
}

/* ==========================================================================
   Admin panel + corrections (SPEC §5, §5.1) — the same ground, cards and controls
   ========================================================================== */

/* ---------- form fields (shared by the panel and the report box) ---------- */

.field-label {
  display: block;
  font: 600 var(--text-xs)/1.3 var(--font-sans); color: var(--color-muted);
}
.field-input {
  width: 100%; min-height: 2.5rem; padding: var(--space-4) var(--space-6);
  background: var(--color-field); color: var(--color-ink);
  border: 1px solid var(--color-line-input); border-radius: var(--radius-btn); /* an empty field is still findable: 3:1 */
  font: 400 var(--text-md)/1.35 var(--font-sans);
}
textarea.field-input { resize: vertical; min-height: 4rem; }
select.field-input { cursor: pointer; }
.field-input::placeholder { color: var(--color-subtle); }
.field-input:hover { border-color: var(--color-muted); }
.field-input:disabled { opacity: 0.55; cursor: not-allowed; }
.field-input[aria-invalid="true"] { border-color: var(--color-danger); }
.field-hint { display: block; margin: var(--space-1) 0 0; font-size: var(--text-xs); color: var(--color-subtle); }
.field-hint.is-error { color: var(--color-danger); }
/* review 2026-09-27: the correction editor's fixture-filled date - a state (amber), empty = no line */
.field-hint.is-warn { color: var(--color-warn); }
.field-hint.fix-learn-warn:empty { display: none; }

.switch { display: inline-flex; align-items: center; gap: var(--space-4); cursor: pointer; color: var(--color-ink-2); font-size: var(--text-sm); font-weight: 600; white-space: nowrap; }

/* ---------- the panel ---------- */

.admin-on .app { display: none; }
.admin { display: grid; gap: var(--space-9); padding-top: var(--space-2); }
.admin-head {
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-4) var(--space-8);
  padding-bottom: var(--space-7); border-bottom: 1px solid var(--color-line);
}
.admin-back { min-height: 2.25rem; color: var(--color-muted); }
.back-glyph { width: 1rem; height: 1rem; }
.admin-title {
  margin: 0; font: 800 var(--text-3xl)/1 var(--font-sans); font-stretch: var(--wdth-display); letter-spacing: -0.02em;
}
/* a focus landing point when the panel opens (admin-panel.js), not a control: no ring */
.admin-title:focus, .admin-title:focus-visible { outline: none; }
/* tabs: the same segmented control as the type filter */
.admin-tabs {
  display: inline-flex; flex-wrap: wrap; gap: 2px; padding: 3px;
  border-radius: var(--radius-control); background: var(--color-field); border: 1px solid var(--color-line-field);
}
.admin-tabs button {
  display: inline-flex; align-items: center; gap: var(--space-3);
  min-height: 34px; padding: 0 var(--space-7); border: 0; border-radius: var(--radius-seg);
  background: none; color: var(--color-ink-2); cursor: pointer; white-space: nowrap;
  font: 600 var(--text-md)/1 var(--font-sans);
}
.admin-tabs button:hover { background: var(--color-raised); color: var(--color-ink); }
.admin-tabs button:active { transform: translateY(1px); }
.admin-tabs button:disabled { opacity: 0.5; cursor: not-allowed; }
.admin-tabs button[aria-selected="true"] { background: var(--color-accent); color: var(--color-accent-ink); font-weight: 700; }
.admin-tabs button[aria-selected="true"] .count { background: var(--color-accent-ink); color: var(--color-accent-text); }
.admin-view { display: grid; gap: var(--space-10); min-width: 0; }

.av-bar { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: var(--space-4) var(--space-8); }
.av-summary { color: var(--color-muted); font-size: var(--text-sm); }
.av-tools { display: flex; flex-wrap: wrap; align-items: center; justify-content: flex-end; gap: var(--space-4); flex: 1 1 28rem; }
.av-filter { display: block; min-width: 0; flex: 1 1 12rem; }
.av-slot:empty { display: none; }
.av-note { display: flex; gap: var(--space-4); align-items: flex-start; margin: 0; color: var(--color-muted); }
.av-note.is-loading { color: var(--color-ink-2); }
.av-note.is-error { color: var(--color-danger); }
.av-note.is-error::before { width: 1rem; height: 1rem; margin-top: 2px; -webkit-mask-image: var(--icon-alert); mask-image: var(--icon-alert); }
.av-help { margin: 0; max-width: 75ch; color: var(--color-muted); font-size: var(--text-sm); }
.av-banner {
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-4) var(--space-8);
  padding: var(--space-4) var(--space-6); border-radius: var(--radius-control);
  background: var(--color-surface-2); border: 1px solid var(--color-line-card); font-size: var(--text-sm);
}
.av-banner + .av-banner { margin-top: var(--space-4); }
/* a full ring in the tone, never a side stripe */
.av-banner.is-ok { color: var(--color-ok); border-color: var(--color-ok-line); }
.av-banner.is-warn { color: var(--color-warn); border-color: var(--color-warn-line); }
.av-banner.is-error { color: var(--color-danger); border-color: var(--color-danger-line); }
.av-empty { padding: var(--space-11) var(--space-8); text-align: center; background: var(--color-surface-2); border: 1px solid var(--color-line-card); border-radius: var(--radius-card); }
.av-empty h3 { margin: 0 0 var(--space-4); font: 800 var(--text-xl)/1.2 var(--font-sans); font-stretch: var(--wdth-strong); }
.av-empty p { margin: 0 auto; max-width: 60ch; color: var(--color-muted); }

.av-section { display: grid; gap: var(--space-6); min-width: 0; }
.av-h, .av-section > summary {
  margin: 0; display: flex; align-items: center; gap: var(--space-4);
  font: 800 var(--text-xl)/1.2 var(--font-sans); font-stretch: var(--wdth-strong); letter-spacing: -0.005em; color: var(--color-ink);
}
.av-section > summary { cursor: pointer; list-style: none; min-height: 2.25rem; }
.av-section > summary::-webkit-details-marker { display: none; }
/* the disclosure chevron: right when closed, down when open (instant) */
.av-section > summary::after {
  content: ""; flex: none; width: 0.5rem; height: 0.5rem; margin-left: var(--space-2);
  border: solid currentColor; border-width: 0 2px 2px 0;
  transform: rotate(-45deg);
}
.av-section[open] > summary::after { transform: translateY(-0.125rem) rotate(45deg); }
.av-section > summary .av-h { font: inherit; letter-spacing: inherit; }
.av-section > summary:hover { color: var(--color-ink-2); }
details.av-section { padding-top: var(--space-4); border-top: 1px solid var(--color-line); }

/* the header numbers, as the page's counters */
.av-stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(10rem, 1fr)); gap: var(--space-4); margin: 0; width: 100%; }
.av-stat { padding: var(--space-7) var(--space-8); background: var(--color-surface-2); border: 1px solid var(--color-line-card); border-radius: var(--radius-card); min-width: 0; }
.av-stat dt { font-size: var(--text-xs); color: var(--color-muted); }
.av-stat dd { margin: var(--space-3) 0 0; display: grid; gap: var(--space-2); }
.av-stat-value { font: 800 var(--text-3xl)/1 var(--font-sans); font-stretch: var(--wdth-counter); color: var(--color-ink); font-variant-numeric: tabular-nums; }
.av-stat.is-accent .av-stat-value { color: var(--color-accent-text); }
.av-stat.is-hero .av-stat-value { font-size: var(--text-4xl); }
.av-stat-sub { font-size: var(--text-xs); color: var(--color-subtle); overflow-wrap: anywhere; }

.savebar {
  position: sticky; bottom: 0; z-index: 3;
  display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: var(--space-4) var(--space-8);
  padding: var(--space-6) var(--space-8) calc(var(--space-6) + env(safe-area-inset-bottom));
  background: var(--color-raised); border: 1px solid var(--color-line-strong); border-radius: var(--radius-card) var(--radius-card) 0 0;
}
.savebar-text { font-weight: 600; color: var(--color-ink); }
.savebar-actions { display: flex; gap: var(--space-4); }

/* ---------- synonyms ---------- */

.syn-groups { display: grid; gap: var(--space-10); }
.syn-group { display: grid; gap: var(--space-4); }
.syn-group-head h3 { margin: 0; display: flex; align-items: center; gap: var(--space-4); font: 800 var(--text-xl)/1.2 var(--font-sans); font-stretch: var(--wdth-strong); }
.syn-help { margin: var(--space-1) 0 0; font-size: var(--text-xs); color: var(--color-subtle); max-width: 75ch; }
.syn-empty { margin: 0; color: var(--color-muted); font-size: var(--text-sm); }
.syn-list { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--space-3); }
/* a plain card row; an entry with a problem gets a full ring in the error tone */
.syn-row {
  display: grid; grid-template-columns: minmax(0, 1fr) auto; align-items: center; gap: var(--space-2) var(--space-8);
  padding: var(--space-5) var(--space-8);
  background: var(--color-surface-2); border: 1px solid var(--color-line-card);
  border-radius: var(--radius-control);
}
.syn-row.has-error { border-color: var(--color-danger); }
/* a switched-off entry dims its terms and name only — its note (why it is off) stays readable */
.syn-row.is-off .syn-terms, .syn-row.is-off .syn-canonical { opacity: 0.6; }
.syn-row.is-editing { grid-template-columns: minmax(0, 1fr); background-color: var(--color-surface); border-color: var(--color-accent-tint-line); }
.syn-main { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2) var(--space-4); min-width: 0; }
.syn-terms { display: flex; flex-wrap: wrap; gap: var(--space-2); }
.term {
  padding: var(--space-1) var(--space-4); border-radius: var(--radius-chip);
  background: var(--color-field); border: 1px solid var(--color-line-field);
  font: 500 var(--text-sm)/1.3 var(--font-sans); color: var(--color-ink-2); overflow-wrap: anywhere;
}
.syn-arrow { color: var(--color-subtle); }
.syn-canonical { font: 700 var(--text-md)/1.2 var(--font-sans); font-stretch: var(--wdth-strong); color: var(--color-ink); overflow-wrap: anywhere; }
.syn-note { flex-basis: 100%; font-size: var(--text-xs); color: var(--color-subtle); overflow-wrap: anywhere; }
.syn-actions { display: flex; flex-wrap: wrap; align-items: center; justify-content: flex-end; gap: var(--space-4); }
.syn-problems { grid-column: 1 / -1; margin: 0; font-size: var(--text-xs); }
.syn-problems:empty { display: none; }
/* the row being edited keeps the line for its problem, so one appearing never moves the buttons */
.syn-row.is-editing .syn-problems:empty { display: block; min-height: 1.45em; min-height: 1lh; }
.syn-problems.is-error { color: var(--color-danger); }
.syn-problems.is-warn { color: var(--color-warn); }
.syn-form { display: grid; grid-template-columns: repeat(auto-fit, minmax(12rem, 1fr)); gap: var(--space-6); align-items: start; }
.fld { display: grid; gap: var(--space-1); min-width: 0; align-content: start; }
.fld.is-wide { grid-column: 1 / -1; }
.fld.is-bool { align-self: end; padding-bottom: var(--space-2); }
.syn-enabled { align-self: end; }
.syn-form-actions { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-4); }

.bulk { display: grid; gap: var(--space-6); padding: var(--space-8); background: var(--color-surface-2); border: 1px solid var(--color-line-card); border-radius: var(--radius-card); }
.bulk-fields { display: grid; grid-template-columns: minmax(0, 1fr) minmax(10rem, 14rem); gap: var(--space-6); align-items: start; }
.bulk-text { font-variant-numeric: tabular-nums; }
.bulk-lines { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--space-1); font-size: var(--text-sm); }
.bulk-lines li { display: grid; grid-template-columns: 2rem minmax(0, 1fr); gap: var(--space-1) var(--space-4); }
.bulk-no { color: var(--color-subtle); text-align: right; font-family: var(--font-mono); }
.bulk-lines .is-ok .bulk-entry { color: var(--color-ink); }
.bulk-lines .is-error .bulk-entry { color: var(--color-ink-2); }
.bulk-err { grid-column: 2; color: var(--color-danger); font-size: var(--text-xs); }
.bulk-actions { display: flex; flex-wrap: wrap; gap: var(--space-4); }

/* ---------- corrections: every flag is a card; its state is the badge and a full ring ---------- */

.flag-list { display: grid; gap: var(--space-6); }
.flag {
  display: grid; gap: var(--space-6); min-width: 0;
  padding: var(--space-8) var(--space-9);
  background: var(--color-surface-2);
  border: 1px solid var(--color-line-card);
  border-radius: var(--radius-card);
}
.flag.is-open { border-color: var(--color-warn-line); }
.flag.is-corrected { border-color: var(--color-ok-line); }
.flag.is-dismissed { border-color: var(--color-line-card); }
.flag.is-editing { border-color: var(--color-accent-tint-line); }
.flag, .admin { scroll-margin-top: var(--space-4); } /* on top of html's scroll-padding (the sticky header) */
.flag:focus { outline: none; }
.flag:focus-visible { outline: 2px solid var(--color-focus); outline-offset: 2px; }
.flag-head { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2) var(--space-6); font-size: var(--text-md); }
.flag-when { color: var(--color-ink-2); font: 400 var(--text-sm)/1.3 var(--font-mono); }
.flag-doc { color: var(--color-subtle); font: 400 var(--text-xs)/1.3 var(--font-mono); }
.badge.st-flag-open { background: none; color: var(--color-warn); box-shadow: inset 0 0 0 1px var(--color-warn-line); }
.badge.st-flag-corrected { background: none; color: var(--color-ok); box-shadow: inset 0 0 0 1px var(--color-ok-line); }
.badge.st-flag-dismissed { background: none; color: var(--color-muted); box-shadow: inset 0 0 0 1px var(--color-line-strong); }
.flag-label { margin-bottom: var(--space-2); font: 700 var(--text-2xs)/1.3 var(--font-sans); letter-spacing: 0.08em; text-transform: uppercase; color: var(--color-faint); }
.flag-reports { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--space-3); font-size: var(--text-md); }
.flag-reports li { display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--space-1) var(--space-4); }
.flag-reports li.is-late { color: var(--color-warn); }
.report-by { font-weight: 600; color: var(--color-ink); overflow-wrap: anywhere; }
.report-at { color: var(--color-subtle); font-size: var(--text-xs); }
.report-note { flex-basis: 100%; color: var(--color-ink-2); overflow-wrap: anywhere; }
.flag-reports-none, .flag-rows-none { margin: 0; color: var(--color-muted); font-size: var(--text-sm); }
.flag-msg .msg-text { padding: var(--space-4) var(--space-6); background: var(--color-bg); border: 1px solid var(--color-line); border-radius: var(--radius-btn); max-height: 16rem; overflow-y: auto; }
.flag-why { margin: var(--space-2) 0 0; color: var(--color-ink-2); }
.flag-why .muted { color: var(--color-muted); font-weight: 600; }
.flag-actions { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-4); }

/* a listing as one small line: type chip + the rest */
.mini-stubs { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--space-2); }
.mini-stub {
  display: flex; align-items: center; gap: var(--space-4); min-width: 0;
  padding: var(--space-3) var(--space-4); background: var(--color-bg); border: 1px solid var(--color-line); border-radius: var(--radius-btn);
}
.mini-line { min-width: 0; overflow-wrap: anywhere; color: var(--color-ink); font-variant-numeric: tabular-nums; }

/* the editor */
.fix { display: grid; gap: var(--space-6); padding-top: var(--space-6); border-top: 1px solid var(--color-line); min-width: 0; }
.fix-title h4 { margin: 0; font: 800 var(--text-xl)/1.2 var(--font-sans); font-stretch: var(--wdth-strong); }
.fix-list { display: grid; gap: var(--space-6); }
.fix-listing { margin: 0; padding: var(--space-6) var(--space-8) var(--space-4); border: 1px solid var(--color-line-strong); border-radius: var(--radius-control); background: var(--color-bg); min-width: 0; }
.fix-listing legend { padding: 0 var(--space-2); font: 700 var(--text-md)/1 var(--font-sans); font-stretch: var(--wdth-strong); color: var(--color-ink-2); }
.fix-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(9.5rem, 1fr)); gap: var(--space-4) var(--space-6); }
/* every field keeps the line for its error, so an error appearing never moves the form */
.fx-err { min-height: 1.45em; min-height: 1lh; font-size: var(--text-xs); color: var(--color-danger); }
.fix-listing-foot { display: flex; justify-content: flex-end; padding-top: var(--space-2); }
.fix-none { margin: 0; padding: var(--space-6) var(--space-8); background: var(--color-bg); border: 1px solid var(--color-line); border-radius: var(--radius-control); color: var(--color-ink-2); }
.fix-add { display: flex; flex-wrap: wrap; gap: var(--space-4); }
.fix-add .btn[aria-pressed="true"] { border-color: var(--color-accent-tint-line); background: var(--color-accent-tint); color: var(--color-accent-tint-ink); }
.fix-learn { justify-self: start; height: auto; min-height: 2.5rem; white-space: normal; }
.fix-problems { margin: 0; min-height: 1.45em; min-height: 1lh; color: var(--color-danger); font-size: var(--text-md); }
.fix-buttons { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-4); }
.fix-status { font-size: var(--text-md); color: var(--color-ink-2); }
.fix-status.is-error { color: var(--color-danger); }

/* cheapest fix first: the synonym suggestion */
.sug {
  display: grid; gap: var(--space-4);
  padding: var(--space-6) var(--space-8); border-radius: var(--radius-control);
  background: var(--color-bg); border: 1px solid var(--color-ok-line);
}
.sug-head { margin: 0; font-weight: 600; color: var(--color-ok); }
.sug-items { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--space-4); color: var(--color-ink); }
.sug-term { display: grid; gap: var(--space-1); max-width: 24rem; }
.sug-actions { display: flex; flex-wrap: wrap; gap: var(--space-4); }

.check-result { display: grid; gap: var(--space-2); }
.check-state { margin: 0; font-size: var(--text-md); }
.check-state.is-busy { color: var(--color-ink-2); }
.check-state.is-ok { color: var(--color-ok); }
.check-state.is-warn { color: var(--color-warn); }
.check-state.is-error { color: var(--color-danger); }
.check-diffs { margin: 0; padding-left: var(--space-10); font-size: var(--text-sm); color: var(--color-ink-2); font-variant-numeric: tabular-nums; }

.teach-list { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--space-3); }
.teach-row {
  display: grid; grid-template-columns: minmax(0, 1fr) auto auto; align-items: center; gap: var(--space-4) var(--space-8);
  padding: var(--space-5) var(--space-8);
  background: var(--color-surface-2); border: 1px solid var(--color-line-card);
  border-radius: var(--radius-control);
}
.teach-main { min-width: 0; }
/* a message that starts in Hebrew keeps its own direction (dir="auto", plaintext bidi) but stays
   left-aligned with the rest of the row */
.teach-text, .req-what { text-align: left; unicode-bidi: plaintext; }
.teach-text { color: var(--color-ink); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.teach-sub { font-size: var(--text-xs); color: var(--color-subtle); overflow-wrap: anywhere; }

/* ---------- costs & stats ---------- */

.cost-tiles { grid-template-columns: repeat(auto-fit, minmax(10.5rem, 1fr)); }
.charts { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 22rem), 1fr)); gap: var(--space-8); }
.chart { margin: 0; padding: var(--space-8); background: var(--color-surface-2); border: 1px solid var(--color-line-card); border-radius: var(--radius-card); min-width: 0; }
.chart-title { display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--space-4); margin-bottom: var(--space-8); font: 800 var(--text-lg)/1.2 var(--font-sans); font-stretch: var(--wdth-strong); }
.chart-sub { font: 400 var(--text-xs)/1.2 var(--font-sans); font-stretch: normal; color: var(--color-subtle); }
.chart-plot { position: relative; height: 10rem; margin-left: 3.25rem; }
.chart-grid { position: absolute; inset: 0; pointer-events: none; }
.grid-line { position: absolute; left: 0; right: 0; height: 1px; background: var(--color-line); }
.grid-label { position: absolute; right: calc(100% + var(--space-4)); top: -0.5rem; font: 400 var(--text-2xs)/1 var(--font-mono); color: var(--color-subtle); white-space: nowrap; }
.chart-bars { position: absolute; inset: 0; display: flex; align-items: stretch; gap: 3px; }
.bar-slot { flex: 1 1 0; min-width: 0; display: flex; align-items: flex-end; justify-content: center; cursor: default; }
.bar { width: 100%; max-width: 24px; background: var(--color-bar); border-radius: 4px 4px 0 0; }
.bar-slot:hover .bar { background: var(--color-muted); }
.bar-slot.is-today .bar { background: var(--color-accent); }
.bar-slot.is-hot .bar { background: var(--color-ink-2); }
.bar-missing { width: 60%; max-width: 12px; height: 2px; background: var(--color-faint); opacity: 0.6; }
.chart-tip {
  position: absolute; top: 0; right: 0; z-index: 1;
  padding: var(--space-1) var(--space-4); border-radius: var(--radius-chip);
  background: var(--color-raised); color: var(--color-ink); border: 1px solid var(--color-line-strong);
  font: 400 var(--text-xs)/1.4 var(--font-mono); pointer-events: none;
}
.chart-x { display: flex; justify-content: space-between; margin: var(--space-2) 0 0 3.25rem; font-size: var(--text-xs); color: var(--color-subtle); }

/* tables as card rows */
.av-table-wrap { min-width: 0; overflow-x: auto; } /* a safety net only: the columns fold before they overflow */
.av-table { width: 100%; border-collapse: separate; border-spacing: 0 var(--space-1); font-size: var(--text-md); }
.av-table th, .av-table td { padding: var(--space-4) var(--space-6); text-align: right; white-space: nowrap; }
.av-table td { font-variant-numeric: tabular-nums; }
.av-table thead th { font: 700 var(--text-2xs)/1.3 var(--font-sans); letter-spacing: 0.08em; text-transform: uppercase; color: var(--color-faint); border-bottom: 1px solid var(--color-line); white-space: normal; vertical-align: bottom; }
.av-table thead th:first-child, .av-table tbody th { text-align: left; }
.th-short { display: none; } /* a phone's short header label (the long one is read) */
.av-table tbody th, .av-table tbody td { background: var(--color-surface-2); }
.av-table tbody th { font-weight: 600; color: var(--color-ink); border-radius: var(--radius-sm) 0 0 var(--radius-sm); }
.av-table tbody td:last-child { border-radius: 0 var(--radius-sm) var(--radius-sm) 0; }
.av-table tbody tr.is-missing th, .av-table tbody tr.is-missing td { background: none; color: var(--color-subtle); font-weight: 400; }
.av-table tbody tr.is-missing td { text-align: left; }

/* requests */
.req-list { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--space-3); }
/* the status badge says where a request stands — no colour band repeating it */
.req {
  display: grid; grid-template-columns: 8rem minmax(0, 1fr) auto; align-items: center; gap: var(--space-4) var(--space-8);
  padding: var(--space-5) var(--space-8);
  background: var(--color-surface-2); border: 1px solid var(--color-line-card);
  border-radius: var(--radius-control);
}
.req-when { display: grid; gap: var(--space-1); }
.req-time { color: var(--color-ink); font-weight: 600; font-variant-numeric: tabular-nums; }
.req-kind { font-size: var(--text-xs); color: var(--color-subtle); }
.req-main { min-width: 0; display: grid; gap: var(--space-1); }
.req-what { color: var(--color-ink); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.req-outcome { font-size: var(--text-sm); color: var(--color-ink-2); overflow-wrap: anywhere; }
.req-side { display: grid; justify-items: end; gap: var(--space-1); }
.req-by { font-size: var(--text-xs); color: var(--color-subtle); }
.badge.st-req-pending { background: none; color: var(--color-pending); box-shadow: inset 0 0 0 1px var(--color-line-strong); }
.badge.st-req-running { background: none; color: var(--color-accent-text); box-shadow: inset 0 0 0 1px var(--color-accent-line); }
.badge.st-req-done { background: none; color: var(--color-ok); box-shadow: inset 0 0 0 1px var(--color-ok-line); }
.badge.st-req-error { background: none; color: var(--color-danger); box-shadow: inset 0 0 0 1px var(--color-danger-line); }

/* medium widths: errors / refused are in the tiles already */
@media (max-width: 999.98px) {
  .stats-table th:nth-child(5), .stats-table td:nth-child(5),
  .stats-table th:nth-child(6), .stats-table td:nth-child(6) { display: none; }
}

/* ---------- admin on phones ---------- */
@media (max-width: 767.98px) {
  .admin { gap: var(--space-8); }
  .admin-head { gap: var(--space-4); }
  .admin-title { font-size: var(--text-2xl); }
  .admin-back { min-height: 44px; }
  /* seven sections (2026-10-12): the feed's four as a 2 × 2 segmented grid, then Members · Sellers · Visits
     sharing the third row — never a row with a lone one */
  .admin-tabs { display: grid; grid-template-columns: repeat(6, minmax(0, 1fr)); width: 100%; }
  .admin-tabs button { grid-column: span 3; justify-content: center; min-height: 44px; padding: 0 var(--space-4); }
  .admin-tabs button[data-group="people"] { grid-column: span 2; }
  .av-section > summary, .fix-learn { min-height: 44px; }
  /* "Re-class." (the long word read, not shown) never breaks at its hyphen; a tighter header
     tracking — Day · Messages · Listings · Re-class. · Cost fit 343px without sideways scrolling */
  .stats-table thead th { letter-spacing: 0.04em; }
  .stats-table .th-long { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
  .stats-table .th-short { display: inline; white-space: nowrap; }
  .field-input { font-size: var(--text-lg); min-height: 44px; }
  .switch input { width: 36px; height: 22px; background: radial-gradient(circle at 11px 50%, var(--color-knob) 0 8px, transparent 8.5px) no-repeat, var(--color-line-strong); }
  .switch input:checked { background: radial-gradient(circle at calc(100% - 11px) 50%, var(--color-knob) 0 8px, transparent 8.5px) no-repeat, var(--color-accent); }
  .switch { min-height: 44px; }
  .syn-row { grid-template-columns: minmax(0, 1fr); padding: var(--space-5) var(--space-6); }
  .syn-actions { justify-content: flex-start; }
  .bulk-fields { grid-template-columns: minmax(0, 1fr); }
  .fix-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .flag { padding: var(--space-6) var(--space-7); }
  .flag-msg .msg-text { max-height: 12rem; }
  .teach-row { grid-template-columns: minmax(0, 1fr) auto; padding: var(--space-5) var(--space-6); }
  .teach-row .btn { grid-column: 1 / -1; justify-self: start; }
  .req { grid-template-columns: minmax(0, 1fr) auto; padding: var(--space-5) var(--space-6); }
  .req-main { grid-column: 1 / -1; grid-row: 2; }
  .chart-plot { height: 8rem; }
  .av-stat.is-hero .av-stat-value { font-size: var(--text-3xl); }
  .av-stats, .cost-tiles { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .av-stat { padding: var(--space-5) var(--space-6); }
  .av-stat-value { font-size: var(--text-2xl); }
  /* the stats table keeps the columns that matter on a phone */
  .stats-table th:nth-child(4), .stats-table td:nth-child(4),
  .stats-table th:nth-child(5), .stats-table td:nth-child(5),
  .stats-table th:nth-child(6), .stats-table td:nth-child(6),
  .stats-table th:nth-child(9), .stats-table td:nth-child(9) { display: none; }
  .av-table th, .av-table td { padding: var(--space-4) var(--space-2); }
  .savebar { flex-direction: column; align-items: stretch; }
  .savebar-actions .btn { flex: 1 1 0; }
}
/* the smallest phones: Re-classified goes too (the RE-CLASSIFIED tile above has the total), so
   Day · Messages · Listings · Cost fit 288px without sideways scrolling */
@media (max-width: 374.98px) {
  .stats-table th:nth-child(7), .stats-table td:nth-child(7) { display: none; }
}

/* ==========================================================================
   Admin people tabs 2026-10-12 (SPEC §11.9, DESIGN.md "Admin people tabs 2026-10-12"): Members, Sellers,
   Visits (admin-people.js) — the panel's own cards, card rows, tiles, chips and tables. No new colour,
   no new token; a card's state is a word and a full ring, never a side stripe.
   ========================================================================== */

/* a control that is busy because it was just pressed stays focusable (aria-disabled, never `disabled`): dimmed like a disabled one */
.admin-view .seg button[aria-disabled="true"], .admin-view .btn-link[aria-disabled="true"] { opacity: 0.5; cursor: not-allowed; }
.admin-view .btn-link[aria-disabled="true"] { text-decoration: none; }

/* a heading or a status line the focus is MOVED to (never a control): no ring, like the Admin title */
.admin-view [tabindex="-1"]:focus, .admin-view [tabindex="-1"]:focus-visible { outline: none; }

.people-card {
  display: grid; gap: var(--space-7); min-width: 0; padding: var(--space-8);
  background: var(--color-surface-2); border: 1px solid var(--color-line-card); border-radius: var(--radius-card);
}
.people-form { display: grid; grid-template-columns: minmax(0, 26rem) minmax(0, 1fr); gap: var(--space-6) var(--space-8); align-items: start; min-width: 0; }
.people-form .people-actions { grid-column: 1 / -1; }
/* the country beside a number field: the field's own edge and radius (as on the sign-in card) */
.people-form .country-select select, .people-country .country-select select {
  width: 100%; max-width: 100%; text-overflow: ellipsis;
  border-color: var(--color-line-input); border-radius: var(--radius-btn);
}
.people-form .country-select select:hover, .people-country .country-select select:hover { border-color: var(--color-muted); }
.people-form .field-hint.is-ok, .sel-edit .field-hint.is-ok { color: var(--color-ink-2); }
.people-actions { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-4) var(--space-6); min-width: 0; }
.people-actions .field-hint { margin: 0; }
/* "are you sure": a small boxed question in the state amber, in place of the buttons that asked */
.people-confirm {
  display: grid; gap: var(--space-4); min-width: 0; padding: var(--space-5) var(--space-6);
  background: var(--color-surface); border: 1px solid var(--color-warn-line); border-radius: var(--radius-control);
}
.people-confirm-text { margin: 0; max-width: 60ch; font-size: var(--text-sm); color: var(--color-ink); overflow-wrap: anywhere; }
.people-confirm-actions { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-4) var(--space-6); }
.people-country { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-3) var(--space-5); min-width: 0; }
.people-country .select { flex: 0 1 15rem; min-width: 0; }

/* ---------- members: the password card, the roster ---------- */

.pw-cards { display: grid; gap: var(--space-4); min-width: 0; }
.pw-card {
  display: grid; gap: var(--space-5); min-width: 0; padding: var(--space-7) var(--space-8);
  background: var(--color-surface-2); border: 1px solid var(--color-line-strong); border-radius: var(--radius-card);
}
.pw-card.is-busy { border-color: var(--color-accent-tint-line); }
.pw-card.is-done { border-color: var(--color-ok-line); }
.pw-card.is-error { border-color: var(--color-danger-line); }
.pw-card > p { margin: 0; }
.pw-title { font: 700 var(--text-lg)/1.3 var(--font-sans); color: var(--color-ink); overflow-wrap: anywhere; }
.pw-line { display: grid; gap: var(--space-2); justify-items: start; min-width: 0; }
/* the password: mono, large, one click selects all of it */
.pw-value {
  max-width: 100%; padding: var(--space-4) var(--space-7);
  background: var(--color-field); border: 1px solid var(--color-line-input); border-radius: var(--radius-btn);
  font: 600 var(--text-xl)/1.3 var(--font-mono); letter-spacing: 0.04em; color: var(--color-ink);
  overflow-wrap: anywhere; user-select: all; -webkit-user-select: all;
}
.pw-actions { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-4) var(--space-6); }
.pw-chat, .mem-phone { color: var(--color-ink); text-decoration: underline; text-decoration-color: var(--color-link-line); text-underline-offset: 4px; }
.pw-chat:hover, .mem-phone:hover { color: var(--color-accent-text); }
.pw-message > summary { display: flex; align-items: center; min-height: 2.25rem; cursor: pointer; font-size: var(--text-sm); font-weight: 600; color: var(--color-muted); }
.pw-message > summary:hover { color: var(--color-ink); }
.pw-text {
  margin: var(--space-2) 0 0; padding: var(--space-5) var(--space-6);
  background: var(--color-field); border: 1px solid var(--color-line-field); border-radius: var(--radius-control);
  font: 400 var(--text-sm)/1.5 var(--font-mono); color: var(--color-ink-2); white-space: pre-wrap; overflow-wrap: anywhere;
}

.mem-list, .sel-list, .imp-rows { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--space-3); min-width: 0; }
.mem-row {
  display: grid; grid-template-columns: minmax(0, 15rem) minmax(0, 1fr) auto auto; grid-template-areas: "id note dates actions";
  align-items: center; gap: var(--space-3) var(--space-8); min-width: 0;
  padding: var(--space-5) var(--space-8);
  background: var(--color-surface-2); border: 1px solid var(--color-line-card); border-radius: var(--radius-control);
}
.mem-id { grid-area: id; display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-3) var(--space-5); min-width: 0; }
.mem-phone { font: 600 var(--text-md)/1.3 var(--font-mono); white-space: nowrap; }
.mem-note { grid-area: note; min-width: 0; font-size: var(--text-sm); color: var(--color-ink-2); overflow-wrap: anywhere; unicode-bidi: plaintext; text-align: left; }
.mem-dates { grid-area: dates; font-size: var(--text-xs); color: var(--color-subtle); font-variant-numeric: tabular-nums; }
.mem-actions { grid-area: actions; justify-self: end; min-width: 0; }
.mem-buttons { display: flex; flex-wrap: wrap; align-items: center; justify-content: flex-end; gap: var(--space-4) var(--space-6); }
.mem-row .people-confirm { max-width: 30rem; }
.mem-notice { grid-column: 1 / -1; margin: 0; flex-wrap: wrap; align-items: center; }
/* a member's state: the word in its ring (teal can sign in, neutral cannot, amber unfinished) */
.badge.st-mem-active { background: none; color: var(--color-ok); box-shadow: inset 0 0 0 1px var(--color-ok-line); }
.badge.st-mem-disabled { background: none; color: var(--color-muted); box-shadow: inset 0 0 0 1px var(--color-line-strong); }
.badge.st-mem-creating, .badge.st-mem-unknown { background: none; color: var(--color-warn); box-shadow: inset 0 0 0 1px var(--color-warn-line); }

/* ---------- sellers: the readiness box, the labels, the table import ---------- */

.ready-box {
  display: grid; gap: var(--space-6); min-width: 0; padding: var(--space-8);
  background: var(--color-surface-2); border: 1px solid var(--color-warn-line); border-radius: var(--radius-card);
}
.ready-steps { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--space-4); min-width: 0; }
.ready-step {
  display: grid; grid-template-columns: 5.5rem auto minmax(0, 1fr) auto; align-items: center; gap: var(--space-3) var(--space-6); min-width: 0;
  padding: var(--space-5) var(--space-6);
  background: var(--color-surface); border: 1px solid var(--color-line-card); border-radius: var(--radius-control);
}
.ready-name { font: 700 var(--text-md)/1.4 var(--font-sans); color: var(--color-ink); white-space: nowrap; }
.ready-step > .tag { justify-self: start; }
.ready-text { max-width: 80ch; font-size: var(--text-sm); color: var(--color-ink-2); }
.ready-manual { grid-column: 3 / -1; display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2) var(--space-6); min-width: 0; }
.ready-manual .switch { white-space: normal; }
.ready-manual .field-hint { margin: 0; }

.sel-row {
  display: grid; grid-template-columns: minmax(0, 1.5fr) minmax(0, 1fr) auto; align-items: center; gap: var(--space-3) var(--space-8); min-width: 0;
  padding: var(--space-5) var(--space-8);
  background: var(--color-surface-2); border: 1px solid var(--color-line-card); border-radius: var(--radius-control);
}
.sel-row.is-editing { background-color: var(--color-surface); border-color: var(--color-accent-tint-line); }
.sel-label { display: grid; gap: var(--space-1); min-width: 0; }
/* a contact label is the owner's own text, often Hebrew, often with emoji: its own direction, left-aligned, never cut */
.sel-name { font: 700 var(--text-md)/1.3 var(--font-sans); font-stretch: var(--wdth-strong); color: var(--color-ink); overflow-wrap: anywhere; unicode-bidi: plaintext; text-align: left; }
.sel-sub { font-size: var(--text-xs); color: var(--color-subtle); overflow-wrap: anywhere; }
.sel-phone { display: grid; gap: var(--space-1); justify-items: start; min-width: 0; }
.sel-number { font: 600 var(--text-md)/1.3 var(--font-mono); color: var(--color-ink); white-space: nowrap; }
.sel-note { font-size: var(--text-xs); color: var(--color-muted); overflow-wrap: anywhere; unicode-bidi: plaintext; text-align: left; }
.sel-actions { display: flex; flex-wrap: wrap; align-items: center; justify-content: flex-end; gap: var(--space-4) var(--space-6); }
.sel-wide { grid-column: 1 / -1; }
p.sel-wide { margin: 0; }
.sel-edit {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 16rem), 1fr)); gap: var(--space-6); align-items: start; min-width: 0;
  padding-top: var(--space-5); border-top: 1px solid var(--color-line);
}
.sel-edit .people-actions { grid-column: 1 / -1; }

/* a pasted table keeps its columns: no wrapping inside the box (it scrolls on its own) */
.imp-text { white-space: pre; overflow-x: auto; font-variant-numeric: tabular-nums; }
.imp-file { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-4) var(--space-6); min-width: 0; }
.imp-file .field-hint { margin: 0; }
.imp-file-name { font-weight: 600; color: var(--color-ink); overflow-wrap: anywhere; unicode-bidi: plaintext; }
.imp-file .tag, .imp-tags .tag { height: auto; min-height: 20px; padding-block: var(--space-1); white-space: normal; }
.imp-read { margin: 0; color: var(--color-ink-2); }
.imp-read b { color: var(--color-ink); }
.imp-preview, .imp-run { display: grid; gap: var(--space-5); min-width: 0; }
.imp-preview:empty, .imp-run:empty { display: none; }
.imp-preview > p, .imp-run > p { margin: 0; }
.imp-run > p { max-width: 90ch; } /* the outcome of a save is a sentence or two: a readable measure on a wide window */
/* what the preview shows: field-shaped toggles with their counts; the pressed one the blue tint */
.imp-chips { display: flex; flex-wrap: wrap; gap: var(--space-3); min-width: 0; }
.imp-chip {
  display: inline-flex; align-items: center; gap: var(--space-3); min-height: 2.25rem; padding: 0 var(--space-6);
  background: var(--color-field); border: 1px solid var(--color-line-field); border-radius: var(--radius-control);
  color: var(--color-ink-2); cursor: pointer; white-space: nowrap;
  font: 600 var(--text-sm)/1 var(--font-sans);
}
.imp-chip:hover { background: var(--color-raised); color: var(--color-ink); }
.imp-chip:active { transform: translateY(1px); }
.imp-chip:disabled { opacity: 0.5; cursor: not-allowed; }
.imp-chip[aria-pressed="true"] { background: var(--color-accent-tint); border-color: var(--color-accent-tint-line); color: var(--color-accent-tint-ink); }
.imp-rows { gap: var(--space-1); }
.imp-row {
  display: grid; grid-template-columns: 4.5rem minmax(0, 1.2fr) minmax(0, 1fr) minmax(0, 1.3fr); align-items: center; gap: var(--space-2) var(--space-6); min-width: 0;
  padding: var(--space-4) var(--space-6);
  background: var(--color-surface); border-radius: var(--radius-sm);
}
.imp-no { font: 400 var(--text-xs)/1.3 var(--font-mono); color: var(--color-subtle); white-space: nowrap; }
.imp-name { font-weight: 600; color: var(--color-ink); overflow-wrap: anywhere; unicode-bidi: plaintext; text-align: left; }
.imp-row.is-invalid .imp-name { font-weight: 400; color: var(--color-ink-2); }
.imp-phone { min-width: 0; font-family: var(--font-mono); font-size: var(--text-sm); color: var(--color-ink-2); overflow-wrap: anywhere; }
.imp-bad { display: grid; gap: var(--space-1); }
.imp-problem { font-family: var(--font-sans); font-size: var(--text-xs); color: var(--color-warn); }
.imp-tags { display: flex; flex-wrap: wrap; gap: var(--space-2); min-width: 0; }
.imp-near { grid-column: 2 / -1; display: grid; gap: var(--space-1); min-width: 0; }
.imp-select { min-width: 0; max-width: 100%; }
.imp-select select { width: 100%; max-width: 100%; text-overflow: ellipsis; }
/* a long sentence beside its switch wraps (the filter rail's switches are one word) */
.imp-keep { justify-self: start; height: auto; min-height: 2.5rem; white-space: normal; }

/* the Sellers list card (2026-10-12): choose a file, ONE Save button, the progress, the summary. Its parts
   are plain blocks of the card; the rows are a disclosure, and their Save bar is the panel's own .savebar */
.list-part { display: grid; gap: var(--space-5); min-width: 0; }
.list-part:empty { display: none; }
.list-part > p { margin: 0; }
.list-part > .btn, .list-part > .btn-link { justify-self: start; }
.list-part > .field-hint, .list-part > .action-msg { max-width: 90ch; } /* a sentence is read, not scanned across 1,400 px */
.list-now { max-width: 80ch; color: var(--color-ink-2); }
.list-now b { color: var(--color-ink); }
.list-save .btn { min-height: 2.75rem; padding: 0 var(--space-10); font-size: var(--text-lg); scroll-margin-bottom: var(--space-10); } /* brought into sight after a file is chosen: with air under it, not on the window's edge */
.list-save .field-hint { flex: 1 1 18rem; max-width: 70ch; font-size: var(--text-sm); color: var(--color-ink-2); }
.list-sheet .select { flex: 0 1 18rem; min-width: 0; }
.list-sheet select { width: 100%; max-width: 100%; text-overflow: ellipsis; }
/* a small disclosure inside the card: its question is the control (a chevron, never a heading) */
.list-more { min-width: 0; }
.list-more > summary {
  display: flex; align-items: center; gap: var(--space-4); min-height: 2.25rem; cursor: pointer; list-style: none;
  font-size: var(--text-sm); font-weight: 600; color: var(--color-ink-2);
}
.list-more > summary::-webkit-details-marker { display: none; }
.list-more > summary::after {
  content: ""; flex: none; width: 0.4375rem; height: 0.4375rem;
  border: solid currentColor; border-width: 0 2px 2px 0; transform: rotate(-45deg);
}
.list-more[open] > summary::after { transform: translateY(-0.125rem) rotate(45deg); }
.list-more > summary:hover { color: var(--color-ink); }
.list-more[open] { display: grid; gap: var(--space-4); }
.list-more[open] > p { margin: 0; }
.list-more > .btn, .list-more > .btn-link { justify-self: start; }
/* the rules as they apply to this list: one line each, the ones with rows open in place */
.list-rules { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--space-2); min-width: 0; max-width: 90ch; font-size: var(--text-sm); color: var(--color-ink-2); }
.list-rules > li { min-width: 0; }
.list-lines {
  list-style: none; margin: 0; padding: var(--space-4) var(--space-6); display: grid; gap: var(--space-3); min-width: 0;
  background: var(--color-surface); border-radius: var(--radius-sm); font-size: var(--text-sm); color: var(--color-ink-2);
}
.list-lines > li { min-width: 0; overflow-wrap: anywhere; }
.list-lines .imp-name { font-weight: 600; }
/* a line of the rules that asks for a look (a number left as written, one number under two names): the state amber */
.list-rules > li.is-check > .list-more > summary { color: var(--color-warn); }
.list-rows { padding-top: var(--space-6); }
.list-bar { margin-top: var(--space-4); }
.list-bar .savebar-text { min-width: 0; font-size: var(--text-sm); font-weight: 400; color: var(--color-ink-2); }
/* beside its button the sentence takes the width that is left. ONLY in the row layout: below 768px .savebar is
   a COLUMN, where a flex-basis is a HEIGHT — "20rem" there made the bar 398px tall and almost empty, over
   most of a phone's window (review 2026-10-12; test/people-style.test.mjs holds this) */
@media (min-width: 768px) {
  .list-bar .savebar-text { flex: 1 1 20rem; }
}
/* the rows end under their sticky Save bar: whatever takes the focus in them is scrolled clear of it (the
   focus was landing on a select the bar covered — WCAG 2.2 "focus not obscured") */
.list-rows :is(.imp-row, select, button, input, summary, a, label) { scroll-margin-bottom: 6rem; }
/* which columns: the question when the page had to guess them — two pickers, one confirmation */
.list-cols { max-width: 44rem; }
.list-cols-fields { display: grid; gap: var(--space-4); min-width: 0; }
.list-cols .people-country .select { flex: 1 1 18rem; min-width: 0; }
.list-cols select { width: 100%; max-width: 100%; text-overflow: ellipsis; }
.list-cols-line .btn-link { vertical-align: baseline; }
/* the save under way: its stage in words, the bar, the count */
.list-progress { display: grid; gap: var(--space-4); min-width: 0; padding: var(--space-6) var(--space-8); background: var(--color-surface); border: 1px solid var(--color-accent-tint-line); border-radius: var(--radius-control); }
.list-progress > p { margin: 0; }
.list-progress > .btn-link { justify-self: start; } /* "Stop here": at the leading edge, like every link button of the card */
.list-meter { height: 0.625rem; min-width: 0; overflow: hidden; background: var(--color-field); border: 1px solid var(--color-line-input); border-radius: var(--radius-pill); }
.list-meter-fill { display: block; height: 100%; min-width: 0; background: var(--color-accent); border-radius: var(--radius-pill); }
/* a suggestion: the seller who posted, then the names of the list that may be his — each its own button */
.list-options { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--space-3); min-width: 0; }
.list-option { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: var(--space-3) var(--space-8); min-width: 0; padding: var(--space-4) var(--space-6); background: var(--color-surface); border-radius: var(--radius-sm); }
.list-option-text { display: grid; gap: var(--space-1); min-width: 0; flex: 1 1 14rem; }

/* ---------- visits: the count question, the members-per-day bars, the table ---------- */

.vis-ask { align-items: center; }
.vis-chart .chart-plot { height: 9rem; }
/* the bars: an inline SVG stretched over the plot (it carries no text — the scale is the HTML grid) */
.vis-svg { position: absolute; inset: 0; display: block; width: 100%; height: 100%; }
.vis-bar { fill: var(--color-bar); }
.vis-bar:hover { fill: var(--color-muted); }
.vis-bar.is-now { fill: var(--color-accent); }
.vis-note { display: block; font-size: var(--text-xs); font-weight: 400; color: var(--color-muted); white-space: normal; overflow-wrap: anywhere; unicode-bidi: plaintext; }

/* laptops and tablets: a member's number and state over the note and the dates, the actions beside them */
@media (max-width: 1199.98px) {
  .mem-row { grid-template-columns: minmax(0, 1fr) auto; grid-template-areas: "id actions" "note actions" "dates actions"; }
  .mem-note:empty { display: none; }
  .people-form { grid-template-columns: minmax(0, 1fr); }
  .imp-row { grid-template-columns: 4.5rem minmax(0, 1fr) minmax(0, 1fr); }
  .imp-tags { grid-column: 2 / -1; }
}
@media (max-width: 767.98px) {
  .people-card, .ready-box { padding: var(--space-7) var(--space-6); }
  .pw-card { padding: var(--space-6); }
  .pw-value { font-size: var(--text-lg); }
  .pw-message > summary, .imp-chip { min-height: 44px; }
  .pw-chat, .mem-phone { display: inline-flex; align-items: center; min-height: 44px; }
  .pw-actions .btn, .people-confirm-actions .btn { flex: 1 1 auto; }
  .mem-row { grid-template-columns: minmax(0, 1fr); grid-template-areas: "id" "note" "dates" "actions"; padding: var(--space-4) var(--space-6) var(--space-5); }
  .mem-actions { justify-self: stretch; }
  .mem-buttons, .sel-actions { justify-content: flex-start; }
  .sel-row { grid-template-columns: minmax(0, 1fr); padding: var(--space-5) var(--space-6); }
  .ready-step { grid-template-columns: auto minmax(0, 1fr); }
  .ready-text, .ready-manual, .ready-step > .btn-link { grid-column: 1 / -1; justify-self: start; }
  .people-country .select { flex: 1 1 100%; }
  /* the rows' Save bar on a phone: the sentence above its button, three lines at most — and more room kept
     clear above it for the control that has the focus */
  .list-bar .savebar-text { display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 3; line-clamp: 3; overflow: hidden; }
  .list-rows :is(.imp-row, select, button, input, summary, a, label) { scroll-margin-bottom: 11rem; }
  /* a bar's tools start at the leading edge; its search takes a row of its own (a placeholder is never cut) */
  .people-tools { justify-content: flex-start; }
  .people-tools .av-filter { flex: 1 1 100%; }
  .imp-row { grid-template-columns: auto minmax(0, 1fr); padding: var(--space-5) var(--space-6); }
  .imp-phone, .imp-tags, .imp-near { grid-column: 1 / -1; }
  .imp-keep { min-height: 44px; padding-block: var(--space-4); }
  /* the Sellers list card on a phone: the Save button takes the row; every disclosure is a 44px target */
  .list-more > summary { min-height: 44px; }
  .list-save .btn { flex: 1 1 100%; }
  .list-sheet .select { flex: 1 1 100%; }
  .list-progress { padding: var(--space-5) var(--space-6); }
  .list-option .btn { flex: 1 1 auto; }
  .vis-chart .chart-plot { height: 7.5rem; }
  /* the table keeps what matters on a phone: who, when, how often (the rest is in the export) */
  .vis-table thead th { letter-spacing: 0.04em; }
  .vis-table th:nth-child(5), .vis-table td:nth-child(5),
  .vis-table th:nth-child(6), .vis-table td:nth-child(6) { display: none; }
}
@media (max-width: 374.98px) {
  .vis-table th:nth-child(3), .vis-table td:nth-child(3) { display: none; }
}

/* ==========================================================================
   Event page (SPEC §7.8): #event/<key>, opened by a row's 📊 button — the same ground, cards, chips
   and controls as the feed. Its table is a real table from 1024px and the feed's cards below.
   ========================================================================== */

.event-on .app, .event-on .hero { display: none; }
/* every grid here is minmax(0, 1fr): a track sized by its content would let the phone filter rail
   (one long line that scrolls on its own) push the page wider than the screen */
.evp { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--space-9); padding-top: var(--space-6); min-width: 0; }
.evp-head, .evp-sec, .evp-stats-body, .evp-cur, .evp-chart, .evp-state-card { grid-template-columns: minmax(0, 1fr); }
.evp, .evp-sec { scroll-margin-top: var(--space-4); }

/* the header: back, the kicker, the match (two crests) or the event, its date · venue · count, LIVE */
.evp-head { display: grid; gap: var(--space-5); padding-bottom: var(--space-8); border-bottom: 1px solid var(--color-line); min-width: 0; }
/* "Back to Live" (owner 2026-10-13: "when entering a specific match page, show a button Back To Live" — it was
   the quiet link "‹ Listings"): the system's primary button at the head's leading edge, the page's first control
   at every width; its chevron a little in from the button's edge, so the words keep the button's optical
   middle; a step of air before the kicker under it */
.evp-back { justify-self: start; padding-left: var(--space-6); margin-bottom: var(--space-2); }
.evp-back .back-glyph { flex: none; }
.evp-titlebar { display: flex; flex-wrap: wrap; align-items: flex-end; justify-content: space-between; gap: var(--space-6) var(--space-10); min-width: 0; }
.evp-titles { display: grid; gap: var(--space-4); min-width: 0; }
.evp-kicker { margin: 0; font: 700 var(--text-xs)/1 var(--font-sans); letter-spacing: 0.12em; text-transform: uppercase; color: var(--color-muted); }
.evp-title {
  margin: 0; display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-4) var(--space-7); min-width: 0;
  font: 800 var(--text-3xl)/1.1 var(--font-sans); font-stretch: var(--wdth-display); letter-spacing: -0.02em; color: var(--color-ink);
}
/* a focus landing point when the page opens, not a control: no ring (like the Admin title) */
.evp-title:focus, .evp-title:focus-visible { outline: none; }
/* the other landing points (focus moves there when the control it was on is rebuilt away, or back
   to the feed from a deep link): headings and notes, never controls — no ring either */
.page-title:focus, .page-title:focus-visible, .evp-h:focus, .evp-h:focus-visible,
.evp-state-title:focus, .evp-state-title:focus-visible, .evp-archive:focus, .evp-archive:focus-visible { outline: none; }
.evp-team { display: inline-flex; min-width: 0; }
.evp-title .team { align-items: center; gap: var(--space-6); min-height: 44px; }
.evp-title .team-name { padding-top: 0; font: inherit; letter-spacing: inherit; }
.evp-title .crest, .evp-title .crest-none { width: 44px; height: 44px; }
.evp-title .crest.is-dark { padding: 5px; }
.evp-title .crest-none::before { width: 20px; height: 20px; }
.evp-vs { font: 600 var(--text-lg)/1 var(--font-sans); font-stretch: var(--wdth-strong); letter-spacing: 0; color: var(--color-subtle); }
.evp-event { display: inline-flex; align-items: center; gap: var(--space-6); min-width: 0; }
.evp-event .ev-icon { width: 44px; height: 44px; }
.evp-event .ev-icon::before { width: 20px; height: 20px; }
.evp-name { min-width: 0; overflow-wrap: anywhere; }
.evp-meta { margin: 0; font-size: var(--text-md); color: var(--color-muted); }
.evp-date { font: 500 var(--text-md)/1.3 var(--font-mono); color: var(--color-ink); }
.evp-date.is-none { font-family: var(--font-sans); color: var(--color-ink-2); }
.evp-city { color: var(--color-ink-2); overflow-wrap: anywhere; }
/* " · " lists that wrap cleanly (event-page.js dotList): every item carries its separator in front;
   the list starts one separator to the left and the host clips it, so a line never ends on a dot
   and a wrapped line never starts with one */
.evp-meta, .evp-wtb-line { --evp-sep-w: 1.25rem; overflow: hidden; }
.evp-dots { display: flex; flex-wrap: wrap; align-items: baseline; row-gap: var(--space-2); margin-left: calc(-1 * var(--evp-sep-w)); }
.evp-dot-item { display: inline-flex; align-items: baseline; min-width: 0; }
.evp-sep { flex: none; width: var(--evp-sep-w); text-align: center; white-space: pre; color: var(--color-sep); }
/* the league line (SPEC §8.6 / §9): icon + name · "Official fixture · Sat 17 Oct · 13:30 your time",
   or a dead match's badge (words + the alert icon — never colour alone), or "Day not confirmed yet" */
.evp-fixture { --evp-sep-w: 1.25rem; overflow: hidden; margin: 0; font-size: var(--text-md); color: var(--color-muted); }
.evp-fixture .evp-dots { align-items: center; }
.evp-fixture .evp-dot-item { align-items: center; }
.evp-league { display: inline-flex; align-items: center; gap: var(--space-4); min-width: 0; font-weight: 600; color: var(--color-ink-2); }
.evp-league .league-icon { width: 18px; height: 18px; }
.evp-official { color: var(--color-ink-2); }
.evp-fx-note { color: var(--color-muted); }
/* API-Football (SPEC §8.6, 2026-10-05): the round after the league, the venue + city last — words that
   wrap inside their item, never cut (a long stadium name at 320px takes a second line) */
.evp-round { min-width: 0; overflow-wrap: anywhere; color: var(--color-muted); }
.evp-venue { min-width: 0; overflow-wrap: anywhere; color: var(--color-ink-2); }
.evp-fx-badge {
  display: inline-flex; align-items: center; gap: var(--space-2); height: 22px; padding: 0 var(--space-4);
  border-radius: var(--radius-chip); border: 1px solid var(--color-warn-line);
  font: 700 var(--text-xs)/1 var(--font-sans); letter-spacing: 0.02em; color: var(--color-warn); white-space: nowrap;
}
.evp-fx-badge::before {
  content: ""; flex: none; width: 13px; height: 13px; background-color: currentColor;
  -webkit-mask: var(--icon-alert) center / contain no-repeat; mask: var(--icon-alert) center / contain no-repeat;
}

/* LIVE: blue while the page's listener is healthy; the dot pulses for a moment after a change */
.evp-live {
  margin: 0; display: inline-flex; align-items: center; gap: var(--space-4); flex-wrap: wrap;
  min-height: 30px; padding: var(--space-1) var(--space-6) var(--space-1) 11px;
  border-radius: var(--radius-pill); background: var(--color-surface); border: 1px solid var(--color-line-strong);
  font-size: var(--text-sm); color: var(--color-muted);
}
.evp-dot { flex: none; width: 8px; height: 8px; border-radius: 50%; background: var(--color-faint); }
.evp-live-word { font: 800 var(--text-xs)/1 var(--font-sans); letter-spacing: 0.1em; text-transform: uppercase; color: var(--color-ink); }
.evp-live-sub { color: var(--color-muted); }
.evp-live[data-level="live"] { background: var(--color-accent-soft); border-color: var(--color-accent-line); }
.evp-live[data-level="live"] .evp-dot { background: var(--color-accent); }
.evp-live.is-updated .evp-dot { animation: pulse var(--dur-pulse) var(--ease-in-out) infinite; }
.evp-live[data-level="offline"] { border-color: var(--color-warn-line); }
.evp-live[data-level="offline"] .evp-dot { background: var(--color-warn); }
.evp-live[data-level="down"] { border-color: var(--color-danger-line); }
.evp-live[data-level="down"] .evp-dot { background: var(--color-danger); }
.evp-live[data-level="down"] .evp-live-word { color: var(--color-danger); }

/* loading / no access / error / nothing yet: one card in place of the sections */
.evp-state-card {
  display: grid; justify-items: start; gap: var(--space-4);
  padding: var(--space-10); background: var(--color-surface-2); border: 1px solid var(--color-line-card); border-radius: var(--radius-card);
}
.evp-state-card p { margin: 0; max-width: 70ch; color: var(--color-muted); overflow-wrap: anywhere; }
.evp-state-title { margin: 0; display: flex; align-items: center; gap: var(--space-4); font: 800 var(--text-xl)/1.2 var(--font-sans); font-stretch: var(--wdth-strong); color: var(--color-ink); }
.evp-state-card.is-error { border-color: var(--color-danger-line); }
.evp-state-card.is-error .evp-state-title { color: var(--color-danger); }
.evp-state-card.is-error .evp-state-title::before, .evp-archive.is-error::before {
  content: ""; flex: none; width: 1.125rem; height: 1.125rem; background-color: currentColor;
  -webkit-mask: var(--icon-alert) center / contain no-repeat; mask: var(--icon-alert) center / contain no-repeat;
}
.evp-state-card .card-actions { margin-top: var(--space-4); }
.evp-progress { width: 100%; max-width: 20rem; }

/* filters: the feed's controls; the two price fields; the sort select only where the headers hide */
.evp-filters { margin-bottom: 0; }
.evp-range { display: inline-flex; align-items: center; gap: var(--space-3); flex: none; }
/* relative: the fields' hidden labels (absolute) stay inside the phone rail's scroll box, or they
   would widen the page past the screen */
.evp-range label { position: relative; display: block; }
/* an empty field shows only its placeholder, so its edge is the 3:1 line-input (never line-field) */
.evp-range input {
  width: 6.5rem; height: 2.5rem; padding: 0 var(--space-6);
  background: var(--color-field); border: 1px solid var(--color-line-input); border-radius: var(--radius-control);
  color: var(--color-ink); font-size: var(--text-md); font-variant-numeric: tabular-nums;
}
.evp-range input::placeholder { color: var(--color-subtle); }
.evp-range input:hover { border-color: var(--color-line-strong); }
.evp-range input[aria-invalid="true"] { border-color: var(--color-danger); }
.evp-range-sep { color: var(--color-subtle); }
/* why a price field is marked (its aria-describedby while invalid) */
.evp-range-hint { margin: calc(-1 * var(--space-4)) 0 0; display: flex; align-items: center; gap: var(--space-3); font-size: var(--text-sm); color: var(--color-danger); }
.evp-range-hint::before {
  content: ""; flex: none; width: 1rem; height: 1rem; background-color: currentColor;
  -webkit-mask: var(--icon-alert) center / contain no-repeat; mask: var(--icon-alert) center / contain no-repeat;
}
/* the summary line, and the sort select where the column headers hide (cards) */
.evp-bar { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: var(--space-4) var(--space-6); min-width: 0; }
.evp-sortsel { display: none; flex: none; }
.evp-summary { padding: 0; }
.evp-archive {
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-4) var(--space-6);
  padding: var(--space-4) var(--space-6); border-radius: var(--radius-control);
  background: var(--color-surface); border: 1px solid var(--color-line); font-size: var(--text-sm); color: var(--color-muted);
}
.evp-archive.is-error { color: var(--color-danger); border-color: var(--color-danger-line); }

/* sections */
.evp-sec { display: grid; gap: var(--space-6); min-width: 0; }
.evp-h {
  margin: 0; display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--space-2) var(--space-5);
  font: 800 var(--text-xl)/1.2 var(--font-sans); font-stretch: var(--wdth-strong); letter-spacing: -0.005em; color: var(--color-ink);
}
.evp-h-sub { font: 400 var(--text-sm)/1.3 var(--font-sans); font-stretch: normal; letter-spacing: 0; color: var(--color-subtle); }
.evp-none { margin: 0; color: var(--color-muted); }
.evp-note { margin: 0; max-width: 90ch; font-size: var(--text-xs); line-height: 1.5; color: var(--color-subtle); }

/* the numbers: an overview line, then one card per currency (the dominant one first) */
.evp-stats-body { display: grid; gap: var(--space-6); min-width: 0; }
.evp-overview { margin: 0; display: flex; flex-wrap: wrap; gap: var(--space-2) var(--space-9); font-size: var(--text-md); color: var(--color-muted); }
.evp-ov { display: inline-flex; align-items: center; gap: var(--space-3); }
.evp-ov b { font-weight: 800; color: var(--color-ink); }
/* two currency cards side by side only when each is ≥ 34rem — its tiles then hold a five-figure
   price at 22px (review 2026-09-27: at 1024px a 142px tile let "£12,475.50" spill) */
.evp-curs { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 34rem), 1fr)); gap: var(--space-6); }
.evp-cur { display: grid; align-content: start; gap: var(--space-6); min-width: 0; padding: var(--space-8); background: var(--color-surface-2); border: 1px solid var(--color-line-card); border-radius: var(--radius-card); }
.evp-cur-head { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2) var(--space-5); }
.evp-cur-name { margin: 0; font: 800 var(--text-lg)/1.2 var(--font-sans); font-stretch: var(--wdth-strong); color: var(--color-ink); }
.evp-main-tag { color: var(--color-accent-text); border-color: var(--color-accent-line); }
.evp-cur-sub { font-size: var(--text-sm); color: var(--color-muted); }
.evp-tiles { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--space-4); margin: 0; }
.evp-tile { min-width: 0; padding: var(--space-5) var(--space-6); background: var(--color-bg); border: 1px solid var(--color-line); border-radius: var(--radius-control); }
.evp-tile dt { font-size: var(--text-xs); color: var(--color-muted); }
.evp-tile dd { margin: var(--space-2) 0 0; font: 800 var(--text-2xl)/1.1 var(--font-sans); font-stretch: var(--wdth-counter); color: var(--color-ink); white-space: nowrap; }
.evp-tile dd.is-none { color: var(--color-faint); }
.evp-tile.is-low dd { color: var(--color-wts); }
.evp-wtb-line { margin: 0; font-size: var(--text-sm); color: var(--color-ink-2); }
.evp-wtb-head { display: inline-flex; align-items: center; gap: var(--space-3); }
.evp-wtb-line .evp-sep { color: var(--color-subtle); }
.evp-wtb-word { font-weight: 800; letter-spacing: 0.06em; color: var(--color-wtb); }
.evp-wtb-line b { color: var(--color-ink); }

/* Demand — the WTB heat index (SPEC §9 contract B), in the WTB colour family: a chip with a flame, four
   bars (level 0 "No requests yet" to 4 "On fire": empty bars are outlined, full ones filled) and the
   level's WORD — so the gauge is never colour or shape alone; the counts and the rank as sentences */
.evp-heat-body { display: grid; gap: var(--space-4); min-width: 0; }
.heat-gauge {
  justify-self: start; display: inline-flex; align-items: center; gap: var(--space-4); max-width: 100%;
  min-height: 34px; padding: var(--space-3) var(--space-7) var(--space-3) var(--space-5);
  border-radius: var(--radius-pill); background: var(--color-wtb-bg); color: var(--color-wtb);
}
.heat-gauge[data-level="0"] { background: var(--color-raised); color: var(--color-muted); }
.heat-flame {
  flex: none; width: 18px; height: 18px; background-color: currentColor;
  -webkit-mask: var(--icon-flame) center / contain no-repeat; mask: var(--icon-flame) center / contain no-repeat;
}
.heat-bars { flex: none; display: inline-flex; align-items: flex-end; gap: 3px; }
.heat-bar { width: 6px; border-radius: 2px; box-shadow: inset 0 0 0 1.5px currentColor; }
.heat-bar:nth-child(1) { height: 8px; }
.heat-bar:nth-child(2) { height: 11px; }
.heat-bar:nth-child(3) { height: 14px; }
.heat-bar:nth-child(4) { height: 17px; }
.heat-bar.is-on { background-color: currentColor; }
.heat-word { font: 800 var(--text-md)/1.1 var(--font-sans); font-stretch: var(--wdth-strong); letter-spacing: 0.01em; }
.evp-heat-line { margin: 0; font-size: var(--text-md); line-height: 1.45; color: var(--color-ink-2); }
.evp-heat-line b { font-weight: 800; color: var(--color-ink); }
.evp-heat-line.is-none { color: var(--color-muted); }
.evp-heat-rank { margin: 0; font-size: var(--text-md); line-height: 1.45; color: var(--color-ink-2); }
.evp-heat-rank.is-counts { color: var(--color-muted); }

/* the price trend: HTML axes around an inline SVG stretched to its box (strokes never scale) */
.evp-chart { margin: 0; display: grid; gap: var(--space-6); min-width: 0; padding: var(--space-8); background: var(--color-surface-2); border: 1px solid var(--color-line-card); border-radius: var(--radius-card); }
.evp-chart-top { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: var(--space-4) var(--space-8); }
.evp-legend { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; gap: var(--space-2) var(--space-8); font-size: var(--text-xs); color: var(--color-muted); }
.evp-legend li { display: inline-flex; align-items: center; gap: var(--space-3); }
.evp-sw { flex: none; display: inline-block; }
.evp-sw-wts { width: 9px; height: 9px; border-radius: 50%; background: var(--color-wts); }
.evp-sw-wtb { width: 8px; height: 8px; background: var(--color-wtb); }
.evp-sw-min { width: 18px; height: 2px; border-radius: 1px; background: var(--color-wts); }
.evp-sw-med { width: 18px; height: 0; border-top: 2px dashed var(--color-ink-2); }
.evp-cur-seg button { height: 30px; padding: 0 var(--space-6); font-size: var(--text-sm); }
/* the chart's own controls — the Category select (owner 2026-10-13: "in the chart show a filter by category"),
   then the currency switch — at the card's far end whether or not a legend stands before them (a category with
   too few posts for a chart has none): choosing never moves them. The select is the page's select with its
   label shown, as tall as the switch beside it; a long category name ends in "…" (the whole name is in the list) */
.evp-chart-ctl { display: flex; flex-wrap: wrap; align-items: center; justify-content: flex-end; gap: var(--space-4) var(--space-6); margin-left: auto; min-width: 0; }
.evp-chart-cat { display: inline-flex; align-items: center; gap: var(--space-4); flex: 0 1 auto; min-width: 0; }
.evp-chart-cat-lbl { flex: none; font: 600 var(--text-sm)/1 var(--font-sans); color: var(--color-muted); }
.evp-chart-cat select { height: 2.375rem; min-width: 0; max-width: 16rem; font-size: var(--text-sm); text-overflow: ellipsis; }
.evp-plot { position: relative; height: 15rem; margin: var(--space-4) var(--space-4) 0 3.75rem; }
.evp-grid { position: absolute; inset: 0; pointer-events: none; }
.evp-grid-line { position: absolute; left: 0; right: 0; height: 1px; background: var(--color-line); }
.evp-grid-label { position: absolute; right: calc(100% + var(--space-4)); top: -0.4375rem; font: 400 var(--text-2xs)/1 var(--font-mono); color: var(--color-subtle); white-space: nowrap; }
.evp-svg { position: absolute; inset: 0; width: 100%; height: 100%; overflow: visible; }
.evp-svg path { fill: none; }
.evp-line-min { stroke: var(--color-wts); stroke-width: 2px; stroke-linejoin: round; stroke-linecap: round; }
.evp-line-med { stroke: var(--color-ink-2); stroke-width: 1.5px; stroke-dasharray: 6 5; stroke-linejoin: round; }
.evp-pt { stroke-width: 9px; }
.evp-pt-wts { stroke: var(--color-wts); stroke-linecap: round; opacity: 0.75; }
.evp-pt-wtb { stroke: var(--color-wtb); stroke-linecap: square; }
.evp-pt:hover { opacity: 1; stroke-width: 13px; }
.evp-x { position: relative; height: 1rem; margin: var(--space-2) var(--space-4) 0 3.75rem; font: 400 var(--text-2xs)/1 var(--font-mono); color: var(--color-subtle); }
.evp-x span { position: absolute; top: 0; transform: translateX(-50%); white-space: nowrap; }
.evp-x span.is-first { transform: none; }
.evp-x span.is-last { transform: translateX(-100%); }
/* too few posts for a trend: one sentence in the card instead of a near-empty plot */
.evp-trend-line { margin: 0; max-width: 70ch; font-size: var(--text-md); line-height: 1.5; color: var(--color-ink-2); }
/* a category picked in the chart's control that has too few posts to draw: its sentence stands in the plot's
   place, on the plot's middle, at the plot's and the axis' height (15rem + 8 + the card's 12px gap + 1rem + 4) —
   the sections under the card do not jump up 240px, and back down on the next choice (review 2026-10-13) */
.evp-chart.is-picked .evp-trend-line { display: flex; align-items: center; min-height: calc(15rem + var(--space-4) + var(--space-6) + 1rem + var(--space-2)); }

/* by category: the admin's table of card rows; a long name wraps. No Currency column — each price
   carries its symbol, several currencies are group rows. Only categories with a usable price (owner
   2026-10-13: "do not show listings without a price" — the muted "no usable price" rows went) */
.evp-cat-table { max-width: 52rem; }
.evp-cat-table tbody th { white-space: normal; overflow-wrap: anywhere; }
.evp-cat-table tbody tr.evp-cat-group > th {
  padding: var(--space-6) var(--space-2) var(--space-2); background: none; border-radius: 0;
  font: 800 var(--text-sm)/1.2 var(--font-sans); font-stretch: var(--wdth-strong); color: var(--color-ink);
}

/* every listing: a real table on desktop */
.evp-table { width: 100%; border-collapse: separate; border-spacing: 0; font-size: var(--text-md); }
.evp-table thead th {
  padding: 0 var(--space-5) var(--space-4); text-align: left; vertical-align: bottom; white-space: nowrap;
  font: 700 var(--text-2xs)/1.3 var(--font-sans); letter-spacing: 0.08em; text-transform: uppercase; color: var(--color-faint);
  border-bottom: 1px solid var(--color-line);
}
.evp-table thead th.e-price, .evp-table thead th.e-qty { text-align: right; }
/* a sortable header is a button; the sorted one is blue with its arrow (aria-sort says which). The
   label and its arrow are one unbreakable row (review 2026-09-27: in a narrow column the hidden
   arrow wrapped under "Qty" and lifted it above the other labels). Every header is ONE line (owner
   2026-10-11): the Posted header's zone is its tooltip, no longer a block under it */
.evp-sort {
  display: inline-block;
  padding: var(--space-2) var(--space-1); margin: calc(-1 * var(--space-2)) calc(-1 * var(--space-1));
  border: 0; border-radius: var(--radius-xs); background: none; cursor: pointer;
  font: inherit; letter-spacing: inherit; text-transform: inherit; color: inherit; text-align: inherit;
  vertical-align: bottom;
}
.evp-sort-lbl { display: inline-flex; align-items: center; gap: var(--space-2); white-space: nowrap; }
.evp-sort:hover { color: var(--color-ink); }
.evp-sort-lbl::after {
  content: ""; flex: none; width: 10px; height: 10px; background-color: currentColor; opacity: 0;
  -webkit-mask: var(--icon-down) center / contain no-repeat; mask: var(--icon-down) center / contain no-repeat;
}
.evp-sort:hover .evp-sort-lbl::after { opacity: 0.5; }
.evp-table th[aria-sort] .evp-sort { color: var(--color-accent-text); }
.evp-table th[aria-sort] .evp-sort-lbl::after { opacity: 1; }
.evp-table th[aria-sort="ascending"] .evp-sort-lbl::after { -webkit-mask-image: var(--icon-up); mask-image: var(--icon-up); }
/* both listing tables (every listing, date not stated) share one set of column widths, so stacked
   they line up (review 2026-09-27); Category and Notes share what is left */
@media (min-width: 1024px) {
  .evp-table { table-layout: fixed; }
  .evp-table col.e-posted { width: 7.25rem; }
  .evp-table col.e-type { width: 7.5rem; }
  .evp-table col.e-qty { width: 3.75rem; }
  .evp-table col.e-block { width: 5rem; }
  .evp-table col.e-row { width: 3.75rem; }
  .evp-table col.e-price { width: 9rem; }
  .evp-table col.e-seller { width: 9.5rem; }
}
@media (min-width: 1400px) {
  .evp-table col.e-posted { width: 8rem; }
  .evp-table col.e-qty { width: 4rem; }
  .evp-table col.e-block { width: 6rem; }
  .evp-table col.e-row { width: 4.5rem; }
  .evp-table col.e-price { width: 10rem; }
  .evp-table col.e-seller { width: 11rem; }
}
/* no Block / Row heading over an empty column (owner 2026-10-11: "show Block, Row only when stated —
   otherwise no headings"; event-page.js renderSeatColumns sets the classes from the rows on screen):
     no-block / no-row        no row of either table states one — the column is left out: its col, its
                              header and its (empty) cells go together, so the remaining cols still match
                              their cells, and Category and Notes share its room
     blank-block / blank-row  this table has none, the other one does — only the heading's word goes (its
                              span: not shown, not read; the header cell keeps the header's rule unbroken);
                              the empty track stays, so the two stacked tables still line up column for column
   Below 1024px there is no header, and a card leaves an empty seat cell out */
@media (min-width: 1024px) {
  .evp-table.no-block col.e-block, .evp-table.no-block th.e-block, .evp-table.no-block td.e-block,
  .evp-table.no-row col.e-row, .evp-table.no-row th.e-row, .evp-table.no-row td.e-row { display: none; }
  .evp-table.blank-block th.e-block .evp-th-lbl, .evp-table.blank-row th.e-row .evp-th-lbl { visibility: hidden; }
}
.evp-table tbody td { padding: var(--cell-pad) var(--space-5); text-align: left; vertical-align: middle; border-bottom: 1px solid var(--color-line-soft); }
.evp-row { position: relative; isolation: isolate; }
/* the arrival tint: a card's own ::after; in the desktop table each cell's (a row's absolutely
   positioned ::after is counted as a tenth column by the fixed table layout, which then left the
   table's last sixth empty) */
.evp-row::after, .evp-table .evp-row > td::after {
  content: ""; position: absolute; inset: 0; z-index: -1;
  border-radius: inherit; background: var(--color-accent-soft); opacity: 0; pointer-events: none;
}
.evp-row.is-new { animation: row-in var(--dur-short) var(--ease-out) backwards; }
.evp-row.is-new::after, .evp-table .evp-row.is-new > td::after { animation: arrive var(--dur-flash) var(--ease-out) both; }
.tab-hidden .evp-row.is-new, .tab-hidden .evp-row.is-new::after, .tab-hidden .evp-table .evp-row.is-new > td::after { animation-play-state: paused; }
@media (min-width: 1024px) {
  .evp-table .evp-row::after { content: none; }
  .evp-table .evp-row > td { position: relative; isolation: isolate; }
}
@media (max-width: 1023.98px) {
  .evp-table .evp-row > td::after { content: none; }
}
.evp-row:hover > td { background: var(--color-surface); }
.evp-when { white-space: nowrap; }
.evp-table .clock-date { font-size: var(--text-sm); color: var(--color-ink-2); }
.evp-table .clock { color: var(--color-muted); }
.evp-table td.e-posted .tag { margin-left: var(--space-3); }
.evp-table td.e-type { white-space: nowrap; }
.evp-table td.e-type .tag { margin-left: var(--space-3); }
.evp-table td.e-qty { text-align: right; font: 700 var(--text-lg)/1 var(--font-sans); font-stretch: var(--wdth-num); }
.evp-table td.e-cat .seat-value { font-weight: 600; color: var(--color-ink); overflow-wrap: anywhere; }
.evp-table td.e-block .seat-value, .evp-table td.e-row .seat-value { font: 400 var(--text-xs-plus)/1.2 var(--font-mono); color: var(--color-ink-2); overflow-wrap: anywhere; }
.evp-table td.is-empty .seat-value, .evp-table td.e-qty.is-empty { color: var(--color-faint); }
.evp-table td.e-price { text-align: right; }
.evp-price { display: inline-flex; flex-direction: column; align-items: flex-end; gap: var(--space-1); }
.evp-price-line { display: inline-flex; flex-wrap: wrap; align-items: baseline; justify-content: flex-end; column-gap: 0; }
.evp-amount { font: 800 var(--text-xl)/1.1 var(--font-sans); font-stretch: var(--wdth-num); letter-spacing: -0.01em; color: var(--color-ink); white-space: nowrap; }
.evp-amount.is-none { font-weight: 600; color: var(--color-faint); }
.evp-q { font-size: var(--text-xs); font-weight: 700; color: var(--color-ink-2); white-space: pre; }
.evp-row.t-wts .evp-q { color: var(--color-wts); }
.evp-row.t-wtb .evp-q { color: var(--color-wtb); }
.evp-unit { font-size: var(--text-xs); color: var(--color-muted); white-space: pre; }
.evp-psub { font-size: var(--text-xs); color: var(--color-muted); }
.evp-table td.e-price .price-hist { margin-top: var(--space-2); }
.evp-table td.e-price.has-history .evp-amount { color: var(--color-flash-ink); }
/* a seller's name wraps between its words, never inside one (review 2026-09-27: "Wembl / ey Mike"):
   break-word leaves the longest word as the cell's minimum, so on a card a name that cannot keep a
   word whole beside the chips takes a line of its own; only a word longer than a whole line breaks */
.evp-table .seller-text:not(.is-number), .evp-table .seller-name:not(.is-number) { overflow-wrap: break-word; }
/* a merged row's ×N chip is a button: it opens the list of every post (time and price) */
button.tag-reposts { position: relative; cursor: pointer; font-family: var(--font-sans); background: none; }
button.tag-reposts:hover { color: var(--color-ink); border-color: var(--color-line-strong); background: var(--color-surface); }
button.tag-reposts[aria-expanded="true"] { color: var(--color-accent-text); border-color: var(--color-accent-line); background: var(--color-accent-soft); }
.evp-posts { margin-top: var(--space-3); font: 400 var(--text-2xs)/1.45 var(--font-mono); color: var(--color-muted); white-space: normal; }
.evp-posts-h { display: block; font-family: var(--font-sans); color: var(--color-subtle); }
.evp-posts ol { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; gap: 0 var(--space-5); }
.evp-post-t, .evp-post-p { white-space: nowrap; }
.evp-post-p { color: var(--color-ink-2); }
.evp-notes {
  max-width: 24rem; font-size: var(--text-sm); line-height: 1.38; color: var(--color-muted); overflow-wrap: anywhere;
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; line-clamp: 2; overflow: hidden;
}
.evp-empty { padding: var(--space-10) var(--space-8); }

/* the undated companion: its own heading and why it is not in the numbers */
.evp-undated { padding-top: var(--space-8); border-top: 1px solid var(--color-line); }
.evp-undated-note { margin: 0; max-width: 80ch; font-size: var(--text-sm); color: var(--color-muted); }

/* below 1024px: every listing is a card (the feed's phone artboard):
     1  type (+ ×N) · posted ··········· seller
     2  Qty · category · Block · Row ··· price ("each" under it, as on the feed's cards)
     3  notes
     4  the row's tags (edited), and a merged row's opened list of posts           */
@media (max-width: 1023.98px) {
  .evp-sortsel { display: block; }
  .evp-table, .evp-table > tbody { display: block; }
  /* gone, not clipped: its sort buttons would be six invisible Tab stops (review 2026-09-27); the
     cards carry their own labels and the sort select above them sorts */
  .evp-table > thead, .evp-table > colgroup { display: none; }
  .evp-table > tbody { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--card-gap); }
  .evp-table .evp-row {
    display: flex; flex-wrap: wrap; align-items: center; align-content: flex-start; column-gap: var(--space-3);
    padding: var(--card-pad-top) var(--space-7) var(--card-pad-bottom);
    border-radius: var(--radius-card); background: var(--color-surface-2); border: 1px solid var(--color-line-card);
  }
  .evp-table .evp-row:hover { background: var(--color-field); }
  .evp-table .evp-row > td { display: block; min-width: 0; padding: 0; border: 0; background: none; }
  .evp-table .evp-row:hover > td { background: none; }
  .evp-table .evp-row > td.e-type { order: 1; flex: none; display: flex; align-items: center; gap: var(--space-3); }
  .evp-table .evp-row > td.e-type .tag { margin-left: 0; }
  /* the Posted cell dissolves (as the feed's does): its date + time sit on line 1 and never wrap,
     its tags and a merged row's list of posts close the card */
  .evp-table .evp-row > td.e-posted { display: contents; }
  .evp-table .evp-row > td.e-posted > .evp-when { order: 2; flex: none; white-space: nowrap; }
  .evp-table .evp-row > td.e-posted .clock-date, .evp-table .evp-row > td.e-posted .clock { font-size: var(--text-xs); }
  /* the tags take a line of their own at the end of the card (never beside the price) */
  .evp-table .evp-row > td.e-posted > .evp-tags { order: 11; flex: 1 1 100%; display: flex; flex-wrap: wrap; gap: var(--space-3); margin-top: var(--space-3); }
  .evp-table .evp-row > td.e-posted .tag { margin-left: 0; }
  .evp-table .evp-row > td.e-posted > .evp-posts { order: 12; flex: 1 1 100%; }
  .evp-table .evp-row > td.e-seller { order: 3; flex: 1 1 0%; min-width: auto; display: flex; justify-content: flex-end; text-align: right; }
  .evp-table .evp-row::before { content: ""; order: 4; flex: 0 0 100%; height: 0; }
  .evp-table .evp-row > td.e-qty { order: 5; flex: none; margin: var(--card-line) var(--space-5) 0 0; font: 600 var(--text-sm)/1.3 var(--font-sans); color: var(--color-ink-2); text-align: left; }
  .evp-table .evp-row > td.e-qty::before { content: attr(data-label) " "; font-weight: 400; color: var(--color-subtle); }
  .evp-table .evp-row > td.e-cat { order: 6; flex: 0 1 auto; margin: var(--card-line) var(--space-5) 0 0; font-size: var(--text-sm); }
  /* a long block / row wraps inside its card (review 2026-09-27: flex: none ran it past the edge) */
  .evp-table .evp-row > td.e-block { order: 7; flex: 0 1 auto; min-width: 0; display: flex; align-items: baseline; gap: 5px; margin: var(--card-line) var(--space-5) 0 0; font-size: var(--text-sm); color: var(--color-subtle); }
  .evp-table .evp-row > td.e-row { order: 8; flex: 0 1 auto; min-width: 0; display: flex; align-items: baseline; gap: 5px; margin: var(--card-line) var(--space-5) 0 0; font-size: var(--text-sm); color: var(--color-subtle); }
  .evp-table .evp-row > td.e-block .seat-value, .evp-table .evp-row > td.e-row .seat-value { min-width: 0; }
  .evp-table .evp-row > td.e-block::before, .evp-table .evp-row > td.e-row::before { content: attr(data-label); flex: none; }
  .evp-table .evp-row > td.e-cat.is-empty, .evp-table .evp-row > td.e-block.is-empty, .evp-table .evp-row > td.e-row.is-empty { display: none; }
  /* the price box: never wider than the feed card's, "each" under the number (the feed's cards) */
  .evp-table .evp-row > td.e-price { order: 9; flex: 1 0 auto; max-width: 9rem; margin: var(--card-line) 0 0 auto; }
  .evp-table .evp-row .evp-price-line { display: block; text-align: right; }
  .evp-table .evp-row .evp-unit { display: block; }
  .evp-table .evp-row > td.e-notes { order: 10; flex: 1 1 100%; margin-top: var(--space-4); }
  .evp-table .evp-row > td.e-notes.is-empty { display: none; }
  .evp-table .evp-notes { max-width: none; display: block; -webkit-line-clamp: unset; line-clamp: none; }
  .evp-price { align-items: flex-end; }
}
/* tablets: a two-column board of the cards */
@media (min-width: 768px) and (max-width: 1023.98px) {
  .evp-table > tbody { grid-template-columns: repeat(2, minmax(0, 1fr)); column-gap: var(--card-gap); row-gap: var(--card-gap); align-items: start; }
}
/* phones */
@media (max-width: 767.98px) {
  .evp { gap: var(--space-8); padding-top: var(--space-4); }
  .evp-back { min-height: 44px; }
  .evp-title { font-size: var(--text-2xl); gap: var(--space-3) var(--space-5); }
  .evp-title .crest, .evp-title .crest-none, .evp-event .ev-icon { width: 36px; height: 36px; }
  .evp-title .team { gap: var(--space-5); min-height: 36px; }
  .evp-range input { width: 6.25rem; height: 44px; font-size: var(--text-lg); }
  .evp-cur-seg button { height: 44px; }
  /* the chart's controls take a line of their own under the legend: the Category select fills what the
     currency switch leaves (44px, 16px — a phone never zooms into it), and on the narrowest screens a line of
     its own. Its label stands OVER the field, as a small caption: beside it "All categories" was cut to "All
     catego…" at 375px. The switch lines up with the field, and the chevron keeps to the field's middle */
  .evp-chart-ctl { flex: 1 1 100%; align-items: flex-end; }
  .evp-chart-cat { flex: 1 1 10rem; flex-direction: column; align-items: stretch; gap: var(--space-2); }
  .evp-chart-cat-lbl { font-size: var(--text-xs); }
  .evp-chart-cat select { width: 100%; max-width: none; height: 44px; font-size: var(--text-lg); }
  .evp-chart-cat::after { top: auto; bottom: 15px; margin-top: 0; }
  .evp-cur, .evp-chart { padding: var(--space-6); }
  .evp-tiles { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .evp-tile { padding: var(--space-4) var(--space-5); }
  .evp-tile dd { font-size: var(--text-xl); }
  .evp-plot { height: 12rem; margin-left: 3.25rem; margin-right: var(--space-2); }
  .evp-chart.is-picked .evp-trend-line { min-height: calc(12rem + var(--space-4) + var(--space-6) + 1rem + var(--space-2)); }
  .evp-x { margin-left: 3.25rem; margin-right: var(--space-2); }
  .evp-state-card { padding: var(--space-8) var(--space-7); }
  /* the 📊 button: 32px to see, 44px to touch (the hit area is its own ::before, measured from the
     padding box: 1px border + 6px = 7px past the 32px border box on each side → 44px) */
  .ev-btn::before { content: ""; position: absolute; inset: -7px; }
  /* the ×N chip's list button: 44px tall to touch (a 20px chip + 12px above and below) */
  button.tag-reposts::after { content: ""; position: absolute; inset: -12px -4px; }
}
/* the smallest phones: a 127px price tile still holds "£12,475.50" */
@media (max-width: 374.98px) {
  .evp-tile dd { font-size: var(--text-lg); }
}

/* ==========================================================================
   Identity 2026-10-12 (SPEC §11, DESIGN.md "Identity"): the sign-in card (phone + password), a member's
   account menu and "Change password", a guest's locked seller cell, "My Listing" / "also in N groups", the
   admin's view switch. The same ground, tokens, cards and controls — no new colour (but the yellow of an own
   row: its own --mine-* family, owner 2026-10-13).
   ========================================================================== */

/* ---------- the seller, by phone number ---------- */

/* the number is the link's first line; the seller's own WhatsApp name the quiet second one (never underlined) */
.seller-alias { font-size: var(--text-xs); font-weight: 400; color: var(--color-muted); line-height: 1.3; overflow-wrap: anywhere; }
.seller-link:hover .seller-alias { color: inherit; }
.evp-table .seller-alias { overflow-wrap: break-word; }

/* "My Listing" (owner 2026-10-13: "a listing the user posted — highlight the WHOLE ROW in yellow, in addition
   to the label. Replace 'Yours' with 'My Listing'"): the signed-in member's own row. Three things say it, all
   in the --mine-* yellow: the chip over the number (its words — never colour alone), the row's yellow GROUND,
   and a FULL yellow ring round the row (a ring, never a side stripe — the system's rule for a card's state).
   The ground is as yellow as the faintest text on it allows: every text colour of a row keeps 4.5:1 on it */
.tag-own { background: var(--mine-chip); border-color: var(--mine-line); color: var(--mine); letter-spacing: 0.04em; }
/* the feed's rows are grids: its seller cell stacks the tag over the number as a flex column */
.feed td.c-seller.is-own { display: flex; flex-direction: column; align-items: flex-start; gap: var(--space-1); }
@media (min-width: 1024px) {
  /* the feed's table: the row is the box. It stands 2px inside its slot — taken from its own padding, so the
     table keeps its rhythm — and two own rows in a row never share an edge */
  .feed tr.row.is-own {
    min-height: calc(var(--row-min) - var(--space-2)); margin-block: var(--space-1);
    padding-top: calc(var(--row-pad) - var(--space-1)); padding-bottom: calc(var(--row-pad) - var(--space-1));
    background: var(--mine-bg); border-radius: var(--radius-control); border-bottom-color: transparent;
    box-shadow: inset 0 0 0 1px var(--mine-line);
  }
  .feed tr.row.is-own:hover, .feed tr.row.is-own:focus-visible { background: var(--mine-bg-hover); box-shadow: inset 0 0 0 1px var(--mine); }
  /* opened, it continues into its message like every row: the ring goes round both */
  .feed tr.row.is-own.is-open { margin-bottom: 0; background: var(--mine-bg); border-color: var(--mine-line); border-bottom-color: transparent; border-radius: var(--radius-control) var(--radius-control) 0 0; box-shadow: none; }
  .feed tr.row.is-own.is-open + tr.detail .detail-inner { border-color: var(--mine-line); }
  /* the event page's table is a REAL table from 1024px: its "My Listing" cell stays a table cell (review
     2026-10-12 — as a flex box it stopped at its own content: a taller row left a hole in the hover tint
     under it and its hairline crossed the middle of the row). The tag is a line of its own over the number. */
  .evp-table td.e-seller.is-own .tag-own { display: flex; width: fit-content; margin-bottom: var(--space-1); }
  /* … and its own row is drawn by its CELLS (a table row has no box of its own to round): each cell the yellow
     ground with the ring's line above and below, the first and the last cell closing it with the row's corners.
     The ground stops at the cell's hairline (padding-box), so two own rows never share an edge here either */
  .evp-table .evp-row.is-own > td { background: var(--mine-bg); background-clip: padding-box; border-bottom-color: transparent; box-shadow: inset 0 1px 0 var(--mine-line), inset 0 -1px 0 var(--mine-line); }
  .evp-table .evp-row.is-own > td:first-child { border-radius: var(--radius-control) 0 0 var(--radius-control); box-shadow: inset 1px 1px 0 var(--mine-line), inset 0 -1px 0 var(--mine-line); }
  .evp-table .evp-row.is-own > td:last-child { border-radius: 0 var(--radius-control) var(--radius-control) 0; box-shadow: inset -1px 1px 0 var(--mine-line), inset 0 -1px 0 var(--mine-line); }
  .evp-table .evp-row.is-own:hover > td { background-color: var(--mine-bg-hover); }
}
@media (max-width: 1023.98px) {
  /* cards (the feed's and the match page's): the card's own ground and edge turn yellow */
  .feed tr.row.is-own, .evp-table .evp-row.is-own { background: var(--mine-bg); border-color: var(--mine-line); }
  .feed tr.row.is-own:hover, .evp-table .evp-row.is-own:hover { background: var(--mine-bg-hover); }
  .feed tr.row.is-own.is-open { border-color: var(--mine-line); border-bottom-color: transparent; }
  .feed tr.row.is-own.is-open + tr.detail .detail-inner { border-color: var(--mine-line); }
}
/* forced colours (a Windows contrast theme): grounds are the system's and a box-shadow is not painted, so the
   yellow ground and the ring are gone — an own row / card is ringed with a real outline in the system's
   highlight colour instead (inside its box: nothing is pushed), beside the chip's words. On a desktop the
   feed row's own hairline — transparent above, which that mode would force visible as a stray line under the
   rounded row — takes the canvas colour */
@media (forced-colors: active) {
  .feed tr.row.is-own, .evp-table .evp-row.is-own { outline: 2px solid Highlight; outline-offset: -2px; }
}
@media (forced-colors: active) and (min-width: 1024px) {
  .feed tr.row.is-own { border-bottom-color: Canvas; }
}
/* one offer, several groups: a neutral outlined tag beside "edited" */
.tag-groups { color: var(--color-ink-2); }

/* a GUEST's seller cell: a padlock + "Members only" over "Sign in to contact the seller" — a button that
   opens the sign-in card. Quiet like the seller link beside it: no box of its own, its second line
   underlined like a link; blue under the pointer */
.seller-lock {
  display: inline-grid; grid-template-columns: auto minmax(0, 1fr); align-items: center; gap: var(--space-1) var(--space-3); max-width: 100%;
  min-height: 32px; padding: var(--space-2) var(--space-4); margin: 0 0 0 calc(-1 * var(--space-4));
  border: 0; border-radius: var(--radius-sm); background: none;
  color: var(--color-muted); text-align: left; cursor: pointer;
  font: 600 var(--text-sm)/1.3 var(--font-sans);
  transition: color var(--dur-micro) var(--ease-out), background-color var(--dur-micro) var(--ease-out);
}
.seller-lock:hover { color: var(--color-accent-text); background: var(--color-accent-soft); }
.seller-lock:active { opacity: 0.75; }
.seller-lock:disabled { opacity: 0.5; cursor: not-allowed; }
.lock-glyph { width: 15px; height: 15px; flex: none; }
.lock-word { color: var(--color-ink-2); white-space: nowrap; }
.seller-lock:hover .lock-word { color: inherit; }
/* the second line spans the padlock's column too, so "Sign in to contact the seller" fits the Seller track */
.lock-sub {
  grid-column: 1 / -1; font-size: var(--text-2xs); font-weight: 500; line-height: 1.35;
  text-decoration: underline; text-decoration-color: var(--color-link-line); text-underline-offset: 3px;
}
.seller-lock:hover .lock-sub { text-decoration-color: currentColor; }

/* a number or a link the runner removed from a guest's copy: a small dashed word, never the brackets */
.redacted {
  display: inline-block; padding: 0 var(--space-2); margin: 0 var(--space-1);
  border: 1px dashed var(--color-line-strong); border-radius: var(--radius-xs);
  font: 600 var(--text-2xs)/1.5 var(--font-sans); letter-spacing: 0.06em; text-transform: uppercase;
  color: var(--color-subtle); vertical-align: 1px; white-space: nowrap;
}

/* a guest's opened row: where a member reads the message — what is locked, and the way in */
.msg-locked {
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-4) var(--space-6);
  margin-top: var(--space-4); padding: var(--space-5) var(--space-6);
  border: 1px dashed var(--color-line-strong); border-radius: var(--radius-btn);
  color: var(--color-muted);
}
.msg-locked-text { flex: 1 1 16rem; min-width: 0; }
.repost-meta:last-child { border-right: 0; } /* a guest's post line has no message beside it */
.feed tr.detail.is-locked .repost { grid-template-columns: minmax(0, 1fr); }

/* ---------- "My listings" (the filter rail) ---------- */

.mine-toggle {
  display: inline-flex; flex: none; align-items: center; gap: var(--space-4);
  height: 2.5rem; padding: 0 var(--space-5) 0 var(--space-6); margin: 0;
  border: 1px solid var(--color-line-field); border-radius: var(--radius-control);
  background: var(--color-field); color: var(--color-ink-2);
  font: 600 var(--text-md)/1 var(--font-sans); white-space: nowrap; cursor: pointer;
  transition: background-color var(--dur-micro) var(--ease-out), border-color var(--dur-micro) var(--ease-out);
}
.mine-toggle:hover { background: var(--color-raised); border-color: var(--color-line-strong); color: var(--color-ink); }
.mine-toggle:active { transform: translateY(1px); }
.mine-toggle:disabled { opacity: 0.5; cursor: not-allowed; }
.mine-toggle[aria-pressed="true"] { background: var(--color-accent-tint); border-color: var(--color-accent); color: var(--color-accent-tint-ink); }
.mine-toggle .count {
  display: inline-flex; align-items: center; justify-content: center; min-width: 1.375rem; height: 1.25rem; padding: 0 var(--space-3);
  border-radius: var(--radius-pill); background: var(--color-raised); color: var(--color-ink-2);
  font: 700 var(--text-2xs)/1 var(--font-sans);
}
.mine-toggle[aria-pressed="true"] .count { background: var(--color-accent); color: var(--color-accent-ink); }

/* ---------- the sign-in card's form: phone + password (the screen around it: "Access 2026-10-12" below) ---------- */

.card-signin { position: relative; }
.card-signin .card-brand { gap: var(--space-5); padding: var(--space-9) var(--space-10) var(--space-8); }
.card-signin .card-logo { width: 64px; height: 64px; }
.card-lead { font-size: var(--text-md); }
/* why the viewer is back here (signed out by the admin's change): a state — amber, an icon, words */
.card-notice {
  display: flex; align-items: flex-start; gap: var(--space-4);
  padding: var(--space-5) var(--space-6); margin: var(--space-2) 0 var(--space-6);
  border: 1px solid var(--color-warn-line); border-radius: var(--radius-btn);
  color: var(--color-warn); font-weight: 600;
}
.card p.card-notice { color: var(--color-warn); margin: var(--space-2) 0 var(--space-6); }
.card-notice::before {
  content: ""; flex: none; width: 1rem; height: 1rem; margin-top: var(--space-1); background-color: currentColor;
  -webkit-mask: var(--icon-alert) center / contain no-repeat; mask: var(--icon-alert) center / contain no-repeat;
}
/* why the screen opened by itself — a match-page link, and match pages are for signed-in members (owner
   2026-10-13): a calm line, the neutral ring and the info icon of "registration is closed" — never the notice's
   amber: nothing went wrong */
.card p.card-info {
  display: flex; align-items: flex-start; gap: var(--space-4);
  padding: var(--space-5) var(--space-6); margin: var(--space-2) 0 var(--space-6);
  border: 1px solid var(--color-line-strong); border-radius: var(--radius-btn);
  color: var(--color-ink-2); font-size: var(--text-md);
}
.card-info::before {
  content: ""; flex: none; width: 1rem; height: 1rem; margin-top: var(--space-1); background-color: var(--color-muted);
  -webkit-mask: var(--icon-info) center / contain no-repeat; mask: var(--icon-info) center / contain no-repeat;
}
.card p.card-ok { color: var(--color-ok); font-weight: 600; }
.signin-form { display: grid; gap: var(--space-7); margin-top: var(--space-8); min-width: 0; }
.signin-form .field { display: grid; gap: var(--space-3); min-width: 0; }
/* 16px in the fields at every width (a phone never zooms into them), 44px tall */
.signin-form .field-input { min-height: 2.75rem; font-size: var(--text-lg); }
.phone-row { display: grid; grid-template-columns: minmax(0, 9.75rem) minmax(0, 1fr); gap: var(--space-4); }
/* the country picker is a field like the two beside it: 16px too (review 2026-10-12 — at 14px an iPhone
   zoomed the page into the dialog when it was tapped, and the dialog stayed zoomed) */
.signin-form .country-select select {
  width: 100%; height: 2.75rem; min-height: 2.75rem; padding: 0 2rem 0 var(--space-6);
  border: 1px solid var(--color-line-input); border-radius: var(--radius-btn);
  font-size: var(--text-lg); font-weight: 500; text-overflow: ellipsis;
}
.signin-form .country-select select:hover { border-color: var(--color-muted); }
.signin-form .field-hint { margin: 0; line-height: 1.4; }
.signin-form .field-hint.is-ok { color: var(--color-ink-2); }
.password-row { position: relative; display: flex; min-width: 0; }
.password-row .field-input { padding-right: 4.25rem; }
.pw-toggle {
  position: absolute; top: 0; right: 0; height: 100%; min-width: 3.75rem; padding: 0 var(--space-6);
  font-size: var(--text-sm); font-weight: 600; color: var(--color-muted);
}
/* an error on the card is the error colour (the card's paragraphs are muted) with the alert icon before its
   words; in the form its slot sits close under the fields */
.card p.card-error { color: var(--color-danger); }
.card-error:not(:empty)::before {
  content: ""; display: inline-block; width: 1rem; height: 1rem; margin-right: var(--space-3); vertical-align: -3px; background-color: currentColor;
  -webkit-mask: var(--icon-alert) center / contain no-repeat; mask: var(--icon-alert) center / contain no-repeat;
}
.signin-form .card-error { margin: calc(-1 * var(--space-3)) 0; }
/* a phone error sits inside the phone field's own block, right under its hint (account.js): the block's gap spaces it */
.signin-form .field > .card-error { margin: 0; }
.signin-form .card-actions { margin-top: 0; }
.signin-submit { flex: 1 1 10rem; min-height: 2.75rem; }
.card p.signin-forgot { margin: var(--space-6) 0 0; font-size: var(--text-sm); color: var(--color-muted); }

/* the card over the feed: a native dialog (the page behind is inert; Esc closes). The veil is the page
   ground itself, mostly opaque — no blur */
dialog.sheet {
  width: min(26rem, calc(100vw - 2rem)); max-width: none; max-height: calc(100dvh - 2rem);
  margin: auto; padding: 0; border: 0; border-radius: var(--radius-panel);
  background: none; color: var(--color-ink); overflow: auto; overscroll-behavior: contain;
}
dialog.sheet::backdrop { background-color: var(--color-bg); opacity: 0.86; }
dialog.sheet .card { max-width: none; border-color: var(--color-line-strong); }
.sheet-close {
  position: absolute; top: var(--space-4); right: var(--space-4);
  display: inline-flex; align-items: center; justify-content: center; width: 44px; height: 44px; padding: 0;
  border: 0; border-radius: var(--radius-control); background: none; color: var(--color-muted); cursor: pointer;
}
.sheet-close::before {
  content: ""; width: 18px; height: 18px; background-color: currentColor;
  -webkit-mask: var(--icon-close) center / contain no-repeat; mask: var(--icon-close) center / contain no-repeat;
}
.sheet-close:hover { background: var(--color-raised); color: var(--color-ink); }
.sheet-close:active { transform: translateY(1px); }

/* ---------- the header: the signed-in account, an admin's view switch ---------- */

.account { position: relative; display: flex; min-width: 0; }
.btn-account {
  display: inline-flex; align-items: center; gap: var(--space-4);
  height: 36px; padding: 0 var(--space-4) 0 var(--space-5);
  border: 1px solid var(--color-line-strong); border-radius: var(--radius-btn);
  background: var(--color-surface-2); color: var(--color-ink);
  font: 600 var(--text-sm)/1 var(--font-sans); white-space: nowrap; cursor: pointer;
  transition: background-color var(--dur-micro) var(--ease-out), transform var(--dur-micro) var(--ease-out);
}
.btn-account:hover { background: var(--color-raised); }
.btn-account:active { transform: translateY(1px); }
.btn-account:disabled { opacity: 0.5; cursor: not-allowed; }
.btn-account[aria-expanded="true"] { background: var(--color-accent-tint); border-color: var(--color-accent-tint-line); color: var(--color-accent-tint-ink); }
.account-glyph { width: 18px; height: 18px; flex: none; color: var(--color-muted); }
.btn-account[aria-expanded="true"] .account-glyph { color: inherit; }
.account-pre { font-weight: 400; color: var(--color-muted); }
.btn-account[aria-expanded="true"] .account-pre { color: inherit; }
.account-phone { font-family: var(--font-mono); font-weight: 500; }
.account-caret {
  flex: none; width: 14px; height: 14px; background-color: currentColor;
  -webkit-mask: var(--icon-chevron) center / contain no-repeat; mask: var(--icon-chevron) center / contain no-repeat;
}
.btn-account[aria-expanded="true"] .account-caret { transform: rotate(180deg); }
.account-menu {
  position: absolute; top: calc(100% + var(--space-3)); right: 0; z-index: 20;
  display: grid; gap: var(--space-1); min-width: 15rem; padding: var(--space-3);
  background: var(--color-surface-2); border: 1px solid var(--color-line-strong); border-radius: var(--radius-control);
  box-shadow: 0 16px 40px var(--color-bg);
}
.account-who { margin: 0 0 var(--space-2); padding: var(--space-3) var(--space-5) var(--space-5); border-bottom: 1px solid var(--color-line); font-size: var(--text-xs); color: var(--color-muted); }
.account-who-phone { display: block; margin-top: var(--space-1); font: 500 var(--text-md)/1.3 var(--font-mono); color: var(--color-ink); }
.account-item {
  display: flex; align-items: center; width: 100%; min-height: 40px; padding: 0 var(--space-5);
  border: 0; border-radius: var(--radius-sm); background: none; color: var(--color-ink);
  font: 600 var(--text-md)/1 var(--font-sans); text-align: left; cursor: pointer;
}
.account-item:hover { background: var(--color-raised); }
.account-item:active { transform: translateY(1px); }
.account-item:disabled { opacity: 0.5; cursor: not-allowed; }

/* Admin / Member / Guest — the page's segmented control, a size down (it sits in the header) */
.view-switch button { height: 28px; padding: 0 var(--space-5); font-size: var(--text-sm); }

/* "Previewing as …" under the header: a state (amber, like demo mode), its words, the way back */
.preview-bar { padding: var(--space-4) var(--page-pad); background: var(--color-surface); border-bottom: 1px solid var(--color-line); }
.preview-chip {
  display: inline-grid; grid-template-columns: auto minmax(0, 1fr) auto; align-items: center; gap: var(--space-1) var(--space-5); max-width: 100%;
  margin: 0; padding: var(--space-2) var(--space-6);
  border: 1px solid var(--color-warn-line); border-radius: var(--radius-control);
  color: var(--color-warn); font-size: var(--text-sm); font-weight: 600;
}
.preview-dot { flex: none; width: 8px; height: 8px; border-radius: 50%; background: var(--color-warn); }
.preview-text { min-width: 0; }
.preview-back { font-size: var(--text-sm); font-weight: 700; color: var(--color-ink); text-decoration: underline; text-decoration-color: var(--color-link-line); text-underline-offset: 3px; }

/* ---------- widths ---------- */

@media (max-width: 1199.98px) {
  /* the admin's switch takes a line of its own under the header's first row (it is the header's last
     item: DOM order = visual order), its three buttons sharing the width */
  .topbar { flex-wrap: wrap; row-gap: 0; }
  .view-switch { flex: 1 1 100%; margin-bottom: var(--space-4); }
  .view-switch button { flex: 1 1 0; justify-content: center; }
}
@media (min-width: 768px) and (max-width: 1023.98px) {
  .account-pre { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
}
@media (max-width: 1023.98px) {
  /* cards: the locked cell is one line on the card's first line — the padlock + "Members only" (its second
     line is read, not shown: the opened card says it in full) — with a 44px target that does not make
     the line taller (the seller link's own trick) */
  .seller-lock { min-height: 44px; margin: calc(-1 * var(--space-6)) calc(-1 * var(--space-2)) calc(-1 * var(--space-6)) 0; padding: var(--space-6) var(--space-2); }
  .lock-sub { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
  /* "My Listing" sits before the number on the card's first line (both tables are cards here: the event
     table's seller cell is a flex item of its card — styles further up) */
  .feed td.c-seller.is-own, .evp-table td.e-seller.is-own { flex-direction: row; align-items: center; justify-content: flex-end; gap: var(--space-3); }
}
@media (max-width: 767.98px) {
  /* a member's account is a 44px person icon (its name says "Signed in as +…"); the menu opens under it */
  .btn-account { width: 44px; height: 44px; padding: 0; justify-content: center; border: 0; background: none; border-radius: var(--radius-control); color: var(--color-ink-2); }
  .account-glyph { width: 22px; height: 22px; color: inherit; }
  .account-label, .account-caret { display: none; }
  .account-menu { position: fixed; top: calc(var(--header-h) + var(--space-2)); right: var(--space-4); left: var(--space-4); min-width: 0; }
  .account-item { min-height: 44px; }
  .view-switch { margin-bottom: var(--space-4); }
  .view-switch button { height: 44px; }
  .mine-toggle { height: 44px; }
  /* the way back under the sentence, lined up with it */
  .preview-chip { display: grid; grid-template-columns: auto minmax(0, 1fr); align-items: start; padding: var(--space-4) var(--space-6) 0; }
  .preview-dot { margin-top: var(--space-3); }
  .preview-back { grid-column: 2; justify-self: start; justify-content: flex-start; }
  dialog.sheet { width: calc(100vw - 1.5rem); max-height: calc(100dvh - 1.5rem); }
  .card-signin .card-body { padding: var(--space-9) var(--space-8) var(--space-9); }
  .card-signin .card-brand { padding: var(--space-8) var(--space-8) var(--space-7); }
  .msg-locked .btn { flex: 1 1 auto; }
}
@media (max-width: 374.98px) {
  /* the narrowest phones: the country over the number, each on the full width */
  .phone-row { grid-template-columns: minmax(0, 1fr); }
}

/* ==========================================================================
   Access 2026-10-12 (SPEC §12, DESIGN.md "Access 2026-10-12") — the sign-in screen comes first and is split
   in two ("TicketHub member" / "Not a member yet"), "Continue as guest", the check card of a new account,
   the access bar of somebody who waits for approval, the role under the number in the account panel, and
   the admin's Registrations area. The same ground, tokens, cards and controls — no new colour, no new token.
   Hallmark · inside the locked system · pre-emit critique: P4 H4 E4 S5 R4 V4
   ========================================================================== */

/* ---------- the sign-in screen: two tabs, one panel ---------- */

.card-access h2 { margin-bottom: var(--space-8); }
/* the split: the page's segmented control at the card's full width — the selected half the brand blue */
.signin-tabs {
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--space-1);
  padding: var(--space-2); border: 1px solid var(--color-line-field); border-radius: var(--radius-control);
  background: var(--color-field);
}
.signin-tab {
  display: inline-flex; align-items: center; justify-content: center; min-height: 2.75rem; padding: var(--space-3) var(--space-4);
  border: 0; border-radius: var(--radius-seg); background: none; color: var(--color-muted);
  font: 600 var(--text-md)/1.2 var(--font-sans); text-align: center; overflow-wrap: anywhere; cursor: pointer;
  transition: background-color var(--dur-micro) var(--ease-out), color var(--dur-micro) var(--ease-out), transform var(--dur-micro) var(--ease-out);
}
.signin-tab:hover { color: var(--color-ink); }
.signin-tab:active { transform: translateY(1px); }
.signin-tab:disabled { opacity: 0.5; cursor: not-allowed; }
.signin-tab[aria-selected="true"] { background: var(--color-accent); color: var(--color-accent-ink); }
.signin-panel { margin-top: var(--space-9); min-width: 0; }
.signin-panel:focus { outline: none; } /* a panel the focus is only parked on is no control */
.signin-panel .signin-form { margin-top: var(--space-7); }
.card .signin-h { margin: 0 0 var(--space-3); font: 700 var(--text-lg)/1.3 var(--font-sans); font-stretch: var(--wdth-strong); color: var(--color-ink); }
.card p.signin-more { margin: var(--space-7) 0 0; font-size: var(--text-md); }
.card p.signin-more + p.signin-forgot { margin-top: var(--space-3); }
.signin-switch { font-weight: 600; color: var(--color-ink); text-decoration: underline; text-decoration-color: var(--color-link-line); text-underline-offset: 4px; text-align: left; }
.signin-switch:hover { color: var(--color-accent-text); }
.card p.signin-closed-line { color: var(--color-subtle); font-size: var(--text-sm); }
/* registration is closed: a calm state — the neutral ring and the info icon, never the error orange */
.card p.signin-closed {
  display: flex; align-items: flex-start; gap: var(--space-4);
  margin: 0; padding: var(--space-6); border: 1px solid var(--color-line-strong); border-radius: var(--radius-btn);
  color: var(--color-ink-2); font-size: var(--text-md);
}
.signin-closed::before {
  content: ""; flex: none; width: 1rem; height: 1rem; margin-top: var(--space-1); background-color: var(--color-muted);
  -webkit-mask: var(--icon-info) center / contain no-repeat; mask: var(--icon-info) center / contain no-repeat;
}

/* a new password: three small bars under the field (decoration — the line under them says it in words) */
.pw-meter { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--space-2); height: 4px; }
.pw-meter > span { border-radius: var(--radius-pill); background: var(--color-raised); transition: background-color var(--dur-micro) var(--ease-out); }
.pw-meter[data-level="1"] > .pw-bar-1,
.pw-meter[data-level="2"] > .pw-bar-1, .pw-meter[data-level="2"] > .pw-bar-2,
.pw-meter[data-level="3"] > span { background: var(--color-accent); }
.signin-form .pw-hint[data-level="2"], .signin-form .pw-hint[data-level="3"] { color: var(--color-ink-2); }

/* the gate's quiet first line under the title: the way down to "Continue as guest" */
.card p.signin-skip-line { margin: calc(-1 * var(--space-5)) 0 var(--space-7); font-size: var(--text-sm); }
.signin-skip { font-size: var(--text-sm); font-weight: 500; color: var(--color-muted); white-space: normal; text-align: left; text-decoration: underline; text-decoration-color: var(--color-link-line); text-underline-offset: 3px; }
.signin-skip:hover { color: var(--color-ink); }
/* over the feed (the dialog): the form's button row stays in reach at the bottom of a short screen while the
   card scrolls (a long error line pushed "Sign in" out of the dialog on a 560px-tall phone) */
.card-signin.is-dialog .signin-form .card-actions {
  position: sticky; bottom: 0; z-index: 1; margin: 0 calc(-1 * var(--space-2)); padding: var(--space-5) var(--space-2);
  background: var(--color-surface-2);
}
.card-signin.is-dialog .card-body > h2 { padding-right: 2.75rem; } /* (clear of the Close button) */
.card-signin.is-dialog { overflow: visible; } /* (the dialog clips and scrolls: the button row sticks to ITS bottom edge) */
/* where height is scarce — a laptop's viewport (650–900px), a phone — the brand head of the sign-in screen and of
   the check card is ONE line (the logo small, beside its kicker): the form and its button stay in sight */
@media (max-height: 920px) {
  .card-access .card-brand, .card-check .card-brand { flex-direction: row; gap: var(--space-5); padding: var(--space-6) var(--space-10); }
  .card-access .card-logo, .card-check .card-logo { width: 40px; height: 40px; }
}

/* "Continue as guest": under a hairline, a secondary button and what a guest does not see */
.signin-guest-box { display: grid; gap: var(--space-5); margin-top: var(--space-10); padding-top: var(--space-9); border-top: 1px solid var(--color-line-strong); }
.signin-guest { width: 100%; min-height: 2.75rem; }
.card p.signin-guest-note { margin: 0; font-size: var(--text-sm); line-height: 1.5; color: var(--color-muted); }

/* ---------- the check card: what really happened so far, then the answer ---------- */

.card-check .check-who { margin-bottom: var(--space-8); }
.check-steps { list-style: none; margin: 0 0 var(--space-8); padding: 0; display: grid; gap: var(--space-5); }
.check-step { display: flex; align-items: center; gap: var(--space-5); font-size: var(--text-md); color: var(--color-subtle); }
.check-step.is-done { color: var(--color-ink-2); }
.check-step.is-now { color: var(--color-ink); font-weight: 600; }
.check-step.is-failed { color: var(--color-danger); font-weight: 600; }
.check-mark {
  flex: none; display: inline-flex; align-items: center; justify-content: center; width: 1.25rem; height: 1.25rem;
  border: 1px solid var(--color-line-strong); border-radius: var(--radius-pill);
}
.check-step.is-done .check-mark { border-color: var(--color-ok-line); color: var(--color-ok); }
.check-step.is-done .check-mark::before {
  content: ""; width: 0.75rem; height: 0.75rem; background-color: currentColor;
  -webkit-mask: var(--icon-check) center / contain no-repeat; mask: var(--icon-check) center / contain no-repeat;
}
.check-step.is-now .check-mark { border-color: var(--color-accent-line); border-top-color: var(--color-accent); animation: spin var(--dur-spin) linear infinite; }
.check-step.is-failed .check-mark { border-color: var(--color-danger-line); }
.check-step.is-failed .check-mark::before {
  content: ""; width: 0.75rem; height: 0.75rem; background-color: currentColor;
  -webkit-mask: var(--icon-alert) center / contain no-repeat; mask: var(--icon-alert) center / contain no-repeat;
}
/* the bar moves only when a step is really done (its fill is scaled by account.js — nothing runs on a timer) */
.check-bar { height: 10px; border-radius: var(--radius-pill); background: var(--color-field); overflow: hidden; }
.check-fill { display: block; height: 100%; border-radius: var(--radius-pill); background: var(--color-accent); transform-origin: left center; transition: transform var(--dur-micro) var(--ease-out); }
.card p.check-count { margin: var(--space-4) 0 0; font: 500 var(--text-sm)/1.4 var(--font-mono); color: var(--color-muted); font-variant-numeric: tabular-nums; }
/* (the seconds sit beside the status line, outside it — account.js: nothing is announced every second) */
.card-check .card-notice { margin-top: var(--space-8); font-weight: 500; }
.card-check .card-error { margin-top: var(--space-8); }
/* the card's two messages are live regions that are THERE from the start (a sentence written into them is
   announced); empty, they take no room and draw nothing — but stay in the accessibility tree */
.card-check .check-slow:empty, .card-check .check-error:empty { min-height: 0; margin: 0; padding: 0; border: 0; }
.card-check .check-slow:empty::before { content: none; }
.card-check h2:focus, .card-access h2:focus { outline: none; } /* a heading the focus is moved to is no control */

/* the number of the "not in the list" card: said, not edited */
.nit-phone { gap: var(--space-2); }
.nit-number { font: 600 var(--text-lg)/1.3 var(--font-mono); color: var(--color-ink); }

/* ---------- the access bar: somebody who registered and is not approved yet (a state, in words) ---------- */

.access-bar { padding: var(--space-4) var(--page-pad); background: var(--color-surface); border-bottom: 1px solid var(--color-line); }
.access-chip {
  display: inline-grid; grid-template-columns: auto minmax(0, 1fr) auto; align-items: center; gap: var(--space-1) var(--space-5); max-width: min(100%, 110ch);
  margin: 0; padding: var(--space-3) var(--space-6);
  border: 1px solid var(--color-accent-tint-line); border-radius: var(--radius-control);
  background: var(--color-accent-soft); color: var(--color-accent-tint-ink); font-size: var(--text-sm); font-weight: 500; line-height: 1.45;
}
.access-dot { flex: none; width: 8px; height: 8px; border-radius: 50%; background: var(--color-accent); }
.access-text { min-width: 0; overflow-wrap: anywhere; }
.access-action { font-size: var(--text-sm); font-weight: 700; color: var(--color-ink); text-decoration: underline; text-decoration-color: var(--color-link-line); text-underline-offset: 3px; }
/* the sentence takes the focus when the guest view opens right after "Register" (app.js): it is no control */
.access-chip:focus { outline: none; }
/* still being checked: the dot breathes like the LIVE light */
.access-bar[data-kind="check"] .access-dot { animation: pulse var(--dur-pulse) var(--ease-in-out) infinite; }
/* not approved: the neutral ring — a "no" said calmly, never the error orange */
.access-bar[data-kind="no"] .access-chip { border-color: var(--color-line-strong); background: var(--color-surface-2); color: var(--color-ink-2); }
.access-bar[data-kind="no"] .access-dot { background: var(--color-muted); }
/* approved while the page was open: the success teal */
.access-bar[data-kind="ok"] .access-chip { border-color: var(--color-ok-line); background: none; color: var(--color-ok); font-weight: 600; }
.access-bar[data-kind="ok"] .access-dot { background: var(--color-ok); }

/* the role under the number in the account panel */
.account-role { display: block; margin-top: var(--space-2); font: 600 var(--text-xs)/1.3 var(--font-sans); color: var(--color-muted); }

/* ---------- Admin → Members: the Registrations area ---------- */

.access-notes { display: grid; gap: var(--space-3); }
.access-notes .action-msg { display: block; max-width: 90ch; margin: 0; padding: var(--space-5) var(--space-6); border: 1px solid var(--color-warn-line); border-radius: var(--radius-btn); font-size: var(--text-sm); }
.reg-head { display: grid; gap: var(--space-3); min-width: 0; }
.reg-lead { max-width: 90ch; }
/* the sign-in screen stands higher on the page than a plain card (the form and its button in sight on a laptop;
   "Continue as guest" is under them — the card's first line goes to it) */
.gate:has(.card-access) { padding-top: var(--space-10); }
.reg-empty { margin: 0; padding: var(--space-7) var(--space-8); border: 1px solid var(--color-line-card); border-radius: var(--radius-card); background: var(--color-surface-2); color: var(--color-muted); font-size: var(--text-md); }
.reg-bulk { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-4) var(--space-8); min-width: 0; }
.reg-pick { display: inline-flex; align-items: center; gap: var(--space-4); font-size: var(--text-sm); color: var(--color-ink-2); cursor: pointer; }
.reg-pick input { width: 1.125rem; height: 1.125rem; margin: 0; accent-color: var(--color-accent); cursor: pointer; }
/* a row's tick: the LABEL is the target — 24px here (the box alone was 18), 44px on a phone (below) */
.reg-pick-row { justify-content: center; min-width: 1.5rem; min-height: 1.5rem; }
/* a registration's row — the roster's row shape, with classes of its own: who (the name, then the number), when, the answer */
.reg-list { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--space-3); min-width: 0; }
.reg-row {
  display: grid; grid-template-columns: minmax(0, 1.4fr) minmax(0, 1fr) auto; grid-template-areas: "id dates actions";
  align-items: center; gap: var(--space-3) var(--space-8); min-width: 0;
  padding: var(--space-5) var(--space-8);
  background: var(--color-surface-2); border: 1px solid var(--color-line-card); border-radius: var(--radius-control);
}
.reg-id { grid-area: id; display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-3) var(--space-5); min-width: 0; }
.reg-phone { font: 600 var(--text-md)/1.3 var(--font-mono); white-space: nowrap; color: var(--color-ink); text-decoration: underline; text-decoration-color: var(--color-link-line); text-underline-offset: 4px; }
.reg-phone:hover { color: var(--color-accent-text); }
.reg-dates { grid-area: dates; font-size: var(--text-xs); color: var(--color-subtle); font-variant-numeric: tabular-nums; }
.reg-actions { grid-area: actions; justify-self: end; min-width: 0; }
.reg-buttons { display: flex; flex-wrap: wrap; align-items: center; justify-content: flex-end; gap: var(--space-4) var(--space-6); }
.reg-row .people-confirm { max-width: 30rem; }
.reg-notice { grid-column: 1 / -1; margin: 0; flex-wrap: wrap; align-items: center; }
.reg-name { font: 700 var(--text-md)/1.3 var(--font-sans); color: var(--color-ink); overflow-wrap: anywhere; unicode-bidi: plaintext; text-align: left; }
.reg-name.is-none { font-weight: 500; color: var(--color-subtle); }
.reg-fold > .av-help { max-width: 90ch; }
.reg-fold > .btn-link { justify-self: start; }
/* self-registered: a word in a tag (the blue tint — look at it), an admin number: a quiet outlined word */
.tag.tag-self { color: var(--color-accent-tint-ink); background: var(--color-accent-tint); border-color: var(--color-accent-tint-line); }
.tag.tag-admin { color: var(--color-ink-2); border-color: var(--color-line-strong); }

@media (max-width: 1199.98px) {
  .reg-row { grid-template-columns: minmax(0, 1fr) auto; grid-template-areas: "id actions" "dates actions"; }
}
@media (max-width: 767.98px) {
  .signin-tab { min-height: 44px; }
  .signin-guest { min-height: 44px; }
  /* the skip line: a 44px target that does not make its line taller */
  .signin-skip { margin-block: calc(-1 * var(--space-5)); justify-content: flex-start; }
  /* the way on under the sentence, lined up with it */
  .access-chip { display: grid; grid-template-columns: auto minmax(0, 1fr); align-items: start; padding: var(--space-4) var(--space-6); }
  .access-dot { margin-top: var(--space-3); }
  .access-action { grid-column: 2; justify-self: start; justify-content: flex-start; }
  .reg-row { grid-template-columns: minmax(0, 1fr); grid-template-areas: "id" "dates" "actions"; padding: var(--space-4) var(--space-6) var(--space-5); }
  .reg-actions { justify-self: stretch; }
  .reg-buttons { justify-content: flex-start; }
  .reg-phone { display: inline-flex; align-items: center; min-height: 44px; }
  .reg-pick { min-height: 44px; }
  .reg-pick-row { min-width: 44px; margin-left: calc(-1 * var(--space-4)); }
  .reg-pick input { width: 1.375rem; height: 1.375rem; }
}
@media (max-width: 374.98px) {
  .signin-tab { font-size: var(--text-sm); padding-inline: var(--space-2); }
}
@media (prefers-reduced-motion: reduce) {
  .check-step.is-now .check-mark, .access-bar[data-kind="check"] .access-dot { animation: none; }
  .signin-tab, .check-fill, .pw-meter > span { transition: none; }
  .signin-tab:active { transform: none; }
}

/* ==========================================================================
   Display names 2026-10-13 (SPEC §13, DESIGN.md "Display names 2026-10-13") — owner: "Allow a Display Name
   next to every user - the user name that is shown under the phone number." The name under a seller's number
   is the seller cell's own second line (.seller-alias — no new look); what is new is the account's "Display
   name" dialog, the one calm bar that offers it, the account panel's own name, and the name line of the
   admin's Sellers / Members rows. The same ground, tokens, cards and controls — no new colour, no new token.
   ========================================================================== */

/* ---------- the account panel: the account's own display name, under its number ---------- */

.account-name { display: block; margin-top: var(--space-1); font: 600 var(--text-sm)/1.3 var(--font-sans); color: var(--color-ink-2); overflow-wrap: anywhere; unicode-bidi: plaintext; text-align: left; }

/* ---------- the bar: "Add your display name" — an invitation, said calmly (the neutral ring), two actions ---------- */

.access-bar[data-kind="name"] .access-chip { grid-template-columns: auto minmax(0, 1fr) auto auto; border-color: var(--color-line-strong); background: var(--color-surface-2); color: var(--color-ink-2); }
/* "Not now": the quiet one of the two */
.access-action.access-more { font-weight: 500; color: var(--color-muted); }
.access-action.access-more:hover { color: var(--color-ink); }

/* ---------- the "Display name" dialog (the sign-in card's form, one field) ---------- */

.card-profile h2:focus { outline: none; } /* a heading the focus is parked on is no control */
/* the name that stands now: a small label over the name itself */
.card p.profile-now { display: grid; gap: var(--space-1); margin: var(--space-7) 0 0; }
.profile-now-label { font-size: var(--text-xs); font-weight: 600; color: var(--color-subtle); }
.profile-now-name { font: 700 var(--text-lg)/1.3 var(--font-sans); font-stretch: var(--wdth-strong); color: var(--color-ink); overflow-wrap: anywhere; unicode-bidi: plaintext; text-align: left; }
.profile-now-name.is-none { font-weight: 500; color: var(--color-subtle); }
/* (… "No name is shown under your number yet" is a sentence, not a name: the body size, so it keeps to one line on a phone) */
.profile-now-name.is-none { font-size: var(--text-md); }
/* a name that cannot be changed here (an admin set it; a request that waits): said, not edited — no field ground, a dashed edge */
.profile-form .field-input[readonly] { background: none; border-style: dashed; border-color: var(--color-line-strong); color: var(--color-ink-2); cursor: default; }
.profile-form .field-input[readonly]:hover { border-color: var(--color-line-strong); }
.profile-form .field-hint.is-error { color: var(--color-danger); }
/* the one status line: a live region that is on the card from the start — empty, it takes no room and draws
   nothing (it stays in the accessibility tree: a sentence written into it is announced). Empty, it leaves the
   form's grid: an empty grid item keeps the gap after it whatever its margins say (a track cannot be negative —
   the negative margin that was here left that gap standing) */
.card p.profile-status { margin: 0; }
.card p.profile-status.card-notice { margin: 0; font-weight: 500; }
.profile-form .profile-status:empty { position: absolute; min-height: 0; padding: 0; border: 0; }
.profile-form .profile-status:empty::before { content: none; }
/* … and the error line under it. While a name is typed its slot stays (the card does not jump when the form
   refuses one); beside a status that speaks — saved, set by an admin, a request that waits — an empty slot was only
   a blank band above the button (review 2026-10-13): there it leaves the grid too */
.profile-form .profile-status:not(:empty) + .card-error:empty { position: absolute; min-height: 0; margin: 0; }
.card p.profile-retry-line { margin-top: var(--space-6); font-size: var(--text-sm); }

/* ---------- Admin → Sellers / Members: a row's display name — the name, who set it, Edit · Clear ---------- */

/* ONE block per row, under the row's own cells. In the roster — a grid of named areas — it starts on the line
   after the actions' area: an auto-placed item would slip into the row of an area that happens to be empty (a
   member without a note, on a phone) and stand ABOVE the dates and the buttons */
.dn-block { grid-column: 1 / -1; display: grid; gap: var(--space-3); min-width: 0; }
.mem-row > .dn-block { grid-row-start: actions-end; }
.dn-wide { grid-column: 1 / -1; }
.dn-line {
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2) var(--space-5); min-width: 0;
  padding-top: var(--space-4); border-top: 1px solid var(--color-line); font-size: var(--text-sm);
}
.dn-label { font-size: var(--text-xs); font-weight: 600; color: var(--color-subtle); }
.dn-name { font-weight: 700; color: var(--color-ink); overflow-wrap: anywhere; unicode-bidi: plaintext; text-align: left; }
.dn-none { color: var(--color-subtle); }
.dn-actions { display: inline-flex; flex-wrap: wrap; align-items: center; gap: var(--space-2) var(--space-6); margin-left: var(--space-2); }
.dn-line .action-msg { font-size: var(--text-sm); }
.dn-line .action-msg:focus { outline: none; } /* a status the focus is moved to is no control */
.dn-notice { margin: 0; flex-wrap: wrap; align-items: center; font-size: var(--text-sm); }
/* the editor, in the line's place: one field, its one hint line, Save · Cancel */
.dn-edit {
  display: grid; grid-template-columns: minmax(0, 26rem); gap: var(--space-5); align-items: start; min-width: 0;
  padding-top: var(--space-5); border-top: 1px solid var(--color-line);
}
.dn-edit .field-hint { margin: 0; }
.dn-edit .field-hint.is-ok { color: var(--color-ink-2); }
.mem-row .people-confirm.dn-wide, .sel-row .people-confirm.dn-wide { max-width: 40rem; }

/* ---------- cards (phones, tablets): a long display name under a right-aligned number ---------- */

@media (max-width: 1023.98px) {
  /* On a card the seller ends the first line, right-aligned. The link was one row — the WhatsApp glyph, then the
     two lines — so a name wider than the number (a display name has up to 40 characters; a WhatsApp name had 25)
     widened the box and left the glyph at its far left, beside the time, away from the number it belongs to
     (review 2026-10-13). Here the link is a grid: the glyph and the number share the first line at the right
     edge, the name takes the box's whole width under them. (A Skipped card's sender is left-aligned: as before.) */
  .feed tr.row.listing > td.c-seller .seller-link.has-alias,
  .feed tr.row.pending > td.c-seller .seller-link.has-alias,
  .evp-table .evp-row > td.e-seller .seller-link.has-alias {
    display: inline-grid; grid-template-columns: minmax(0, 1fr) auto max-content; align-items: center; align-content: center; column-gap: var(--space-3); row-gap: 0;
  }
  /* (max-content for the number's track, not auto: the number is a grid item with min-width 0 — the flex row's own
     rule — and an auto track may then shrink under it; the card would count the link narrower than it is, keep it
     on a full first line, and the number would run out of the card's edge) */
  .feed tr.row.listing > td.c-seller .seller-link.has-alias > .seller-lines,
  .feed tr.row.pending > td.c-seller .seller-link.has-alias > .seller-lines,
  .evp-table .evp-row > td.e-seller .seller-link.has-alias > .seller-lines { display: contents; }
  .feed tr.row.listing > td.c-seller .seller-link.has-alias > .wa-glyph,
  .feed tr.row.pending > td.c-seller .seller-link.has-alias > .wa-glyph,
  .evp-table .evp-row > td.e-seller .seller-link.has-alias > .wa-glyph { grid-column: 2; grid-row: 1; }
  .feed tr.row.listing > td.c-seller .seller-link.has-alias .seller-text,
  .feed tr.row.pending > td.c-seller .seller-link.has-alias .seller-text,
  .evp-table .evp-row > td.e-seller .seller-link.has-alias .seller-text { grid-column: 3; grid-row: 1; }
  .feed tr.row.listing > td.c-seller .seller-link.has-alias .seller-alias,
  .feed tr.row.pending > td.c-seller .seller-link.has-alias .seller-alias,
  .evp-table .evp-row > td.e-seller .seller-link.has-alias .seller-alias { grid-column: 1 / -1; grid-row: 2; justify-self: end; min-width: 0; max-width: 100%; }
}

@media (max-width: 767.98px) {
  /* the bar on a phone: the sentence over its two actions, side by side under it */
  .access-bar[data-kind="name"] .access-chip { grid-template-columns: auto auto minmax(0, 1fr); padding-bottom: 0; } /* (the two 44px actions close the chip) */
  .access-bar[data-kind="name"] .access-text { grid-column: 2 / -1; }
  .access-bar[data-kind="name"] .access-more { grid-column: 3; }
  /* 16px in the editor's field (a phone never zooms into it), 44px tall */
  .dn-edit { grid-template-columns: minmax(0, 1fr); }
  .dn-edit .field-input { min-height: 2.75rem; font-size: var(--text-lg); }
  .dn-actions { margin-left: 0; }
}

/* ==========================================================================
   Upgrade 2026-10-11 (DESIGN.md "Upgrade 2026-10-11", SPEC §6.12) — browsing by match:
   the "Hot right now" rail, the league chips, the Matches view, the activity sparkline, the loading
   skeleton (the match-day strip was removed on 2026-10-13 — owner: "remove the match-day row with the
   dates on the home page"). The locked system's tokens, chips and controls; nothing here is a
   new colour. Everything is drawn from rows the page has already loaded (discover.js).
   Hallmark · upgrade inside the locked system · pre-emit critique: P4 H5 E4 S5 R4 V4
   ========================================================================== */

/* ---------- the two rails: a label column, then a strip that scrolls sideways on its own ---------- */

.rail {
  display: grid; grid-template-columns: var(--rail-label) minmax(0, 1fr); align-items: center; column-gap: var(--space-6);
  min-width: 0;
}
/* the label: the table headers' small capitals; its second line says what the numbers are */
.rail-h {
  margin: 0; min-width: 0;
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-1) var(--space-3);
  font: 700 var(--text-2xs)/1.3 var(--font-sans); letter-spacing: 0.08em; text-transform: uppercase; color: var(--color-faint);
}
.rail-sub { flex: 0 0 100%; font: 400 var(--text-2xs)/1.35 var(--font-sans); letter-spacing: 0; text-transform: none; color: var(--color-faint); }
.rail-body { position: relative; display: flex; align-items: center; min-width: 0; }
/* the strip scrolls (so it clips): 4px of padding on every side — cancelled by its margins — keeps a
   button's focus ring (2px + 2px offset) whole, as in the phone filter rail */
.rail-track {
  /* relative: a button's read-not-shown words (absolute) stay inside the strip's scroll box, or they
     would widen the page past the screen */
  position: relative; flex: 1 1 auto; min-width: 0;
  display: flex; align-items: stretch; gap: var(--item-gap);
  margin: calc(-1 * var(--space-2)); padding: var(--space-2);
  overflow-x: auto; overscroll-behavior-x: contain; scrollbar-width: none;
  /* a chip snaps to rest clear of the mouse's arrows (each covers 22px of the strip's end): the scroll
     padding is their room, and where discover-view.js revealInRail puts a chip the keyboard reached */
  scroll-snap-type: x proximity; scroll-padding-inline: var(--space-11);
}
.rail-track::-webkit-scrollbar { display: none; }
.rail-track > * { flex: none; scroll-snap-align: start; }
/* a mouse's way along a strip wider than its box: an arrow at each end, only while there is more that
   way (discover-view.js railArrows); the keyboard has the arrow keys, a finger the swipe */
.rail-nav {
  position: absolute; top: 50%; z-index: 1;
  display: inline-flex; align-items: center; justify-content: center;
  width: 2rem; height: 2rem; margin-top: -1rem; padding: 0;
  border: 1px solid var(--color-line-strong); border-radius: 50%;
  background: var(--color-raised); color: var(--color-ink); cursor: pointer;
  transition: background-color var(--dur-micro) var(--ease-out), transform var(--dur-micro) var(--ease-out);
}
.rail-nav::before {
  content: ""; width: 14px; height: 14px; background-color: currentColor;
  -webkit-mask: var(--icon-chevron) center / contain no-repeat; mask: var(--icon-chevron) center / contain no-repeat;
  transform: rotate(90deg);
}
.rail-next::before { transform: rotate(-90deg); }
.rail-prev { left: calc(-1 * var(--space-5)); }
.rail-next { right: calc(-1 * var(--space-5)); }
.rail-nav:hover { background: var(--color-field); border-color: var(--color-link-line); }
.rail-nav:active { transform: translateY(1px); }

/* the Skipped view: a skipped message has no event or league — the rails stay in place (the tabs
   under them do not jump) but dimmed like any disabled control, and inert (app.js sets the attribute) */
.rail-hot.is-off, .refine.is-off { opacity: 0.5; }

/* ---------- Hot right now: the most requested upcoming events (the heat is WTB demand: its red) ---------- */

.rail-hot { margin-bottom: var(--band-gap); }
.rail-hot .rail-h { color: var(--color-ink-2); }
.rail-hot .rail-h .heat-flame { width: 14px; height: 14px; color: var(--color-wtb); }
/* a chip and, while it is the picked event, its own clear button: one rounded box */
.hot-item {
  display: inline-flex; align-items: stretch;
  border: 1px solid var(--color-line-card); border-radius: var(--radius-control);
  background: var(--color-surface-2);
  transition: background-color var(--dur-micro) var(--ease-out), border-color var(--dur-micro) var(--ease-out);
}
.hot-item:hover { background: var(--color-field); border-color: var(--color-line-strong); }
.hot-item.is-on { background: var(--color-accent-tint); border-color: var(--color-accent-tint-line); }
.hot-chip {
  display: inline-flex; align-items: center; gap: var(--space-6);
  min-height: 2.875rem; padding: var(--space-2) var(--space-8) var(--space-2) var(--space-6); margin: 0;
  border: 0; border-radius: inherit; background: none; color: var(--color-ink);
  text-align: left; white-space: nowrap; cursor: pointer;
}
.hot-chip:active { transform: translateY(1px); }
.hot-chip:disabled { opacity: 0.5; cursor: not-allowed; }
.hot-crests { flex: none; display: inline-flex; align-items: center; gap: var(--space-2); }
.hot-crests .crest, .hot-crests .crest-none, .hot-crests .ev-icon { width: 26px; height: 26px; }
.hot-crests .crest-none::before { width: 12px; height: 12px; }
.hot-crests .ev-icon::before { width: 13px; height: 13px; }
.hot-text { display: grid; gap: var(--space-1); min-width: 0; }
.hot-title { font: 700 var(--text-md)/1.25 var(--font-sans); font-stretch: var(--wdth-strong); letter-spacing: -0.005em; }
.hot-vs { font-weight: 400; color: var(--color-subtle); }
.hot-meta { display: inline-flex; align-items: center; gap: var(--space-3); font-size: var(--text-xs); line-height: 1.25; color: var(--color-muted); }
.hot-item.is-on .hot-meta { color: var(--color-accent-tint-ink); }
/* while the heat summary is on its way the rail keeps its place: chip-shaped placeholders (decoration) */
/* … each exactly a chip's box (46px + the 1px edges), so the chips that replace them move nothing */
.hot-bone { align-items: center; gap: var(--space-6); width: 15rem; min-height: 3rem; padding: var(--space-2) var(--space-8) var(--space-2) var(--space-6); }
.hot-bone:hover { background: var(--color-surface-2); border-color: var(--color-line-card); }
.hot-bone .skel-disc { width: 26px; height: 26px; }
.hot-bone .hot-text { flex: 1 1 auto; gap: var(--space-3); }
.hot-bone .skel-line { flex: none; width: 70%; height: 10px; }
.hot-bone .skel-line:last-child { width: 45%; height: 8px; }
/* the level: the flame, the Demand gauge's four bars (as many filled as the level) and its word */
.hot-level { display: inline-flex; align-items: center; gap: var(--space-2); font-weight: 800; color: var(--color-wtb); }
.hot-level .heat-flame { width: 13px; height: 13px; }
.hot-level .heat-bars { gap: 2px; }
.hot-level .heat-bar { width: 3px; border-radius: 1px; box-shadow: inset 0 0 0 1px currentColor; }
.hot-level .heat-bar:nth-child(1) { height: 5px; }
.hot-level .heat-bar:nth-child(2) { height: 7px; }
.hot-level .heat-bar:nth-child(3) { height: 9px; }
.hot-level .heat-bar:nth-child(4) { height: 11px; }
.hot-sep { color: var(--color-sep); }
.hot-clear {
  flex: none; display: inline-flex; align-items: center; justify-content: center;
  width: 2.5rem; padding: 0; margin: 0;
  border: 0; border-left: 1px solid var(--color-accent-tint-line); border-radius: 0 var(--radius-control) var(--radius-control) 0;
  background: none; color: var(--color-accent-tint-ink); cursor: pointer;
}
.hot-clear::before {
  content: ""; width: 14px; height: 14px; background-color: currentColor;
  -webkit-mask: var(--icon-close) center / contain no-repeat; mask: var(--icon-close) center / contain no-repeat;
}
.hot-clear:hover { background: var(--color-accent-soft); }
.hot-clear:active { opacity: 0.75; }

/* ---------- the league chips (inside the filters form) ---------- */

.refine { flex: 1 1 100%; display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--band-gap-tight); min-width: 0; }
/* The button the league chips fold behind where height is scarce ("compact refine", further down). Not
   displayed anywhere else: there the chips always show. A field-shaped button that says what it opens —
   "Leagues" and a chevron (it carried a calendar mark while it also folded the match days); open, it is
   the blue tint ringed in blue (a pressed league chip); the number on it is the leagues that are picked
   behind it — a closed panel never hides a filter silently */
.refine-toggle {
  display: none; flex: none; position: relative; align-items: center; justify-content: center; gap: var(--space-3);
  min-width: 2.5rem; height: 2.5rem; padding: 0 var(--space-5) 0 var(--space-6); margin: 0;
  border: 1px solid var(--color-line-field); border-radius: var(--radius-control);
  background: var(--color-field); color: var(--color-ink-2);
  font: 600 var(--text-md)/1 var(--font-sans); white-space: nowrap; cursor: pointer;
  transition: background-color var(--dur-micro) var(--ease-out), border-color var(--dur-micro) var(--ease-out);
}
.refine-toggle::after {
  content: ""; flex: none; width: 14px; height: 14px; background-color: currentColor;
  -webkit-mask: var(--icon-chevron) center / contain no-repeat; mask: var(--icon-chevron) center / contain no-repeat;
}
.refine-toggle[aria-expanded="true"]::after { transform: rotate(180deg); }
.refine-toggle:hover { background: var(--color-raised); border-color: var(--color-line-strong); color: var(--color-ink); }
.refine-toggle:active { transform: translateY(1px); }
.refine-toggle:disabled { opacity: 0.5; cursor: not-allowed; }
.refine-toggle[aria-expanded="true"] { background: var(--color-accent-tint); border-color: var(--color-accent); color: var(--color-accent-tint-ink); }
.refine-count {
  min-width: 1.125rem; padding: 0 var(--space-2); border-radius: var(--radius-pill);
  background: var(--color-accent); color: var(--color-accent-ink);
  font: 700 var(--text-2xs)/1.125rem var(--font-sans); text-align: center;
}
/* a league: its mark on the light tile, its name, its number of listings. Any number at a time: a
   pressed one is the blue tint of a switch chip that is on. On desktop the chips wrap (never scroll) */
.rail-leagues .rail-track { flex-wrap: wrap; overflow: visible; }
.lg-chip {
  display: inline-flex; align-items: center; gap: var(--space-4);
  position: relative; height: var(--chip-h); padding: 0 var(--space-7) 0 var(--space-3); margin: 0;
  border: 1px solid var(--color-line-field); border-radius: var(--radius-pill);
  background: var(--color-field); color: var(--color-ink-2);
  font: 600 var(--text-sm)/1 var(--font-sans); white-space: nowrap; cursor: pointer;
  transition: background-color var(--dur-micro) var(--ease-out), border-color var(--dur-micro) var(--ease-out);
  /* never wider than its strip: a very long league name is cut with "…" (the chip's tooltip and the
     name it is read by are whole) instead of running off a narrow screen once "More" wraps the chips */
  max-width: 100%;
}
.lg-name { min-width: 0; overflow: hidden; text-overflow: ellipsis; line-height: 1.3; }
.lg-chip .league-icon, .lg-count { flex: none; }
.lg-chip.is-all, .lg-chip.lg-more { padding: 0 var(--space-7); }
.lg-chip .league-icon { width: 16px; height: 16px; }
.lg-count { font: 500 var(--text-2xs)/1 var(--font-mono); color: var(--color-muted); }
.lg-count:empty { display: none; }
.lg-chip:hover { background: var(--color-raised); border-color: var(--color-line-strong); color: var(--color-ink); }
.lg-chip:active { transform: translateY(1px); }
.lg-chip:disabled { opacity: 0.5; cursor: not-allowed; }
/* pressed: the blue tint, ringed in the brand blue (a clearly lighter edge — not hue alone) */
.lg-chip[aria-pressed="true"] { background: var(--color-accent-tint); border-color: var(--color-accent); color: var(--color-accent-tint-ink); }
.lg-chip[aria-pressed="true"] .lg-count { color: var(--color-accent-tint-ink); }
.lg-chip.is-all[aria-pressed="true"] { background: var(--color-accent); border-color: var(--color-accent); color: var(--color-accent-ink); font-weight: 700; }
/* "More": an outlined chip with the count it would add; open, it reads "Fewer" */
.lg-more { background: none; border-color: var(--color-line-strong); color: var(--color-ink-2); }
.lg-more .lg-count { color: var(--color-accent-text); }

/* ---------- the Matches view: one card per event, the day where a score would stand ---------- */

.matches { margin-top: var(--space-8); }
/* as many cards per row as fit, none under --card-min — and never wider than a narrow screen */
.mgrid {
  list-style: none; margin: 0; padding: 0;
  display: grid; grid-template-columns: repeat(auto-fill, minmax(min(100%, var(--card-min)), 1fr)); gap: var(--space-8);
}
/* a card is its own measure (a container): its names take the size its width has room for — below */
.mcard-item { display: flex; min-width: 0; container-type: inline-size; }
.mcard {
  position: relative; isolation: isolate; /* the arrival tint (::after, z-index -1) paints above the card's own ground */
  flex: 1 1 auto; min-width: 0;
  display: flex; flex-direction: column; gap: var(--space-7);
  padding: var(--space-8) var(--space-7) var(--space-8);
  border: 1px solid var(--color-line-card); border-radius: var(--radius-card);
  background: var(--color-surface-2); color: var(--color-ink); text-decoration: none;
  transition: background-color var(--dur-micro) var(--ease-out), border-color var(--dur-micro) var(--ease-out), transform var(--dur-micro) var(--ease-out);
}
.mcard:hover { background: var(--color-field); border-color: var(--color-line-strong); color: var(--color-ink); }
.mcard:active { transform: translateY(1px); }
/* a GUEST's card (owner 2026-10-13: "hide the match page from guests as well, so that they cannot enter"): the
   same card, but no link — nothing answers the pointer, nothing can be pressed to no effect */
.mcard.is-static:hover { background: var(--color-surface-2); border-color: var(--color-line-card); }
.mcard.is-static:active { transform: none; }
/* a card whose event just got a listing arrives like a new row: the feed's ONE entrance (the item slides
   6px into place, a blue tint fades out once on the card) */
.mcard::after {
  content: ""; position: absolute; inset: 0; z-index: -1;
  border-radius: inherit; background: var(--color-accent-soft); opacity: 0; pointer-events: none;
}
.mcard-item.is-new { animation: row-in var(--dur-short) var(--ease-out) backwards; }
.mcard-item.is-new .mcard::after { animation: arrive var(--dur-flash) var(--ease-out) both; }
.tab-hidden .mcard-item.is-new, .tab-hidden .mcard-item.is-new .mcard::after { animation-play-state: paused; }
/* the league (or the kind of event) · the heat when the event is hot */
.mcard-top { display: flex; align-items: center; justify-content: space-between; gap: var(--space-4); min-height: 22px; min-width: 0; }
.mcard-league { display: inline-flex; flex-wrap: wrap; align-items: center; gap: var(--space-2) var(--space-3); min-width: 0; font-size: var(--text-xs-plus); line-height: 1.3; color: var(--color-muted); }
.mcard-league.is-kind { color: var(--color-subtle); }
.mcard-heat {
  flex: none; display: inline-flex; align-items: center; gap: var(--space-2);
  height: 22px; padding: 0 var(--space-4) 0 var(--space-3); border-radius: var(--radius-pill);
  box-shadow: inset 0 0 0 1px var(--color-wtb-bg); color: var(--color-wtb);
  font: 800 var(--text-2xs)/1 var(--font-sans); letter-spacing: 0.06em; text-transform: uppercase; white-space: nowrap;
}
.mcard-heat.is-fire { background: var(--color-wtb-bg); }
.mcard-heat .heat-flame { width: 13px; height: 13px; }
/* the face: [crest] Home · the day · Away [crest] */
.mcard-face { display: grid; grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr); align-items: start; gap: var(--space-4); text-align: center; }
.mcard-team { display: grid; justify-items: center; align-content: start; gap: var(--space-3); min-width: 0; }
.mcard-team .crest, .mcard-team .crest-none, .mcard-face .ev-icon { width: 44px; height: 44px; }
.mcard-team .crest.is-dark { padding: 5px; }
.mcard-team .crest-none::before, .mcard-face .ev-icon::before { width: 20px; height: 20px; }
/* a name wraps between its words, never cut. A word must fit its column: the card's own width picks
   the name's size (the three steps under "the card's width decides"), so "Wolverhampton" or
   "Mönchengladbach" stand whole on every card of 316px or more. Only a word still wider than its column
   breaks — with a hyphen where the browser has a dictionary, anywhere as the last resort */
.mcard-name {
  max-width: 100%; min-width: 0;
  font: 700 var(--text-lg-minus)/1.25 var(--font-sans); font-stretch: var(--wdth-strong); letter-spacing: -0.005em;
  overflow-wrap: break-word; -webkit-hyphens: auto; hyphens: auto; hyphenate-limit-chars: 8 3 3;
}
.mcard-when { display: grid; justify-items: center; align-content: center; gap: var(--space-1); min-width: 4.5rem; min-height: 44px; }
.mcard-dow { font: 700 var(--text-2xs)/1.2 var(--font-sans); letter-spacing: 0.1em; text-transform: uppercase; color: var(--color-muted); }
.mcard-date { font: 500 var(--text-lg)/1.2 var(--font-mono); color: var(--color-ink); white-space: nowrap; }
.mcard-ko { font: 400 var(--text-xs)/1.2 var(--font-mono); color: var(--color-muted); white-space: nowrap; }
.mcard-when.is-undated .mcard-date { font: 600 var(--text-sm)/1.2 var(--font-sans); color: var(--color-subtle); }
.mcard-when.is-likely .mcard-date { font: 500 var(--text-md)/1.2 var(--font-mono); color: var(--color-muted); }
.mcard-when.is-past .mcard-date { color: var(--color-muted); }
.mcard-when.is-upcoming .mcard-dow { color: var(--color-ink-2); }
/* an event that is not a match: the calendar disc, its name, the day */
.mcard-face.is-event { grid-template-columns: auto minmax(0, 1fr) auto; align-items: center; text-align: left; gap: var(--space-6); }
.mcard-venue { text-align: center; font-size: var(--text-xs-plus); line-height: 1.35; color: var(--color-muted); overflow-wrap: anywhere; }
/* the foot: the cheapest offer (the hero, as in a row) · for sale / wanted · their split · the newest post */
.mcard-foot { display: grid; gap: var(--space-5); margin-top: auto; padding-top: var(--space-7); border-top: 1px solid var(--color-line); }
.mcard-deal { display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between; gap: var(--space-2) var(--space-6); }
.mcard-price { display: inline-flex; flex-wrap: wrap; align-items: baseline; gap: 0 5px; }
.mcard-price .price-q { color: var(--color-wts); }
.mcard-amount { font: 800 var(--text-2xl)/1.1 var(--font-sans); font-stretch: var(--wdth-num); letter-spacing: -0.01em; color: var(--color-ink); white-space: nowrap; }
.mcard-alt { font-size: var(--text-sm); color: var(--color-muted); white-space: nowrap; }
.mcard-noprice { font-size: var(--text-sm); line-height: 1.75; color: var(--color-subtle); }
.mcard-counts { display: inline-flex; flex-wrap: wrap; align-items: center; gap: var(--space-1) var(--space-6); font-size: var(--text-sm); color: var(--color-muted); }
.mcard-count { display: inline-flex; align-items: center; gap: var(--space-3); white-space: nowrap; }
.mcard-n { font-weight: 800; color: var(--color-ink); }
.mcard-count.is-zero .mcard-n { font-weight: 600; color: var(--color-faint); }
.mcard-count.is-zero .key { background: var(--color-line-strong); }
.mcard .mcard-split { height: 4px; margin-left: 0; }
/* the last line: the newest post · on a hot card the heat's own numbers and span (the flame + "6 buyers
   in 14 days"), so "ON FIRE" over "0 wanted" reads as what it is — two different time spans */
.mcard-last { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: var(--space-1) var(--space-6); font-size: var(--text-xs); }
.mcard-new { color: var(--color-faint); }
.mcard-demand { display: inline-flex; align-items: center; gap: var(--space-2); font-weight: 600; color: var(--color-wtb); white-space: nowrap; }
.mcard-demand .heat-flame { width: 12px; height: 12px; }

/* the card's width decides (container queries on .mcard-item — the viewport cannot say how wide a card
   of an auto-filled grid is): from 416px down a name is the body size at the font's normal width, from
   360px down a step smaller and narrower, and the day column gives up what it does not need */
@container (max-width: 26rem) {
  .mcard-name { font-size: var(--text-md); font-stretch: normal; }
}
@container (max-width: 22.5rem) {
  .mcard-name { font-size: var(--text-sm); font-stretch: var(--wdth-tight); }
  .mcard-when { min-width: 3.75rem; }
  .mcard-dow { font-stretch: var(--wdth-tight); letter-spacing: 0.04em; }
  .mcard-when.is-likely .mcard-ko { white-space: normal; text-align: center; }
}

/* ---------- the activity sparkline (the scoreboard): messages per clock hour, last 24 h ---------- */

/* a fifth cell of the counters: the bars where the digits stand, the caption where the label does */
.spark { margin: 0; display: flex; flex-direction: column; gap: var(--space-3); min-width: 0; padding-left: var(--space-10); border-left: 1px solid var(--color-line); }
.spark-svg { display: block; width: 10.5rem; max-width: 100%; height: 28px; overflow: visible; }
.spark-cap { position: relative; font-size: var(--text-xs); line-height: 1.45; color: var(--color-muted); white-space: nowrap; }
.spark-bar { fill: var(--color-bar); }
/* an hour without a message: a tick on the baseline in the bars' own colour (3:1 on the ground — it is
   still there at a phone's 1px; a counted hour is at least 5 units tall, so one message never reads as
   none). An hour the window never loaded: a faint dot that a small chart loses altogether — a gap, not
   a zero. The two were the same dim colour and could not be told apart on a phone (review 2026-10-12) */
.spark-bar.is-zero { fill: var(--color-bar); }
.spark-bar.is-off { fill: var(--color-line-strong); }
.spark-bar.is-now { fill: var(--color-accent); }

/* ---------- the loading skeleton: the shape of what is coming, never a blank table ---------- */

.skel { display: grid; grid-template-columns: minmax(0, 1fr); }
.skel-b { display: block; border-radius: var(--radius-xs); background: var(--color-skeleton); animation: skeleton 1.4s var(--ease-in-out) infinite; }
.skel-row {
  display: grid; grid-template-columns: 4.5rem minmax(0, 1.2fr) minmax(0, 0.7fr) minmax(0, 0.7fr) 7rem minmax(0, 0.9fr); align-items: center; column-gap: var(--space-9);
  min-height: var(--row-min); padding: var(--row-pad) var(--space-6); border-bottom: 1px solid var(--color-line-soft);
}
.skel-row:nth-child(2) .skel-b, .skel-card:nth-child(2) .skel-b { animation-delay: 0.12s; }
.skel-row:nth-child(3) .skel-b, .skel-card:nth-child(3) .skel-b { animation-delay: 0.24s; }
.skel-row:nth-child(4) .skel-b, .skel-card:nth-child(4) .skel-b { animation-delay: 0.36s; }
.skel-row:nth-child(5) .skel-b, .skel-card:nth-child(5) .skel-b { animation-delay: 0.48s; }
.skel-row:nth-child(6) .skel-b, .skel-card:nth-child(6) .skel-b { animation-delay: 0.6s; }
.skel-chip { width: 44px; height: 22px; border-radius: var(--radius-chip); }
.skel-match { display: grid; gap: var(--space-3); min-width: 0; }
.skel-team, .skel-side { display: flex; align-items: center; gap: var(--space-5); min-width: 0; }
.skel-disc { flex: none; width: 28px; height: 28px; border-radius: 50%; }
.skel-line { flex: 0 1 11rem; height: 12px; }
.skel-team:nth-child(2) .skel-line { flex-basis: 8rem; }
.skel-date, .skel-seats, .skel-seller { height: 12px; max-width: 8rem; }
.skel-price { justify-self: end; width: 5rem; height: 24px; border-radius: var(--radius-sm); }
.skel.is-cards { grid-template-columns: repeat(auto-fill, minmax(min(100%, var(--card-min)), 1fr)); gap: var(--space-8); margin-top: var(--space-8); }
.skel-card {
  display: grid; gap: var(--space-8); align-content: start; min-height: 15rem; padding: var(--space-8) var(--space-7);
  border: 1px solid var(--color-line-card); border-radius: var(--radius-card); background: var(--color-surface-2);
}
.skel-lead { width: 8rem; flex: none; }
.skel-face { display: grid; grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr); align-items: start; gap: var(--space-4); }
.skel-side { flex-direction: column; }
.skel-side .skel-disc { width: 44px; height: 44px; }
.skel-side .skel-line { flex: none; width: 70%; }
.skel-when { width: 3.5rem; height: 2.25rem; border-radius: var(--radius-sm); }
.skel-foot { width: 60%; flex: none; height: 22px; }
.skel-bar { height: 4px; border-radius: var(--radius-pill); }

/* small desktops: "Messages · last 24 h" under a narrower sparkline ("per hour" is read, not shown), so the
   scoreboard still stands beside the title */
@media (min-width: 768px) and (max-width: 1199.98px) {
  .spark-svg { width: 7.25rem; }
  .spark-per {
    position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
    overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
  }
}

/* ---------- compact refine: where height is scarce the listings keep the screen ----------
   On a phone, and on any screen under 800px tall (a 1366 × 768 or 1280 × 720 laptop, a tablet on its
   side), the league chips cost the feed a row. There they fold behind one button in the filters
   (.refine-toggle — "Leagues"; app.js remembers whether the reader opened them). On a taller screen the
   button is not displayed and the chips always are. (Until 2026-10-13 it folded the match-day strip too;
   the search no longer gives way for it — the row has the room since All / WTS / WTB and the currency
   select left it.) */
@media (max-width: 767.98px), (max-height: 800px) {
  .refine-toggle { display: inline-flex; }
  .refine:not(.is-open) { display: none; }
}
@media (min-width: 768px) and (max-height: 800px) {
  :root { --band-gap: var(--space-8); } /* rhythm 2026-10-13: a short window keeps a step of the bands' air for its rows */
}
/* tablets and a small desktop (768–1199px): the search starts narrower, so the filters stay ONE row where
   they can — a small desktop with the "Leagues" button in it, a guest's tablet (a wrapping row never shrinks
   an item to keep the line: at 21.25rem the switches dropped to a second line). It still grows into what is
   left. app.js gives the field its short hint below 1200px */
@media (min-width: 768px) and (max-width: 1199.98px) {
  .search { flex-basis: 14rem; }
  /* … and at those 14rem its short hint is whole: 8px after the text, not 14 (the hint needs 170 of the 174px
     that leaves; the row's one-line widths are what they were) */
  .search input { padding-right: var(--space-4); }
}

/* ---------- desktop only: the rows' quieter rhythm ---------- */

/* rhythm 2026-10-13: a tall desktop window has the room — the bands above the table stand a step further apart */
@media (min-width: 1024px) and (min-height: 1000px) {
  :root { --band-gap: var(--space-10); }
}
@media (min-width: 1024px) {
  .tabs { gap: var(--space-10); }
  .tabs button { height: 2.5rem; }
  /* All / WTS / WTB beside them: a size down, like the header's view switch — 28 + 2 × 3 + 2 × 1 = 36px, 4px
     over the hairline: the bar is as tall as its 40px tabs */
  .type-seg button { height: 28px; padding: 0 var(--space-6); font-size: var(--text-sm); }
  /* … and the bar is ONE line whatever the summary says: the tabs and the control keep their widths, the
     summary takes what is left at the far end and wraps INSIDE its own box (its words and "Clear filters" are
     one run of text) — two of its lines are no taller than the 40px tabs, so the table under the bar never
     moves when the summary grows (a filter pressed, a message being classified, an admin's third tab) */
  .viewbar { flex-wrap: nowrap; }
  .viewbar .summary { display: block; flex: 0 1 auto; min-width: 0; align-self: center; padding: 0; line-height: 1.25; text-align: right; }
  /* ("Clear filters" keeps its 24px to press: its padding stands outside the line — negative margins — so it never makes a line of the summary taller) */
  .viewbar .summary .btn-link { display: inline; margin: calc(-1 * var(--space-2)) 0 calc(-1 * var(--space-2)) var(--space-4); }
  /* a hovered / focused row: one surface step up, eased; the notch lengthens (transform only) */
  .feed tr.row { transition: background-color var(--dur-micro) var(--ease-out); }
  .feed tr.row:focus-visible { background: var(--color-surface); }
}

/* ---------- below 1024px: the rails lose their label column and bleed to the screen's edges ---------- */

@media (max-width: 1023.98px) {
  .rail { display: flex; align-items: center; gap: var(--space-4); }
  /* "League" is read, not shown (the chips carry their marks) */
  .rail-leagues .rail-h, .rail-sub-long, .rail-word-rest {
    position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
    overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
  }
  /* "Hot": the flame over the word, a small badge in front of the strip — and under the word the span
     the buyers are counted over ("14 days"), so "6 buyers" is never a number without its time */
  .rail-hot .rail-h { flex: none; flex-direction: column; flex-wrap: nowrap; justify-content: center; gap: var(--space-1); min-width: 2.25rem; color: var(--color-wtb); }
  .rail-hot .rail-h .heat-flame { width: 14px; height: 14px; }
  .rail-hot .rail-sub { flex: none; line-height: 1.1; white-space: nowrap; }
  .rail-body { flex: 1 1 auto; }
  /* no arrows here: a chip rests at the strip's own edge */
  .rail-track { margin-right: calc(-1 * var(--gutter)); padding-right: var(--gutter); scroll-padding-inline: var(--space-2); }
  .rail-leagues .rail-track {
    margin: calc(-1 * var(--space-2)) calc(-1 * var(--gutter)); padding: var(--space-2) var(--gutter);
    scroll-padding-inline: var(--gutter);
  }
  /* a finger swipes: no arrows */
  .rail-nav { display: none; }
  /* the league chips scroll in one line until "More" is open — then they wrap, all on screen */
  .rail-leagues .rail-track { flex-wrap: nowrap; overflow-x: auto; }
  .rail-leagues.is-open .rail-track { flex-wrap: wrap; overflow: visible; }
  /* the skeleton's rows are cards, like the rows they stand for */
  .skel { gap: var(--card-gap); margin-top: var(--space-6); }
  .skel-row {
    grid-template-columns: auto minmax(0, 1fr) auto; row-gap: var(--space-6); column-gap: var(--space-6);
    min-height: 0; padding: var(--card-pad-top) var(--space-7) var(--card-pad-bottom);
    border: 1px solid var(--color-line-card); border-radius: var(--radius-card); background: var(--color-surface-2);
  }
  .skel-row .skel-date, .skel-row .skel-seats, .skel-row .skel-seller { display: none; }
  .skel-row .skel-disc { width: 24px; height: 24px; }
}
@media (min-width: 768px) and (max-width: 1023.98px) {
  .skel { grid-template-columns: repeat(2, minmax(0, 1fr)); column-gap: var(--space-8); row-gap: var(--space-8); }
  .skel.is-cards { grid-template-columns: repeat(auto-fill, minmax(min(100%, var(--card-min)), 1fr)); }
}

/* ---------- phones: 44px to touch; the strips snap under the thumb ---------- */

@media (max-width: 767.98px) {
  .rail-track { scroll-snap-type: x mandatory; }
  .rail-leagues.is-open .rail-track { scroll-snap-type: none; }
  /* a chip is 44px to touch; without its date the next one shows at the screen's edge — there is more */
  .hot-chip { min-height: 44px; padding: var(--space-1) var(--space-6) var(--space-1) var(--space-4); gap: var(--space-4); }
  .hot-title { font-size: var(--text-sm); }
  .hot-date, .hot-buyers + .hot-sep { display: none; }
  .hot-clear { width: 44px; }
  /* a league chip: 36px to see, 44px to touch (its hit area is its own ::after, 4px past it above and below) */
  .lg-chip { padding: 0 var(--space-7) 0 var(--space-3); }
  .lg-chip::after { content: ""; position: absolute; inset: -5px 0; }
  .lg-chip.is-all, .lg-chip.lg-more { min-width: 44px; justify-content: center; padding: 0 var(--space-7); }
  .rail-leagues .rail-track { padding-block: var(--space-3); margin-block: calc(-1 * var(--space-3)); }
  /* the fold button: 44px to touch, the first control of the filter rail */
  .refine-toggle { min-width: 44px; height: 44px; padding: 0 var(--space-5); }
  .hot-bone { min-height: 2.875rem; } /* a phone's chip: 44px + its edges */
  /* three tabs on one line */
  .tabs { gap: var(--space-7); }
  .matches { margin-top: var(--space-6); }
  .mgrid { gap: var(--card-gap); }
  /* the sparkline stands beside the caption: the bars over their own label ("Messages per hour · last
     24 h" — a chart is never shown without saying what it counts) */
  .sb-counters.has-spark .counters-cap { grid-column: 1; align-self: end; margin: 0 0 var(--space-2); }
  .spark { grid-column: 2; grid-row: 1; justify-self: end; align-items: flex-end; gap: var(--space-1); padding: 0; border: 0; margin-bottom: var(--space-2); }
  .spark-svg { width: 6.5rem; height: 16px; }
  .spark-cap { font-size: var(--text-2xs); line-height: 1.2; color: var(--color-subtle); }
}
/* the narrowest phones: the three tabs and their counts still fit one line */
@media (max-width: 374.98px) {
  /* "Messages · last 24 h" ("per hour" is read, not shown) keeps to its half of the caption's line */
  .spark-per {
    position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
    overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
  }
  .tabs { gap: var(--space-5); }
  .tabs button { gap: var(--space-2); }
  .tabs .count { padding: 0 var(--space-2); }
  .mcard { padding: var(--space-8) var(--space-6); }
}

/* ---------- motion ----------
   One entrance: a new row slides 6px into place (220 ms) while a blue tint fades out once
   ("arrive" — app.js drops the row's "new" class on its animationend). The other loops are state
   indicators the artboards show: the LIVE dot, the classifying spinner and its skeleton. */
@keyframes row-in { from { opacity: 0; transform: translateY(-6px); } to { opacity: 1; transform: none; } }
@keyframes arrive { 0%, 20% { opacity: 1; } 100% { opacity: 0; } }
@keyframes fade-in { from { opacity: 0; } }
@keyframes pulse { 50% { opacity: 0.3; } }
@keyframes spin { to { transform: rotate(360deg); } }
@keyframes skeleton { 50% { opacity: 0.45; } }
@keyframes loading-pulse { 50% { opacity: 0.55; } }
@keyframes dot { 0%, 80%, 100% { opacity: 0.25; } 40% { opacity: 1; } }

/* Reduced motion: opacity only, 150 ms at most; every loop stops. The arrival highlight is not shown at
   all (upgrade 2026-10-11): its tint is hidden, while its animation still runs to its end — app.js drops
   the row's "new" class on that animationend. */
@media (prefers-reduced-motion: reduce) {
  .progress-fill { transition: none; }
  .progress:not(.done):not(.failed) .progress-fill { animation: none; }
  .dots span { animation: none; opacity: 0.6; }
  .feed tr.row.is-new { animation: fade-in 150ms linear both; }
  .feed tr.row.is-new::after { animation-duration: 150ms; }
  .live[data-level="live"] .led, .badge.pending, .feed td.c-price.is-blank::before { animation: none; }
  .btn, .btn:active, .seg button:active, .tabs button:active, .more:active { transition: none; transform: none; }
  .btn-admin, .btn-admin:active, .admin-tabs button:active { transition: none; transform: none; }
  .evp-row.is-new { animation: fade-in 150ms linear both; }
  .evp-row.is-new::after, .evp-table .evp-row.is-new > td::after { animation-duration: 150ms; }
  .evp-live.is-updated .evp-dot { animation: none; }
  .ev-btn, .ev-btn:active { transition: none; transform: none; }
  /* upgrade 2026-10-11: the skeleton holds still; nothing eases or presses; a card arrives with a fade */
  .skel-b { animation: none; }
  .mcard-item.is-new { animation: fade-in 150ms linear both; }
  .mcard-item.is-new .mcard::after { animation-duration: 150ms; }
  .feed tr.row.is-new::after, .mcard-item.is-new .mcard::after, .evp-row.is-new::after, .evp-table .evp-row.is-new > td::after { visibility: hidden; }
  .feed tr.row, .feed tr.row.listing > td.c-type::before, .hot-item, .lg-chip { transition: none; }
  .feed tr.row.listing:hover > td.c-type::before, .feed tr.row.listing:focus-visible > td.c-type::before { transform: none; }
  .mcard, .mcard:active, .rail-nav, .rail-nav:active, .hot-chip:active, .lg-chip:active { transition: none; transform: none; }
  .refine-toggle, .refine-toggle:active { transition: none; transform: none; }
  /* identity 2026-10-12: nothing eases or presses */
  .seller-lock, .mine-toggle, .mine-toggle:active, .btn-account, .btn-account:active, .account-item:active, .sheet-close:active { transition: none; transform: none; }
}
