:root {
    /* "In the Shop": workshop dark theme (from rctuners-coming-soon.html) — asphalt ground, steel surfaces,
       paper ink, caution-amber accent, rust for danger/pop. Dark by design (no light variant). */
    color-scheme: dark;
    font-family: "IBM Plex Sans", system-ui, -apple-system, "Segoe UI", sans-serif;
    --font-display: "Oswald", system-ui, sans-serif;   /* headings: bold, uppercase, stencil-y */
    --font-mono: "IBM Plex Mono", ui-monospace, "SFMono-Regular", monospace; /* tags, timestamps, small caps */
    --bg: #1c1c1a;                 /* asphalt */
    --surface: #302c27;            /* steel */
    --ink: #ece5d6;                /* paper */
    --ink-dim: #b8b0a0;            /* paper, dimmed */
    --muted: var(--ink-dim);
    --placeholder: #8e8b81;        /* hints only: dimmer than --muted, still above the 4.5:1 AA floor (5.01:1) */       /* alias: 6 rules referenced var(--muted) but it was never defined, so they were inert */
    --line: #4a453d;               /* steel-line */
    --soft: color-mix(in srgb, var(--ink) 8%, transparent);
    --accent: #e0a83a;             /* caution amber: buttons, active states */
    --accent-strong: #f0c364;      /* caution amber, brighter for text links on dark */
    --accent-ink: #1c1c1a;         /* text color on top of an amber accent surface */
    --accent-soft: color-mix(in srgb, var(--accent) 16%, transparent);
    --pop: #c8592e;                /* rust: badges, live dots, highlights */
    --ok: #6a9c5c;
    --no: #c1503a;
    --presence-off: var(--line);   /* avatar ring while a member is away */
    --presence-on: #7ed957;        /* avatar ring while online: brighter than --ok so it reads at 28px */
}
* { box-sizing: border-box; }
body { margin: 0; padding: 0 1rem 0; max-width: 85rem; /* 1360px on desktop */ margin-inline: auto; line-height: 1.45; background: var(--bg); background-image: repeating-linear-gradient(135deg, rgba(255,255,255,0.02) 0 2px, transparent 2px 26px); color: var(--ink); }
a { color: inherit; }
img { max-width: 100%; display: block; }
/* The header floats: it stays on screen at every scroll depth. Three details make that work rather than
   half-work. It needs an opaque background or the page scrolls through it — and it carries the body's own
   diagonal stripe, not just --bg, so the bar does not read as a different surface from the page behind it.
   The negative inline margin cancels the body's 1rem side padding so content cannot peek through the
   gutters as it passes underneath. z-index 10 sits above page content and both sticky composers (2, 5) and
   below the emoji popover (20), the phone drawer sheet (25) and the burger (30).
   On a phone `header.menu-open` (further down) overrides this with position: fixed for the full-screen
   sheet: it is later and more specific, and its `margin: 0` cancels the negative margin here. */
header {
    display: flex; flex-direction: column; gap: 0.5rem; padding-block: 0.8rem;
    border-bottom: 1px solid var(--line); margin-bottom: 1.5rem;
    position: sticky; top: 0; z-index: 10;
    margin-inline: -1rem; padding-inline: 1rem;
    background: var(--bg);
    background-image: repeating-linear-gradient(135deg, rgba(255,255,255,0.02) 0 2px, transparent 2px 26px);
}
header .topbar { display: flex; justify-content: space-between; align-items: center; gap: 1rem; flex-wrap: wrap; }
header nav.account { gap: 0.9rem; }
header nav.site { gap: 1rem; }
header nav { display: flex; gap: 1rem; align-items: center; flex-wrap: wrap; }
header nav a { position: relative; text-decoration: none; color: var(--ink-dim); transition: color 0.15s; }
@media (hover: hover) { header nav a:hover { color: var(--ink); } }
header nav a.active { color: var(--accent-strong); }
/* Active nav item: a muddy amber underline (no glow), the width of the link. */
header nav a.active::after {
    content: "";
    position: absolute;
    left: 0;
    right: 0;
    bottom: -0.6rem;
    height: 2px;
    border-radius: 1px;
    background: color-mix(in srgb, var(--accent) 55%, var(--line) 45%);
}
.brand { font-family: var(--font-display); font-weight: 700; letter-spacing: 0.02em; text-transform: uppercase; text-decoration: none; font-size: 1.2rem; color: var(--ink); }
.brand::after { content: ""; display: inline-block; width: 0.5rem; height: 0.5rem; border-radius: 50%; background: var(--pop); margin-left: 0.2rem; vertical-align: super; }
.me { display: inline-flex; align-items: center; gap: 0.4rem; }

h1 { font-family: var(--font-display); font-weight: 700; text-transform: uppercase; letter-spacing: 0.01em; font-size: 1.5rem; margin: 0 0 0.75rem; }
h2 { font-family: var(--font-display); font-weight: 600; text-transform: uppercase; letter-spacing: 0.01em; font-size: 1.05rem; margin: 1.5rem 0 0.5rem; }
h3 { font-size: 1rem; margin: 0.5rem 0 0.15rem; }
.muted { color: var(--ink-dim); }
.small { font-size: 0.85rem; }
.error { color: var(--no); font-size: 0.9rem; }
/* Your own profile: Sign out sits under Edit profile rather than beside it, so the two never read as a pair
   of equal actions. Stacked, left-aligned, and only rendered on your own profile. */
.profile-own-actions { display: flex; flex-direction: column; align-items: flex-start; gap: 0.4rem; }
/* Recovery actions under a failed sign-in: shown only once the submit has actually been rejected. */
.auth-recovery { display: flex; flex-direction: column; gap: 0.4rem; margin: 0.1rem 0 0; }
.auth-recovery .row { gap: 0.5rem; flex-wrap: wrap; }
.empty { padding: 1.5rem; text-align: center; border: 1px dashed var(--line); border-radius: 0.75rem; }
.prose { white-space: pre-wrap; }
hr { border: 0; border-top: 1px solid var(--line); margin: 1rem 0; }

.card { border: 1px solid var(--line); border-radius: 3px; padding: 1.25rem; background: linear-gradient(180deg, rgba(255,255,255,0.025), transparent 40%), var(--surface); box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2); }
input, select, textarea { background: var(--surface); }
.card.narrow { max-width: 30rem; margin-inline: auto; }
.page-head { display: flex; justify-content: space-between; align-items: flex-start; gap: 1rem; flex-wrap: wrap; margin-bottom: 1rem; }
.page-head h1 { margin-bottom: 0.25rem; }
.hero { padding: 2rem 0 1rem; }
.hero h1 { font-size: 2rem; }
.hero p { max-width: 36rem; }
.hero.compact { padding: 0.5rem 0 1rem; border-bottom: 1px solid var(--line); margin-bottom: 1rem; }
.hero.compact h1 { font-size: 1.6rem; }

.tabs { display: flex; gap: 1.25rem; flex-wrap: wrap; margin-bottom: 1rem; border-bottom: 1px solid var(--line); }
.tab { background: none; border: 0; border-bottom: 2px solid transparent; border-radius: 0; margin-bottom: -1px; padding: 0.5rem 0.05rem; cursor: pointer; color: var(--ink-dim); text-decoration: none; font: inherit; transition: color 0.15s, border-color 0.15s; }
@media (hover: hover) { .tab:hover { color: var(--ink); } }
.tab .count { opacity: 0.6; }
.tabs.big .tab { font-size: 1.05rem; font-weight: 600; }

.field { display: block; margin-bottom: 0.9rem; }
.field > span { display: block; font-size: 0.85rem; margin-bottom: 0.25rem; font-weight: 600; }
.field small { display: block; margin-top: 0.25rem; }
input, select, textarea { width: 100%; padding: 0.55rem 0.7rem; border-radius: 0.5rem; border: 1px solid color-mix(in srgb, currentColor 30%, transparent); background: transparent; color: inherit; font: inherit; }
/* Placeholders read as hints, not as typed values. opacity:1 because Firefox dims placeholders by default,
   which would compound with the colour and push it below a readable contrast. */
::placeholder { color: var(--placeholder); opacity: 1; }
input[type="checkbox"], input[type="file"] { width: auto; }
/* Without an explicit accent-color, dark color-scheme renders a checked box's fill/tick too close to
   the surrounding dark fill to read as "checked" in some browsers (notably Safari). */
input[type="checkbox"], input[type="radio"] { accent-color: var(--accent); }
textarea { resize: vertical; min-height: 4.5rem; }
fieldset { border: 1px solid var(--line); border-radius: 0.75rem; padding: 0.75rem 1rem; margin: 0 0 1rem; }
legend { font-weight: 600; padding: 0 0.35rem; }
.two, .grid-fields { display: grid; grid-template-columns: repeat(auto-fit, minmax(10rem, 1fr)); gap: 0 1rem; }
.check { display: flex; align-items: center; gap: 0.5rem; margin-bottom: 1rem; }

button, .button { font: inherit; padding: 0.39rem 0.9rem; border-radius: 3px; border: 1px solid var(--line); background: var(--soft); color: inherit; cursor: pointer; text-decoration: none; display: inline-block; }
button[disabled] { opacity: 0.5; cursor: default; }
/* Buttons scale to their context: full-size in page heads and forms, compact inside lists, sidebars and cards. */
button.small, .button.small,
.list button:not(.link), .list .button,
.side-col button:not(.link), .side-col .button,
.product-card .button, .product-card button:not(.link),
.suggested .button, .users .button, .verify .actions .button,
.table button:not(.link), .table .button { padding: 0.3rem 0.65rem; font-size: 0.85rem; border-radius: 0.4rem; }
.list button.link, .table button.link, .product-card button.link { font-size: 0.85rem; }
.register .button.wide { padding: 0.6rem 0.9rem; font-size: 1rem; }
.list li > div:has(> button) { display: inline-flex; align-items: center; gap: 0.4rem; }
.primary { background: var(--accent); border-color: var(--accent); color: var(--accent-ink); font-weight: 600; box-shadow: 0 1px 2px color-mix(in srgb, var(--accent) 35%, transparent); }
@media (hover: hover) { .primary:hover:not([disabled]) { filter: brightness(1.06); } }
a { color: var(--accent-strong); }
.chip-user, .rig-card, .page-card, .event-card, .report-link, .sponsor { color: inherit; }
.side-col .board .chip-user .muted { display: none; }
.tab.active { border-color: transparent; border-bottom-color: var(--accent); color: var(--accent-strong); background: none; font-weight: 600; }
input:focus, select:focus, textarea:focus { outline: 2px solid color-mix(in srgb, var(--accent) 55%, transparent); outline-offset: 1px; border-color: var(--accent); }
.rank-master { border-color: var(--pop); color: var(--pop); }
button.link { background: none; border: none; padding: 0; text-decoration: none; font-size: inherit; display: inline; }
.danger { color: var(--no); }
.google { width: 100%; }
.actions { display: flex; gap: 0.75rem; align-items: center; flex-wrap: wrap; }
.row { display: flex; gap: 0.5rem; align-items: center; flex-wrap: wrap; }
.row input { flex: 1; width: auto; min-width: 8rem; }

.avatar { border-radius: 50%; object-fit: cover; flex: none; }
.avatar.placeholder { display: inline-flex; align-items: center; justify-content: center; background: var(--soft); font-weight: 700; }
/* Presence ring around a member's avatar (UserAvatar, shell.js). A box-shadow, not a border, so the avatar keeps its size. */
.presence-ring { display: inline-flex; flex: none; border-radius: 50%; box-shadow: 0 0 0 2px var(--presence-off); transition: box-shadow 0.3s ease; }
.presence-ring.online { box-shadow: 0 0 0 2px var(--presence-on), 0 0 6px color-mix(in srgb, var(--presence-on) 45%, transparent); }
/* Follower bubbles already draw a 2px border on the avatar: colour that instead of adding a second ring. */
.follower-bubble .presence-ring { width: 100%; height: 100%; box-shadow: none; }
.follower-bubble .presence-ring.online .avatar { border-color: var(--presence-on); }
.rank { font-size: 0.72rem; padding: 0.12rem 0.45rem; border-radius: 999px; border: 1px solid var(--line); vertical-align: middle; white-space: nowrap; }
.rank-tuner { border-color: #74c0fc; } .rank-dialed { border-color: #63e6be; } .rank-pro-tuner { border-color: #ffd43b; }
.tag { font-family: var(--font-mono); font-size: 0.72rem; letter-spacing: 0.04em; padding: 0.18rem 0.55rem; border: 1px solid var(--line); border-radius: 2px; background: var(--soft); margin-right: 0.25rem; }
.tag.ok { color: var(--ok); } .tag.no { color: var(--no); }
.tags { margin-top: 0.4rem; }
.socials { display: flex; flex-wrap: wrap; gap: 0.4rem; margin-top: 0.6rem; }
.social { font-size: 0.8rem; padding: 0.2rem 0.6rem; border-radius: 999px; border: 1px solid var(--line); text-decoration: none; }
@media (hover: hover) { .social:hover { border-color: currentColor; } }
.social-tiktok { border-color: #69c9d0; } .social-youtube { border-color: #ff0000; } .social-instagram { border-color: #e1306c; } .social-facebook { border-color: #1877f2; }
.chips { display: flex; flex-wrap: wrap; gap: 0.4rem; }
.chip { border: 1px solid var(--line); border-radius: 999px; padding: 0.25rem 0.7rem; cursor: pointer; display: inline-flex; align-items: center; gap: 0.35rem; }
.chip.on { border-color: var(--accent); }
.chip input { margin: 0; }
.chip-user { display: inline-flex; align-items: center; gap: 0.4rem; text-decoration: none; white-space: nowrap; min-width: 0; }
.chip-user b, .chip-user .muted { overflow: hidden; text-overflow: ellipsis; }
.users li, .suggested li, .board li { flex-wrap: nowrap; }
.users li .chip-user, .suggested li .chip-user { flex: 1; overflow: hidden; }
.side-col .suggested .grow { display: none; }
.side-col .chip-user .rank, .side-col .suggested .chip-user .muted { display: none; }

.profile-head { display: flex; gap: 1.25rem; align-items: flex-start; flex-wrap: wrap; margin-bottom: 1rem; }
.profile-head .grow { flex: 1; min-width: 14rem; }
.profile-head h1 { margin-bottom: 0.1rem; }
.profile-head .button, .profile-head button:not(.link) { padding: 0.45rem 0.85rem; font-size: 0.9rem; }

.grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(14rem, 1fr)); gap: 1rem; }
.rig-card { text-decoration: none; padding: 0; overflow: hidden; }
.rig-card img, .rig-card .cover { width: 100%; aspect-ratio: 4 / 3; object-fit: cover; background: var(--soft); }
.rig-card h3, .rig-card p { padding: 0 0.9rem; }
.rig-card p { padding-bottom: 0.8rem; margin: 0; font-size: 0.85rem; }
.gallery { display: grid; grid-template-columns: repeat(auto-fill, minmax(9rem, 1fr)); gap: 0.5rem; margin: 1rem 0; }
.gallery img { width: 100%; aspect-ratio: 1; object-fit: cover; border-radius: 0.5rem; }
.thumbs { display: flex; flex-wrap: wrap; gap: 0.5rem; margin-bottom: 0.5rem; }
.thumbs figure { margin: 0; text-align: center; font-size: 0.8rem; }
.thumbs img { width: 5rem; height: 5rem; object-fit: cover; border-radius: 0.5rem; }
.parts { display: flex; flex-direction: column; gap: 0.4rem; margin-bottom: 0.5rem; }
.part-row { display: grid; grid-template-columns: 10rem 1fr 1.4fr auto; gap: 0.4rem; align-items: center; }
@media (max-width: 40rem) { .part-row { grid-template-columns: 1fr 1fr; } }

.table { width: 100%; border-collapse: collapse; }
.table th, .table td { text-align: left; padding: 0.4rem 0.5rem; border-bottom: 1px solid var(--line); }
.table th { font-size: 0.8rem; text-transform: uppercase; letter-spacing: 0.04em; opacity: 0.7; }
.sheet dl { display: grid; grid-template-columns: max-content 1fr; gap: 0.3rem 1rem; margin: 0.5rem 0; }
.sheet dt { font-weight: 600; }
.sheet dd { margin: 0; }
.list { list-style: none; padding: 0; margin: 0; }
.list li { padding: 0.55rem 0; border-bottom: 1px solid var(--line); display: flex; align-items: center; gap: 0.5rem; flex-wrap: wrap; }
.list li details { flex-basis: 100%; }
.report-link { text-decoration: none; }
.fits { font-size: 0.75rem; padding: 0.15rem 0.5rem; border-radius: 0.35rem; font-weight: 600; color: white; vertical-align: middle; }
.fits-yes { background: var(--ok); } .fits-yes_with_mods { background: var(--pop); } .fits-no { background: var(--no); }
.verify { margin-top: 1.5rem; }

.board { list-style: none; padding: 0; margin: 0; }
.board li { display: flex; align-items: center; gap: 0.75rem; padding: 0.5rem 0; border-bottom: 1px solid var(--line); }
.board .pos { width: 2rem; text-align: right; font-variant-numeric: tabular-nums; opacity: 0.6; }
.board .chip-user { flex: 1; }
.board .score { font-variant-numeric: tabular-nums; font-weight: 600; }
details summary { cursor: pointer; }

.with-side { display: grid; grid-template-columns: minmax(0, 2fr) minmax(14rem, 1fr); gap: 2rem; }
@media (max-width: 48rem) { .with-side { grid-template-columns: minmax(0, 1fr); } }
.side-col h2 { margin-top: 0.5rem; }
.related { margin-top: 2rem; padding-top: 1rem; border-top: 1px solid var(--line); }
.related h3 { margin-top: 1rem; }
.suggested li .grow { flex: 1; }
.bell { position: relative; text-decoration: none; }
.badge { position: absolute; top: -0.5rem; right: -0.7rem; background: var(--pop); color: white; font-size: 0.65rem; padding: 0.05rem 0.35rem; border-radius: 999px; font-weight: 700; }
.stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(7rem, 1fr)); gap: 0.75rem; margin: 1rem 0 1.5rem; }
.stat { border: 1px solid var(--line); border-radius: 0.75rem; padding: 0.8rem 1rem; display: flex; flex-direction: column; gap: 0.15rem; }
.stat b { font-size: 1.5rem; font-variant-numeric: tabular-nums; letter-spacing: -0.02em; }
.stat span { font-size: 0.8rem; opacity: 0.7; }
.stat-online b::before { content: ""; display: inline-block; width: 0.55rem; height: 0.55rem; border-radius: 50%; background: var(--pop); margin-right: 0.4rem; vertical-align: middle; }
.stats.compact { display: flex; flex-wrap: wrap; gap: 1.25rem; margin: 0 0 0.5rem; }
.stats.compact .stat { border: 0; padding: 0; flex-direction: row; align-items: baseline; gap: 0.35rem; }
.stats.compact .stat b { font-size: 1rem; }
footer { margin-top: auto; padding: 1.25rem 1.25rem 1.5rem; border-radius: 3px 3px 0 0; background: #131211; color: var(--ink-dim); border-top: 1px solid var(--line); font-family: var(--font-mono); font-size: 0.78rem; letter-spacing: 0.03em; }
footer a { color: var(--ink-dim); }
footer .muted { opacity: 0.8; }
.sponsor-card { display: block; text-decoration: none; color: inherit; border: 1px solid var(--line); border-radius: 0.75rem; padding: 0.75rem 0.9rem; background: var(--surface); }
@media (hover: hover) { .sponsor-card:hover { border-color: var(--accent); } }
.sponsor-card .banner-img { width: 100%; aspect-ratio: 3 / 1; object-fit: cover; border-radius: 0.5rem; margin-bottom: 0.6rem; }
.sponsor-card.compact { padding: 0.5rem 0.7rem; }
.sponsor-card .row { flex-wrap: nowrap; }
.sponsor-card b { display: block; }
.tag.sponsored { font-size: 0.65rem; text-transform: uppercase; letter-spacing: 0.06em; color: var(--pop); background: color-mix(in srgb, var(--pop) 10%, transparent); margin: 0; }
.sponsor-list-v { display: flex; flex-direction: column; gap: 0.5rem; }
.sponsors-side h2 { margin-top: 0.5rem; }
.sponsor-inline { margin: 0.25rem 0; }
.sponsor-inline.stream { margin: 0; }
.list.reports li.sponsor-inline.stream { border-bottom: 0; padding: 0.3rem 0; display: block; }
.sponsor-form { margin-top: 1.25rem; padding-top: 1rem; border-top: 1px dashed var(--line); }
.mention { font-weight: 600; text-decoration: none; color: var(--accent-strong); }
.replies { border-top: 1px dashed var(--line); padding-top: 0.5rem; }
.thread { margin-top: 0.5rem; }
/* Reply thread reads like a DM: others' replies on the left with their avatar, yours on the right with none. */
.replies-list li.reply { display: flex; flex-direction: column; align-items: flex-start; gap: 0.2rem; border-bottom: 0; padding: 0.3rem 0; }
.replies-list li.reply.mine { align-items: flex-end; }
.replies-list li.reply .bubble { background: var(--soft); border-radius: 1rem; padding: 0.5rem 0.85rem; max-width: 80%; white-space: pre-wrap; }
.replies-list li.reply.mine .bubble { background: var(--accent-soft); }
.replies-list li.reply .reply-meta { gap: 0.5rem; font-size: 0.8rem; }
.replies-list li.reply.grouped { margin-top: -0.3rem; padding-top: 0; }
.reply-form { margin-top: 0.5rem; width: 100%; }
.reply-form input { flex: 1; }
.topic-form summary { cursor: pointer; list-style: none; }
.topic-form summary h2 { display: inline; margin: 0; }
.topic-form form { margin-top: 1rem; }
.topics li { align-items: flex-start; }
.topic-title { text-decoration: none; }
.group-card { text-decoration: none; padding: 0; overflow: hidden; }
.group-card img, .group-card .cover { width: 100%; aspect-ratio: 16 / 7; object-fit: cover; background: var(--soft); }
.group-card .body { padding: 0.8rem 0.9rem 1rem; }
.group-card h3 { margin: 0 0 0.2rem; }
.event-card { text-decoration: none; padding: 0; overflow: hidden; }
.event-card img, .event-card .cover { width: 100%; aspect-ratio: 16 / 9; object-fit: cover; background: var(--soft); }
.event-card .body { padding: 0.8rem 0.9rem 1rem; }
.event-card h3 { margin: 0 0 0.2rem; }
.status-draft { color: #868e96; } .status-published { color: var(--ok); } .status-closed { color: var(--no); }
.register .slots { font-size: 1.1rem; margin: 0 0 0.6rem; }
.register .slots b { font-size: 1.6rem; }
.button.wide { width: 100%; text-align: center; }
.share { margin-top: 1rem; }
.share input { font-size: 0.8rem; }
.sponsors { margin-top: 1.5rem; }
.tier h3 { margin: 1rem 0 0.4rem; font-size: 0.85rem; text-transform: uppercase; letter-spacing: 0.05em; opacity: 0.7; }
.tier-title h3, .tier-gold h3 { opacity: 1; color: var(--pop); }
.sponsor-list { display: flex; flex-wrap: wrap; gap: 0.5rem; }
.sponsor { display: inline-flex; align-items: center; gap: 0.5rem; text-decoration: none; border: 1px solid var(--line); border-radius: 0.6rem; padding: 0.35rem 0.7rem; }
.sponsor.big { padding: 0.6rem 0.9rem; font-weight: 600; border-color: var(--pop); }
.list.compact li { padding: 0.3rem 0; border-bottom: 0; }
.list.dates li { gap: 0.6rem; }
.event-form .rows { display: flex; flex-direction: column; gap: 0.4rem; margin-bottom: 0.5rem; }
.date-row { display: grid; grid-template-columns: 1fr 1fr 1fr auto; gap: 0.4rem; align-items: center; }
.raffle-row { display: grid; grid-template-columns: 1.2fr 1.2fr 1fr auto; gap: 0.4rem; align-items: center; }
.sponsor-row { display: grid; grid-template-columns: 2fr 1fr auto; gap: 0.4rem; align-items: center; }
@media (max-width: 40rem) { .date-row, .raffle-row, .sponsor-row { grid-template-columns: 1fr 1fr; } }
.composer { display: flex; flex-direction: column; gap: 0.5rem; margin-bottom: 1.25rem; }
.composer textarea { min-height: 3.5rem; }
.share-preview { display: flex; flex-direction: column; gap: 0.35rem; }
.post-list { display: flex; flex-direction: column; gap: 0.9rem; }
.post { display: flex; flex-direction: column; gap: 0.6rem; }
.post-text { margin: 0; }
.embed { width: 100%; border-radius: 0.6rem; overflow: hidden; background: var(--soft); }
.embed iframe { width: 100%; height: 100%; border: 0; display: block; }
.link-card { display: inline-flex; align-items: center; gap: 0.35rem; padding: 0.5rem 0.75rem; border: 1px solid var(--line); border-radius: 0.5rem; text-decoration: none; }
.feed li.feed-post { display: block; border-bottom: 0; padding: 0; }
.feed li.feed-post .post { margin-bottom: 0.5rem; }
.narrow-col { max-width: 40rem; margin-inline: auto; }
.banners { display: flex; flex-direction: column; gap: 0.5rem; margin: -0.75rem 0 1.25rem; }
.banner { display: flex; gap: 0.75rem; align-items: flex-start; padding: 0.7rem 0.9rem; border-radius: 0.6rem; border: 1px solid; }
.banner-info { border-color: #74c0fc; background: color-mix(in srgb, #74c0fc 15%, transparent); }
.banner-warning { border-color: #ffd43b; background: color-mix(in srgb, #ffd43b 18%, transparent); }
.banner-maintenance { border-color: var(--accent); background: color-mix(in srgb, var(--accent) 12%, transparent); }
.banner .link { font-size: 1.2rem; line-height: 1; text-decoration: none; }
.announcement { align-items: flex-start; }
.announcement.live { background: color-mix(in srgb, var(--accent) 6%, transparent); border-radius: 0.5rem; padding-inline: 0.5rem; }
.tag.level-warning { color: #b7791f; } .tag.level-maintenance { color: #c0392b; } .tag.level-info { color: var(--accent-strong); }
.banner-maintenance { border-color: #c0392b; background: color-mix(in srgb, #c0392b 10%, transparent); }
.page-cover { width: 100%; max-height: 16rem; object-fit: cover; border-radius: 0.9rem; margin-bottom: 1rem; }
.page-card { text-decoration: none; }
.page-card h3 { margin: 0; }
.product-card { padding: 0; overflow: hidden; display: flex; flex-direction: column; }
.product-card img, .product-card .cover { width: 100%; aspect-ratio: 1; object-fit: cover; background: var(--soft); }
.product-card .body { padding: 0.8rem 0.9rem 1rem; display: flex; flex-direction: column; gap: 0.35rem; }
.product-card h3 { margin: 0; }
/* Product layouts (ProductLayoutSwitcher): one card markup, the container class lays it out. Grid is the default.
   Large: fewer, bigger cards. List: a row per product, photo left. Compact: small tiles, photo and name only. */
.product-bar { display: flex; flex-wrap: wrap; align-items: center; gap: 0.5rem 0.9rem; margin: 0.2rem 0 0.6rem; }
.product-bar .product-filter { flex: 1 1 14rem; max-width: 22rem; }
.load-more { margin-top: 1rem; display: flex; align-items: center; gap: 0.6rem; }
.product-layouts { display: flex; flex-wrap: wrap; align-items: center; gap: 0.4rem; margin: 0.6rem 0 0.9rem; }
.product-layouts .chip { cursor: pointer; }
.grid.products-large { grid-template-columns: repeat(auto-fill, minmax(20rem, 1fr)); }
.grid.products-compact { grid-template-columns: repeat(auto-fill, minmax(8.5rem, 1fr)); gap: 0.7rem; }
.products-compact .product-card .body { padding: 0.5rem 0.6rem 0.7rem; gap: 0.2rem; }
.products-compact .product-card h3 { font-size: 0.95rem; }
.products-compact .product-card .price, .products-compact .product-card .product-details-wrap, .products-compact .product-card .muted.small { display: none; }
.products-compact .product-card .product-actions .button, .products-compact .product-card .product-owner .button { font-size: 0.75rem; padding: 0.2rem 0.45rem; }
.grid.products-list { grid-template-columns: minmax(0, 1fr); gap: 0.6rem; }
.products-list .product-card { flex-direction: row; align-items: stretch; }
.products-list .product-card img, .products-list .product-card .cover { width: 8rem; aspect-ratio: 1; flex: none; }
.products-list .product-card .body { flex: 1; padding: 0.6rem 0.9rem; }
.products-list .product-card .product-details { -webkit-line-clamp: 2; line-clamp: 2; }
.products-list .product-card .product-owner { margin-top: 0.3rem; padding-top: 0; }
@media (max-width: 40rem) {
    .products-list .product-card img, .products-list .product-card .cover { width: 5.5rem; }
    .grid.products-compact { grid-template-columns: repeat(auto-fill, minmax(7rem, 1fr)); }
}

/* Product details are clipped to 3 lines on display so cards stay a similar height; "more"/"less" toggles .expanded
   (pages.js). The stored description is never shortened. */
.product-card .product-details-wrap { display: grid; justify-items: start; gap: 0.1rem; }
.product-card .product-more { padding: 0; }
.product-card .product-details { margin: 0; display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 3; line-clamp: 3; overflow: hidden; }
.product-card .product-details.expanded { display: block; -webkit-line-clamp: unset; line-clamp: unset; overflow: visible; }
.product-card .product-actions { margin: 0.15rem 0 0.1rem; }
.product-card .product-owner { margin-top: auto; padding-top: 0.35rem; }
.price { font-weight: 700; margin: 0; display: flex; gap: 0.5rem; align-items: center; }
.event { align-items: flex-start; }
.event .thumb { width: 6rem; height: 6rem; object-fit: cover; border-radius: 0.5rem; }
/* The two inboxes share one treatment. Both emit `li.unread clickable`, but these rules used to name only
   .feed, so the Messages list rendered the same markup with nothing styling it: no row background, no
   pointer, no hover — just floating text. Widen the selector rather than write bespoke message rules, so
   the two stay identical instead of drifting apart. Rows are marked by background and radius, never a
   divider: those are removed deliberately further down ("No divider lines under linked rows"). */
/* Rows read as rows AT REST, not only under the pointer. Nothing else in the app marks a row: line 176 sets
   a border-bottom that line 429 unconditionally zeroes for every .list, so that base rule is dead. A subtle
   fill carries it instead, with the same inset the unread and hover states use so nothing shifts between
   states. feed-post rows are excluded: they render a whole post card and opt out of row chrome above.
   This rule must stay ABOVE the unread rule - the two have identical specificity (:not counts its
   argument), so source order alone decides which wins, and unread has to. */
.feed li:not(.feed-post), .conversations li { background: var(--soft); border-radius: 0.4rem; padding-inline: 0.4rem; }
.feed li.unread, .conversations li.unread { background: color-mix(in srgb, var(--accent) 8%, transparent); border-radius: 0.4rem; padding-inline: 0.4rem; }
.purchase-form summary { cursor: pointer; list-style: none; }
.purchase-form summary h2 { display: inline; margin: 0; }
.purchase-form summary::after { content: " ▸"; opacity: 0.6; }
.purchase-form[open] summary::after { content: " ▾"; }
.purchase-form form { margin-top: 1rem; }

.link-preview { display: flex; gap: 0.75rem; align-items: stretch; border: 1px solid var(--line); border-radius: 0.6rem; overflow: hidden; text-decoration: none; color: inherit; background: var(--surface); max-width: 40rem; margin-top: 0.5rem; }
.link-preview img { width: 7rem; min-width: 7rem; object-fit: cover; }
.link-preview .lp-body { padding: 0.55rem 0.75rem; display: flex; flex-direction: column; gap: 0.15rem; min-width: 0; }
.link-preview b { display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.feed li.clickable, .conversations li.clickable { cursor: pointer; }
@media (hover: hover) { .feed li.clickable:hover, .conversations li.clickable:hover { background: var(--accent-soft); border-radius: 0.4rem; padding-inline: 0.4rem; } }
.mute { display: inline-flex; align-items: center; gap: 0.3rem; }
.mute select { width: auto; padding: 0.2rem 0.4rem; font-size: 0.8rem; }
.catalog h2 { margin-top: 1.25rem; }
.card.compact { padding: 0.75rem 1rem; margin-top: 0.75rem; }
.card.compact > summary { cursor: pointer; font-weight: 600; }

.about h2 { margin-top: 1.25rem; }
.about .contact { list-style: none; padding: 0; margin: 0; display: grid; gap: 0.25rem; }
label.check { display: flex; gap: 0.5rem; align-items: center; margin: 0.75rem 0; }
label.check input { width: auto; }

.like { display: inline-flex; align-items: center; gap: 0.15rem; border: 1px solid var(--line); background: var(--surface); color: var(--ink); border-radius: 999px; padding: 0.15rem 0.6rem; cursor: pointer; text-decoration: none; font: inherit; }
.like.on { color: var(--pop); border-color: var(--pop); }
.like .count { font-variant-numeric: tabular-nums; }
.post-actions { margin-top: 0.4rem; }

.conversations li .ellipsis { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.messages { list-style: none; padding: 0; display: grid; gap: 0.5rem; }
.messages .msg { display: flex; flex-direction: column; align-items: flex-start; gap: 0.1rem; }
.messages .msg.mine { align-items: flex-end; }
.messages .bubble { background: var(--soft); border: none; border-radius: 1rem; padding: 0.5rem 0.85rem; max-width: 75%; white-space: pre-wrap; }
.messages .msg.mine .bubble { background: var(--accent-soft); border-color: var(--accent); }
/* The reply form never leaves the screen. The thread scrolls the page, the same model the group chat uses, and
   the composer sticks to the bottom of the viewport — rather than giving the page a fixed height, which would
   need a magic header offset and break as mobile browser chrome grows and shrinks. Opaque background so
   messages cannot show through it, and the safe-area inset keeps it clear of a phone's home indicator. */
.dm-form { position: sticky; bottom: 0; z-index: 2; background: var(--bg); padding-block: 0.5rem; padding-bottom: calc(0.5rem + env(safe-area-inset-bottom, 0px)); }
.dm-form input { flex: 1; }
/* Brand challenges: criteria rows, the judge's star pickers and the ranking list. */
.challenge-cover { width: 100%; max-height: 16rem; object-fit: cover; border-radius: 0.6rem; margin: 0.5rem 0; }
.criteria-rows { display: grid; gap: 0.35rem; }
.criteria-rows .row input[type="number"] { width: 5rem; flex: none; }
.criteria-list li { gap: 0.4rem; }
.judge-panel .judge-row { display: flex; flex-direction: column; align-items: flex-start; gap: 0.3rem; padding: 0.6rem 0; }
.criteria-scores { display: grid; gap: 0.2rem; }
.criteria-scores .row { gap: 0.5rem; }
.stars-pick { display: inline-flex; gap: 0.1rem; }
.stars-pick .star { border: none; background: transparent; padding: 0 0.1rem; font-size: 1.15rem; line-height: 1; cursor: pointer; color: var(--muted); }
.stars-pick .star.on { color: var(--accent); }
.ranking { counter-reset: none; }
.ranking li { gap: 0.5rem; }
.challenge-results .ranking li b { min-width: 3rem; }

/* Rich text box: a contenteditable styled like an input, with its format toolbar above. */
.rich-editor-wrap { flex: 1; min-width: 0; display: flex; flex-direction: column; }
.rich-editor { min-height: 2.4rem; max-height: 12rem; overflow-y: auto; padding: 0.45rem 0.7rem; border: 1px solid var(--line); border-radius: 3px; background: var(--surface); color: inherit; text-align: left; }
.rich-editor:focus { outline: 2px solid var(--accent); outline-offset: -1px; }
.rich-editor:empty::before { content: attr(data-placeholder); color: var(--placeholder); pointer-events: none; }
.rich-editor a { color: var(--accent); }
.format-toolbar { display: flex; gap: 0.15rem; margin-bottom: 0.3rem; }
.fmt-btn { border: none; background: transparent; padding: 0.15rem 0.45rem; border-radius: 0.35rem; cursor: pointer; font-size: 0.9rem; line-height: 1.4; min-width: 1.6rem; text-align: center; }
@media (hover: hover) { .fmt-btn:hover { background: var(--soft); } }
.fmt-bold { font-weight: 700; }
.fmt-italic { font-style: italic; }
.fmt-underline { text-decoration: underline; }
.fmt-strike { text-decoration: line-through; }
.emoji-picker { position: relative; flex: none; }
button.emoji-toggle { font-size: 1.6rem; line-height: 1; padding: 0.25rem 0.4rem; }
/* Emoji within posted text (posts, replies): a bit larger than the surrounding body text, like most chat apps. */
.emoji-inline { font-size: 1.3em; line-height: 1; vertical-align: -0.2em; }
.emoji-pop { position: absolute; bottom: 100%; left: 0; z-index: 20; display: grid; grid-template-columns: repeat(6, 2.1rem); gap: 0.15rem; background: var(--surface); border: 1px solid var(--line); border-radius: 0.5rem; padding: 0.4rem; margin-bottom: 0.3rem; box-shadow: 0 4px 16px rgba(0, 0, 0, 0.35); }
.emoji-btn { border: none; background: transparent; font-size: calc(1.2rem + 2px); line-height: 1; padding: 0.25rem; border-radius: 0.35rem; cursor: pointer; }
@media (hover: hover) { .emoji-btn:hover { background: var(--soft); } }
.composer .emoji-picker { align-self: flex-start; }
details.report summary { display: inline; cursor: pointer; color: var(--muted); }
.report-form { display: flex; gap: 0.4rem; flex-wrap: wrap; margin-top: 0.4rem; }
.report-form select, .report-form input { width: auto; flex: 1; min-width: 8rem; }
.sponsor-tiny a { color: inherit; }
.donation-link { display: inline-block; margin-top: 0.25rem; text-decoration: none; color: var(--accent); }
@media (hover: hover) { .donation-link:hover { text-decoration: underline; } }
.tag.danger { color: var(--no); }
.locations-list .location { align-items: flex-start; }
.draws { margin-top: 1rem; }

.banner { padding: 0.75rem 1rem; border-radius: 0.6rem; margin: 0.5rem 0 1rem; }
.banner.danger { background: color-mix(in srgb, var(--no) 16%, var(--surface)); color: #f3c9bf; border: 1px solid color-mix(in srgb, var(--no) 45%, transparent); }
.banner.warn { background: color-mix(in srgb, var(--accent) 16%, var(--surface)); color: var(--accent-strong); border: 1px solid color-mix(in srgb, var(--accent) 45%, transparent); }
/* Group/admin warnings are sentences (bold title, text, a link): read them as text, not as flex columns — .banner is a
   flex row for announcement banners and their dismiss button, which squeezed each piece into its own narrow column. */
.banner.warn, .banner.danger { display: block; }
.mod-controls { display: flex; gap: 0.5rem; align-items: center; flex-wrap: wrap; }
.card.mod-controls h2 { margin: 0 0.5rem 0 0; font-size: 1rem; }
.search-form input { flex: 1; }
.table { width: 100%; border-collapse: collapse; font-size: 0.9rem; }
.table th, .table td { text-align: left; padding: 0.4rem 0.5rem; border-bottom: 1px solid var(--line); vertical-align: top; }

.verified { color: var(--accent-strong); font-weight: 700; }
.verified.small { font-size: 0.85em; }
.stars { color: var(--pop); letter-spacing: 0.05em; }
.stars.small { font-size: 0.85em; }
.rating-summary { display: inline-flex; align-items: center; gap: 0.2rem; }
/* Ratings & comments: the rows were cramped — the chip, the stars line and the comment all ran together, and your own
   row was pulled outwards by negative margins. Each row now has room of its own and the comment breathes under it. */
.reviews { margin-top: 1.5rem; margin-bottom: 1.25rem; }
.reviews > h2 { margin: 0 0 0.6rem; }
.review-form select { max-width: 14rem; }
.review-list { margin-top: 0.25rem; }
.review-list li { align-items: flex-start; gap: 0.9rem; padding: 0.9rem 0; }
.review-list li:last-child { border-bottom: 0; }
.review-list li.mine { background: var(--soft); border-radius: 0.5rem; padding: 0.9rem; margin: 0 0 0.6rem; border-bottom: 0; }
.review-list .grow { display: grid; gap: 0.5rem; min-width: 0; }
.review-list .grow > .prose { margin: 0; }
.review-list .gallery { margin-top: 0.15rem; }
.review-list .row.inline { display: inline-flex; }
.tag.live { color: var(--pop); border-color: var(--pop); font-weight: 700; }
.tag.mentor { color: var(--accent-strong); }
.mentors li .grow { margin-left: 0.5rem; }

/* No underlined text anywhere: links read by colour and weight instead. */
a, a:hover, a:focus, button.link, .link, summary, u { text-decoration: none !important; }
.follower-bubbles { display: flex; flex-wrap: wrap; align-items: flex-end; gap: 0.6rem; padding: 0.5rem 0; }
.follower-bubble { position: relative; display: inline-flex; align-items: center; justify-content: center; border-radius: 50%; flex: none; text-decoration: none; }
.follower-bubble .avatar, .follower-bubble .avatar.placeholder { width: 100% !important; height: 100% !important; border: 2px solid var(--line); transition: transform 0.15s ease, border-color 0.15s ease; }
@media (hover: hover) { .follower-bubble:hover .avatar, .follower-bubble:hover .avatar.placeholder { transform: scale(1.08); border-color: var(--accent); } }
.follower-bubble-name { display: none; }

.avatar-wrap { position: relative; display: inline-block; flex: none; }
.verified-badge { position: absolute; right: -2px; bottom: -2px; border-radius: 50%; background: var(--accent); border: 2px solid var(--surface); display: inline-flex; align-items: center; justify-content: center; }
.verified-badge { color: var(--accent-ink); font-size: 0.6em; font-weight: 900; line-height: 1; }
.tag.verified-tag { color: var(--accent-strong); border-color: var(--accent); }

/* Names: display name with the @handle underneath, smaller. */
.chip-user .chip-names { display: inline-flex; flex-direction: column; line-height: 1.15; min-width: 0; }
.chip-user .chip-names .handle { font-size: 0.75rem; }
.chip-user .rank { font-size: 0.65rem; vertical-align: middle; }
/* No divider lines under linked rows or between messages. */
.list li, .board li, .conversations li, .messages li { border-bottom: 0; }
.messages .msg.mine .bubble { background: var(--accent-soft); border: none; }

/* Guest landing: hero on the left, sign-in / sign-up card on the right. */
.guest-landing { display: grid; grid-template-columns: minmax(0, 1.3fr) minmax(18rem, 1fr); gap: 1.5rem; align-items: start; margin-bottom: 1.5rem; }
.guest-landing .hero { padding: 0.5rem 0; }
.guest-landing .hero h1 { font-size: 2.2rem; line-height: 1.1; margin: 0 0 0.75rem; }
.guest-landing .lead { font-size: 1.05rem; }
.hero-points { list-style: none; padding: 0; margin: 1rem 0; display: grid; gap: 0.5rem; }
.hero-points li::before { content: "▸ "; color: var(--pop); }
.auth-card { padding: 1rem 1.25rem; }
@media (max-width: 760px) { .guest-landing { grid-template-columns: 1fr; } }

/* Settings: vertical tabs on the left, the form using the rest of the page width. */
.settings-layout { display: grid; grid-template-columns: 12rem minmax(0, 1fr); gap: 1.5rem; align-items: start; }
/* Clears the floating header instead of sliding under it. --header-h is measured and published by App
   (shell.js) because the header's height genuinely varies — two rows on desktop, a slim bar on a phone,
   three when the signed-in account row wraps — so a fixed offset would be wrong on most of them. The
   fallback only applies if ResizeObserver is unavailable. */
.tabs.vertical { flex-direction: column; align-items: stretch; gap: 0.1rem; border-bottom: 0; position: sticky; top: calc(var(--header-h, 6.5rem) + 1rem); }
.tabs.vertical .tab { text-align: left; border-bottom: 0; border-left: 2px solid transparent; margin-bottom: 0; padding: 0.45rem 0.7rem; }
.tabs.vertical .tab.active { border-bottom-color: transparent; border-left-color: var(--accent); }
.settings-body .tab-panel .two { max-width: 48rem; }
@media (max-width: 700px) { .settings-layout { grid-template-columns: minmax(0, 1fr); } .tabs.vertical { flex-direction: row; flex-wrap: wrap; position: static; } }

/* Page header: name + type, tagline, then one airy meta row and a separate "Run by" line. */
.profile-head .tagline { margin: 0.25rem 0 0.5rem; }
.page-meta { display: flex; flex-wrap: wrap; align-items: center; gap: 0.5rem 1.25rem; margin: 0.35rem 0; color: var(--muted); font-size: 0.95rem; }
.page-meta .meta-item { display: inline-flex; align-items: center; gap: 0.25rem; }
.page-meta.run-by { gap: 0.5rem; }
.page-meta .chip-user .rank { display: none; }

.badge-strip { display: inline-flex; gap: 0.75rem; margin-top: 0.4rem; font-size: 1.05rem; }
.badge-item b { font-size: 0.9rem; }
.podium li, .trophies li { gap: 0.6rem; }
.medal { font-size: 1.3rem; }
.result-row { display: grid; gap: 0.5rem; }
.result-row .row .field { flex: 1; }
.results { margin-top: 1rem; }

/* Footer sits at the bottom of the viewport on short pages and after the content on long ones (never over it). */
.app { min-height: 100dvh; display: flex; flex-direction: column; }
.app > main { flex: 1 0 auto; padding-bottom: 3rem; }

.board .stats { white-space: nowrap; }
.board .badges { display: inline-flex; gap: 0.5rem; }
@media (max-width: 700px) { .board .stats { flex-basis: 100%; padding-left: 2.2rem; } }

.chip-user.compact b { font-weight: 600; }

/* Slightly larger profile photos in posts and replies. */
.post .chip-user .avatar, .replies-list .chip-user .avatar { width: 33px !important; height: 33px !important; }

.grow { flex: 1 1 auto; min-width: 0; }
.post > .row:first-child { justify-content: flex-end; }
.post > .row:first-child .chip-user { margin-right: auto; }
.role-badges { display: inline-flex; gap: 0.3rem; margin-left: 0.3rem; }
.tag.role.super { color: #e8917c; border-color: #e8917c; }
.tag.role.admin { color: var(--pop); border-color: var(--pop); }
.tag.role.moderator { color: var(--accent-strong); border-color: var(--accent-strong); }
.page-meta.team { gap: 0.5rem; }

/* Header search box */
/* Logo · search · space · account on one row. The search box is anchored right after the logo at a fixed 21rem, so it
   never moves or resizes when the account links change (they load after sign-in, and differ per account); only the
   empty column between search and account absorbs that. The page is at most 85rem (1360px) wide, and 21rem fits beside a signed-in admin's links in it;
   it shrinks towards 12rem only if an unusually long handle leaves no room. */
header .topbar { display: grid; grid-template-columns: max-content minmax(12rem, 21rem) minmax(0, 1fr) max-content; grid-template-areas: "brand search . account"; column-gap: 1.5rem; }
header .topbar .brand { grid-area: brand; justify-self: start; }
header .topbar form.header-search { grid-area: search; width: 100%; margin: 0; }
header .topbar nav.account { grid-area: account; justify-self: end; }
header .topbar form.header-search input { width: 100%; padding: 0.35rem 0.7rem; font-size: 0.9rem; border-radius: 999px; }
/* Tablet and phone: logo and account share the first row (the account links wrap if they must, logo stays at the
   top); the search box takes the full width of the second row, so it never gets squeezed by a long account side. */
@media (max-width: 60rem) {
    header .topbar { grid-template-columns: auto 1fr; grid-template-areas: "brand account" "search search"; row-gap: 0.6rem; align-items: start; }
    /* room for the bell badge (it sits 0.7rem right of the bell) when the bell ends a wrapped line */
    header .topbar nav.account { justify-content: flex-end; padding-right: 0.7rem; }
}
/* Feature requests */
.requests li { align-items: flex-start; }
.tag.status-open { color: var(--muted); } .tag.status-planned { color: var(--accent-strong); } .tag.status-in_progress { color: var(--pop); } .tag.status-done { color: var(--ok); } .tag.status-declined { color: var(--no); }
.requests li.highlight { background: var(--accent-soft); border-radius: 0.5rem; padding: 0.5rem; }
.status-form select, .status-form input { width: auto; }
.status-form input { flex: 1; min-width: 10rem; }

header select.lang { width: auto; padding: 0.2rem 0.4rem; font-size: 0.85rem; border-radius: 999px; }
.settings-body select.lang { width: auto; }

.attachment-list li.attachment { display: grid; gap: 0.3rem; padding: 0.6rem 0; }
.attachment-list li.attachment img { max-width: 16rem; max-height: 12rem; border-radius: 0.5rem; display: block; }
.attachment-list .doc { display: inline-flex; gap: 0.4rem; align-items: center; }
.attachment-list .warn, .attachments .warn { color: var(--pop); font-weight: 600; }
.upload-row { display: grid; gap: 0.5rem; margin-bottom: 0.75rem; }
.upload-row form input[name="url"] { flex: 2; } .upload-row form input[name="name"] { flex: 1; }
.dm-form .attach { padding: 0.35rem 0.6rem; }
.messages .msg-image { max-width: 14rem; max-height: 12rem; border-radius: 0.6rem; display: block; }
.attachment-list .image { display: grid; gap: 0.25rem; justify-items: start; }
.attachment-list .media, .messages .msg-media { display: grid; gap: 0.25rem; justify-items: start; }
.attachment-media { max-width: min(100%, 28rem); border-radius: 0.5rem; background: #000; }
audio.attachment-media { background: transparent; width: min(100%, 22rem); }
.messages .msg-media .attachment-media { max-width: min(100%, 18rem); }

/* Feature toggles: the wrapper around a gated link must not affect layout. */
.feature-gate { display: contents; }
/* Feature switches: a grid so the columns line up — each row is a fixed checkbox column then its label, and the
   legend and hint span the full width. Wrapping flex items left the checkboxes and labels ragged. */
.feature-toggles { flex-basis: 100%; width: 100%; display: grid; grid-template-columns: repeat(auto-fit, minmax(min(15rem, 100%), 1fr)); gap: 0.4rem 1.5rem; border: 0; padding: 0; margin: 0.5rem 0; }
.feature-toggles legend { font-weight: 600; padding: 0; }
.feature-toggles legend, .feature-toggles small { grid-column: 1 / -1; }
.feature-toggles label { display: grid; grid-template-columns: 1.15rem 1fr; align-items: center; gap: 0.5rem; min-width: 0; }
/* The settings form is a .row, and `.row input { min-width: 8rem }` also matched these checkboxes: each box was
   forced to 128px, overflowing its column and sitting on top of the label. Let them be their natural size. */
.feature-toggles label input[type="checkbox"] { width: auto; min-width: 0; flex: none; margin: 0; justify-self: start; }

/* Reply forms: the Reply (or Save) button sits beside the text box, pinned to the box's top edge, so it doesn't
   move as the box grows. The editor wrapper's toolbar and box become grid items so the button lines up with the box. */
.reply-form { display: grid; grid-template-columns: minmax(0, 1fr) auto; column-gap: 0.5rem; align-items: start; }
.reply-form .rich-editor-wrap { display: contents; }
.reply-form .rich-editor-wrap > * { grid-column: 1; }
.reply-form .format-toolbar { grid-row: 1; }
.reply-form .rich-editor { grid-row: 2; }
.reply-form > .row { grid-column: 2; grid-row: 2; align-self: start; margin: 0; flex-wrap: nowrap; }
.reply-form > .error { grid-column: 1 / -1; }

/* Phone: the site links are one row you swipe sideways instead of three wrapped rows. The row runs to the screen
   edges (cancelling the body's 1rem side padding) and keeps room underneath for the active-link underline. */
@media (max-width: 40rem) {
    /* Full-screen chats - a DM thread, and a group's Posts tab - hide the footer on a phone. Its links are
       dead weight under a message stream that already owns the screen, and they sit right where the composer
       wants to be: the floating composer only COVERS them, leaving them in the layout and reachable by
       scrolling. The class is set on the footer by App, since CSS cannot reach a sibling of main from inside
       the page, and the media query keeps desktop untouched. */
    footer.hide-on-phone { display: none; }
    /* The divider under the header is its border-bottom, so bottom padding is what moves the line down. */
    header { padding-bottom: calc(0.2rem + 5px); }
    header nav.site { flex-wrap: nowrap; overflow-x: auto; scrollbar-width: none; margin-inline: -1rem; padding: 0 1rem 0.7rem; gap: 1.1rem; }
    header nav.site::-webkit-scrollbar { display: none; }
    header nav.site a { white-space: nowrap; flex: none; }
}
/* Phone: product images are 16:9 instead of full-width squares, and a product with no photo gets a thin strip
   rather than a big empty square. File links: the link field takes its own row so it isn't squeezed. */
@media (max-width: 40rem) {
    .product-card img, .product-card .cover { aspect-ratio: 16 / 9; }
    .product-card .cover:empty { aspect-ratio: auto; height: 4rem; }
    .attachments form.row input[name="url"] { flex-basis: 100%; }
}

/* Touch screens: fingers need targets of about 44px. Small and text-style buttons keep their look; they get a bigger
   hit area (link buttons grow invisibly — vertical padding on an inline element doesn't move the text around it). */
@media (pointer: coarse) {
    button.small, .button.small { min-height: 2.5rem; padding-inline: 0.8rem; }
    button.link { padding: 0.6rem 0.4rem; margin-inline: -0.4rem; }
    .fmt-btn, .emoji-btn, button.emoji-toggle { min-width: 2.5rem; min-height: 2.5rem; }
    .tab { padding-block: 0.5rem; }
}

/* A button switched off by a feature toggle says why in plain text (hover tooltips never show on touch). */
.feature-off { display: inline-flex; flex-direction: column; align-items: flex-start; gap: 0.2rem; }
.feature-off-note { display: block; color: var(--muted); font-size: 0.8rem; }
.actions .feature-off-note { flex-basis: 100%; }
.reply-form > .feature-off-note, .dm-form > .feature-off-note { grid-column: 1 / -1; margin-top: 0.25rem; }

/* Admin → Sponsors: live preview of the sponsor in every placement (unticked placements dimmed). */
.sponsor-preview { margin: 1rem 0; }
.sponsor-preview h4 { margin: 0 0 0.25rem; }
.sponsor-preview-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(16rem, 100%), 1fr)); gap: 0.75rem; margin-top: 0.5rem; }
.sponsor-preview-slot { border: 1px dashed var(--line); border-radius: 0.6rem; padding: 0.6rem; display: grid; gap: 0.45rem; align-content: start; min-width: 0; }
.sponsor-preview-slot.off { opacity: 0.45; }
.sponsor-preview-label { display: grid; gap: 0.1rem; }
.sponsor-preview-frame { background: var(--bg); border-radius: 0.4rem; padding: 0.5rem; min-width: 0; }
.sponsor-preview-frame .list.reports li { padding: 0.3rem 0; }
.sponsor-preview-context { margin: 0 0 0.25rem; font-style: italic; }

/* The group composer floats below the thread and doubles as the reply box. Sticky, not fixed, so it sits inside
   the column and never covers the page on a phone; it keeps its card look and lifts above the posts. */
.composer.floating { position: sticky; bottom: 0.5rem; z-index: 5; margin: 1rem 0 0; box-shadow: 0 -2px 12px rgba(0, 0, 0, 0.35); }
/* On a phone `sticky` pins the bar inside the scrolling column, so it sat over the posts mid-page instead of
   floating above them. Fix it to the viewport bottom there, and pad the page so the last post clears it. */
@media (max-width: 60rem) {
    .composer.floating { position: fixed; left: 0; right: 0; bottom: 0; margin: 0; border-radius: 0.6rem 0.6rem 0 0; padding: 0.6rem 1rem calc(0.6rem + env(safe-area-inset-bottom, 0px)); }
    .group-page-with-composer { padding-bottom: 14rem; }
}
.composer.floating .rich-editor { max-height: 8rem; }
.composer.replying { border-color: color-mix(in srgb, var(--accent) 45%, var(--line)); }
.composer .replying-to { display: flex; flex-wrap: wrap; align-items: baseline; gap: 0.15rem 0.4rem; }
.composer .replying-to > :first-child { flex: 0 0 auto; white-space: nowrap; }

/* A quoted reply: the snapshot of what it answers, above the reply's own text. */
.quoted { border-left: 2px solid color-mix(in srgb, var(--accent) 55%, transparent); padding: 0.15rem 0 0.15rem 0.5rem; margin-bottom: 0.35rem; opacity: 0.85; }
.quoted .quoted-text { font-size: 0.9rem; overflow: hidden; display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; }
.quoted .chip-user { pointer-events: none; }
.replying-to .quoted-preview { flex: 1 0 100%; margin: 0.25rem 0 0; }

/* Phone: the site header collapses to a slim bar holding just the logo, behind a floating burger at the top left.
   The chrome (search, account links, 11 nav links) plus a group's own header used to fill the screen before the
   first post. Open, it overlays the page rather than pushing it down, so it is reachable however far you scrolled;
   a link, Escape or the ✕ closes it. Desktop and tablet are untouched: the burger only exists below 40rem. */
.burger { display: none; }
@media (max-width: 40rem) {
    .burger {
        display: inline-flex; position: fixed; top: calc(0.45rem - 2px); left: 0.5rem; z-index: 30;
        align-items: center; justify-content: center; min-width: 2.5rem; min-height: 2.5rem;
        padding: 0; border-radius: 999px; font-size: 1.05rem; line-height: 1;
    }
    /* Collapsed: the logo stays (so you know where you are), everything else folds away. */
    header { padding-left: 3.2rem; }
    header .topbar { grid-template-columns: minmax(0, 1fr); grid-template-areas: "brand"; }
    header > .topbar form.header-search, header > .topbar nav.account, header > nav.site { display: none; }
    /* Open: a full-screen sheet over the page. The logo must not move when the sheet opens, so the sheet keeps the
       collapsed bar's top padding (0.8rem) and the logo is pushed right to the bar's 3.2rem, clear of the burger;
       the menu then unfolds below it. (It used to pad 3.2rem top / 1rem left, dropping the logo down and left.) */
    header.menu-open {
        position: fixed; inset: 0; z-index: 25; margin: 0; overflow-y: auto;
        background: var(--bg); padding: 0.8rem 1rem calc(1.5rem + env(safe-area-inset-bottom, 0px));
    }
    header.menu-open .topbar .brand { margin-left: 2.2rem; }
    header.menu-open .topbar { grid-template-columns: minmax(0, 1fr); grid-template-areas: "brand" "search" "account"; row-gap: 0.7rem; }
    header.menu-open > .topbar form.header-search, header.menu-open > .topbar nav.account { display: flex; }
    header.menu-open .topbar nav.account { justify-content: flex-start; }
    /* In the sheet the site links wrap into a block instead of the one-row sideways swipe. */
    header.menu-open > nav.site { display: flex; flex-wrap: wrap; overflow-x: visible; margin-inline: 0; padding: 0; gap: 1rem 1.1rem; }
}

/* A group's own header (title, run-by, tagline, platforms) folds away on a phone so the posts get the screen.
   The title and the join/leave action always stay: collapsed is still a usable bar, not a mystery. */
.head-toggle { display: none; }
@media (max-width: 40rem) {
    .head-toggle { display: inline-flex; align-self: flex-start; }
    .profile-head.collapsed { margin-bottom: 0.6rem; }
    .profile-head.collapsed .grow > :not(h1) { display: none; }
    .profile-head.collapsed h1 { font-size: 1.15rem; margin-bottom: 0; }
}

/* Phone: the group's chrome (only-admin nag, moderation card, cover) folds away with the header, and an
   owner's Manage/notifications row folds with it. A non-member's join button stays — it is the point of the page. */
@media (max-width: 40rem) {
    .group-chrome.collapsed { display: none; }
    /* Collapsed on a phone the group bar is exactly two things: the burger and the group name. The privacy
       tag lives inside the <h1> so it survives the .grow > :not(h1) fold above and needs hiding by name, and
       the whole actions block folds - join, member/admin tags, mute, leave, and the pending/blocked states,
       which all come from joinBlock. This deliberately supersedes an earlier decision to keep a non-member's
       join button visible: joining from a phone now means opening the burger first. */
    .profile-head.collapsed h1 .tag { display: none; }
    .profile-head.collapsed .head-actions { display: none; }
    .profile-head.collapsed { padding-block: 0.6rem; }
}

/* A group Posts tab reads as a chat: posts and replies in one stream, yours on the right, everyone else on the
   left with their avatar, runs from one person grouped tight. Same bubble vocabulary as DMs and replies. */
.chat-list { display: flex; flex-direction: column; gap: 0.35rem; }
.chat-list li.chat-msg { display: flex; flex-direction: column; align-items: flex-start; gap: 0.15rem; border-bottom: 0; padding: 0.25rem 0; }
.chat-list li.chat-msg.mine { align-items: flex-end; }
.chat-list li.chat-msg.grouped { margin-top: -0.25rem; padding-top: 0; }
/* The bubble is wrapped so the like can hang off its bottom corner: the wrapper carries the width limit,
   the bubble fills it. Away from the reader's side — right for other people, left for your own. */
.chat-list .bubble-wrap { position: relative; max-width: 80%; min-width: 0; }
.chat-list .bubble { background: var(--soft); border-radius: 1rem; padding: 0.5rem 0.85rem; max-width: 100%; white-space: pre-wrap; min-width: 0; }
.chat-list li.chat-msg.mine .bubble { background: var(--accent-soft); }
/* The chip hangs off the bubble's outer corner, 20px further out on each side, and drops the outline the base
   .like carries — scoped here, since that rule also styles likes on posts, events, products and platforms.
   Offsets shrink to move the chip outward: 0.7rem is 11.2px, so this lands at -8.8px, overhanging the edge.
   Safe from clipping because nothing in the ancestor chain sets overflow. Note border: none makes
   `.like.on { border-color }` inert here, so a liked chip reads from the heart's colour alone. */
.chat-list .bubble-wrap > .like { position: absolute; bottom: -0.65rem; right: calc(0.7rem - 20px); border: none; padding: 1px; font-size: 0.75rem; background: var(--surface); box-shadow: 0 1px 3px rgb(0 0 0 / 0.35); z-index: 1; }
.chat-list li.chat-msg.mine .bubble-wrap > .like { right: auto; left: calc(0.7rem - 20px); }
/* Only a message that offers a like reserves room for the chip; replies have no like path, so their
   runs stay tight. */
.chat-list li.chat-msg:has(.bubble-wrap > .like) { padding-bottom: 0.8rem; }
.chat-list .bubble .gallery img { max-width: 100%; border-radius: 0.6rem; }
.chat-list .chat-meta { gap: 0.5rem; font-size: 0.8rem; align-items: center; }
/* The meta row is quiet until you want it, so the stream reads as messages rather than a toolbar per line. */
.chat-list .chat-meta .link { opacity: 0.75; }
@media (hover: hover) { .chat-list li.chat-msg:hover .chat-meta .link { opacity: 1; } }
@media (max-width: 40rem) {
    .chat-list .bubble-wrap { max-width: 88%; }
}

/* Admin → Billing (estimate) */
.billing .billing-summary { display: flex; flex-wrap: wrap; gap: 1rem 2rem; margin: 1rem 0; }
.billing .billing-summary > div { display: grid; gap: 0.15rem; }
.billing .billing-summary b { font-size: 1.15rem; }
.billing .billing-summary .billing-total { font-size: 1.6rem; color: var(--accent-strong); }
.billing-table { width: 100%; border-collapse: collapse; font-size: 0.9rem; margin-bottom: 0.75rem; }
.billing-table th, .billing-table td { text-align: left; padding: 0.35rem 0.5rem; border-bottom: 1px solid var(--line); }
.billing-table th { font-weight: 600; color: var(--ink-dim); }
.billing-table .num { text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; }
.billing .billing-report, .billing .billing-days { overflow-x: auto; }

/* Products → Import from Shopify */
.shopify-import { flex-basis: 100%; }
.shopify-import > summary { display: inline-block; list-style: none; cursor: pointer; }
.shopify-import > summary::-webkit-details-marker { display: none; }
.shopify-import[open] { padding: 0.6rem 0; }
.shopify-import form input[name="shop"] { min-width: min(100%, 18rem); }
