/* Color tokens. Two-tier: Radix gray primitives + semantic aliases.
   Dark mode (future) swaps the --gray-* values; semantic tokens follow. */
:root {
    /* Tell the UA this site supports both modes; affects scrollbars and
       default form-control styling so they don't look out of place. */
    color-scheme: light dark;

    /* Radix gray (light) */
    --gray-1:  #fcfcfc;
    --gray-2:  #f9f9f9;
    --gray-3:  #f0f0f0;
    --gray-4:  #e8e8e8;
    --gray-5:  #e0e0e0;
    --gray-6:  #d9d9d9;
    --gray-7:  #cecece;
    --gray-8:  #bbbbbb;
    --gray-9:  #8d8d8d;
    --gray-10: #838383;
    --gray-11: #646464;
    --gray-12: #202020;

    /* Surfaces */
    --color-bg-primary:    #fff;
    --color-bg-secondary:  var(--gray-2);  /* table header, calendar tooltip th */
    --color-bg-hover:      var(--gray-3);  /* button / menu hover */
    --color-bg-stripe:     var(--gray-2);  /* table row alternation */
    --color-bg-empty-cell: var(--gray-3);  /* empty calendar cell */

    /* Text */
    --color-text-primary:   var(--gray-12);
    --color-text-secondary: var(--gray-11); /* labels, captions, bar chart labels */
    --color-text-tertiary:  var(--gray-10); /* empty-state, separator */
    --color-text-chart:     var(--gray-9);  /* D3 chart annotations */
    --color-text-light:     #fff;            /* text on dark/colored bg (absolute) */
    --color-text-dark:      #000;            /* text on bright/colored bg (absolute) */
    --color-text-emphasis:  #000;            /* one shade past primary; flips with mode */
    --color-text-error:     #E63946;

    /* Borders */
    --color-border-subtle:         var(--gray-3); /* faint dividers */
    --color-border-standard:       var(--gray-5); /* most borders */
    --color-border-strong:         var(--gray-7); /* section dividers, button group outlines */
    --color-border-month-divider:  var(--color-bg-primary); /* calendar month separator */

    /* Accent / actions */
    --color-accent:            #4CAF50;
    --color-accent-hover:      #45a049;
    --color-accent-selected:   #2e7d32;
    --color-accent-focus-ring: rgba(76, 175, 80, 0.2);
    --color-destructive:       #c0392b;
    --color-highlight:         #ffcc00; /* play-square hover */

    /* Part colors (instruments) */
    --color-part-v1:        #1ceaf9;
    --color-part-v2:        #01c472;
    --color-part-va:        #007961;
    --color-part-vc:        #003b85;
    --color-part-fallback:  var(--gray-9);
    --color-part-v1-text:   var(--color-text-dark);  /* dark text on bright cyan */
    --color-part-v2-text:   var(--color-text-light);
    --color-part-va-text:   var(--color-text-light);
    --color-part-vc-text:   var(--color-text-light);

    /* Shadows */
    --color-shadow-subtle: rgba(0, 0, 0, 0.12);
    --color-shadow-light:  rgba(0, 0, 0, 0.1);
}

/* Dark mode. Swap the Radix gray scale to grayDark; semantic tokens flow
   through automatically (e.g. --color-text-primary still resolves to
   var(--gray-12), which now points at near-white #eee instead of #202020).
   Only tokens whose mapping doesn't naturally invert get an explicit override.

   Applied via two parallel paths:
   - `@media (prefers-color-scheme: dark) :root:not([data-theme])` — system
     dark mode, when the user hasn't manually overridden.
   - `:root[data-theme="dark"]` — explicit user choice (light is the base
     :root, so it needs no override block).

   The blocks duplicate; CSS has no syntactic way to share a block between an
   @media-scoped selector and an attribute-scoped selector. Keep them in sync.

   The theme manager (src/themeManager.js) sets/removes data-theme on <html>;
   components that bake colors at render (calendarComponent) re-render via
   subscribe(); see CLAUDE.md for the contract. */
@media (prefers-color-scheme: dark) {
    :root:not([data-theme]) {
        /* Radix grayDark */
        --gray-1:  #111111;
        --gray-2:  #191919;
        --gray-3:  #222222;
        --gray-4:  #2a2a2a;
        --gray-5:  #313131;
        --gray-6:  #3a3a3a;
        --gray-7:  #484848;
        --gray-8:  #606060;
        --gray-9:  #6e6e6e;
        --gray-10: #7b7b7b;
        --gray-11: #b4b4b4;
        --gray-12: #eeeeee;

        color-scheme: dark;

        --color-bg-primary:    var(--gray-1);
        --color-text-emphasis: #fff;
        --color-shadow-subtle: rgba(0, 0, 0, 0.5);
        --color-shadow-light:  rgba(0, 0, 0, 0.4);
    }

    :root:not([data-theme]) #tabs button {
        background-color: transparent;
    }
}

:root[data-theme="dark"] {
    --gray-1:  #111111;
    --gray-2:  #191919;
    --gray-3:  #222222;
    --gray-4:  #2a2a2a;
    --gray-5:  #313131;
    --gray-6:  #3a3a3a;
    --gray-7:  #484848;
    --gray-8:  #606060;
    --gray-9:  #6e6e6e;
    --gray-10: #7b7b7b;
    --gray-11: #b4b4b4;
    --gray-12: #eeeeee;

    color-scheme: dark;

    --color-bg-primary:    var(--gray-1);
    --color-text-emphasis: #fff;
    --color-shadow-subtle: rgba(0, 0, 0, 0.5);
    --color-shadow-light:  rgba(0, 0, 0, 0.4);
}

:root[data-theme="dark"] #tabs button {
    background-color: transparent;
}

body, html {
    margin: 0;
    padding: 0;
    height: 100%;
    width: 100%;
    font-size: 16px; /* Adjust base font-size if needed */
    color: var(--color-text-primary);
    background-color: var(--color-bg-primary);
}

/* iOS standalone safe-area handling. viewport-fit=cover (set in <head>) makes
   env(safe-area-inset-*) report real values on notched/Dynamic Island devices,
   so we have to lay out around the status bar, home indicator, and (in
   landscape) the side notches. body padding handles bottom + sides for normal
   in-flow content; absolute/fixed chrome (#menu, fullscreen overlay) handles
   its own top inset since absolute positioning ignores body padding. */
body {
    padding-bottom: env(safe-area-inset-bottom);
    padding-left: env(safe-area-inset-left);
    padding-right: env(safe-area-inset-right);
}
h1 {
    margin-top: 5px;

}

/* Container for the hamburger menu */
#menu {
    position: absolute;
    /* Keep the menu clear of the Dynamic Island / status bar (top inset) and
       the landscape side notch (left inset) when running standalone with
       viewport-fit=cover. Both fall back to 0 in normal mobile Safari. */
    top: env(safe-area-inset-top);
    left: env(safe-area-inset-left);
    padding: 10px;
    z-index: 1000;
}

/* Big tap target around the icon (~44×44) without enlarging the icon.
   Filled rounded backdrop scopes the icon as an obvious button — important on
   mobile where the icon sits in the same left column as the "All" date-range
   button and could otherwise be mistaken for inline UI. */
.hamburger-menu {
    cursor: pointer;
    padding: 8px;
    margin: -8px -8px 2px -8px;
    -webkit-tap-highlight-color: transparent;
    background-color: var(--color-bg-hover);
    border-radius: 8px;
    transition: background-color 0.15s;
}

.hamburger-menu:active {
    background-color: var(--gray-5);
}

@media (hover: hover) {
    .hamburger-menu:hover {
        background-color: var(--gray-5);
    }
}

.bar {
    display: block;
    width: 28px;
    height: 3px;
    margin: 5px 0;
    background-color: var(--color-text-primary);
    border-radius: 1px;
}

.menu-items {
    display: none;
    background-color: var(--color-bg-primary);
    padding: 4px 0;
    border: 1px solid var(--color-border-standard);
    border-radius: 8px;
    box-shadow: 0 4px 12px var(--color-shadow-subtle);
    min-width: 220px;
}

.menu-item {
    display: flex;
    align-items: center;
    gap: 10px;
    color: var(--color-text-primary);
    text-decoration: none;
    padding: 12px 16px;
    margin: 0;
    font-size: 15px;
    border-top: 1px solid var(--color-border-subtle);
    -webkit-tap-highlight-color: transparent;
}

.menu-item:first-child {
    border-top: none;
}

.menu-item:active {
    background-color: var(--color-bg-hover);
}

/* Inline SVG icons inherit the menu-item's text color. Disable pointer
   events so clicks always register on the <a>, never on the <svg>. */
.menu-icon {
    flex-shrink: 0;
    pointer-events: none;
}

/* Stronger divider above the first action item, separating it visually from
   the navigation items above. Only matches the first action-classed item
   because subsequent action items follow another action item. */
.menu-item:not(.menu-item--action) + .menu-item.menu-item--action {
    border-top: 1px solid var(--color-border-strong);
    margin-top: 4px;
}

.menu-item--destructive {
    color: var(--color-destructive);
}

/* Version row highlights in the accent color only when a newer deploy is
   available; otherwise it reads as a plain "Up to date" status line. Toggled by
   App._checkVersion(). */
.menu-item--update {
    color: var(--color-accent);
    font-weight: 600;
}

@media (hover: hover) {
    .menu-item:hover {
        background-color: var(--color-bg-hover);
    }
}

@media only screen and (max-width: 600px) {
    .menu-items {
        min-width: 240px;
    }
    .menu-item {
        padding: 14px 18px;
        font-size: 16px;
    }

    /* The hamburger is position: absolute, so layout treats it as
       zero-height. On mobile its visible backdrop extends to ~y=54 and
       overlaps the filter row directly below the page h1. Push the bottom
       margin of the h1 out so whatever follows it (date filter, calendar
       grid, dashboard filter) clears the hamburger zone via margin
       collapse — while letting the centered h1 itself stay in the header
       band, sharing the page-top zone with the corner hamburger. */
    h1 {
        margin-bottom: 30px;
    }
}

#dateSlider, #tabs {
    display: flex;
    justify-content: space-around;
    align-items: center;
    margin: 5px 0;
}

/* Row 2 of the header: Part filter (left) + Player dropdown pushed right
   for visual separation. */
#radioButtons {
    display: flex;
    justify-content: flex-start;
    align-items: center;
    flex-wrap: wrap;
    gap: 10px;
    margin: 5px 0;
    padding: 0 5px;
    box-sizing: border-box;
}

.player-filter {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    /* Take remaining horizontal space on the row, pushing the dropdown to
       the right edge. If the row wraps on a very narrow viewport, the
       Player widget lands on its own line at the right. */
    margin-left: auto;
}

.player-filter-label {
    color: var(--color-text-secondary);
    font-size: 13px;
}

/* Date range filter (segmented button group + custom date picker) */
.date-filter-container {
    display: flex;
    flex-wrap: nowrap;
    align-items: center;
    justify-content: flex-start;
    gap: 10px;
    width: 100%;
    overflow-x: auto;
    padding: 0 5px;
    box-sizing: border-box;
    -webkit-overflow-scrolling: touch;
}

.date-range-buttons {
    display: inline-flex;
    flex-shrink: 0;
    border: 1px solid var(--color-border-strong);
    border-radius: 6px;
    overflow: hidden;
    background: var(--color-bg-primary);
}

.date-range-btn {
    padding: 6px 14px;
    background: var(--color-bg-primary);
    border: none;
    border-right: 1px solid var(--color-border-strong);
    cursor: pointer;
    font: inherit;
    color: var(--color-text-primary);
    transition: background-color 0.15s, color 0.15s;
    -webkit-tap-highlight-color: transparent;
    white-space: nowrap;
}

.date-range-btn:last-child {
    border-right: none;
}

.date-range-btn:hover {
    background: var(--color-bg-hover);
}

.date-range-btn.active {
    background: var(--color-accent);
    color: var(--color-text-light);
}

.custom-date-range {
    display: flex;
    flex-wrap: nowrap;
    align-items: center;
    gap: 6px;
    flex-shrink: 0;
}

.custom-date-sep {
    color: var(--color-text-tertiary);
    font-size: 14px;
}

.custom-date-input {
    padding: 4px 6px;
    /* 16px prevents iOS from zooming on focus */
    font-size: 16px;
    border: 1px solid var(--color-border-strong);
    border-radius: 4px;
    background: var(--color-bg-primary);
    color: var(--color-text-primary);
    -webkit-appearance: none;
    appearance: none;
    box-sizing: border-box;
    /* Just enough room for YYYY-MM-DD on all platforms */
    min-width: 130px;
}

/* Part filter (V1 / V2 / VA / ANY) — same segmented-button look as the date
   range. Active state uses each part's color so the filter doubles as a
   colour legend for the part palette used throughout the app. */
.part-buttons {
    display: inline-flex;
    flex-shrink: 0;
    border: 1px solid var(--color-border-strong);
    border-radius: 6px;
    overflow: hidden;
    background: var(--color-bg-primary);
}

.part-btn {
    padding: 6px 14px;
    background: var(--color-bg-primary);
    border: none;
    border-right: 1px solid var(--color-border-strong);
    cursor: pointer;
    font: inherit;
    color: var(--color-text-primary);
    transition: background-color 0.15s, color 0.15s;
    -webkit-tap-highlight-color: transparent;
    white-space: nowrap;
}

.part-btn:last-child {
    border-right: none;
}

.part-btn:hover {
    background: var(--color-bg-hover);
}

.part-btn.active {
    color: var(--color-text-light);
}

/* Active backgrounds — part colors. JS reads these same tokens via
   getComputedStyle so the SPA + CSS stay in sync. */
.part-btn.active[data-part="V1"] {
    background: var(--color-part-v1);
    color: var(--color-part-v1-text);
}

.part-btn.active[data-part="V2"] {
    background: var(--color-part-v2);
    color: var(--color-part-v2-text);
}

.part-btn.active[data-part="VA"] {
    background: var(--color-part-va);
    color: var(--color-part-va-text);
}

.part-btn.active[data-part="ANY"] {
    background: var(--color-accent);
}

@media only screen and (max-width: 800px) {
    .date-filter-container {
        gap: 6px;
    }

    .date-range-btn,
    .part-btn {
        padding: 7px 10px;
    }

    .custom-date-input {
        padding: 3px 4px;
        min-width: 120px;
    }
}

#tabs {
    overflow-x: auto;
    white-space: nowrap;
    -webkit-overflow-scrolling: touch; /* Smooth scrolling on touch devices */
    justify-content: flex-start; /* Aligns tabs to the start */
    max-width: 100%; /* Adjust as necessary */
}

#tabs button {
    display: inline-block; /* Inline-block for horizontal layout */
    background-color: var(--color-bg-hover);
    color: var(--color-text-primary); /* explicit — browser default doesn't inherit body color */
    border: 1px solid var(--color-border-strong);
    border-bottom: none;
    padding: 5px 10px;
    margin-right: 2px;
    cursor: pointer;
    transition: background-color 0.3s;
    flex-shrink: 0;
}

#tabs button:hover {
    background-color: var(--gray-5);
}

#tabs button.active-tab-button {
    background-color: var(--color-bg-primary);
    border-top: 2px solid var(--color-accent); /* Highlight color for active tab */
}

.tab {
    border: 1px solid var(--color-border-strong);
    padding: 10px;
}

/* Per-composer data table (rendered by tableComponent.js). */
.data-table th {
    border: 1px solid var(--color-border-standard);
    background-color: var(--color-bg-secondary);
}

.data-table tr {
    border: 1px solid var(--color-border-standard);
}

.data-table tbody tr:nth-child(even) {
    background-color: var(--color-bg-stripe);
}

.data-table td {
    border: 1px solid var(--color-border-standard);
}

.tab, .tooltip {
    display: none;
}

.active-tab {
    display: block;
}

.random-button-container {
    margin: 10px 0;
    display: flex;
    align-items: center;
}

.random-button {
    padding: 5px 10px;
    cursor: pointer;
}

.random-work-display {
    margin-left: 10px;
    color: var(--color-text-tertiary);
}

.tooltip {
    position: absolute;
    background: var(--color-bg-primary);
    border: 1px solid var(--color-text-emphasis);
    padding: 5px;
    border-radius: 5px;
    /* Above the network's fullscreen overlay (z-index 1000) so tooltips
       stay visible when interacting with the lightbox. */
    z-index: 1001;
}

.tooltip h4 {
    margin: 0;
}

.tooltip p {
    margin: 5px 0 0 0;
}

.calendar-tooltip-table {
    margin-top: 10px;
    border-collapse: collapse;
    font-size: 12px;
    width: 100%;
}

.calendar-tooltip-table th {
    background: var(--color-bg-secondary);
    padding: 4px 6px;
    text-align: left;
    border-bottom: 1px solid var(--color-border-strong);
    font-weight: bold;
}

.calendar-tooltip-table td {
    padding: 4px 6px;
    border-bottom: 1px solid var(--color-border-subtle);
}

.calendar-tooltip-table tbody tr:last-child td {
    border-bottom: none;
}

.tooltip-close {
    position: absolute;
    top: 2px;
    right: 5px;
    font-size: 20px;
    font-weight: bold;
    color: var(--color-text-chart);
    cursor: pointer;
    line-height: 1;
    user-select: none;
}

.tooltip-close:hover {
    color: var(--color-text-emphasis);
}

.calendar-top {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-start;
    gap: 20px;
    max-width: 1000px;
    margin: 0 auto 6px;
}

/* Center the calendar grid SVG under the "Quartet Days" title.
   `> svg` is intentional — the legend SVG lives inside .calendar-top and
   shouldn't be re-centered here. */
#calendar > svg {
    display: block;
    margin: 0 auto;
}

/* Calendar SVG text defaults. The d3-created text elements (year labels,
   day-of-week, per-year stats, month labels, legend ticks) don't set an
   explicit fill, so they'd render black-on-bg. Default them to primary text,
   and set `color` so any `currentColor` references (legend title) resolve
   the same way. Text elements with explicit fill (chart annotations using
   --color-text-chart) keep their attribute. */
#calendar svg {
    color: var(--color-text-primary);
}

#calendar svg text:not([fill]) {
    fill: var(--color-text-primary);
}

/* Legend SVG sized + positioned to track the calendar's first 10 day cells.
   The calendar SVG has natural width 1000 (viewBox) and max-width: 100%, so
   its 10 cells render at min(container * 17%, 170px) and start at
   min(container * 4.05%, 40.5px). Mirror that here. */
.calendar-top > svg {
    width: min(170px, 17%);
    margin-left: min(40.5px, 4.05%);
    height: auto;
    flex-shrink: 0;
}

.recent-stats {
    font: 10px sans-serif;
    flex: 1;
    min-width: 0;
}

.recent-stats h4 {
    /* +3px nudges the heading baseline to match the legend title's baseline. */
    margin: 3px 0 4px 0;
    font-size: 11px;
    font-weight: bold;
}

.recent-stats-row {
    display: flex;
    flex-wrap: wrap;
    gap: 16px;
}

.recent-stat {
    display: inline-flex;
    gap: 4px;
}

.recent-stat-label {
    color: var(--color-text-secondary);
}

.recent-stat-value {
    font-weight: bold;
}

/* ALL tab: aggregate stats over the current Date/Part/Player filter. */
.all-stats {
    font: 12px sans-serif;
    margin: 8px 0;
}

.all-stats-row {
    display: flex;
    flex-wrap: wrap;
    gap: 18px;
}

.all-stat {
    display: inline-flex;
    gap: 4px;
}

.all-stat-label {
    color: var(--color-text-secondary);
}

.all-stat-value {
    font-weight: bold;
}

/* ---------- Dashboard view ---------- */

#dashboard {
    max-width: 1000px;
    margin: 0 auto;
    padding: 0 10px;
    box-sizing: border-box;
}

#dashboardDateFilter {
    margin: 10px 0;
}

.dashboard-charts {
    display: flex;
    flex-direction: column;
    gap: 18px;
    margin-top: 10px;
}

.dashboard-section {
    width: 100%;
}

.dashboard-chart-title {
    font: bold 13px sans-serif;
    color: var(--color-text-primary);
    margin: 0 0 6px 0;
}

.dashboard-empty {
    font: 12px sans-serif;
    color: var(--color-text-tertiary);
    margin: 6px 0;
}

/* SVGs inside the dashboard share the same font as the rest of the app */
#dashboardPartBar svg,
#dashboardComposerChart svg,
#dashboardMusicianChart svg,
#dashboardMusicianNetworkGraph svg,
#dashboardMusicianNetworkMatrix svg,
#dashboardMusicianNetworkChord svg {
    font-family: sans-serif;
}

/* Reserve room for the full TOP_N rows so filter toggles (and the empty
   "no data" state) don't change the chart container's height — otherwise
   the network section below would scroll up/down with every filter click.
   Keep in sync with TOP_N=20 + rankedRowHeight in src/dashboardComponent.js. */
#dashboardComposerChart,
#dashboardMusicianChart {
    min-height: 456px; /* desktop: 20 * 22 + 8 + 8 */
}
@media (max-width: 599px) {
    #dashboardComposerChart,
    #dashboardMusicianChart {
        min-height: 652px; /* mobile: 20 * 32 + 6 + 6 */
    }
}

/* Pull-to-refresh indicator. Built by src/pullToRefresh.js and only
   appended to the DOM when running as an installed PWA (standalone
   display mode). The spinner sits just below the top safe-area inset,
   translated up by its own height by default; --ptr-y (set in JS during
   a pull) translates it back down so it appears to be pulled out of the
   top of the screen. transitions are disabled via .pulling so it tracks
   the finger 1:1, then re-enabled for the snap-to-refresh / release. */
#ptr-indicator {
    position: fixed;
    top: env(safe-area-inset-top, 0);
    left: 50%;
    width: 36px;
    height: 36px;
    margin-left: -18px;
    border-radius: 50%;
    background: var(--color-bg-primary);
    box-shadow: 0 2px 6px rgba(0, 0, 0, 0.2);
    display: flex;
    align-items: center;
    justify-content: center;
    z-index: 2000;
    pointer-events: none;
    transform: translateY(calc(-100% + var(--ptr-y, 0px)));
    opacity: var(--ptr-opacity, 0);
    transition: transform 0.2s ease-out, opacity 0.2s ease-out;
}
#ptr-indicator.pulling {
    transition: none;
}
#ptr-indicator .ptr-spinner {
    width: 18px;
    height: 18px;
    border: 2px solid var(--color-border-strong);
    border-top-color: var(--color-accent);
    border-radius: 50%;
}
#ptr-indicator.refreshing .ptr-spinner {
    animation: ptr-spin 0.8s linear infinite;
}
@keyframes ptr-spin {
    to { transform: rotate(360deg); }
}

/* Reserve room for the tallest network view (chord on desktop, graph on
   mobile) so switching tabs doesn't shift the page. Inactive views are
   display:none so they don't double-count. Keep in sync with sizing() in
   src/musicianNetworkComponent.js (graphHeight, chordDiameter). */
.network-view {
    min-height: 500px; /* desktop: max(graphHeight 460, chordDiameter 500) */
}
@media (max-width: 599px) {
    .network-view {
        min-height: 380px; /* mobile: max(graphHeight 380, chordDiameter 340) */
    }
}

/* Musician network: tab toggle + min-sessions slider + view containers. */
.network-controls {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 14px;
    margin-bottom: 8px;
    /* Match the visualization's MAX_DESIGN_WIDTH from
       musicianNetworkComponent.js so the right-aligned fullscreen button
       lines up with the graph view's right edge instead of the section's. */
    max-width: 720px;
}

#dashboardMusicianNetwork.fullscreen .network-controls {
    max-width: none;
}

.network-threshold {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    font: 12px sans-serif;
    color: var(--color-text-secondary);
}

.network-threshold-value {
    color: var(--color-text-primary);
    font-weight: 600;
    min-width: 1.5em;
    display: inline-block;
}

.network-threshold input[type="range"] {
    width: 140px;
    accent-color: var(--color-accent);
}

.network-fullscreen-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    background: var(--color-bg-primary);
    border: 1px solid var(--color-border-strong);
    border-radius: 6px;
    cursor: pointer;
    padding: 5px 9px;
    color: var(--color-text-primary);
    /* Push to the right edge of the flex controls row so it sits opposite
       the tabs/slider instead of immediately to the right of them. */
    margin-left: auto;
    -webkit-tap-highlight-color: transparent;
}

.network-fullscreen-btn:hover {
    background: var(--color-bg-hover);
}

.network-fullscreen-btn svg {
    pointer-events: none;
}

/* Mobile breakpoint mirrors MOBILE_BREAKPOINT in musicianNetworkComponent.js.
   A lightbox on phones isn't useful when the viewport is already small. */
@media only screen and (max-width: 599px) {
    .network-fullscreen-btn { display: none; }
}

/* Lightbox mode: the section fills the viewport. The tabs / slider / caption
   stay in place; the active view's SVG grows because render() re-measures
   the container and re-runs the force layout at the new size. */
#dashboardMusicianNetwork.fullscreen {
    position: fixed;
    top: 0;
    left: 0;
    width: 100vw;
    height: 100vh;
    z-index: 1000;
    background: var(--color-bg-primary);
    /* 20px design padding, expanded on standalone iOS to clear the status bar
       / home indicator / landscape notches. max() picks whichever is larger
       per side so notched devices don't lose visual breathing room. */
    padding-top: max(20px, env(safe-area-inset-top));
    padding-right: max(20px, env(safe-area-inset-right));
    padding-bottom: max(20px, env(safe-area-inset-bottom));
    padding-left: max(20px, env(safe-area-inset-left));
    overflow: auto;
    box-sizing: border-box;
}

.network-tabs {
    display: inline-flex;
    border: 1px solid var(--color-border-strong);
    border-radius: 6px;
    overflow: hidden;
    background: var(--color-bg-primary);
}

.network-tab-btn {
    padding: 6px 14px;
    background: var(--color-bg-primary);
    border: none;
    border-right: 1px solid var(--color-border-strong);
    cursor: pointer;
    font: inherit;
    color: var(--color-text-primary);
    transition: background-color 0.15s, color 0.15s;
    -webkit-tap-highlight-color: transparent;
}

.network-tab-btn:last-child {
    border-right: none;
}

.network-tab-btn:hover {
    background: var(--color-bg-hover);
}

.network-tab-btn.active {
    background: var(--color-accent);
    color: var(--color-text-light);
}

.network-view svg .network-node,
.network-view svg .network-edge,
.network-view svg .matrix-cell,
.network-view svg .matrix-label,
.network-view svg .network-arc,
.network-view svg .network-chord {
    cursor: pointer;
}

/* Chord arc labels match the graph-node label halo so they stay legible
   over ribbons in both themes. */
.network-view svg .network-arc-label {
    fill: var(--color-text-primary);
    pointer-events: none;
    paint-order: stroke;
    stroke: var(--color-bg-primary);
    stroke-width: 3px;
    stroke-linejoin: round;
}

/* Halo around graph labels so they stay legible over edges in both themes
   without any JS color reads. */
.network-view svg .network-label {
    fill: var(--color-text-primary);
    pointer-events: none;
    paint-order: stroke;
    stroke: var(--color-bg-primary);
    stroke-width: 3px;
    stroke-linejoin: round;
}

/* Matrix can exceed container width on narrow viewports; let it scroll
   horizontally rather than shrinking node-set parity with the graph. */
#dashboardMusicianNetworkMatrix {
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
}

.network-caption {
    font: 11px sans-serif;
    color: var(--color-text-tertiary);
    margin: 6px 0 0 0;
    flex: 1;
}

/* Caption sits to the left, "Names" toggle on the right. Same max-width
   as the controls row so it lines up with the visualization's right edge. */
.network-caption-row {
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 12px;
    max-width: 720px;
}

#dashboardMusicianNetwork.fullscreen .network-caption-row {
    max-width: none;
}

.network-show-names {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    font: 11px sans-serif;
    color: var(--color-text-secondary);
    cursor: pointer;
    -webkit-tap-highlight-color: transparent;
    white-space: nowrap;
}

.network-show-names input[type="checkbox"] {
    margin: 0;
    accent-color: var(--color-accent);
    cursor: pointer;
}

.work-row {
    display: grid;
    grid-template-columns: auto 1fr;
    align-items: center;
    /*
    margin-bottom: 5px;
    gap: 10px;
    */
}

/* MISC tab needs wider first column for composer-prefixed titles */
#MISC .work-row {
    grid-template-columns: 130px 1fr;
}

.work-label-container {
    margin-left: 10px;
    min-width: 50px;
    white-space: nowrap;
}

.squares-container {
    display: flex;
    align-items: center;
    overflow-x: auto; /* In case of many squares */
}

.play-square {
    width: 10px;
    height: 10px;
    margin-right: 2px;
}

@media only screen and (max-width: 800px) { /* iPhone screen size (2x pixels)*/
    /* Adjust styles for smaller screens */
    body {
        font-size: 12px;
    }
    .tab {
        font-size: 12px;
    }
}

/* Player multiselect dropdown */
.player-multiselect {
    position: relative;
    display: inline-block;
}

.player-select-trigger {
    padding: 4px 8px;
    border: 1px solid var(--color-border-strong);
    background: var(--color-bg-primary);
    cursor: pointer;
    min-width: 100px;
    user-select: none;
}

.player-select-trigger:hover {
    background: var(--color-bg-stripe);
}

.player-dropdown {
    display: none;
    position: absolute;
    top: 100%;
    left: 0;
    background: var(--color-bg-primary);
    border: 1px solid var(--color-border-strong);
    max-height: 300px;
    overflow-y: auto;
    z-index: 100;
    min-width: 150px;
    box-shadow: 0 2px 4px var(--color-shadow-light);
}

.player-dropdown.open {
    display: block;
}

.player-option {
    padding: 4px 8px;
    cursor: pointer;
    display: flex;
    align-items: center;
    gap: 6px;
    white-space: nowrap;
}

.player-option:hover {
    background: var(--color-bg-hover);
}

.player-option label {
    cursor: pointer;
    flex: 1;
}

.player-clear {
    padding: 4px 8px;
    border-bottom: 1px solid var(--color-border-strong);
    color: var(--color-text-secondary);
    cursor: pointer;
    font-size: 0.9em;
}

.player-clear:hover {
    background: var(--color-bg-hover);
}

/* Setup view styles */
#setupView {
    display: flex;
    justify-content: center;
    align-items: center;
    min-height: 100vh;
    padding: 20px;
    box-sizing: border-box;
}

.setup-container {
    max-width: 600px;
    width: 100%;
    text-align: center;
}

.setup-container h1 {
    margin-bottom: 10px;
}

.setup-container p {
    color: var(--color-text-secondary);
    margin-bottom: 20px;
}

#setupForm {
    display: flex;
    flex-direction: column;
    gap: 15px;
}

#dataUrlInput {
    width: 100%;
    padding: 12px;
    font-size: 14px;
    border: 1px solid var(--color-border-strong);
    border-radius: 4px;
    box-sizing: border-box;
}

#dataUrlInput:focus {
    outline: none;
    border-color: var(--color-accent);
    box-shadow: 0 0 0 2px var(--color-accent-focus-ring);
}

#setupForm button {
    padding: 12px 24px;
    font-size: 16px;
    background-color: var(--color-accent);
    color: var(--color-text-light);
    border: none;
    border-radius: 4px;
    cursor: pointer;
    transition: background-color 0.2s;
}

#setupForm button:hover {
    background-color: var(--color-accent-hover);
}

/* Secondary action inside the setup form — outlined, lower visual weight
   than the primary "Load Data" CTA. Used by the Copy-mobile-setup-link
   button. */
#setupForm button.setup-secondary {
    background-color: transparent;
    color: var(--color-accent);
    border: 1px solid var(--color-accent);
}

#setupForm button.setup-secondary:hover {
    background-color: var(--color-bg-hover);
}

.setup-error {
    color: var(--color-text-error);
    font-size: 14px;
    text-align: left;
    display: none;
}

.setup-help {
    margin-top: 20px;
    font-size: 14px;
}

.setup-help a {
    color: var(--color-accent);
    text-decoration: none;
}

.setup-help a:hover {
    text-decoration: underline;
}
