/*
 * 01-tokens-layout.css
 *
 * Palette variables, page shell, sidebar, navigation and the dashboard header.
 *
 * Extracted from templates/index.html in Phase 8. ORDER IS LOAD-BEARING: nineteen selectors in
 * this stylesheet are defined more than once and rely on source order to resolve. The <link>
 * tags in index.html must stay in numeric order, and rules must not be moved between files.
 * tests/unit/test_contracts.py::test_css_concatenation_matches enforces both.
 */
        @font-face {
            font-family: 'Komika Axis';
            src: url('/static/fonts/KomikaAxis-Regular.ttf') format('truetype');
            font-weight: 100 900;
            font-style: normal;
            font-display: swap;
        }

        :root {
            /* How tall the fixed mobile bottom nav actually is, kept in sync by
               syncBottomNavHeight() in 10-audio-scroll.js. The nav wraps: revealing the collapsed
               stats row (#bNavCollapsedStats) puts a second line in it, and the reel-armed label
               wraps to two lines, so its height is not a constant anyone can hardcode. Anything
               that has to sit clear of the nav - the mobile video drawer - reads this rather than
               a fixed 64px, which is what left the bottom of the video hidden behind the stats
               row. The fallback is the one-line height, used before the script runs. */
            --bottom-nav-h: 64px;
            /*
             * User: "In the desktop version, make all the UI elements in the tool 20% bigger."
             * Delivered as a page zoom on <html> at the bottom of this file, which is what the
             * browser's own 120% zoom does and therefore scales every element, not just fonts.
             *
             * Zoom does NOT scale viewport units, so a rule asking for 100vh would render 20%
             * taller than the window. Anything that must match the window exactly is written in
             * these units instead and they shrink by the same factor on desktop.
             */
            /*
             * The strip of empty page kept below everything, so the fixed bottom bar never covers
             * the last line of the transcript. It is `body`'s padding-bottom (02-animations-tooltip.css)
             * and it has to be subtracted from the full-height minimums below, or an empty page is
             * 140px taller than the window and scrolls with nothing to scroll to - which is exactly
             * what the user reported.
             */
            --body-bottom-pad: 140px;
            --vh-u: 1vh;
            --dvh-u: 1dvh;
            --vw-u: 1vw;
            /* SetReport AI Palette */
            --bg-dark: #151111;
            --bg-main: #0c0a0a;
            /* Very dark warm brown (stage black) */
            --bg-card: #201919;
            --bg-card-glass: rgba(32, 25, 25, 0.7);

            --primary: #ffc62e;
            /* Marquee yellow */
            --primary-light: #ffd866;

            --accent-blue: #c1121f;
            /* Curtain red as TEXT. #c1121f on the dark page is only 2.9:1; this is 7.3:1. Fills and
               edges keep --accent-blue, anything that is read uses this. */
            --accent-red-text: #ff7b72;
            --accent-pink: #ec4899;
            --accent-yellow: #fbbf24;

            --text-main: #f8f0e3;
            --text-muted: #ab9c97;

            --border: #3a2e2c;

            /* Gradients */
            --gradient-main: linear-gradient(135deg, #0c0a0a 0%, #2b0d0d 100%);
            --gradient-card: linear-gradient(145deg, rgba(32, 25, 25, 0.8) 0%, rgba(21, 17, 17, 0.9) 100%);
            --gradient-glow: linear-gradient(90deg, #ffc62e, #ff9f1c);

            /* Shadows */
            --shadow-card: 0 4px 6px -1px rgba(0, 0, 0, 0.3), 0 2px 4px -1px rgba(0, 0, 0, 0.2);
            --shadow-glow: 0 0 15px rgba(255, 198, 46, 0.3);

            /* Legacy overrides */
            --bg-page: #151111;
            --active-red: #ec4899;
            /* Pink instead of red */
            --tag-time-bg: #201919;
            --tag-laugh-bg: rgba(234, 179, 8, 0.2);
            --tag-laugh-text: #fcd34d;
            --highlight: rgba(255, 198, 46, 0.3);
        }

        body {
            font-family: 'Inter', sans-serif;
            background: var(--gradient-main);
            color: var(--text-main);
            margin: 0;
            padding: 0;
            min-height: calc(var(--vh-u) * 100);
            /* border-box, so the 140px padding below is INSIDE the 100vh minimum rather than added
               to it. User: "There's no reason to let the user scroll down in an empty page." */
            box-sizing: border-box;
            display: flex;
            /* Sidebar layout */
        }

        /* Layout Structure */
        .app-shell {
            display: flex;
            width: 100%;
            /* The window MINUS the strip body reserves for the bottom bar - together they come to
               exactly one screen, so an empty page has nothing to scroll. */
            min-height: calc(var(--vh-u) * 100 - var(--body-bottom-pad));
        }

        .main-content {
            flex: 1;
            padding: 20px 20px 100px 20px;
            /* Space for bottom nav check */
            /* NO overflow-y here, and that is deliberate. This element has no height of its own -
               .app-shell only sets min-height - so it can never actually scroll; the window does.
               But `overflow-y: auto` still made it the scrollport that any `position: sticky`
               descendant is measured against, and a scrollport that never scrolls pins nothing.
               That is what stopped the sticky bit headers in .bit-layout from ever moving. */
            max-width: 1400px;
            margin: 0 auto;
            position: relative;
        }

        /* User: "what changes do i need to make to the landing page and the app page so that they
           meet the minimimum requireents to be ada-compliant?" Three small rules for the second
           half of that work.

           .sr-only: on the page for a screen reader, invisible to everyone else. It is what the
           two regions at the end of templates/index.html wear - the ones announce()
           (01a-screen-reader.js) writes into to have something said aloud. Clipped to one pixel
           rather than display:none, because a screen reader skips what is not displayed. */
        .sr-only {
            position: absolute;
            width: 1px;
            height: 1px;
            margin: -1px;
            padding: 0;
            overflow: hidden;
            clip-path: inset(50%);
            white-space: nowrap;
            border: 0;
        }

        /* The main area and the boxes that open over the page take focus from a script only
           (tabindex="-1"), so that a screen reader names the box and the next Tab starts inside
           it. They are not controls, so the focus ring that would otherwise be drawn round the
           whole of them is switched off; every button inside keeps its own. */
        .main-content:focus,
        .mobile-sheet:focus,
        .confirm-card:focus,
        .sdo-card:focus,
        .reel-picks-drawer:focus,
        .reel-detail-section:focus {
            outline: none;
        }

        /* Sidebar */
        .sidebar {
            width: 285px;
            background: rgba(21, 17, 17, 0.95);
            backdrop-filter: blur(10px);
            border-right: 1px solid var(--border);
            padding: 20px 14px;
            display: flex;
            flex-direction: column;
            gap: 20px;
            position: fixed;
            top: 0;
            left: 0;
            height: calc(var(--vh-u) * 100);
            height: calc(var(--dvh-u) * 100);
            box-sizing: border-box;
            overflow-y: auto;
            overflow-x: hidden;
            z-index: 2000;
            transform: translateX(-100%);
            transition: transform 0.3s ease-in-out;
        }

        .sidebar.open {
            transform: translateX(0);
        }

        /* Phone only. User, 2026-10-28: "add a button under the "Log out" button showing a left
           pointing arrow" and "make the right edge of the menu draggable". */
        .menu-arrow-close {
            display: none;
        }

        .sidebar-grip {
            display: none;
        }

        @media (max-width: 767px) {
            .menu-arrow-close {
                display: flex;
                align-items: center;
                justify-content: center;
                width: 100%;
                padding: 10px 0;
                background: none;
                border: none;
                color: var(--text-muted);
                cursor: pointer;
            }

            .sidebar {
                touch-action: pan-y;
            }

            /* A pill straddling the menu's right edge, shown only while the menu is open and not
               being dragged. Pointer-events off: the touch lands on the menu, whose edge zone
               (04-lifecycle.js) does the dragging. */
            .sidebar-grip {
                display: block;
                position: fixed;
                top: 50%;
                left: 285px;
                width: 6px;
                height: 56px;
                margin-left: -10px;
                border-radius: 3px;
                background: var(--text-muted);
                opacity: 0;
                pointer-events: none;
                z-index: 2001;
                transform: translateY(-50%);
                transition: opacity 0.2s ease;
            }

            .sidebar.open ~ .sidebar-grip {
                opacity: 0.7;
            }

            .sidebar.edge-dragging ~ .sidebar-grip {
                opacity: 0;
            }
        }

        .nav-brand {
            font-size: 1.5rem;
            font-weight: 800;
            color: var(--accent-red-text);
            margin-bottom: 20px;
            display: flex;
            align-items: center;
            gap: 10px;
        }

        /* Was the ⚡ emoji's "spotlight" yellow. User: "Remove the lightning emoji from the left
           menu next to the word Ovation" - the span it colored now holds the gradient-clipped
           "Ovation" text (.dh-name-main) instead, and this selector's specificity (a class plus a
           type) would otherwise outrank that class-only rule and override its `color: transparent`
           with this yellow, silencing the gradient clip. Left as a comment, not deleted outright,
           because .nav-brand's own flex/gap layout still assumes a span child here and a future
           icon would want this rule's spot back. */

        .nav-item {
            display: flex;
            align-items: center;
            gap: 12px;
            padding: 12px;
            border-radius: 12px;
            color: var(--text-muted);
            font-weight: 500;
            cursor: pointer;
            transition: all 0.2s;
        }

        .nav-item:hover,
        .nav-item.active {
            background: rgba(193, 18, 31, 0.1);
            color: var(--text-main);
        }

        .nav-item.active {
            background: linear-gradient(90deg, rgba(193, 18, 31, 0.2), transparent);
            border-left: 3px solid var(--accent-blue);
        }

        /* Bottom Nav (Mobile) */
        .bottom-nav {
            position: fixed;
            bottom: 0;
            left: 0;
            right: 0;
            background: rgba(21, 17, 17, 0.9);
            backdrop-filter: blur(10px);
            border-top: 1px solid var(--border);
            padding: 12px 20px;
            padding-bottom: calc(12px + env(safe-area-inset-bottom, 0px));
            display: flex;
            flex-wrap: wrap;
            justify-content: space-around;
            z-index: 1200;
            touch-action: none;
        }

        /* The collapsed stats row inside the nav, and the items in it, were removed with the row
           itself - see the note where it used to sit in templates/index.html. */

        @media (min-width: 768px) {
            .bottom-nav {
                display: none;
            }

            /* Desktop only: the content column runs at 80% of the width it would otherwise take,
               still centred by the margin: 0 auto above. Phones are below this breakpoint and keep
               the full width they need.

               Three things are needed to actually get 80%, and dropping any one silently gives a
               wider column:
                 - flex-grow 0, because .main-content is a flex item with flex: 1 and would
                   otherwise grow straight back to filling its container regardless of width;
                 - border-box, because the rule's 20px side padding is outside the width in this
                   stylesheet's default content-box, which would add 40px back on;
                 - both the percentage and the cap scaled, since either can be the one in force -
                   the percentage governs screens the cap never reaches, the cap governs wide ones.
                   1152px is 80% of the 1440px this used to occupy at its cap (1400 + 40 padding). */
            /* WIDENED on request: "Make the tool screen wider ... on both desktop and mobile."
               The three requirements above still hold; only the two numbers moved, and they moved
               together for the reason the last bullet gives - the percentage governs screens the
               cap never reaches, the cap governs wide ones, so raising one alone would leave the
               other in force and look like the change had no effect. 94% keeps a visible margin
               either side rather than running the cards into the window edge. The cap is raised
               with it and .container's own max-width, which is the real limiter on a wide
               monitor, is raised to match in 04-controls-status-upload.css. */
            .main-content {
                box-sizing: border-box;
                width: 94%;
                max-width: 1600px;
                flex-grow: 0;
                flex-basis: auto;
            }
        }

        /* Mobile transport: Play/Pause glyph + the audio timeline on the nav's top border.
           One SVG carries both icons; .playing on .bottom-nav decides which is drawn, so switching
           is a class toggle rather than a markup swap. */
        .b-nav-play-svg {
            width: 1.2rem;
            height: 1.2rem;
            display: block;
            fill: currentColor;
        }

        .b-nav-play-svg .icon-pause { display: none; }
        .bottom-nav.playing .b-nav-play-svg .icon-play { display: none; }
        .bottom-nav.playing .b-nav-play-svg .icon-pause { display: block; }

        #bNavPlayBtn { cursor: pointer; }
        .bottom-nav.playing #bNavPlayBtn { color: var(--accent-red-text); }

        /* The desktop dock's own play/pause (.dock-play-btn, static/css/05-timeline-bits-dock.css)
           reuses the exact same SVG and the exact same class-toggle trick, scoped to #audioDock
           instead of .bottom-nav - see navSetPlaying() in 10-audio-scroll.js, which now toggles
           .playing on both. */
        #audioDock.playing .dock-play-btn .b-nav-play-svg .icon-play { display: none; }
        #audioDock.playing .dock-play-btn .b-nav-play-svg .icon-pause { display: block; }

        /* NO position override here. .bottom-nav is already `position: fixed` further up this same
           file, and fixed positioning establishes a containing block for absolutely-positioned
           children on its own - so the scrubber anchors to it without help. Re-declaring
           `position: relative` in a later rule silently beat the earlier `fixed` and unpinned the
           whole bar from the bottom of the screen: it became an in-flow 168px block at the top.
           This stylesheet has nineteen selectors defined more than once; source order decides. */

        /* The timeline itself: a thin white rule lying along the nav's top border. Hidden until
           audio has been started once, so the bar looks untouched before any playback. */
        .nav-scrubber {
            position: absolute;
            top: -1px;
            left: 0;
            right: 0;
            height: 2px;
            background: rgba(255, 255, 255, 0.85);
            display: none;
            touch-action: none;
        }

        /* "In the mobile version only, shrink the audio player track to be 10% smaller, 5% from
           each side." Now also applied to the desktop dock (.audio-dock .nav-scrubber), making both
           audio track bars 5% shorter on both sides. */
        .bottom-nav .nav-scrubber,
        .audio-dock .nav-scrubber {
            left: 5%;
            right: 5%;
        }

        .nav-scrubber.visible {
            display: block;
        }

        /* The played portion of the track, same purple as .nav-playhead. Sits between the base
           white line and the playhead/grab-band, so it reads as "filled up to here" rather than
           replacing the track. Width is set in px by navRenderPosition() in 10-audio-scroll.js -
           same x the playhead uses, so the fill always ends exactly under the circle. */
        .nav-scrub-fill {
            position: absolute;
            top: 0;
            left: 0;
            width: 0;
            height: 2px;
            background: var(--primary);
            pointer-events: none;
        }

        /* A 20px invisible grab band above and below the 2px line. Without it the playhead is a
           two-pixel target on a touch screen, and every miss lands on the nav buttons underneath.
           pointer-events: none because the drag detection in 10-audio-scroll.js listens on
           `document` and does its own geometry check (a click's coordinates against this same
           20px margin) rather than relying on `event.target` - so this band never needs to
           actually catch a click itself, and leaving it catching one anyway would let it swallow
           clicks meant for whatever real control sits within 20px underneath it (the desktop
           audio dock's native <audio> seek bar sits exactly that close). */
        .nav-scrubber::before {
            content: "";
            position: absolute;
            left: 0;
            right: 0;
            top: -20px;
            bottom: -20px;
            pointer-events: none;
        }

        .nav-playhead {
            position: absolute;
            top: 50%;
            left: 0;
            width: 12px;
            height: 12px;
            margin-left: -6px;
            margin-top: -6px;
            border-radius: 50%;
            background: var(--primary);
            box-shadow: 0 0 6px rgba(255, 198, 46, 0.9);
            pointer-events: none;
            transition: transform 0.12s ease;
        }

        /* "When the user is manually moving the playhead, make the playhead twice the size" */
        .nav-scrubber.scrubbing .nav-playhead {
            transform: scale(2);
            box-shadow: 0 0 10px rgba(255, 198, 46, 1);
        }

        /* "create chapters (similar to how youtube does it using gaps in the track line) based on
           the bit timings." A small cut in the line at each bit boundary, coloured to match the
           nav bar behind it so it reads as a gap rather than a mark. Positioned in px by
           navRenderChapterGaps() in 10-audio-scroll.js - one per bit start, first bit excluded.
           z-index above .nav-scrub-fill so the gap still shows once playback has filled past it. */
        .nav-chapter-gap {
            position: absolute;
            top: 0;
            width: 3px;
            height: 2px;
            margin-left: -1.5px;
            background: rgba(21, 17, 17, 0.95);
            pointer-events: none;
            z-index: 1;
        }

        /* Time readout, shown only while dragging. translateX(-50%) centres it on the playhead;
           it is carried by the playhead's own left offset via the parent's inline style.
           "show the bit name that they're currently in" appends after the time - navRenderPosition()
           in 10-audio-scroll.js builds the combined string. max-width/ellipsis because a bit topic
           can run much longer than a timestamp and this must not overflow a phone screen. */
        .nav-scrub-time {
            position: absolute;
            bottom: 14px;
            left: 0;
            transform: translateX(-50%);
            padding: 2px 7px;
            border-radius: 5px;
            background: rgba(21, 17, 17, 0.95);
            border: 1px solid var(--border);
            color: var(--text-main);
            font-size: 0.7rem;
            font-weight: 600;
            max-width: min(70vw, 260px);
            overflow: hidden;
            text-overflow: ellipsis;
            white-space: nowrap;
            pointer-events: none;
            opacity: 0;
            transition: opacity 0.12s ease;
        }

        /* "When audio is playing, make sure the audio playhead is showing the current time."
           The readout used to appear only under a finger or cursor mid-drag - .scrubbing - which
           meant that during ordinary playback the playhead moved with no time next to it. .playing
           is set on every scrubber by navSetPlaying() in 10-audio-scroll.js from the audio
           element's own play/pause events, so it covers every way playback can start. */
        /* `show-briefly` is the three-second tail after playback stops (and the same window when
           it starts), set on every scrubber by navSetPlaying() - see the comment there. */
        .nav-scrubber.scrubbing .nav-scrub-time,
        .nav-scrubber.show-briefly .nav-scrub-time,
        .nav-scrubber.playing .nav-scrub-time {
            opacity: 1;
        }

        /* The two pieces of the readout, set independently by navRenderPosition() in
           static/js/10-audio-scroll.js. Plain inline spans by default (no display/overflow rules
           of their own) - the desktop reading is unchanged from before this split: both spans flow
           on the single line .nav-scrub-time above already makes nowrap+ellipsis, joined by the
           "·" this ::before inserts, exactly reproducing the one combined string that used to be
           written here directly. */
        .nav-scrub-time-bit:not(:empty)::before {
            content: " · ";
        }

        /* "In the mobile version only, when a user moves the tracker... change the timestamp and
           bit name display such that they would be in two lines." Scoped to .bottom-nav's copy for
           the same reason the track-shrink rule above is - .bottom-nav only renders below 768px,
           so no media query is needed to keep this off the desktop dock.

           flex-direction: column is what turns the two inline spans above into two stacked lines;
           the "·" ::before is switched off here since a line of its own no longer needs a
           separator in front of it. Each span becomes its own block with its own ellipsis, so a
           long bit name truncates on its own line without eating into room the timestamp needs on
           the other. */
        .bottom-nav .nav-scrub-time {
            display: flex;
            flex-direction: column;
            align-items: center;
            white-space: normal;
            transition: opacity 0.6s ease-out;
        }

        /* User: "When audio is not playing, don't display the time and bit name on the audio/video
           playhead." This rule used to do the opposite - a paused phone kept the readout up for
           good, which is what sat on top of the Set Stats sheet. Nothing shows it now except
           .playing, .scrubbing or the post-pause .show-briefly window. */
        .bottom-nav .nav-scrubber:not(.playing):not(.show-briefly):not(.scrubbing) .nav-scrub-time {
            opacity: 0;
        }

        .bottom-nav .nav-scrubber.playing .nav-scrub-time {
            opacity: 0;
        }

        .bottom-nav .nav-scrubber.playing.show-briefly .nav-scrub-time,
        .bottom-nav .nav-scrubber.scrubbing .nav-scrub-time {
            opacity: 1;
            transition: opacity 0.15s ease-in;
        }

        .bottom-nav .nav-scrub-time-value,
        .bottom-nav .nav-scrub-time-bit {
            display: block;
            max-width: 100%;
            overflow: hidden;
            text-overflow: ellipsis;
            white-space: nowrap;
        }

        .bottom-nav .nav-scrub-time-bit:empty {
            display: none;
        }

        .bottom-nav .nav-scrub-time-bit::before {
            content: none;
        }

        .b-nav-item {
            display: flex;
            flex-direction: column;
            align-items: center;
            justify-content: flex-start;
            font-size: 0.75rem;
            color: var(--text-muted);
            gap: 4px;
            cursor: pointer;
            /* User: "When a user clicks the play button or a word ... the play/pause button and
               the Home button at the bottom shift a little bit. Make it so they don't move at
               all." .bottom-nav lays these two out with justify-content: space-around, which
               shares space by each item's OWN width - "Play" and "Pause" are different lengths, so
               swapping the label changed this item's width and space-around redistributed the gaps
               around it, nudging Home sideways too (measured: 2.7px). flex: 1 1 0 gives Home and
               Play each a fixed, EQUAL half of the bar regardless of their content's width, so
               neither one's label length can ever move the other. */
            flex: 1 1 0;
            text-align: center;
        }

        .b-nav-item span {
            text-align: center;
        }

        /* User: "In the mobile version, change the buttons at the bottom (and the rings) such that
           they show some hover effect that's consistent with hover effects other places in the
           tool and in the desktop version". The desktop dock's buttons (.btn-dock-action:hover in
           05-timeline-bits-dock.css) lighten their panel and lift 1px; this is the same two moves.
           The item has no box of its own, so the lighter panel is painted as a background plus a
           same-colour spread shadow - it grows outward without changing the item's footprint,
           which flex: 1 1 0 above depends on. Hover is gated to real pointers, because a tap on a
           touch screen leaves :hover stuck on; :active gives the phone the same look for as long
           as the finger is down. The armed-reel item keeps its own glow. */
        .b-nav-item {
            border-radius: 6px;
            transition: background-color 0.2s ease, box-shadow 0.2s ease, transform 0.2s ease;
        }

        @media (hover: hover) {
            .b-nav-item:not(.reel-armed):hover {
                background-color: rgba(58, 46, 44, 0.9);
                box-shadow: 0 0 0 6px rgba(58, 46, 44, 0.9);
                transform: translateY(-1px);
            }
        }

        .b-nav-item:not(.reel-armed):active {
            background-color: rgba(58, 46, 44, 0.9);
            box-shadow: 0 0 0 6px rgba(58, 46, 44, 0.9);
            transform: translateY(-1px);
        }

        .b-nav-item.active {
            color: var(--accent-red-text);
        }

        /* User: "make that whole button glow in cyan-magenta in the same way that the 'Generate
           Reel' buttons look and behave" - the SAME animation (reelArmedGlow, defined once in
           05-timeline-bits-dock.css - keyframe names resolve globally regardless of which
           stylesheet defines them or load order) the per-bit Generate Reel button uses for this
           exact state,
           reused rather than redefined so the two glows can never drift apart. Only the box shape
           differs: .btn-control already has its own background/border for the glow to trace, where
           this item normally has neither, so padding + a matching negative margin give it one
           without shifting its neighbour - the margin cancels the padding's effect on this item's
           own footprint, and flex: 1 1 0 above means that footprint was never based on content size
           to begin with. line-height keeps the two-line armed label (line break in its own text)
           from reading as more cramped than the icon above it. */
        .b-nav-item.reel-armed {
            border-radius: 10px;
            padding: 6px 10px;
            margin: -6px -10px;
            line-height: 1.3;
            animation: reelArmedGlow 1.8s ease-in-out infinite;
        }

        .b-nav-icon {
            font-size: 1.2rem;
            height: 24px;
            display: flex;
            align-items: center;
            justify-content: center;
            line-height: 1;
            flex-shrink: 0;
        }

        #bNavStartOverBtn {
            color: #38bdf8;
            text-align: center;
        }
        #bNavStartOverBtn span {
            text-align: center;
            display: block;
            width: 100%;
        }
        /* No resting glow on any icon in this row. The icons used to carry a drop-shadow of their
           caption colour, and a later request ("Create a glow effect around the 'set stats' and
           'insights' emojis, same to the glow effect that the other buttons in that row have")
           extended it to all of them. DESIGN.md's Glow Means Now Rule then reserved glow for
           whatever is happening now, and the user chose: "Follow the new rules". So the
           drop-shadows are gone from every item at once - removing only two would leave the row
           inconsistent. The armed-reel pulse (.b-nav-item.reel-armed above) stays: that one IS
           a "now" state. Caption colours are untouched. */
        #bNavMenuBtn {
            color: var(--text-main);
        }

        #bNavInsightsBtn {
            color: #facc15;
        }

        #bNavReelsBtn {
            color: #ffd866;
        }

        #bNavPlayBtn {
            color: #4ade80;
        }
        #bNavPlayBtn .b-nav-play-svg {
            color: #4ade80;
            fill: currentColor;
        }

        #bNavVideoBtn {
            color: #fb923c;
        }

        /* The popup .b-nav-tooltip-popup styled was showMobileMetricTooltip's, removed with the
           collapsed stats row that was its only caller. */

        /* Dashboard Header */
        header {
            display: none;
            /* Hide old header */
        }

        .dashboard-header {
            margin-bottom: 24px;
        }

        .dh-title {
            font-size: 1.8rem;
            font-weight: 700;
            color: var(--accent-red-text);
            display: flex;
            align-items: center;
            gap: 8px;

            /* The anchor for the menu button, which is positioned out of the flow so it cannot
             * push the centred title off-centre. */
            position: relative;
        }

        /* Menu button. User, 2026-10-28: "In desktop, move the menu hamburger button from the top left
         * side of the screen to the bottom dashboard, on the far left side of it, and put it in a
         * square button like the other buttons in that dashboard." Pinned fixed to the viewport's
         * bottom-left corner, level with the dock's row of buttons (the markup says why it is not
         * inside #audioDock). Its size and look come from .btn-dock-square / .btn-dock-action in
         * 05-timeline-bits-dock.css. When the sidebar opens it slides alongside it, so clicking it
         * again closes the sidebar. On a phone it is hidden: the bottom bar's #bNavMenuBtn clicks it. */
        #hamburgerBtn,
        .dh-title #hamburgerBtn {
            position: fixed;
            left: 12px;
            bottom: 9px;
            z-index: 2100;
            margin-right: 0 !important;
            transition: left 0.3s ease-in-out, background 0.2s ease, border-color 0.2s ease;
        }

        @media (max-width: 767px) {
            #hamburgerBtn,
            .dh-title #hamburgerBtn {
                display: none;
            }
        }

        .sidebar.open ~ #hamburgerBtn {
            left: 312px;
        }

        /* The two lines of the name, stacked beside the menu button.
         *
         * A column inside the existing flex row, so the hamburger stays vertically centred against
         * both lines rather than against the first one.
         *
         * Sized in `em`, not `rem` or px: the mobile rule in 06 overrides .dh-title's font-size,
         * and em makes both lines follow it automatically. With rem the subtitle would keep its
         * desktop size on a phone and the two lines would drift apart at every breakpoint. */
        .dh-name {
            display: flex;
            flex-direction: column;
            line-height: 1.15;

            /* Centred on the HEADER, not merely centred between the menu button and the right
             * edge. The button is taken out of the flow below, so this block gets the full width
             * and its centre is the page's centre - otherwise the title would sit about 20px
             * right of centre, which reads as "slightly wrong" rather than as a choice.
             *
             * text-align centres the two lines against each other as well, so the subtitle sits
             * under the middle of "Ovation" rather than under its first letter. */
            flex: 1;
            text-align: center;
        }

        .dh-name-sub {
            font-size: 0.45em;
            font-weight: 600;
            color: var(--text-muted);
            letter-spacing: 0.02em;
        }

        /* "match 100% the gradient color and design of the word 'Ovation' on the landing page."
         * Every property .landing-logo sets in static/css/landing.css, copied verbatim - weight
         * and letter-spacing included, not just the gradient clip - so this reads as the same
         * brand mark rather than a same-color-different-design cousin of it. Only font-size is
         * left to .dh-title's own sizing, which is the app's page-title size, not the logo's.
         * color: transparent alongside -webkit-text-fill-color: transparent covers a
         * non-WebKit engine that honours background-clip: text without the -webkit- fill
         * property - without it such an engine would paint the inherited --accent-blue
         * .dh-title colour on top of the clipped gradient instead of revealing it. */
        .dh-name-main {
            font-weight: 800;
            letter-spacing: -0.02em;
            background: var(--gradient-glow);
            -webkit-background-clip: text;
            background-clip: text;
            -webkit-text-fill-color: transparent;
            color: transparent;
        }


        /*
         * User: "In the desktop version, make all the UI elements in the tool 20% bigger. ... The
         * one with the bigger text is showing my browser after I zoomed in to 125%, which looks
         * better to me but just a tad big, so make all the elements just 20% bigger."
         *
         * A page zoom rather than a font-size bump: the layout is built from px paddings, icon
         * sizes and ring diameters as much as from type, and only zoom scales all of them at once
         * - exactly the 120% the user compared against in their browser.
         *
         * Desktop only (the mobile layout is untouched, per the request) and keyed to the same
         * 768px breakpoint the rest of the stylesheets use. Media queries are evaluated against
         * the unzoomed viewport, so this cannot flip the page between its own breakpoints.
         *
         * --vh-u / --vw-u shrink in step because zoom does not scale viewport units; see the
         * comment on their definition in :root above.
         */
        @media (min-width: 768px) {
            html.app-page {
                --vh-u: calc(1vh / 1.2);
                --dvh-u: calc(1dvh / 1.2);
                --vw-u: calc(1vw / 1.2);
            }

            html.app-page {
                zoom: 1.2;
            }
        }

        /*
         * User: "change the 'Filler Words' emoji from '🗣️' to the emoji picture in this image."
         * A PNG rather than a character, so it is sized in `em` and inherits the font-size of
         * whatever pill, stat row or drawer heading it sits in - the same way the emoji it
         * replaces did.
         */
        .filler-emoji-icon {
            width: 1.15em;
            height: 1.15em;
            vertical-align: -0.2em;
            object-fit: contain;
        }
