:root {
    --color-primary: #000080;
    --color-primary-dark: #00004d;
    --color-primary-soft: #e6e6f2;
    --color-ink: #212529;
    --color-muted: #6c757d;
    --color-border: #e9ecef;
    --color-surface: #ffffff;
    --color-bg: #f6f7f9;
    --color-success: #2b8a3e;
    --color-success-soft: #ebfbee;
    --color-danger: #c1121f;
    --color-danger-soft: #fdecee;
    --color-pending: #495057;
    --color-pending-soft: #f1f3f5;

    --radius-sm: 6px;
    --radius: 10px;
    --shadow: 0 1px 4px rgba(20, 20, 30, 0.08);
    --shadow-lg: 0 8px 30px rgba(20, 20, 30, 0.12);

    --font-sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
}

* {
    box-sizing: border-box;
}

body {
    margin: 0;
    background: var(--color-bg);
    color: var(--color-ink);
    font-family: var(--font-sans);
    font-size: 15px;
    line-height: 1.5;
    -webkit-font-smoothing: antialiased;
}

h1, h2, h3 {
    line-height: 1.25;
    font-weight: 600;
    margin: 0 0 12px;
}

h1 { font-size: 26px; }
h2 { font-size: 18px; margin-top: 32px; }

a {
    color: var(--color-primary);
    text-decoration: none;
}

a:hover {
    text-decoration: underline;
}

.page {
    max-width: 1240px;
    margin: 0 auto;
    padding: 20px 20px 60px;
}

/* Filter sidebar layout - list/import-log pages */
.layout {
    display: flex;
    align-items: flex-start;
    gap: 20px;
}

.layout .content {
    flex: 1;
    min-width: 0;
}

.filters {
    width: 240px;
    flex-shrink: 0;
    background: var(--color-surface);
    border-radius: var(--radius);
    box-shadow: var(--shadow);
    padding: 18px;
}

.filters h2 {
    margin: 0 0 14px;
    font-size: 14px;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--color-muted);
}

/* General form field styling - originally scoped to .filters only (the workout/import-log list
   pages' sidebar), promoted to apply everywhere so every label/input/select/textarea in the app gets
   the same treatment without each page needing its own copy or its own opt-in. Genuinely global, not
   scoped to `form` - a `form`-ancestor requirement missed any field associated to its form via the
   HTML5 `form="..."` attribute instead of DOM nesting (e.g. series_items.html.twig's reorder-position
   inputs, which live in a <td> rather than inside the <form> they submit to - CSS's `form input`
   selector only understands DOM nesting, not the `form=` attribute), which is exactly backwards from
   the point of a *general* style: every text-like field should get it by default, not by remembering
   to nest it correctly. Checkbox/radio/file/hidden input types are excluded explicitly since "bordered,
   full-width, 13px" is a text-field treatment that would look broken applied to them (a hidden input
   doesn't render at all regardless, but the other three do). The map page's layer-toggle checkboxes
   (assets/controllers/map_controller.js's addBoundaryLayerCheckbox()) are the one place a bare `label`
   rule would reach that isn't a form field - `.layer-control label` below opts back out for that
   specific, deliberately different (compact, native-checkbox) context, the same targeted-override
   pattern `.row-action select` already uses for its own exception. */
label {
    display: block;
    font-size: 13px;
    font-weight: 600;
    color: var(--color-muted);
    margin-bottom: 4px;
}

input:not([type="hidden"]):not([type="file"]):not([type="checkbox"]):not([type="radio"]),
select,
textarea {
    width: 100%;
    padding: 8px 10px;
    border: 1px solid var(--color-border);
    border-radius: var(--radius-sm);
    background: var(--color-bg);
    font: inherit;
    font-size: 13px;
    color: var(--color-ink);
}

.field {
    margin-bottom: 14px;
}

.field-row {
    display: flex;
    gap: 8px;
}

.field-row .field {
    flex: 1;
    min-width: 0;
}

.filters .btn {
    width: 100%;
    justify-content: center;
    margin-top: 4px;
}

.filters .clear-filters {
    display: block;
    text-align: center;
    margin-top: 10px;
    font-size: 13px;
}

@media (max-width: 780px) {
    .layout { flex-direction: column; }
    .filters { width: 100%; }
}

/* Top nav - the same white/shadow/rounded/14px treatment as the map page's
   floating nav, just docked in normal document flow instead of positioned
   absolutely over a full-bleed canvas. */
.topbar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 16px;
    background: var(--color-surface);
    border-radius: var(--radius);
    box-shadow: var(--shadow);
    padding: 10px 18px;
    margin-bottom: 24px;
    font-size: 14px;
    flex-wrap: wrap;
}

.topbar-brand {
    display: flex;
    align-items: center;
    gap: 8px;
    font-weight: 700;
    font-size: 15px;
    color: var(--color-ink);
    letter-spacing: -0.01em;
}

.topbar-brand img {
    display: block;
    width: 28px;
    height: 28px;
    border-radius: 6px;
}

.topbar-brand:hover {
    text-decoration: none;
}

.topbar-links {
    display: flex;
    align-items: center;
    gap: 4px;
    flex-wrap: wrap;
}

.topbar-links a,
.topbar-links button {
    color: var(--color-muted);
    padding: 6px 10px;
    border-radius: var(--radius-sm);
    /* The logout link is a <button> now (see _nav.html.twig) - reset its
       default chrome so it matches the plain <a> links beside it. */
    font: inherit;
    background: none;
    border: none;
    cursor: pointer;
}

.topbar-links a:hover,
.topbar-links button:hover {
    background: var(--color-bg);
    color: var(--color-ink);
    text-decoration: none;
}

/* Deliberately no font-weight here (T20.8, docs/TASKS.md Epic 20) - it used to set font-weight: 600,
   which made the active link's rendered width differ from an inactive one, shifting every other link
   sideways in the flex row each time the active link changed. Removed rather than applied to every
   link instead, to keep every page's nav looking exactly as before except for the one previously-bold
   active link; color/background (the pill) alone now carries the active state. */
.topbar-links a.is-active {
    color: var(--color-primary);
    background: var(--color-primary-soft);
}

/* The logout <button> lives inside a <form> now (POST + CSRF, to stop
   browser prefetch from silently logging users out - see security.yaml) -
   display: contents makes the form itself invisible to flex layout, so the
   button lays out exactly like the sibling <a> links. */
.topbar-logout-form {
    display: contents;
}

/* Floating variant - used on the full-bleed map page, positioned over the
   canvas instead of sitting in normal document flow. */
.topbar--floating {
    position: absolute;
    top: 14px;
    right: 14px;
    z-index: 1000;
    margin-bottom: 0;
    width: auto;
}

/* The map's own layer-toggle checkboxes (map_controller.js's addBoundaryLayerCheckbox()) sit outside
   any <form> and pair a native checkbox with its label inline ("[x] Grid"), a compact control-panel
   look rather than a form field - opts back out of the general `label` rule's block/bold/muted-gray
   treatment above, the same targeted-exception pattern `.row-action select` uses for its own case.
   `map/index.html.twig` already declares its own `.layer-control label` (display: flex; gap: 6px;
   cursor: pointer; white-space: nowrap;) in a page-level <style> block - `display: flex` is restated
   here (not left to whichever rule happens to load later) so this override can't fight it regardless
   of load order; `gap`/`cursor`/`white-space` are deliberately left undeclared here so that page's own
   rule keeps controlling them either way. */
.layer-control label {
    display: flex;
    font-size: inherit;
    font-weight: normal;
    color: inherit;
    margin-bottom: 0;
}

.topbar-username {
    font-weight: 600;
    padding-right: 10px;
    margin-right: 2px;
    border-right: 1px solid var(--color-border);
}

/* A row of page-level call-to-action buttons/links - e.g. the workout list's "Fetch from Suunto" +
   "Upload a workout" pair. Deliberately a plain flex row, not a .card - these are actions, not
   content. `.btn` already renders consistently on both a <button> (a form submit, like the Suunto
   fetch) and an <a> (a plain navigation link, like Upload) - a page-actions row lets the two sit next
   to each other looking like matched buttons despite being different elements underneath. */
.page-actions {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 10px;
    margin-bottom: 20px;
}

/* Cards */
.card {
    background: var(--color-surface);
    border-radius: var(--radius);
    box-shadow: var(--shadow);
    padding: 20px;
    margin-bottom: 20px;
}

.card-title {
    margin: 0 0 4px;
}

.meta {
    color: var(--color-muted);
    font-size: 14px;
    margin: 0 0 16px;
}

/* T20.5 (docs/TASKS.md Epic 20) - .osm-credit sits right after a table/empty-state with no breathing
   room otherwise, since .meta's own margin is bottom-only. Wins over .meta's shorthand by appearing
   later in the cascade (same specificity, source order decides). */
.osm-credit {
    margin-top: 24px;
}

/* Buttons */
.btn {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    font: inherit;
    font-size: 14px;
    font-weight: 600;
    border: 1px solid transparent;
    border-radius: var(--radius-sm);
    padding: 9px 16px;
    cursor: pointer;
    background: var(--color-surface);
    color: var(--color-ink);
    border-color: var(--color-border);
}

.btn:hover {
    text-decoration: none;
    border-color: #d0d4d9;
}

.btn-primary {
    background: var(--color-primary);
    border-color: var(--color-primary);
    color: #fff;
}

.btn-primary:hover {
    background: var(--color-primary-dark);
    border-color: var(--color-primary-dark);
}

.btn-danger {
    background: var(--color-danger-soft);
    border-color: var(--color-danger);
    color: var(--color-danger);
}

.btn-danger:hover {
    background: var(--color-danger);
    color: #fff;
}

/* Definition table - key/value rows (workout detail) */
.detail-table {
    width: 100%;
    border-collapse: collapse;
}

.detail-table th, .detail-table td {
    text-align: left;
    padding: 10px 4px;
    border-bottom: 1px solid var(--color-border);
    font-weight: 400;
}

.detail-table tr:last-child th, .detail-table tr:last-child td {
    border-bottom: none;
}

.detail-table th {
    width: 140px;
    color: var(--color-muted);
    font-size: 13px;
    text-transform: uppercase;
    letter-spacing: 0.04em;
}

/* Data table (lists) */
.table-wrap {
    overflow-x: auto;
    background: var(--color-surface);
    border-radius: var(--radius);
    box-shadow: var(--shadow);
}

table.data {
    width: 100%;
    border-collapse: collapse;
    font-size: 14px;
}

table.data th, table.data td {
    text-align: left;
    padding: 12px 16px;
    border-bottom: 1px solid var(--color-border);
}

table.data th {
    color: var(--color-muted);
    font-weight: 600;
    font-size: 12px;
    text-transform: uppercase;
    letter-spacing: 0.04em;
}

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

table.data tbody tr:hover {
    background: var(--color-bg);
}

/* T20.6 (docs/TASKS.md Epic 20) - an action cell that sometimes holds a .btn (e.g. series/show's "View
   workouts") and sometimes nothing made its row noticeably taller than a row with no button, since
   table.data's row height is dictated by its tallest cell. Reserves the button's own rendered height
   whether or not a button is present. 41px, not the originally-estimated 37px - the first pass missed
   the 1.5 line-height on the button's 14px text (21px, not 14px), reported back as a real 4px
   discrepancy (66px vs 62px measured row heights) after the initial fix shipped. */
.row-action {
    display: flex;
    align-items: center;
    min-height: 41px;
}

/* boundary_overlaps.html.twig's inline status-change <select> sits inside a .row-action flex row next
   to its own submit button - the general `form select` rule above (width: 100%) would stretch it to
   fill the row and crowd the button out, so it's opted back out to its natural inline width here.
   The form itself becomes the flex row (not just .row-action, which only ever had the one <form> as
   its single flex item) with its own gap and wrap - without an explicit gap, the select and button
   sat right up against each other, and on a narrow column wrapped onto two lines with zero space
   between them (inline default wrapping has no vertical gap on its own). `gap` applies in both
   directions under flex-wrap, so this covers side-by-side and stacked alike. */
.row-action form {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 8px;
}

.row-action select {
    width: auto;
}

/* Status badges */
.badge {
    display: inline-block;
    padding: 3px 10px;
    border-radius: 999px;
    font-size: 12px;
    font-weight: 600;
    text-transform: capitalize;
    background: var(--color-pending-soft);
    color: var(--color-pending);
}

.badge-processed { background: var(--color-success-soft); color: var(--color-success); }
.badge-failed, .badge-invalid { background: var(--color-danger-soft); color: var(--color-danger); }
.badge-duplicate { background: #fff3bf; color: #966b00; }
.badge-pending { background: var(--color-pending-soft); color: var(--color-pending); }

/* Workout list modal - shared by the map page's cell/boundary click-through and the series item
   page's "view workouts" click-through (T20.7, docs/TASKS.md Epic 20). Originally only defined,
   scoped by id, in map/index.html.twig's own <style> block; pulled out here so a second page reusing
   the same modal concept doesn't end up with plain unstyled browser <dialog> chrome. */
.workout-modal {
    border: none;
    border-radius: var(--radius);
    padding: 20px 22px;
    box-shadow: var(--shadow-lg);
    font-family: var(--font-sans);
    min-width: 240px;
    max-width: 340px;
}

.workout-modal::backdrop { background: rgba(20, 20, 30, 0.35); }
.workout-modal h2 { margin: 0 0 12px; font-size: 16px; padding-right: 60px; }
.workout-modal ul { list-style: none; padding: 0; margin: 0; }
.workout-modal li { padding: 8px 0; border-bottom: 1px solid var(--color-border); font-size: 14px; }
.workout-modal li:last-child { border-bottom: none; }
.workout-modal .btn { position: absolute; top: 16px; right: 16px; padding: 5px 12px; font-size: 13px; }

/* Alerts / flash messages */
.alert {
    border-radius: var(--radius-sm);
    padding: 12px 16px;
    margin-bottom: 16px;
    font-size: 14px;
}

.alert-error {
    background: var(--color-danger-soft);
    color: var(--color-danger);
}

.alert-notice {
    background: var(--color-success-soft);
    color: var(--color-success);
}

/* Empty states */
.empty-state {
    background: var(--color-surface);
    border-radius: var(--radius);
    box-shadow: var(--shadow);
    padding: 40px 20px;
    text-align: center;
    color: var(--color-muted);
}

/* Pagination */
.pager {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 4px;
    margin-top: 16px;
    font-size: 14px;
}

.pager a, .pager-current {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 32px;
    height: 32px;
    padding: 0 8px;
    border-radius: var(--radius-sm);
}

.pager a {
    color: var(--color-ink);
}

.pager a:hover {
    background: var(--color-bg);
    text-decoration: none;
}

.pager-current {
    background: var(--color-primary);
    color: #fff;
    font-weight: 600;
}

/* Upload form */
.upload-form input[type="file"] {
    display: block;
    width: 100%;
    padding: 10px;
    border: 1px dashed var(--color-border);
    border-radius: var(--radius-sm);
    background: var(--color-bg);
    margin: 8px 0 16px;
    font: inherit;
    font-size: 13px;
}

.upload-form label {
    font-weight: 600;
    font-size: 14px;
}

/* Login page */
.auth-shell {
    min-height: 100vh;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 20px;
}

.auth-card {
    background: var(--color-surface);
    border-radius: var(--radius);
    box-shadow: var(--shadow-lg);
    padding: 40px 36px;
    max-width: 360px;
    width: 100%;
    text-align: center;
}

.auth-mark {
    width: 64px;
    height: 64px;
    margin: 0 auto 16px;
}

.auth-mark img {
    display: block;
    width: 100%;
    height: 100%;
    border-radius: 14px;
}

.auth-card h1 {
    font-size: 22px;
    margin-bottom: 4px;
}

.auth-card p.tagline {
    color: var(--color-muted);
    font-size: 14px;
    margin: 0 0 24px;
}

.auth-card .btn {
    width: 100%;
    justify-content: center;
    padding: 11px 16px;
}

@media (max-width: 600px) {
    .page { padding: 12px 12px 40px; }
    .topbar { border-radius: var(--radius-sm); }
    table.data th, table.data td { padding: 10px; }
}
