Files
bwf-analyser/build/src/bwf-analyser.css
T
Vincent 2d0b7fe8b5 BWF Analyser: browser page and macOS app
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.
2026-08-17 22:50:39 +08:00

3082 lines
98 KiB
CSS
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* 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, 09 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;
}
}