Files
bwf-analyser/build/overrides.css
T
Vincent 6b7c09bacd Reorderable columns, a loaded first file, and a spectrogram worth keeping
Drag a heading and the column moves, with a copy of it following the pointer
and the column it came from stepping back. One array of keys — every column,
shown or hidden — backs the table, the Settings list and what is saved, so a
hidden column keeps its neighbour and returns where it was rather than at the
end. A drag ends in a click, and the click is what sorting listens for, so
that click is swallowed.

Opening a folder loads its first file: the read, the peaks, the channel chips
and the waveform, then paused at the start rather than playing. playRow gained
a paused mode instead of a second loader that could drift from it. It cannot
stop a folder opening, and a file that will not decode no longer writes a
playback error over the line saying the folder opened — nobody asked it to
play. The badge follows the transport now, so a paused file stops claiming to
be playing.

A bulk run re-reads every file it touches, but nothing told the transport to
look again: rename the boom across a card and the mixer went on showing the
old name for the very file it had just rewritten.

Settings loses its Done button and the mixer its Close — both apply as you
touch them, and the window's own close is enough. Save Image moves onto the
file name's line; the rule meant to do that had been dead for some time,
overwritten by the shared modal header. The exported spectrogram carries the
folder, the file and the frequency axis, drawn in, because a PNG read
somewhere else has none of that. The viewer is unchanged.

Mixer track names wear the player's pill, the waveform thumbnails give their
grey panel back to the row, and every button is the transport's size.

Build output that had been committed by accident is untracked, and .gitignore
grows the entries for it.

459 checks.
2026-09-04 00:28:31 +02:00

1668 lines
53 KiB
CSS

/* ---------------------------------------------------------------
Fixes applied to both builds, kept separate from the plugin's own
stylesheet so that file stays byte-identical to the original.
--------------------------------------------------------------- */
/* The detail modal laid its metadata out with
`repeat(auto-fill, minmax(180px, 1fr))`, and dt/dd are separate grid
items flowing in sequence. Any odd column count therefore splits the
pairs: three columns give you label, value, label / value, label,
value, so every other row reads inside out. Pinning the layout to
exactly two label+value pairs per row makes that impossible. */
.bwfa-scope .bwfa-meta-grid {
grid-template-columns:
minmax(6.5rem, max-content) minmax(0, 1fr)
minmax(6.5rem, max-content) minmax(0, 1fr);
column-gap: var(--space-6, 32px);
row-gap: var(--space-3, 12px);
align-items: baseline;
}
/* Label and value now sit side by side rather than stacked. */
.bwfa-scope .bwfa-meta-grid dt {
margin-bottom: 0;
}
/* Narrow enough and two pairs stop fitting; drop to one, still paired. */
@media (max-width: 760px) {
.bwfa-scope .bwfa-meta-grid {
grid-template-columns: minmax(6rem, max-content) minmax(0, 1fr);
}
}
/* Room for two columns of metadata without the modal feeling cramped. */
/* The edit form gets the same extra width, so give its fields a wider
minimum rather than letting them multiply into thin strips. */
.bwfa-scope .bwfa-edit-grid {
grid-template-columns: repeat(auto-fill, minmax(210px, 1fr));
/* Was `end`, which bottom-aligns cells of differing heights and
leaves the labels scattered at whatever height their own text
happens to reach. Stretch instead, and let each field place its
own control. */
align-items: stretch;
}
/* Label on top, control pinned to the bottom of the cell — so every
control across a row shares one baseline no matter how tall the
labels above them are. */
.bwfa-scope .bwfa-edit-field {
display: flex;
flex-direction: column;
}
.bwfa-scope .bwfa-edit-field > .form-control,
.bwfa-scope .bwfa-edit-field > .form-select {
margin-top: auto;
}
/* One height for every control in the form. A native <select> sizes
itself from the platform's own metrics and ignores the padding that
makes an <input> 38px, so both get told explicitly. */
.bwfa-scope .bwfa-edit-grid .form-control,
.bwfa-scope .bwfa-edit-grid .form-select {
height: 38px;
min-height: 38px;
padding: 0 10px;
line-height: 1.2;
box-sizing: border-box;
font-size: var(--fs-sm);
}
.bwfa-scope .bwfa-edit-grid .form-select {
-webkit-appearance: none;
appearance: none;
padding-right: 30px;
background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 8'%3E%3Cpath d='M1 1.5 6 6.5l5-5' fill='none' stroke='%2371717a' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
background-repeat: no-repeat;
background-position: right 11px center;
background-size: 11px;
}
/* Steppers on an empty number field are just clutter; the value is
typed, never nudged. */
.bwfa-scope .bwfa-edit-grid input[type="number"] {
-moz-appearance: textfield;
}
.bwfa-scope .bwfa-edit-grid input[type="number"]::-webkit-outer-spin-button,
.bwfa-scope .bwfa-edit-grid input[type="number"]::-webkit-inner-spin-button {
-webkit-appearance: none;
margin: 0;
}
/* The toggles keep the framework's inline label+switch pairing, but sit
on the same line as the controls beside them rather than floating in
the middle of a taller cell. */
.bwfa-scope .bwfa-edit-field.form-switch {
flex-direction: row;
align-items: center;
justify-content: flex-start;
gap: var(--space-2, 8px);
align-self: end;
min-height: 38px;
}
/* Note and Description are multi-line; they get the full row. */
.bwfa-scope .bwfa-edit-field:has(textarea) {
grid-column: 1 / -1;
}
.bwfa-scope .bwfa-edit-grid textarea.form-control {
height: auto;
min-height: 5rem;
padding: 8px 10px;
line-height: 1.45;
}
/* ---------------------------------------------------------------
Bulk edit
No per-field checkboxes: a field you fill in gets written to every
file, a field left blank is left alone. So each cell is just a label
over a control, same shape as the per-file form, and the grid is
capped so a wide window doesn't spread thirteen fields across eight
columns.
--------------------------------------------------------------- */
.bwfa-scope .bwfa-bulk-edit-fields {
grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
gap: 16px 20px;
max-width: 1080px;
align-items: start;
}
.bwfa-scope .bwfa-bulk-edit-row {
display: flex;
flex-direction: column;
gap: 6px;
}
.bwfa-scope .bwfa-bulk-edit-row > .form-label {
min-width: 0;
margin: 0;
color: var(--color-text-muted);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
/* A filled-in field is one that's about to be written, so it earns a
darker label than the ones being skipped. */
.bwfa-scope .bwfa-bulk-edit-row:has(.form-control:not(:placeholder-shown)) > .form-label,
.bwfa-scope .bwfa-bulk-edit-row:has(select.form-select option:not([value=""]):checked) > .form-label {
color: var(--color-text);
font-weight: var(--fw-semibold);
}
.bwfa-scope .bwfa-bulk-edit-row > .form-control,
.bwfa-scope .bwfa-bulk-edit-row > .form-select {
width: 100%;
height: 38px;
min-height: 38px;
padding: 0 10px;
line-height: 1.2;
box-sizing: border-box;
font-size: var(--fs-sm);
}
.bwfa-scope .bwfa-bulk-edit-row > .form-select {
-webkit-appearance: none;
appearance: none;
padding-right: 30px;
background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 8'%3E%3Cpath d='M1 1.5 6 6.5l5-5' fill='none' stroke='%2371717a' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
background-repeat: no-repeat;
background-position: right 11px center;
background-size: 11px;
}
.bwfa-scope .bwfa-bulk-edit-row > textarea.form-control {
height: auto;
min-height: 60px;
padding: 8px 10px;
line-height: 1.45;
resize: vertical;
}
.bwfa-scope .bwfa-bulk-edit-row input[type="number"] {
-moz-appearance: textfield;
}
.bwfa-scope .bwfa-bulk-edit-row input[type="number"]::-webkit-outer-spin-button,
.bwfa-scope .bwfa-bulk-edit-row input[type="number"]::-webkit-inner-spin-button {
-webkit-appearance: none;
margin: 0;
}
.bwfa-scope .bwfa-bulk-edit-panel {
/* Padding comes from --modal-pad, in the unified block below. */
}
.bwfa-scope .bwfa-bulk-edit-header p {
margin-bottom: var(--space-2);
}
.bwfa-scope .bwfa-bulk-edit-hint {
font-size: var(--fs-sm);
color: var(--color-text-muted);
margin: 0 0 var(--space-5);
}
.bwfa-scope .bwfa-bulk-edit-actions {
margin-top: var(--space-2);
padding-top: var(--space-4);
border-top: 1px solid var(--color-border);
}
/* ---------------------------------------------------------------
Text selection
The framework selects text in near-black with white text, which
looks like a terminal rather than a Mac. This is macOS's own
selection blue, with the text colour left alone.
--------------------------------------------------------------- */
::selection {
background-color: #b4d5fe;
color: inherit;
}
.bwfa-scope ::selection {
background-color: #b4d5fe;
color: inherit;
}
/* ---------------------------------------------------------------
Export field picker
--------------------------------------------------------------- */
.bwfa-scope .bwfa-export-actions-row {
display: flex;
gap: 8px;
margin-bottom: 16px;
padding-bottom: 14px;
border-bottom: 1px solid var(--color-border);
}
.bwfa-scope .bwfa-export-fields {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
gap: 4px 20px;
}
.bwfa-scope .bwfa-export-field {
display: flex;
align-items: center;
gap: 8px;
padding: 5px 6px;
border-radius: 6px;
cursor: pointer;
font-size: var(--fs-sm);
min-width: 0;
}
.bwfa-scope .bwfa-export-field:hover {
background-color: var(--color-surface-alt, #f4f4f5);
}
.bwfa-scope .bwfa-export-field input {
flex: none;
width: 15px;
height: 15px;
margin: 0;
}
.bwfa-scope .bwfa-export-field span {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
/* ---------------------------------------------------------------
Row and header accents
The plugin marks state with coloured slivers: a gold inset on the
first cell of a circled take, a blue one on the playing row, and a
blue rule under the table header. All three go. A playing row says
so by being green all the way across, which is legible at a glance
from across a room; circled takes are still reported by their own
column.
--------------------------------------------------------------- */
.bwfa-scope .bwfa-table tbody tr.bwfa-row-circled td:first-child,
.bwfa-scope .bwfa-table tbody tr.bwfa-row-playing td:first-child,
.bwfa-scope .bwfa-table tbody tr.bwfa-row-playing.bwfa-row-circled td:first-child {
box-shadow: none;
}
.bwfa-scope .bwfa-table thead th {
border-bottom: 1px solid var(--color-border);
}
.bwfa-scope .bwfa-table.table-striped tbody tr.bwfa-row-playing td,
.bwfa-scope .bwfa-table.table-striped tbody tr.bwfa-row-playing:hover td,
.bwfa-scope .bwfa-table.table-hover tbody tr.bwfa-row-playing:hover td {
background-color: #dff5e4;
}
/* Play and Pause are different lengths, so the button used to resize
mid-playback and shove everything after it sideways. One width for
both states. */
.bwfa-scope .bwfa-table td [data-bwfa-play-btn],
.bwfa-scope .bwfa-player-playpause,
.bwfa-scope [data-bwfa-modal-play] {
min-width: 68px;
text-align: center;
}
/* ---------------------------------------------------------------
The hidden attribute has to win
`[hidden] { display: none }` comes from the UA stylesheet, so any
author rule that sets display beats it. The plugin sets
`display: flex` on the player, and the app shell does the same for
the results panel — so both stayed on screen with their hidden
attribute set, which is why the toolbar, an empty table and the
transport were all visible before a folder was ever opened.
One rule fixes every case, present and future: if the markup says
hidden, it's hidden.
--------------------------------------------------------------- */
.bwfa-scope [hidden] {
display: none !important;
}
/* ---------------------------------------------------------------
Modal backdrop
A modal exists to say that the thing behind it isn't what you're
working on, and blur says that far better than a flat wash of
opacity does. Both builds, every modal.
--------------------------------------------------------------- */
.bwfa-scope .modal-backdrop {
background-color: rgba(24, 24, 27, 0.55);
-webkit-backdrop-filter: blur(10px) saturate(0.8);
backdrop-filter: blur(10px) saturate(0.8);
}
/* ---------------------------------------------------------------
Sound report
Export Fields, Export CSV and Export PDF became one button and a
modal: the report's details, its format and its fields are one
decision, and three buttons in the toolbar made it three. The
originals stay in the DOM because the analyser wires its export
pipeline to them at startup and the modal drives them rather than
reimplementing any of it.
--------------------------------------------------------------- */
.bwfa-scope .bwfa-legacy-export {
display: none !important;
}
.bwfa-scope .bwfa-report-details {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
gap: 14px 18px;
margin-bottom: var(--space-5);
}
.bwfa-scope .bwfa-report-row {
display: flex;
flex-direction: column;
gap: 6px;
min-width: 0;
}
.bwfa-scope .bwfa-report-row > .form-label {
margin: 0;
color: var(--color-text-muted);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.bwfa-scope .bwfa-report-row > .form-control,
.bwfa-scope .bwfa-report-row > .form-select {
width: 100%;
height: 38px;
min-height: 38px;
padding: 0 10px;
line-height: 1.2;
box-sizing: border-box;
font-size: var(--fs-sm);
}
.bwfa-scope .bwfa-report-row > .form-select {
-webkit-appearance: none;
appearance: none;
padding-right: 30px;
background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 8'%3E%3Cpath d='M1 1.5 6 6.5l5-5' fill='none' stroke='%2371717a' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
background-repeat: no-repeat;
background-position: right 11px center;
background-size: 11px;
}
/* The field list is the long half of the modal, so it gets a heading of
its own rather than starting where the details stop. */
.bwfa-scope .bwfa-report-fields-head {
display: flex;
align-items: baseline;
flex-wrap: wrap;
gap: 10px;
padding-top: var(--space-4);
margin-bottom: var(--space-3);
border-top: 1px solid var(--color-border);
}
.bwfa-scope .bwfa-report-fields-head h4 {
margin: 0;
}
.bwfa-scope .bwfa-report-count {
color: var(--color-text-muted);
font-size: var(--fs-sm);
}
.bwfa-scope .bwfa-report-fields-head .bwfa-export-actions-row {
margin: 0 0 0 auto;
}
/* ---------------------------------------------------------------
The columns cogwheel
The columns control used to be a dropdown in the toolbar, one
more button in a row of them, describing something that happens
in the table. It's now a cogwheel in the table's own first header
cell — the empty one above Play, which was doing nothing — and it
opens a modal listing every column.
--------------------------------------------------------------- */
/* Built to read as a smaller sibling of the eject button in the folder
bar: same circle, same hairline border, same muted glyph. Smaller
because a table header is not the place for a 30px control. */
.bwfa-scope .bwfa-columns-cog {
display: inline-flex;
align-items: center;
justify-content: center;
box-sizing: border-box;
width: 22px;
height: 22px;
padding: 0;
/* Lighter than the framework's own border and text-muted: this is a
control you look for rather than one that asks to be noticed, so at
rest it's barely there and it comes forward under the pointer. */
border: 1px solid #eaeaee;
border-radius: 50%;
background-color: transparent;
color: #c6c6cc;
line-height: 0;
cursor: pointer;
transition: border-color var(--duration-fast, 120ms), color var(--duration-fast, 120ms);
}
.bwfa-scope .bwfa-columns-cog:hover,
.bwfa-scope .bwfa-columns-cog:focus-visible {
border-color: #b4b4bb;
color: #6e6e77;
}
.bwfa-scope .bwfa-columns-cog::before {
content: "";
display: block;
width: 12px;
height: 12px;
background-color: currentColor;
-webkit-mask-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M12 15.5a3.5 3.5 0 1 0 0-7 3.5 3.5 0 0 0 0 7z' fill='none' stroke='black' stroke-width='1.8'/%3E%3Cpath d='M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 1 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 1 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 1 1-2.83-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 1 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 1 1 2.83-2.83l.06.06A1.65 1.65 0 0 0 9 4.6h.09A1.65 1.65 0 0 0 10 3.09V3a2 2 0 1 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 1 1 2.83 2.83l-.06.06A1.65 1.65 0 0 0 19.4 9v.09a1.65 1.65 0 0 0 1.51 1H21a2 2 0 1 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z' fill='none' stroke='black' stroke-width='1.8'/%3E%3C/svg%3E");
mask-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M12 15.5a3.5 3.5 0 1 0 0-7 3.5 3.5 0 0 0 0 7z' fill='none' stroke='black' stroke-width='1.8'/%3E%3Cpath d='M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 1 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 1 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 1 1-2.83-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 1 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 1 1 2.83-2.83l.06.06A1.65 1.65 0 0 0 9 4.6h.09A1.65 1.65 0 0 0 10 3.09V3a2 2 0 1 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 1 1 2.83 2.83l-.06.06A1.65 1.65 0 0 0 19.4 9v.09a1.65 1.65 0 0 0 1.51 1H21a2 2 0 1 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z' fill='none' stroke='black' stroke-width='1.8'/%3E%3C/svg%3E");
-webkit-mask-repeat: no-repeat;
mask-repeat: no-repeat;
-webkit-mask-size: contain;
mask-size: contain;
}
/* Two columns of switches: 26 of them in one list is a scroll for no
reason, and the modal has the width for it. */
.bwfa-scope .bwfa-columns-dialog .bwfa-columns-menu {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
gap: 2px 20px;
max-height: none;
min-width: 0;
overflow: visible;
}
/* The header cell it lives in is otherwise empty, so it sets the column's
width — keep it to the button and align it left, with the Play buttons
under it rather than centred over them. */
.bwfa-scope .bwfa-table thead th:first-child {
text-align: left;
vertical-align: middle;
white-space: nowrap;
}
/* ---------------------------------------------------------------
Settings modal
Three topics behind three accordions, all closed to start with:
the point of the cog is that you go looking for one thing, not
that you read a wall of options every time.
--------------------------------------------------------------- */
.bwfa-scope .bwfa-settings-section {
border-bottom: 1px solid var(--color-border);
}
.bwfa-scope .bwfa-settings-section:first-child {
border-top: 1px solid var(--color-border);
}
.bwfa-scope .bwfa-settings-section > summary {
list-style: none;
cursor: pointer;
padding: 12px 4px 12px 22px;
position: relative;
font-weight: var(--fw-semibold);
color: var(--color-text);
}
.bwfa-scope .bwfa-settings-section > summary::-webkit-details-marker {
display: none;
}
/* A disclosure triangle of our own, so it points where we want it. */
.bwfa-scope .bwfa-settings-section > summary::before {
content: "";
position: absolute;
left: 4px;
top: 50%;
width: 0;
height: 0;
margin-top: -4px;
border-left: 5px solid var(--color-text-muted);
border-top: 4px solid transparent;
border-bottom: 4px solid transparent;
transition: transform var(--duration-fast, 120ms) ease;
}
.bwfa-scope .bwfa-settings-section[open] > summary::before {
transform: rotate(90deg);
}
.bwfa-scope .bwfa-settings-body {
padding: 0 4px 16px 22px;
}
.bwfa-scope .bwfa-choice-list {
display: grid;
gap: 4px;
}
.bwfa-scope .bwfa-choice {
display: flex;
align-items: flex-start;
gap: 10px;
padding: 8px 10px;
border-radius: var(--radius-sm, 6px);
cursor: pointer;
}
.bwfa-scope .bwfa-choice:hover {
background-color: var(--color-surface-alt);
}
.bwfa-scope .bwfa-choice input {
margin: 2px 0 0;
flex: none;
}
.bwfa-scope .bwfa-choice span {
display: flex;
flex-direction: column;
gap: 2px;
}
.bwfa-scope .bwfa-choice small {
color: var(--color-text-muted);
font-size: var(--fs-sm);
}
/* ---------------------------------------------------------------
Dark mode
The framework maps every surface, border and text colour onto one
grayscale ramp, so the whole app turns over by redefining the ramp
— no per-component overrides, and anything added later comes along
for free. Anthracite rather than black: a true-black window in a
dark room is a light source with a hole in it, and the greys need
somewhere to sit below the page.
The ramp is not a straight inversion. Dark interfaces need less
contrast at the top end (white on near-black glares) and more
separation at the bottom, so the surfaces sit closer together and
the text stops short of white.
--------------------------------------------------------------- */
/* html and body are outside the scoped container but still paint the
window, so the ramp is mirrored onto the root. Only the handful of
values anything outside the app actually reads. */
:root {
--color-bg: #ffffff;
--color-text: #333333;
}
.bwfa-scope[data-bwfa-theme="dark"] {
--gray-0: #202225;
--gray-50: #25272b;
--gray-100: #2a2d31;
--gray-150: #313438;
--gray-200: #3b3e43;
--gray-300: #4c5057;
--gray-400: #6d727a;
--gray-500: #8a9098;
--gray-600: #a6abb3;
--gray-700: #c3c7cd;
--gray-800: #e3e5e8;
--gray-900: #f1f2f4;
--gray-1000: #ffffff;
--color-success: #6cc38d;
--color-success-bg: #1e3328;
--color-danger: #f08b84;
--color-danger-bg: #3a2422;
--color-warning: #ddb85a;
--color-warning-bg: #35301f;
--color-info: #8ab4e8;
--color-info-bg: #22303f;
}
/* Selection: the same idea as the light theme's macOS blue, at a
weight that works under light text. */
.bwfa-scope[data-bwfa-theme="dark"] ::selection {
background-color: #2f4f75;
color: #f1f2f4;
}
/* The playing row, and the Play button that has to stay legible on it. */
.bwfa-scope[data-bwfa-theme="dark"] .bwfa-table.table-striped tbody tr.bwfa-row-playing td,
.bwfa-scope[data-bwfa-theme="dark"] .bwfa-table.table-striped tbody tr.bwfa-row-playing:hover td,
.bwfa-scope[data-bwfa-theme="dark"] .bwfa-table.table-hover tbody tr.bwfa-row-playing:hover td {
background-color: #22382c;
}
.bwfa-scope[data-bwfa-theme="dark"] .bwfa-columns-cog {
border-color: #3b3e43;
color: #6d727a;
}
.bwfa-scope[data-bwfa-theme="dark"] .bwfa-columns-cog:hover,
.bwfa-scope[data-bwfa-theme="dark"] .bwfa-columns-cog:focus-visible {
border-color: #6d727a;
color: #c3c7cd;
}
/* The backdrop is already a dark wash; on a dark page it needs to be
deeper to read as one at all. */
.bwfa-scope[data-bwfa-theme="dark"] .modal-backdrop {
background-color: rgba(8, 9, 10, 0.62);
}
/* The window itself, when the app is in dark mode. `:has()` is how a
scoped theme reaches its own page furniture; WebKit has had it since
Safari 15.4, and the fallback is simply the light window it is today. */
html:has(.bwfa-scope[data-bwfa-theme="dark"]) {
--color-bg: #202225;
--color-text: #e3e5e8;
background-color: #202225;
}
/* ---------------------------------------------------------------
Settings, one size down
Nothing in the cog is prose — it's a list of switches and short
labels — and at body size it read as the loudest thing on screen
for the least important. Everything in the dialog steps down a
size together, controls included, so the proportions hold.
--------------------------------------------------------------- */
.bwfa-scope .bwfa-columns-dialog {
font-size: var(--fs-sm);
}
/* The title size comes from the shared header rule, with every other
modal's. This set its own and made Settings the odd one out. */
.bwfa-scope .bwfa-settings-section > summary {
font-size: var(--fs-sm);
padding: 10px 4px 10px 20px;
}
.bwfa-scope .bwfa-choice {
padding: 6px 8px;
font-size: var(--fs-sm);
}
.bwfa-scope .bwfa-choice small,
.bwfa-scope .bwfa-columns-dialog .form-switch {
font-size: var(--fs-xs);
}
/* The radios come from the platform, so they're sized rather than
restyled — a scaled transform would blur them. */
.bwfa-scope .bwfa-choice input[type="radio"] {
width: 13px;
height: 13px;
margin-top: 1px;
}
/* The switch is drawn by the framework out of one input; both halves
of it have to come down together or the knob leaves the track. */
.bwfa-scope .bwfa-columns-dialog .form-switch input {
width: 2rem;
height: 1.1rem;
}
.bwfa-scope .bwfa-columns-dialog .form-switch input::before {
width: calc(1.1rem - 4px);
height: calc(1.1rem - 4px);
}
.bwfa-scope .bwfa-columns-dialog .form-switch input:checked::before {
transform: translateX(0.9rem);
}
.bwfa-scope .bwfa-columns-dialog .bwfa-columns-menu .form-switch {
padding: 3px var(--space-2);
}
/* ---------------------------------------------------------------
Note and Description, side by side
They're the only two free-text fields in either edit form, and
they were laid out as leftovers: Note ending up in whatever cell
was going spare and Description alone on a row of its own. As a
pair on their own row, at the same width and the same height,
the form has a bottom edge instead of a ragged tail.
--------------------------------------------------------------- */
.bwfa-scope .bwfa-edit-field:has(.bwfa-edit-textarea),
.bwfa-scope .bwfa-bulk-edit-row:has(textarea) {
grid-column: span 2;
}
/* The first of the pair starts a fresh row, so the second lands
beside it rather than wherever the flow left off. On a narrow
window the grid is two columns wide and they stack — still equal,
still full width. */
.bwfa-scope .bwfa-edit-field:has(.bwfa-edit-textarea):nth-last-child(2),
.bwfa-scope .bwfa-bulk-edit-row:has(textarea):nth-last-child(2) {
grid-column-start: 1;
}
.bwfa-scope .bwfa-edit-grid textarea.form-control,
.bwfa-scope .bwfa-bulk-edit-row > textarea.form-control {
min-height: 76px;
height: 76px;
}
/* The audio reset is an app-only escape hatch: in a browser tab a reload
loses the folder along with the audio. */
.bwfa-scope .bwfa-settings-action {
display: none;
}
.bwfa-scope .bwfa-settings-action small {
display: block;
margin-top: 6px;
color: var(--color-text-muted);
font-size: var(--fs-xs);
max-width: 46ch;
}
/* ---- What it's doing goes beside the buttons ------------------------
It used to be a line of its own below them, which only existed while
a job was running: the modal grew by a line the moment you pressed
the button and shrank again when it finished, under the cursor. On
the same row as the buttons it can't change the height at all, and a
long message is cut off before it reaches them rather than wrapping.
------------------------------------------------------------------ */
.bwfa-scope .bwfa-bulk-edit-actions {
display: flex;
align-items: center;
gap: var(--space-3);
}
.bwfa-scope .bwfa-bulk-edit-actions > .btn {
flex: none;
}
.bwfa-scope .bwfa-bulk-edit-progress,
.bwfa-scope .bwfa-bulk-edit-progress[hidden] {
display: block;
flex: 1 1 auto;
min-width: 0;
margin: 0;
margin-right: auto;
text-align: left;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
/* The Apply button while it is waiting to be told twice. */
.bwfa-scope .bwfa-bulk-edit-actions .btn.is-confirming {
background-color: var(--color-danger, #b42318);
border-color: var(--color-danger, #b42318);
color: #fff;
}
/* The question, while it is being asked. */
.bwfa-scope .bwfa-bulk-edit-progress.is-asking {
color: var(--color-text);
font-weight: var(--fw-medium, 500);
}
/* ---- Track names, as fields rather than labels ----------------------
What a track is called changes on the day: a radio moves from one
actor to another between setups. It was a row of read-only badges,
which is the one piece of metadata you most want to correct.
------------------------------------------------------------------ */
.bwfa-scope .bwfa-track-names {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
gap: 8px;
}
.bwfa-scope .bwfa-track-name {
display: flex;
align-items: center;
gap: 8px;
min-width: 0;
}
.bwfa-scope .bwfa-track-name-index {
flex: none;
width: 1.5em;
text-align: right;
color: var(--color-text-muted);
font-variant-numeric: tabular-nums;
font-size: var(--fs-sm);
}
.bwfa-scope .bwfa-track-name > .form-control {
flex: 1 1 auto;
min-width: 0;
height: 32px;
padding: 0 8px;
font-size: var(--fs-sm);
}
/* ---- Track names are chips, and the chip is the thing you click -----
The player already shows channels as chips, so the same names in the
edit and bulk panels read as the same objects. A row of permanent
input boxes turned a list of what is there into a form waiting to be
filled in; this stays a list until you click one.
------------------------------------------------------------------ */
.bwfa-scope .bwfa-name-edit {
display: inline-flex;
min-width: 0;
max-width: 100%;
}
.bwfa-scope .bwfa-name-chip {
max-width: 100%;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
cursor: text;
}
.bwfa-scope .bwfa-name-chip:hover {
border-color: var(--color-action, var(--color-text));
}
.bwfa-scope .bwfa-name-chip.is-empty {
color: var(--color-text-muted);
font-style: italic;
}
.bwfa-scope .bwfa-name-edit-field {
height: 30px;
min-width: 8em;
max-width: 16em;
padding: 0 8px;
font-size: var(--fs-sm);
}
.bwfa-scope .bwfa-track-names,
.bwfa-scope .bwfa-bulk-tracks-list {
display: flex;
flex-wrap: wrap;
gap: 8px;
align-items: center;
}
.bwfa-scope .bwfa-track-name,
.bwfa-scope .bwfa-bulk-track {
display: inline-flex;
align-items: center;
gap: 6px;
min-width: 0;
}
/* How many takes carry this name, sitting with the chip rather than in a
column of its own: it is a note on the chip, not a field of data. */
.bwfa-scope .bwfa-bulk-track-count {
flex: none;
font-size: var(--fs-xs, 12px);
color: var(--color-text-muted);
font-variant-numeric: tabular-nums;
margin-right: var(--space-2);
}
.bwfa-scope .bwfa-bulk-track.is-renamed .bwfa-bulk-track-count {
color: var(--color-info, #375a7f);
}
.bwfa-scope .bwfa-bulk-track.is-unnamed {
opacity: 0.75;
}
.bwfa-scope .bwfa-bulk-tracks {
margin-top: var(--space-4);
padding-top: var(--space-4);
border-top: 1px solid var(--color-border);
}
.bwfa-scope .bwfa-bulk-tracks-title {
font-weight: var(--fw-semibold);
font-size: var(--fs-sm);
margin-bottom: var(--space-2);
}
/* ---- Spectrogram ----------------------------------------------------
Time across, frequency up, brightness as level. Round like the eject
button, at the end of the player's header row, because it is about
the file that is loaded rather than about the table.
------------------------------------------------------------------ */
.bwfa-scope .bwfa-spectro-open {
margin-left: auto;
}
.bwfa-scope .bwfa-mixer-open {
margin-left: var(--space-1, 4px);
}
.bwfa-scope .bwfa-round-btn {
/* flex: none belongs here, not on one button. Without it these shrink in
the player's flex row: the icon still paints (it overflows) so the
button looks perfectly normal, while its hit area has gone to nothing
and the click lands on the header behind it. A dead button that looks
alive, and no test with a layout engine behind it can see the
difference. */
flex: none;
width: 26px;
min-width: 26px;
height: 26px;
padding: 0;
display: inline-flex;
align-items: center;
justify-content: center;
border: 1px solid var(--gray-200, #eaeaee);
border-radius: 50%;
background: none;
color: var(--gray-500, #c6c6cc);
cursor: pointer;
}
.bwfa-scope .bwfa-round-btn:hover {
border-color: var(--gray-400, #b4b4bb);
color: var(--color-text, #6e6e77);
}
.bwfa-scope .bwfa-round-btn svg {
fill: currentColor;
}
.bwfa-scope .bwfa-spectro-dialog {
position: relative;
}
.bwfa-scope .bwfa-spectro-head h4 {
margin: 0;
}
.bwfa-scope .bwfa-spectro-head p {
margin: 4px 0 var(--space-4);
font-size: var(--fs-sm);
color: var(--color-text-muted);
}
/* The picture is one pixel per column and per bin, stretched to fit:
smoothing it would invent detail that isn't in the file. */
.bwfa-scope .bwfa-spectro-canvas {
display: block;
width: 100%;
height: min(52vh, 460px);
background-color: #440154;
border-radius: var(--radius-sm, 6px);
image-rendering: pixelated;
}
/* The frequency axis, beside the picture rather than floating over it: a
flex row means the labels line up with the image at any height without
anything being positioned by hand. */
.bwfa-scope .bwfa-spectro-plot {
display: flex;
align-items: stretch;
min-height: 0;
flex: 1 1 auto;
}
.bwfa-scope .bwfa-spectro-scale {
display: flex;
flex-direction: column;
justify-content: space-between;
flex: none;
width: 4.5em;
padding: 0 8px 0 0;
text-align: right;
font-size: 11px;
color: var(--color-text-muted);
font-variant-numeric: tabular-nums;
}
/* The top and bottom labels sit at the very edges of the image, so they
are nudged inward to stay level with the pixels they describe. */
.bwfa-scope .bwfa-spectro-scale span:first-child {
transform: translateY(-0.15em);
}
.bwfa-scope .bwfa-spectro-scale span:last-child {
transform: translateY(0.15em);
}
/* The layout of this block is further down, after the shared modal header
rule that sets every dialog's heading to display: block — anything about
it written here is overwritten by that. */
.bwfa-scope .bwfa-spectro-save {
flex: none;
}
/* ---- Every modal closes the way a Mac window closes -----------------
One red button, top left, at the margins a real window uses. Every
dialog in the app carries the same .modal-close, so this is one rule
and there is nothing that can be missed: the detail view, bulk edit,
export, the export report, the sound report, the columns picker and
the spectrogram all get it from here.
The glyph is hidden until you point at it, which is what the system
does — the colour is the affordance, the cross is the confirmation.
------------------------------------------------------------------ */
/* The two numbers that decide whether it looks like a Mac window or like
a web page pretending to be one. Kept as variables because they are the
thing most likely to want a nudge, and nothing else should hardcode them.
Measured against a real title bar: the dot sits closer to the corner
than its own width, which is what makes it read as part of the frame
rather than as a control placed inside the content. */
.bwfa-scope {
--traffic-size: 14px;
--traffic-inset: 18px;
/* Clear air between the traffic light and the title. Roughly a
centimetre on a laptop screen, which is what it takes to stop the
heading reading as a label attached to the button. */
--traffic-gap: 40px;
}
.bwfa-scope .modal-close {
position: absolute;
top: var(--traffic-inset);
left: var(--traffic-inset);
right: auto;
width: var(--traffic-size);
height: var(--traffic-size);
min-width: 0;
padding: 0;
display: flex;
align-items: center;
justify-content: center;
box-sizing: border-box;
/* An inset shadow rather than a border, so the edge can't add to the
12px the system uses. */
border: 0;
box-shadow: inset 0 0 0 0.5px rgba(0, 0, 0, 0.16);
border-radius: 50%;
background-color: #ff5f57;
color: transparent;
font-size: 11px;
line-height: 1;
cursor: default;
opacity: 1;
flex: none;
z-index: 2;
}
.bwfa-scope .modal-close:hover {
background-color: #ff5f57;
color: rgba(0, 0, 0, 0.55);
}
.bwfa-scope .modal-close:active {
background-color: #bf4943;
}
/* Room for it, on the side it is now on. */
/* An earlier round's version of this rule. The unified header block below
owns the padding now; leaving this here set a second, smaller padding-top
on four of the six headers. */
/* The close button is positioned against its dialog, so every dialog has
to be something to position against. Without this it anchors to the
overlay instead and lands off the corner of the panel entirely, which
is why the Sound Report looked like it had no close button at all. */
.bwfa-scope .modal-dialog {
position: relative;
}
/* The Sound Report was the odd one out: 760px against the wide sheets'
1120px, so the same three columns of fields sat in a narrower box and
the whole modal read as a different piece of furniture. */
/* ====================================================================
One modal, six times over
--------------------------------------------------------------------
These grew one at a time and it showed: three had a rule under the
heading and three didn't, the titles came in three sizes, the widths
ran from 660 to 1120, and the traffic light sat anywhere from level
with the title to a centimetre above it. Nothing below is new design,
it is the same design applied to all of them.
The rules, and there are only four:
1. One width. Two values, and a modal picks one deliberately.
2. A header block: title, optional single line under it, always a
rule beneath, always the same room above for the traffic light.
3. A body with one padding.
4. A footer, when there are actions, always ruled and right-aligned.
==================================================================== */
.bwfa-scope {
--modal-width: 1120px;
/* Wide enough to clear the traffic light, so the title starts to the
right of it rather than against it — and because everything else in
the dialog uses the same gutter, the title, the fields and the
buttons all line up on one edge. */
--modal-pad: 42px;
}
/* Rule 5, which the export report already followed and the rest didn't:
the heading and the buttons stay put and the middle scrolls. A dialog
that scrolls as a whole makes you hunt for the button that dismisses
it, and one that doesn't scroll at all simply loses its bottom half —
which is what the Sound Report was doing with 26 fields in it. */
.bwfa-scope .modal-dialog {
width: min(var(--modal-width), 92vw);
max-width: min(var(--modal-width), 92vw);
max-height: 88vh;
display: flex;
flex-direction: column;
border-radius: 12px;
overflow: hidden;
}
.bwfa-scope .modal-header,
.bwfa-scope .bwfa-bulk-edit-header,
.bwfa-scope .bwfa-spectro-head,
.bwfa-scope .modal-footer,
.bwfa-scope .bwfa-bulk-edit-actions {
flex: none;
}
.bwfa-scope .modal-body,
.bwfa-scope .bwfa-sheet-dialog .bwfa-export-fields,
.bwfa-scope .bwfa-sheet-dialog .bwfa-bulk-edit-fields,
.bwfa-scope .bwfa-spectro-dialog .bwfa-spectro-canvas {
flex: 1 1 auto;
min-height: 0;
overflow-y: auto;
}
/* Inside a dialog, a panel gives up its own card: the dialog is the card.
Leaving it its background, border and rounded corners is what produced a
second box inside the first, visible as a stray strip below the footer.
The padding rule that used to carry this went when the padding did. */
.bwfa-scope .modal-dialog .bwfa-bulk-edit-panel,
.bwfa-scope .modal-dialog .bwfa-result-panel,
.bwfa-scope .modal-dialog .bwfa-export-panel {
background: none;
border: 0;
border-radius: 0;
box-shadow: none;
margin: 0;
}
/* The panels inside the sheets are the flex column, not the dialog. */
.bwfa-scope .bwfa-sheet-dialog .bwfa-bulk-edit-panel {
display: flex;
flex-direction: column;
min-height: 0;
flex: 1 1 auto;
}
/* One width, no exceptions. Export Files is a shorter form than the rest
and there was a case for narrowing it, but a set of dialogs that open at
the same size reads as one application and a set that doesn't reads as
several. The consistency is worth more than the tighter box. */
/* ---- 2. The header ---- */
.bwfa-scope .modal-header,
.bwfa-scope .bwfa-bulk-edit-header,
.bwfa-scope .bwfa-spectro-head {
display: block;
margin: 0;
padding: calc(var(--traffic-inset) + var(--traffic-size) + var(--traffic-gap))
var(--modal-pad) var(--space-4);
border-bottom: 1px solid var(--color-border);
}
/* Descendants, not direct children. Sound Report and File metadata wrap
their title in a .bwfa-modal-title-group so it can sit beside the close
button; Bulk Edit and Export put the heading straight in the header. A
`>` matched the second pair and silently missed the first, which is the
whole reason Sound Report kept a different title size after every other
difference had been dealt with. */
.bwfa-scope .modal-header h3,
.bwfa-scope .modal-header h4,
.bwfa-scope .modal-header .modal-title,
.bwfa-scope .bwfa-columns-dialog .modal-title,
.bwfa-scope .bwfa-bulk-edit-header h4,
.bwfa-scope .bwfa-spectro-head h4 {
margin: 0;
font-size: var(--fs-xl, 20px);
font-weight: var(--fw-semibold);
line-height: 1.25;
}
/* One line under the title, and only one: everything else these panels
wanted to say has been folded into it or into a hint. */
.bwfa-scope .modal-header p,
.bwfa-scope .bwfa-bulk-edit-header p,
.bwfa-scope .bwfa-spectro-head p,
.bwfa-scope .modal-header .bwfa-modal-filename,
.bwfa-scope .bwfa-bulk-edit-header .bwfa-bulk-edit-hint {
display: block;
margin: 3px 0 0;
font-size: var(--fs-sm);
line-height: 1.45;
color: var(--color-text-muted);
}
/* ---- Save Image belongs on the file name's line --------------------
The spectrogram's heading holds three things where the other dialogs
hold two, and the rule above lays every heading out as a plain block —
so the button, being inline, dropped to a line of its own under the
name. Laid out as a wrapping flex row instead: the name and the button
share the first line, the reading takes the second.
This has to sit after that shared rule rather than with the rest of the
spectrogram's styling further up. An earlier attempt lived up there and
was overwritten by it, which is why nothing moved.
------------------------------------------------------------------ */
.bwfa-scope .bwfa-spectro-head {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 0 var(--space-4);
}
.bwfa-scope .bwfa-spectro-head h4 {
flex: none;
}
.bwfa-scope .bwfa-spectro-head p {
flex: 1 0 100%;
}
.bwfa-scope .bwfa-spectro-save {
margin-left: auto;
}
/* ---- 3. The body ---- */
.bwfa-scope .modal-body,
.bwfa-scope .bwfa-sheet-dialog .bwfa-export-fields,
.bwfa-scope .bwfa-sheet-dialog .bwfa-bulk-edit-fields {
padding: var(--modal-pad);
}
.bwfa-scope .bwfa-sheet-dialog .bwfa-bulk-edit-panel,
.bwfa-scope .bwfa-spectro-dialog {
padding: 0;
}
/* ---- 4. The footer ---- */
.bwfa-scope .modal-footer,
.bwfa-scope .bwfa-bulk-edit-actions {
margin: 0;
padding: var(--space-4) var(--modal-pad);
border-top: 1px solid var(--color-border);
justify-content: flex-end;
}
/* Anything the panels put between the body and the footer sits in the
body's gutter, not against the edge of the dialog. */
/* Whatever a panel puts between the body and the footer sits in the body's
gutter, not against the edge of the dialog — and keeps its distance from
the footer's rule. Without the bottom margin the last block butts against
that line, which reads as two rules with the content trapped between. */
.bwfa-scope .bwfa-export-summary-line,
.bwfa-scope .bwfa-export-names,
.bwfa-scope .bwfa-bulk-tracks,
.bwfa-scope .bwfa-export-report {
margin-left: var(--modal-pad);
margin-right: var(--modal-pad);
margin-bottom: var(--space-5);
}
/* The wrapper that lets a title sit beside the close button carries no
spacing of its own: the header owns the padding, for all of them. */
.bwfa-scope .bwfa-modal-title-group {
margin: 0;
padding: 0;
min-width: 0;
}
/* ---- Stepping through the card -------------------------------------
The arrows were in the header, which pushed the title right and made
this the one modal whose heading didn't start on the shared gutter.
In the footer they have the empty space to themselves, and each one
can say which file it leads to.
------------------------------------------------------------------ */
.bwfa-scope .modal-footer .bwfa-modal-nav {
display: flex;
align-items: center;
gap: 8px;
min-width: 0;
/* Each group is only as wide as what it holds. The framework gives
.bwfa-modal-nav flex: 1 1 auto for the header it was written for, and
in the footer that made both groups grow to fill the row: the forward
arrow ended up stranded in the middle of the footer with its name, a
long way from the buttons it belongs beside, and the margin-left: auto
below could do nothing about it because there was no free space left
for it to absorb. */
flex: 0 0 auto;
}
/* Back sits hard left; forward sits against the buttons it precedes, with
its filename on the inside so each name is beside the arrow that leads
to it and the two groups read outward from the middle. */
.bwfa-scope .modal-footer .bwfa-modal-nav-back {
margin-right: 0;
}
/* All the free space goes here, so the forward arrow and the name of the file
it leads to sit against the buttons rather than floating in the middle. */
.bwfa-scope .modal-footer .bwfa-modal-nav-on {
margin-left: auto;
margin-right: 0;
}
.bwfa-scope .bwfa-modal-nav-name {
font-size: var(--fs-sm);
color: var(--color-text-muted);
max-width: 14em;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
/* A name with no file to name takes no room. */
.bwfa-scope .bwfa-modal-nav-name:empty {
display: none;
}
/* Play becomes Pause, which is a wider word: without a floor the button
changes size the moment you press it and the row shuffles under the
cursor. Sized for the longer of the two. */
.bwfa-scope [data-bwfa-modal-play],
.bwfa-scope [data-bwfa-player-playpause] {
min-width: 5.5em;
}
/* The detail modal puts focus on its close button as it opens, which drew
the framework's black focus ring around the traffic light — on that one
modal only, since it's the only one that focuses anything. No ring on
focus. Keyboard users still get one, in the button's own red rather than
a black box, so it reads as part of the control instead of around it. */
.bwfa-scope .modal-close:focus {
outline: none;
box-shadow: inset 0 0 0 0.5px rgba(0, 0, 0, 0.16);
}
.bwfa-scope .modal-close:focus-visible {
outline: 2px solid rgba(255, 95, 87, 0.55);
outline-offset: 2px;
}
/* ---- the mixer ---------------------------------------------------
One row per track: name, fader, level, mute, solo. Horizontal rather
than a wall of vertical faders, because track names here are words
like "Boom" and "Lav Sarah" and they need room to be read.
------------------------------------------------------------------ */
.bwfa-scope .bwfa-mixer-strips {
display: flex;
flex-direction: column;
gap: var(--space-2);
/* Room for the scale, which hangs above the first meter. */
padding-top: 1.1em;
}
.bwfa-scope .bwfa-mixer-strip {
display: grid;
grid-template-columns: 9em minmax(0, 1.5fr) minmax(0, 1fr) 4.5em auto auto;
align-items: center;
gap: var(--space-3);
}
/* The meter's cell. The scale goes inside it rather than in a row of its own:
a second grid, however carefully its columns are copied, resolves its fr
units from its own contents and lands the marks somewhere else entirely. */
.bwfa-scope .bwfa-mixer-meter-cell {
position: relative;
display: block;
min-width: 0;
}
/* A track name reads the same here as it does in the player's row: the same
pill, the same size, the same colours, so the two places that list the
tracks of a file look like the same thing.
It takes the look and not the behaviour — in the player the pill is the
mute button, here it is a label and mute has its own M — so no pointer
cursor and none of the muted/solo states. inline-block rather than the
chip's inline-flex, because the name has to be able to ellipsis inside a
9em column and text-overflow doesn't reach the children of a flex box. */
.bwfa-scope .bwfa-mixer-name {
display: inline-block;
max-width: 100%;
padding: 0.3rem 0.5rem 0.3rem 0.85rem;
border: 1px solid var(--color-border);
border-radius: var(--radius-pill);
background-color: var(--color-surface-alt);
font-size: var(--fs-sm);
font-weight: 500;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.bwfa-scope .bwfa-mixer-fader {
width: 100%;
margin: 0;
accent-color: var(--color-action);
}
.bwfa-scope .bwfa-mixer-db {
font-variant-numeric: tabular-nums;
text-align: right;
color: var(--text-muted, #8a8a8e);
font-size: 0.86em;
}
.bwfa-scope .bwfa-mixer-mute,
.bwfa-scope .bwfa-mixer-solo {
/* .chip's padding is 0.85rem on the left and 0.5rem on the right — fine
under a word, visibly lopsided under a single letter. Zero it and let
flex do the centring, with the line box collapsed so the glyph sits on
the middle rather than on its own baseline. */
flex: none;
width: 28px;
min-width: 28px;
height: 28px;
padding: 0;
gap: 0;
border-radius: 50%;
display: inline-flex;
align-items: center;
justify-content: center;
line-height: 1;
font-variant-numeric: normal;
text-align: center;
}
.bwfa-scope .bwfa-mixer-mute.is-muted,
.bwfa-scope .bwfa-mixer-solo.is-soloed {
background: var(--color-action);
border-color: var(--color-action);
color: #fff;
}
.bwfa-scope .bwfa-mixer-transport {
display: flex;
align-items: center;
gap: var(--space-3);
margin-top: var(--space-4);
padding-top: var(--space-4);
border-top: 1px solid var(--border, rgba(128, 128, 128, 0.25));
}
.bwfa-scope .bwfa-mixer-transport .btn,
.bwfa-scope .bwfa-player-playpause {
min-width: 6em;
}
/* ---- Dragging a column heading ---------------------------------------
The heading you are holding, following the pointer, so the gesture says
what it is moving. Everything here is the app's own surface, border and
text — the only new thing is the shadow, which is what lifts it off the
table and tells you it is in your hand rather than in the row.
It exists only while a drag is in progress, and it never takes a click:
the pointer has to reach the headings underneath to know where it is.
------------------------------------------------------------------- */
.bwfa-scope .bwfa-col-ghost {
position: fixed;
z-index: 60;
pointer-events: none;
padding: 0.3rem 0.6rem;
border: 1px solid var(--color-border);
border-radius: var(--radius-sm);
background: var(--color-surface);
color: var(--color-text);
font-size: var(--fs-sm);
font-weight: var(--fw-semibold);
white-space: nowrap;
box-shadow: 0 4px 14px rgba(0, 0, 0, 0.18);
}
/* And the column it came from steps back while it is out of place. */
.bwfa-scope .bwfa-table thead th.is-dragging {
opacity: 0.45;
}
/* The waveform thumbnails sat on their own grey panel, which drew a row of
boxes down the table and cut across the striping and the hover. The
drawing itself only ever clears the canvas, so taking the fill off is
enough for the row's own background to show through — the wave keeps its
blue, and the column stops being a column of tiles. */
.bwfa-scope .bwfa-waveform-thumb {
background-color: transparent;
}
/* ---- One button size --------------------------------------------------
The transport's buttons set the standard, and the standard is what the
framework calls .btn-sm. Everything else was a size larger, so a modal's
Close and the toolbar's buttons stood a good 8px taller than the Play
button right beneath them.
Done here, on .btn itself, rather than by adding .btn-sm to every button
in the markup: buttons get built in JavaScript too, and a rule covers the
ones written later without anybody having to remember. .btn-sm keeps
working and is now simply the same thing.
Not included, and deliberately: .bwfa-round-btn, .modal-close and .chip
are their own shapes and carry no .btn at all, so none of this reaches
them.
This sits after the framework's own .btn-sm / .btn-lg / .btn-icon and
matches their specificity, so it outranks all three. .btn-sm is now the
same declaration twice over; .btn-lg and .btn-icon are unused, and a
size modifier would have to be written to beat this rule to work again.
The one button it does not size is the Choose beside the export
destination, which takes its height from the field it stands next to.
--------------------------------------------------------------------- */
.bwfa-scope .btn {
padding: 0.35rem 0.75rem;
font-size: var(--fs-sm);
}
/* The word inside the transport button is not a target. It is rewritten as
playback runs, and a node that changes under the pointer between mousedown
and mouseup takes the click with it. */
.bwfa-scope .bwfa-btn-label {
pointer-events: none;
}
.bwfa-scope .bwfa-mixer-note,
.bwfa-scope .bwfa-mixer-empty {
margin: var(--space-3) 0 0;
font-size: 0.85em;
color: var(--text-muted, #8a8a8e);
}
/* ---- the meters --------------------------------------------------
Pre-fader peak, so the bar says what is on the track rather than what
you have done to it: mute a channel and it carries on reading.
------------------------------------------------------------------ */
.bwfa-scope .bwfa-mixer-meter {
position: relative;
display: block;
width: 100%;
height: 14px;
border-radius: 3px;
border: 1px solid var(--color-border);
overflow: hidden;
/* The scale's own colours, fixed to position: green until about -18, amber
through the last few dB, red at the top. Stops are placed where those
levels actually fall on a -60 to 0 scale, so the colour under the end of
the bar means the same thing every time. */
background: linear-gradient(
to right,
#1e7d3a 0%,
#3aa757 55%,
#7dbb3f 70%,
#e0b13c 85%,
#e07a2f 93%,
#c62828 100%
);
}
/* Covers the part of the scale that isn't lit. Moving this rather than
growing a coloured bar is what keeps each colour tied to its level. */
.bwfa-scope .bwfa-mixer-meter-mask {
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
background: var(--color-surface-alt);
}
/* The highest peak of the last moment or so, left behind as a mark. This is
the part you actually read when something clipped a second ago. */
.bwfa-scope .bwfa-mixer-meter-hold {
position: absolute;
top: 0;
bottom: 0;
width: 2px;
margin-left: -1px;
background: var(--color-text);
opacity: 0;
}
.bwfa-scope .bwfa-mixer-meter.is-hot .bwfa-mixer-meter-hold {
background: #fff;
box-shadow: 0 0 0 1px #c62828;
}
/* The scale, on the strips' own grid so the marks sit over the meters. */
.bwfa-scope .bwfa-mixer-scale {
position: absolute;
left: 0;
right: 0;
bottom: calc(100% + 3px);
height: 1em;
display: block;
}
.bwfa-scope .bwfa-mixer-scale span {
position: absolute;
top: 0;
transform: translateX(-50%);
font-size: 9px;
line-height: 1;
color: var(--color-text-muted);
font-variant-numeric: tabular-nums;
}