/*
 * css/clanstop-app.css
 *
 * Companion sheet to css/clanstop.css (the designer's theme file).
 *
 * clanstop.css is copied verbatim from NEW_clan_stop_/clanstop.css and must
 * stay that way so it can be re-synced whenever the design source updates —
 * DO NOT hand-edit it. Anything the static mock has no concept of lives here
 * instead: signed-in chrome (notification bell, user menu), the language
 * switcher, the site announcement banner, and small helpers the PHP pages
 * need. Everything below is written in the theme's own vocabulary
 * (--panel/--line/--red/--blue/--brass, chamfer clip-paths, Martian Mono
 * micro-labels) so it reads as one system.
 *
 * Load order: fonts/clanstop.css → clanstop.css → clanstop-app.css
 */

/* ============ LOGO MARK FIX ============ */
/* clanstop.css colours the house mark with descendant selectors:
     .logo__svg .lred  { fill: var(--red) }
     .logo__svg .lblue { fill: var(--blue) }
   Those never match. The mark is drawn with <use href="#cs-mark">, and
   the cloned content lives in a shadow tree that outside descendant
   selectors can't reach — so the polygons fall back to fill:black and the
   logo renders as a dark smudge. The gold variant works precisely because
   its rules (.lgold / .lgoldhot) are flat: they match the polygons inside
   the <symbol> itself, and <use> inherits the resolved presentation.
   These are the same rules, flattened. Delete them if the theme file is
   ever re-synced with the descendant selectors removed. */
.lred {
     fill: var(--red);
}

.lblue {
     fill: var(--blue);
}

.hot {
     fill: #fff;
     filter: drop-shadow(0 0 4px rgba(255, 255, 255, .8));
}

/* One row of pennants stretches edge to edge when there are only one or
   two clans to show, because the theme's grid uses auto-fit + 1fr and the
   mock always had six. Capping the card keeps a short row looking
   deliberate; at six across the tracks are narrower than the cap, so the
   full-width case is untouched. */
.pennants {
     justify-content: center;
}

.pennant {
     max-width: 260px;
     width: 100%;
     margin-inline: auto;
}

/* ============ NAV UTILITIES (signed-in / locale chrome) ============ */
/* Sits between .nav__links and .nav__cta. On desktop it's pushed right by
   .nav__cta's margin-left:auto; when there's no CTA cluster (signed-in) the
   utilities take over that job. */
.nav__util {
     display: flex;
     align-items: center;
     gap: .55rem;
     margin-left: auto;
}

.nav__util + .nav__cta {
     margin-left: 0;
}

/* Between the nav's 1024px collapse and roughly 1400px the bar is tight:
   logo + seven links + locale select + two CTAs. Without this, "Sign in"
   breaks across two lines inside its own chamfered button. */
.nav__cta .btn {
     white-space: nowrap;
}

/* --- generic square-ish icon button, chamfered like .btn --- */
.navbtn {
     position: relative;
     display: inline-flex;
     align-items: center;
     justify-content: center;
     background: var(--panel);
     box-shadow: inset 0 0 0 1px var(--line);
     clip-path: var(--chamfer);
     color: var(--muted);
     border: none;
     padding: .5rem .6rem;
     line-height: 0;
     transition: color .15s, box-shadow .2s;
}

.navbtn:hover,
.navbtn[aria-expanded="true"] {
     color: #fff;
     box-shadow: inset 0 0 0 1px var(--blue);
}

.navbtn svg {
     width: 18px;
     height: 18px;
     fill: currentColor;
}

/* Unread pip — brass so it reads as "yours", not as an error. */
.navbtn__count {
     position: absolute;
     top: -5px;
     right: -5px;
     min-width: 17px;
     height: 17px;
     padding: 0 4px;
     background: var(--red);
     color: #fff;
     font-family: var(--mono);
     font-size: .55rem;
     font-weight: 700;
     line-height: 17px;
     text-align: center;
     border-radius: 999px;
     box-shadow: 0 0 0 2px var(--ink);
}

.navbtn__count[hidden] {
     display: none;
}

/* --- shared dropdown panel --- */
.navdrop {
     position: absolute;
     top: calc(100% + .75rem);
     right: 0;
     z-index: 60;
     width: min(340px, calc(100vw - 2rem));
     background: var(--panel-2);
     box-shadow: inset 0 0 0 1px var(--line), 0 24px 48px rgba(0, 0, 0, .55);
     clip-path: polygon(14px 0, 100% 0, 100% calc(100% - 14px), calc(100% - 14px) 100%, 0 100%, 0 14px);
     opacity: 0;
     visibility: hidden;
     transform: translateY(-6px);
     transition: opacity .18s, transform .18s, visibility 0s .18s;
}

.navdrop--open {
     opacity: 1;
     visibility: visible;
     transform: translateY(0);
     transition: opacity .18s, transform .18s;
}

.navdrop__wrap {
     position: relative;
}

.navdrop__head {
     display: flex;
     align-items: center;
     justify-content: space-between;
     gap: 1rem;
     padding: .85rem 1rem .7rem;
     border-bottom: 1px solid var(--line);
}

.navdrop__title {
     font-family: var(--mono);
     font-size: .6rem;
     letter-spacing: .18em;
     text-transform: uppercase;
     color: var(--brass);
}

.navdrop__action {
     background: none;
     border: none;
     color: var(--muted);
     font-family: var(--mono);
     font-size: .58rem;
     letter-spacing: .1em;
     text-transform: uppercase;
     transition: color .15s;
}

.navdrop__action:hover {
     color: var(--blue);
}

.navdrop__tabs {
     display: flex;
     border-bottom: 1px solid var(--line);
}

.navdrop__tabs button {
     flex: 1;
     background: none;
     border: none;
     color: var(--muted);
     font-family: var(--body);
     font-weight: 600;
     font-size: .78rem;
     padding: .55rem 0 .6rem;
     position: relative;
     transition: color .15s;
}

.navdrop__tabs button:hover {
     color: var(--text);
}

.navdrop__tabs button[aria-selected="true"] {
     color: var(--text);
}

.navdrop__tabs button[aria-selected="true"]::after {
     content: "";
     position: absolute;
     left: 22%;
     right: 22%;
     bottom: -1px;
     height: 3px;
     background: var(--blue);
     border-radius: 3px 3px 0 0;
}

.navdrop__body {
     max-height: min(60vh, 420px);
     overflow-y: auto;
}

.navdrop__empty {
     padding: 1.6rem 1rem;
     text-align: center;
     font-size: .82rem;
     color: var(--muted);
}

/* --- notification rows --- */
.notif {
     display: flex;
     gap: .7rem;
     align-items: flex-start;
     padding: .75rem 1rem;
     border-bottom: 1px solid var(--line);
     transition: background .15s;
}

.notif:last-child {
     border-bottom: none;
}

.notif:hover {
     background: rgba(255, 255, 255, .03);
}

.notif--unread {
     background: rgba(0, 166, 255, .05);
}

.notif__icon {
     width: 34px;
     height: 34px;
     flex: none;
     display: grid;
     place-items: center;
     background: linear-gradient(135deg, var(--red), var(--red-deep));
     clip-path: polygon(7px 0, 100% 0, 100% calc(100% - 7px), calc(100% - 7px) 100%, 0 100%, 0 7px);
}

.notif__icon svg {
     width: 18px;
     height: 18px;
     fill: #fff;
}

.notif__body {
     min-width: 0;
}

.notif__title {
     font-family: var(--display);
     font-weight: 800;
     font-style: italic;
     text-transform: uppercase;
     font-size: .78rem;
     letter-spacing: .01em;
     line-height: 1.25;
}

.notif__text {
     font-size: .82rem;
     color: #c6cede;
     margin-top: .15rem;
     overflow-wrap: anywhere;
}

.notif__time {
     display: block;
     font-family: var(--mono);
     font-size: .56rem;
     letter-spacing: .1em;
     color: var(--muted);
     margin-top: .35rem;
}

/* --- user menu --- */
.usermenu__toggle {
     display: inline-flex;
     align-items: center;
     gap: .5rem;
     background: var(--panel);
     box-shadow: inset 0 0 0 1px var(--line);
     clip-path: var(--chamfer);
     border: none;
     color: var(--text);
     padding: .42rem .8rem .42rem .45rem;
     font-family: var(--display);
     font-weight: 800;
     font-style: italic;
     font-size: .78rem;
     text-transform: uppercase;
     letter-spacing: .02em;
     max-width: 190px;
     transition: box-shadow .2s;
}

.usermenu__toggle:hover,
.usermenu__toggle[aria-expanded="true"] {
     box-shadow: inset 0 0 0 1px var(--blue);
}

.usermenu__name {
     overflow: hidden;
     text-overflow: ellipsis;
     white-space: nowrap;
}

.usermenu__chev {
     font-size: .6rem;
     color: var(--muted);
     font-style: normal;
}

.navdrop--menu {
     width: min(250px, calc(100vw - 2rem));
}

.navdrop__list {
     list-style: none;
     padding: .4rem 0;
}

.navdrop__list a {
     display: block;
     padding: .55rem 1rem;
     font-size: .86rem;
     color: var(--text);
     transition: background .15s, padding-left .15s, color .15s;
}

.navdrop__list a:hover {
     background: rgba(255, 255, 255, .04);
     padding-left: 1.25rem;
     color: #fff;
}

.navdrop__foot {
     border-top: 1px solid var(--line);
     padding: .6rem 1rem .7rem;
     display: flex;
     flex-wrap: wrap;
     gap: .35rem .9rem;
     font-family: var(--mono);
     font-size: .56rem;
     letter-spacing: .1em;
     text-transform: uppercase;
     color: var(--muted);
}

.navdrop__foot a:hover {
     color: var(--red-text);
}

/* --- language switcher --- */
.langswitch {
     position: relative;
     display: inline-flex;
     align-items: center;
}

.langswitch select {
     appearance: none;
     -webkit-appearance: none;
     background: var(--panel);
     box-shadow: inset 0 0 0 1px var(--line);
     clip-path: var(--chamfer);
     border: none;
     color: var(--muted);
     font-family: var(--mono);
     font-size: .62rem;
     letter-spacing: .08em;
     text-transform: uppercase;
     padding: .5rem 1.7rem .5rem .8rem;
     cursor: pointer;
     transition: color .15s, box-shadow .2s;
}

.langswitch select:hover {
     color: #fff;
     box-shadow: inset 0 0 0 1px var(--blue);
}

.langswitch select option {
     background: var(--panel-2);
     color: var(--text);
     font-family: var(--body);
     text-transform: none;
     letter-spacing: 0;
}

.langswitch::after {
     content: "▾";
     position: absolute;
     right: .65rem;
     top: 50%;
     transform: translateY(-50%);
     pointer-events: none;
     color: var(--muted);
     font-size: .6rem;
}

/* Below the nav's own 1024px collapse the utilities move into the drawer. */
@media (max-width: 1024px) {
     .nav__util {
          display: none;
     }
}

/* ============ DRAWER ADDITIONS ============ */
.drawer__user {
     display: flex;
     align-items: center;
     gap: .7rem;
     padding: .8rem;
     background: var(--panel);
     clip-path: var(--chamfer);
     margin-bottom: .4rem;
}

.drawer__user .avatar--round {
     width: 38px;
     height: 38px;
}

.drawer__user-id {
     min-width: 0;
}

.drawer__user-id b {
     display: block;
     font-family: var(--display);
     font-weight: 800;
     font-style: italic;
     text-transform: uppercase;
     font-size: .95rem;
     line-height: 1.15;
     overflow: hidden;
     text-overflow: ellipsis;
     white-space: nowrap;
}

.drawer__user-id span {
     font-family: var(--mono);
     font-size: .55rem;
     letter-spacing: .12em;
     text-transform: uppercase;
     color: var(--muted);
}

/* Secondary drawer links (account/logout) — quieter than the big nav list. */
.drawer__sub {
     list-style: none;
     display: flex;
     flex-direction: column;
}

.drawer__sub a {
     display: block;
     padding: .5rem .2rem;
     font-size: .88rem;
     color: var(--muted);
     border-bottom: 1px solid var(--line);
     transition: color .15s, padding-left .15s;
}

.drawer__sub a:hover {
     color: #fff;
     padding-left: .5rem;
}

.drawer__sub .is-danger:hover {
     color: var(--red-text);
}

.drawer .langswitch {
     display: block;
     margin-top: .3rem;
}

.drawer .langswitch select {
     width: 100%;
     font-size: .68rem;
     padding: .6rem 1.7rem .6rem .8rem;
}

/* The drawer scrolls on short viewports — the theme's flex column would
   otherwise clip the CTA block once the signed-in extras are added. */
.drawer {
     overflow-y: auto;
}

/* ============ SITE ANNOUNCEMENT BANNER ============ */
/* Emitted by core/SiteAnnouncements.php at the top of <main>. Its markup
   is shared with the legacy theme, so match its class names rather than
   asking that file to know which theme is rendering it. */
#main-content .site-announcement {
     position: relative;
     max-width: 1200px;
     margin: 1.5rem auto 0;
     padding: .85rem 2.6rem .85rem 1.1rem;
     background: var(--panel);
     box-shadow: inset 0 0 0 1px var(--line);
     clip-path: var(--chamfer);
     font-size: .88rem;
     color: #c6cede;
}

#main-content .site-announcement-title {
     display: block;
     font-family: var(--display);
     font-weight: 800;
     font-style: italic;
     text-transform: uppercase;
     font-size: .85rem;
     color: var(--text);
     margin-bottom: .2rem;
}

/* Severity accent — one border colour, same three tones the theme uses
   everywhere else for info / warning / trouble. */
#main-content .site-announcement.severity-info {
     box-shadow: inset 0 0 0 1px rgba(0, 166, 255, .45);
}

#main-content .site-announcement.severity-warning {
     box-shadow: inset 0 0 0 1px rgba(255, 184, 28, .45);
}

#main-content .site-announcement.severity-critical {
     box-shadow: inset 0 0 0 1px rgba(232, 0, 45, .55);
}

#main-content .site-announcement-close {
     position: absolute;
     top: .5rem;
     right: .7rem;
     background: none;
     border: none;
     color: var(--muted);
     font-size: 1.1rem;
     line-height: 1;
     padding: .2rem .4rem;
     transition: color .15s;
}

#main-content .site-announcement-close:hover {
     color: #fff;
}

/* ============ FEED PANEL — real posts in the mock's frame ============ */
/* The mock's feed is a drawing: its composer is a <div>, its post actions
   are <button>s that do nothing. Ours are links to the places that
   actually do the work (your wall, the post permalink), so the same
   styling has to reach <a> too. */
.composer__input,
.composer .btn--post {
     display: inline-block;
     text-align: center;
     white-space: nowrap;
}

.composer__input {
     text-align: left;
     transition: border-color .15s, color .15s;
}

.composer__input:hover {
     border-color: var(--blue);
     color: var(--text);
}

/* Direct children only: discover's foot is three bare <a>s, but the wall's
   foot also contains a share dropdown whose <ul> is full of <a>s. A
   descendant selector here would dress those menu items as pill buttons. */
.post__foot > a {
     background: none;
     border: none;
     color: var(--muted);
     display: inline-flex;
     align-items: center;
     gap: .35rem;
     font-size: .75rem;
     padding: .2rem .3rem;
     border-radius: 999px;
     transition: color .15s;
}

.post__foot > a:nth-child(1):hover {
     color: var(--blue);
}

.post__foot > a:nth-child(2):hover {
     color: var(--red);
}

.post__foot > a:nth-child(3):hover {
     color: var(--text);
}

/* Inline markup produced by backend/find_wall_posts.php's
   format_wall_post_text(): hashtags, mentions and bare URLs. The theme
   styles its own .tag; these are the same idea under the class names the
   formatter emits, so post bodies render identically wherever they appear. */
.post .hashtag-link,
.post .mention-link,
.post .post-link {
     color: var(--blue);
     font-weight: 600;
}

.post .hashtag-link:hover,
.post .mention-link:hover,
.post .post-link:hover {
     text-decoration: underline;
}

.post .mention-unresolved,
.post .mention-missing {
     color: var(--muted);
     font-weight: 600;
}

/* Fixture rows link their clan names; the theme assumed plain text. */
.fixture__team .nm a:hover,
.matchup__side h3 a:hover,
.champs__name a:hover {
     color: var(--red-text);
}

/* ============ AUTH PAGES — bits the mock had no need for ============ */
/* Honeypot. Off-screen rather than display:none, because some bots skip
   fields they can tell are hidden. aria-hidden + tabindex="-1" in the
   markup keep it away from real users. */
.hp-sec {
     opacity: 0;
     position: absolute;
     top: 0;
     left: 0;
     height: 0;
     width: 0;
     z-index: -1;
}

/* Status banner inside the auth card. The old theme inline-styled these
   with light Bootstrap-ish colours (#cce5ff on #004085) — unreadable on a
   near-black card. Same three states, tinted for this palette. */
.auth-msg {
     font-size: .84rem;
     line-height: 1.5;
     padding: .65rem .85rem;
     margin-bottom: 1.1rem;
     background: var(--panel-2);
     box-shadow: inset 0 0 0 1px var(--line);
     clip-path: polygon(9px 0, 100% 0, 100% calc(100% - 9px), calc(100% - 9px) 100%, 0 100%, 0 9px);
     color: #c6cede;
}

.auth-msg[hidden] {
     display: none;
}

.auth-msg--success {
     color: #a7e9c0;
     box-shadow: inset 0 0 0 1px rgba(89, 214, 131, .45);
}

.auth-msg--error {
     color: #ffb3bd;
     box-shadow: inset 0 0 0 1px rgba(232, 0, 45, .5);
}

.auth-msg--info {
     color: #b9dcff;
     box-shadow: inset 0 0 0 1px rgba(0, 166, 255, .45);
}

/* The theme styles .field__link as an <a>. Here it's a <button>, because
   "Forgot?" switches sign-in method rather than navigating — this site has
   no password-reset route, the email code is the recovery path. */
button.field__link {
     background: none;
     border: none;
     padding: 0;
     cursor: pointer;
}

/* ============ PROFILE PAGE — additions ============ */
/* The theme covers .pf-hero / .pf-stats / .pf-tabs / .rail-card / .panelbox.
   What's below is the parts a static mock had no reason to have: a real
   avatar image, analytics, notices, and the per-user theme hooks. */

/* Real avatars sit on top of the initials fallback; if the image 404s its
   onerror hides it and the initials show through. */
.pf-avatar {
     position: relative;
     overflow: hidden;
}

.pf-avatar img {
     position: absolute;
     inset: 0;
     width: 100%;
     height: 100%;
     object-fit: cover;
}

.pf-avatar__initials {
     font-family: var(--display);
     font-weight: 900;
     font-style: italic;
     font-size: 2.2rem;
}

/* The live-stream badges JS drops next to the display name. */
.pf-id h1 #profile_live_badge {
     display: inline-flex;
     gap: .4rem;
     align-items: center;
}

.live-badge {
     display: inline-flex;
     align-items: center;
     gap: .35rem;
     font-family: var(--mono);
     font-size: .55rem;
     letter-spacing: .12em;
     text-transform: uppercase;
     color: #fff;
     background: linear-gradient(160deg, var(--red), var(--red-deep));
     padding: .28rem .6rem;
     clip-path: polygon(6px 0, 100% 0, calc(100% - 6px) 100%, 0 100%);
     font-style: normal;
}

.live-badges-row {
     display: inline-flex;
     gap: .4rem;
     flex-wrap: wrap;
     vertical-align: middle;
}

.pf-bio {
     color: var(--muted);
     font-size: .9rem;
     line-height: 1.55;
     max-width: 60ch;
     margin-top: .8rem;
}

/* Stat strip reused inside panels (combat record, tournaments tab), where
   it shouldn't carry the hero's page-width margins. */
.pf-stats--inline {
     margin: 0 0 1.1rem;
     padding: 0;
}

.pf-stat__sep {
     color: var(--muted);
     margin: 0 .15rem;
}

/* Notices between hero and body: status messages, block warning, the
   "in game right now" strip. */
.pf-notices {
     max-width: 1200px;
     margin: 1.4rem auto 0;
     padding: 0 1.25rem;
}

.pf-notices:empty {
     display: none;
}

.pf-notices .auth-msg {
     margin-bottom: .8rem;
}

/* Rail analytics tiles. */
.pf-analytics {
     font-size: .84rem;
     color: var(--muted);
}

.pf-analytics__grid {
     display: grid;
     grid-template-columns: 1fr 1fr;
     gap: .5rem;
     margin-bottom: .7rem;
}

.pf-analytics__tile {
     background: var(--panel-2);
     box-shadow: inset 0 0 0 1px var(--line);
     clip-path: polygon(7px 0, 100% 0, 100% calc(100% - 7px), calc(100% - 7px) 100%, 0 100%, 0 7px);
     padding: .55rem .4rem;
     text-align: center;
}

.pf-analytics__tile b {
     display: block;
     font-family: var(--display);
     font-weight: 900;
     font-style: italic;
     font-size: 1.15rem;
     color: var(--text);
     line-height: 1;
}

.pf-analytics__tile span {
     font-family: var(--mono);
     font-size: .5rem;
     letter-spacing: .1em;
     text-transform: uppercase;
     color: var(--muted);
}

.pf-analytics__peak {
     font-family: var(--mono);
     font-size: .56rem;
     letter-spacing: .06em;
     color: var(--muted);
}

/* Rail cards whose contents arrive over AJAX would otherwise render as an
   empty bordered box on first paint. */
.rail-card:empty {
     display: none;
}

.rail-card h2 a:hover {
     color: var(--blue);
}

.rail-clan .avatar--round img,
.friend .avatar--round img,
.member .avatar--round img {
     width: 100%;
     height: 100%;
     object-fit: cover;
     border-radius: inherit;
}

/* Request menu in the hero actions. Reuses the legacy .dropdown-link
   /.dropdown-menu hooks the page's jQuery already toggles with .active. */
.pf-request {
     position: relative;
}

.pf-request__menu {
     position: absolute;
     top: calc(100% + .5rem);
     right: 0;
     z-index: 40;
     min-width: 220px;
     list-style: none;
     background: var(--panel-2);
     box-shadow: inset 0 0 0 1px var(--line), 0 20px 40px rgba(0, 0, 0, .55);
     clip-path: polygon(12px 0, 100% 0, 100% calc(100% - 12px), calc(100% - 12px) 100%, 0 100%, 0 12px);
     padding: .35rem 0;
     opacity: 0;
     visibility: hidden;
     transform: translateY(-6px);
     transition: opacity .16s, transform .16s, visibility 0s .16s;
}

.pf-request.active .pf-request__menu {
     opacity: 1;
     visibility: visible;
     transform: translateY(0);
     transition: opacity .16s, transform .16s;
}

.pf-request__menu a {
     display: block;
     padding: .5rem 1rem;
     font-size: .85rem;
     color: var(--text);
     transition: background .15s, padding-left .15s;
}

.pf-request__menu a:hover {
     background: rgba(255, 255, 255, .05);
     padding-left: 1.25rem;
}

.pf-notfound {
     max-width: 620px;
     margin: 3rem auto;
     padding: 0 1.25rem;
     text-align: center;
}

.pf-notfound h2 {
     font-family: var(--display);
     font-weight: 900;
     font-style: italic;
     text-transform: uppercase;
     font-size: 1.4rem;
}

.pf-notfound p {
     color: var(--muted);
     margin: .6rem 0 1.3rem;
}

.pf-notfound__actions {
     display: flex;
     gap: .7rem;
     justify-content: center;
     flex-wrap: wrap;
}

.panelbox__count {
     font-family: var(--mono);
     font-size: .58rem;
     letter-spacing: .1em;
     color: var(--muted);
}

/* Battle rows show a scoreline in the middle of the name cell. */
.trow__score {
     font-family: var(--mono);
     font-weight: 700;
     color: var(--brass);
     margin: 0 .5rem;
     font-style: normal;
}

/* Roster rows (friends / clans tabs). */
.roster {
     display: flex;
     flex-direction: column;
}

.member {
     display: flex;
     align-items: center;
     gap: .75rem;
     padding: .6rem .4rem;
     border-bottom: 1px solid var(--line);
     transition: background .15s, transform .12s;
}

.member:last-child {
     border-bottom: none;
}

.member:hover {
     background: rgba(0, 166, 255, .05);
     transform: translateX(3px);
}

.member .avatar--round {
     width: 38px;
     height: 38px;
     font-size: .62rem;
}

.member__id {
     min-width: 0;
     flex: 1;
}

.member__id b {
     display: block;
     font-family: var(--display);
     font-weight: 800;
     font-style: italic;
     text-transform: uppercase;
     font-size: .9rem;
     line-height: 1.2;
     overflow: hidden;
     text-overflow: ellipsis;
     white-space: nowrap;
}

.member__id span {
     font-family: var(--mono);
     font-size: .56rem;
     letter-spacing: .08em;
     color: var(--muted);
}

/* ============ THE WALL — skinning a shared component ============ */
/* pages/profile/P_post.php and backend/find_wall_posts.php render the wall
   for the profile page AND the clan page. The clan page is still on
   style.css, and this file only loads on converted pages — so the rules
   below re-dress the same markup for the new theme without the clan page
   seeing any of it. When the clan page is converted, this section starts
   serving both and nothing here needs to change.
   Specificity note: P_post.php ships inline <style> blocks in the body,
   which land after this file. Everything below is scoped under .pf-main to
   win on specificity rather than with !important. */
.pf-main .card,
.pf-main .post-card {
     background: var(--panel);
     box-shadow: inset 0 0 0 1px var(--line);
     clip-path: polygon(14px 0, 100% 0, 100% calc(100% - 14px), calc(100% - 14px) 100%, 0 100%, 0 14px);
     border: none;
     border-radius: 0;
     padding: 1.1rem 1.2rem;
     margin-bottom: 1.1rem;
}

.pf-main .post-header {
     display: flex;
     align-items: center;
     gap: .75rem;
     margin-bottom: .7rem;
}

.pf-main .post-avatar {
     width: 42px;
     height: 42px;
     border-radius: 50%;
     object-fit: cover;
     flex: none;
}

.pf-main .post-author h5,
.pf-main .post-author b {
     font-family: var(--display);
     font-weight: 800;
     font-style: italic;
     text-transform: uppercase;
     font-size: .92rem;
     line-height: 1.2;
     color: var(--text);
}

.pf-main .post-meta-area {
     min-width: 0;
     flex: 1;
}

.pf-main .post-body,
.pf-main .actual-content {
     font-size: .92rem;
     line-height: 1.6;
     color: #d3dae6;
}

.pf-main .post-body a,
.pf-main .actual-content a {
     color: var(--blue);
     font-weight: 600;
}

.pf-main .pinned-badge {
     font-family: var(--mono);
     font-size: .52rem;
     letter-spacing: .1em;
     text-transform: uppercase;
     color: var(--brass);
     box-shadow: inset 0 0 0 1px rgba(255, 184, 28, .4);
     padding: .2rem .5rem;
     clip-path: polygon(5px 0, 100% 0, calc(100% - 5px) 100%, 0 100%);
}

.pf-main .post-actions {
     display: flex;
     align-items: center;
     gap: .4rem;
     flex-wrap: wrap;
     border-top: 1px solid var(--line);
     margin-top: .8rem;
     padding-top: .7rem;
}

.pf-main .action-button {
     background: none;
     border: none;
     color: var(--muted);
     font-family: var(--body);
     font-size: .8rem;
     padding: .35rem .7rem;
     border-radius: 999px;
     cursor: pointer;
     transition: color .15s, background .15s;
}

.pf-main .action-button:hover {
     color: var(--text);
     background: rgba(255, 255, 255, .04);
}

.pf-main .action-button.reacted {
     color: var(--blue);
}

/* The themed foot carries the mock's own classes and data-act attributes,
   so clanstop.css styles it directly — including the per-action hover
   colours and .post__views' margin-left:auto. Nothing to add here; the
   rules below only stop this file's generic .post-actions/.action-button
   styling (which the clan-style foot and the modal composers still need)
   from overriding it on specificity. */
.pf-main .post-actions.post__foot {
     border-top: none;
     margin-top: .55rem;
     padding-top: 0;
     gap: .1rem;
}

.post__foot .action-button {
     display: inline-flex;
     align-items: center;
     gap: .35rem;
     font-size: .75rem;
     padding: .25rem .5rem;
     border-radius: 999px;
     background: none;
     border: none;
     color: var(--muted);
     cursor: pointer;
     transition: color .15s, background .15s;
}

.post__foot .action-button:hover {
     background: rgba(255, 255, 255, .05);
}

/* Counts read brighter than the icon beside them, like the mock's views. */
.post__foot .action-button b {
     color: var(--text);
     font-weight: 600;
}

/* The reaction button sits in a wrapper (it owns the emoji picker), so the
   wrapper has to behave like the other inline items in the strip and
   position the picker. */
.post__foot .reaction-container {
     display: inline-flex;
     align-items: center;
     position: relative;
}

/* Keep the byline clear of the ⋯ pinned in the corner above it. */
.post-card .post__meta {
     padding-right: 2.2rem;
}

/* Post media takes the mock's chamfer instead of its 8px radius. Only the
   corner treatment — the column/row layout comes from the .layout-* classes
   buildImagesHtml() assigns per image count, and overriding the grid here
   would flatten every one of them to the same shape. */
.post .post-images-grid {
     clip-path: polygon(8px 0, 100% 0, 100% calc(100% - 8px), calc(100% - 8px) 100%, 0 100%, 0 8px);
     border-radius: 0;
     margin-top: .6rem;
}

/* Quoted posts and link previews are the body's other block-level pieces;
   the mock gives its .embed-match the same inner-surface treatment. */
.post .quotebox,
.post .link-preview-card {
     background: var(--panel);
     box-shadow: inset 0 0 0 1px var(--line);
     clip-path: polygon(10px 0, 100% 0, 100% calc(100% - 10px), calc(100% - 10px) 100%, 0 100%, 0 10px);
     border: none;
     border-radius: 0;
     margin-top: .6rem;
}

.pf-main .submit-button {
     background: linear-gradient(160deg, var(--red), var(--red-deep));
     color: #fff;
     border: none;
     clip-path: var(--chamfer);
     font-family: var(--display);
     font-weight: 800;
     font-style: italic;
     font-size: .88rem;
     text-transform: uppercase;
     letter-spacing: .03em;
     padding: .65rem 1.4rem;
     cursor: pointer;
     transition: filter .15s;
}

.pf-main .submit-button:hover {
     filter: brightness(1.15);
}

.pf-main .post-options-menu,
.pf-main .dropdown-menu {
     background: var(--panel-2);
     box-shadow: inset 0 0 0 1px var(--line), 0 16px 32px rgba(0, 0, 0, .5);
     border: none;
     border-radius: 0;
     clip-path: polygon(10px 0, 100% 0, 100% calc(100% - 10px), calc(100% - 10px) 100%, 0 100%, 0 10px);
}

.pf-main .comment-section {
     border-top: 1px solid var(--line);
     margin-top: .8rem;
     padding-top: .8rem;
}

.pf-main input[type="text"],
.pf-main input[type="search"],
.pf-main textarea {
     background: var(--panel-2);
     color: var(--text);
     border: none;
     box-shadow: inset 0 0 0 1px var(--line);
     border-radius: 0;
     clip-path: polygon(9px 0, 100% 0, 100% calc(100% - 9px), calc(100% - 9px) 100%, 0 100%, 0 9px);
}

.pf-main input[type="text"]:focus,
.pf-main textarea:focus {
     outline: none;
     box-shadow: inset 0 0 0 1px var(--blue);
}

.pf-main .post-filter-btn {
     font-family: var(--mono);
     font-size: .58rem;
     letter-spacing: .1em;
     text-transform: uppercase;
}

/* --- the wall panel: head, filters, composer ---------------------------
   P_post.php now renders the wall the way the mock does — one .panelbox
   (.wall-panel) holding the head, the composer and the feed — so the rules
   below only have to cover the pieces the mock fakes with static markup:
   a real <input> in the filter row, an <img> where the mock has a text
   avatar, and post cards that come from find_wall_posts.php rather than
   being hand-written .post rows. */
.pf-main .wall-panel .panelbox__head {
     flex-wrap: wrap;
}

/* Chips only, like the mock. */
.pf-main .wall-filters {
     justify-content: flex-end;
     align-items: center;
}

.pf-main .wall-search-status {
     font-family: var(--mono);
     font-size: .58rem;
     letter-spacing: .1em;
     text-transform: uppercase;
     color: var(--muted);
     margin: -.4rem 0 .8rem;
}

/* Composer avatar is a real uploaded image, not the mock's initials tile —
   .avatar--round is a grid box with no sizing for a replaced element. */
.pf-main .wall-composer .avatar--round {
     width: 38px;
     height: 38px;
     object-fit: cover;
}

/* The composer trigger is a <button> standing in for a text field, so it
   has to shed the button defaults .wall-composer__input doesn't cover. */
.pf-main .wall-composer__input {
     font-family: var(--body);
     text-align: left;
     border: none;
     cursor: text;
     min-width: 0;
     overflow: hidden;
     text-overflow: ellipsis;
     white-space: nowrap;
     transition: color .15s, box-shadow .15s;
}

.pf-main .wall-composer__input:hover {
     color: var(--text);
     box-shadow: inset 0 0 0 1px var(--blue);
}

/* Posts inside the panel read as inner surfaces, the way .sys-post does —
   otherwise a var(--panel) card on a var(--panel) panel has no edge at all.
   A user profile theme overrides these backgrounds from its inline <style>
   (it loads after this file), which is the intended behaviour: the wall is
   where profile themes are supposed to show.
   border-bottom: the card carries .post for the mock's row layout, but .post
   is written for flat rows in a list and brings a hairline the framed card
   doesn't want (and that the chamfer would clip anyway). */
.pf-main .wall-panel .post-card {
     background: var(--panel-2);
     margin-bottom: .9rem;
     padding: 1rem 1.1rem;
     border-bottom: none;
}

.pf-main .wall-panel .post-card:last-of-type {
     margin-bottom: 0;
}

/* --- letting the ⋯ and share menus out of the card ---------------------
   A clip-path on an ancestor clips EVERY descendant. No z-index, position
   or overflow on the menu can escape it. There are TWO chamfered ancestors
   between a dropdown and the page, and both have to let go while a menu is
   open or it gets sliced:

     .panelbox  (.wall-panel)   the panel's 14px corner
     .post-card                 the card's own 14px corner  <- this one is
                                what cut the menu off inside the post body

   Only the panel was handled before, which is why the menus still came out
   trimmed at the card edge.

   Driven by classes the JS already sets rather than by :has():
   attachDropdownListeners() in P_post.php marks the card
   (.has-open-share / .has-open-post-options, also used for z-index in
   find_wall_posts.php) and the panel (.has-open-menu). A half-visible menu
   is a hard break, and :has() is still missing on older Safari and Firefox,
   so this path works everywhere. The :has() rules are kept underneath as a
   belt-and-braces for any open menu that isn't inside a .post-card.

   The chamfer comes back on close — a 14px corner is a smaller loss than a
   clipped menu, but only for as long as the menu is up. */
.panelbox.has-open-menu,
.pf-main .card.has-open-share,
.pf-main .card.has-open-post-options,
.pf-main .post-card.has-open-share,
.pf-main .post-card.has-open-post-options,
.pf-main .wall-panel:has(.has-open-share),
.pf-main .wall-panel:has(.has-open-post-options) {
     clip-path: none;
     overflow: visible;
}

/* --- themed post internals (find_wall_posts.php's .post dress) ---------
   Scoped to the card, NOT to .pf-main: openViewPostModal() clones a post
   into the view-post modal, which lives outside the column. This whole file
   only loads on converted pages, so a card-level scope can't reach the clan
   page.
   The mock hand-writes an initials tile and plain text; the real card has a
   linked avatar image and linked names, so these rules cover the wiring the
   mock had no need for. */
.post__avatar {
     flex: none;
     display: block;
}

.post__avatar .avatar--round {
     display: block;
     object-fit: cover;
}

/* Names in the byline are links here, not bare text. */
.post__meta b a,
.post__meta .wallto a {
     color: inherit;
     font-weight: inherit;
}

.post__meta b a:hover {
     color: var(--blue);
}

.post__meta .time time {
     font: inherit;
     color: inherit;
}

/* The mock's ⋯ is a bare button; ours wraps it with its own <ul>, so the
   wrapper takes the corner pinning and the button goes back to static.
   Both selectors are over-qualified on purpose: find_wall_posts.php ships an
   inline <style> in the body — i.e. AFTER this file — carrying
   `.post-card .post-options-container { position: relative }`. A plain
   `.post__owner-wrap` loses that fight and the menu stays in the flow
   instead of pinning to the corner. */
.post-card .post-options-container.post__owner-wrap {
     position: absolute;
     top: .5rem;
     right: .55rem;
     z-index: 3;
}

.post-card .post__owner-wrap .post__owner {
     position: static;
}

/* Post dropdowns (⋯ and Share). style.css used to supply the open/closed
   states for these, and it isn't loaded on converted pages — so without
   this the menus have no positioning and sit open in the flow. The JS
   toggles .active on the container, same as it always has. */
.post-card .post-options-container,
.post-card .share-container {
     position: relative;
}

.post-card .post-options-menu,
.post-card .share-container .dropdown-menu {
     position: absolute;
     right: 0;
     left: auto;
     min-width: 170px;
     list-style: none;
     display: flex;
     flex-direction: column;
     opacity: 0;
     visibility: hidden;
     transform: translateY(-6px);
     transition: opacity .16s, transform .16s, visibility 0s .16s;
}

.post-card .post-options-menu {
     top: calc(100% + .35rem);
}

/* Share opens UPWARD. It lives on the card's last row, and .panelbox is
   clip-path'd — a menu dropping below the last post would be cut off. */
.post-card .share-container .dropdown-menu {
     bottom: calc(100% + .35rem);
     top: auto;
     transform: translateY(6px);
}

.post-card .dropdown-link.active > .post-options-menu,
.post-card .dropdown-link.active > .dropdown-menu {
     opacity: 1;
     visibility: visible;
     transform: translateY(0);
     transition: opacity .16s, transform .16s;
}

/* Both menus carry .postmenu, so the chrome (panel-2 fill, hairline, chamfer,
   drop shadow, padding) comes straight from clanstop.css. The mock's items
   are <button>s and ours are <li><a> — these are the same rules retargeted,
   nothing invented. position: the mock's .postmenu is fixed and placed by
   script; ours is anchored to its container by the rules above, which is why
   position is re-declared there rather than inherited from .postmenu. */
.post-card .postmenu li {
     list-style: none;
     display: block;
     width: 100%;
}

.post-card .postmenu li > a {
     display: flex;
     align-items: center;
     gap: .65rem;
     width: 100%;
     font-family: var(--body);
     font-size: .85rem;
     color: var(--text);
     padding: .55rem .7rem;
     white-space: nowrap;
     clip-path: polygon(6px 0, 100% 0, 100% calc(100% - 6px), calc(100% - 6px) 100%, 0 100%, 0 6px);
     transition: background .12s;
}

/* Fixed-width icon column so the labels line up. */
.post-card .postmenu li > a > span:first-child {
     width: 1.1em;
     text-align: center;
     flex: none;
     font-style: normal;
}

.post-card .postmenu li > a:hover {
     background: rgba(0, 166, 255, .12);
}

.post-card .postmenu .postmenu__sep {
     height: 1px;
     background: var(--line);
     margin: .25rem .35rem;
     padding: 0;
}

.post-card .postmenu li > a.postmenu__danger {
     color: var(--red-text);
}

.post-card .postmenu li > a.postmenu__danger:hover {
     background: rgba(232, 0, 45, .13);
}

/* Already-reported: present but inert, so it reads as state not an action. */
.post-card .postmenu li > a.postmenu__done {
     color: var(--muted);
     cursor: default;
}

.post-card .postmenu li > a.postmenu__done:hover {
     background: none;
}

/* The feed containers are plain wrappers — don't let the generic
   .pf-main .card framing leak onto messages/empty states inside them. */
.pf-main #body_messages_container:empty {
     display: none;
}

.pf-main #wall-posts-loading {
     text-transform: uppercase;
}

/* ============ SHARED MODAL SHELLS (P_post, clan invite) ============ */
/* Distinct from the theme's own #modal shell — these are the older
   .modal-overlay/.modal-box dialogs the wall and the profile's Request menu
   drive with a .visible class. Same visual language, different plumbing.

   :not(#modalOverlay) is load-bearing, not tidiness. index.php's shared
   modal shell is <div class="modal-overlay" id="modalOverlay">, so it
   matched this block too — and inherited `display: none`. It never adds
   .visible (it toggles body.modal-open and clears the hidden attribute
   instead), and clanstop.css's `body.modal-open .modal-overlay` rule only
   restores opacity/pointer-events, not display. Net effect: the theme
   modal's backdrop was never rendered at all. No dim behind the dialog,
   and — since there was no painted element to receive the click —
   clicking outside could not close it. shell.js's
   `modalOverlay.addEventListener('click', closeModal)` had been correct
   the whole time; it just had nothing to fire on. */
.modal-overlay:not(#modalOverlay) {
     position: fixed;
     inset: 0;
     z-index: 200;
     display: none;
     align-items: center;
     justify-content: center;
     padding: 1.25rem;
     background: rgba(0, 0, 0, .68);
     backdrop-filter: blur(3px);
}

/* opacity/pointer-events are declared on .modal-overlay in clanstop.css —
   where the class means the mock's backdrop, not our dialog container — and
   default to 0/none there. Our .visible state has to turn them back on
   itself rather than leaning on the theme's unrelated body.modal-open rule
   to do it. */
.modal-overlay:not(#modalOverlay).visible {
     display: flex;
     opacity: 1;
     pointer-events: auto;
}

.modal-box {
     width: min(560px, 100%);
     max-height: 88vh;
     overflow-y: auto;
     background: var(--panel);
     box-shadow: inset 0 0 0 1px var(--line), 0 30px 60px rgba(0, 0, 0, .6);
     clip-path: polygon(16px 0, 100% 0, 100% calc(100% - 16px), calc(100% - 16px) 100%, 0 100%, 0 16px);
     padding: 1.5rem 1.5rem 1.3rem;
}

/* The mock's signature left edge (.modal::before) — a 3px red / white / blue
   stripe. Painted as a background layer rather than a ::before because
   .modal-box is the scroll container here (the mock scrolls an inner
   .modal__body instead), and an absolutely-positioned pseudo-element would
   scroll away with the content. Default background-attachment keeps a
   background fixed to the border box, so the stripe stays put.
   Excluded from the view-post modal, whose box is deliberately transparent —
   the stripe would hang in mid-air with no panel behind it. */
.modal-overlay:not(.modal-view-post) .modal-box {
     background:
          linear-gradient(180deg, var(--red) 0 34%, #fff 34% 36%, var(--blue) 36% 100%)
               left top / 3px 100% no-repeat,
          var(--panel);
}

.modal-title {
     font-family: var(--display);
     font-weight: 900;
     font-style: italic;
     text-transform: uppercase;
     font-size: 1.15rem;
     margin-bottom: .5rem;
}

.modal-message {
     color: var(--muted);
     font-size: .9rem;
     line-height: 1.55;
}

.modal-actions {
     display: flex;
     gap: .7rem;
     margin-top: 1.2rem;
     flex-wrap: wrap;
}

/* Per-dialog sizing, moved off the shells' style="" attributes. */
.modal-create-post .modal-box,
.modal-edit-post .modal-box,
.modal-delete-post .modal-box {
     max-width: 550px;
}

.modal-report-post .modal-box {
     max-width: 500px;
}

/* Above the compose modal it interrupts. */
.modal-discard-post {
     z-index: 10001;
}

.modal-discard-post .modal-box {
     max-width: 400px;
     text-align: center;
}

.modal-discard-post .modal-message {
     margin-top: .5rem;
}

.modal-actions--end {
     justify-content: flex-end;
}

.modal-actions--end .modal-cancel {
     flex-grow: 0;
}

.modal-content--flush {
     margin-bottom: 0;
}

.modal-button {
     flex: 1;
     min-width: 120px;
     border: none;
     cursor: pointer;
     clip-path: var(--chamfer);
     font-family: var(--display);
     font-weight: 800;
     font-style: italic;
     font-size: .85rem;
     text-transform: uppercase;
     letter-spacing: .03em;
     padding: .65rem 1.2rem;
     transition: filter .15s, box-shadow .2s;
}

.modal-cancel {
     background: var(--panel-2);
     color: var(--text);
     box-shadow: inset 0 0 0 1px var(--line);
}

.modal-cancel:hover {
     box-shadow: inset 0 0 0 1px var(--blue);
}

.modal-yes,
.modal-discard {
     background: linear-gradient(160deg, var(--red), var(--red-deep));
     color: #fff;
}

.modal-yes:hover,
.modal-discard:hover {
     filter: brightness(1.15);
}

/* ── CREATE-POST MODAL (the mock's #modal-compose) ─────────────────────
   The .compose__* chrome comes from clanstop.css. These are the joins the
   mock had no need for: a real uploaded avatar rather than an initials
   tile, a static audience badge (there's no picker behind it), a <label>
   standing in for the mock's tool <button>s so it can drive the hidden file
   input, and the attachment previews the mock never drew. */
.compose .compose__id .avatar--round {
     object-fit: cover;
}

/* Static because we have one audience — everything on a wall is public.
   Drop the affordances .compose__aud carries as a button. */
.compose__aud--static {
     display: inline-block;
     cursor: default;
}

.compose__aud--static:hover {
     color: var(--muted);
     box-shadow: inset 0 0 0 1px var(--line);
}

/* The compose textarea, as a real field.

   The mock's .compose__text is frameless — background:none, border:none —
   because in the mock it's the only thing in the dialog. In ours it sits
   between the identity row and the "Add to your post" strip, and the
   legacy modal it replaced had a visible 1px box, so losing the outline
   made it read as body text rather than something you type into.

   Dressed with the theme's own field treatment rather than a plain border:
   .input/.textarea in clanstop.css use an inset ring plus the 9px chamfer,
   so this matches every other input on the site. The ring is a box-shadow,
   which draws inside the element and so survives the clip-path — a real
   `border` would get its corners sliced off by it.

   Padding is the theme's field padding, which also lines the text up with
   the .compose__add label directly beneath it. It previously fell back to
   the browser's ~2px UA default, which sat the post text a dozen pixels
   left of that label and read as it escaping the modal's inner margin. */
.compose .compose__text {
     margin-top: .4rem;
     padding: .7rem .9rem;
     background: var(--panel-2);
     border: none;
     box-shadow: inset 0 0 0 1px var(--line);
     clip-path: polygon(9px 0, 100% 0, 100% calc(100% - 9px), calc(100% - 9px) 100%, 0 100%, 0 9px);
     max-height: clamp(180px, 40vh, 400px);
}
.compose .compose__text:focus {
     outline: none;
     box-shadow: inset 0 0 0 1px var(--blue);
}

/* A <label> has to look and behave like .compose__tools' buttons. */
.compose__tools .attachment-label {
     display: inline-flex;
     align-items: center;
     background: none;
     border: none;
     cursor: pointer;
     font-size: 1.2rem;
     line-height: 1;
     padding: .3rem .35rem;
     border-radius: 7px;
     transition: background .12s;
}

.compose__tools .attachment-label:hover {
     background: rgba(255, 255, 255, .07);
}

/* Armed drop zone. The JS toggles .is-dragover (it used to write an inline
   background), so the hint can be the theme's blue rather than a grey wash. */
.compose__add.is-dragover {
     box-shadow: inset 0 0 0 1px var(--blue);
     background: rgba(0, 166, 255, .08);
}

.compose__preview {
     display: flex;
     flex-wrap: wrap;
     gap: .5rem;
     max-height: 150px;
     overflow-y: auto;
}

.compose__preview:not(:empty) {
     margin-top: .8rem;
}

.compose .attachment-thumb {
     position: relative;
     width: 60px;
     height: 60px;
     overflow: hidden;
     clip-path: polygon(7px 0, 100% 0, 100% calc(100% - 7px), calc(100% - 7px) 100%, 0 100%, 0 7px);
}

.compose .attachment-thumb img {
     width: 100%;
     height: 100%;
     object-fit: cover;
}

.compose .remove-image-btn {
     position: absolute;
     top: 2px;
     right: 2px;
     width: 18px;
     height: 18px;
     line-height: 1;
     font-size: 14px;
     cursor: pointer;
     border: none;
     border-radius: 50%;
     background: rgba(0, 0, 0, .6);
     color: #fff;
}

.compose .remove-image-btn:hover {
     background: var(--red);
}

/* .modal-button's flex:1 would fight .btn--block inside the one-item foot. */
.compose-foot .post-submit-btn {
     width: 100%;
     margin-top: 0;
}

/* --- edit variant of the compose modal ---------------------------------
   Same shell; the only extra is the block listing what's already attached,
   each thumbnail with an unlink button. Scoped under .compose so the delete
   dialog's own .current-post-preview (still inline-styled, red-bordered)
   isn't caught by these. */
.compose .current-post-preview {
     background: var(--panel-2);
     box-shadow: inset 0 0 0 1px var(--line);
     clip-path: polygon(10px 0, 100% 0, 100% calc(100% - 10px), calc(100% - 10px) 100%, 0 100%, 0 10px);
     padding: .8rem .9rem;
     margin-top: .8rem;
}

.compose__section-label {
     font-family: var(--mono);
     font-size: .58rem;
     letter-spacing: .1em;
     text-transform: uppercase;
     color: var(--muted);
     margin: 0 0 .55rem;
}

.compose .post-image-edit-area {
     flex-wrap: wrap;
     gap: .5rem;
}

.compose .edit-thumb {
     position: relative;
     width: 80px;
     height: 80px;
     overflow: hidden;
     border-radius: 0;
     clip-path: polygon(7px 0, 100% 0, 100% calc(100% - 7px), calc(100% - 7px) 100%, 0 100%, 0 7px);
}

.compose .edit-thumb img {
     width: 100%;
     height: 100%;
     object-fit: cover;
}

.compose .unlink-image-btn {
     position: absolute;
     top: 2px;
     right: 2px;
     width: 20px;
     height: 20px;
     display: flex;
     align-items: center;
     justify-content: center;
     background: rgba(0, 0, 0, .6);
     color: #fff;
     border: none;
     border-radius: 50%;
     cursor: pointer;
     font-size: 16px;
     line-height: 1;
     transition: background .15s;
}

.compose .unlink-image-btn:hover {
     background: var(--red);
}

/* Draft-restore bar, in the theme's own alert language. */
.compose .draft-bar {
     display: flex;
     justify-content: space-between;
     align-items: center;
     gap: .6rem;
     padding: .55rem .8rem;
     margin-bottom: .75rem;
     background: rgba(0, 166, 255, .1);
     box-shadow: inset 0 0 0 1px rgba(0, 166, 255, .35);
     border: none;
     border-radius: 0;
     clip-path: polygon(8px 0, 100% 0, 100% calc(100% - 8px), calc(100% - 8px) 100%, 0 100%, 0 8px);
     font-size: .82rem;
     color: var(--text);
}

.compose .draft-bar button {
     font-family: var(--mono);
     font-size: .58rem;
     letter-spacing: .1em;
     text-transform: uppercase;
     padding: .35rem .7rem;
     border: none;
     border-radius: 0;
     cursor: pointer;
     clip-path: polygon(6px 0, 100% 0, calc(100% - 6px) 100%, 0 100%);
}

.compose .draft-bar .draft-restore {
     background: var(--blue);
     color: #fff;
}

.compose .draft-bar .draft-discard {
     background: var(--panel);
     color: var(--muted);
     box-shadow: inset 0 0 0 1px var(--line);
}

.compose .draft-bar .draft-discard:hover {
     color: var(--text);
}

/* Clan picker inside the "send clan invite" dialog. */
.clan-selection-list {
     display: flex;
     flex-direction: column;
     gap: .4rem;
     margin: .8rem 0;
     list-style: none;
}

.clan-select-item {
     display: flex;
     align-items: center;
     gap: .7rem;
     padding: .55rem .7rem;
     background: var(--panel-2);
     box-shadow: inset 0 0 0 1px var(--line);
     clip-path: polygon(8px 0, 100% 0, 100% calc(100% - 8px), calc(100% - 8px) 100%, 0 100%, 0 8px);
}

.select-clan-btn {
     margin-left: auto;
     background: none;
     border: none;
     cursor: pointer;
     font-family: var(--mono);
     font-size: .58rem;
     letter-spacing: .1em;
     text-transform: uppercase;
     color: var(--blue);
}

.select-clan-btn:hover {
     text-decoration: underline;
}

/* Custom link list in the profile rail (moved out of pages/profile.php). */
.clan-link-list {
     list-style: none;
}

.clan-link-item + .clan-link-item {
     border-top: 1px solid var(--line);
}

.clan-link-item a {
     display: flex;
     align-items: center;
     gap: .6rem;
     padding: .5rem .2rem;
     font-size: .84rem;
     color: var(--muted);
     min-width: 0;
     transition: color .15s, padding-left .15s;
}

.clan-link-item a:hover {
     color: var(--blue);
     padding-left: .4rem;
}

.clan-link-favicon,
.clan-link-favicon-fallback {
     width: 16px;
     height: 16px;
     flex-shrink: 0;
     object-fit: contain;
     opacity: .85;
}

.clan-link-label {
     white-space: nowrap;
     overflow: hidden;
     text-overflow: ellipsis;
     min-width: 0;
}

.clan-link-nsf-badge {
     flex-shrink: 0;
     font-family: var(--mono);
     font-size: .5rem;
     letter-spacing: .08em;
     color: var(--red-text);
     box-shadow: inset 0 0 0 1px rgba(232, 0, 45, .45);
     padding: .12rem .35rem;
}

.clan-link-arrow {
     flex-shrink: 0;
     margin-left: auto;
     color: var(--line);
     transition: color .15s, transform .15s;
}

.clan-link-item a:hover .clan-link-arrow {
     color: var(--blue);
     transform: translateX(2px);
}

/* ============ SMALL HELPERS ============ */
.cs-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;
}

/* The theme sizes .avatar per context (.pennant .avatar, .matchup__side
   .avatar, …) but never declares a base rule, so the initials inside one
   sit top-left and the block stays a plain square. This is the missing
   base: same centring/typography as .avatar--round, chamfered instead of
   circular so the two read as a matched pair. */
.avatar {
     display: grid;
     place-items: center;
     flex: none;
     font-family: var(--display);
     font-weight: 800;
     font-style: italic;
     font-size: .9rem;
     color: #fff;
     clip-path: polygon(8px 0, 100% 0, 100% calc(100% - 8px), calc(100% - 8px) 100%, 0 100%, 0 8px);
}

/* Avatar images reuse .avatar / .avatar--round geometry from the theme but
   need object-fit so uploads don't distort. The theme's own avatars are
   initials in a gradient, so it never had to care. */
.avatar img,
.avatar--round img {
     width: 100%;
     height: 100%;
     object-fit: cover;
     border-radius: inherit;
}

/* ============ CLAN PAGE — additions ============
   /clan runs on the same .pf-* chrome as /profile (see $cs_new_theme_pages
   in index.php), so most of this page needs no CSS of its own. What's here
   is the handful of pieces the profile page has no equivalent for: the
   public summary band, the featured-stream card, the sortable roster and
   the Join/Manage button states. Rules that used to live in <style> blocks
   inside pages/clan.php and pages/clan-mods/* were retuned onto the theme's
   tokens and moved here. */

/* The .cl-summary band and its .memberchips variant were removed with
   the public-summary panel; the rail carries that content now. */


/* --- featured live stream --- */
.featured-stream-wrap {
     max-width: 1200px;
     margin: 1.2rem auto 0;
     padding: 0 1.2rem;
}
.featured-stream-card {
     background: var(--panel);
     box-shadow: inset 0 0 0 1px var(--line);
     clip-path: var(--chamfer);
     padding: .85rem 1rem;
     display: flex;
     flex-direction: column;
     gap: .65rem;
     overflow: hidden;
}
.featured-stream-card header {
     display: flex;
     align-items: center;
     gap: .55rem;
     flex-wrap: wrap;
}
.featured-stream-card .live-dot {
     display: inline-block;
     width: 9px;
     height: 9px;
     border-radius: 50%;
     background: var(--red);
     box-shadow: 0 0 0 0 rgba(232, 0, 45, .6);
     animation: feat-live-pulse 1.5s infinite;
}
@keyframes feat-live-pulse {
     0%   { box-shadow: 0 0 0 0 rgba(232, 0, 45, .6); }
     70%  { box-shadow: 0 0 0 8px rgba(232, 0, 45, 0); }
     100% { box-shadow: 0 0 0 0 rgba(232, 0, 45, 0); }
}
@media (prefers-reduced-motion: reduce) {
     .featured-stream-card .live-dot { animation: none; }
}
.featured-stream-card .title { color: var(--text); font-weight: 600; }
.featured-stream-card .meta {
     color: var(--muted);
     font-size: .82rem;
     margin-left: auto;
}
.featured-stream-card .meta a { color: var(--blue); }
.featured-stream-card .platform-pill {
     font-family: var(--mono);
     font-size: .62rem;
     letter-spacing: .06em;
     padding: .2rem .5rem;
     font-weight: 700;
     color: #fff;
}
.featured-stream-embed {
     position: relative;
     width: 100%;
     aspect-ratio: 16 / 9;
     background: #000;
     overflow: hidden;
}
.featured-stream-embed iframe {
     width: 100%;
     height: 100%;
     border: 0;
     display: block;
}
.featured-stream-cta-card {
     display: flex;
     align-items: center;
     gap: 1rem;
     background: var(--panel-2);
     box-shadow: inset 0 0 0 1px var(--line);
     padding: 1rem;
     color: var(--text);
     font-size: .9rem;
}
.featured-stream-cta-card a.watch-btn {
     margin-left: auto;
     padding: .5rem 1rem;
     font-weight: 700;
     text-decoration: none;
     color: #fff;
     clip-path: var(--chamfer);
}

/* The .cl-roster sortable-table rules were removed with the table: the
   roster is the theme's .roster / .member card grid now. */


/* --- allies rail card --- */
.cl-allies__more {
     display: block;
     text-align: center;
     padding: .6rem 0 .1rem;
     color: var(--blue);
     font-size: .82rem;
     text-decoration: none;
}
.cl-allies__more:hover { color: var(--text); }

/* --- ally cards (built by displayFullAllyList() in pages/clan.php) --- */
#ally-card-grid {
     display: grid;
     grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
     gap: 1rem;
     padding: .3rem 0 .5rem;
}
.ally-banner-card {
     display: block;
     text-decoration: none;
     overflow: hidden;
     clip-path: var(--chamfer);
     box-shadow: inset 0 0 0 1px var(--line);
     transition: transform .2s ease, box-shadow .2s ease;
}
.ally-banner-card:hover {
     transform: translateY(-3px);
     box-shadow: inset 0 0 0 1px var(--blue);
}
.ally-card-banner {
     position: relative;
     height: 110px;
     background-color: var(--panel-2);
}
.ally-card-overlay {
     position: absolute;
     inset: 0;
     background: linear-gradient(to top, rgba(0, 0, 0, .8) 0%, rgba(0, 0, 0, .25) 60%, transparent 100%);
     display: flex;
     align-items: flex-end;
     gap: .6rem;
     padding: .6rem .7rem;
}
.ally-card-icon {
     width: 40px;
     height: 40px;
     border-radius: 50%;
     object-fit: cover;
     box-shadow: 0 0 0 2px rgba(255, 255, 255, .25);
     flex-shrink: 0;
}
.ally-card-name {
     color: var(--text);
     font-weight: 600;
     font-size: .92rem;
     line-height: 1.2;
     white-space: nowrap;
     overflow: hidden;
     text-overflow: ellipsis;
}
@media (max-width: 600px) {
     #ally-card-grid {
          grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
          gap: .75rem;
     }
     .ally-card-banner { height: 90px; }
}
/* The rail's compact ally panel uses the same overlay treatment. */
.ally-panel-banner { position: relative; height: 62px; background-color: var(--panel-2); }
.ally-panel-overlay {
     position: absolute;
     inset: 0;
     background: linear-gradient(to top, rgba(0, 0, 0, .8), transparent 100%);
     display: flex;
     align-items: flex-end;
     padding: .4rem .55rem;
}
.ally-panel-name {
     color: var(--text);
     font-size: .8rem;
     font-weight: 600;
     white-space: nowrap;
     overflow: hidden;
     text-overflow: ellipsis;
}

/* --- hero bits --- */
.clan-tag-inline { opacity: .7; font-weight: 500; }

/* --- Join / Manage button states ---
   These ride on top of the theme's .btn, so they only need to say what's
   different: the state colour. The old rules set background, border, glow
   and radius with !important against css/style.css, which fought the
   theme's chamfer and sizing. */
.btn.request-button.btn-manage   { background: var(--blue); color: #001523; }
.btn.request-button.btn-joined   { color: #35D07F; box-shadow: inset 0 0 0 1px #35D07F; cursor: default; }
.btn.request-button.btn-pending  { color: var(--brass); box-shadow: inset 0 0 0 1px var(--brass); cursor: default; }
.btn.request-button.btn-banned,
.btn.request-button.btn-max      { color: var(--red-text); box-shadow: inset 0 0 0 1px var(--red-deep); cursor: not-allowed; opacity: .85; }
.btn.request-button.btn-cooldown { color: var(--muted); box-shadow: inset 0 0 0 1px var(--line); cursor: not-allowed; opacity: .85; }

/* .about-mobile-only went away with the About tab — About is a standalone
   full-width page now (pages/clan-mods/clanPage_about.php), so there are no
   rail cards to repeat for narrow screens. */
/* --- ally cards, rail variant: the card wrapper --- */
/* .ally-panel-banner / __overlay / __name are above; this is the <a> the
   rail's renderAlliesList() wraps them in. */
.ally-panel-card {
     display: block;
     text-decoration: none;
     overflow: hidden;
     clip-path: var(--chamfer);
     box-shadow: inset 0 0 0 1px var(--line);
     transition: transform .2s ease, box-shadow .2s ease;
}
.ally-panel-card + .ally-panel-card { margin-top: .5rem; }
.ally-panel-card:hover {
     transform: translateY(-2px);
     box-shadow: inset 0 0 0 1px var(--blue);
}

/* --- clan page loading veil ---
   #loading is toggled by the clan API fetch in pages/clan.php. It used to
   be dressed by css/style.css, which the page no longer loads. It matters
   less than it did — hero, rail and tabs are all server-rendered now, so
   this only covers the live refresh — but an undressed empty div still
   takes up space, so give it a real (small, centred) spinner. */
/* text-align, not flex: jQuery's .show() writes display:block inline, which
   would drop a flex container back to block and lose the centring. */
#loading {
     display: none;
     text-align: center;
     padding: 1.2rem 0;
}
#loading .spinner-border {
     display: inline-block;
     width: 22px;
     height: 22px;
     border: 2px solid var(--line);
     border-top-color: var(--blue);
     border-radius: 50%;
     animation: cs-spin .7s linear infinite;
}
@keyframes cs-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) {
     #loading .spinner-border { animation-duration: 2.4s; }
}
/* Visually-hidden label inside the spinner. */
#loading .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;
}
/* --- /clan/{id}/about: game chips in the hero ---
   The clan's blurb used to sit under the name, duplicating the About panel
   a few hundred pixels below. Games took its place: the fastest read on
   whether a clan is for you. The theme's .gamechips only dresses <a>
   children — these are identity, not navigation (the "What we play" panel
   handles that), so they're spans and need the same treatment. */
.about-hero-games { margin: .5rem 0 .1rem; }
.about-hero-games span {
     box-shadow: inset 0 0 0 1px var(--line);
     color: var(--muted);
     clip-path: polygon(7px 0, 100% 0, calc(100% - 7px) 100%, 0 100%);
     padding: .35rem .8rem;
     font-size: .76rem;
}

/* A game the clan plays that has no SupportedGames row can't link to a hub,
   so "What we play" renders it as a plain card. Match the linked .ag apart
   from the hover affordance. */
.about-games .ag:not(a) { cursor: default; }
/* --- drawer notifications (mobile) ---
   Below 1024px .nav__cta is display:none, taking the whole .nav__me chip
   (bell included) with it — so the drawer carries the notification cards
   themselves rather than a link to a page this site doesn't have. Same
   .notif rows as the nav popover; this just gives them a scroll bound so a
   busy account can't push the account block off the bottom of the drawer. */
.drawer__notifs {
     max-height: 40vh;
     overflow-y: auto;
     margin-bottom: 1rem;
}
/* The count next to the drawer's "// Notifications" label sits inline
   rather than pinned to a bell. */
.drawer__label .nav__badge {
     position: static;
     border-color: var(--panel-2);
     vertical-align: middle;
     margin-left: .4rem;
}
/* --- nav avatar: real picture over the initials fallback ---
   .nav__avatar is a gradient circle with the user's initials as its text.
   The <img> sits on top when they have an uploaded picture; if it 404s its
   onerror swaps to the site default, and if that fails too the initials
   underneath are still there. Same layering the profile hero uses. */
.nav__avatar {
     position: relative;
     overflow: hidden;
}
.nav__avatar img {
     position: absolute;
     inset: 0;
     width: 100%;
     height: 100%;
     object-fit: cover;
     border-radius: inherit;
}
/* ============ TOURNAMENT BROWSE (/tournaments) ============
   The page is built on the mock's .clanfind / .filterbar / .tourncard set,
   which clanstop.css already dresses. Two things it doesn't cover, both a
   consequence of keeping the filtering server-side:

     1. The filter chips are <select>s, not buttons opening picker modals.
     2. Results are paginated, so there's a pager the mock has no need for.
*/

/* --- filter chip wrapping a native select ---
   The <label> is the visible chip; the <select> sits invisibly on top of it
   so the whole chip is the hit target and the browser draws its own option
   list. Keeps real form semantics — the filter submits, deep links work,
   and there's no client-side filter state to drift out of sync. */
.filterbar .filterbtn {
     position: relative;
     cursor: pointer;
}
.filterbar .filterbtn select {
     position: absolute;
     inset: 0;
     width: 100%;
     height: 100%;
     opacity: 0;
     cursor: pointer;
     font: inherit;
}
/* An active filter reads as selected rather than just showing its value. */
.filterbar .filterbtn--on {
     color: var(--text);
     box-shadow: inset 0 0 0 1px var(--blue);
}
.filterbar .filterbtn--on .filterbtn__val { color: var(--blue); }
/* "My tournaments" and the reset are links, not selects — stop them
   inheriting the underline links get elsewhere in the bar. */
.filterbar a.filterbtn,
.filterbar a.filterbar__reset { text-decoration: none; }

/* --- pagination ---
   Not in the mock (it filters one page client-side). Same chamfer and
   ring language as the theme's other controls. */
.bw-pagination {
     display: flex;
     justify-content: center;
     align-items: center;
     gap: .4rem;
     flex-wrap: wrap;
     margin: 2rem auto 3rem;
     padding: 0 1.2rem;
}
.bw-page-btn {
     min-width: 38px;
     padding: .45rem .7rem;
     text-align: center;
     color: var(--muted);
     font-family: var(--mono);
     font-size: .74rem;
     text-decoration: none;
     background: var(--panel);
     box-shadow: inset 0 0 0 1px var(--line);
     clip-path: polygon(7px 0, 100% 0, 100% calc(100% - 7px), calc(100% - 7px) 100%, 0 100%, 0 7px);
     transition: color .15s, box-shadow .15s;
}
.bw-page-btn:hover { color: var(--text); box-shadow: inset 0 0 0 1px var(--blue); }
.bw-page-btn--active {
     color: #001523;
     background: var(--blue);
     box-shadow: none;
     font-weight: 700;
}
.bw-page-btn--disabled {
     opacity: .4;
     pointer-events: none;
}
.bw-page-gap { color: var(--muted); padding: 0 .2rem; }
/* ============ CREATE A CLAN (/createclan) ============
   Built on the mock's .formpage / .fieldset / .field / .chipgroup set,
   which clanstop.css dresses. What's here is the handful of pieces the
   mock's static version had no need for. */

/* The reserved-name rules paragraph under the name/tag row. Emits inline
   <a> and <strong>, so it's a real prose note rather than a .field__hint. */
.field__note {
     margin: -.4rem 0 .2rem;
     padding: .55rem .7rem;
     font-size: .78rem;
     line-height: 1.5;
     color: var(--muted);
     background: rgba(0, 166, 255, .06);
     box-shadow: inset 0 0 0 1px var(--line);
     border-left: 3px solid var(--blue);
}
.field__note a { color: var(--blue); }

/* Chips in the .chosen strip — the games already picked, shown in the
   field rather than only as a count on the button. */
.chosen__chip {
     display: inline-block;
     padding: .25rem .6rem;
     margin: 0 .3rem .3rem 0;
     font-family: var(--mono);
     font-size: .64rem;
     letter-spacing: .06em;
     text-transform: uppercase;
     color: var(--text);
     background: var(--panel);
     box-shadow: inset 0 0 0 1px var(--line);
     clip-path: polygon(5px 0, 100% 0, calc(100% - 5px) 100%, 0 100%);
}

/* Preview ribbon states. The mock only ever shows --open, since it's a
   static page; the real form can be set to invite-only or closed. */
.clancard__ribbon--invite { background: linear-gradient(160deg, var(--brass), #8a5f10); color: #1a1205; }
.clancard__ribbon--closed { background: var(--panel); color: var(--muted); box-shadow: inset 0 0 0 1px var(--line); }

/* --- platform chips that ARE the checkboxes ---
   Each .filterchip is a <label> around the original inPlatform[] input, so
   the form posts exactly as it did before and no JS is involved. The input
   is hidden but still focusable, so keyboard users get a visible ring. */
.chipgroup .filterchip { position: relative; cursor: pointer; }
.chipgroup .filterchip input[type="checkbox"] {
     position: absolute;
     opacity: 0;
     width: 100%;
     height: 100%;
     inset: 0;
     margin: 0;
     cursor: pointer;
}
.chipgroup .filterchip input[type="checkbox"]:checked ~ span { color: var(--text); }
.chipgroup .filterchip:has(input:checked) {
     color: var(--text);
     box-shadow: inset 0 0 0 1px var(--blue);
     background: rgba(0, 166, 255, .08);
}
.chipgroup .filterchip:focus-within { box-shadow: inset 0 0 0 2px var(--blue); }
/* --- two-up field rows ---
   .field-row is display:flex in the theme, but nothing sizes the fields
   inside it: the mock leans on an inline max-width on the tag field and
   lets the other one be the only flexible item. With two equal fields
   (age/voice, region/language) that leaves both sized to their content
   while the .input inside is width:100% of that — so they came out wrong.
   Give every field in a row an equal share, and pin the tag to the mock's
   150px instead of carrying an inline style. */
.field-row > .field {
     flex: 1 1 0;
     min-width: 0;
}
.field-row > .field--tag {
     flex: 0 0 150px;
     max-width: 150px;
}
@media (max-width: 560px) {
     .field-row > .field,
     .field-row > .field--tag { flex: 1 1 100%; max-width: none; }
}

/* --- selected-count badge on the "Add games" button ---
   .filterbtn has no .count rule of its own; without this the number sat as
   bare text against the label. */
.filterbtn .count {
     margin-left: .45rem;
     min-width: 18px;
     padding: .1rem .35rem;
     display: inline-block;
     text-align: center;
     font-family: var(--mono);
     font-size: .58rem;
     line-height: 1.5;
     color: #001523;
     background: var(--blue);
     clip-path: polygon(4px 0, 100% 0, calc(100% - 4px) 100%, 0 100%);
}
.filterbtn .count:empty { display: none; }

/* Native selects wearing .input: strip the platform chrome so they match
   the text inputs beside them, and leave room for the arrow. */
select.input {
     appearance: none;
     -webkit-appearance: none;
     padding-right: 2.2rem;
     background-image: linear-gradient(45deg, transparent 50%, var(--muted) 50%),
                       linear-gradient(135deg, var(--muted) 50%, transparent 50%);
     background-position: calc(100% - 1.1rem) 1.15em, calc(100% - .75rem) 1.15em;
     background-size: 5px 5px, 5px 5px;
     background-repeat: no-repeat;
     cursor: pointer;
}
select.input option { background: var(--panel-2); color: var(--text); }
/* ============ TOURNAMENT CREATE WIZARD — additions ============
   The wizard was re-skinned by renaming presentational class tokens onto the
   theme's .tc-* / .set-* vocabulary. Seventeen classes could NOT be renamed
   because the page's own JavaScript addresses them — .field,
   .form-section-body and .step-btn are the structural backbone, and a dozen
   more are state classes it adds at runtime. Those keep their names and are
   dressed here instead. Values mirror the designer's .tc-* rules so a renamed
   element and a kept one look identical. */

/* --- the step rail --- */
.step-btn {
     display: flex;
     align-items: center;
     gap: .4rem;
     background: none;
     border: none;
     cursor: pointer;
     padding: .35rem .55rem;
     font-family: var(--mono);
     font-size: .58rem;
     letter-spacing: .04em;
     text-transform: uppercase;
     color: var(--muted);
     white-space: nowrap;
     transition: color .15s;
}
.step-btn:hover { color: #fff; }
/* .active / .done / .locked are what the wizard's JS toggles; the theme's
   equivalents are .is-active / .is-done / .is-locked. */
.tc-step.active, .step-btn.active { color: var(--red-text); }
.tc-step.done,   .step-btn.done   { color: #59D683; }
.tc-step.locked, .step-btn.locked { opacity: .4; cursor: not-allowed; }
.tc-step.locked:hover, .step-btn.locked:hover { color: var(--muted); }
.tc-step.active .tc-stepnum { background: var(--red); border-color: var(--red); color: #fff; }
.tc-step.done   .tc-stepnum { background: #59D683; border-color: #59D683; color: #04120a; }

/* The step rail is sticky here, which the mock's .tc-steps isn't — this page
   is a long scrolling form and losing the rail costs you your place. */
.tc-steps { position: sticky; top: 0; z-index: 100; }

/* --- a step panel --- */
/* .step-hidden is the JS's own hide flag; the theme uses .is-hidden. */
.tc-sec.step-hidden, .set-sec.step-hidden { display: none; }
.form-section-body { display: flex; flex-direction: column; gap: .9rem; padding: 1rem 1.1rem 1.2rem; }
.form-section-body.set-2col,
.set-sec__body.set-2col { display: grid; }

/* --- fields ---
   The wrappers now carry BOTH `field` (their JS hook) and `set-field`, so
   clanstop.css styles the labels and every input/select/textarea inside them
   directly. That descendant selector is the whole reason the controls looked
   unthemed: 54 inputs, 9 selects and 13 textareas sat under `.field`, which
   the theme knows nothing about.

   The rules that used to be duplicated here are gone — this file loads after
   clanstop.css, so they would override the designer's work rather than match
   it. Only what the theme genuinely lacks stays. */
.tc-form .field.full { grid-column: 1 / -1; }
.tc-form .req { color: var(--red); }
/* Validation state, added and removed by the wizard's own checks. */
.tc-form .f-error input,
.tc-form .f-error select,
.tc-form .f-error textarea,
.tc-form input.f-error,
.tc-form select.f-error,
.tc-form textarea.f-error { box-shadow: inset 0 0 0 1px var(--red); }
.tc-form .f-error > label,
.tc-form .f-error > span:first-child { color: var(--red-text); }

/* --- game picker ---
   The markup now carries the theme's combobox classes (.combo, .combo__ic,
   .combo__input, .combo__dd, .combo__opt) alongside its own game-search-*
   names, so clanstop.css supplies the look and the designer's refinements to
   it land here automatically. Everything that used to be duplicated in this
   file is gone — those rules load AFTER clanstop.css and would have quietly
   overridden the very changes they were meant to mirror.

   Only the state-class alias remains: this page's keyboard handler marks the
   highlighted row .focused, where the theme expects .is-focus. */
.combo__opt.focused { background: color-mix(in srgb, var(--blue) 12%, transparent); }
.game-selected-pill {
     display: inline-flex;
     align-items: center;
     gap: .45rem;
     background: var(--panel);
     box-shadow: inset 0 0 0 1px var(--line);
     clip-path: var(--chamfer);
     padding: .35rem .6rem;
}
.game-selected-pill-clear, .game-search-clear {
     background: none;
     border: 0;
     color: var(--muted);
     cursor: pointer;
     font-size: 1rem;
     line-height: 1;
}
.game-selected-pill-clear:hover, .game-search-clear:hover { color: var(--red-text); }

/* --- blocks the wizard builds that the theme has no equivalent for ---
   Everything with a 2026 counterpart (prize rows, fee tiers, rule cards, the
   clan combobox) now carries that class in the markup, so clanstop.css dresses
   it directly. What follows is only what the theme genuinely doesn't ship. */

/* Section-icon tints used to colour-code steps. */
.set-sec__ico.icon-blue  { color: var(--blue); }
.set-sec__ico.icon-gold  { color: var(--brass); }
.set-sec__ico.icon-green { color: #59D683; }
.set-sec__ico.icon-pink  { color: #FF6BAA; }

/* Button rows / submit area. */
.btn-row { display: flex; gap: .5rem; flex-wrap: wrap; align-items: center; }
.submit-area { display: flex; flex-direction: column; gap: .6rem; margin-top: 1rem; }
.btn-draft { white-space: nowrap; }

/* Informational and warning blocks. The wizard leans on these heavily —
   entry-fee terms, eligibility notes, date conflicts, validation errors. */
.reg-info-box,
.create-disclaimer,
.entry-fee-disclaimer,
.allow-exclude-note,
.rule-note {
     background: color-mix(in srgb, var(--blue) 7%, var(--panel-2));
     box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--blue) 22%, transparent);
     clip-path: var(--chamfer);
     padding: .7rem .9rem;
     font-size: .8rem;
     color: var(--muted);
     line-height: 1.5;
}
.create-disclaimer,
.entry-fee-disclaimer {
     background: rgba(255, 184, 28, .06);
     box-shadow: inset 0 0 0 1px rgba(255, 184, 28, .28);
     border-left: 3px solid var(--brass);
}
.date-conflict-banner,
.step-err-banner {
     display: none;
     background: color-mix(in srgb, var(--red) 10%, var(--panel-2));
     box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--red) 35%, transparent);
     clip-path: var(--chamfer);
     padding: .7rem .9rem;
     font-size: .82rem;
     color: var(--red-text);
     margin-bottom: .8rem;
}
.date-conflict-banner.visible,
.date-conflict-banner.show,
.step-err-banner.visible,
.step-err-banner.show { display: block; }
.eligibility-sub { font-size: .78rem; color: var(--muted); }
.ef-free-badge {
     font-family: var(--mono);
     font-size: .55rem;
     letter-spacing: .06em;
     text-transform: uppercase;
     color: #59D683;
     background: color-mix(in srgb, #59D683 12%, transparent);
     box-shadow: inset 0 0 0 1px color-mix(in srgb, #59D683 35%, transparent);
     clip-path: var(--chamfer);
     padding: .15rem .45rem;
}

/* Allow / exclude cards — two mirrored panels of clan pickers. */
.allow-exclude-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: .8rem; }
.allow-exclude-card {
     background: var(--panel);
     box-shadow: inset 0 0 0 1px var(--line);
     clip-path: var(--chamfer);
     overflow: hidden;
}
.allow-exclude-head {
     display: flex;
     align-items: center;
     gap: .4rem;
     padding: .5rem .8rem;
     background: var(--ink);
     border-bottom: 1px solid var(--line);
}
.allow-exclude-head-icon { font-size: .95rem; line-height: 1; }
.allow-exclude-head-label {
     font-family: var(--mono);
     font-size: .6rem;
     letter-spacing: .08em;
     text-transform: uppercase;
     color: var(--text);
}
.allow-exclude-body { display: flex; flex-direction: column; gap: .55rem; padding: .8rem; }
.allow-card .allow-exclude-head-label  { color: #59D683; }
.exclude-card .allow-exclude-head-label { color: var(--red-text); }

/* Clan picker — the combobox itself now uses .combo, so this is the
   surrounding chrome and the dropdown's row internals. */
.clan-picker { display: flex; flex-direction: column; gap: .5rem; }
.cp-tags { display: flex; flex-wrap: wrap; gap: .3rem; }
.clan-tag-emoji { font-size: .9rem; line-height: 1; }
.clan-tag-name  { font-weight: 600; }
.cp-dd-option {
     display: flex;
     align-items: center;
     gap: .5rem;
     padding: .45rem .7rem;
     cursor: pointer;
     font-size: .84rem;
     color: var(--text);
     border-bottom: 1px solid var(--line);
}
.cp-dd-option:last-child { border-bottom: none; }
.cp-dd-option:hover, .cp-dd-option.focused { background: color-mix(in srgb, var(--blue) 12%, transparent); }
.cp-dd-emoji { flex: none; width: 1.4rem; text-align: center; }
.cp-dd-name  { flex: 1; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.cp-dd-category, .cp-dd-type {
     flex: none;
     font-family: var(--mono);
     font-size: .52rem;
     letter-spacing: .04em;
     text-transform: uppercase;
     color: var(--muted);
}
.cp-dd-conflict { color: var(--red-text); }
.cp-custom { color: var(--blue); font-style: italic; }
.cp-no-results {
     padding: .85rem;
     text-align: center;
     color: var(--muted);
     font-family: var(--mono);
     font-size: .68rem;
}

/* Rules builder — a stack of .rule-card panels. */
.rules-builder { display: flex; flex-direction: column; gap: .7rem; }
.rbi { font-size: .95rem; line-height: 1; }

/* Prize allocation readout under the payout rows. */
.prize-alloc-track {
     height: 6px;
     background: var(--panel);
     box-shadow: inset 0 0 0 1px var(--line);
     overflow: hidden;
}
.prize-alloc-labels {
     display: flex;
     justify-content: space-between;
     gap: .5rem;
     font-family: var(--mono);
     font-size: .58rem;
     letter-spacing: .05em;
     color: var(--muted);
}

/* Entry-fee tier internals not covered by .set-fee__*. */
.entry-fee-tier-label { display: flex; flex-direction: column; gap: .1rem; }
.entry-fee-tier-sub   { font-size: .72rem; color: var(--muted); }
.entry-fee-input-group { display: flex; align-items: center; gap: .35rem; }
.entry-fee-payment-fields { display: flex; flex-direction: column; gap: .5rem; }
.entry-fee-payment-row { display: flex; gap: .5rem; flex-wrap: wrap; align-items: center; }

/* Misc leftovers. */
.bracket-desc-body { min-width: 0; }
.mt-custom-input { flex: 1; min-width: 160px; }
.game-option-emoji { flex: none; width: 1.4rem; text-align: center; }
.game-option-name  { flex: 1; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.game-option-genre {
     flex: none;
     font-family: var(--mono);
     font-size: .52rem;
     letter-spacing: .04em;
     text-transform: uppercase;
     color: var(--muted);
}

/* --- section icon tint ---
   The page colours a couple of section icons purple; the theme's .set-sec__ico
   is neutral. */
.set-sec__ico.icon-purple { color: #B36BFF; }

/* --- draft restore ---
   The timestamp inside the restore sentence. */
.draft-restore-time { font-family: var(--mono); font-size: .68rem; color: var(--text); }

/* --- selected-game pill internals ---
   The pill itself is styled below; these are its three parts. */
.game-selected-pill-emoji { font-size: 1rem; line-height: 1; }
.game-selected-pill-name  { font-weight: 600; color: var(--text); }
.game-selected-pill-genre {
     font-family: var(--mono);
     font-size: .52rem;
     letter-spacing: .05em;
     text-transform: uppercase;
     color: var(--muted);
}
.game-search-input-row { position: relative; display: flex; align-items: center; }
/* The clear button sits inside the input's right edge. */
.game-search-input-row .game-search-clear {
     position: absolute;
     right: .5rem;
     display: none;
}
.game-search-input-row .game-search-clear.visible { display: block; }

/* --- icon picker ---
   A live preview of the chosen emoji next to a free-text field. */
.emoji-custom-wrap { display: flex; align-items: center; gap: .6rem; flex-wrap: wrap; }
.emoji-preview {
     width: 40px;
     height: 40px;
     flex: none;
     display: grid;
     place-items: center;
     font-size: 1.3rem;
     background: var(--panel-2);
     box-shadow: inset 0 0 0 1px var(--line);
     clip-path: var(--chamfer);
}

/* --- match types block ---
   A titled sub-panel inside a step. The theme has no equivalent, so it
   borrows .set-sec's language at one level down. */
.match-types-section {
     background: var(--panel);
     box-shadow: inset 0 0 0 1px var(--line);
     clip-path: var(--chamfer);
     overflow: hidden;
}
.match-types-head {
     display: flex;
     align-items: center;
     gap: .45rem;
     flex-wrap: wrap;
     padding: .55rem .8rem;
     background: var(--ink);
     border-bottom: 1px solid var(--line);
}
.match-types-head-icon { font-size: .95rem; line-height: 1; }
.match-types-head-label {
     font-family: var(--mono);
     font-size: .62rem;
     letter-spacing: .08em;
     text-transform: uppercase;
     color: var(--text);
}
.match-types-head-hint { font-size: .74rem; color: var(--muted); }
.match-types-body { display: flex; flex-direction: column; gap: .6rem; padding: .8rem; }
.mt-suggestions-label {
     font-family: var(--mono);
     font-size: .58rem;
     letter-spacing: .08em;
     text-transform: uppercase;
     color: var(--muted);
}

/* --- match-type chips + tags --- */
.mt-tags { display: flex; flex-wrap: wrap; gap: .35rem; }
.mt-tag {
     display: inline-flex;
     align-items: center;
     gap: .3rem;
     font-family: var(--mono);
     font-size: .58rem;
     letter-spacing: .04em;
     text-transform: uppercase;
     color: var(--blue);
     background: color-mix(in srgb, var(--blue) 12%, var(--panel-2));
     box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--blue) 35%, transparent);
     clip-path: var(--chamfer);
     padding: .2rem .5rem;
}
.mt-tag-remove { background: none; border: 0; color: inherit; cursor: pointer; line-height: 1; }
.mt-tag-remove:hover { color: var(--red-text); }
.mt-chip.active, .tc-chip.active { color: var(--blue); border-color: color-mix(in srgb, var(--blue) 45%, transparent); }
/* A suggestion drawn from what other organizers actually typed, rather than
   from the built-in list. Dashed edge only — it behaves identically, so the
   cue stays quiet enough not to read as a warning. */
.mt-chip--community { border-style: dashed; }
.mt-chip--community.active { border-style: solid; }
.mt-custom-row { display: flex; gap: .4rem; flex-wrap: wrap; }
.mt-custom-row input { flex: 1; min-width: 160px; }

/* --- entry-fee tiers + prize allocation --- */
.entry-fee-tiers { display: flex; flex-direction: column; gap: .5rem; }
.prize-alloc-bar {
     height: 6px;
     background: var(--panel);
     box-shadow: inset 0 0 0 1px var(--line);
     overflow: hidden;
}
.prize-alloc-fill { display: block; height: 100%; background: var(--brass); transition: width .2s ease; }
.prize-alloc-text { font-family: var(--mono); font-size: .6rem; letter-spacing: .06em; color: var(--muted); }
.prize-alloc-text.over { color: var(--red-text); }

/* --- clan tag pills (eligibility picker) --- */
.clan-tag {
     display: inline-flex;
     align-items: center;
     gap: .3rem;
     font-size: .76rem;
     color: var(--text);
     background: var(--panel);
     box-shadow: inset 0 0 0 1px var(--line);
     clip-path: var(--chamfer);
     padding: .25rem .55rem;
}
.clan-tag-remove { background: none; border: 0; color: var(--muted); cursor: pointer; line-height: 1; }
.clan-tag-remove:hover { color: var(--red-text); }
.cp-dropdown {
     position: absolute;
     z-index: 40;
     display: none;
     background: var(--panel-2);
     box-shadow: inset 0 0 0 1px var(--line);
     clip-path: var(--chamfer);
}
.cp-dropdown.open { display: block; }

/* --- draft restore bar + toast --- */
.draft-restore-bar {
     display: flex;
     align-items: center;
     gap: .6rem;
     flex-wrap: wrap;
     margin-bottom: 1rem;
     padding: .6rem .9rem;
     background: color-mix(in srgb, var(--blue) 10%, var(--panel));
     box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--blue) 35%, transparent);
     clip-path: var(--chamfer);
     font-size: .82rem;
}
.draft-toast {
     position: fixed;
     bottom: 1.2rem;
     left: 50%;
     transform: translateX(-50%) translateY(1rem);
     z-index: 140;
     opacity: 0;
     pointer-events: none;
     transition: opacity .2s, transform .2s;
     background: var(--panel-2);
     box-shadow: inset 0 0 0 1px var(--line), 0 18px 40px -18px rgba(0, 0, 0, .8);
     clip-path: var(--chamfer);
     padding: .55rem .9rem;
     font-size: .82rem;
     color: var(--text);
}
.draft-toast.visible { opacity: 1; transform: translateX(-50%) translateY(0); }

/* --- misc --- */
.rule-builder-body { display: flex; flex-direction: column; gap: .6rem; }
.date-err input, input.date-err { box-shadow: inset 0 0 0 1px var(--red); }
.btn-submit { width: 100%; }

/* ============ TOURNAMENT SETTINGS + APPROVALS — additions ============
   Both tabs were re-skinned by renaming class tokens onto the theme's
   .set-* / .ap-* vocabulary. Anything their JavaScript addresses BY CLASS was
   deliberately left under its original name — renaming it would have broken
   the page — so those classes lost their styling when the legacy <style>
   blocks were removed. This restores them in theme vocabulary. */

/* --- settings: bits with no theme equivalent --- */
.set-field .req { color: var(--red); }
.set-sec__head .section-title-block { display: flex; flex-direction: column; gap: .15rem; }
.set-sec__head .section-title {
     font-family: var(--display);
     font-weight: 800;
     font-style: italic;
     font-size: 1.05rem;
     color: var(--text);
}
.set-sec__head .section-desc { font-size: .82rem; color: var(--muted); }
.set-emoji .emoji-opt {
     font-size: 1.1rem;
     line-height: 1;
     padding: .35rem .45rem;
     background: var(--panel);
     border: 0;
     box-shadow: inset 0 0 0 1px var(--line);
     clip-path: var(--chamfer);
     cursor: pointer;
}
.set-emoji .emoji-opt:hover { box-shadow: inset 0 0 0 1px var(--blue); }
.set-emoji .emoji-opt.is-active,
.set-emoji .emoji-opt.active { box-shadow: inset 0 0 0 2px var(--blue); }
.delay-row { display: flex; gap: .4rem; flex-wrap: wrap; align-items: center; }
.delay-row input { flex: 1; min-width: 160px; }
.date-err-note { display: none; font-size: .74rem; color: var(--red-text); margin-top: .25rem; }
.date-err-note.date-err,
.date-err .date-err-note { display: block; }
.entry-fee-tiers { display: flex; flex-direction: column; gap: .5rem; }
.entry-fee-currency-row { display: flex; gap: .5rem; flex-wrap: wrap; align-items: center; }
.ef-input-group { display: flex; align-items: center; gap: .35rem; }
.ef-icon-clan, .ef-icon-allies, .ef-icon-general { font-size: 1rem; }
.toggle-opt {
     display: inline-flex;
     align-items: center;
     gap: .4rem;
     padding: .4rem .7rem;
     font-size: .82rem;
     color: var(--muted);
     background: var(--panel);
     box-shadow: inset 0 0 0 1px var(--line);
     clip-path: var(--chamfer);
     cursor: pointer;
}
.toggle-opt:has(input:checked) { color: var(--text); box-shadow: inset 0 0 0 1px var(--blue); }
.rule-card { display: flex; flex-direction: column; gap: .4rem; }
.rule-card-head {
     font-family: var(--mono);
     font-size: .62rem;
     letter-spacing: .1em;
     text-transform: uppercase;
     color: var(--muted);
}
/* The lock overlay greys a section out until the organizer unlocks it. */
.is-locked { opacity: .45; pointer-events: none; }

/* --- approvals: JS-locked classes --- */
.ap-tbl .cb, .ap-tbl .p-row-cb { width: 16px; height: 16px; accent-color: var(--blue); cursor: pointer; }
.ap-tbl .tc, .ap-tbl .tr { vertical-align: middle; }
.td-check { width: 34px; }
.td-actions { white-space: nowrap; }
.pl-name { font-weight: 600; color: var(--text); }
/* The registrant's name links to their profile. Inherits the row's colour so
   it doesn't read as a stray blue link in a dense table; the underline on
   hover is what signals it's clickable. */
.pl-name a { color: inherit; text-decoration: none; }
.pl-name a:hover { color: var(--blue); text-decoration: underline; }

/* .ap-av is a 32px flex box the theme built for an emoji. It now holds a real
   avatar, so it has to clip — object-fit stops a non-square upload
   distorting, and overflow keeps it inside the chamfered frame. */
.ap-av { overflow: hidden; }
.ap-av img { width: 100%; height: 100%; object-fit: cover; display: block; }
/* When the avatar is a link, kill the anchor's own text styling. */
a.ap-av { text-decoration: none; }
a.ap-av:hover { box-shadow: inset 0 0 0 1px var(--blue); }
.row-denied { opacity: .5; }
.empty-row td { text-align: center; padding: 2rem 1rem; color: var(--muted); }
.empty-ico { display: block; font-size: 1.6rem; margin-bottom: .4rem; }
.stat-body { display: flex; flex-direction: column; }
.t-search-ico { color: var(--muted); }

/* action buttons in a row */
.ab, .assign-btn, .remove-btn, .bulk-btn, .f-btn {
     font-family: var(--mono);
     font-size: .6rem;
     letter-spacing: .05em;
     text-transform: uppercase;
     color: var(--text);
     background: var(--panel);
     border: 0;
     box-shadow: inset 0 0 0 1px var(--line);
     clip-path: var(--chamfer);
     padding: .35rem .6rem;
     cursor: pointer;
     white-space: nowrap;
}
.ab:hover, .assign-btn:hover, .remove-btn:hover, .bulk-btn:hover, .f-btn:hover { box-shadow: inset 0 0 0 1px var(--blue); }
.ab--approve, .bulk-btn--approve { color: #59D683; box-shadow: inset 0 0 0 1px color-mix(in srgb, #59D683 45%, transparent); }
.ab--deny, .ab--revoke, .ab--remove-team, .bulk-btn--deny { color: var(--red-text); box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--red) 45%, transparent); }
.ab--paid, .bulk-btn--paid { color: var(--brass); box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--brass) 45%, transparent); }
.ab--unpaid, .ab--reinstate { color: var(--muted); }
.f-btn.on { color: var(--text); box-shadow: inset 0 0 0 1px var(--blue); background: color-mix(in srgb, var(--blue) 14%, var(--panel)); }
.t-filters { display: flex; gap: .35rem; flex-wrap: wrap; }

/* status badges */
.badge, .a-pay-badge, .p-pay-badge, .a-team-badge, .ci-badge {
     display: inline-block;
     font-family: var(--mono);
     font-size: .55rem;
     letter-spacing: .06em;
     text-transform: uppercase;
     padding: .16rem .42rem;
     clip-path: var(--chamfer);
     background: var(--panel-2);
     box-shadow: inset 0 0 0 1px var(--line);
     color: var(--muted);
}
.b-approved, .b-assigned { color: #59D683; box-shadow: inset 0 0 0 1px color-mix(in srgb, #59D683 40%, transparent); }
.b-pending  { color: var(--brass); box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--brass) 40%, transparent); }
.b-denied   { color: var(--red-text); box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--red) 40%, transparent); }
.b-unassign { color: var(--muted); }

/* bulk bar — .show is toggled by the page's JS */
.bulk-bar {
     display: none;
     align-items: center;
     gap: .5rem;
     flex-wrap: wrap;
     margin: .8rem 0;
     padding: .6rem .8rem;
     background: color-mix(in srgb, var(--blue) 10%, var(--panel));
     box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--blue) 35%, transparent);
     clip-path: var(--chamfer);
}
.bulk-bar.show { display: flex; }

/* team assignment dropdown
   position: FIXED, not absolute. openTeamDD() measures the button with
   getBoundingClientRect() and writes viewport coordinates into style.top /
   style.left, and below 640px it converts the panel into a bottom sheet with
   top:auto; left:0; right:0; bottom:0. Absolute positioning broke both: the
   coordinates landed in the wrong place, and the panel was trapped inside
   .ap-tblwrap { overflow-x: auto } — so instead of overlaying the table it
   was clipped and pushed the wrapper into a horizontal scroll.

   Fixed positioning also takes it out of that scroll container entirely,
   which is the behaviour the JS was always written for. */
/* While a dropdown is open, suspend the table frame's clip-path.
   .ap-tblwrap is clip-path: var(--chamfer), and clip-path clips EVERY
   descendant — position:fixed included — as well as creating a stacking
   context, so the panel was cut off at the table edge and sat beneath later
   elements however high its z-index went. openTeamDD()/closeAllDDs() toggle
   .dd-unclip; driving it from the JS rather than :has() keeps it working
   regardless of selector support, since this is core functionality.

   overflow-x stays put — the table genuinely needs to scroll below 640px,
   and overflow alone doesn't clip a fixed element once the clip-path (and
   with it the containing block) is gone. */
.ap-tblwrap.dd-unclip { clip-path: none; }

.dd-wrap { position: relative; display: inline-block; }
.dd-backdrop {
     position: fixed;
     inset: 0;
     z-index: 100;
     display: none;
}
/* The JS toggles .show here (closeAllDDs / openTeamDD), not .open — the
   backdrop only appears for the mobile bottom sheet. */
.dd-backdrop.show { display: block; }
.team-dd {
     position: fixed;
     z-index: 101;   /* above the sticky nav (50), below the modal shell (110) */
     min-width: 220px;
     max-height: 320px;
     overflow-y: auto;
     display: none;
     background: var(--panel-2);
     box-shadow: inset 0 0 0 1px var(--line), 0 18px 40px -18px rgba(0, 0, 0, .8);
     clip-path: var(--chamfer);
}
.team-dd.open { display: block; }
/* Below 640px openTeamDD() turns the panel into a bottom sheet and sets an
   inline border-radius. The chamfer would clip that off, so drop it there. */
@media (max-width: 640px) {
     .team-dd { clip-path: none; max-height: none; }
}

/* Rows inside the team dropdown, built by populateTeamDD(). */
.team-dd-item .tdi-icon { flex: none; }
.team-dd-item .tdi-name { flex: 1; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.team-dd-item .tdi-slots,
.team-dd-item .tdi-full {
     flex: none;
     font-family: var(--mono);
     font-size: .52rem;
     letter-spacing: .04em;
     text-transform: uppercase;
     color: var(--muted);
}
.team-dd-item .tdi-full { color: var(--red-text); }

/* Payment badges — the JS swaps between these two on every toggle. */
.b-paid   { color: #59D683; box-shadow: inset 0 0 0 1px color-mix(in srgb, #59D683 40%, transparent); }
.b-unpaid { color: var(--brass); box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--brass) 40%, transparent); }

/* Toasts (showToast) — built entirely in JS, so they had no styling at all
   once the page's <style> was removed. */
.toast {
     position: fixed;
     left: 50%;
     bottom: 1.2rem;
     transform: translateX(-50%);
     z-index: 130;
     display: flex;
     align-items: center;
     gap: .55rem;
     max-width: min(520px, calc(100vw - 2rem));
     padding: .6rem .9rem;
     font-size: .84rem;
     color: var(--text);
     background: var(--panel-2);
     box-shadow: inset 0 0 0 1px var(--line), 0 18px 40px -18px rgba(0, 0, 0, .8);
     clip-path: var(--chamfer);
}
.toast--success,
.toast--ok      { box-shadow: inset 0 0 0 1px color-mix(in srgb, #59D683 45%, transparent); }
.toast--error   { box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--red) 45%, transparent); }
.toast--info    { box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--blue) 45%, transparent); }
.toast .t-ico { flex: none; font-size: 1rem; line-height: 1; }
.toast .t-msg { flex: 1; min-width: 0; }
.toast .t-undo {
     flex: none;
     font-family: var(--mono);
     font-size: .58rem;
     letter-spacing: .06em;
     text-transform: uppercase;
     color: var(--blue);
     background: none;
     border: 0;
     cursor: pointer;
     text-decoration: underline;
}

/* The player column in the approvals tables. */
.td-player { min-width: 0; }

/* Settings: the save indicator goes amber while there are unsaved edits. */
.set-savestatus.unsaved { color: var(--brass); }
.team-dd-head {
     font-family: var(--mono);
     font-size: .58rem;
     letter-spacing: .08em;
     text-transform: uppercase;
     color: var(--muted);
     padding: .5rem .7rem;
     border-bottom: 1px solid var(--line);
}
.team-dd-list { max-height: 240px; overflow-y: auto; }
.team-dd-item {
     display: flex;
     align-items: center;
     justify-content: space-between;
     gap: .5rem;
     width: 100%;
     text-align: left;
     font: inherit;
     font-size: .8rem;
     color: var(--text);
     background: none;
     border: 0;
     padding: .45rem .7rem;
     cursor: pointer;
}
.team-dd-item:hover { background: color-mix(in srgb, var(--blue) 12%, transparent); }
.team-dd-item.full { opacity: .45; cursor: not-allowed; }
/* "create a new team" row at the foot of the dropdown.
   This is a CONTAINER holding a bare <input> and a bare <button> — neither
   carries a class, so it was previously styled as if it were the button
   itself and both controls rendered as browser defaults inside the themed
   panel. The element selectors below are scoped to .team-dd-create so they
   can't leak. */
.team-dd-create {
     display: flex;
     align-items: center;
     gap: .4rem;
     padding: .5rem .6rem;
     border-top: 1px solid var(--line);
     background: var(--panel);
}
.team-dd-create input {
     flex: 1;
     min-width: 0;
     font: inherit;
     font-size: .8rem;
     color: var(--text);
     background: var(--panel-2);
     border: 0;
     box-shadow: inset 0 0 0 1px var(--line);
     clip-path: var(--chamfer);
     padding: .4rem .55rem;
     outline: none;
}
.team-dd-create input::placeholder { color: var(--muted); }
.team-dd-create input:focus { box-shadow: inset 0 0 0 1px var(--blue); }
.team-dd-create button {
     flex: none;
     font-family: var(--mono);
     font-size: .58rem;
     letter-spacing: .06em;
     text-transform: uppercase;
     white-space: nowrap;
     color: var(--blue);
     background: color-mix(in srgb, var(--blue) 14%, transparent);
     border: 0;
     box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--blue) 45%, transparent);
     clip-path: var(--chamfer);
     padding: .42rem .6rem;
     cursor: pointer;
}
.team-dd-create button:hover {
     color: var(--text);
     background: color-mix(in srgb, var(--blue) 26%, transparent);
}

/* ============ TOURNAMENT VIEW (/tournament/{id}/bracket) — additions ====
   clanstop.css ships the whole .pf-hero / .bracket / .match / .tv-* set, so
   this is only what a static mock has no concept of: the check-in window,
   the screenshot gallery (a real feature with no slot in the mock), and the
   roster pills carried over from the old page. */

/* --- check-in banner ---
   Sits above the hero during the check-in window only. Amber because it's a
   deadline, not an error; green once the viewer has checked in. */
.tv-checkin {
     display: flex;
     align-items: center;
     gap: .9rem;
     max-width: 1200px;
     margin: 1rem auto 0;
     padding: .8rem 1.1rem;
     background: color-mix(in srgb, var(--brass) 12%, var(--panel));
     box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--brass) 45%, transparent);
     clip-path: var(--chamfer);
}
.tv-checkin--done {
     background: color-mix(in srgb, #59D683 10%, var(--panel));
     box-shadow: inset 0 0 0 1px color-mix(in srgb, #59D683 40%, transparent);
}
.tv-checkin__ic { font-size: 1.3rem; flex: none; }
.tv-checkin__body { display: flex; flex-direction: column; gap: .1rem; flex: 1; min-width: 0; }
.tv-checkin__body strong { font-family: var(--display); font-style: italic; font-weight: 800; color: var(--text); }
.tv-checkin__body span { font-size: .8rem; color: var(--muted); }
@media (max-width: 640px) {
     .tv-checkin { flex-wrap: wrap; }
     .tv-checkin .btn { width: 100%; }
}

/* --- standings: no scrollbar just for hovering a row ---
   .tv-row:hover applies transform: translateX(2px), and the rows live inside
   .tv-standwrap { overflow-x: auto }. That 2px nudge pushes the row past the
   container's right edge, so a horizontal scrollbar appeared (and the layout
   jumped) every time the pointer crossed a row.
   Reserving the same 2px inside the scroller means the shifted row still
   fits. The genuine overflow — .tv-stand has min-width: 600px, so narrow
   screens really do need to scroll — is untouched. */
.tv-stand { padding-right: 3px; }

/* --- a team's roster pills inside .tv-teamcard ---
   cs_render_team_roster() is reused verbatim from the old page; this is the
   theme dressing for what it emits. The card is a grid, so the roster spans
   every column rather than being squeezed into the last one. */
.tv-teamcard .ladder-roster {
     grid-column: 1 / -1;
     display: flex;
     flex-wrap: wrap;
     gap: .3rem;
     margin-top: .55rem;
}
.tv-teamcard .lr-member {
     display: inline-flex;
     align-items: center;
     gap: .22rem;
     padding: .16rem .45rem;
     font-family: var(--mono);
     font-size: .56rem;
     letter-spacing: .04em;
     color: var(--muted);
     background: var(--panel-2);
     box-shadow: inset 0 0 0 1px var(--line);
     clip-path: var(--chamfer);
     text-decoration: none;
}
a.tv-teamcard .lr-member:hover,
.tv-teamcard a.lr-member:hover { color: var(--text); box-shadow: inset 0 0 0 1px var(--blue); }
.tv-teamcard .lr-member--cap { color: var(--text); box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--brass) 50%, transparent); }
.tv-teamcard .lr-star { color: var(--brass); }

/* --- rules, rendered as stacked numbered lists inside the Info panel --- */
.tv-rules__h {
     font-family: var(--mono);
     font-size: .62rem;
     letter-spacing: .1em;
     text-transform: uppercase;
     color: var(--muted);
     margin: 1rem 0 .35rem;
}
.tv-rules__h:first-of-type { margin-top: 0; }

/* --- screenshots (the fifth tab) --- */
.ss-upload-bar {
     display: flex;
     align-items: center;
     gap: .7rem;
     flex-wrap: wrap;
     margin-bottom: 1rem;
}
.ss-quota { font-family: var(--mono); font-size: .62rem; letter-spacing: .06em; color: var(--muted); }
.ss-grid {
     display: grid;
     grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
     gap: .6rem;
}
.ss-item { position: relative; margin: 0; }
.ss-item img {
     width: 100%;
     aspect-ratio: 16 / 9;
     object-fit: cover;
     display: block;
     cursor: zoom-in;
     background: var(--panel-2);
     box-shadow: inset 0 0 0 1px var(--line);
     clip-path: var(--chamfer);
}
.ss-item img:hover { box-shadow: inset 0 0 0 1px var(--blue); }
.ss-del {
     position: absolute;
     top: .3rem;
     right: .3rem;
     width: 22px;
     height: 22px;
     display: grid;
     place-items: center;
     border: 0;
     cursor: pointer;
     color: #fff;
     background: rgba(0, 0, 0, .6);
     clip-path: var(--chamfer);
}
.ss-del:hover { background: var(--red); }

/* Lightbox. z-index clears the theme's modal shell (111) since it's a
   separate overlay that can be opened from inside the media panel. */
.ss-modal {
     position: fixed;
     inset: 0;
     z-index: 130;
     display: none;
     align-items: center;
     justify-content: center;
     padding: 1.25rem;
     background: rgba(3, 4, 7, .82);
     backdrop-filter: blur(4px);
}
.ss-modal[aria-hidden="false"] { display: flex; }
.ss-modal-inner {
     position: relative;
     width: min(980px, 100%);
     max-height: 90vh;
     overflow: auto;
     background: var(--panel-2);
     box-shadow: inset 0 0 0 1px var(--line);
     clip-path: var(--chamfer);
}
.ss-modal-close {
     position: absolute;
     top: .5rem;
     right: .5rem;
     z-index: 1;
     width: 30px;
     height: 30px;
     border: 0;
     cursor: pointer;
     font-size: 1.1rem;
     color: var(--text);
     background: rgba(0, 0, 0, .55);
     clip-path: var(--chamfer);
}
.ss-modal-stage { background: #05070b; }
.ss-modal-stage img { width: 100%; max-height: 70vh; object-fit: contain; display: block; }
.ss-modal-meta { padding: .9rem 1.1rem 1.1rem; }
.ss-modal-by { font-family: var(--display); font-style: italic; font-weight: 800; color: var(--text); }
.ss-modal-cap2 { font-size: .84rem; color: var(--muted); margin-top: .2rem; }
.ss-share-row { display: flex; flex-wrap: wrap; gap: .4rem; margin-top: .8rem; }
.ss-share-row input {
     flex: 1 1 200px;
     min-width: 0;
     font-family: var(--mono);
     font-size: .62rem;
     color: var(--muted);
     background: var(--panel);
     border: 0;
     box-shadow: inset 0 0 0 1px var(--line);
     padding: .45rem .6rem;
     clip-path: var(--chamfer);
}
.ss-share-btn {
     font-family: var(--mono);
     font-size: .6rem;
     letter-spacing: .06em;
     text-transform: uppercase;
     color: var(--text);
     background: var(--panel);
     box-shadow: inset 0 0 0 1px var(--line);
     border: 0;
     cursor: pointer;
     padding: .45rem .7rem;
     clip-path: var(--chamfer);
     text-decoration: none;
}
.ss-share-btn:hover { box-shadow: inset 0 0 0 1px var(--blue); }

/* A "registered" pill reuses .btn for sizing but isn't interactive. */
.pf-actions .btn.is-static { cursor: default; pointer-events: none; opacity: .85; }

/* --- JOIN / FOLLOW dialogs (.bkt-modal-*) ---
   These two dialogs predate the theme's shared #modal shell and are driven by
   their own script: openJoinModal() / window.openFollowModal() toggle
   `.is-open` on the overlay. Their styling used to live in the page's own
   <style> block, which the 2026 rebuild removed — leaving them with no
   position and no hidden state, so both rendered inline at the bottom of the
   page instead of as modals. This is that styling, in theme vocabulary.

   Kept as their own overlay rather than ported onto the shared shell because
   the follow dialog loads its rows over the network and both carry bespoke
   submit gating; re-plumbing them is a separate job from making them display. */
.bkt-modal-overlay {
     position: fixed;
     inset: 0;
     z-index: 120;
     display: none;
     align-items: center;
     justify-content: center;
     padding: 1.25rem;
     background: rgba(3, 4, 7, .72);
     backdrop-filter: blur(4px);
}
.bkt-modal-overlay.is-open { display: flex; }
.bkt-modal {
     position: relative;
     width: min(520px, 100%);
     max-height: 88vh;
     overflow-y: auto;
     background: var(--panel-2);
     box-shadow: inset 0 0 0 1px var(--line), 0 24px 60px -24px rgba(0, 0, 0, .85);
     clip-path: var(--chamfer);
     padding: 1.3rem;
}
.bkt-modal-close {
     position: absolute;
     top: .55rem;
     right: .55rem;
     width: 30px;
     height: 30px;
     display: grid;
     place-items: center;
     font-size: 1.2rem;
     line-height: 1;
     color: var(--muted);
     background: none;
     border: 0;
     cursor: pointer;
}
.bkt-modal-close:hover { color: var(--text); }

.bkt-modal-header { display: flex; align-items: flex-start; gap: .8rem; margin-bottom: 1rem; padding-right: 2rem; }
.bkt-modal-icon { font-size: 1.6rem; line-height: 1; flex: none; }
.bkt-modal-title-block { min-width: 0; }
.bkt-modal-title {
     font-family: var(--display);
     font-weight: 800;
     font-style: italic;
     font-size: 1.15rem;
     color: var(--text);
     margin: 0;
}
.bkt-modal-subtitle { font-size: .82rem; color: var(--muted); margin-top: .15rem; }
.bkt-modal-body { display: flex; flex-direction: column; gap: .9rem; }

.bkt-modal-info { display: flex; flex-wrap: wrap; gap: .35rem; }
.bkt-modal-info-pill {
     font-family: var(--mono);
     font-size: .58rem;
     letter-spacing: .06em;
     text-transform: uppercase;
     color: var(--muted);
     background: var(--panel);
     box-shadow: inset 0 0 0 1px var(--line);
     clip-path: var(--chamfer);
     padding: .2rem .5rem;
}

/* Entry-fee box — brass, because it's money owed to the host, not a warning. */
.bkt-modal-fee-box {
     background: color-mix(in srgb, var(--brass) 10%, var(--panel));
     box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--brass) 38%, transparent);
     clip-path: var(--chamfer);
     padding: .85rem 1rem;
}
.bkt-modal-fee-title {
     font-family: var(--mono);
     font-size: .58rem;
     letter-spacing: .1em;
     text-transform: uppercase;
     color: var(--muted);
}
.bkt-modal-fee-amount {
     font-family: var(--display);
     font-weight: 900;
     font-style: italic;
     font-size: 1.5rem;
     color: var(--brass);
     line-height: 1.1;
}
.bkt-modal-fee-note { font-size: .78rem; color: var(--muted); margin-top: .3rem; }
.bkt-modal-fee-link { display: inline-block; margin-top: .5rem; font-size: .8rem; color: var(--blue); }

.bkt-modal-reqs-title {
     font-family: var(--mono);
     font-size: .6rem;
     letter-spacing: .1em;
     text-transform: uppercase;
     color: var(--muted);
     margin-bottom: .35rem;
}
.bkt-modal-reqs { margin: 0; padding-left: 1.1rem; font-size: .84rem; color: var(--text); }
.bkt-modal-reqs li { margin-bottom: .25rem; }

.bkt-modal-checks { display: flex; flex-direction: column; gap: .5rem; }
.bkt-check-label {
     display: flex;
     align-items: flex-start;
     gap: .55rem;
     font-size: .84rem;
     color: var(--text);
     cursor: pointer;
}
.bkt-check-label input { margin-top: .15rem; width: 16px; height: 16px; accent-color: var(--blue); cursor: pointer; }

.bkt-modal-submit {
     width: 100%;
     font-family: var(--display);
     font-weight: 800;
     font-style: italic;
     font-size: .95rem;
     color: #fff;
     background: linear-gradient(160deg, var(--red), var(--red-deep));
     border: 0;
     cursor: pointer;
     padding: .7rem 1rem;
     clip-path: var(--chamfer);
}
.bkt-modal-submit:disabled { opacity: .4; cursor: not-allowed; }
.bkt-modal-cancel {
     width: 100%;
     font-size: .84rem;
     color: var(--muted);
     background: var(--panel);
     border: 0;
     box-shadow: inset 0 0 0 1px var(--line);
     cursor: pointer;
     padding: .55rem 1rem;
     clip-path: var(--chamfer);
}
.bkt-modal-cancel:hover { color: var(--text); box-shadow: inset 0 0 0 1px var(--blue); }

/* Follow dialog rows — "follow as yourself" and one per clan you manage.
   Higher specificity than the page's surviving inline <style>, which still
   paints these in the pre-2026 palette. */
.bkt-modal .follow-row,
.bkt-modal .follow-clan-row {
     background: var(--panel);
     border: 0;
     box-shadow: inset 0 0 0 1px var(--line);
     border-radius: 0;
     clip-path: var(--chamfer);
}
.bkt-modal .follow-row-title { color: var(--text); }
.bkt-modal .follow-row-sub { color: var(--muted); }
.bkt-modal .follow-row-btn {
     border: 0;
     border-radius: 0;
     clip-path: var(--chamfer);
     color: var(--blue);
     background: color-mix(in srgb, var(--blue) 14%, transparent);
     box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--blue) 45%, transparent);
}
.bkt-modal .follow-row-btn:hover { background: color-mix(in srgb, var(--blue) 24%, transparent); }
.bkt-modal .follow-row-btn.is-following {
     color: var(--muted);
     background: var(--panel-2);
     box-shadow: inset 0 0 0 1px var(--line);
}

/* ============ LEADERBOARD (/leaderboard) — additions ============ */

/* The mock's .avatar--round holds two initials over a gradient. Real clans
   here have uploaded icons, so the same box holds an <img> when one exists
   and falls back to the monogram when it doesn't. overflow+object-fit stop
   a non-square upload distorting or spilling past the round mask. */
.lbrow .avatar--round { overflow: hidden; }
.lbrow .avatar--round img {
     width: 100%;
     height: 100%;
     object-fit: cover;
     display: block;
}

/* The ladder's "no results" line is a panel child, not a grid item — give
   it the breathing room the finder pages get from .results. */
.lbpanel .results__empty { padding: 2rem 1rem; }

/* ============ PLAYER FINDER (/users) — additions ============ */

/* --- avatar: real image, not a monogram ---
   The theme styles .usercard__avatar as a flex box centring two initials,
   which is what the mock's static markup contains. Real users here have
   uploaded avatars, so the box holds an <img> instead and the initials are
   only the fallback. object-fit stops non-square uploads distorting; the
   parent already supplies the chamfer and the ring, so the image just has
   to fill it edge to edge. */
.usercard__avatar img {
     width: 100%;
     height: 100%;
     object-fit: cover;
     display: block;
}

/* The banner is a flex row with space-between, built to hold a status chip
   on the left and a level chip on the right. This site has no level, so the
   status would stretch across the whole row on its own. */
.usercard__banner > :only-child { margin-right: auto; }

/* --- the user's own banner image ---
   The theme paints .usercard__banner as a gradient from the card's --u
   accent. Real users have uploaded banners, so the image goes UNDERNEATH
   that gradient rather than replacing it: layering keeps the accent colour
   reading through, keeps the ONLINE/AWAY chip legible over an arbitrary
   photo, and means a card still looks right when the image is the site
   default. background-image stacks front-to-back, so the gradient is listed
   first. Only applies when the markup opts in with .has-img, so a banner
   with no --banner-img can't collapse to url(). */
.usercard__banner.has-img {
     background-image:
          linear-gradient(120deg,
               color-mix(in srgb, var(--u, #E8002D) 62%, transparent),
               color-mix(in srgb, #0a0d14 78%, transparent)),
          var(--banner-img);
     background-size: cover, cover;
     background-position: center, center;
     background-repeat: no-repeat, no-repeat;
}
@supports not (color: color-mix(in srgb, red 50%, blue)) {
     /* color-mix is the whole trick above; without it the gradient layer
        would be dropped and the chip could sit on bare photo. */
     .usercard__banner.has-img {
          background-image:
               linear-gradient(120deg, rgba(10, 13, 20, .55), rgba(10, 13, 20, .8)),
               var(--banner-img);
     }
}

/* ============ CLAN FINDER (/clans) — additions ============ */

/* --- sticky filter bar: NOW OWNED BY clanstop.css ---
   This used to carry a global `.filterbar { position: sticky }` rule, added
   back when the theme's own markup comment promised a "sticky auto-hide
   filter toolbar" but shipped .filterbar as a plain flex row.

   The designer's stylesheet implements it properly now — scoped to
   [data-page="clans"] and [data-page="users"], with --filter-top, .is-stuck
   and .is-hidden. Keeping the old rule here would do active harm: being
   unscoped it also stuck the bar on /tournaments, which the designer's
   comment explicitly says should keep a static bar.

   The scroll behaviour that drives .is-stuck / .is-hidden lives in
   js/clanstop-shell.js — the theme ships the CSS but not the JS, and both
   finders need identical behaviour, so it belongs in shared chrome rather
   than duplicated inline on two pages. */

/* --- multi-value filter as a native disclosure ---
   Platform can hold several values, so it can't be a <select> like the
   other chips. <details>/<summary> gives a real dropdown with no JS: the
   summary is the chip, the panel holds the checkboxes. */
.filterdrop { position: relative; }
.filterdrop > summary {
     list-style: none;
     cursor: pointer;
}
.filterdrop > summary::-webkit-details-marker { display: none; }
.filterdrop[open] > summary .caret { transform: rotate(180deg); }
.filterdrop > summary .caret { display: inline-block; transition: transform .15s ease; }
.filterdrop__panel {
     position: absolute;
     top: calc(100% + .4rem);
     left: 0;
     z-index: 45;
     min-width: 220px;
     padding: .7rem;
     background: var(--panel-2);
     box-shadow: inset 0 0 0 1px var(--line), 0 18px 40px rgba(0, 0, 0, .55);
     clip-path: var(--chamfer);
}

/* --- games picker modal ---
   .modal-overlay / .modal-box / .modal-title / .modal-message /
   .modal-actions are already dressed further up this file. These inner
   pieces were not: they were styled in css/style.css, which this page
   stopped loading when it moved to the 2026 theme, so the modal opened
   unformatted. */
.modal-box .modal-content { margin-bottom: .9rem; }
.modal-open-button,
.modal-box #game-search {
     width: 100%;
     font: inherit;
     color: var(--text);
     background: var(--panel-2);
     box-shadow: inset 0 0 0 1px var(--line);
     border: none;
     padding: .7rem .9rem;
     clip-path: polygon(9px 0, 100% 0, 100% calc(100% - 9px), calc(100% - 9px) 100%, 0 100%, 0 9px);
}
.modal-box #game-search:focus { outline: none; box-shadow: inset 0 0 0 1px var(--blue); }

/* Chips for what's picked so far, above the list. */
.selected-games-display {
     display: flex;
     flex-wrap: wrap;
     gap: .35rem;
     min-height: 1.6rem;
     margin: .7rem 0;
}
.selected-games-display .game-chip {
     padding: .25rem .6rem;
     font-family: var(--mono);
     font-size: .62rem;
     letter-spacing: .06em;
     text-transform: uppercase;
     color: var(--text);
     background: var(--panel);
     box-shadow: inset 0 0 0 1px var(--blue);
     clip-path: polygon(5px 0, 100% 0, calc(100% - 5px) 100%, 0 100%);
     cursor: pointer;
}
.selected-games-display .game-chip:hover { color: var(--red-text); box-shadow: inset 0 0 0 1px var(--red); }

/* The scrollable game grid. Height is bounded so the modal's actions stay
   reachable no matter how many games the catalogue holds. */
.game-list-container {
     max-height: min(46vh, 380px);
     overflow-y: auto;
     margin: .2rem 0 1rem;
     padding-right: .2rem;
}
.game-list-container ul {
     list-style: none;
     margin: 0;
     padding: 0;
     display: grid;
     grid-template-columns: repeat(auto-fill, minmax(104px, 1fr));
     gap: .5rem;
}
.game-list-item { position: relative; }
.game-list-item .game-item-content {
     display: flex;
     flex-direction: column;
     gap: .35rem;
     cursor: pointer;
}
.game-list-item .game-icon-bg {
     position: relative;
     aspect-ratio: 15 / 8;
     background-color: var(--panel);
     box-shadow: inset 0 0 0 1px var(--line);
     clip-path: polygon(7px 0, 100% 0, 100% calc(100% - 7px), calc(100% - 7px) 100%, 0 100%, 0 7px);
     transition: box-shadow .15s ease;
}
.game-list-item:hover .game-icon-bg { box-shadow: inset 0 0 0 1px var(--blue); }
.game-list-item .selection-overlay {
     position: absolute;
     inset: 0;
     display: none;
     align-items: center;
     justify-content: center;
     background: rgba(0, 166, 255, .45);
     color: #fff;
}
.game-list-item .selection-overlay svg { width: 26px; height: 26px; }
.game-list-item.selected .selection-overlay { display: flex; }
.game-list-item.selected .game-icon-bg { box-shadow: inset 0 0 0 2px var(--blue); }
.game-list-item .game-name {
     font-size: .72rem;
     line-height: 1.25;
     color: var(--muted);
     text-align: center;
}
.game-list-item.selected .game-name { color: var(--text); }
/* --- game tile crest, for a game with no artwork ---
   SupportedGames carries no colour column, so there is nothing per-game to
   read. Both pickers (this page's and /clans') used to fall back to a
   placehold.co image tinted by a g.color that was always undefined — one
   external request per tile, and every artless game the same grey-blue.
   The tile now draws itself from a --game-accent the JS derives from the
   game's own short name, so the grid reads as distinct games and makes no
   third-party requests. */
.game-icon-bg--crest {
     background:
       radial-gradient(120% 140% at 78% 8%, color-mix(in srgb, var(--game-accent, var(--blue)) 55%, transparent), transparent 68%),
       repeating-linear-gradient(-55deg, rgba(255,255,255,.05) 0 8px, transparent 8px 20px),
       linear-gradient(140deg, color-mix(in srgb, var(--game-accent, var(--blue)) 28%, #0b0e15), #0b0e15 78%);
     display: grid;
     place-items: center;
}
.game-icon-bg__mark {
     font-family: var(--display);
     font-weight: 900;
     font-style: italic;
     font-size: .8rem;
     letter-spacing: .04em;
     text-transform: uppercase;
     color: #fff;
     text-shadow: 0 1px 4px rgba(0, 0, 0, .55);
     padding: 0 .35rem;
     text-align: center;
     line-height: 1.1;
}
/* The check overlay sits above the crest. */
.game-icon-bg--crest .selection-overlay { grid-area: 1 / 1; }
.game-icon-bg--crest .game-icon-bg__mark { grid-area: 1 / 1; }

/* --- the same tiles inside the shared picker modal (/users) ---
   /clans and /createclan hand-roll their games modal and mark a chosen tile
   with a .selected class. /users uses the shared picker in
   js/clanstop-shell.js, which speaks aria-pressed instead. Rather than
   teach the shell a second vocabulary — it is generic machinery, and
   "selected" means nothing to it — the selected-state rules above are
   mirrored onto [aria-pressed="true"] here, so one set of tile styles
   serves all three modals.

   The tiles are <button>s here (the picker's contract) where the other two
   use <li><label>, hence the reset: a button brings its own background,
   border, padding and centred text. */
/* .picker-list is display:flex/column in the theme. Same specificity as
   this, so it only loses because clanstop-app.css is linked after
   clanstop.css in index.php. */
.picker-list--tiles {
     display: grid;
     grid-template-columns: repeat(auto-fill, minmax(104px, 1fr));
     gap: .5rem;
}

/* Strip the theme's .picker-opt row chrome off a tile.
   .picker-opt is a full-width flex ROW with its own panel background, inset
   ring, chamfer and padding — everything a tile must not have, because on
   /clans the ring and the chamfer belong to .game-icon-bg inside the tile,
   not to the tile's outer box. Leaving any of it on is what made these look
   like list rows wearing artwork.

   Every property here is undoing something .picker-opt sets; none of it is
   decoration. Note display: block — without it the button stays a flex row
   and the artwork/label column inside can't size itself. */
.picker-list--tiles .game-list-item {
     display: block;
     appearance: none;
     -webkit-appearance: none;
     background: none;
     box-shadow: none;
     clip-path: none;
     border: 0;
     padding: 0;
     margin: 0;
     font: inherit;
     color: inherit;
     text-align: left;
     cursor: pointer;
     transition: none;
}
/* .picker-opt:hover and .picker-opt[aria-pressed="true"] paint a blue and a
   RED ring on the outer box. /clans marks selection with a blue ring on the
   artwork instead, so both are cancelled here and re-expressed below on
   .game-icon-bg. The [aria-pressed] selector is (0,2,0), hence the extra
   class on ours to win outright rather than on source order. */
.picker-list--tiles .game-list-item:hover,
.picker-list--tiles .game-list-item[aria-pressed="true"] {
     box-shadow: none;
     background: none;
}
.picker-list--tiles .game-item-content { display: flex; flex-direction: column; gap: .35rem; }
/* .picker-opt__name is flex:1 + weight 500 for the row layout; the tile
   caption is a centred block. The class stays because shell.js's search
   reads its textContent to decide what matches. */
.picker-list--tiles .game-name.picker-opt__name {
     flex: none;
     font-weight: 600;
}
.picker-list--tiles .game-list-item[aria-pressed="true"] .selection-overlay { display: flex; }
.picker-list--tiles .game-list-item[aria-pressed="true"] .game-icon-bg { box-shadow: inset 0 0 0 2px var(--blue); }
.picker-list--tiles .game-list-item[aria-pressed="true"] .game-name { color: var(--text); }
/* Keyboard focus has to be visible: the tile is a real button, and the
   selection ring alone doesn't say "focused". */
.picker-list--tiles .game-list-item:focus-visible { outline: 2px solid var(--blue); outline-offset: 3px; }
/* The empty-search message is a grid child; let it span the full row
   instead of being squeezed into one 104px column. */
.picker-list--tiles .picker-empty { grid-column: 1 / -1; }

/* "Display selected only" — the picker's optional filter toggle. Matches
   the equivalent control in the /clans games modal. */
.picker-onlysel {
     display: flex;
     align-items: center;
     gap: .5rem;
     margin: .2rem 0 .1rem;
     font-size: .8rem;
     color: var(--muted);
     cursor: pointer;
}
.picker-onlysel input { width: 16px; height: 16px; accent-color: var(--blue); cursor: pointer; }
/* The chips row hides itself when nothing is picked, so it must not keep
   reserving the min-height the standalone modal's version does. */
.selected-games-display[hidden] { display: none; }
/* The grid keeps its own bounded scroller inside the shared modal, exactly
   as it has in the standalone one.

   Releasing it here (max-height:none) let the whole dialog grow to the
   theme's 86vh cap, so the games modal on /users stood noticeably taller
   than the identical modal on /clans. Bounding the grid instead is what
   keeps the two the same size — and it's also what keeps the search box
   and the Apply/Clear footer on screen while you scroll a long catalogue,
   which is the reason /clans bounds it in the first place. */
.modal__body .game-list-container {
     max-height: min(46vh, 380px);
     overflow-y: auto;
}

/* ==========================================================================
   DISCORD BOT PAGE  (/discord)
   clanstop.css ships the whole .dc-* set. These are the two hooks the live
   page needs that the static comp had no reason to define: the optional
   argument chip inside a slash-command name, and the note the hero shows a
   signed-in user who manages no clans.
   ========================================================================== */
.dc-cmd__arg {
     font-family: var(--mono);
     font-size: .78rem;
     font-weight: 500;
     color: var(--muted);
}

.dc-hero__note {
     max-width: 52ch;
     margin: 1.1rem auto 0;
     padding: .55rem .9rem;
     background: var(--panel-2);
     box-shadow: inset 0 0 0 1px var(--line);
     clip-path: var(--chamfer);
     color: var(--muted);
     font-size: .82rem;
     line-height: 1.5;
}

/* ==========================================================================
   CTA BAND - mobile overflow
   .cta-band__actions is flex:none, so it sizes to max-content and never
   shrinks. One button fits a phone; two do not - on /discord the actions row
   measured 463px inside a 258px content box, which is what let the page
   scroll sideways. Give the row its own line below 640px (its flex-wrap
   already handles the buttons once it is allowed to be narrow) and trim the
   band padding, which was tuned for desktop.
   ========================================================================== */
@media (max-width: 640px) {
     .cta-band {
          padding: 0 1rem;
     }

     .cta-band__inner {
          padding: 1.9rem 1.4rem;
          gap: 1.4rem;
     }

     .cta-band__actions {
          flex: 1 1 100%;
          min-width: 0;
     }

     .cta-band__actions > .btn {
          flex: 1 1 auto;
          min-width: 0;
          justify-content: center;
          text-align: center;
          white-space: normal;
     }
}

/* ==========================================================================
   PROFILE / CLAN SIDE RAIL - collapsible below 920px
   Pairs with the rail block in js/clanstop-shell.js. display:none (not
   visibility) matters on desktop: .pf-body is a 300px|1fr grid, so a third
   visible child would claim a row of its own.
   ========================================================================== */
.pf-railtoggle {
     display: none;
}

@media (max-width: 920px) {
     .pf-railtoggle {
          display: flex;
          align-items: center;
          justify-content: space-between;
          gap: .8rem;
          width: 100%;
          padding: .8rem 1rem;
          background: var(--panel);
          box-shadow: inset 0 0 0 1px var(--line);
          clip-path: polygon(12px 0, 100% 0, 100% calc(100% - 12px), calc(100% - 12px) 100%, 0 100%, 0 12px);
          color: var(--text);
          font-family: var(--mono);
          font-size: .68rem;
          letter-spacing: .08em;
          text-transform: uppercase;
          cursor: pointer;
          border: 0;
          -webkit-appearance: none;
          appearance: none;
     }

     .pf-railtoggle:hover,
     .pf-railtoggle:focus-visible {
          color: var(--gold, var(--brass));
     }

     .pf-railtoggle__label::before {
          content: "// ";
          color: var(--muted);
     }

     .pf-railtoggle__chev {
          width: 8px;
          height: 8px;
          flex: none;
          border-right: 2px solid currentColor;
          border-bottom: 2px solid currentColor;
          transform: rotate(45deg) translate(-2px, -2px);
          transition: transform .18s ease;
     }

     .pf-railtoggle[aria-expanded="true"] .pf-railtoggle__chev {
          transform: rotate(-135deg) translate(-2px, -2px);
     }

     /* Closed is the default, so the tab panel is the first thing under the
        tab strip. The rail is one tap away and the choice is remembered. */
     .pf-body:not(.pf-rail-open) > .pf-rail {
          display: none;
     }
}

/* ==========================================================================
   EVENT CALENDAR (/calendar)
   clanstop.css ships the whole .cal-* / .ce-* set. These are the gaps between
   the comp and the live page: the comp used <button>s and a hardcoded array,
   the page uses <a>s with real hrefs so the month works without JS.
   ========================================================================== */

/* [hidden] loses to .cal-ev{display:block} / .cal-more, so the type filter
   would have had no effect at all without these two. */
.cal-ev[hidden],
.cal-more[hidden],
.ce-facts > div[hidden] {
     display: none !important;
}

/* Nav and events are links here, not buttons. */
.cal-navbtn {
     display: inline-flex;
     align-items: center;
     text-decoration: none;
     line-height: 1.5;
}

.cal-ev {
     text-decoration: none;
}

/* .cal-more is a real <button>; clanstop.css styles its text but assumes the
   comp's <span>, so the UA chrome still has to go. */
.cal-more {
     background: none;
     border: 0;
     text-align: left;
     align-self: flex-start;
     -webkit-appearance: none;
     appearance: none;
}

/* Type badge in the detail modal. clanstop.css sizes .ce-type but leaves the
   colour to the page, which is what the comp did inline. */
.ce-type {
     color: var(--ec, var(--muted));
     box-shadow: inset 0 0 0 1px var(--ec, var(--line));
}

.ce-type--war         { --ec: var(--red); }
.ce-type--battle      { --ec: var(--blue); }
.ce-type--tournament  { --ec: var(--brass); }
.ce-type--event       { --ec: #B36BFF; }

.cal-empty {
     margin-top: 1.1rem;
     padding: 1.1rem 1rem;
     background: var(--panel);
     box-shadow: inset 0 0 0 1px var(--line);
     clip-path: var(--chamfer);
     color: var(--muted);
     text-align: center;
}

.cal-hint {
     margin-top: .8rem;
     color: var(--muted);
     font-size: .78rem;
     line-height: 1.5;
}
