Renders the real templates every send function uses (claim approved, membership invoice, invoice reminder) against hand-built placeholder context -- no real Member/ClubMembership/DuesInvoice row needed, so nothing here can leak real data. Each card shows the subject line and toggles between the HTML render (in an iframe, so the email's own markup can't clash with the page's) and the plain-text body, using the same bg-steel pill-toggle pattern as the news preview's NL/EN switch (generalised to .view-toggle-btn, kept .news-lang-btn as an alias). Admin-only, alongside Club identity in Settings. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ECGMEwrc2k4D8VQuwjstj9
2112 lines
64 KiB
CSS
2112 lines
64 KiB
CSS
/* RosterChief club management -- a deliberately separate stylesheet from both
|
|
* assets/app.css (the old daisyUI skin, still used by the auth/public chain via
|
|
* _club_base.html) and assets/controlpanel.css (the platform-staff surface, which is
|
|
* explicitly never club-branded).
|
|
*
|
|
* Management IS club-branded (see design_handoff_rosterchief_platform/README.md, "Club
|
|
* theming"): --color-club and --color-club-dark below resolve through --tenant-club /
|
|
* --tenant-club-dark custom properties, which management/templates/management/base.html
|
|
* sets per-request from Club.secondary_color -- everything else (structure, type,
|
|
* neutrals, status colours, table chrome, form fields) is locked platform-wide, same
|
|
* rule the design doc gives for the public site.
|
|
*
|
|
* Same "hand-roll the daisyUI-shaped class vocabulary" pattern as controlpanel.css: a
|
|
* few shared templates (`_form_fields.html`, `templatetags/field.html`,
|
|
* `controlpanel/templatetags/ui.py`'s `daisy`/`form_field`) render class names
|
|
* (`input`, `select`, `textarea`, `checkbox`, `toggle`, `btn`, `badge`, `card`, `table`,
|
|
* `modal`, `alert`, `avatar`, `progress`, `form-control`, `label`, `divide-y`, `link`)
|
|
* without daisyUI itself supplying their CSS here (it isn't loaded here either) -- see
|
|
* management/templates/management/_form_fields.html for management's own copy of that
|
|
* shared machinery (forked, not reused, so the login/MFA pages stay untouched -- same
|
|
* reasoning as controlpanel's fork, see that file's own banner comment).
|
|
*/
|
|
@import "tailwindcss";
|
|
|
|
@source "../management";
|
|
|
|
@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;
|
|
--color-navy: #101e36;
|
|
--color-steel: #1b2b47;
|
|
--color-hairline: #1e2b42;
|
|
--color-paper: #f4f5f7;
|
|
--color-line: #e3e6eb;
|
|
--color-rule: #eef0f3;
|
|
--color-edge: #d6dae1;
|
|
--color-stroke: #c9cfd8;
|
|
--color-muted: #6c7787;
|
|
--color-dim: #8b95a4;
|
|
--color-slate: #3a4658;
|
|
--color-on-dark: #93a0b4;
|
|
--color-on-dark-dim: #7c8aa0;
|
|
--color-on-dark-faint: #5c6b85;
|
|
|
|
/* Club-themeable: see the file banner. Falls back to the platform red so a club
|
|
that hasn't set a secondary_color yet still gets a sane, branded-looking accent. */
|
|
--color-club: var(--tenant-club, #e4002b);
|
|
--color-club-dark: var(--tenant-club-dark, #b00021);
|
|
/* base.html sets --tenant-club-content from Club.secondary_content_color (already
|
|
WCAG-luminance-computed black/white, see Club._content_color_for) -- e.g. the
|
|
active nav item's own text, which sits on --color-club and can't assume white
|
|
reads on every club's chosen colour. */
|
|
--color-club-content: var(--tenant-club-content, #ffffff);
|
|
|
|
/* The sidebar's own background/foreground -- base.html sets --tenant-sidebar-bg/fg
|
|
from Club.primary_color/primary_content_color when the club has set one, same
|
|
WCAG black-or-white choice as club-content above. Falls back to the original
|
|
fixed dark-ink look otherwise. Derived shades (dim/faint/hairline/chip) are
|
|
color-mix'd toward the background rather than kept as a separate light/dark
|
|
palette -- that self-corrects for either direction (dark-on-light primary colour,
|
|
or light-on-dark) instead of needing two hand-tuned scales. */
|
|
--color-sidebar-bg: var(--tenant-sidebar-bg, var(--color-ink));
|
|
--color-sidebar-fg: var(--tenant-sidebar-fg, #ffffff);
|
|
--color-sidebar-fg-dim: color-mix(in srgb, var(--color-sidebar-fg) 68%, var(--color-sidebar-bg));
|
|
--color-sidebar-fg-faint: color-mix(in srgb, var(--color-sidebar-fg) 38%, var(--color-sidebar-bg));
|
|
--color-sidebar-hairline: color-mix(in srgb, var(--color-sidebar-fg) 16%, var(--color-sidebar-bg));
|
|
--color-sidebar-chip: color-mix(in srgb, var(--color-sidebar-fg) 24%, var(--color-sidebar-bg));
|
|
|
|
--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: #14b8e8;
|
|
--color-info-bg: #eaf7fc;
|
|
--color-info-border: #c3e7f4;
|
|
--color-info-text: #0a6f91;
|
|
|
|
--color-row-sel: #fff7f8;
|
|
--color-row-focus: #f4f9ff;
|
|
--color-row-warn: #fffdf6;
|
|
--color-subhead: #f8f9fa;
|
|
|
|
/* Calendar resource colour (training rink, in the design's Season calendar screen) --
|
|
management-only, controlpanel.css has no equivalent. */
|
|
--color-violet: #7c5cfc;
|
|
|
|
--radius-box: 0.75rem; /* management cards/buttons are rounded-xl, not controlpanel's square 4px */
|
|
}
|
|
|
|
/* --- fonts: identical set to controlpanel.css, same self-hosted files (shared static/fonts/
|
|
directory -- one download, two stylesheets). See that file for the licensing/privacy
|
|
rationale (no Google Fonts CDN request). --- */
|
|
|
|
@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 {
|
|
body {
|
|
background: var(--color-paper);
|
|
color: var(--color-slate);
|
|
font-family: var(--font-sans);
|
|
}
|
|
}
|
|
|
|
/* --- component layer: same daisyUI-shaped class vocabulary as controlpanel.css, rounded
|
|
-xl (0.75rem) rather than controlpanel's square 4px -- see design tokens table,
|
|
"Radius: mobile cards rounded-[14px]; ... desktop cards rounded-xl". --- */
|
|
|
|
@layer components {
|
|
.btn {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
gap: 0.5rem;
|
|
height: 2.25rem;
|
|
padding-inline: 1rem;
|
|
border-radius: var(--radius-box);
|
|
font-family: var(--font-display);
|
|
font-weight: 800;
|
|
font-size: 0.8125rem;
|
|
letter-spacing: 0.08em;
|
|
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;
|
|
}
|
|
|
|
/* "ice" in the mobile/coach-app design tokens -- same #14B8E8 as --color-info here,
|
|
used e.g. for the Sign-up page's oldest-waiting row accent (signup_list.html). */
|
|
.btn-info {
|
|
background: var(--color-info);
|
|
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);
|
|
}
|
|
|
|
.btn-outline.btn-info {
|
|
background: #fff;
|
|
color: var(--color-info-text);
|
|
border-color: var(--color-info-border);
|
|
}
|
|
|
|
.btn-sm {
|
|
height: 1.875rem;
|
|
padding-inline: 0.75rem;
|
|
font-size: 0.75rem;
|
|
}
|
|
|
|
.btn-xs {
|
|
height: 1.5rem;
|
|
padding-inline: 0.5rem;
|
|
font-size: 0.6875rem;
|
|
gap: 0.25rem;
|
|
}
|
|
|
|
.btn-square {
|
|
padding-inline: 0;
|
|
width: 2.25rem;
|
|
}
|
|
|
|
/* The design's own D-series badges/chips (D8's "Draft"/status pills aside) mostly run
|
|
small cornered boxes, not a full pill -- e.g. D2's team tags (U16/U18/Women, 8px)
|
|
and D8's filter chips (All 42/Overdue, 6px). A flat 999px read as noticeably rounder
|
|
than either once actually rendered at badge height, so this is a fixed value rather
|
|
than reusing --radius-box (0.75rem/12px), which is tuned for cards, not a ~22px chip. */
|
|
.badge {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 0.25rem;
|
|
padding: 0.15rem 0.6rem;
|
|
border-radius: 6px;
|
|
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);
|
|
}
|
|
|
|
.card {
|
|
background: #fff;
|
|
border: 1px solid var(--color-line);
|
|
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: 1rem;
|
|
letter-spacing: 0.04em;
|
|
text-transform: uppercase;
|
|
color: var(--color-ink);
|
|
}
|
|
|
|
.table {
|
|
width: 100%;
|
|
border-collapse: collapse;
|
|
font-size: 0.875rem;
|
|
}
|
|
|
|
.table thead tr {
|
|
background: var(--color-subhead);
|
|
border-bottom: 1px solid var(--color-line);
|
|
}
|
|
|
|
.table thead th {
|
|
padding: 0.5rem 1rem;
|
|
text-align: left;
|
|
font-family: var(--font-display);
|
|
font-weight: 700;
|
|
font-size: 0.6875rem;
|
|
letter-spacing: 0.1em;
|
|
text-transform: uppercase;
|
|
color: var(--color-muted);
|
|
}
|
|
|
|
.table tbody tr {
|
|
border-bottom: 1px solid var(--color-rule);
|
|
}
|
|
|
|
.table tbody tr:last-child {
|
|
border-bottom: none;
|
|
}
|
|
|
|
/* Scoped to .cursor-pointer, not every .table row -- most tables in the app
|
|
aren't themselves clickable (actions live in per-row buttons instead), and a
|
|
row-wide hover there would suggest an interaction that isn't there. Currently
|
|
just the Sign-up page's queue (signup_list.html), whose rows open the detail
|
|
drawer on click. --color-subhead, not --color-row-focus: that token is also
|
|
used as signup_list.html's persistent "oldest pending" row background, and
|
|
sharing it made that row look permanently hovered. */
|
|
.table tbody tr.cursor-pointer:hover {
|
|
background: var(--color-subhead);
|
|
}
|
|
|
|
.table tbody td {
|
|
padding: 0.75rem 1rem;
|
|
color: var(--color-ink);
|
|
vertical-align: middle;
|
|
}
|
|
|
|
.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-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);
|
|
}
|
|
|
|
.avatar > div {
|
|
border-radius: 999px;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.avatar-placeholder > div {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
background: var(--color-steel);
|
|
color: #fff;
|
|
font-family: var(--font-display);
|
|
font-weight: 800;
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
.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;
|
|
height: 2.25rem;
|
|
}
|
|
|
|
.select {
|
|
appearance: none;
|
|
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%236C7787' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
|
|
background-repeat: no-repeat;
|
|
background-position: right 0.6rem center;
|
|
background-size: 1rem;
|
|
padding-right: 2rem;
|
|
}
|
|
|
|
/* The browser's own "Choose File" control has no author styling at all, so it
|
|
renders as a second, differently-chromed box nested inside .file-input's own
|
|
border -- restyle the button itself as a small pill so there's one field
|
|
boundary, not two. The trailing "no file chosen" text is UA shadow content
|
|
(can't be restyled beyond color, which it inherits from .file-input). */
|
|
.file-input {
|
|
/* display:flex/align-items:center alone don't reliably center a file
|
|
input's native button+label in every browser -- Safari lays that out
|
|
as shadow content that ignores the host's flex alignment. Centering it
|
|
through the box model instead (top/bottom padding sized to leave equal
|
|
space above and below the button below) works regardless: 2.25rem box
|
|
- 2 * 1px border - 1.5rem button, split evenly. */
|
|
display: flex;
|
|
align-items: center;
|
|
padding-block: 0.3125rem;
|
|
color: var(--color-dim);
|
|
}
|
|
|
|
.file-input::file-selector-button,
|
|
.file-input::-webkit-file-upload-button {
|
|
/* Safari ignores author background/border-radius on this pseudo-element
|
|
and keeps drawing its own native bezeled button unless appearance is
|
|
reset first -- everything below was silently a no-op in Safari without
|
|
this line (same family of "Safari overrides author styling on a native
|
|
control" issue as the sign-up drawer's <dialog> corners). */
|
|
appearance: none;
|
|
-webkit-appearance: none;
|
|
height: 1.5rem;
|
|
padding-inline: 0.75rem;
|
|
margin-right: 0.75rem;
|
|
border: none;
|
|
/* Not 999px: that pill radius is reserved elsewhere (.avatar, .toggle,
|
|
.progress, .crest) for genuinely round/pill shapes -- every real .btn
|
|
uses var(--radius-box) (rounded-xl), so a pill here read as a toggle
|
|
chip rather than a button. */
|
|
border-radius: 0.375rem;
|
|
background: var(--color-ink);
|
|
color: #fff;
|
|
font-family: var(--font-display);
|
|
font-weight: 800;
|
|
font-size: 0.6875rem;
|
|
letter-spacing: 0.08em;
|
|
text-transform: uppercase;
|
|
cursor: pointer;
|
|
transition: opacity 0.15s ease;
|
|
}
|
|
|
|
.file-input:hover::file-selector-button,
|
|
.file-input:hover::-webkit-file-upload-button {
|
|
opacity: 0.85;
|
|
}
|
|
|
|
.input:focus,
|
|
.select:focus,
|
|
.textarea:focus,
|
|
.file-input:focus-within {
|
|
outline: 2px solid var(--color-club);
|
|
outline-offset: -1px;
|
|
border-color: var(--color-club);
|
|
}
|
|
|
|
.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). Same pair as
|
|
controlpanel.css's own copy -- see that file's .otp comment for why the real
|
|
input stays invisible-but-focusable rather than trying to align its own glyphs
|
|
with the box pitch via CSS alone. */
|
|
.input-lg {
|
|
height: 3rem;
|
|
font-family: var(--font-mono);
|
|
font-size: 1.125rem;
|
|
letter-spacing: 0.2em;
|
|
text-align: center;
|
|
}
|
|
|
|
.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-club);
|
|
}
|
|
|
|
.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;
|
|
}
|
|
|
|
.label-text {
|
|
font-size: 0.8125rem;
|
|
font-weight: 600;
|
|
color: var(--color-slate);
|
|
}
|
|
|
|
.label-text-alt {
|
|
font-size: 0.75rem;
|
|
color: var(--color-muted);
|
|
}
|
|
|
|
.link {
|
|
color: var(--color-club-dark);
|
|
text-decoration: none;
|
|
}
|
|
|
|
.link-hover:hover {
|
|
text-decoration: underline;
|
|
}
|
|
|
|
.divide-y > * + * {
|
|
border-top: 1px solid var(--color-rule);
|
|
}
|
|
|
|
/* Native <dialog>-based modals -- see management/templates/management/_modal_form.html
|
|
and _confirm_modal.html (forked from controlpanel's, same reasoning). */
|
|
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-line);
|
|
border-radius: var(--radius-box);
|
|
padding: 1.5rem;
|
|
width: 32rem;
|
|
max-width: 91vw;
|
|
max-height: 85vh;
|
|
overflow-y: auto;
|
|
}
|
|
|
|
/* No .modal-start variant here (unlike controlpanel.css) -- a native <dialog>
|
|
shown via showModal() gets its own corner-rounding applied by Safari
|
|
regardless of author CSS (confirmed: zeroing border-radius on both the
|
|
<dialog> itself and its .modal-box still left rounded corners), so a
|
|
right-side slide-in drawer uses the plain .drawer-backdrop/.drawer classes
|
|
below instead -- see signup_list.html. */
|
|
|
|
.modal-action {
|
|
display: flex;
|
|
justify-content: flex-end;
|
|
gap: 0.5rem;
|
|
margin-top: 1.5rem;
|
|
}
|
|
|
|
.modal-backdrop {
|
|
display: none;
|
|
}
|
|
|
|
/* A right-side slide-in drawer (signup_list.html) -- a plain fixed-position <div>
|
|
toggled by JS (openDrawer/closeDrawer), not a native <dialog>. Safari applies its
|
|
own corner-rounding to a <dialog> shown via showModal() regardless of an author's
|
|
own border-radius: 0 (confirmed -- zeroing radius on both the <dialog> itself and
|
|
its .modal-box still left rounded corners in Safari), so a plain div sidesteps that
|
|
native top-layer rendering entirely. transform/transition also gets a real slide
|
|
animation for free, which @starting-style-based <dialog> entry animations can't
|
|
do reliably across browsers yet. */
|
|
.drawer-backdrop {
|
|
display: none;
|
|
position: fixed;
|
|
inset: 0;
|
|
z-index: 50;
|
|
background: rgba(11, 18, 32, 0);
|
|
transition: background-color 0.2s ease;
|
|
}
|
|
|
|
/* Split from .open on purpose -- display can't itself transition, so JS adds
|
|
.visible (display:block) and forces a reflow *before* adding .open (the
|
|
background/transform change), otherwise the browser never renders the
|
|
"just-appeared, still off-screen" frame to animate away from and the drawer
|
|
would just snap into place instead of sliding. */
|
|
.drawer-backdrop.visible {
|
|
display: block;
|
|
}
|
|
|
|
.drawer-backdrop.open {
|
|
background: rgba(11, 18, 32, 0.55);
|
|
}
|
|
|
|
.drawer {
|
|
position: absolute;
|
|
top: 0;
|
|
right: 0;
|
|
height: 100%;
|
|
width: 420px;
|
|
max-width: 91vw;
|
|
display: flex;
|
|
flex-direction: column;
|
|
background: #fff;
|
|
border-left: 1px solid var(--color-line);
|
|
transform: translateX(100%);
|
|
transition: transform 0.2s ease;
|
|
}
|
|
|
|
.drawer-backdrop.open .drawer {
|
|
transform: translateX(0);
|
|
}
|
|
|
|
/* The club crest mark -- a circle fallback for a club with no logo uploaded (see
|
|
controlpanel.css's own .crest, which settled on a circle over the design's original
|
|
hexagon after it read as a "5-pointed" shape rather than a shield in practice). */
|
|
.crest {
|
|
border-radius: 999px;
|
|
}
|
|
|
|
/* "Place in" big team buttons on the Sign-up page (signup_list.html) -- a plain
|
|
CSS state class rather than juggling several Tailwind utilities via classList
|
|
in JS (see the page's own fetch handler), so toggling "placed" on click can't
|
|
drift out of sync with what the template renders server-side on a full reload. */
|
|
.team-chip {
|
|
border-radius: 0.5rem;
|
|
padding: 0.5rem 0.875rem;
|
|
font-family: var(--font-display);
|
|
font-weight: 700;
|
|
font-size: 0.875rem;
|
|
letter-spacing: 0.08em;
|
|
text-transform: uppercase;
|
|
border: 1px solid var(--color-line);
|
|
background: var(--color-paper);
|
|
color: var(--color-muted);
|
|
cursor: pointer;
|
|
transition:
|
|
background-color 0.15s ease,
|
|
color 0.15s ease,
|
|
border-color 0.15s ease;
|
|
}
|
|
|
|
.team-chip:hover:not(:disabled) {
|
|
border-color: var(--color-club);
|
|
color: var(--color-ink);
|
|
}
|
|
|
|
.team-chip.placed,
|
|
.team-chip:disabled {
|
|
background: var(--color-ink);
|
|
color: #fff;
|
|
border-color: var(--color-ink);
|
|
cursor: default;
|
|
}
|
|
|
|
/* Sidebar nav (management/templates/management/_nav_items.html). */
|
|
.nav-item {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 0.625rem;
|
|
height: 2.5rem;
|
|
padding-inline: 0.75rem;
|
|
border-radius: 0.5rem;
|
|
font-family: var(--font-display);
|
|
font-weight: 700;
|
|
font-size: 1rem;
|
|
letter-spacing: 0.04em;
|
|
text-transform: uppercase;
|
|
color: var(--color-sidebar-fg-dim);
|
|
}
|
|
|
|
.nav-item:hover {
|
|
color: var(--color-sidebar-fg);
|
|
}
|
|
|
|
.nav-item.active {
|
|
background: var(--color-club);
|
|
color: var(--color-club-content);
|
|
}
|
|
|
|
.nav-subitem {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 0.5rem;
|
|
height: 1.875rem;
|
|
padding-left: 1.375rem;
|
|
font-size: 0.875rem;
|
|
color: var(--color-sidebar-fg-faint);
|
|
}
|
|
|
|
.nav-subitem:hover,
|
|
.nav-subitem.active {
|
|
color: var(--color-sidebar-fg);
|
|
font-weight: 600;
|
|
}
|
|
|
|
/* static/js/searchable-select.js's dropdown -- the script is shared with controlpanel.css
|
|
(see that file's own copy of this block) and hardcodes these daisyUI-era class names as
|
|
its public API rather than importing a stylesheet, so each app supplies its own visual
|
|
treatment under the same selectors instead of the script picking a look. */
|
|
.bg-base-100 {
|
|
background: #fff;
|
|
}
|
|
|
|
.menu {
|
|
list-style: none;
|
|
margin: 0;
|
|
padding: 0.375rem;
|
|
}
|
|
|
|
.menu li a {
|
|
display: block;
|
|
padding: 0.5rem 0.75rem;
|
|
border-radius: 0.375rem;
|
|
font-family: var(--font-sans);
|
|
font-size: 0.875rem;
|
|
color: var(--color-ink);
|
|
cursor: pointer;
|
|
}
|
|
|
|
.menu li a:hover {
|
|
background: var(--color-rule);
|
|
}
|
|
|
|
.menu li a.menu-active {
|
|
background: var(--color-club);
|
|
color: #fff;
|
|
}
|
|
}
|
|
|
|
/* --- Events calendar grid ---
|
|
Week/Month/Season views for management/templates/management/event_list.html
|
|
(EventListView, events/services/calendar.py). Kind -> colour follows the same
|
|
mapping as the table's own badges (see that template's inline badge classes),
|
|
just built from the underlying tokens instead of the badge component -- these
|
|
are big rectangles/chips, not small pills, so a tinted fill + solid left edge
|
|
reads better than a bordered badge blown up to size.
|
|
|
|
Percentage math: week_grid() (events/services/calendar.py) hands each block
|
|
top_pct/height_pct/left_pct/width_pct as floats relative to (a) the visible
|
|
hour span for top/height and (b) same-day overlap columns for left/width.
|
|
Percentage heights only resolve against a containing block with a *definite*
|
|
height, so .cal-week-body gets an explicit inline height (hours_span * the
|
|
fixed per-hour row height below) from the template -- everything inside it
|
|
is then free to size purely off the server-computed percentages. */
|
|
@layer components {
|
|
.cal-week-inner {
|
|
min-width: 55rem;
|
|
}
|
|
|
|
.cal-week-header {
|
|
display: flex;
|
|
border-bottom: 1px solid var(--color-line);
|
|
}
|
|
|
|
.cal-week-gutter-spacer {
|
|
width: 3.5rem;
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
.cal-week-daycol-header {
|
|
flex: 1 1 0;
|
|
min-width: 7.5rem;
|
|
padding: 0.5rem 0.25rem;
|
|
text-align: center;
|
|
border-left: 1px solid var(--color-rule);
|
|
}
|
|
|
|
.cal-week-daycol-header.is-today {
|
|
background: var(--color-row-focus);
|
|
}
|
|
|
|
.cal-daycol-dow {
|
|
display: block;
|
|
font-family: var(--font-display);
|
|
font-size: 0.6875rem;
|
|
font-weight: 700;
|
|
letter-spacing: 0.08em;
|
|
text-transform: uppercase;
|
|
color: var(--color-muted);
|
|
}
|
|
|
|
.cal-week-daycol-header.is-today .cal-daycol-dow {
|
|
color: var(--color-club-dark);
|
|
}
|
|
|
|
.cal-daycol-num {
|
|
display: block;
|
|
font-family: var(--font-display);
|
|
font-size: 1.25rem;
|
|
font-weight: 800;
|
|
color: var(--color-ink);
|
|
}
|
|
|
|
/* Explicit height set inline by the template (hours_span * row height) --
|
|
this is what makes the children's percentage top/height resolve at all.
|
|
display:flex is just as load-bearing here as that inline height: the
|
|
gutter and day columns are meant to sit side by side, each stretched
|
|
(flex's default align-items) to the row's full height -- without it
|
|
they're plain block children, which stack vertically instead of
|
|
side-by-side and, since their real content is all position:absolute
|
|
(contributing nothing to auto-height), each collapses to near-zero
|
|
height too. */
|
|
.cal-week-body {
|
|
position: relative;
|
|
display: flex;
|
|
}
|
|
|
|
.cal-week-gridlines {
|
|
position: absolute;
|
|
inset: 0;
|
|
pointer-events: none;
|
|
}
|
|
|
|
.cal-hour-line {
|
|
position: absolute;
|
|
left: 0;
|
|
right: 0;
|
|
height: 1px;
|
|
background: var(--color-rule);
|
|
}
|
|
|
|
.cal-week-gutter {
|
|
position: relative;
|
|
width: 3.5rem;
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
.cal-hour-label {
|
|
position: absolute;
|
|
right: 0.625rem;
|
|
transform: translateY(-50%);
|
|
font-family: var(--font-mono);
|
|
font-size: 0.6875rem;
|
|
color: var(--color-dim);
|
|
white-space: nowrap;
|
|
}
|
|
|
|
.cal-week-daycol {
|
|
position: relative;
|
|
flex: 1 1 0;
|
|
min-width: 7.5rem;
|
|
border-left: 1px solid var(--color-rule);
|
|
}
|
|
|
|
.cal-week-daycol.is-today {
|
|
background: var(--color-row-focus);
|
|
}
|
|
|
|
/* Positioned by the template's inline style (top/height/left/width, straight
|
|
from the block dict) -- this element owns the percentage box, a plain
|
|
inset+margin child owns the visual pill, so the 1px gap between
|
|
side-by-side overlapping blocks doesn't have to come out of the
|
|
server-computed width itself. */
|
|
.cal-block-slot {
|
|
position: absolute;
|
|
box-sizing: border-box;
|
|
}
|
|
|
|
.cal-block {
|
|
position: absolute;
|
|
inset: 0;
|
|
margin: 1px;
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 0.125rem;
|
|
overflow: hidden;
|
|
border-radius: 0.375rem;
|
|
border-left: 3px solid;
|
|
padding: 0.25rem 0.375rem;
|
|
text-decoration: none;
|
|
transition: opacity 0.15s ease;
|
|
}
|
|
|
|
.cal-block:hover {
|
|
opacity: 0.82;
|
|
}
|
|
|
|
.cal-block-time {
|
|
font-family: var(--font-mono);
|
|
font-size: 0.625rem;
|
|
font-weight: 600;
|
|
opacity: 0.85;
|
|
white-space: nowrap;
|
|
}
|
|
|
|
.cal-block-title {
|
|
font-size: 0.6875rem;
|
|
font-weight: 700;
|
|
line-height: 1.15;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
white-space: nowrap;
|
|
}
|
|
|
|
/* Kind -> colour, same source palette as the table's badges (event_list.html):
|
|
game=club-dark/error, training=info, tournament=warn, meeting=ink/neutral,
|
|
social=violet, other=neutral outline. Solid tint + left edge, not a badge --
|
|
these blocks are far bigger than a pill. */
|
|
.cal-block-game {
|
|
background: var(--color-danger-bg);
|
|
border-color: var(--color-club-dark);
|
|
color: var(--color-club-dark);
|
|
}
|
|
|
|
.cal-block-training {
|
|
background: var(--color-info-bg);
|
|
border-color: var(--color-info);
|
|
color: var(--color-info-text);
|
|
}
|
|
|
|
.cal-block-tournament {
|
|
background: var(--color-warn-bg);
|
|
border-color: var(--color-warn);
|
|
color: var(--color-warn-text);
|
|
}
|
|
|
|
.cal-block-meeting {
|
|
background: var(--color-rule);
|
|
border-color: var(--color-ink);
|
|
color: var(--color-ink);
|
|
}
|
|
|
|
.cal-block-social {
|
|
background: color-mix(in srgb, var(--color-violet) 12%, white);
|
|
border-color: var(--color-violet);
|
|
color: var(--color-violet);
|
|
}
|
|
|
|
.cal-block-other {
|
|
background: #fff;
|
|
border-color: var(--color-edge);
|
|
color: var(--color-muted);
|
|
}
|
|
|
|
/* --- Month grid --- */
|
|
.cal-month-daynames {
|
|
display: grid;
|
|
grid-template-columns: repeat(7, 1fr);
|
|
border-bottom: 1px solid var(--color-line);
|
|
background: var(--color-subhead);
|
|
}
|
|
|
|
.cal-month-dayname {
|
|
padding: 0.4rem 0.25rem;
|
|
text-align: center;
|
|
font-family: var(--font-display);
|
|
font-size: 0.625rem;
|
|
font-weight: 700;
|
|
letter-spacing: 0.1em;
|
|
text-transform: uppercase;
|
|
color: var(--color-muted);
|
|
}
|
|
|
|
.cal-month-grid {
|
|
display: grid;
|
|
grid-template-columns: repeat(7, 1fr);
|
|
border-top: 1px solid var(--color-line);
|
|
border-left: 1px solid var(--color-line);
|
|
}
|
|
|
|
.cal-month-cell {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 0.25rem;
|
|
min-height: 6.75rem;
|
|
padding: 0.375rem;
|
|
background: #fff;
|
|
border-right: 1px solid var(--color-line);
|
|
border-bottom: 1px solid var(--color-line);
|
|
}
|
|
|
|
.cal-month-cell.is-outside {
|
|
background: var(--color-paper);
|
|
}
|
|
|
|
.cal-month-cell.is-outside .cal-month-cell-num {
|
|
color: var(--color-dim);
|
|
}
|
|
|
|
.cal-month-cell.is-today {
|
|
background: var(--color-row-focus);
|
|
}
|
|
|
|
.cal-month-cell-num {
|
|
font-family: var(--font-display);
|
|
font-size: 0.875rem;
|
|
font-weight: 800;
|
|
color: var(--color-ink);
|
|
}
|
|
|
|
.cal-month-cell.is-today .cal-month-cell-num {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
width: 1.375rem;
|
|
height: 1.375rem;
|
|
border-radius: 999px;
|
|
background: var(--color-club);
|
|
color: var(--color-club-content);
|
|
}
|
|
|
|
.cal-month-cell-events {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 0.1875rem;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.cal-chip {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 0.3125rem;
|
|
padding: 0.0625rem 0.25rem;
|
|
border-radius: 0.25rem;
|
|
font-size: 0.625rem;
|
|
font-weight: 600;
|
|
line-height: 1.4;
|
|
text-decoration: none;
|
|
overflow: hidden;
|
|
white-space: nowrap;
|
|
text-overflow: ellipsis;
|
|
}
|
|
|
|
.cal-chip-game {
|
|
background: var(--color-danger-bg);
|
|
color: var(--color-club-dark);
|
|
}
|
|
|
|
.cal-chip-training {
|
|
background: var(--color-info-bg);
|
|
color: var(--color-info-text);
|
|
}
|
|
|
|
.cal-chip-tournament {
|
|
background: var(--color-warn-bg);
|
|
color: var(--color-warn-text);
|
|
}
|
|
|
|
.cal-chip-meeting {
|
|
background: var(--color-rule);
|
|
color: var(--color-ink);
|
|
}
|
|
|
|
.cal-chip-social {
|
|
background: color-mix(in srgb, var(--color-violet) 12%, white);
|
|
color: var(--color-violet);
|
|
}
|
|
|
|
.cal-chip-other {
|
|
background: var(--color-paper);
|
|
color: var(--color-muted);
|
|
}
|
|
|
|
.cal-dot {
|
|
width: 0.375rem;
|
|
height: 0.375rem;
|
|
border-radius: 999px;
|
|
flex-shrink: 0;
|
|
background: currentColor;
|
|
}
|
|
|
|
.cal-chip-more {
|
|
padding: 0.0625rem 0.25rem;
|
|
font-size: 0.625rem;
|
|
font-weight: 600;
|
|
color: var(--color-muted);
|
|
text-decoration: none;
|
|
}
|
|
|
|
.cal-chip-more:hover {
|
|
color: var(--color-ink);
|
|
text-decoration: underline;
|
|
}
|
|
|
|
/* --- Season grid: one mini month-calendar per month, counts only (no titles --
|
|
there's no room at this scale, see events/services/calendar.py's season_grid
|
|
docstring). --- */
|
|
.cal-season-daynames {
|
|
display: grid;
|
|
grid-template-columns: repeat(7, 1fr);
|
|
margin-bottom: 0.25rem;
|
|
}
|
|
|
|
.cal-season-dayname {
|
|
text-align: center;
|
|
font-family: var(--font-mono);
|
|
font-size: 0.5625rem;
|
|
color: var(--color-dim);
|
|
text-transform: uppercase;
|
|
}
|
|
|
|
.cal-season-grid {
|
|
display: grid;
|
|
grid-template-columns: repeat(7, 1fr);
|
|
gap: 2px;
|
|
}
|
|
|
|
.cal-season-cell {
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
justify-content: center;
|
|
gap: 0.0625rem;
|
|
aspect-ratio: 1;
|
|
border-radius: 0.25rem;
|
|
text-decoration: none;
|
|
}
|
|
|
|
.cal-season-daynum {
|
|
font-family: var(--font-mono);
|
|
font-size: 0.625rem;
|
|
color: var(--color-slate);
|
|
}
|
|
|
|
.cal-season-cell.is-outside .cal-season-daynum {
|
|
color: var(--color-dim);
|
|
opacity: 0.6;
|
|
}
|
|
|
|
.cal-season-cell.is-today {
|
|
background: var(--color-row-focus);
|
|
}
|
|
|
|
.cal-season-cell.is-today .cal-season-daynum {
|
|
font-weight: 700;
|
|
color: var(--color-club-dark);
|
|
}
|
|
|
|
.cal-season-cell.has-events {
|
|
cursor: pointer;
|
|
}
|
|
|
|
.cal-season-cell.has-events:hover {
|
|
background: var(--color-subhead);
|
|
}
|
|
|
|
.cal-season-count {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
min-width: 0.875rem;
|
|
height: 0.875rem;
|
|
padding: 0 0.1875rem;
|
|
border-radius: 999px;
|
|
background: var(--color-club);
|
|
color: var(--color-club-content);
|
|
font-family: var(--font-mono);
|
|
font-size: 0.5rem;
|
|
font-weight: 700;
|
|
line-height: 1;
|
|
}
|
|
}
|
|
|
|
/* --- Event form ---
|
|
management/templates/management/event_form.html and event_series_form.html
|
|
(EventForm/EventSeriesForm) -- both share the exact same treatment: a
|
|
prominent kind picker up top (a row of clickable "kind cards" layered over
|
|
the real, always-visible <select id="id_kind"> -- the select stays the
|
|
single source of truth so it's still fully keyboard/screen-reader operable;
|
|
see each template's own inline <script> for how the two stay in sync), then
|
|
the rest of the form broken into titled .card sections instead of one long
|
|
flat form. Kind -> colour on a selected card reuses the exact same tokens
|
|
as the event list's own badges and the calendar grid just above (game =
|
|
club-dark/danger, training = info, tournament = warn, meeting = ink/
|
|
neutral, social = violet), so this reads as part of the same system rather
|
|
than a fourth palette. .game-only itself carries no rule here -- same as
|
|
before this section existed, it's a plain marker class the template's own
|
|
script toggles Tailwind's `hidden` utility on/off. */
|
|
@layer components {
|
|
.event-section-header {
|
|
display: flex;
|
|
align-items: flex-start;
|
|
gap: 0.75rem;
|
|
margin-bottom: 1rem;
|
|
}
|
|
|
|
.event-section-icon {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
width: 2.25rem;
|
|
height: 2.25rem;
|
|
flex-shrink: 0;
|
|
border-radius: 0.625rem;
|
|
background: var(--color-rule);
|
|
color: var(--color-ink);
|
|
}
|
|
|
|
/* Game details' own section icon -- a small accent so the one card that's
|
|
conditionally shown/hidden also reads as "special" at a glance. */
|
|
.event-section-icon-accent {
|
|
background: var(--color-danger-bg);
|
|
color: var(--color-club-dark);
|
|
}
|
|
|
|
.event-section-title {
|
|
font-family: var(--font-display);
|
|
font-weight: 800;
|
|
font-size: 0.9375rem;
|
|
letter-spacing: 0.03em;
|
|
color: var(--color-ink);
|
|
text-transform: uppercase;
|
|
}
|
|
|
|
.event-section-hint {
|
|
margin-top: 0.125rem;
|
|
font-size: 0.8125rem;
|
|
color: var(--color-muted);
|
|
}
|
|
|
|
/* The kind-card row -- 2 columns on mobile, up to 6 (one per EventKind) on
|
|
desktop, so all of them are glanceable in a single row on a normal-width
|
|
screen without ever needing to scroll the picker itself. */
|
|
.kind-grid {
|
|
display: grid;
|
|
grid-template-columns: repeat(2, 1fr);
|
|
gap: 0.625rem;
|
|
}
|
|
|
|
@media (min-width: 640px) {
|
|
.kind-grid {
|
|
grid-template-columns: repeat(3, 1fr);
|
|
}
|
|
}
|
|
|
|
@media (min-width: 1024px) {
|
|
.kind-grid {
|
|
grid-template-columns: repeat(6, 1fr);
|
|
}
|
|
}
|
|
|
|
.kind-card {
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
justify-content: center;
|
|
gap: 0.375rem;
|
|
padding: 0.875rem 0.5rem;
|
|
border: 1.5px solid var(--color-line);
|
|
border-radius: var(--radius-box);
|
|
background: #fff;
|
|
color: var(--color-muted);
|
|
cursor: pointer;
|
|
text-align: center;
|
|
transition:
|
|
border-color 0.15s ease,
|
|
background-color 0.15s ease,
|
|
color 0.15s ease;
|
|
}
|
|
|
|
.kind-card:hover {
|
|
border-color: var(--color-stroke);
|
|
color: var(--color-ink);
|
|
}
|
|
|
|
/* The card's own radio is visually sr-only (see event_form.html/
|
|
event_series_form.html) -- :has() reads its checked/focus state back
|
|
onto the label so there's a visible focus ring for keyboard users even
|
|
though the input itself is off-screen. */
|
|
.kind-card:has(input:focus-visible) {
|
|
outline: 2px solid var(--color-club);
|
|
outline-offset: 2px;
|
|
}
|
|
|
|
.kind-card-label {
|
|
font-family: var(--font-display);
|
|
font-weight: 700;
|
|
font-size: 0.6875rem;
|
|
letter-spacing: 0.06em;
|
|
text-transform: uppercase;
|
|
}
|
|
|
|
/* Default (also covers "meeting" and "other", which share the app's own
|
|
ink/neutral and muted/outline treatment elsewhere) -- the per-kind
|
|
overrides below only replace it for the four kinds with their own
|
|
colour in the badge/calendar system. */
|
|
.kind-card:has(input:checked) {
|
|
border-color: var(--color-ink);
|
|
background: var(--color-subhead);
|
|
color: var(--color-ink);
|
|
}
|
|
|
|
.kind-card:has(input:checked)[data-kind="game"] {
|
|
border-color: var(--color-club-dark);
|
|
background: var(--color-danger-bg);
|
|
color: var(--color-club-dark);
|
|
}
|
|
|
|
.kind-card:has(input:checked)[data-kind="training"] {
|
|
border-color: var(--color-info);
|
|
background: var(--color-info-bg);
|
|
color: var(--color-info-text);
|
|
}
|
|
|
|
.kind-card:has(input:checked)[data-kind="tournament"] {
|
|
border-color: var(--color-warn);
|
|
background: var(--color-warn-bg);
|
|
color: var(--color-warn-text);
|
|
}
|
|
|
|
.kind-card:has(input:checked)[data-kind="social"] {
|
|
border-color: var(--color-violet);
|
|
background: color-mix(in srgb, var(--color-violet) 12%, white);
|
|
color: var(--color-violet);
|
|
}
|
|
|
|
/* club_wide's own toggle, pulled out of the plain field grid into its own
|
|
framed row -- a whole-club event is a bigger decision than any other
|
|
audience field here, so it gets to look like one. */
|
|
.event-clubwide-toggle {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
gap: 1rem;
|
|
margin-bottom: 1rem;
|
|
padding: 0.75rem 1rem;
|
|
border: 1px solid var(--color-line);
|
|
border-radius: 0.625rem;
|
|
background: var(--color-paper);
|
|
}
|
|
|
|
/* The live "what you're scheduling" recap -- a dashed border marks it as a
|
|
read-only, generated-for-you strip rather than another input card. */
|
|
.event-summary {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 0.75rem;
|
|
padding: 0.875rem 1.125rem;
|
|
border-style: dashed;
|
|
color: var(--color-slate);
|
|
font-size: 0.875rem;
|
|
}
|
|
|
|
.event-summary-icon {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
flex-shrink: 0;
|
|
width: 2rem;
|
|
height: 2rem;
|
|
border-radius: 999px;
|
|
background: var(--color-club);
|
|
color: var(--color-club-content);
|
|
}
|
|
}
|
|
|
|
/* --- In-page content-swap toggle (bg-steel pill shell) ---
|
|
Same bg-steel pill shell as member_list.html's Members/Guardians/Both
|
|
selector, but a JS-toggled pair (or more) of <button>s -- an in-page
|
|
content swap, not a page navigation with its own querystring state -- so a
|
|
dedicated small class pair is simpler than juggling several Tailwind
|
|
utilities via classList from the script.
|
|
|
|
.view-toggle-btn is the generic name (email_previews.html's Email/Plain
|
|
text toggle); .news-lang-btn is kept as an alias so _news_preview.html's
|
|
NL/EN toggle (and its own JS, which reads that exact class) needs no
|
|
change. */
|
|
@layer components {
|
|
.news-lang-btn,
|
|
.view-toggle-btn {
|
|
border-radius: 999px;
|
|
padding: 0.375rem 0.75rem;
|
|
font-family: var(--font-display);
|
|
font-size: 0.75rem;
|
|
font-weight: 700;
|
|
letter-spacing: 0.08em;
|
|
text-transform: uppercase;
|
|
color: var(--color-on-dark);
|
|
cursor: pointer;
|
|
transition: background-color 0.15s ease, color 0.15s ease;
|
|
}
|
|
|
|
.news-lang-btn:hover,
|
|
.view-toggle-btn:hover {
|
|
color: #fff;
|
|
}
|
|
|
|
.news-lang-btn.active,
|
|
.view-toggle-btn.active {
|
|
background: #fff;
|
|
color: var(--color-ink);
|
|
}
|
|
}
|
|
|
|
/* --- Member attendance sparkline (management/templates/management/member_detail.html) ---
|
|
D7's 12-bar strip: uniform-height blocks, not a value chart -- each bar is
|
|
just one event's outcome (ok present, club absent, edge upcoming), not a
|
|
magnitude, so there's nothing for a bar's height to encode. */
|
|
@layer components {
|
|
.attendance-sparkline {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 3px;
|
|
}
|
|
|
|
.attendance-bar {
|
|
width: 0.5rem;
|
|
height: 2rem;
|
|
border-radius: 2px;
|
|
background: var(--color-edge);
|
|
}
|
|
|
|
.attendance-bar-present {
|
|
background: var(--color-ok);
|
|
}
|
|
|
|
.attendance-bar-absent {
|
|
background: var(--color-club);
|
|
}
|
|
}
|
|
|
|
/* --- Club identity page (management/templates/management/club_settings.html) ---
|
|
Colours: swatch + mono hex merged into one bordered row (D9's own look),
|
|
not a labelled field with a decorative swatch floating below it. Live
|
|
preview: a mini mock of this app's own sidebar/button (the real, live
|
|
surface primary_color/secondary_color affect today, via base.html's
|
|
--tenant-* custom properties) plus a mobile app mock (the same colours
|
|
applied to the surface that's built next) -- not D9's fabricated App/
|
|
Website/Email tabs. Colours are set inline by that template's own script
|
|
as the form fields change; only layout/typography lives here. */
|
|
@layer components {
|
|
.club-color-row {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 0.625rem;
|
|
border: 1px solid var(--color-edge);
|
|
border-radius: 0.5rem;
|
|
padding: 0.5rem 0.625rem;
|
|
}
|
|
|
|
.club-color-swatch {
|
|
width: 1.625rem;
|
|
height: 1.625rem;
|
|
flex-shrink: 0;
|
|
padding: 0;
|
|
border: none;
|
|
border-radius: 0.375rem;
|
|
background: none;
|
|
cursor: pointer;
|
|
}
|
|
|
|
.club-color-hex-input {
|
|
flex: 1;
|
|
min-width: 0;
|
|
border: none;
|
|
background: none;
|
|
outline: none;
|
|
padding: 0;
|
|
font-family: var(--font-mono);
|
|
font-size: 0.875rem;
|
|
color: var(--color-ink);
|
|
}
|
|
|
|
.club-contrast-box {
|
|
flex: 1;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
gap: 0.5rem;
|
|
border-radius: 0.5rem;
|
|
padding: 0.625rem 0.75rem;
|
|
font-size: 0.8125rem;
|
|
transition: background-color 0.15s ease, color 0.15s ease;
|
|
}
|
|
|
|
.club-logo-dropzone {
|
|
display: flex;
|
|
width: 4.75rem;
|
|
height: 4.75rem;
|
|
flex-shrink: 0;
|
|
align-items: center;
|
|
justify-content: center;
|
|
overflow: hidden;
|
|
border: 1.5px dashed var(--color-stroke);
|
|
border-radius: 0.5rem;
|
|
background: var(--color-paper);
|
|
}
|
|
|
|
/* Live preview tabs -- a real (if minimal) tab control: click toggles which
|
|
.preview-panel is shown (club_settings.html's own inline script). App and
|
|
Website share one tab since the mobile app isn't built yet and the two
|
|
mocks were already always shown together underneath it. */
|
|
.preview-tab {
|
|
padding: 0.3125rem 0.625rem;
|
|
font-family: var(--font-display);
|
|
font-size: 0.75rem;
|
|
font-weight: 700;
|
|
letter-spacing: 0.04em;
|
|
text-transform: uppercase;
|
|
color: var(--color-muted);
|
|
cursor: pointer;
|
|
}
|
|
|
|
.preview-tab-active {
|
|
background: var(--color-ink);
|
|
color: #fff;
|
|
}
|
|
|
|
.preview-crest {
|
|
display: flex;
|
|
width: 1.5rem;
|
|
height: 1.5rem;
|
|
flex-shrink: 0;
|
|
align-items: center;
|
|
justify-content: center;
|
|
border-radius: 0.25rem;
|
|
background: rgba(255, 255, 255, 0.15);
|
|
font-family: var(--font-display);
|
|
font-size: 0.625rem;
|
|
font-weight: 800;
|
|
}
|
|
|
|
.preview-skeleton-line {
|
|
height: 0.5rem;
|
|
border-radius: 999px;
|
|
background: var(--color-line);
|
|
}
|
|
|
|
.preview-skeleton-line-short {
|
|
width: 60%;
|
|
}
|
|
|
|
/* The mobile app mock -- not built yet, but its header/role-switcher/
|
|
"Next up" card take the same primary/secondary colours a real one
|
|
would, so this preview stays accurate once it exists. Sized closer to
|
|
an actual phone aspect ratio (tall, mostly-empty screen below the
|
|
first couple of cards) rather than a cropped sliver. */
|
|
.preview-phone-lg {
|
|
width: 12.5rem;
|
|
flex-shrink: 0;
|
|
overflow: hidden;
|
|
border: 5px solid var(--color-ink);
|
|
border-radius: 1.375rem;
|
|
background: var(--color-paper);
|
|
}
|
|
|
|
.preview-phone-lg .preview-phone-body {
|
|
min-height: 15rem;
|
|
}
|
|
|
|
.preview-phone-header {
|
|
padding: 0.75rem 0.625rem 0.625rem;
|
|
background: var(--color-ink);
|
|
color: #fff;
|
|
transition: background-color 0.15s ease, color 0.15s ease;
|
|
}
|
|
|
|
.preview-phone-header-row {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 0.375rem;
|
|
}
|
|
|
|
.preview-crest-sm {
|
|
width: 1.125rem;
|
|
height: 1.125rem;
|
|
font-size: 0.5rem;
|
|
border-radius: 0.25rem;
|
|
}
|
|
|
|
.preview-phone-name {
|
|
font-family: var(--font-display);
|
|
font-size: 0.6875rem;
|
|
font-weight: 800;
|
|
letter-spacing: 0.04em;
|
|
text-transform: uppercase;
|
|
}
|
|
|
|
.preview-phone-roles {
|
|
display: flex;
|
|
gap: 2px;
|
|
margin-top: 0.5rem;
|
|
padding: 2px;
|
|
border-radius: 999px;
|
|
background: rgba(255, 255, 255, 0.12);
|
|
}
|
|
|
|
.preview-phone-role {
|
|
flex: 1;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
height: 1.25rem;
|
|
border-radius: 999px;
|
|
font-family: var(--font-display);
|
|
font-size: 0.5625rem;
|
|
font-weight: 800;
|
|
letter-spacing: 0.04em;
|
|
text-transform: uppercase;
|
|
color: var(--color-on-dark);
|
|
}
|
|
|
|
.preview-phone-role-active {
|
|
background: #fff;
|
|
color: var(--color-ink);
|
|
}
|
|
|
|
.preview-phone-body {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 0.5rem;
|
|
padding: 0.625rem;
|
|
}
|
|
|
|
.preview-phone-card {
|
|
border-radius: 0.5rem;
|
|
padding: 0.5rem;
|
|
background: var(--color-ink);
|
|
color: #fff;
|
|
transition: background-color 0.15s ease, color 0.15s ease;
|
|
}
|
|
|
|
.preview-phone-eyebrow {
|
|
font-family: var(--font-display);
|
|
font-size: 0.5625rem;
|
|
font-weight: 800;
|
|
letter-spacing: 0.06em;
|
|
text-transform: uppercase;
|
|
color: var(--color-info);
|
|
}
|
|
|
|
.preview-phone-title {
|
|
margin-top: 0.125rem;
|
|
font-family: var(--font-display);
|
|
font-size: 0.75rem;
|
|
line-height: 1.05;
|
|
font-weight: 800;
|
|
text-transform: uppercase;
|
|
}
|
|
|
|
.preview-phone-bar {
|
|
height: 1rem;
|
|
margin-top: 0.5rem;
|
|
border-radius: 0.25rem;
|
|
background: var(--color-ok);
|
|
}
|
|
|
|
.preview-phone-skeleton-card {
|
|
border: 1px solid var(--color-line);
|
|
border-radius: 0.5rem;
|
|
padding: 0.5rem;
|
|
background: #fff;
|
|
}
|
|
|
|
/* The public-site mock -- same "not built yet, but coloured accurately"
|
|
reasoning as the phone mock above. */
|
|
.preview-website {
|
|
overflow: hidden;
|
|
border: 1px solid var(--color-line);
|
|
border-radius: 0.625rem;
|
|
}
|
|
|
|
.preview-website-header {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 0.5rem;
|
|
padding: 0.625rem 0.75rem;
|
|
background: var(--color-ink);
|
|
color: #fff;
|
|
transition: background-color 0.15s ease, color 0.15s ease;
|
|
}
|
|
|
|
.preview-website-name {
|
|
font-family: var(--font-display);
|
|
font-size: 0.8125rem;
|
|
font-weight: 800;
|
|
letter-spacing: 0.04em;
|
|
text-transform: uppercase;
|
|
}
|
|
|
|
.preview-website-nav {
|
|
margin-left: auto;
|
|
font-family: var(--font-display);
|
|
font-size: 0.625rem;
|
|
font-weight: 700;
|
|
letter-spacing: 0.04em;
|
|
text-transform: uppercase;
|
|
color: var(--color-on-dark);
|
|
}
|
|
|
|
.preview-website-join {
|
|
border-radius: 0.25rem;
|
|
padding: 0.1875rem 0.4375rem;
|
|
background: var(--color-club);
|
|
color: #fff;
|
|
font-family: var(--font-display);
|
|
font-size: 0.625rem;
|
|
font-weight: 800;
|
|
letter-spacing: 0.04em;
|
|
text-transform: uppercase;
|
|
transition: background-color 0.15s ease, color 0.15s ease;
|
|
}
|
|
|
|
.preview-website-hero {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 0.5rem;
|
|
padding: 0.875rem 0.75rem;
|
|
background: var(--color-subhead);
|
|
}
|
|
|
|
.preview-website-headline {
|
|
font-family: var(--font-display);
|
|
font-size: 1.375rem;
|
|
line-height: 0.98;
|
|
font-weight: 800;
|
|
text-transform: uppercase;
|
|
color: var(--color-ink);
|
|
}
|
|
|
|
.preview-brand-dot {
|
|
width: 0.5rem;
|
|
height: 0.5rem;
|
|
flex-shrink: 0;
|
|
border-radius: 0.125rem;
|
|
background: var(--color-club);
|
|
}
|
|
|
|
.preview-brand-dot-neutral {
|
|
background: var(--color-dim);
|
|
}
|
|
|
|
/* The Email tab -- a generic sample, not any one real message. Shaped like
|
|
templates/email/_base.html's actual output (plain white header with a
|
|
coloured crest badge, a primary-coloured button in the body) rather than
|
|
the phone/website mocks' solid colour bar: that's genuinely how every
|
|
branded email (club/templates/club/email/*.html, members/.../claim_approved.html)
|
|
looks, so this preview is accurate, not another approximation. */
|
|
.preview-email {
|
|
overflow: hidden;
|
|
border: 1px solid var(--color-line);
|
|
border-radius: 0.625rem;
|
|
}
|
|
|
|
.preview-email-header {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 0.5rem;
|
|
padding: 0.625rem 0.75rem;
|
|
background: #fff;
|
|
border-bottom: 1px solid var(--color-line);
|
|
}
|
|
|
|
.preview-email-crest {
|
|
transition: background-color 0.15s ease, color 0.15s ease;
|
|
}
|
|
|
|
.preview-email-name {
|
|
font-family: var(--font-display);
|
|
font-size: 0.8125rem;
|
|
font-weight: 800;
|
|
letter-spacing: 0.04em;
|
|
text-transform: uppercase;
|
|
color: var(--color-ink);
|
|
}
|
|
|
|
.preview-email-body {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 0.5rem;
|
|
padding: 0.875rem 0.75rem;
|
|
}
|
|
|
|
.preview-email-button {
|
|
align-self: flex-start;
|
|
border-radius: 0.25rem;
|
|
padding: 0.3125rem 0.75rem;
|
|
margin: 0.125rem 0;
|
|
font-family: var(--font-display);
|
|
font-size: 0.6875rem;
|
|
font-weight: 800;
|
|
letter-spacing: 0.04em;
|
|
text-transform: uppercase;
|
|
transition: background-color 0.15s ease, color 0.15s ease;
|
|
}
|
|
}
|