Files
RosterChief/assets/controlpanel.css
Bernard Siebens 7ab7ac1982 Fix control panel dropdown text clipping at the bottom of the field
.select's content box (2.125rem height minus padding and border) left
only ~16px for a 14px line -- descenders (g/y/p/q) rendered clipped in
some browsers, most visibly on the Competition edit modal's Sport
dropdown. Bumped .input/.select to 2.25rem for headroom, and .btn/
.btn-square the same amount so a button next to a field in the same
row still lines up -- .btn itself never had the clipping problem
(flex centring), this is purely for row alignment.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ECGMEwrc2k4D8VQuwjstj9
2026-08-20 22:47:35 +02:00

1074 lines
35 KiB
CSS

/* RosterChief control panel — a deliberately separate stylesheet.
*
* The control panel is a distinct surface (see design_handoff_rosterchief_platform/README.md):
* industrial, dark command bar over a light workspace, its own type system (Barlow /
* Barlow Condensed / IBM Plex Mono) and colour tokens, and — per that handoff — "never
* club-branded". It does not import assets/app.css and does not load daisyUI: sharing either
* would mean the panel inherits the club-facing app's theme variables and component shapes,
* which is exactly what "standalone" rules out.
*
* One wrinkle: a handful of templates the control panel renders — `controlpanel/_form_fields.html`,
* `templatetags/field.html`, and the `daisy`/`form_field` template tags in
* `controlpanel/templatetags/ui.py` — are also used by the sitewide login/MFA templates
* (templates/account/, templates/allauth/), so their *markup* is off limits here (changing it
* would reskin the login page too). Those templates emit daisyUI-shaped class names
* (`input`, `select`, `textarea`, `checkbox`, `toggle`, `file-input`, `btn`, `badge`, `card`,
* `table`, `modal`, `alert`, `avatar`, `progress`, `form-control`, `label`, `divide-y`, `link`)
* without daisyUI itself supplying their CSS (it isn't loaded here). So this file hand-rolls
* industrial-styled definitions for exactly that class vocabulary — same names, new look —
* scoped to controlpanel templates only via @source below, so nothing here reaches the
* pages built on assets/app.css.
*/
@import "tailwindcss";
@source "../controlpanel";
@theme {
--font-display: "Barlow Condensed", ui-sans-serif, system-ui, sans-serif;
--font-sans: "Barlow", ui-sans-serif, system-ui, sans-serif;
--font-mono: "IBM Plex Mono", ui-monospace, SFMono-Regular, monospace;
--color-ink: #0b1220; /* darkest -- command bar, sidebars, dark cards */
--color-navy: #101e36;
--color-steel: #1b2b47; /* inset controls on dark */
--color-hairline: #1e2b42; /* rules on dark surfaces */
--color-paper: #f4f5f7; /* app/page background */
--color-line: #e3e6eb; /* 1px borders on light */
--color-rule: #eef0f3; /* table row dividers */
--color-edge: #d6dae1; /* stronger light border -- control panel, inputs */
--color-stroke: #c9cfd8; /* secondary-button border */
--color-muted: #6c7787; /* secondary text */
--color-dim: #8b95a4; /* tertiary text, inactive icons */
--color-slate: #3a4658; /* body copy on light */
--color-on-dark: #93a0b4;
--color-on-dark-dim: #7c8aa0;
--color-on-dark-faint: #5c6b85;
/* Fixed platform brand red -- the control panel is never club-branded (per the design
handoff), so this is NOT the club.secondary_color CSS variable used elsewhere. */
--color-club: #e4002b;
--color-club-dark: #b00021;
--color-ice: #14b8e8;
--color-ice-ink: #04212c;
--color-ok: #14a05a;
--color-ok-bg: #e6f6ee;
--color-ok-border: #bfe7d3;
--color-ok-text: #0c7a43;
--color-warn: #f0a22e;
--color-warn-bg: #fff5e4;
--color-warn-border: #f6e0b8;
--color-warn-text: #9a6410;
--color-warn-deep: #7a4e08;
--color-danger-bg: #fdecec;
--color-danger-border: #f5c9ce;
--color-info-bg: #eaf7fc;
--color-info-border: #c3e7f4;
--color-info-text: #0a6f91;
--color-row-sel: #fff7f8; /* selected table row */
--color-row-focus: #f4f9ff; /* focused/active row */
--color-row-warn: #fffdf6; /* row needing attention */
--color-subhead: #f8f9fa; /* table header fill */
--radius-box: 0.25rem; /* control panel is square by intent -- 4px, not the usual rounded-xl */
}
/* --- fonts: self-hosted, same latin/latin-ext split as assets/app.css and the same
reasoning -- no request to Google's CDN on an EU club platform's admin surface. --- */
@font-face {
font-family: "Barlow";
font-style: normal;
font-weight: 400;
font-display: swap;
src: url("../fonts/barlow-latin-400-normal.woff2") format("woff2");
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
font-family: "Barlow";
font-style: normal;
font-weight: 400;
font-display: swap;
src: url("../fonts/barlow-latin-ext-400-normal.woff2") format("woff2");
unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
font-family: "Barlow";
font-style: normal;
font-weight: 500;
font-display: swap;
src: url("../fonts/barlow-latin-500-normal.woff2") format("woff2");
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
font-family: "Barlow";
font-style: normal;
font-weight: 500;
font-display: swap;
src: url("../fonts/barlow-latin-ext-500-normal.woff2") format("woff2");
unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
font-family: "Barlow";
font-style: normal;
font-weight: 600;
font-display: swap;
src: url("../fonts/barlow-latin-600-normal.woff2") format("woff2");
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
font-family: "Barlow";
font-style: normal;
font-weight: 600;
font-display: swap;
src: url("../fonts/barlow-latin-ext-600-normal.woff2") format("woff2");
unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
font-family: "Barlow";
font-style: normal;
font-weight: 700;
font-display: swap;
src: url("../fonts/barlow-latin-700-normal.woff2") format("woff2");
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
font-family: "Barlow";
font-style: normal;
font-weight: 700;
font-display: swap;
src: url("../fonts/barlow-latin-ext-700-normal.woff2") format("woff2");
unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
font-family: "Barlow Condensed";
font-style: normal;
font-weight: 600;
font-display: swap;
src: url("../fonts/barlow-condensed-latin-600-normal.woff2") format("woff2");
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
font-family: "Barlow Condensed";
font-style: normal;
font-weight: 600;
font-display: swap;
src: url("../fonts/barlow-condensed-latin-ext-600-normal.woff2") format("woff2");
unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
font-family: "Barlow Condensed";
font-style: normal;
font-weight: 700;
font-display: swap;
src: url("../fonts/barlow-condensed-latin-700-normal.woff2") format("woff2");
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
font-family: "Barlow Condensed";
font-style: normal;
font-weight: 700;
font-display: swap;
src: url("../fonts/barlow-condensed-latin-ext-700-normal.woff2") format("woff2");
unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
font-family: "Barlow Condensed";
font-style: normal;
font-weight: 800;
font-display: swap;
src: url("../fonts/barlow-condensed-latin-800-normal.woff2") format("woff2");
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
font-family: "Barlow Condensed";
font-style: normal;
font-weight: 800;
font-display: swap;
src: url("../fonts/barlow-condensed-latin-ext-800-normal.woff2") format("woff2");
unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
font-family: "IBM Plex Mono";
font-style: normal;
font-weight: 400;
font-display: swap;
src: url("../fonts/ibm-plex-mono-latin-400-normal.woff2") format("woff2");
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
font-family: "IBM Plex Mono";
font-style: normal;
font-weight: 400;
font-display: swap;
src: url("../fonts/ibm-plex-mono-latin-ext-400-normal.woff2") format("woff2");
unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
font-family: "IBM Plex Mono";
font-style: normal;
font-weight: 500;
font-display: swap;
src: url("../fonts/ibm-plex-mono-latin-500-normal.woff2") format("woff2");
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
font-family: "IBM Plex Mono";
font-style: normal;
font-weight: 500;
font-display: swap;
src: url("../fonts/ibm-plex-mono-latin-ext-500-normal.woff2") format("woff2");
unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
font-family: "IBM Plex Mono";
font-style: normal;
font-weight: 600;
font-display: swap;
src: url("../fonts/ibm-plex-mono-latin-600-normal.woff2") format("woff2");
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
font-family: "IBM Plex Mono";
font-style: normal;
font-weight: 600;
font-display: swap;
src: url("../fonts/ibm-plex-mono-latin-ext-600-normal.woff2") format("woff2");
unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* @layer base, not a bare rule: a bare `body { background: ... }` is unlayered CSS, which
* beats every Tailwind utility class regardless of specificity -- including a `bg-ink` on
* <body> itself (see controlpanel/templates/controlpanel/_auth_base.html), which would
* silently lose to this and always render var(--color-paper) instead. Layering it puts it
* where Tailwind's own base styles live, so a `bg-*`/`text-*` utility on <body> wins as
* expected; this only supplies the default for whichever page doesn't set one. */
@layer base {
body {
background: var(--color-paper);
color: var(--color-slate);
font-family: var(--font-sans);
}
}
/* --- component layer: the daisyUI-shaped class vocabulary the shared form/message
templates render (controlpanel/templates/controlpanel/_form_fields.html,
controlpanel/templates/templatetags/field.html, controlpanel/templatetags/ui.py), plus
the same names used directly in controlpanel's own page templates. Hand-rolled rather
than @plugin "daisyui" -- see the file banner. --- */
@layer components {
/* Buttons -- 34-36px desktop per the handoff; controlpanel is desktop-only. */
.btn {
display: inline-flex;
align-items: center;
justify-content: center;
gap: 0.5rem;
/* Matches .input/.select's own 2.25rem -- see that rule's comment; .btn itself
never had the clipping problem (flex centring), this is purely so a button
sitting next to a field in the same row lines up. */
height: 2.25rem;
padding-inline: 0.875rem;
border-radius: var(--radius-box);
font-family: var(--font-display);
font-weight: 800;
font-size: 0.8125rem;
letter-spacing: 0.1em;
text-transform: uppercase;
background: var(--color-ink);
color: #fff;
border: 1px solid transparent;
cursor: pointer;
transition: opacity 0.15s ease;
white-space: nowrap;
}
.btn:hover {
opacity: 0.85;
}
.btn:disabled {
opacity: 0.45;
cursor: not-allowed;
}
.btn-primary {
background: var(--color-club);
color: #fff;
}
.btn-success {
background: var(--color-ok);
color: #fff;
}
.btn-warning {
background: var(--color-warn);
color: var(--color-ink);
}
.btn-error {
background: var(--color-club-dark);
color: #fff;
}
.btn-ghost {
background: transparent;
color: var(--color-ink);
}
.btn-ghost:hover {
background: var(--color-rule);
opacity: 1;
}
.btn-outline {
background: #fff;
color: var(--color-ink);
border-color: var(--color-stroke);
}
.btn-outline:hover {
background: var(--color-subhead);
opacity: 1;
}
.btn-outline.btn-primary {
background: #fff;
color: var(--color-club-dark);
border-color: var(--color-danger-border);
}
.btn-outline.btn-error {
background: #fff;
color: var(--color-club-dark);
border-color: var(--color-danger-border);
}
.btn-outline.btn-success {
background: #fff;
color: var(--color-ok-text);
border-color: var(--color-ok-border);
}
/* accent -- allauth tags the passkey/security-key buttons "accent" (see
templates/allauth/elements/button.html and the manual btn-accent usages in
templates/account/login.html, password_change.html, mfa/authenticate.html).
Not one of the daisyUI semantic colours the rest of the app uses, so it gets
its own token: the ice cyan, which otherwise only appears on focus rings. */
.btn-accent {
background: var(--color-ice);
color: var(--color-ice-ink);
}
.btn-outline.btn-accent {
background: #fff;
color: var(--color-ice-ink);
border-color: var(--color-ice);
}
/* A bare text link shaped like a button-slot -- allauth's button element renders
this for anything tagged "link" (a lower-emphasis alternative action). */
.btn-link {
height: auto;
padding-inline: 0;
background: transparent;
color: var(--color-club-dark);
border-color: transparent;
text-transform: none;
letter-spacing: normal;
font-family: var(--font-sans);
font-weight: 600;
text-decoration: underline;
}
.btn-link:hover {
opacity: 1;
text-decoration: none;
}
.btn-soft {
background: color-mix(in oklab, currentColor 12%, transparent);
}
.btn-square {
padding-inline: 0;
width: 2.25rem;
}
.btn-sm {
height: 1.75rem;
padding-inline: 0.625rem;
font-size: 0.75rem;
}
.btn-xs {
height: 1.5rem;
padding-inline: 0.5rem;
font-size: 0.6875rem;
gap: 0.25rem;
}
/* Badges / status pills -- rounded-full per the handoff's "status pills" spec. */
.badge {
display: inline-flex;
align-items: center;
gap: 0.25rem;
padding: 0.15rem 0.6rem;
border-radius: 999px;
font-family: var(--font-display);
font-weight: 700;
font-size: 0.6875rem;
letter-spacing: 0.08em;
text-transform: uppercase;
background: var(--color-rule);
color: var(--color-slate);
border: 1px solid var(--color-line);
white-space: nowrap;
}
.badge-sm {
font-size: 0.625rem;
padding: 0.1rem 0.45rem;
}
.badge-xs {
font-size: 0.5625rem;
padding: 0.05rem 0.35rem;
}
.badge-success {
background: var(--color-ok-bg);
color: var(--color-ok-text);
border-color: var(--color-ok-border);
}
.badge-error {
background: var(--color-danger-bg);
color: var(--color-club-dark);
border-color: var(--color-danger-border);
}
.badge-warning {
background: var(--color-warn-bg);
color: var(--color-warn-text);
border-color: var(--color-warn-border);
}
.badge-info {
background: var(--color-info-bg);
color: var(--color-info-text);
border-color: var(--color-info-border);
}
.badge-ghost,
.badge-outline {
background: #fff;
color: var(--color-muted);
border-color: var(--color-edge);
}
.badge-neutral {
background: var(--color-ink);
color: #fff;
border-color: var(--color-ink);
}
/* Cards -- square by intent (4px radius), hairline border, no shadow. */
.card {
background: #fff;
border: 1px solid var(--color-edge);
border-radius: var(--radius-box);
}
.card-body {
padding: 1.125rem;
display: flex;
flex-direction: column;
gap: 0.5rem;
}
.card-title {
display: flex;
align-items: center;
gap: 0.5rem;
font-family: var(--font-display);
font-weight: 800;
font-size: 0.9375rem;
letter-spacing: 0.06em;
text-transform: uppercase;
color: var(--color-ink);
}
/* The action row a card/form/panel puts its buttons in -- allauth's own form and
panel elements (templates/allauth/elements/form.html, panel.html) render this
unconditionally, pairing it with justify-end plus a margin/padding utility that
varies per call site. */
.card-actions {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 0.5rem;
}
/* Tables -- CSS-grid-flavoured look via plain <table>: subhead fill, mono headings,
hairline row dividers. */
.table {
width: 100%;
border-collapse: collapse;
font-size: 0.8125rem;
}
.table thead tr {
background: var(--color-subhead);
border-bottom: 1px solid var(--color-line);
}
.table thead th {
padding: 0.5rem 0.875rem;
text-align: left;
font-family: var(--font-mono);
font-size: 0.6875rem;
letter-spacing: 0.06em;
text-transform: uppercase;
color: var(--color-muted);
font-weight: 500;
}
.table tbody tr {
border-bottom: 1px solid var(--color-rule);
}
.table tbody tr:last-child {
border-bottom: none;
}
.table tbody td {
padding: 0.625rem 0.875rem;
color: var(--color-ink);
vertical-align: middle;
}
/* Alerts -- left-accented banner, per the handoff's severity-bar language. */
.alert {
display: flex;
align-items: flex-start;
gap: 0.75rem;
padding: 0.875rem 1rem;
border-radius: var(--radius-box);
border: 1px solid var(--color-line);
background: #fff;
font-size: 0.875rem;
}
.alert-soft {
background: var(--color-warn-bg);
}
.alert-error {
background: var(--color-danger-bg);
border-color: var(--color-danger-border);
color: var(--color-club-dark);
}
.alert-warning {
background: var(--color-warn-bg);
border-color: var(--color-warn-border);
color: var(--color-warn-deep);
}
.alert-success {
background: var(--color-ok-bg);
border-color: var(--color-ok-border);
color: var(--color-ok-text);
}
.alert-info {
background: var(--color-info-bg);
border-color: var(--color-info-border);
color: var(--color-info-text);
}
/* Avatars -- fallback initials block; a real club.logo uses the <img> branch. */
.avatar > div {
border-radius: 999px;
overflow: hidden;
}
.avatar-placeholder > div {
display: flex;
align-items: center;
justify-content: center;
background: var(--color-ink);
color: #fff;
font-family: var(--font-display);
font-weight: 800;
}
/* Progress bars. */
progress.progress {
appearance: none;
width: 100%;
height: 0.375rem;
border-radius: 999px;
overflow: hidden;
background: var(--color-rule);
border: none;
}
progress.progress::-webkit-progress-bar {
background: var(--color-rule);
}
progress.progress::-webkit-progress-value {
background: var(--color-ink);
}
progress.progress::-moz-progress-bar {
background: var(--color-ink);
}
progress.progress-success::-webkit-progress-value {
background: var(--color-ok);
}
progress.progress-success::-moz-progress-bar {
background: var(--color-ok);
}
progress.progress-warning::-webkit-progress-value {
background: var(--color-warn);
}
progress.progress-warning::-moz-progress-bar {
background: var(--color-warn);
}
progress.progress-error::-webkit-progress-value {
background: var(--color-club);
}
progress.progress-error::-moz-progress-bar {
background: var(--color-club);
}
/* Form controls -- rounded-lg per the handoff's field spec (a touch softer than the
square 4px chrome, matching "buttons and inputs rounded-lg"). */
.input,
.select,
.textarea,
.file-input {
width: 100%;
border: 1px solid var(--color-edge);
border-radius: 0.5rem;
background: #fff;
color: var(--color-ink);
font-family: var(--font-sans);
font-size: 0.875rem;
padding: 0.5rem 0.75rem;
}
/* .input and .select get an explicit, identical height rather than relying on padding
plus the browser's own line-height to land on the same box: a <select> reserves extra
space for its native dropdown arrow and several browsers give it a different default
line-height than a text <input>, so the same padding alone does not guarantee the
same rendered height. appearance: none plus a hand-drawn arrow replaces the native
one, since that reserved space was part of what caused the mismatch.
2.25rem, not 2.125rem: at the tighter height, a <select>'s own vertical text
centring (unlike .btn's flex centring below, a <select> isn't a flex container)
left only ~16px of content box for a 14px line -- descenders (g/y/p/q) rendered
clipped at the bottom in some browsers. .btn is bumped the same amount purely
for row alignment (a filter bar mixing buttons and inputs) -- flex centring
itself was never the problem. */
.input,
.select {
height: 2.25rem;
}
.select {
padding-right: 2rem;
appearance: none;
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20' fill='none' stroke='%236c7787' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M5 7.5L10 12.5L15 7.5'/%3E%3C/svg%3E");
background-repeat: no-repeat;
background-position: right 0.6rem center;
background-size: 1rem;
}
.input:focus,
.select:focus,
.textarea:focus {
outline: 2px solid var(--color-ice);
outline-offset: -1px;
border-color: var(--color-ice);
}
.textarea {
height: auto;
min-height: 6rem;
}
.input-error,
.select-error,
.textarea-error {
border-color: var(--color-club);
color: var(--color-club-dark);
}
/* Taller variant for a code-entry input that has fallen back out of the boxed
.otp layout below (a recovery code is longer than the boxes fit). */
.input-lg {
height: 3rem;
font-family: var(--font-mono);
font-size: 1.125rem;
letter-spacing: 0.2em;
text-align: center;
}
/* Boxed one-time-code input -- recreates daisyUI's `otp`/`otp-lg` layout (a row of
character boxes with the real, accessible <input> sitting on top, invisible but still
focusable/typeable, so a tap or click still lands on it) without daisyUI. The boxes are
drawn as plain sibling <span>s -- see templates/allauth/elements/fields.html, which
keeps the exact daisyUI-shaped markup/class names because the same template also
renders on a club subdomain, where daisyUI itself (assets/app.css) still styles them.
A letter-spacing/text-indent overlay (faking alignment by spacing the real input's own
glyphs to match the box pitch) turned out too font-metric-fragile to land reliably --
drifted more with every character typed, in a way that resisted two rounds of tuning.
Instead the real input's text is made invisible (`color: transparent`, only the caret
shows) and controlpanel/templates/controlpanel/_auth_base.html's `data-otp` script
writes each typed character into its own <span>'s textContent -- exact by construction,
no font metrics involved. */
.otp {
position: relative;
display: inline-flex;
gap: 0.5rem;
}
.otp span {
display: flex;
align-items: center;
justify-content: center;
width: 2.75rem;
height: 3rem;
border: 1px solid var(--color-edge);
border-radius: 0.5rem;
background: #fff;
font-family: var(--font-mono);
font-size: 1.25rem;
color: var(--color-ink);
}
.otp:focus-within span {
border-color: var(--color-ice);
}
.otp input {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
padding: 0;
border: none;
outline: none;
background: transparent;
color: transparent;
caret-color: var(--color-ink);
}
.otp-lg span {
width: 3.25rem;
height: 3.5rem;
font-size: 1.375rem;
}
.checkbox {
width: 1.125rem;
height: 1.125rem;
border-radius: 0.25rem;
border: 1.5px solid var(--color-stroke);
accent-color: var(--color-club);
}
.toggle {
width: 2.5rem;
height: 1.375rem;
border-radius: 999px;
accent-color: var(--color-ok);
}
.form-control {
display: flex;
flex-direction: column;
}
/* Wraps a .label-text (+ an optional trailing badge/alt), a row by default -- see
templates/allauth/elements/field.html and fields.html, whose checkbox layout
pairs it with justify-start/gap-3 utilities for the checkbox-then-text order. */
.label {
display: flex;
align-items: center;
gap: 0.5rem;
padding-bottom: 0.375rem;
}
.label-text {
font-size: 0.8125rem;
font-weight: 600;
color: var(--color-slate);
}
.label-text-alt {
font-size: 0.75rem;
color: var(--color-muted);
}
/* Links inside table rows / prose. */
.link {
color: var(--color-club-dark);
text-decoration: none;
}
.link-hover:hover {
text-decoration: underline;
}
/* A plain horizontal rule between sections -- allauth's `hr` element
(templates/allauth/elements/hr.html) renders a bare `<div class="divider">`. */
.divider {
margin: 1.5rem 0;
border-top: 1px solid var(--color-line);
}
/* A native <details>/<summary> disclosure -- allauth's `details` element
(templates/allauth/elements/details.html), not currently exercised by any of
the pages this redesign covers, restyled defensively since it is shared
low-level allauth plumbing like the rest of templates/allauth/elements/. The
browser's own default marker on <summary> is left alone rather than replaced. */
.collapse {
border: 1px solid var(--color-line);
border-radius: var(--radius-box);
overflow: hidden;
}
.collapse-title {
padding: 0.75rem 1rem;
cursor: pointer;
font-weight: 600;
color: var(--color-ink);
}
.collapse-content {
padding: 0 1rem 1rem;
color: var(--color-slate);
}
.border-base-300 {
border-color: var(--color-line);
}
/* The command bar's account menu (controlpanel/base.html) -- a native
<details>/<summary> disclosure, so the only CSS it actually needs is hiding the
browser's own marker triangle; position/spacing are plain utilities at the call
site. */
.account-menu > summary {
list-style: none;
}
.account-menu > summary::-webkit-details-marker {
display: none;
}
/* --- daisyUI's semantic "base"/"content" vocabulary, aliased rather than renamed.
templates/allauth/elements/*.html, templates/allauth/layouts/base.html and
templates/403.html/maintenance.html are shared with the club-branded skin
(assets/app.css + real daisyUI, untouched, still in charge there), so they keep
daisyUI's own class names rather than controlpanel-specific ones. These give
those exact names an industrial-appropriate meaning for pages rendered through
this stylesheet; shadow-xl/shadow and .prose are deliberately left undefined --
no shadow and no auto-prose-spacing is the point of the square, hairline-bordered
look here, not a gap. --- */
.bg-base-100 {
background: #fff;
}
.border-base-content {
border-color: var(--color-line);
}
.border-base-content\/20 {
border-color: var(--color-line);
}
.text-base-content {
color: var(--color-slate);
}
.text-base-content\/70 {
color: var(--color-muted);
}
.text-base-content\/80 {
color: var(--color-slate);
}
.text-error,
.text-error-content {
color: var(--color-club-dark);
}
.text-warning {
color: var(--color-warn-deep);
}
.text-neutral-content {
color: #fff;
}
/* Divide-y utility (used by label/value stat lists). */
.divide-y > * + * {
border-top: 1px solid var(--color-rule);
}
/* Native <dialog>-based modals -- see controlpanel/templates/controlpanel/_modal_form.html
and _confirm_modal.html. No JS beyond the inline .showModal()/method=dialog already in
those templates; this only supplies the visual treatment daisyUI would otherwise. */
dialog.modal {
margin: auto;
padding: 0;
border: none;
border-radius: var(--radius-box);
background: transparent;
max-width: none;
max-height: none;
}
dialog.modal::backdrop {
background: rgba(11, 18, 32, 0.55);
}
dialog.modal .modal-box {
background: #fff;
border: 1px solid var(--color-edge);
border-radius: var(--radius-box);
padding: 1.5rem;
width: 32rem;
max-width: 91vw;
max-height: 85vh;
overflow-y: auto;
}
dialog.modal.modal-start {
margin: 0 0 0 auto;
height: 100vh;
max-height: none;
}
dialog.modal.modal-start .modal-box {
height: 100%;
max-height: none;
border-radius: 0;
border-left: 1px solid var(--color-edge);
}
.modal-action {
display: flex;
justify-content: flex-end;
gap: 0.5rem;
margin-top: 1.5rem;
}
.modal-backdrop {
display: none;
}
/* The club crest mark -- fallback for every club until a real Club.logo is uploaded.
A plain circle, same convention as .avatar-placeholder elsewhere in this file. Size
and colour are set per usage site (bg-* + explicit width/height utilities); this only
supplies the shape. */
.crest {
border-radius: 999px;
}
/* A club's own primary_color, applied as a ring around its uploaded logo -- scoped to a
locally-set --color-primary custom property (see club_detail.html) rather than a
page-wide override, because the control panel itself is never club-branded. */
.ring-primary {
box-shadow:
0 0 0 2px #fff,
0 0 0 4px var(--color-primary, var(--color-edge));
}
/* Sidebar menu (mobile nav drawer only -- see base.html). */
.menu li a {
display: flex;
align-items: center;
gap: 0.625rem;
padding: 0.5rem 0.75rem;
border-radius: 0.375rem;
font-family: var(--font-display);
font-weight: 700;
font-size: 0.875rem;
letter-spacing: 0.04em;
text-transform: uppercase;
color: var(--color-slate);
}
.menu li a:hover {
background: var(--color-rule);
}
.menu li a.menu-active {
background: var(--color-club);
color: #fff;
}
.menu-title {
padding: 0.5rem 0.75rem 0.25rem;
font-family: var(--font-mono);
font-size: 0.6875rem;
letter-spacing: 0.06em;
text-transform: uppercase;
color: var(--color-dim);
}
}
/* SubscriptionForm.notes (Change plan / Start billing modal), PlanForm.description (New/Edit
* plan modal) and FlagForm.note (Edit feature modal): templatetags/field.html's textarea
* branch hard-codes an `h-72` (18rem) utility class on every textarea with no per-field way
* to pass rows/height, so the only remaining hook is the rendered `name` attribute -- unique
* to these fields within the control panel (no other controlpanel form uses "notes",
* "description" or "note"). Kept unlayered, rather than inside `@layer components` above, so
* it beats the `.h-72` utility class regardless of selector specificity: Tailwind's utilities
* layer is declared after components and always wins at equal or lower specificity, and only
* an unlayered rule is guaranteed to outrank it. Every other textarea in the panel
* (maintenance message, etc.) is untouched, so this does not shrink anything else. */
textarea[name="notes"],
textarea[name="description"],
textarea[name="note"] {
height: 5rem;
}