Reads and edits BWF metadata for production sound. One source tree builds a single self-contained page and a native Tauri app with a Rust audio engine and WAV writer. Around 370 checks across seven test suites. First commit of the existing state, so that from here every change can be seen and undone.
3082 lines
98 KiB
CSS
3082 lines
98 KiB
CSS
/* Monochrome Framework, auto-scoped under .bwfa-scope so it only affects BWF Analyser markup. */
|
||
/**
|
||
* Monochrome Framework
|
||
* A grayscale-first CSS boilerplate covering layout, components and utilities.
|
||
* No build step required, no dependencies. Just link this file.
|
||
*
|
||
* Table of contents:
|
||
* 1. Design Tokens (CSS custom properties)
|
||
* 2. Reset & Base Elements
|
||
* 3. Typography
|
||
* 4. Layout: Container & Grid
|
||
* 5. Flexbox Utilities
|
||
* 6. Spacing Utilities
|
||
* 7. Sizing Utilities
|
||
* 8. Display & Position Utilities
|
||
* 9. Text & Color Utilities
|
||
* 10. Border, Radius & Shadow Utilities
|
||
* 11. Buttons
|
||
* 12. Forms
|
||
* 13. Cards
|
||
* 14. Navbar
|
||
* 15. Alerts
|
||
* 16. Badges
|
||
* 17. Tables
|
||
* 18. Tabs
|
||
* 19. Accordion
|
||
* 20. Dropdown
|
||
* 21. Modal
|
||
* 22. Tooltip
|
||
* 23. Toast
|
||
* 24. Pagination & Breadcrumb
|
||
* 25. Progress & Spinner
|
||
* 26. Avatar & List Group
|
||
* 27. Drawer / Offcanvas
|
||
* 28. Skeleton Loader
|
||
* 29. Misc Utilities
|
||
* 30. Dark Mode
|
||
* 31. Accessibility
|
||
* 32. Print Styles
|
||
*/
|
||
|
||
|
||
/* ==========================================================================
|
||
1. DESIGN TOKENS
|
||
========================================================================== */
|
||
|
||
.bwfa-scope {
|
||
/* Grayscale palette. 50 is near-white, 900 is near-black. */
|
||
--gray-0: #ffffff;
|
||
--gray-50: #fafafa;
|
||
--gray-100: #f2f2f2;
|
||
--gray-150: #e9e9e9;
|
||
--gray-200: #dddddd;
|
||
--gray-300: #c4c4c4;
|
||
--gray-400: #a3a3a3;
|
||
--gray-500: #808080;
|
||
--gray-600: #5f5f5f;
|
||
--gray-700: #4f4f4f;
|
||
--gray-800: #333333;
|
||
--gray-900: #1a1a1a;
|
||
--gray-1000: #000000;
|
||
|
||
/* Semantic surface / text roles, mapped to the grayscale above.
|
||
Swap these three lines to re-skin the whole framework. */
|
||
--color-bg: var(--gray-0);
|
||
--color-surface: var(--gray-50);
|
||
--color-surface-alt: var(--gray-100);
|
||
--color-border: var(--gray-200);
|
||
--color-text: var(--gray-800);
|
||
--color-text-muted: var(--gray-600);
|
||
--color-text-inverse: var(--gray-0);
|
||
|
||
/* Brand/action color. Kept black-on-white rather than a hue, on purpose. */
|
||
--color-action: var(--gray-900);
|
||
--color-action-hover: var(--gray-1000);
|
||
--color-action-muted: var(--gray-150);
|
||
|
||
/* Feedback colors. Real interfaces still need a way to say "this failed"
|
||
that isn't just "darker gray" — so these stay slightly desaturated
|
||
rather than pure red/green. Delete this block if you truly want zero hue. */
|
||
--color-success: #2e7d4f;
|
||
--color-success-bg: #eaf5ee;
|
||
--color-danger: #b3261e;
|
||
--color-danger-bg: #fbeae9;
|
||
--color-warning: #8a6d1d;
|
||
--color-warning-bg: #fbf3df;
|
||
--color-info: #375a7f;
|
||
--color-info-bg: #eaf0f7;
|
||
|
||
/* Typography */
|
||
--font-sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
|
||
--font-serif: Georgia, "Times New Roman", serif;
|
||
--font-mono: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, monospace;
|
||
|
||
--fs-xs: 0.75rem;
|
||
--fs-sm: 0.875rem;
|
||
--fs-base: 1rem;
|
||
--fs-md: 1.125rem;
|
||
--fs-lg: 1.25rem;
|
||
--fs-xl: 1.5rem;
|
||
--fs-2xl: 1.875rem;
|
||
--fs-3xl: 2.25rem;
|
||
--fs-4xl: 3rem;
|
||
|
||
--lh-tight: 1.2;
|
||
--lh-normal: 1.5;
|
||
--lh-loose: 1.75;
|
||
|
||
--fw-normal: 400;
|
||
--fw-medium: 500;
|
||
--fw-semibold: 600;
|
||
--fw-bold: 700;
|
||
|
||
/* Spacing scale, 4px base unit */
|
||
--space-0: 0;
|
||
--space-1: 0.25rem; /* 4px */
|
||
--space-2: 0.5rem; /* 8px */
|
||
--space-3: 0.75rem; /* 12px */
|
||
--space-4: 1rem; /* 16px */
|
||
--space-5: 1.5rem; /* 24px */
|
||
--space-6: 2rem; /* 32px */
|
||
--space-7: 3rem; /* 48px */
|
||
--space-8: 4rem; /* 64px */
|
||
--space-9: 6rem; /* 96px */
|
||
|
||
/* Radius */
|
||
--radius-sm: 3px;
|
||
--radius-md: 6px;
|
||
--radius-lg: 10px;
|
||
--radius-xl: 16px;
|
||
--radius-pill: 999px;
|
||
|
||
/* Shadows — kept faint and neutral, no colored glow */
|
||
--shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.06);
|
||
--shadow-md: 0 2px 8px rgba(0, 0, 0, 0.08);
|
||
--shadow-lg: 0 8px 24px rgba(0, 0, 0, 0.12);
|
||
--shadow-xl: 0 16px 40px rgba(0, 0, 0, 0.16);
|
||
--shadow-inset: inset 0 1px 2px rgba(0, 0, 0, 0.08);
|
||
|
||
/* Motion */
|
||
--ease-standard: cubic-bezier(0.4, 0, 0.2, 1);
|
||
--duration-fast: 120ms;
|
||
--duration-base: 200ms;
|
||
--duration-slow: 320ms;
|
||
|
||
/* Layout */
|
||
--container-sm: 540px;
|
||
--container-md: 720px;
|
||
--container-lg: 960px;
|
||
--container-xl: 1140px;
|
||
--container-xxl: 1320px;
|
||
--gutter: 1.5rem;
|
||
|
||
/* z-index scale, named instead of arbitrary numbers */
|
||
--z-dropdown: 1000;
|
||
--z-sticky: 1020;
|
||
--z-navbar: 1030;
|
||
--z-drawer: 1040;
|
||
--z-modal-backdrop: 1050;
|
||
--z-modal: 1060;
|
||
--z-tooltip: 1070;
|
||
--z-toast: 1080;
|
||
}
|
||
|
||
/* Breakpoints (for reference — CSS custom properties can't be used inside
|
||
@media conditions, so these are documented here and hard-coded below):
|
||
sm: 576px
|
||
md: 768px
|
||
lg: 992px
|
||
xl: 1200px
|
||
xxl: 1400px
|
||
*/
|
||
|
||
|
||
/* ==========================================================================
|
||
2. RESET & BASE ELEMENTS
|
||
========================================================================== */
|
||
|
||
.bwfa-scope *,
|
||
.bwfa-scope *::before,
|
||
.bwfa-scope *::after {
|
||
box-sizing: border-box;
|
||
}
|
||
|
||
.bwfa-scope * {
|
||
margin: 0;
|
||
}
|
||
|
||
.bwfa-scope {
|
||
-webkit-text-size-adjust: 100%;
|
||
tab-size: 4;
|
||
}
|
||
|
||
.bwfa-scope {
|
||
font-family: var(--font-sans);
|
||
font-size: var(--fs-base);
|
||
line-height: var(--lh-normal);
|
||
color: var(--color-text);
|
||
background-color: var(--color-bg);
|
||
min-height: 100vh;
|
||
-webkit-font-smoothing: antialiased;
|
||
}
|
||
|
||
.bwfa-scope img,
|
||
.bwfa-scope picture,
|
||
.bwfa-scope video,
|
||
.bwfa-scope canvas,
|
||
.bwfa-scope svg {
|
||
display: block;
|
||
max-width: 100%;
|
||
}
|
||
|
||
.bwfa-scope input,
|
||
.bwfa-scope button,
|
||
.bwfa-scope textarea,
|
||
.bwfa-scope select {
|
||
font: inherit;
|
||
color: inherit;
|
||
}
|
||
|
||
.bwfa-scope button {
|
||
cursor: pointer;
|
||
background: none;
|
||
border: none;
|
||
}
|
||
|
||
.bwfa-scope a {
|
||
color: var(--color-action);
|
||
text-decoration: underline;
|
||
text-underline-offset: 2px;
|
||
}
|
||
|
||
.bwfa-scope a:hover {
|
||
color: var(--color-action-hover);
|
||
}
|
||
|
||
.bwfa-scope ul,
|
||
.bwfa-scope ol {
|
||
padding-left: 1.25rem;
|
||
}
|
||
|
||
.bwfa-scope table {
|
||
border-collapse: collapse;
|
||
width: 100%;
|
||
}
|
||
|
||
.bwfa-scope hr {
|
||
border: none;
|
||
border-top: 1px solid var(--color-border);
|
||
margin: var(--space-5) 0;
|
||
}
|
||
|
||
.bwfa-scope ::selection {
|
||
background-color: var(--gray-900);
|
||
color: var(--gray-0);
|
||
}
|
||
|
||
|
||
/* ==========================================================================
|
||
3. TYPOGRAPHY
|
||
========================================================================== */
|
||
|
||
.bwfa-scope h1,
|
||
.bwfa-scope h2,
|
||
.bwfa-scope h3,
|
||
.bwfa-scope h4,
|
||
.bwfa-scope h5,
|
||
.bwfa-scope h6 {
|
||
font-weight: var(--fw-semibold);
|
||
line-height: var(--lh-tight);
|
||
color: var(--color-text);
|
||
}
|
||
|
||
.bwfa-scope h1 { font-size: var(--fs-4xl); }
|
||
.bwfa-scope h2 { font-size: var(--fs-3xl); }
|
||
.bwfa-scope h3 { font-size: var(--fs-2xl); }
|
||
.bwfa-scope h4 { font-size: var(--fs-xl); }
|
||
.bwfa-scope h5 { font-size: var(--fs-md); }
|
||
.bwfa-scope h6 { font-size: var(--fs-base); text-transform: uppercase; letter-spacing: 0.04em; }
|
||
|
||
.bwfa-scope p {
|
||
margin-bottom: var(--space-4);
|
||
}
|
||
|
||
.bwfa-scope small,
|
||
.bwfa-scope .text-small { font-size: var(--fs-sm); }
|
||
|
||
.bwfa-scope blockquote {
|
||
border-left: 3px solid var(--color-border);
|
||
padding-left: var(--space-4);
|
||
color: var(--color-text-muted);
|
||
font-style: italic;
|
||
}
|
||
|
||
.bwfa-scope code,
|
||
.bwfa-scope kbd,
|
||
.bwfa-scope samp,
|
||
.bwfa-scope pre {
|
||
font-family: var(--font-mono);
|
||
font-size: 0.9em;
|
||
}
|
||
|
||
.bwfa-scope code {
|
||
background-color: var(--color-surface-alt);
|
||
padding: 0.15em 0.4em;
|
||
border-radius: var(--radius-sm);
|
||
}
|
||
|
||
.bwfa-scope pre {
|
||
background-color: var(--gray-900);
|
||
color: var(--gray-100);
|
||
padding: var(--space-4);
|
||
border-radius: var(--radius-md);
|
||
overflow-x: auto;
|
||
}
|
||
|
||
.bwfa-scope pre code {
|
||
background: none;
|
||
padding: 0;
|
||
color: inherit;
|
||
}
|
||
|
||
.bwfa-scope .lead {
|
||
font-size: var(--fs-md);
|
||
font-weight: var(--fw-normal);
|
||
color: var(--color-text-muted);
|
||
}
|
||
|
||
|
||
/* ==========================================================================
|
||
4. LAYOUT: CONTAINER & GRID
|
||
========================================================================== */
|
||
|
||
.bwfa-scope .container {
|
||
width: 100%;
|
||
margin-inline: auto;
|
||
padding-inline: var(--gutter);
|
||
}
|
||
|
||
.bwfa-scope .container-sm { max-width: var(--container-sm); }
|
||
.bwfa-scope .container-md { max-width: var(--container-md); }
|
||
.bwfa-scope .container-lg { max-width: var(--container-lg); }
|
||
.bwfa-scope .container-xl { max-width: var(--container-xl); }
|
||
.bwfa-scope .container-xxl { max-width: var(--container-xxl); }
|
||
.bwfa-scope .container-fluid { max-width: none; }
|
||
|
||
@media (min-width: 576px) {
|
||
.bwfa-scope .container { max-width: var(--container-sm); }
|
||
}
|
||
@media (min-width: 768px) {
|
||
.bwfa-scope .container { max-width: var(--container-md); }
|
||
}
|
||
@media (min-width: 992px) {
|
||
.bwfa-scope .container { max-width: var(--container-lg); }
|
||
}
|
||
@media (min-width: 1200px) {
|
||
.bwfa-scope .container { max-width: var(--container-xl); }
|
||
}
|
||
@media (min-width: 1400px) {
|
||
.bwfa-scope .container { max-width: var(--container-xxl); }
|
||
}
|
||
|
||
/* 12-column flex grid, Bootstrap-style */
|
||
.bwfa-scope .row {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
margin-inline: calc(var(--gutter) / -2);
|
||
}
|
||
|
||
.bwfa-scope .row > * {
|
||
padding-inline: calc(var(--gutter) / 2);
|
||
width: 100%;
|
||
}
|
||
|
||
.bwfa-scope .col { flex: 1 0 0%; }
|
||
|
||
/* Generate .col-1 .. .col-12 */
|
||
.bwfa-scope .col-1 { flex: 0 0 auto; width: 8.3333%; }
|
||
.bwfa-scope .col-2 { flex: 0 0 auto; width: 16.6667%; }
|
||
.bwfa-scope .col-3 { flex: 0 0 auto; width: 25%; }
|
||
.bwfa-scope .col-4 { flex: 0 0 auto; width: 33.3333%; }
|
||
.bwfa-scope .col-5 { flex: 0 0 auto; width: 41.6667%; }
|
||
.bwfa-scope .col-6 { flex: 0 0 auto; width: 50%; }
|
||
.bwfa-scope .col-7 { flex: 0 0 auto; width: 58.3333%; }
|
||
.bwfa-scope .col-8 { flex: 0 0 auto; width: 66.6667%; }
|
||
.bwfa-scope .col-9 { flex: 0 0 auto; width: 75%; }
|
||
.bwfa-scope .col-10 { flex: 0 0 auto; width: 83.3333%; }
|
||
.bwfa-scope .col-11 { flex: 0 0 auto; width: 91.6667%; }
|
||
.bwfa-scope .col-12 { flex: 0 0 auto; width: 100%; }
|
||
|
||
.bwfa-scope .col-auto { flex: 0 0 auto; width: auto; }
|
||
|
||
/* Responsive column variants, one breakpoint shown fully; repeat pattern as needed */
|
||
@media (min-width: 768px) {
|
||
|
||
.bwfa-scope .col-md-1 { flex: 0 0 auto; width: 8.3333%; }
|
||
.bwfa-scope .col-md-2 { flex: 0 0 auto; width: 16.6667%; }
|
||
.bwfa-scope .col-md-3 { flex: 0 0 auto; width: 25%; }
|
||
.bwfa-scope .col-md-4 { flex: 0 0 auto; width: 33.3333%; }
|
||
.bwfa-scope .col-md-5 { flex: 0 0 auto; width: 41.6667%; }
|
||
.bwfa-scope .col-md-6 { flex: 0 0 auto; width: 50%; }
|
||
.bwfa-scope .col-md-7 { flex: 0 0 auto; width: 58.3333%; }
|
||
.bwfa-scope .col-md-8 { flex: 0 0 auto; width: 66.6667%; }
|
||
.bwfa-scope .col-md-9 { flex: 0 0 auto; width: 75%; }
|
||
.bwfa-scope .col-md-10 { flex: 0 0 auto; width: 83.3333%; }
|
||
.bwfa-scope .col-md-11 { flex: 0 0 auto; width: 91.6667%; }
|
||
.bwfa-scope .col-md-12 { flex: 0 0 auto; width: 100%; }
|
||
|
||
}
|
||
|
||
@media (min-width: 992px) {
|
||
|
||
.bwfa-scope .col-lg-1 { flex: 0 0 auto; width: 8.3333%; }
|
||
.bwfa-scope .col-lg-2 { flex: 0 0 auto; width: 16.6667%; }
|
||
.bwfa-scope .col-lg-3 { flex: 0 0 auto; width: 25%; }
|
||
.bwfa-scope .col-lg-4 { flex: 0 0 auto; width: 33.3333%; }
|
||
.bwfa-scope .col-lg-5 { flex: 0 0 auto; width: 41.6667%; }
|
||
.bwfa-scope .col-lg-6 { flex: 0 0 auto; width: 50%; }
|
||
.bwfa-scope .col-lg-7 { flex: 0 0 auto; width: 58.3333%; }
|
||
.bwfa-scope .col-lg-8 { flex: 0 0 auto; width: 66.6667%; }
|
||
.bwfa-scope .col-lg-9 { flex: 0 0 auto; width: 75%; }
|
||
.bwfa-scope .col-lg-10 { flex: 0 0 auto; width: 83.3333%; }
|
||
.bwfa-scope .col-lg-11 { flex: 0 0 auto; width: 91.6667%; }
|
||
.bwfa-scope .col-lg-12 { flex: 0 0 auto; width: 100%; }
|
||
|
||
}
|
||
|
||
/* CSS Grid alternative, for when flex-grid isn't the right tool */
|
||
.bwfa-scope .grid {
|
||
display: grid;
|
||
gap: var(--gutter);
|
||
grid-template-columns: repeat(12, 1fr);
|
||
}
|
||
|
||
.bwfa-scope .grid-cols-1 { grid-template-columns: repeat(1, 1fr); }
|
||
.bwfa-scope .grid-cols-2 { grid-template-columns: repeat(2, 1fr); }
|
||
.bwfa-scope .grid-cols-3 { grid-template-columns: repeat(3, 1fr); }
|
||
.bwfa-scope .grid-cols-4 { grid-template-columns: repeat(4, 1fr); }
|
||
.bwfa-scope .grid-cols-6 { grid-template-columns: repeat(6, 1fr); }
|
||
|
||
.bwfa-scope .grid-auto-fit {
|
||
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
|
||
}
|
||
|
||
|
||
/* ==========================================================================
|
||
5. FLEXBOX UTILITIES
|
||
========================================================================== */
|
||
|
||
.bwfa-scope .d-flex { display: flex; }
|
||
.bwfa-scope .d-inline-flex { display: inline-flex; }
|
||
.bwfa-scope .flex-row { flex-direction: row; }
|
||
.bwfa-scope .flex-row-reverse { flex-direction: row-reverse; }
|
||
.bwfa-scope .flex-column { flex-direction: column; }
|
||
.bwfa-scope .flex-column-reverse { flex-direction: column-reverse; }
|
||
.bwfa-scope .flex-wrap { flex-wrap: wrap; }
|
||
.bwfa-scope .flex-nowrap { flex-wrap: nowrap; }
|
||
.bwfa-scope .flex-1 { flex: 1 1 0%; }
|
||
.bwfa-scope .flex-auto { flex: 1 1 auto; }
|
||
.bwfa-scope .flex-none { flex: none; }
|
||
.bwfa-scope .flex-grow-0 { flex-grow: 0; }
|
||
.bwfa-scope .flex-grow-1 { flex-grow: 1; }
|
||
.bwfa-scope .flex-shrink-0 { flex-shrink: 0; }
|
||
|
||
.bwfa-scope .justify-start { justify-content: flex-start; }
|
||
.bwfa-scope .justify-center { justify-content: center; }
|
||
.bwfa-scope .justify-end { justify-content: flex-end; }
|
||
.bwfa-scope .justify-between { justify-content: space-between; }
|
||
.bwfa-scope .justify-around { justify-content: space-around; }
|
||
.bwfa-scope .justify-evenly { justify-content: space-evenly; }
|
||
|
||
.bwfa-scope .items-start { align-items: flex-start; }
|
||
.bwfa-scope .items-center { align-items: center; }
|
||
.bwfa-scope .items-end { align-items: flex-end; }
|
||
.bwfa-scope .items-stretch { align-items: stretch; }
|
||
.bwfa-scope .items-baseline { align-items: baseline; }
|
||
|
||
.bwfa-scope .self-start { align-self: flex-start; }
|
||
.bwfa-scope .self-center { align-self: center; }
|
||
.bwfa-scope .self-end { align-self: flex-end; }
|
||
.bwfa-scope .self-stretch { align-self: stretch; }
|
||
|
||
.bwfa-scope .gap-0 { gap: var(--space-0); }
|
||
.bwfa-scope .gap-1 { gap: var(--space-1); }
|
||
.bwfa-scope .gap-2 { gap: var(--space-2); }
|
||
.bwfa-scope .gap-3 { gap: var(--space-3); }
|
||
.bwfa-scope .gap-4 { gap: var(--space-4); }
|
||
.bwfa-scope .gap-5 { gap: var(--space-5); }
|
||
.bwfa-scope .gap-6 { gap: var(--space-6); }
|
||
|
||
|
||
/* ==========================================================================
|
||
6. SPACING UTILITIES (margin / padding, 0–9 scale)
|
||
========================================================================== */
|
||
|
||
/* Generated for all four sides, plus x/y/t/r/b/l shorthand, for margin (m)
|
||
and padding (p). Values map to the --space-* scale above. */
|
||
|
||
.bwfa-scope .m-0 { margin: var(--space-0); } .bwfa-scope .p-0 { padding: var(--space-0); }
|
||
.bwfa-scope .m-1 { margin: var(--space-1); } .bwfa-scope .p-1 { padding: var(--space-1); }
|
||
.bwfa-scope .m-2 { margin: var(--space-2); } .bwfa-scope .p-2 { padding: var(--space-2); }
|
||
.bwfa-scope .m-3 { margin: var(--space-3); } .bwfa-scope .p-3 { padding: var(--space-3); }
|
||
.bwfa-scope .m-4 { margin: var(--space-4); } .bwfa-scope .p-4 { padding: var(--space-4); }
|
||
.bwfa-scope .m-5 { margin: var(--space-5); } .bwfa-scope .p-5 { padding: var(--space-5); }
|
||
.bwfa-scope .m-6 { margin: var(--space-6); } .bwfa-scope .p-6 { padding: var(--space-6); }
|
||
.bwfa-scope .m-7 { margin: var(--space-7); } .bwfa-scope .p-7 { padding: var(--space-7); }
|
||
.bwfa-scope .m-8 { margin: var(--space-8); } .bwfa-scope .p-8 { padding: var(--space-8); }
|
||
.bwfa-scope .m-auto { margin: auto; }
|
||
|
||
.bwfa-scope .mt-0 { margin-top: var(--space-0); } .bwfa-scope .pt-0 { padding-top: var(--space-0); }
|
||
.bwfa-scope .mt-1 { margin-top: var(--space-1); } .bwfa-scope .pt-1 { padding-top: var(--space-1); }
|
||
.bwfa-scope .mt-2 { margin-top: var(--space-2); } .bwfa-scope .pt-2 { padding-top: var(--space-2); }
|
||
.bwfa-scope .mt-3 { margin-top: var(--space-3); } .bwfa-scope .pt-3 { padding-top: var(--space-3); }
|
||
.bwfa-scope .mt-4 { margin-top: var(--space-4); } .bwfa-scope .pt-4 { padding-top: var(--space-4); }
|
||
.bwfa-scope .mt-5 { margin-top: var(--space-5); } .bwfa-scope .pt-5 { padding-top: var(--space-5); }
|
||
.bwfa-scope .mt-6 { margin-top: var(--space-6); } .bwfa-scope .pt-6 { padding-top: var(--space-6); }
|
||
.bwfa-scope .mt-auto { margin-top: auto; }
|
||
|
||
.bwfa-scope .mb-0 { margin-bottom: var(--space-0); } .bwfa-scope .pb-0 { padding-bottom: var(--space-0); }
|
||
.bwfa-scope .mb-1 { margin-bottom: var(--space-1); } .bwfa-scope .pb-1 { padding-bottom: var(--space-1); }
|
||
.bwfa-scope .mb-2 { margin-bottom: var(--space-2); } .bwfa-scope .pb-2 { padding-bottom: var(--space-2); }
|
||
.bwfa-scope .mb-3 { margin-bottom: var(--space-3); } .bwfa-scope .pb-3 { padding-bottom: var(--space-3); }
|
||
.bwfa-scope .mb-4 { margin-bottom: var(--space-4); } .bwfa-scope .pb-4 { padding-bottom: var(--space-4); }
|
||
.bwfa-scope .mb-5 { margin-bottom: var(--space-5); } .bwfa-scope .pb-5 { padding-bottom: var(--space-5); }
|
||
.bwfa-scope .mb-6 { margin-bottom: var(--space-6); } .bwfa-scope .pb-6 { padding-bottom: var(--space-6); }
|
||
.bwfa-scope .mb-auto { margin-bottom: auto; }
|
||
|
||
.bwfa-scope .ms-0 { margin-left: var(--space-0); } .bwfa-scope .ps-0 { padding-left: var(--space-0); }
|
||
.bwfa-scope .ms-1 { margin-left: var(--space-1); } .bwfa-scope .ps-1 { padding-left: var(--space-1); }
|
||
.bwfa-scope .ms-2 { margin-left: var(--space-2); } .bwfa-scope .ps-2 { padding-left: var(--space-2); }
|
||
.bwfa-scope .ms-3 { margin-left: var(--space-3); } .bwfa-scope .ps-3 { padding-left: var(--space-3); }
|
||
.bwfa-scope .ms-4 { margin-left: var(--space-4); } .bwfa-scope .ps-4 { padding-left: var(--space-4); }
|
||
.bwfa-scope .ms-auto { margin-left: auto; }
|
||
|
||
.bwfa-scope .me-0 { margin-right: var(--space-0); } .bwfa-scope .pe-0 { padding-right: var(--space-0); }
|
||
.bwfa-scope .me-1 { margin-right: var(--space-1); } .bwfa-scope .pe-1 { padding-right: var(--space-1); }
|
||
.bwfa-scope .me-2 { margin-right: var(--space-2); } .bwfa-scope .pe-2 { padding-right: var(--space-2); }
|
||
.bwfa-scope .me-3 { margin-right: var(--space-3); } .bwfa-scope .pe-3 { padding-right: var(--space-3); }
|
||
.bwfa-scope .me-4 { margin-right: var(--space-4); } .bwfa-scope .pe-4 { padding-right: var(--space-4); }
|
||
.bwfa-scope .me-auto { margin-right: auto; }
|
||
|
||
.bwfa-scope .mx-0 { margin-inline: var(--space-0); } .bwfa-scope .px-0 { padding-inline: var(--space-0); }
|
||
.bwfa-scope .mx-1 { margin-inline: var(--space-1); } .bwfa-scope .px-1 { padding-inline: var(--space-1); }
|
||
.bwfa-scope .mx-2 { margin-inline: var(--space-2); } .bwfa-scope .px-2 { padding-inline: var(--space-2); }
|
||
.bwfa-scope .mx-3 { margin-inline: var(--space-3); } .bwfa-scope .px-3 { padding-inline: var(--space-3); }
|
||
.bwfa-scope .mx-4 { margin-inline: var(--space-4); } .bwfa-scope .px-4 { padding-inline: var(--space-4); }
|
||
.bwfa-scope .mx-5 { margin-inline: var(--space-5); } .bwfa-scope .px-5 { padding-inline: var(--space-5); }
|
||
.bwfa-scope .mx-auto { margin-inline: auto; }
|
||
|
||
.bwfa-scope .my-0 { margin-block: var(--space-0); } .bwfa-scope .py-0 { padding-block: var(--space-0); }
|
||
.bwfa-scope .my-1 { margin-block: var(--space-1); } .bwfa-scope .py-1 { padding-block: var(--space-1); }
|
||
.bwfa-scope .my-2 { margin-block: var(--space-2); } .bwfa-scope .py-2 { padding-block: var(--space-2); }
|
||
.bwfa-scope .my-3 { margin-block: var(--space-3); } .bwfa-scope .py-3 { padding-block: var(--space-3); }
|
||
.bwfa-scope .my-4 { margin-block: var(--space-4); } .bwfa-scope .py-4 { padding-block: var(--space-4); }
|
||
.bwfa-scope .my-5 { margin-block: var(--space-5); } .bwfa-scope .py-5 { padding-block: var(--space-5); }
|
||
.bwfa-scope .my-6 { margin-block: var(--space-6); } .bwfa-scope .py-6 { padding-block: var(--space-6); }
|
||
.bwfa-scope .my-auto { margin-block: auto; }
|
||
|
||
|
||
/* ==========================================================================
|
||
7. SIZING UTILITIES
|
||
========================================================================== */
|
||
|
||
.bwfa-scope .w-25 { width: 25%; }
|
||
.bwfa-scope .w-50 { width: 50%; }
|
||
.bwfa-scope .w-75 { width: 75%; }
|
||
.bwfa-scope .w-100 { width: 100%; }
|
||
.bwfa-scope .w-auto { width: auto; }
|
||
.bwfa-scope .w-screen { width: 100vw; }
|
||
|
||
.bwfa-scope .h-25 { height: 25%; }
|
||
.bwfa-scope .h-50 { height: 50%; }
|
||
.bwfa-scope .h-75 { height: 75%; }
|
||
.bwfa-scope .h-100 { height: 100%; }
|
||
.bwfa-scope .h-auto { height: auto; }
|
||
.bwfa-scope .h-screen { height: 100vh; }
|
||
|
||
.bwfa-scope .mw-100 { max-width: 100%; }
|
||
.bwfa-scope .mh-100 { max-height: 100%; }
|
||
.bwfa-scope .min-w-0 { min-width: 0; }
|
||
.bwfa-scope .min-h-screen { min-height: 100vh; }
|
||
|
||
|
||
/* ==========================================================================
|
||
8. DISPLAY & POSITION UTILITIES
|
||
========================================================================== */
|
||
|
||
.bwfa-scope .d-none { display: none; }
|
||
.bwfa-scope .d-block { display: block; }
|
||
.bwfa-scope .d-inline { display: inline; }
|
||
.bwfa-scope .d-inline-block { display: inline-block; }
|
||
.bwfa-scope .d-grid { display: grid; }
|
||
.bwfa-scope .d-table { display: table; }
|
||
|
||
@media (min-width: 768px) {
|
||
|
||
.bwfa-scope .d-md-none { display: none; }
|
||
.bwfa-scope .d-md-block { display: block; }
|
||
.bwfa-scope .d-md-flex { display: flex; }
|
||
|
||
}
|
||
|
||
@media (min-width: 992px) {
|
||
|
||
.bwfa-scope .d-lg-none { display: none; }
|
||
.bwfa-scope .d-lg-block { display: block; }
|
||
.bwfa-scope .d-lg-flex { display: flex; }
|
||
|
||
}
|
||
|
||
.bwfa-scope .position-static { position: static; }
|
||
.bwfa-scope .position-relative { position: relative; }
|
||
.bwfa-scope .position-absolute { position: absolute; }
|
||
.bwfa-scope .position-fixed { position: fixed; }
|
||
.bwfa-scope .position-sticky { position: sticky; top: 0; }
|
||
|
||
.bwfa-scope .top-0 { top: 0; }
|
||
.bwfa-scope .bottom-0 { bottom: 0; }
|
||
.bwfa-scope .start-0 { left: 0; }
|
||
.bwfa-scope .end-0 { right: 0; }
|
||
.bwfa-scope .inset-0 { top: 0; right: 0; bottom: 0; left: 0; }
|
||
|
||
.bwfa-scope .translate-middle {
|
||
transform: translate(-50%, -50%);
|
||
top: 50%;
|
||
left: 50%;
|
||
}
|
||
|
||
.bwfa-scope .float-start { float: left; }
|
||
.bwfa-scope .float-end { float: right; }
|
||
.bwfa-scope .clearfix::after { content: ""; display: table; clear: both; }
|
||
|
||
|
||
/* ==========================================================================
|
||
9. TEXT & COLOR UTILITIES
|
||
========================================================================== */
|
||
|
||
.bwfa-scope .text-xs { font-size: var(--fs-xs); }
|
||
.bwfa-scope .text-sm { font-size: var(--fs-sm); }
|
||
.bwfa-scope .text-base { font-size: var(--fs-base); }
|
||
.bwfa-scope .text-md { font-size: var(--fs-md); }
|
||
.bwfa-scope .text-lg { font-size: var(--fs-lg); }
|
||
.bwfa-scope .text-xl { font-size: var(--fs-xl); }
|
||
|
||
.bwfa-scope .fw-normal { font-weight: var(--fw-normal); }
|
||
.bwfa-scope .fw-medium { font-weight: var(--fw-medium); }
|
||
.bwfa-scope .fw-semibold { font-weight: var(--fw-semibold); }
|
||
.bwfa-scope .fw-bold { font-weight: var(--fw-bold); }
|
||
.bwfa-scope .fst-italic { font-style: italic; }
|
||
|
||
.bwfa-scope .text-start { text-align: left; }
|
||
.bwfa-scope .text-center { text-align: center; }
|
||
.bwfa-scope .text-end { text-align: right; }
|
||
.bwfa-scope .text-justify { text-align: justify; }
|
||
|
||
.bwfa-scope .text-uppercase { text-transform: uppercase; }
|
||
.bwfa-scope .text-lowercase { text-transform: lowercase; }
|
||
.bwfa-scope .text-capitalize { text-transform: capitalize; }
|
||
.bwfa-scope .text-decoration-none { text-decoration: none; }
|
||
.bwfa-scope .text-nowrap { white-space: nowrap; }
|
||
|
||
.bwfa-scope .text-truncate {
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
white-space: nowrap;
|
||
}
|
||
|
||
/* Multi-line clamp, bonus over Bootstrap's single-line truncate */
|
||
.bwfa-scope .text-clamp-2 { display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
|
||
.bwfa-scope .text-clamp-3 { display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden; }
|
||
|
||
.bwfa-scope .text-white { color: var(--gray-0); }
|
||
.bwfa-scope .text-black { color: var(--gray-1000); }
|
||
.bwfa-scope .text-muted { color: var(--color-text-muted); }
|
||
.bwfa-scope .text-gray-400 { color: var(--gray-400); }
|
||
.bwfa-scope .text-gray-600 { color: var(--gray-600); }
|
||
.bwfa-scope .text-success { color: var(--color-success); }
|
||
.bwfa-scope .text-danger { color: var(--color-danger); }
|
||
.bwfa-scope .text-warning { color: var(--color-warning); }
|
||
.bwfa-scope .text-info { color: var(--color-info); }
|
||
|
||
.bwfa-scope .bg-white { background-color: var(--gray-0); }
|
||
.bwfa-scope .bg-black { background-color: var(--gray-1000); }
|
||
.bwfa-scope .bg-surface { background-color: var(--color-surface); }
|
||
.bwfa-scope .bg-surface-alt { background-color: var(--color-surface-alt); }
|
||
.bwfa-scope .bg-gray-100 { background-color: var(--gray-100); }
|
||
.bwfa-scope .bg-gray-200 { background-color: var(--gray-200); }
|
||
.bwfa-scope .bg-gray-800 { background-color: var(--gray-800); }
|
||
.bwfa-scope .bg-gray-900 { background-color: var(--gray-900); }
|
||
.bwfa-scope .bg-success { background-color: var(--color-success-bg); }
|
||
.bwfa-scope .bg-danger { background-color: var(--color-danger-bg); }
|
||
.bwfa-scope .bg-warning { background-color: var(--color-warning-bg); }
|
||
.bwfa-scope .bg-info { background-color: var(--color-info-bg); }
|
||
.bwfa-scope .bg-transparent { background-color: transparent; }
|
||
|
||
|
||
/* ==========================================================================
|
||
10. BORDER, RADIUS & SHADOW UTILITIES
|
||
========================================================================== */
|
||
|
||
.bwfa-scope .border { border: 1px solid var(--color-border); }
|
||
.bwfa-scope .border-0 { border: none; }
|
||
.bwfa-scope .border-top { border-top: 1px solid var(--color-border); }
|
||
.bwfa-scope .border-bottom { border-bottom: 1px solid var(--color-border); }
|
||
.bwfa-scope .border-start { border-left: 1px solid var(--color-border); }
|
||
.bwfa-scope .border-end { border-right: 1px solid var(--color-border); }
|
||
.bwfa-scope .border-dashed { border-style: dashed; }
|
||
.bwfa-scope .border-2 { border-width: 2px; }
|
||
|
||
.bwfa-scope .rounded-none { border-radius: 0; }
|
||
.bwfa-scope .rounded-sm { border-radius: var(--radius-sm); }
|
||
.bwfa-scope .rounded-md { border-radius: var(--radius-md); }
|
||
.bwfa-scope .rounded-lg { border-radius: var(--radius-lg); }
|
||
.bwfa-scope .rounded-xl { border-radius: var(--radius-xl); }
|
||
.bwfa-scope .rounded-pill { border-radius: var(--radius-pill); }
|
||
.bwfa-scope .rounded-circle { border-radius: 50%; }
|
||
|
||
.bwfa-scope .shadow-none { box-shadow: none; }
|
||
.bwfa-scope .shadow-sm { box-shadow: var(--shadow-sm); }
|
||
.bwfa-scope .shadow-md { box-shadow: var(--shadow-md); }
|
||
.bwfa-scope .shadow-lg { box-shadow: var(--shadow-lg); }
|
||
.bwfa-scope .shadow-xl { box-shadow: var(--shadow-xl); }
|
||
|
||
|
||
/* ==========================================================================
|
||
11. BUTTONS
|
||
========================================================================== */
|
||
|
||
.bwfa-scope .btn {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
gap: var(--space-2);
|
||
padding: 0.55rem 1.1rem;
|
||
font-size: var(--fs-base);
|
||
font-weight: var(--fw-medium);
|
||
line-height: 1;
|
||
border: 1px solid transparent;
|
||
border-radius: var(--radius-md);
|
||
text-decoration: none;
|
||
transition: background-color var(--duration-base) var(--ease-standard),
|
||
border-color var(--duration-base) var(--ease-standard),
|
||
color var(--duration-base) var(--ease-standard),
|
||
transform var(--duration-fast) var(--ease-standard);
|
||
white-space: nowrap;
|
||
}
|
||
|
||
.bwfa-scope .btn:active { transform: translateY(1px); }
|
||
|
||
.bwfa-scope .btn:disabled,
|
||
.bwfa-scope .btn.disabled {
|
||
opacity: 0.5;
|
||
cursor: not-allowed;
|
||
transform: none;
|
||
}
|
||
|
||
.bwfa-scope .btn-primary {
|
||
background-color: var(--color-action);
|
||
color: var(--color-text-inverse);
|
||
border-color: var(--color-action);
|
||
}
|
||
.bwfa-scope .btn-primary:hover:not(:disabled) { background-color: var(--color-action-hover); color: var(--color-text-inverse); }
|
||
|
||
.bwfa-scope .btn-secondary {
|
||
background-color: var(--color-surface-alt);
|
||
color: var(--color-text);
|
||
border-color: var(--color-border);
|
||
}
|
||
.bwfa-scope .btn-secondary:hover:not(:disabled) { background-color: var(--gray-150); color: var(--color-text); }
|
||
|
||
.bwfa-scope .btn-outline {
|
||
background-color: transparent;
|
||
color: var(--color-text);
|
||
border-color: var(--gray-400);
|
||
}
|
||
.bwfa-scope .btn-outline:hover:not(:disabled) { background-color: var(--color-surface-alt); color: var(--color-text); }
|
||
|
||
.bwfa-scope .btn-ghost {
|
||
background-color: transparent;
|
||
color: var(--color-text);
|
||
border-color: transparent;
|
||
}
|
||
.bwfa-scope .btn-ghost:hover:not(:disabled) { background-color: var(--color-surface-alt); color: var(--color-text); }
|
||
|
||
.bwfa-scope .btn-danger {
|
||
background-color: var(--color-danger);
|
||
color: var(--gray-0);
|
||
border-color: var(--color-danger);
|
||
}
|
||
.bwfa-scope .btn-danger:hover:not(:disabled) { filter: brightness(0.9); }
|
||
|
||
.bwfa-scope .btn-link {
|
||
background: none;
|
||
border-color: transparent;
|
||
color: var(--color-action);
|
||
text-decoration: underline;
|
||
padding-inline: 0;
|
||
}
|
||
|
||
.bwfa-scope .btn-sm { padding: 0.35rem 0.75rem; font-size: var(--fs-sm); }
|
||
.bwfa-scope .btn-lg { padding: 0.85rem 1.5rem; font-size: var(--fs-md); }
|
||
.bwfa-scope .btn-icon { padding: 0.55rem; aspect-ratio: 1; }
|
||
.bwfa-scope .btn-block { display: flex; width: 100%; }
|
||
|
||
.bwfa-scope .btn-group {
|
||
display: inline-flex;
|
||
}
|
||
.bwfa-scope .btn-group .btn {
|
||
border-radius: 0;
|
||
margin-left: -1px;
|
||
}
|
||
.bwfa-scope .btn-group .btn:first-child { border-top-left-radius: var(--radius-md); border-bottom-left-radius: var(--radius-md); margin-left: 0; }
|
||
.bwfa-scope .btn-group .btn:last-child { border-top-right-radius: var(--radius-md); border-bottom-right-radius: var(--radius-md); }
|
||
.bwfa-scope .btn-group .btn:focus-visible {
|
||
outline: none;
|
||
box-shadow: inset 0 0 0 2px var(--gray-900);
|
||
z-index: 1;
|
||
}
|
||
.bwfa-scope .btn-group .btn-primary:focus-visible,
|
||
.bwfa-scope .btn-group .btn-danger:focus-visible {
|
||
box-shadow: inset 0 0 0 2px var(--gray-0);
|
||
}
|
||
|
||
|
||
/* ==========================================================================
|
||
12. FORMS
|
||
========================================================================== */
|
||
|
||
.bwfa-scope .form-group { margin-bottom: var(--space-4); }
|
||
|
||
.bwfa-scope .form-label {
|
||
display: block;
|
||
font-weight: var(--fw-medium);
|
||
font-size: var(--fs-sm);
|
||
margin-bottom: var(--space-2);
|
||
}
|
||
|
||
.bwfa-scope .form-label .required { color: var(--color-danger); margin-left: 2px; }
|
||
|
||
.bwfa-scope .form-control,
|
||
.bwfa-scope .form-select {
|
||
display: block;
|
||
width: 100%;
|
||
padding: 0.55rem 0.75rem;
|
||
font-size: var(--fs-base);
|
||
color: var(--color-text);
|
||
background-color: var(--color-bg);
|
||
border: 1px solid var(--color-border);
|
||
border-radius: var(--radius-md);
|
||
transition: border-color var(--duration-base) var(--ease-standard),
|
||
box-shadow var(--duration-base) var(--ease-standard);
|
||
}
|
||
|
||
.bwfa-scope .form-control::placeholder { color: var(--gray-400); }
|
||
|
||
.bwfa-scope .form-control:focus,
|
||
.bwfa-scope .form-select:focus {
|
||
outline: none;
|
||
border-color: var(--gray-600);
|
||
box-shadow: 0 0 0 3px var(--gray-150);
|
||
}
|
||
|
||
.bwfa-scope .form-control:disabled {
|
||
background-color: var(--color-surface-alt);
|
||
color: var(--gray-400);
|
||
cursor: not-allowed;
|
||
}
|
||
|
||
.bwfa-scope textarea.form-control { min-height: 6rem; resize: vertical; }
|
||
|
||
.bwfa-scope .form-text { font-size: var(--fs-xs); color: var(--color-text-muted); margin-top: var(--space-1); }
|
||
|
||
.bwfa-scope .form-control.is-invalid { border-color: var(--color-danger); }
|
||
.bwfa-scope .form-control.is-valid { border-color: var(--color-success); }
|
||
.bwfa-scope .invalid-feedback { font-size: var(--fs-xs); color: var(--color-danger); margin-top: var(--space-1); }
|
||
.bwfa-scope .valid-feedback { font-size: var(--fs-xs); color: var(--color-success); margin-top: var(--space-1); }
|
||
|
||
/* Checkbox / radio, custom styled rather than left as browser default */
|
||
.bwfa-scope .form-check {
|
||
display: flex;
|
||
align-items: flex-start;
|
||
gap: var(--space-2);
|
||
margin-bottom: var(--space-2);
|
||
}
|
||
|
||
.bwfa-scope .form-check-input {
|
||
appearance: none;
|
||
width: 1.1rem;
|
||
height: 1.1rem;
|
||
flex-shrink: 0;
|
||
border: 1px solid var(--gray-400);
|
||
border-radius: var(--radius-sm);
|
||
margin-top: 0.15rem;
|
||
display: grid;
|
||
place-content: center;
|
||
cursor: pointer;
|
||
}
|
||
|
||
.bwfa-scope .form-check-input[type="radio"] { border-radius: 50%; }
|
||
|
||
.bwfa-scope .form-check-input::before {
|
||
content: "";
|
||
width: 0.6rem;
|
||
height: 0.6rem;
|
||
transform: scale(0);
|
||
transition: transform var(--duration-fast) var(--ease-standard);
|
||
box-shadow: inset 1em 1em var(--color-action);
|
||
border-radius: 2px;
|
||
}
|
||
|
||
.bwfa-scope .form-check-input[type="radio"]::before { border-radius: 50%; }
|
||
.bwfa-scope .form-check-input:checked::before { transform: scale(1); }
|
||
.bwfa-scope .form-check-input:checked { border-color: var(--color-action); }
|
||
|
||
.bwfa-scope .form-check-label { font-size: var(--fs-sm); }
|
||
|
||
/* Toggle switch */
|
||
.bwfa-scope .form-switch {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: var(--space-2);
|
||
}
|
||
|
||
.bwfa-scope .form-switch input {
|
||
appearance: none;
|
||
width: 2.5rem;
|
||
height: 1.4rem;
|
||
background-color: var(--gray-300);
|
||
border-radius: var(--radius-pill);
|
||
position: relative;
|
||
cursor: pointer;
|
||
transition: background-color var(--duration-base) var(--ease-standard);
|
||
}
|
||
|
||
.bwfa-scope .form-switch input::before {
|
||
content: "";
|
||
position: absolute;
|
||
top: 2px;
|
||
left: 2px;
|
||
width: calc(1.4rem - 4px);
|
||
height: calc(1.4rem - 4px);
|
||
background-color: var(--gray-0);
|
||
border-radius: 50%;
|
||
transition: transform var(--duration-base) var(--ease-standard);
|
||
}
|
||
|
||
.bwfa-scope .form-switch input:checked { background-color: var(--color-action); }
|
||
.bwfa-scope .form-switch input:checked::before { transform: translateX(1.1rem); }
|
||
|
||
/* Input group, e.g. prefix/suffix icons or buttons attached to a field */
|
||
.bwfa-scope .input-group {
|
||
display: flex;
|
||
}
|
||
.bwfa-scope .input-group .form-control {
|
||
border-radius: 0;
|
||
}
|
||
.bwfa-scope .input-group > :first-child { border-top-left-radius: var(--radius-md); border-bottom-left-radius: var(--radius-md); }
|
||
.bwfa-scope .input-group > :last-child { border-top-right-radius: var(--radius-md); border-bottom-right-radius: var(--radius-md); }
|
||
.bwfa-scope .input-group-text {
|
||
display: flex;
|
||
align-items: center;
|
||
padding: 0 0.75rem;
|
||
background-color: var(--color-surface-alt);
|
||
border: 1px solid var(--color-border);
|
||
font-size: var(--fs-sm);
|
||
color: var(--color-text-muted);
|
||
}
|
||
|
||
/* Floating label, common Bootstrap 5 pattern */
|
||
.bwfa-scope .form-floating {
|
||
position: relative;
|
||
}
|
||
.bwfa-scope .form-floating .form-control {
|
||
padding: 1.3rem 0.75rem 0.4rem;
|
||
}
|
||
.bwfa-scope .form-floating label {
|
||
position: absolute;
|
||
top: 0;
|
||
left: 0;
|
||
padding: 0.75rem;
|
||
color: var(--gray-500);
|
||
pointer-events: none;
|
||
transform-origin: 0 0;
|
||
transition: transform var(--duration-base) var(--ease-standard), color var(--duration-base) var(--ease-standard);
|
||
}
|
||
.bwfa-scope .form-floating .form-control:focus ~ label,
|
||
.bwfa-scope .form-floating .form-control:not(:placeholder-shown) ~ label {
|
||
transform: scale(0.8) translateY(-0.6rem);
|
||
color: var(--color-text-muted);
|
||
}
|
||
|
||
|
||
/* ==========================================================================
|
||
13. CARDS
|
||
========================================================================== */
|
||
|
||
.bwfa-scope .card {
|
||
background-color: var(--color-bg);
|
||
border: 1px solid var(--color-border);
|
||
border-radius: var(--radius-lg);
|
||
overflow: hidden;
|
||
}
|
||
|
||
.bwfa-scope .card-img { width: 100%; aspect-ratio: 16 / 9; object-fit: cover; }
|
||
.bwfa-scope .card-body { padding: var(--space-5); }
|
||
.bwfa-scope .card-title { margin-bottom: var(--space-2); font-size: var(--fs-lg); }
|
||
.bwfa-scope .card-subtitle { color: var(--color-text-muted); font-size: var(--fs-sm); margin-bottom: var(--space-3); }
|
||
.bwfa-scope .card-text { color: var(--color-text-muted); }
|
||
.bwfa-scope .card-footer {
|
||
padding: var(--space-4) var(--space-5);
|
||
background-color: var(--color-surface);
|
||
border-top: 1px solid var(--color-border);
|
||
}
|
||
.bwfa-scope .card-hover {
|
||
transition: box-shadow var(--duration-base) var(--ease-standard), transform var(--duration-base) var(--ease-standard);
|
||
}
|
||
.bwfa-scope .card-hover:hover { box-shadow: var(--shadow-lg); transform: translateY(-2px); }
|
||
|
||
|
||
/* ==========================================================================
|
||
14. NAVBAR
|
||
========================================================================== */
|
||
|
||
.bwfa-scope .navbar {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
padding: var(--space-3) var(--gutter);
|
||
background-color: var(--color-bg);
|
||
border-bottom: 1px solid var(--color-border);
|
||
position: sticky;
|
||
top: 0;
|
||
z-index: var(--z-navbar);
|
||
}
|
||
|
||
.bwfa-scope .navbar-brand {
|
||
font-weight: var(--fw-bold);
|
||
font-size: var(--fs-lg);
|
||
text-decoration: none;
|
||
color: var(--color-text);
|
||
}
|
||
|
||
.bwfa-scope .navbar-nav {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: var(--space-5);
|
||
list-style: none;
|
||
padding: 0;
|
||
}
|
||
|
||
.bwfa-scope .navbar-nav a {
|
||
text-decoration: none;
|
||
color: var(--color-text-muted);
|
||
font-size: var(--fs-sm);
|
||
font-weight: var(--fw-medium);
|
||
}
|
||
.bwfa-scope .navbar-nav a:hover,
|
||
.bwfa-scope .navbar-nav a.active { color: var(--color-text); }
|
||
|
||
.bwfa-scope .navbar-toggler { display: none; }
|
||
|
||
@media (max-width: 767px) {
|
||
|
||
.bwfa-scope .navbar-nav { display: none; }
|
||
.bwfa-scope .navbar-toggler { display: block; }
|
||
|
||
}
|
||
|
||
|
||
/* ==========================================================================
|
||
15. ALERTS
|
||
========================================================================== */
|
||
|
||
.bwfa-scope .alert {
|
||
display: flex;
|
||
align-items: flex-start;
|
||
gap: var(--space-3);
|
||
padding: var(--space-4);
|
||
border: 1px solid transparent;
|
||
border-radius: var(--radius-md);
|
||
font-size: var(--fs-sm);
|
||
margin-bottom: var(--space-4);
|
||
}
|
||
|
||
.bwfa-scope .alert-neutral { background-color: var(--color-surface-alt); border-color: var(--color-border); color: var(--color-text); }
|
||
.bwfa-scope .alert-success { background-color: var(--color-success-bg); border-color: #c6e6d1; color: var(--color-success); }
|
||
.bwfa-scope .alert-danger { background-color: var(--color-danger-bg); border-color: #f1c9c6; color: var(--color-danger); }
|
||
.bwfa-scope .alert-warning { background-color: var(--color-warning-bg); border-color: #eeddb0; color: var(--color-warning); }
|
||
.bwfa-scope .alert-info { background-color: var(--color-info-bg); border-color: #c9d8e8; color: var(--color-info); }
|
||
|
||
.bwfa-scope .alert-dismiss {
|
||
margin-left: auto;
|
||
background: none;
|
||
border: none;
|
||
cursor: pointer;
|
||
opacity: 0.6;
|
||
line-height: 1;
|
||
}
|
||
.bwfa-scope .alert-dismiss:hover { opacity: 1; }
|
||
|
||
|
||
/* ==========================================================================
|
||
16. BADGES
|
||
========================================================================== */
|
||
|
||
.bwfa-scope .badge {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
padding: 0.2rem 0.55rem;
|
||
font-size: var(--fs-xs);
|
||
font-weight: var(--fw-semibold);
|
||
border-radius: var(--radius-pill);
|
||
line-height: 1.4;
|
||
}
|
||
|
||
.bwfa-scope .badge-neutral { background-color: var(--color-surface-alt); color: var(--color-text); }
|
||
.bwfa-scope .badge-dark { background-color: var(--gray-900); color: var(--gray-0); }
|
||
.bwfa-scope .badge-outline { background-color: transparent; border: 1px solid var(--gray-400); color: var(--color-text); }
|
||
.bwfa-scope .badge-success { background-color: var(--color-success-bg); color: var(--color-success); }
|
||
.bwfa-scope .badge-danger { background-color: var(--color-danger-bg); color: var(--color-danger); }
|
||
.bwfa-scope .badge-warning { background-color: var(--color-warning-bg); color: var(--color-warning); }
|
||
.bwfa-scope .badge-info { background-color: var(--color-info-bg); color: var(--color-info); }
|
||
|
||
.bwfa-scope .badge-dot {
|
||
width: 0.5rem;
|
||
height: 0.5rem;
|
||
border-radius: 50%;
|
||
display: inline-block;
|
||
margin-right: var(--space-2);
|
||
}
|
||
|
||
|
||
/* ==========================================================================
|
||
17. TABLES
|
||
========================================================================== */
|
||
|
||
.bwfa-scope .table {
|
||
width: 100%;
|
||
border-collapse: collapse;
|
||
font-size: var(--fs-sm);
|
||
}
|
||
|
||
.bwfa-scope .table th,
|
||
.bwfa-scope .table td {
|
||
padding: var(--space-3) var(--space-4);
|
||
text-align: left;
|
||
border-bottom: 1px solid var(--color-border);
|
||
}
|
||
|
||
.bwfa-scope .table th {
|
||
font-weight: var(--fw-semibold);
|
||
color: var(--color-text-muted);
|
||
text-transform: uppercase;
|
||
font-size: var(--fs-xs);
|
||
letter-spacing: 0.04em;
|
||
background-color: var(--color-surface);
|
||
}
|
||
|
||
.bwfa-scope .table-striped tbody tr:nth-child(odd) { background-color: var(--color-surface); }
|
||
.bwfa-scope .table-hover tbody tr:hover { background-color: var(--color-surface-alt); }
|
||
.bwfa-scope .table-bordered th,
|
||
.bwfa-scope .table-bordered td { border: 1px solid var(--color-border); }
|
||
.bwfa-scope .table-borderless th,
|
||
.bwfa-scope .table-borderless td { border: none; }
|
||
.bwfa-scope .table-sm th,
|
||
.bwfa-scope .table-sm td { padding: var(--space-2) var(--space-3); }
|
||
|
||
.bwfa-scope .table-responsive {
|
||
width: 100%;
|
||
overflow-x: auto;
|
||
-webkit-overflow-scrolling: touch;
|
||
}
|
||
|
||
|
||
/* ==========================================================================
|
||
18. TABS
|
||
========================================================================== */
|
||
|
||
.bwfa-scope .tabs {
|
||
border: 1px solid var(--color-border);
|
||
border-radius: var(--radius-md);
|
||
overflow: hidden;
|
||
}
|
||
|
||
.bwfa-scope .tab-list {
|
||
display: flex;
|
||
background-color: var(--color-surface);
|
||
border-bottom: 1px solid var(--color-border);
|
||
}
|
||
|
||
.bwfa-scope .tab-button {
|
||
flex: 1;
|
||
padding: var(--space-3) var(--space-4);
|
||
text-align: center;
|
||
font-size: var(--fs-sm);
|
||
font-weight: var(--fw-medium);
|
||
color: var(--color-text-muted);
|
||
border-right: 1px solid var(--color-border);
|
||
transition: background-color var(--duration-base) var(--ease-standard), color var(--duration-base) var(--ease-standard);
|
||
}
|
||
.bwfa-scope .tab-button:last-child { border-right: none; }
|
||
.bwfa-scope .tab-button:hover { background-color: var(--color-surface-alt); }
|
||
.bwfa-scope .tab-button.active { background-color: var(--gray-900); color: var(--gray-0); }
|
||
|
||
/* Grouped buttons sit flush against each other, so the default
|
||
outline + outline-offset (see section 31) pokes out past the button's
|
||
own edge and overlaps the neighbor — the "chunky double border" look.
|
||
An inset box-shadow stays entirely inside the button instead, and
|
||
flips to a light ring automatically when the tab's own background
|
||
is already dark. */
|
||
.bwfa-scope .tab-button:focus-visible {
|
||
outline: none;
|
||
box-shadow: inset 0 0 0 2px var(--gray-900);
|
||
position: relative;
|
||
z-index: 1;
|
||
}
|
||
.bwfa-scope .tab-button.active:focus-visible {
|
||
box-shadow: inset 0 0 0 2px var(--gray-0);
|
||
}
|
||
|
||
.bwfa-scope .tab-panel { display: none; padding: var(--space-5); }
|
||
.bwfa-scope .tab-panel.active { display: block; }
|
||
|
||
|
||
/* ==========================================================================
|
||
19. ACCORDION
|
||
========================================================================== */
|
||
|
||
.bwfa-scope .accordion-item {
|
||
border: 1px solid var(--color-border);
|
||
border-radius: var(--radius-md);
|
||
margin-bottom: var(--space-2);
|
||
overflow: hidden;
|
||
}
|
||
|
||
.bwfa-scope .accordion-header {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
width: 100%;
|
||
padding: var(--space-4);
|
||
font-weight: var(--fw-medium);
|
||
background-color: var(--color-bg);
|
||
cursor: pointer;
|
||
}
|
||
|
||
.bwfa-scope .accordion-header:hover { background-color: var(--color-surface); }
|
||
|
||
.bwfa-scope .accordion-icon {
|
||
transition: transform var(--duration-base) var(--ease-standard);
|
||
flex-shrink: 0;
|
||
}
|
||
.bwfa-scope .accordion-item.open .accordion-icon { transform: rotate(180deg); }
|
||
|
||
/* Grid-rows trick: animating from 0fr to 1fr animates a "height: auto"
|
||
target smoothly, without any JS measuring scrollHeight. Far fewer
|
||
moving parts than the old max-height/scrollHeight approach, and it
|
||
can't get out of sync with content that changes size dynamically. */
|
||
.bwfa-scope .accordion-body {
|
||
display: grid;
|
||
grid-template-rows: 0fr;
|
||
transition: grid-template-rows var(--duration-base) var(--ease-standard);
|
||
}
|
||
.bwfa-scope .accordion-item.open .accordion-body {
|
||
grid-template-rows: 1fr;
|
||
}
|
||
.bwfa-scope .accordion-body > * {
|
||
overflow: hidden;
|
||
min-height: 0;
|
||
}
|
||
/* This element must stay padding-free — it's the one grid measures for the
|
||
0fr/1fr collapse, and padding on it would show up as a leftover sliver
|
||
even at "0" height, since padding is part of an element's own box and
|
||
isn't something overflow:hidden can clip away. Put the visible padding
|
||
one level deeper instead, on .accordion-body-content. */
|
||
.bwfa-scope .accordion-body-inner {
|
||
padding: 0;
|
||
}
|
||
.bwfa-scope .accordion-body-content {
|
||
padding: 0 var(--space-4) var(--space-4);
|
||
color: var(--color-text-muted);
|
||
font-size: var(--fs-sm);
|
||
}
|
||
|
||
@media (prefers-reduced-motion: reduce) {
|
||
|
||
.bwfa-scope .accordion-body { transition: none; }
|
||
|
||
}
|
||
|
||
|
||
/* ==========================================================================
|
||
20. DROPDOWN
|
||
========================================================================== */
|
||
|
||
.bwfa-scope .dropdown { position: relative; display: inline-block; }
|
||
|
||
.bwfa-scope .dropdown-menu {
|
||
position: absolute;
|
||
top: calc(100% + 4px);
|
||
left: 0;
|
||
min-width: 180px;
|
||
background-color: var(--color-bg);
|
||
border: 1px solid var(--color-border);
|
||
border-radius: var(--radius-md);
|
||
box-shadow: var(--shadow-md);
|
||
padding: var(--space-2);
|
||
z-index: var(--z-dropdown);
|
||
display: none;
|
||
}
|
||
|
||
.bwfa-scope .dropdown.open .dropdown-menu { display: block; }
|
||
|
||
.bwfa-scope .dropdown-item {
|
||
display: block;
|
||
width: 100%;
|
||
text-align: left;
|
||
padding: var(--space-2) var(--space-3);
|
||
border-radius: var(--radius-sm);
|
||
font-size: var(--fs-sm);
|
||
color: var(--color-text);
|
||
text-decoration: none;
|
||
}
|
||
.bwfa-scope .dropdown-item:hover { background-color: var(--color-surface-alt); }
|
||
.bwfa-scope .dropdown-divider { border-top: 1px solid var(--color-border); margin: var(--space-2) 0; }
|
||
|
||
|
||
/* ==========================================================================
|
||
21. MODAL
|
||
========================================================================== */
|
||
|
||
.bwfa-scope .modal-backdrop {
|
||
position: fixed;
|
||
inset: 0;
|
||
background-color: rgba(0, 0, 0, 0.45);
|
||
z-index: var(--z-modal-backdrop);
|
||
display: none;
|
||
}
|
||
|
||
.bwfa-scope .modal {
|
||
position: fixed;
|
||
inset: 0;
|
||
z-index: var(--z-modal);
|
||
display: none;
|
||
align-items: center;
|
||
justify-content: center;
|
||
padding: var(--space-4);
|
||
}
|
||
|
||
.bwfa-scope .modal.open,
|
||
.bwfa-scope .modal-backdrop.open { display: flex; }
|
||
|
||
.bwfa-scope .modal-dialog {
|
||
background-color: var(--color-bg);
|
||
border-radius: var(--radius-lg);
|
||
box-shadow: var(--shadow-xl);
|
||
width: 100%;
|
||
max-height: 90vh;
|
||
overflow-y: auto;
|
||
animation: modal-in var(--duration-base) var(--ease-standard);
|
||
}
|
||
|
||
.bwfa-scope .modal-header {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
padding: var(--space-4) var(--space-5);
|
||
border-bottom: 1px solid var(--color-border);
|
||
}
|
||
|
||
.bwfa-scope .modal-title { font-size: var(--fs-md); font-weight: var(--fw-semibold); }
|
||
.bwfa-scope .modal-close { background: none; border: none; font-size: var(--fs-lg); cursor: pointer; color: var(--color-text-muted); }
|
||
.bwfa-scope .modal-body { padding: var(--space-5); }
|
||
.bwfa-scope .modal-footer {
|
||
display: flex;
|
||
justify-content: flex-end;
|
||
gap: var(--space-3);
|
||
padding: var(--space-4) var(--space-5);
|
||
border-top: 1px solid var(--color-border);
|
||
}
|
||
|
||
@keyframes modal-in {
|
||
from { opacity: 0; transform: translateY(8px) scale(0.98); }
|
||
to { opacity: 1; transform: translateY(0) scale(1); }
|
||
}
|
||
|
||
|
||
/* ==========================================================================
|
||
22. TOOLTIP
|
||
========================================================================== */
|
||
|
||
.bwfa-scope .tooltip-wrapper { position: relative; display: inline-block; }
|
||
|
||
.bwfa-scope .tooltip-content {
|
||
position: absolute;
|
||
bottom: calc(100% + 6px);
|
||
left: 50%;
|
||
transform: translateX(-50%) translateY(4px);
|
||
background-color: var(--gray-900);
|
||
color: var(--gray-0);
|
||
font-size: var(--fs-xs);
|
||
padding: var(--space-1) var(--space-2);
|
||
border-radius: var(--radius-sm);
|
||
white-space: nowrap;
|
||
opacity: 0;
|
||
pointer-events: none;
|
||
transition: opacity var(--duration-fast) var(--ease-standard), transform var(--duration-fast) var(--ease-standard);
|
||
z-index: var(--z-tooltip);
|
||
}
|
||
|
||
.bwfa-scope .tooltip-content::after {
|
||
content: "";
|
||
position: absolute;
|
||
top: 100%;
|
||
left: 50%;
|
||
transform: translateX(-50%);
|
||
border: 5px solid transparent;
|
||
border-top-color: var(--gray-900);
|
||
}
|
||
|
||
.bwfa-scope .tooltip-wrapper:hover .tooltip-content {
|
||
opacity: 1;
|
||
transform: translateX(-50%) translateY(0);
|
||
}
|
||
|
||
|
||
/* ==========================================================================
|
||
23. TOAST
|
||
========================================================================== */
|
||
|
||
.bwfa-scope .toast-container {
|
||
position: fixed;
|
||
bottom: var(--space-5);
|
||
right: var(--space-5);
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: var(--space-3);
|
||
z-index: var(--z-toast);
|
||
}
|
||
|
||
.bwfa-scope .toast {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: var(--space-3);
|
||
min-width: 280px;
|
||
max-width: 360px;
|
||
padding: var(--space-3) var(--space-4);
|
||
background-color: var(--gray-900);
|
||
color: var(--gray-0);
|
||
border-radius: var(--radius-md);
|
||
box-shadow: var(--shadow-lg);
|
||
font-size: var(--fs-sm);
|
||
animation: toast-in var(--duration-base) var(--ease-standard);
|
||
}
|
||
|
||
@keyframes toast-in {
|
||
from { opacity: 0; transform: translateX(20px); }
|
||
to { opacity: 1; transform: translateX(0); }
|
||
}
|
||
|
||
|
||
/* ==========================================================================
|
||
24. PAGINATION & BREADCRUMB
|
||
========================================================================== */
|
||
|
||
.bwfa-scope .pagination {
|
||
display: flex;
|
||
list-style: none;
|
||
padding: 0;
|
||
gap: var(--space-1);
|
||
}
|
||
|
||
.bwfa-scope .page-item a,
|
||
.bwfa-scope .page-item span {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
min-width: 2.25rem;
|
||
height: 2.25rem;
|
||
border: 1px solid var(--color-border);
|
||
border-radius: var(--radius-sm);
|
||
text-decoration: none;
|
||
color: var(--color-text);
|
||
font-size: var(--fs-sm);
|
||
}
|
||
|
||
.bwfa-scope .page-item a:hover { background-color: var(--color-surface-alt); }
|
||
.bwfa-scope .page-item.active span { background-color: var(--gray-900); color: var(--gray-0); border-color: var(--gray-900); }
|
||
.bwfa-scope .page-item.disabled span { color: var(--gray-300); cursor: not-allowed; }
|
||
|
||
.bwfa-scope .breadcrumb {
|
||
display: flex;
|
||
align-items: center;
|
||
list-style: none;
|
||
padding: 0;
|
||
font-size: var(--fs-sm);
|
||
color: var(--color-text-muted);
|
||
}
|
||
|
||
.bwfa-scope .breadcrumb-item + .breadcrumb-item::before {
|
||
content: "/";
|
||
margin: 0 var(--space-2);
|
||
color: var(--gray-300);
|
||
}
|
||
|
||
.bwfa-scope .breadcrumb-item a { color: var(--color-text-muted); text-decoration: none; }
|
||
.bwfa-scope .breadcrumb-item a:hover { color: var(--color-text); }
|
||
.bwfa-scope .breadcrumb-item.active { color: var(--color-text); }
|
||
|
||
|
||
/* ==========================================================================
|
||
25. PROGRESS & SPINNER
|
||
========================================================================== */
|
||
|
||
.bwfa-scope .progress {
|
||
width: 100%;
|
||
height: 0.5rem;
|
||
background-color: var(--color-surface-alt);
|
||
border-radius: var(--radius-pill);
|
||
overflow: hidden;
|
||
}
|
||
|
||
.bwfa-scope .progress-bar {
|
||
height: 100%;
|
||
background-color: var(--gray-900);
|
||
border-radius: var(--radius-pill);
|
||
transition: width var(--duration-slow) var(--ease-standard);
|
||
}
|
||
|
||
.bwfa-scope .progress-bar-striped {
|
||
background-image: linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%,
|
||
rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);
|
||
background-size: 1rem 1rem;
|
||
}
|
||
|
||
.bwfa-scope .spinner {
|
||
width: 1.5rem;
|
||
height: 1.5rem;
|
||
border: 2px solid var(--color-border);
|
||
border-top-color: var(--gray-900);
|
||
border-radius: 50%;
|
||
animation: spin 0.7s linear infinite;
|
||
}
|
||
|
||
.bwfa-scope .spinner-sm { width: 1rem; height: 1rem; border-width: 2px; }
|
||
.bwfa-scope .spinner-lg { width: 2.5rem; height: 2.5rem; border-width: 3px; }
|
||
|
||
@keyframes spin {
|
||
to { transform: rotate(360deg); }
|
||
}
|
||
|
||
|
||
/* ==========================================================================
|
||
26. AVATAR & LIST GROUP
|
||
========================================================================== */
|
||
|
||
.bwfa-scope .avatar {
|
||
width: 2.5rem;
|
||
height: 2.5rem;
|
||
border-radius: 50%;
|
||
object-fit: cover;
|
||
background-color: var(--color-surface-alt);
|
||
display: inline-flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
font-weight: var(--fw-semibold);
|
||
color: var(--color-text-muted);
|
||
font-size: var(--fs-sm);
|
||
}
|
||
|
||
.bwfa-scope .avatar-sm { width: 1.75rem; height: 1.75rem; font-size: var(--fs-xs); }
|
||
.bwfa-scope .avatar-lg { width: 3.5rem; height: 3.5rem; font-size: var(--fs-md); }
|
||
|
||
.bwfa-scope .avatar-group {
|
||
display: flex;
|
||
}
|
||
.bwfa-scope .avatar-group .avatar {
|
||
border: 2px solid var(--color-bg);
|
||
margin-left: -0.6rem;
|
||
}
|
||
.bwfa-scope .avatar-group .avatar:first-child { margin-left: 0; }
|
||
|
||
.bwfa-scope .list-group {
|
||
list-style: none;
|
||
padding: 0;
|
||
border: 1px solid var(--color-border);
|
||
border-radius: var(--radius-md);
|
||
overflow: hidden;
|
||
}
|
||
|
||
.bwfa-scope .list-group-item {
|
||
padding: var(--space-3) var(--space-4);
|
||
border-bottom: 1px solid var(--color-border);
|
||
background-color: var(--color-bg);
|
||
}
|
||
.bwfa-scope .list-group-item:last-child { border-bottom: none; }
|
||
.bwfa-scope .list-group-item.active { background-color: var(--gray-900); color: var(--gray-0); }
|
||
.bwfa-scope .list-group-item-action:hover { background-color: var(--color-surface-alt); cursor: pointer; }
|
||
|
||
|
||
/* ==========================================================================
|
||
27. DRAWER / OFFCANVAS
|
||
========================================================================== */
|
||
|
||
.bwfa-scope .drawer {
|
||
position: fixed;
|
||
top: 0;
|
||
right: 0;
|
||
height: 100%;
|
||
width: min(360px, 90vw);
|
||
background-color: var(--color-bg);
|
||
box-shadow: var(--shadow-xl);
|
||
z-index: var(--z-drawer);
|
||
transform: translateX(100%);
|
||
transition: transform var(--duration-slow) var(--ease-standard);
|
||
display: flex;
|
||
flex-direction: column;
|
||
}
|
||
|
||
.bwfa-scope .drawer.open { transform: translateX(0); }
|
||
.bwfa-scope .drawer-left { right: auto; left: 0; transform: translateX(-100%); }
|
||
.bwfa-scope .drawer-left.open { transform: translateX(0); }
|
||
|
||
.bwfa-scope .drawer-header {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
padding: var(--space-4) var(--space-5);
|
||
border-bottom: 1px solid var(--color-border);
|
||
}
|
||
|
||
.bwfa-scope .drawer-body { padding: var(--space-5); overflow-y: auto; flex: 1; }
|
||
|
||
|
||
/* ==========================================================================
|
||
28. SKELETON LOADER
|
||
========================================================================== */
|
||
|
||
.bwfa-scope .skeleton {
|
||
background: linear-gradient(90deg, var(--color-surface-alt) 25%, var(--gray-150) 37%, var(--color-surface-alt) 63%);
|
||
background-size: 400% 100%;
|
||
animation: skeleton-loading 1.4s ease infinite;
|
||
border-radius: var(--radius-sm);
|
||
}
|
||
|
||
.bwfa-scope .skeleton-text { height: 0.9em; margin-bottom: var(--space-2); }
|
||
.bwfa-scope .skeleton-title { height: 1.5em; width: 60%; margin-bottom: var(--space-3); }
|
||
.bwfa-scope .skeleton-avatar { width: 2.5rem; height: 2.5rem; border-radius: 50%; }
|
||
|
||
@keyframes skeleton-loading {
|
||
0% { background-position: 100% 50%; }
|
||
100% { background-position: 0 50%; }
|
||
}
|
||
|
||
|
||
/* ==========================================================================
|
||
29. MISC UTILITIES
|
||
========================================================================== */
|
||
|
||
.bwfa-scope .cursor-pointer { cursor: pointer; }
|
||
.bwfa-scope .cursor-not-allowed { cursor: not-allowed; }
|
||
.bwfa-scope .cursor-grab { cursor: grab; }
|
||
|
||
.bwfa-scope .select-none { user-select: none; }
|
||
.bwfa-scope .select-all { user-select: all; }
|
||
|
||
.bwfa-scope .opacity-0 { opacity: 0; }
|
||
.bwfa-scope .opacity-25 { opacity: 0.25; }
|
||
.bwfa-scope .opacity-50 { opacity: 0.5; }
|
||
.bwfa-scope .opacity-75 { opacity: 0.75; }
|
||
.bwfa-scope .opacity-100 { opacity: 1; }
|
||
|
||
.bwfa-scope .overflow-hidden { overflow: hidden; }
|
||
.bwfa-scope .overflow-auto { overflow: auto; }
|
||
.bwfa-scope .overflow-x-auto { overflow-x: auto; }
|
||
.bwfa-scope .overflow-y-auto { overflow-y: auto; }
|
||
.bwfa-scope .overflow-scroll { overflow: scroll; }
|
||
|
||
.bwfa-scope .aspect-square { aspect-ratio: 1 / 1; }
|
||
.bwfa-scope .aspect-video { aspect-ratio: 16 / 9; }
|
||
.bwfa-scope .aspect-portrait { aspect-ratio: 3 / 4; }
|
||
|
||
.bwfa-scope .object-cover { object-fit: cover; }
|
||
.bwfa-scope .object-contain { object-fit: contain; }
|
||
|
||
.bwfa-scope .z-0 { z-index: 0; }
|
||
.bwfa-scope .z-10 { z-index: 10; }
|
||
.bwfa-scope .z-dropdown { z-index: var(--z-dropdown); }
|
||
.bwfa-scope .z-modal { z-index: var(--z-modal); }
|
||
|
||
.bwfa-scope .visually-hidden {
|
||
position: absolute !important;
|
||
width: 1px; height: 1px;
|
||
padding: 0; margin: -1px;
|
||
overflow: hidden;
|
||
clip: rect(0, 0, 0, 0);
|
||
white-space: nowrap;
|
||
border: 0;
|
||
}
|
||
|
||
.bwfa-scope .divider {
|
||
height: 1px;
|
||
background-color: var(--color-border);
|
||
border: none;
|
||
margin: var(--space-5) 0;
|
||
}
|
||
|
||
.bwfa-scope .divider-vertical {
|
||
width: 1px;
|
||
align-self: stretch;
|
||
background-color: var(--color-border);
|
||
}
|
||
|
||
|
||
/* ==========================================================================
|
||
30. DARK MODE
|
||
========================================================================== */
|
||
|
||
/* Intentionally removed: this plugin only ever renders in light mode,
|
||
regardless of the visitor's system/browser dark-mode preference. The
|
||
framework's dark-mode variable overrides (both the explicit
|
||
data-theme="dark" toggle and the automatic prefers-color-scheme
|
||
follow) have been deliberately stripped out below, rather than merely
|
||
overridden, so there's no dark-mode styling left anywhere in this file
|
||
that could be triggered under any circumstance. */
|
||
|
||
|
||
/* ==========================================================================
|
||
31. ACCESSIBILITY
|
||
========================================================================== */
|
||
|
||
.bwfa-scope .skip-link {
|
||
position: absolute;
|
||
left: -999px;
|
||
top: 0;
|
||
background-color: var(--gray-900);
|
||
color: var(--gray-0);
|
||
padding: var(--space-2) var(--space-4);
|
||
z-index: 2000;
|
||
}
|
||
.bwfa-scope .skip-link:focus { left: var(--space-2); top: var(--space-2); }
|
||
|
||
.bwfa-scope :focus-visible {
|
||
outline: 2px solid var(--gray-900);
|
||
outline-offset: 2px;
|
||
}
|
||
|
||
@media (prefers-reduced-motion: reduce) {
|
||
|
||
.bwfa-scope *,
|
||
.bwfa-scope *::before,
|
||
.bwfa-scope *::after {
|
||
animation-duration: 0.01ms !important;
|
||
animation-iteration-count: 1 !important;
|
||
transition-duration: 0.01ms !important;
|
||
scroll-behavior: auto !important;
|
||
}
|
||
|
||
}
|
||
|
||
|
||
/* ==========================================================================
|
||
32. PRINT STYLES
|
||
========================================================================== */
|
||
|
||
@media print {
|
||
|
||
.bwfa-scope .navbar,
|
||
.bwfa-scope .drawer,
|
||
.bwfa-scope .modal,
|
||
.bwfa-scope .modal-backdrop,
|
||
.bwfa-scope .toast-container,
|
||
.bwfa-scope .btn,
|
||
.bwfa-scope .no-print {
|
||
display: none !important;
|
||
}
|
||
.bwfa-scope {
|
||
color: #000;
|
||
background: #fff;
|
||
}
|
||
.bwfa-scope a { text-decoration: underline; color: #000; }
|
||
.bwfa-scope .card,
|
||
.bwfa-scope .table { box-shadow: none !important; border: 1px solid #999; }
|
||
|
||
}
|
||
|
||
|
||
/* ==========================================================================
|
||
33. ADVANCED TRICKS — things Bootstrap doesn't ship with
|
||
========================================================================== */
|
||
|
||
/* Native form controls (date pickers, scrollbars, selection UI) render in
|
||
the browser's dark theme automatically when this is set, instead of
|
||
staying stuck light inside a dark page. */
|
||
.bwfa-scope { color-scheme: light; }
|
||
|
||
/* Makes native checkboxes/radios/range inputs that you didn't restyle
|
||
still match the palette, cheaply. */
|
||
.bwfa-scope input,
|
||
.bwfa-scope select,
|
||
.bwfa-scope textarea { accent-color: var(--color-action); }
|
||
|
||
/* --- Scroll reveal ---------------------------------------------------
|
||
Pair with the IntersectionObserver in framework.js. Add .reveal to any
|
||
element; it fades/slides in once .revealed is toggled on by JS. */
|
||
.bwfa-scope .reveal {
|
||
opacity: 0;
|
||
transform: translateY(16px);
|
||
transition: opacity var(--duration-slow) var(--ease-standard),
|
||
transform var(--duration-slow) var(--ease-standard);
|
||
}
|
||
.bwfa-scope .reveal.revealed { opacity: 1; transform: translateY(0); }
|
||
|
||
@media (prefers-reduced-motion: reduce) {
|
||
|
||
.bwfa-scope .reveal { opacity: 1; transform: none; transition: none; }
|
||
|
||
}
|
||
|
||
/* --- Sticky header shadow-on-scroll -----------------------------------
|
||
framework.js toggles this class once the page scrolls past a threshold,
|
||
so the navbar only casts a shadow when there's content sliding under it. */
|
||
.bwfa-scope .navbar-scrolled {
|
||
box-shadow: var(--shadow-sm);
|
||
border-bottom-color: transparent;
|
||
}
|
||
|
||
/* --- Glassmorphism, used sparingly (e.g. a navbar over a hero image) -- */
|
||
.bwfa-scope .glass {
|
||
background-color: rgba(255, 255, 255, 0.6);
|
||
backdrop-filter: blur(12px) saturate(140%);
|
||
-webkit-backdrop-filter: blur(12px) saturate(140%);
|
||
border: 1px solid rgba(255, 255, 255, 0.3);
|
||
}
|
||
|
||
/* --- Gradient text, grayscale only so it stays on-brand --------------- */
|
||
.bwfa-scope .text-gradient {
|
||
background: linear-gradient(90deg, var(--gray-900), var(--gray-500));
|
||
-webkit-background-clip: text;
|
||
background-clip: text;
|
||
color: transparent;
|
||
-webkit-text-fill-color: transparent; /* Safari ignores color:transparent here without this */
|
||
}
|
||
|
||
/* --- Custom scrollbar (WebKit + Firefox) ------------------------------ */
|
||
.bwfa-scope .scroll-styled {
|
||
scrollbar-width: thin;
|
||
scrollbar-color: var(--gray-400) transparent;
|
||
}
|
||
.bwfa-scope .scroll-styled::-webkit-scrollbar { width: 8px; height: 8px; }
|
||
.bwfa-scope .scroll-styled::-webkit-scrollbar-track { background: transparent; }
|
||
.bwfa-scope .scroll-styled::-webkit-scrollbar-thumb {
|
||
background-color: var(--gray-400);
|
||
border-radius: var(--radius-pill);
|
||
}
|
||
.bwfa-scope .scroll-styled::-webkit-scrollbar-thumb:hover { background-color: var(--gray-600); }
|
||
|
||
/* --- Masonry-style columns, no JS needed for the layout itself -------- */
|
||
.bwfa-scope .masonry {
|
||
column-count: 3;
|
||
column-gap: var(--gutter);
|
||
}
|
||
.bwfa-scope .masonry > * {
|
||
break-inside: avoid;
|
||
margin-bottom: var(--gutter);
|
||
}
|
||
@media (max-width: 991px) {
|
||
.bwfa-scope .masonry { column-count: 2; }
|
||
}
|
||
@media (max-width: 575px) {
|
||
.bwfa-scope .masonry { column-count: 1; }
|
||
}
|
||
|
||
/* --- Container queries -------------------------------------------------
|
||
A card that switches to a horizontal layout once its own container is
|
||
wide enough, regardless of viewport width. Wrap it in .card-container. */
|
||
.bwfa-scope .card-container { container-type: inline-size; container-name: card; }
|
||
|
||
@container card (min-width: 420px) {
|
||
|
||
.bwfa-scope .card-adaptive { display: flex; }
|
||
.bwfa-scope .card-adaptive .card-img { width: 40%; aspect-ratio: 1; }
|
||
.bwfa-scope .card-adaptive .card-body { flex: 1; }
|
||
|
||
}
|
||
|
||
/* --- Notification pulse, for "something needs your attention" dots --- */
|
||
.bwfa-scope .badge-pulse { position: relative; }
|
||
.bwfa-scope .badge-pulse::before {
|
||
content: "";
|
||
position: absolute;
|
||
inset: 0;
|
||
border-radius: inherit;
|
||
background: inherit;
|
||
animation: pulse-ring 1.6s cubic-bezier(0.4, 0, 0.6, 1) infinite;
|
||
}
|
||
@keyframes pulse-ring {
|
||
0% { opacity: 0.7; transform: scale(1); }
|
||
100% { opacity: 0; transform: scale(2.2); }
|
||
}
|
||
|
||
/* --- Ripple effect surface ---------------------------------------------
|
||
framework.js injects a .ripple-dot span with inline position/size on
|
||
click; this just defines what that span looks like and how it animates. */
|
||
.bwfa-scope .ripple-surface { position: relative; overflow: hidden; }
|
||
.bwfa-scope .ripple-dot {
|
||
position: absolute;
|
||
border-radius: 50%;
|
||
background-color: rgba(255, 255, 255, 0.5);
|
||
transform: scale(0);
|
||
animation: ripple 550ms var(--ease-standard);
|
||
pointer-events: none;
|
||
}
|
||
.bwfa-scope .btn-outline .ripple-dot,
|
||
.bwfa-scope .btn-secondary .ripple-dot,
|
||
.bwfa-scope .btn-ghost .ripple-dot { background-color: rgba(0, 0, 0, 0.08); }
|
||
@keyframes ripple {
|
||
to { transform: scale(2.5); opacity: 0; }
|
||
}
|
||
|
||
/* --- Copy-to-clipboard button state ------------------------------------
|
||
framework.js toggles [data-copied] on the trigger for ~1.5s after a
|
||
successful copy; style the "Copied!" state here instead of in JS. */
|
||
.bwfa-scope [data-copy][data-copied] { color: var(--color-success); border-color: var(--color-success); }
|
||
|
||
/* --- Count-up numbers, purely typographic; JS drives the actual count - */
|
||
.bwfa-scope .stat-number {
|
||
font-size: var(--fs-3xl);
|
||
font-weight: var(--fw-bold);
|
||
font-variant-numeric: tabular-nums;
|
||
letter-spacing: -0.02em;
|
||
}
|
||
|
||
/* --- Marquee / ticker, pure CSS, pauses on hover ------------------------ */
|
||
.bwfa-scope .marquee {
|
||
overflow: hidden;
|
||
white-space: nowrap;
|
||
-webkit-mask-image: linear-gradient(90deg, transparent, #000 8%, #000 92%, transparent);
|
||
mask-image: linear-gradient(90deg, transparent, #000 8%, #000 92%, transparent);
|
||
}
|
||
.bwfa-scope .marquee-track {
|
||
display: inline-block;
|
||
padding-left: 100%;
|
||
animation: marquee 18s linear infinite;
|
||
}
|
||
.bwfa-scope .marquee:hover .marquee-track { animation-play-state: paused; }
|
||
@keyframes marquee {
|
||
from { transform: translateX(0); }
|
||
to { transform: translateX(-100%); }
|
||
}
|
||
|
||
/* --- Focus trap boundary marker (used by JS, no visual by itself) ----- */
|
||
.bwfa-scope [data-focus-trap] { outline: none; }
|
||
|
||
/* --- Skip-to-top button, appears once page has scrolled --------------- */
|
||
.bwfa-scope .back-to-top {
|
||
position: fixed;
|
||
bottom: var(--space-5);
|
||
right: var(--space-5);
|
||
width: 2.75rem;
|
||
height: 2.75rem;
|
||
border-radius: 50%;
|
||
background-color: var(--gray-900);
|
||
color: var(--gray-0);
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
box-shadow: var(--shadow-md);
|
||
opacity: 0;
|
||
visibility: hidden;
|
||
transform: translateY(8px);
|
||
transition: opacity var(--duration-base) var(--ease-standard),
|
||
transform var(--duration-base) var(--ease-standard),
|
||
visibility var(--duration-base);
|
||
z-index: var(--z-sticky);
|
||
}
|
||
.bwfa-scope .back-to-top.visible { opacity: 1; visibility: visible; transform: translateY(0); }
|
||
|
||
/* --- High-contrast mode support ----------------------------------------- */
|
||
@media (prefers-contrast: more) {
|
||
|
||
.bwfa-scope {
|
||
--color-border: var(--gray-700);
|
||
--color-text-muted: var(--gray-800);
|
||
}
|
||
.bwfa-scope .btn,
|
||
.bwfa-scope .form-control { border-width: 2px; }
|
||
|
||
}
|
||
|
||
/* --- View Transitions API hook ------------------------------------------
|
||
If the browser supports it, cross-page or state navigations get a
|
||
quiet built-in crossfade for free — nothing else needs to change. */
|
||
@media (prefers-reduced-motion: no-preference) {
|
||
|
||
.bwfa-scope ::view-transition-old(root),
|
||
.bwfa-scope ::view-transition-new(root) {
|
||
animation-duration: var(--duration-slow);
|
||
}
|
||
|
||
}
|
||
|
||
|
||
/* ==========================================================================
|
||
34. CODE SAMPLE / SNIPPET VIEWER
|
||
Reusable "view code" disclosure with a one-click copy button, used
|
||
throughout documentation or demo pages. Needs no JS to open/close
|
||
(native <details>); the copy button is wired by data-copy-target
|
||
in framework.js.
|
||
========================================================================== */
|
||
|
||
.bwfa-scope .code-sample {
|
||
border: 1px solid var(--color-border);
|
||
border-radius: var(--radius-md);
|
||
margin-top: var(--space-3);
|
||
}
|
||
|
||
.bwfa-scope .code-sample summary {
|
||
list-style: none;
|
||
cursor: pointer;
|
||
padding: var(--space-2) var(--space-3);
|
||
font-size: var(--fs-xs);
|
||
color: var(--color-text-muted);
|
||
display: flex;
|
||
align-items: center;
|
||
gap: var(--space-2);
|
||
user-select: none;
|
||
}
|
||
.bwfa-scope .code-sample summary::-webkit-details-marker { display: none; }
|
||
.bwfa-scope .code-sample summary::before {
|
||
content: "▸";
|
||
transition: transform var(--duration-fast) var(--ease-standard);
|
||
}
|
||
.bwfa-scope .code-sample[open] summary::before { transform: rotate(90deg); }
|
||
.bwfa-scope .code-sample summary:hover { color: var(--color-text); background-color: var(--color-surface); }
|
||
|
||
.bwfa-scope .code-block {
|
||
position: relative;
|
||
border-top: 1px solid var(--color-border);
|
||
}
|
||
|
||
.bwfa-scope .code-block pre {
|
||
margin: 0;
|
||
border-radius: 0;
|
||
max-height: 320px;
|
||
overflow: auto;
|
||
}
|
||
|
||
.bwfa-scope .code-block .code-copy-btn {
|
||
position: absolute;
|
||
top: var(--space-2);
|
||
right: var(--space-2);
|
||
}
|
||
|
||
|
||
/* ==========================================================================
|
||
35. STACK & DIVIDE UTILITIES (Chakra UI's Stack, Tailwind's space-x/y)
|
||
Chakra composes layout with <VStack>/<HStack> instead of manually gapping
|
||
every parent; Tailwind's space-y-* adds margin between children instead
|
||
of using gap. Both land on the same trick: style the relationship between
|
||
siblings, not each child individually.
|
||
========================================================================== */
|
||
|
||
.bwfa-scope .stack-v { display: flex; flex-direction: column; }
|
||
.bwfa-scope .stack-h { display: flex; flex-direction: row; align-items: center; }
|
||
|
||
.bwfa-scope .stack-v > * + * { margin-top: var(--space-4); }
|
||
.bwfa-scope .stack-h > * + * { margin-left: var(--space-4); }
|
||
|
||
/* Divide: adds a rule *between* children without adding one at the edges —
|
||
the classic `> * + *` sibling trick, so you don't need a wrapper div per
|
||
divider. */
|
||
.bwfa-scope .divide-y > * + * { border-top: 1px solid var(--color-border); }
|
||
.bwfa-scope .divide-x > * + * { border-left: 1px solid var(--color-border); }
|
||
.bwfa-scope .divide-y-thick > * + * { border-top-width: 2px; }
|
||
|
||
|
||
/* ==========================================================================
|
||
36. RING UTILITY (Tailwind's focus/ring system)
|
||
Tailwind deliberately separates "ring" (an outer glow, box-shadow based)
|
||
from "border" (part of layout). Useful when you want a focus/selection
|
||
indicator that doesn't reflow surrounding content the way a border does.
|
||
========================================================================== */
|
||
|
||
.bwfa-scope .ring { box-shadow: 0 0 0 3px var(--gray-150); }
|
||
.bwfa-scope .ring-dark { box-shadow: 0 0 0 3px var(--gray-400); }
|
||
.bwfa-scope .ring-offset { box-shadow: 0 0 0 2px var(--color-bg), 0 0 0 4px var(--gray-900); }
|
||
|
||
|
||
/* ==========================================================================
|
||
37. STRETCHED LINK (Bootstrap)
|
||
Makes an entire card clickable via a single link inside it, without
|
||
wrapping the whole card in an <a> (which breaks nested interactive
|
||
elements like buttons). Add .stretched-link to the link/heading, and
|
||
.position-relative to the card ancestor.
|
||
========================================================================== */
|
||
|
||
.bwfa-scope .stretched-link::after {
|
||
content: "";
|
||
position: absolute;
|
||
inset: 0;
|
||
z-index: 1;
|
||
}
|
||
|
||
|
||
/* ==========================================================================
|
||
38. BUTTON LOADING STATE (Bulma's `is-loading`)
|
||
Toggle with Mono.setLoading(button, true/false) in framework.js, or add
|
||
data-loading directly for a static example. The label stays in the DOM
|
||
(screen readers still announce it) but is visually replaced by a spinner.
|
||
========================================================================== */
|
||
|
||
.bwfa-scope .btn[data-loading] {
|
||
color: transparent !important;
|
||
pointer-events: none;
|
||
position: relative;
|
||
}
|
||
.bwfa-scope .btn[data-loading]::after {
|
||
content: "";
|
||
position: absolute;
|
||
width: 1rem;
|
||
height: 1rem;
|
||
top: 50%;
|
||
left: 50%;
|
||
margin: -0.5rem 0 0 -0.5rem;
|
||
border: 2px solid rgba(255, 255, 255, 0.35);
|
||
border-top-color: #fff;
|
||
border-radius: 50%;
|
||
animation: spin 0.7s linear infinite;
|
||
}
|
||
.bwfa-scope .btn-outline[data-loading]::after,
|
||
.bwfa-scope .btn-secondary[data-loading]::after,
|
||
.bwfa-scope .btn-ghost[data-loading]::after {
|
||
border-color: var(--color-border);
|
||
border-top-color: var(--gray-900);
|
||
}
|
||
|
||
|
||
/* ==========================================================================
|
||
39. FLOATING ACTION BUTTON (Material Design FAB)
|
||
========================================================================== */
|
||
|
||
.bwfa-scope .fab {
|
||
position: fixed;
|
||
bottom: var(--space-6);
|
||
right: var(--space-6);
|
||
width: 3.5rem;
|
||
height: 3.5rem;
|
||
border-radius: 50%;
|
||
background-color: var(--gray-900);
|
||
color: var(--gray-0);
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
font-size: var(--fs-xl);
|
||
box-shadow: var(--shadow-lg);
|
||
z-index: var(--z-sticky);
|
||
transition: transform var(--duration-fast) var(--ease-standard), box-shadow var(--duration-base) var(--ease-standard);
|
||
}
|
||
.bwfa-scope .fab:hover { transform: translateY(-2px); box-shadow: var(--shadow-xl); }
|
||
.bwfa-scope .fab-extended { width: auto; border-radius: var(--radius-pill); padding: 0 var(--space-5); gap: var(--space-2); font-size: var(--fs-base); }
|
||
|
||
|
||
/* ==========================================================================
|
||
40. CHIPS / TAGS (Material Chips, Bulma Tags — interactive, removable;
|
||
distinct from .badge, which is a static label)
|
||
========================================================================== */
|
||
|
||
.bwfa-scope .chip {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: var(--space-2);
|
||
padding: 0.3rem 0.5rem 0.3rem 0.85rem;
|
||
border-radius: var(--radius-pill);
|
||
background-color: var(--color-surface-alt);
|
||
border: 1px solid var(--color-border);
|
||
font-size: var(--fs-sm);
|
||
}
|
||
|
||
.bwfa-scope .chip-remove {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
width: 1.1rem;
|
||
height: 1.1rem;
|
||
border-radius: 50%;
|
||
background: none;
|
||
color: var(--color-text-muted);
|
||
font-size: var(--fs-xs);
|
||
line-height: 1;
|
||
}
|
||
.bwfa-scope .chip-remove:hover { background-color: var(--gray-200); color: var(--color-text); }
|
||
|
||
.bwfa-scope .chip-group { display: flex; flex-wrap: wrap; gap: var(--space-2); }
|
||
|
||
|
||
/* ==========================================================================
|
||
41. SEGMENTED CONTROL (Material/iOS segmented buttons, Ant Design's
|
||
Radio.Group buttonStyle="solid" — a toggle group styled as one control,
|
||
visually distinct from .tabs even though the interaction is similar)
|
||
========================================================================== */
|
||
|
||
.bwfa-scope .segmented {
|
||
display: inline-flex;
|
||
padding: 3px;
|
||
background-color: var(--color-surface-alt);
|
||
border-radius: var(--radius-md);
|
||
gap: 2px;
|
||
}
|
||
|
||
.bwfa-scope .segmented-option {
|
||
padding: 0.4rem 0.9rem;
|
||
font-size: var(--fs-sm);
|
||
font-weight: var(--fw-medium);
|
||
color: var(--color-text-muted);
|
||
border-radius: calc(var(--radius-md) - 3px);
|
||
transition: background-color var(--duration-fast) var(--ease-standard), color var(--duration-fast) var(--ease-standard);
|
||
}
|
||
.bwfa-scope .segmented-option:hover { color: var(--color-text); }
|
||
.bwfa-scope .segmented-option.active {
|
||
background-color: var(--color-bg);
|
||
color: var(--color-text);
|
||
box-shadow: var(--shadow-sm);
|
||
}
|
||
.bwfa-scope .segmented-option:focus-visible {
|
||
outline: none;
|
||
box-shadow: inset 0 0 0 2px var(--gray-900);
|
||
}
|
||
.bwfa-scope .segmented-option.active:focus-visible {
|
||
box-shadow: var(--shadow-sm), inset 0 0 0 2px var(--gray-900);
|
||
}
|
||
|
||
|
||
/* ==========================================================================
|
||
42. ELEVATION SCALE (Material Design's numbered elevation levels —
|
||
same box-shadow tokens as section 10, just exposed under the naming
|
||
convention Material developers already know)
|
||
========================================================================== */
|
||
|
||
.bwfa-scope .elevation-0 { box-shadow: none; }
|
||
.bwfa-scope .elevation-1 { box-shadow: var(--shadow-sm); }
|
||
.bwfa-scope .elevation-2 { box-shadow: var(--shadow-md); }
|
||
.bwfa-scope .elevation-3 { box-shadow: var(--shadow-lg); }
|
||
.bwfa-scope .elevation-4 { box-shadow: var(--shadow-xl); }
|
||
.bwfa-scope .elevation-5 { box-shadow: 0 24px 56px rgba(0, 0, 0, 0.22); }
|
||
|
||
|
||
/* ==========================================================================
|
||
43. KBD (Chakra/Semantic/GitHub-style keyboard key)
|
||
========================================================================== */
|
||
|
||
.bwfa-scope .kbd {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
min-width: 1.6em;
|
||
padding: 0.15em 0.5em;
|
||
font-family: var(--font-mono);
|
||
font-size: 0.8em;
|
||
color: var(--color-text);
|
||
background-color: var(--color-surface);
|
||
border: 1px solid var(--color-border);
|
||
border-bottom-width: 2px;
|
||
border-radius: var(--radius-sm);
|
||
}
|
||
|
||
|
||
/* ==========================================================================
|
||
44. STAR RATING (Ant Design's Rate — pure CSS, no JS)
|
||
Radio inputs in reverse DOM order + the general sibling combinator, so
|
||
hovering or selecting a star also fills every star before it, using only
|
||
forward CSS selectors (CSS can't select backwards, so the trick is to
|
||
lay the stars out backwards and un-reverse them visually with flex).
|
||
========================================================================== */
|
||
|
||
.bwfa-scope .star-rating {
|
||
display: inline-flex;
|
||
flex-direction: row-reverse;
|
||
font-size: var(--fs-xl);
|
||
}
|
||
.bwfa-scope .star-rating input {
|
||
position: absolute;
|
||
opacity: 0;
|
||
pointer-events: none;
|
||
}
|
||
.bwfa-scope .star-rating label {
|
||
color: var(--gray-300);
|
||
cursor: pointer;
|
||
padding: 0 0.05em;
|
||
transition: color var(--duration-fast) var(--ease-standard);
|
||
}
|
||
.bwfa-scope .star-rating input:checked ~ label,
|
||
.bwfa-scope .star-rating label:hover,
|
||
.bwfa-scope .star-rating label:hover ~ label {
|
||
color: var(--gray-900);
|
||
}
|
||
|
||
|
||
/* ==========================================================================
|
||
45. EMPTY STATE (Ant Design's Empty)
|
||
========================================================================== */
|
||
|
||
.bwfa-scope .empty-state {
|
||
text-align: center;
|
||
padding: var(--space-8) var(--space-4);
|
||
color: var(--color-text-muted);
|
||
}
|
||
.bwfa-scope .empty-state-icon {
|
||
font-size: var(--fs-4xl);
|
||
margin-bottom: var(--space-4);
|
||
opacity: 0.4;
|
||
}
|
||
.bwfa-scope .empty-state-title {
|
||
font-weight: var(--fw-semibold);
|
||
color: var(--color-text);
|
||
margin-bottom: var(--space-2);
|
||
}
|
||
|
||
|
||
/* ==========================================================================
|
||
46. RESULT / STATUS PAGE (Ant Design's Result — the "big icon, heading,
|
||
description, actions" pattern used for 404s, success confirmations,
|
||
permission-denied pages, etc.)
|
||
========================================================================== */
|
||
|
||
.bwfa-scope .result {
|
||
max-width: 480px;
|
||
margin-inline: auto;
|
||
text-align: center;
|
||
padding: var(--space-8) var(--space-4);
|
||
}
|
||
.bwfa-scope .result-icon { font-size: var(--fs-4xl); margin-bottom: var(--space-5); }
|
||
.bwfa-scope .result-icon.success { color: var(--color-success); }
|
||
.bwfa-scope .result-icon.danger { color: var(--color-danger); }
|
||
.bwfa-scope .result-title { margin-bottom: var(--space-2); }
|
||
.bwfa-scope .result-description { color: var(--color-text-muted); margin-bottom: var(--space-5); }
|
||
.bwfa-scope .result-actions { display: flex; justify-content: center; gap: var(--space-3); }
|
||
|
||
|
||
/* ==========================================================================
|
||
47. DIVIDER WITH TEXT (Semantic UI's Divider — the "or continue with"
|
||
pattern on login screens)
|
||
========================================================================== */
|
||
|
||
.bwfa-scope .divider-text {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: var(--space-3);
|
||
color: var(--color-text-muted);
|
||
font-size: var(--fs-xs);
|
||
text-transform: uppercase;
|
||
letter-spacing: 0.04em;
|
||
margin: var(--space-5) 0;
|
||
}
|
||
.bwfa-scope .divider-text::before,
|
||
.bwfa-scope .divider-text::after {
|
||
content: "";
|
||
flex: 1;
|
||
height: 1px;
|
||
background-color: var(--color-border);
|
||
}
|
||
|
||
|
||
/* ==========================================================================
|
||
48. STEPPER / STEPS (Material Stepper, Ant Design Steps)
|
||
========================================================================== */
|
||
|
||
.bwfa-scope .steps {
|
||
display: flex;
|
||
align-items: flex-start;
|
||
}
|
||
|
||
.bwfa-scope .step {
|
||
flex: 1;
|
||
text-align: center;
|
||
position: relative;
|
||
font-size: var(--fs-sm);
|
||
color: var(--color-text-muted);
|
||
}
|
||
|
||
.bwfa-scope .step:not(:last-child)::after {
|
||
content: "";
|
||
position: absolute;
|
||
top: 1rem;
|
||
left: 50%;
|
||
width: 100%;
|
||
height: 1px;
|
||
background-color: var(--color-border);
|
||
z-index: 0;
|
||
}
|
||
|
||
.bwfa-scope .step-circle {
|
||
position: relative;
|
||
z-index: 1;
|
||
width: 2rem;
|
||
height: 2rem;
|
||
margin: 0 auto var(--space-2);
|
||
border-radius: 50%;
|
||
border: 1px solid var(--color-border);
|
||
background-color: var(--color-bg);
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
font-weight: var(--fw-semibold);
|
||
}
|
||
|
||
.bwfa-scope .step.done .step-circle { background-color: var(--gray-900); border-color: var(--gray-900); color: var(--gray-0); }
|
||
.bwfa-scope .step.done:not(:last-child)::after { background-color: var(--gray-900); }
|
||
.bwfa-scope .step.active .step-circle { border-color: var(--gray-900); border-width: 2px; }
|
||
.bwfa-scope .step.active { color: var(--color-text); font-weight: var(--fw-medium); }
|
||
|
||
|
||
/* ==========================================================================
|
||
49. TIMELINE (Ant Design's Timeline)
|
||
========================================================================== */
|
||
|
||
.bwfa-scope .timeline {
|
||
position: relative;
|
||
padding-left: var(--space-5);
|
||
border-left: 2px solid var(--color-border);
|
||
}
|
||
|
||
.bwfa-scope .timeline-item {
|
||
position: relative;
|
||
padding-bottom: var(--space-6);
|
||
}
|
||
.bwfa-scope .timeline-item:last-child { padding-bottom: 0; }
|
||
|
||
.bwfa-scope .timeline-item::before {
|
||
content: "";
|
||
position: absolute;
|
||
left: calc(var(--space-5) * -1 - 5px);
|
||
top: 0.2rem;
|
||
width: 10px;
|
||
height: 10px;
|
||
border-radius: 50%;
|
||
background-color: var(--gray-900);
|
||
border: 2px solid var(--color-bg);
|
||
box-shadow: 0 0 0 1px var(--color-border);
|
||
}
|
||
|
||
.bwfa-scope .timeline-time { font-size: var(--fs-xs); color: var(--color-text-muted); margin-bottom: var(--space-1); }
|
||
.bwfa-scope .timeline-title { font-weight: var(--fw-medium); }
|
||
|
||
|
||
/* ==========================================================================
|
||
50. STATISTIC (Ant Design's Statistic — a formal version of the bare
|
||
.stat-number from section 33, with a label and optional trend indicator)
|
||
========================================================================== */
|
||
|
||
.bwfa-scope .statistic-label {
|
||
font-size: var(--fs-sm);
|
||
color: var(--color-text-muted);
|
||
margin-bottom: var(--space-1);
|
||
}
|
||
.bwfa-scope .statistic-value {
|
||
font-size: var(--fs-2xl);
|
||
font-weight: var(--fw-bold);
|
||
font-variant-numeric: tabular-nums;
|
||
display: flex;
|
||
align-items: baseline;
|
||
gap: var(--space-2);
|
||
}
|
||
.bwfa-scope .statistic-trend { font-size: var(--fs-sm); font-weight: var(--fw-medium); }
|
||
.bwfa-scope .statistic-trend.up { color: var(--color-success); }
|
||
.bwfa-scope .statistic-trend.down { color: var(--color-danger); }
|
||
|
||
/* ==========================================================================
|
||
BWF ANALYSER — PLUGIN-SPECIFIC ADDITIONS
|
||
Everything below this line was written for this plugin. It only ever
|
||
targets descendants of .bwfa-scope, on purpose, so it can't leak into
|
||
the surrounding theme.
|
||
========================================================================== */
|
||
|
||
.bwfa-scope.bwfa-app {
|
||
max-width: var(--container-lg);
|
||
margin-inline: auto;
|
||
padding-block: var(--space-5);
|
||
}
|
||
|
||
/* ---- Header ------------------------------------------------------------ */
|
||
|
||
.bwfa-scope .bwfa-header {
|
||
margin-bottom: var(--space-5);
|
||
}
|
||
|
||
.bwfa-scope .bwfa-title {
|
||
font-size: var(--fs-2xl);
|
||
font-weight: var(--fw-semibold);
|
||
margin-bottom: var(--space-2);
|
||
}
|
||
|
||
.bwfa-scope .bwfa-privacy-note {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: var(--space-2);
|
||
color: var(--color-text-muted);
|
||
font-size: var(--fs-sm);
|
||
}
|
||
|
||
/* ---- Dropzone ------------------------------------------------------------ */
|
||
|
||
.bwfa-scope .bwfa-visually-hidden {
|
||
position: absolute !important;
|
||
width: 1px;
|
||
height: 1px;
|
||
padding: 0;
|
||
margin: -1px;
|
||
overflow: hidden;
|
||
clip: rect(0, 0, 0, 0);
|
||
white-space: nowrap;
|
||
border: 0;
|
||
}
|
||
|
||
.bwfa-scope .bwfa-dropzone {
|
||
border: 2px dashed var(--color-border);
|
||
border-radius: var(--radius-lg);
|
||
background-color: var(--color-surface);
|
||
padding: var(--space-7) var(--space-5);
|
||
text-align: center;
|
||
transition: border-color var(--duration-base) var(--ease-standard),
|
||
background-color var(--duration-base) var(--ease-standard);
|
||
}
|
||
|
||
.bwfa-scope .bwfa-dropzone:focus-visible {
|
||
outline: 2px solid var(--color-action);
|
||
outline-offset: 2px;
|
||
}
|
||
|
||
.bwfa-scope .bwfa-dropzone.is-dragover {
|
||
border-color: var(--color-action);
|
||
background-color: var(--color-surface-alt);
|
||
}
|
||
|
||
.bwfa-scope .bwfa-dropzone-text {
|
||
margin-bottom: var(--space-4);
|
||
color: var(--color-text-muted);
|
||
font-size: var(--fs-md);
|
||
}
|
||
|
||
.bwfa-scope .bwfa-dropzone-actions {
|
||
display: flex;
|
||
justify-content: center;
|
||
gap: var(--space-3);
|
||
flex-wrap: wrap;
|
||
}
|
||
|
||
.bwfa-scope .bwfa-dropzone-subnote {
|
||
margin-top: var(--space-4);
|
||
margin-bottom: 0;
|
||
font-size: var(--fs-xs);
|
||
color: var(--color-text-muted);
|
||
max-width: 480px;
|
||
margin-inline: auto;
|
||
}
|
||
|
||
/* ---- Status & progress --------------------------------------------------- */
|
||
|
||
.bwfa-scope .bwfa-status {
|
||
margin-top: var(--space-4);
|
||
font-size: var(--fs-sm);
|
||
color: var(--color-text-muted);
|
||
min-height: 1.25em;
|
||
}
|
||
|
||
.bwfa-scope .bwfa-status.has-error {
|
||
color: var(--color-danger);
|
||
}
|
||
|
||
.bwfa-scope .bwfa-progress {
|
||
margin-top: var(--space-3);
|
||
}
|
||
|
||
.bwfa-scope .bwfa-progress .progress-bar {
|
||
transition: width var(--duration-fast) linear;
|
||
}
|
||
|
||
/* ---- Results toolbar ------------------------------------------------------ */
|
||
|
||
.bwfa-scope .bwfa-results {
|
||
margin-top: var(--space-6);
|
||
}
|
||
|
||
.bwfa-scope .bwfa-toolbar {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
gap: var(--space-4);
|
||
flex-wrap: wrap;
|
||
margin-bottom: var(--space-4);
|
||
}
|
||
|
||
.bwfa-scope .bwfa-search-group {
|
||
margin-bottom: 0;
|
||
flex: 1 1 260px;
|
||
max-width: 360px;
|
||
}
|
||
|
||
.bwfa-scope .bwfa-export-actions {
|
||
display: flex;
|
||
gap: var(--space-2);
|
||
flex-wrap: wrap;
|
||
}
|
||
|
||
/* ---- Results table --------------------------------------------------------- */
|
||
|
||
/*
|
||
* The framework's own .table-responsive only handles horizontal
|
||
* scrolling; this gives the table a defined edge — border, rounded
|
||
* corners, a light shadow — using nothing but the framework's own
|
||
* tokens. overflow-y is set explicitly (rather than left to default) so
|
||
* the rounded corners actually clip the header's background without
|
||
* introducing an unwanted vertical scrollbar.
|
||
*/
|
||
.bwfa-scope .table-responsive {
|
||
overflow-x: auto;
|
||
overflow-y: hidden;
|
||
border: 1px solid var(--color-border);
|
||
border-radius: var(--radius-lg);
|
||
box-shadow: var(--shadow-sm);
|
||
}
|
||
|
||
.bwfa-scope .bwfa-table {
|
||
font-size: var(--fs-sm);
|
||
white-space: nowrap;
|
||
}
|
||
|
||
/* A single accent line under the header is the one deliberate spot of
|
||
color on an otherwise grayscale table — drawn from --color-info, the
|
||
same blue already used to mark the currently-playing row, so the two
|
||
read as related rather than arbitrary. */
|
||
.bwfa-scope .bwfa-table thead th {
|
||
border-bottom: 2px solid var(--color-info);
|
||
}
|
||
|
||
.bwfa-scope .bwfa-table th[data-bwfa-sort] {
|
||
cursor: pointer;
|
||
user-select: none;
|
||
position: relative;
|
||
padding-right: var(--space-5);
|
||
}
|
||
|
||
.bwfa-scope .bwfa-table th[data-bwfa-sort]:hover {
|
||
background-color: var(--color-surface-alt);
|
||
}
|
||
|
||
.bwfa-scope .bwfa-table th[data-bwfa-sort] .bwfa-sort-indicator {
|
||
position: absolute;
|
||
right: var(--space-2);
|
||
opacity: 0.35;
|
||
}
|
||
|
||
.bwfa-scope .bwfa-table th[data-bwfa-sort].is-sorted .bwfa-sort-indicator {
|
||
opacity: 1;
|
||
color: var(--color-info);
|
||
}
|
||
|
||
/* Circled takes get a warm gold accent (the framework's "warning" token,
|
||
repurposed here as a "starred" color) so it reads as a distinct signal
|
||
from the blue "currently playing" stripe rather than competing with it. */
|
||
.bwfa-scope .bwfa-table tbody tr.bwfa-row-circled td:first-child {
|
||
box-shadow: inset 3px 0 0 var(--color-warning);
|
||
}
|
||
|
||
.bwfa-scope .bwfa-table td.bwfa-col-description,
|
||
.bwfa-scope .bwfa-table td.bwfa-col-note {
|
||
max-width: 240px;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
white-space: nowrap;
|
||
}
|
||
|
||
.bwfa-scope .bwfa-table-cell-muted {
|
||
color: var(--color-text-muted);
|
||
}
|
||
|
||
/* ---- Empty state ----------------------------------------------------------- */
|
||
|
||
.bwfa-scope .bwfa-empty {
|
||
text-align: center;
|
||
color: var(--color-text-muted);
|
||
padding: var(--space-7) var(--space-4);
|
||
border: 1px dashed var(--color-border);
|
||
border-radius: var(--radius-lg);
|
||
margin-top: var(--space-5);
|
||
}
|
||
|
||
/* ---- Modal (per-file detail view) ------------------------------------------ */
|
||
|
||
.bwfa-scope .bwfa-modal-dialog {
|
||
}
|
||
|
||
.bwfa-scope .modal-header {
|
||
gap: var(--space-3);
|
||
}
|
||
|
||
.bwfa-scope .bwfa-modal-nav {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: var(--space-2);
|
||
min-width: 0;
|
||
flex: 1 1 auto;
|
||
}
|
||
|
||
.bwfa-scope .bwfa-modal-title-group {
|
||
min-width: 0;
|
||
flex: 1 1 auto;
|
||
}
|
||
|
||
.bwfa-scope .bwfa-modal-title-group .modal-title {
|
||
margin-bottom: 0;
|
||
}
|
||
|
||
.bwfa-scope .bwfa-modal-filename {
|
||
display: block;
|
||
font-size: var(--fs-xs);
|
||
color: var(--color-text-muted);
|
||
white-space: nowrap;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
}
|
||
|
||
.bwfa-scope [data-bwfa-modal-prev],
|
||
.bwfa-scope [data-bwfa-modal-next] {
|
||
flex: 0 0 auto;
|
||
line-height: 1;
|
||
font-size: var(--fs-md);
|
||
padding-inline: var(--space-3);
|
||
}
|
||
|
||
.bwfa-scope [data-bwfa-modal-prev]:disabled,
|
||
.bwfa-scope [data-bwfa-modal-next]:disabled {
|
||
opacity: 0.35;
|
||
cursor: default;
|
||
}
|
||
|
||
.bwfa-scope .bwfa-modal-section {
|
||
margin-bottom: var(--space-5);
|
||
}
|
||
|
||
.bwfa-scope .bwfa-modal-section:last-child {
|
||
margin-bottom: 0;
|
||
}
|
||
|
||
.bwfa-scope .bwfa-modal-section-title {
|
||
font-size: var(--fs-sm);
|
||
font-weight: var(--fw-semibold);
|
||
text-transform: uppercase;
|
||
letter-spacing: 0.03em;
|
||
color: var(--color-text-muted);
|
||
margin-bottom: var(--space-2);
|
||
padding-bottom: var(--space-1);
|
||
border-bottom: 1px solid var(--color-border);
|
||
}
|
||
|
||
.bwfa-scope .bwfa-meta-grid {
|
||
display: grid;
|
||
grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
|
||
gap: var(--space-3) var(--space-4);
|
||
}
|
||
|
||
.bwfa-scope .bwfa-meta-grid dt {
|
||
font-size: var(--fs-xs);
|
||
color: var(--color-text-muted);
|
||
margin-bottom: var(--space-1);
|
||
}
|
||
|
||
.bwfa-scope .bwfa-meta-grid dd {
|
||
font-size: var(--fs-sm);
|
||
word-break: break-word;
|
||
}
|
||
|
||
.bwfa-scope .bwfa-chip-list {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
gap: var(--space-2);
|
||
}
|
||
|
||
.bwfa-scope .bwfa-coding-history {
|
||
font-family: var(--font-mono);
|
||
font-size: var(--fs-xs);
|
||
background-color: var(--color-surface);
|
||
border: 1px solid var(--color-border);
|
||
border-radius: var(--radius-md);
|
||
padding: var(--space-3);
|
||
white-space: pre-wrap;
|
||
word-break: break-word;
|
||
}
|
||
|
||
/* ---- Now-playing bar ------------------------------------------------------- */
|
||
|
||
.bwfa-scope .bwfa-player {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: var(--space-3);
|
||
background-color: var(--color-surface);
|
||
border: 1px solid var(--color-border);
|
||
border-radius: var(--radius-lg);
|
||
padding: var(--space-3) var(--space-4);
|
||
margin-bottom: var(--space-4);
|
||
}
|
||
|
||
.bwfa-scope .bwfa-player-label {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: var(--space-2);
|
||
font-size: var(--fs-sm);
|
||
flex-wrap: wrap;
|
||
}
|
||
|
||
.bwfa-scope .bwfa-player-filename {
|
||
font-weight: var(--fw-medium);
|
||
word-break: break-all;
|
||
}
|
||
|
||
.bwfa-scope .bwfa-player-decoding {
|
||
color: var(--color-text-muted);
|
||
font-style: italic;
|
||
}
|
||
|
||
.bwfa-scope .bwfa-player-transport {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: var(--space-3);
|
||
}
|
||
|
||
.bwfa-scope .bwfa-player-time {
|
||
font-variant-numeric: tabular-nums;
|
||
font-size: var(--fs-sm);
|
||
color: var(--color-text-muted);
|
||
flex: 0 0 auto;
|
||
}
|
||
|
||
.bwfa-scope .bwfa-player-waveform {
|
||
flex: 1 1 auto;
|
||
min-width: 0;
|
||
width: 100%;
|
||
height: 56px;
|
||
background-color: var(--color-surface-alt);
|
||
border-radius: var(--radius-md);
|
||
cursor: pointer;
|
||
}
|
||
|
||
.bwfa-scope .bwfa-channel-row {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: var(--space-3);
|
||
flex-wrap: wrap;
|
||
}
|
||
|
||
.bwfa-scope .bwfa-channel-hint {
|
||
font-size: var(--fs-xs);
|
||
color: var(--color-text-muted);
|
||
flex: 0 0 auto;
|
||
}
|
||
|
||
.bwfa-scope .bwfa-channel-chip {
|
||
cursor: pointer;
|
||
border: 1px solid var(--color-border);
|
||
background-color: var(--color-surface-alt);
|
||
color: var(--color-text);
|
||
transition: opacity var(--duration-fast) var(--ease-standard),
|
||
border-color var(--duration-fast) var(--ease-standard);
|
||
}
|
||
|
||
.bwfa-scope .bwfa-channel-chip.is-muted {
|
||
opacity: 0.45;
|
||
text-decoration: line-through;
|
||
}
|
||
|
||
.bwfa-scope .bwfa-channel-chip.is-solo {
|
||
border-color: var(--color-action);
|
||
border-width: 2px;
|
||
font-weight: var(--fw-semibold);
|
||
opacity: 1;
|
||
}
|
||
|
||
.bwfa-scope .bwfa-table tbody tr.bwfa-row-playing td:first-child {
|
||
box-shadow: inset 3px 0 0 var(--color-info);
|
||
}
|
||
|
||
.bwfa-scope .bwfa-table tbody tr.bwfa-row-playing.bwfa-row-circled td:first-child {
|
||
box-shadow: inset 3px 0 0 var(--color-action), inset 6px 0 0 var(--color-info);
|
||
}
|
||
|
||
/* ---- Waveform thumbnail (table column) ------------------------------------- */
|
||
|
||
.bwfa-scope .bwfa-waveform-thumb {
|
||
display: block;
|
||
width: 120px;
|
||
height: 28px;
|
||
background-color: var(--color-surface-alt);
|
||
border-radius: var(--radius-sm);
|
||
}
|
||
|
||
.bwfa-scope .bwfa-waveform-thumb.is-loading {
|
||
opacity: 0.4;
|
||
}
|
||
|
||
/* ---- Columns dropdown ------------------------------------------------------- */
|
||
|
||
.bwfa-scope .bwfa-columns-menu {
|
||
max-height: 320px;
|
||
overflow-y: auto;
|
||
min-width: 220px;
|
||
}
|
||
|
||
.bwfa-scope .bwfa-columns-menu .form-switch {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
width: 100%;
|
||
padding: var(--space-1) var(--space-2);
|
||
}
|
||
|
||
.bwfa-scope .bwfa-columns-menu .form-switch:hover {
|
||
background-color: var(--color-surface-alt);
|
||
border-radius: var(--radius-sm);
|
||
}
|
||
|
||
/* ---- Metadata editing: entry point, indicator, forms ------------------------ */
|
||
|
||
.bwfa-scope .bwfa-edit-entry {
|
||
text-align: center;
|
||
margin-top: var(--space-4);
|
||
}
|
||
|
||
.bwfa-scope .bwfa-edit-divider {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: var(--space-3);
|
||
color: var(--color-text-muted);
|
||
font-size: var(--fs-xs);
|
||
text-transform: uppercase;
|
||
letter-spacing: 0.05em;
|
||
margin-bottom: var(--space-3);
|
||
}
|
||
|
||
.bwfa-scope .bwfa-edit-divider::before,
|
||
.bwfa-scope .bwfa-edit-divider::after {
|
||
content: "";
|
||
flex: 1 1 auto;
|
||
height: 1px;
|
||
background-color: var(--color-border);
|
||
}
|
||
|
||
.bwfa-scope .bwfa-editing-note {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: var(--space-2);
|
||
font-size: var(--fs-sm);
|
||
color: var(--color-text-muted);
|
||
margin-bottom: var(--space-4);
|
||
}
|
||
|
||
.bwfa-scope .bwfa-modal-edit-section {
|
||
background-color: var(--color-surface);
|
||
border: 1px solid var(--color-border);
|
||
border-radius: var(--radius-md);
|
||
padding: var(--space-4);
|
||
}
|
||
|
||
.bwfa-scope .bwfa-edit-grid {
|
||
display: grid;
|
||
grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
|
||
gap: var(--space-4);
|
||
align-items: end;
|
||
}
|
||
|
||
.bwfa-scope .bwfa-edit-field {
|
||
margin-bottom: 0;
|
||
}
|
||
|
||
/* Intentionally no custom layout override here: the framework's own
|
||
.form-switch (display:inline-flex, small gap) already keeps a toggle
|
||
and its label tightly grouped in natural reading order — a previous
|
||
version of this fought that with row-reverse + space-between, which
|
||
is exactly what made it unclear which switch belonged to which label
|
||
once several sat next to each other in a grid. */
|
||
|
||
.bwfa-scope .bwfa-edit-textarea {
|
||
resize: vertical;
|
||
min-height: 4.5em;
|
||
font-family: inherit;
|
||
}
|
||
|
||
/* Note and Description carry more text than a single grid cell fits
|
||
comfortably — give them the full row's width. */
|
||
.bwfa-scope .bwfa-edit-field:has(.bwfa-edit-textarea) {
|
||
grid-column: 1 / -1;
|
||
}
|
||
|
||
/* ---- Bulk edit panel --------------------------------------------------------- */
|
||
|
||
.bwfa-scope .bwfa-bulk-edit-panel {
|
||
background-color: var(--color-surface);
|
||
border: 1px solid var(--color-border);
|
||
border-radius: var(--radius-lg);
|
||
margin-bottom: var(--space-4);
|
||
}
|
||
|
||
.bwfa-scope .bwfa-bulk-edit-header h4 {
|
||
margin-bottom: var(--space-1);
|
||
}
|
||
|
||
.bwfa-scope .bwfa-bulk-edit-header p {
|
||
color: var(--color-text-muted);
|
||
font-size: var(--fs-sm);
|
||
margin-bottom: var(--space-4);
|
||
}
|
||
|
||
.bwfa-scope .bwfa-bulk-edit-fields {
|
||
display: grid;
|
||
grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
|
||
gap: var(--space-3);
|
||
margin-bottom: var(--space-4);
|
||
}
|
||
|
||
.bwfa-scope .bwfa-bulk-edit-row {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: var(--space-2);
|
||
}
|
||
|
||
.bwfa-scope .bwfa-bulk-edit-row .form-label {
|
||
flex: 0 0 auto;
|
||
min-width: 90px;
|
||
margin-bottom: 0;
|
||
font-size: var(--fs-sm);
|
||
}
|
||
|
||
.bwfa-scope .bwfa-bulk-edit-row input:not([type="checkbox"]),
|
||
.bwfa-scope .bwfa-bulk-edit-row select,
|
||
.bwfa-scope .bwfa-bulk-edit-row textarea {
|
||
flex: 1 1 auto;
|
||
min-width: 0;
|
||
}
|
||
|
||
.bwfa-scope .bwfa-bulk-edit-actions {
|
||
display: flex;
|
||
justify-content: flex-end;
|
||
gap: var(--space-3);
|
||
}
|
||
|
||
.bwfa-scope .bwfa-bulk-edit-progress {
|
||
margin-top: var(--space-3);
|
||
font-size: var(--fs-sm);
|
||
color: var(--color-text-muted);
|
||
}
|
||
|
||
/* ---- Responsive ------------------------------------------------------------ */
|
||
|
||
@media (max-width: 767px) {
|
||
.bwfa-scope .bwfa-toolbar {
|
||
flex-direction: column;
|
||
align-items: stretch;
|
||
}
|
||
|
||
.bwfa-scope .bwfa-search-group {
|
||
max-width: none;
|
||
}
|
||
|
||
.bwfa-scope .bwfa-export-actions {
|
||
justify-content: stretch;
|
||
}
|
||
|
||
.bwfa-scope .bwfa-export-actions .btn {
|
||
flex: 1;
|
||
}
|
||
|
||
.bwfa-scope .bwfa-player-transport {
|
||
flex-wrap: wrap;
|
||
}
|
||
|
||
.bwfa-scope .bwfa-player-waveform {
|
||
flex-basis: 100%;
|
||
}
|
||
|
||
.bwfa-scope [data-bwfa-player-duration] {
|
||
margin-left: auto;
|
||
}
|
||
}
|