/* Barrel Roll Time Tracker - admin styles.
   Self-contained: no web fonts, no CDN, nothing the CSP would block. */

:root {
    --bg: #f4f6f8;
    --surface: #ffffff;
    --border: #dfe4ea;
    --text: #1f2933;
    --muted: #6b7683;
    --accent: #1f6feb;
    --accent-dark: #1a5fd0;
    --good: #1a7f4b;
    --good-bg: #e8f6ee;
    --warn: #9a6300;
    --warn-bg: #fdf3e0;
    --bad: #b3261e;
    --bad-bg: #fdecea;
    --radius: 6px;
    /* The one gutter. Cards, card headers, table edge columns and any prose
       inside them all inset by this, so every panel has a single left edge to
       read down. */
    --card-pad: 20px;
}

* { box-sizing: border-box; }

body {
    margin: 0;
    background: var(--bg);
    color: var(--text);
    font: 15px/1.5 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
}

a { color: var(--accent); }
a:hover { color: var(--accent-dark); }

/* ---------- chrome ---------- */

.topbar {
    background: #16202b;
    color: #e8edf2;
    /* Stays put on scroll. The import review and the ledger are both long
       enough that losing the nav means scrolling back up to leave the page. */
    position: sticky;
    top: 0;
    z-index: 20;
}

/* Above the topbar's z-index so the action bar is not covered by it, and
   below nothing else — these are the only two fixed layers in the app. */
.sticky-actions { z-index: 10; }

.topbar-inner {
    max-width: 1280px;
    margin: 0 auto;
    padding: 0 20px;
    display: flex;
    align-items: center;
    gap: 24px;
    min-height: 54px;
    flex-wrap: wrap;
}

.brand {
    font-weight: 600;
    color: #fff;
    text-decoration: none;
    white-space: nowrap;
}

.nav { display: flex; gap: 18px; flex: 1; flex-wrap: wrap; }
.nav a { color: #b9c4d0; text-decoration: none; padding: 4px 0; }
.nav a:hover { color: #fff; }

.topbar-user { display: flex; align-items: center; gap: 12px; }
/* A link to your own account, styled as the label it replaced so the header
   does not suddenly grow a blue underline. */
.who {
    color: #b9c4d0;
    font-size: 13px;
    text-decoration: none;
    display: inline-flex;
    align-items: center;
    gap: 8px;
}
.who:hover { color: #fff; text-decoration: none; }

.role {
    display: inline-block;
    margin-left: 6px;
    padding: 1px 7px;
    border-radius: 10px;
    font-size: 11px;
    text-transform: uppercase;
    letter-spacing: .04em;
}
.role-admin { background: #2b4a6f; color: #cfe2ff; }
.role-tech { background: #3b3f45; color: #cfd4da; }

.page { max-width: 1280px; margin: 0 auto; padding: 24px 20px 64px; }

.readonly-footer {
    max-width: 1280px;
    margin: 0 auto 32px;
    padding: 0 20px;
    color: var(--muted);
    font-size: 13px;
}

/* ---------- blocks ---------- */

h1 { font-size: 22px; margin: 0 0 4px; }
h2 { font-size: 17px; margin: 0 0 12px; }
h3 { font-size: 15px; margin: 0 0 8px; }

.lede { color: var(--muted); margin: 0 0 24px; }

.card {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: var(--card-pad);
    margin-bottom: 20px;
}

.card-tight { padding: 0; overflow: hidden; }
.card-header {
    padding: 16px var(--card-pad);
    border-bottom: 1px solid var(--border);
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 16px;
    flex-wrap: wrap;
}

/*
 * A .card-tight gives up the card's padding so a table can meet its edges.
 * Anything in it that is NOT a table then has to bring the gutter back, or it
 * renders hard against the border -- which is how several explanatory
 * paragraphs came to sit a full 20px left of the heading above them.
 *
 * Forms are listed because the audit screens wrap their tables in one, so the
 * prose beside those tables is a grandchild rather than a child.
 */
.card-tight > p,
.card-tight > ul,
.card-tight > ol,
.card-tight > h3,
.card-tight > .card-note,
.card-tight > form > p,
.card-tight > form > ul,
.card-tight > form > .card-note {
    padding-left: var(--card-pad);
    padding-right: var(--card-pad);
}

/* A block of ordinary content inside a tight card: same gutter as everything
   else in it, rather than each screen picking its own inset. */
.card-body { padding: 16px var(--card-pad); }

/* Note beneath a table: same gutter, and clear of the last row. */
.card-note { margin: 14px 0; }
.card > .card-note:last-child,
.card-tight > .card-note:last-child { margin-bottom: var(--card-pad); }

.grid { display: grid; gap: 20px; }
.grid-2 { grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); }
.grid-3 { grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); }
/* Four stats across on a wide screen, wrapping to two on a narrow one. The
   smaller minimum is what lets four fit before it wraps. */
.grid-4 { grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); }

/* ---------- stats ---------- */

.stat { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius); padding: 16px 18px; }
.stat-label { color: var(--muted); font-size: 12px; text-transform: uppercase; letter-spacing: .05em; }
.stat-value { font-size: 26px; font-weight: 600; margin-top: 4px; }
.stat-note { color: var(--muted); font-size: 13px; margin-top: 2px; }

/* The staged-but-not-applied dates, under the applied-through figure. Ruled
   off because it answers a different question from the line above it: one is
   where the ledger ends, the other is what is queued behind it. */
.stat-pending { margin-top: 8px; padding-top: 8px; border-top: 1px solid var(--border); }
.stat-pending strong { color: var(--text, inherit); font-weight: 600; }

.value-good { color: var(--good); }
.value-warn { color: var(--warn); }
.value-bad { color: var(--bad); }

/* ---------- tables ---------- */

table { width: 100%; border-collapse: collapse; }
th, td { text-align: left; padding: 10px 14px; border-bottom: 1px solid var(--border); vertical-align: top; }
th { font-size: 12px; text-transform: uppercase; letter-spacing: .04em; color: var(--muted); font-weight: 600; }

/* A sortable column heading. Inherits the heading's own colour so the table
   header still reads as a header rather than as a row of links. */
.th-sort { color: inherit; text-decoration: none; display: inline-flex; align-items: center; gap: 4px; white-space: nowrap; }
.th-sort:hover { color: var(--accent); text-decoration: none; }
.th-sort.is-sorted { color: var(--text); }
.th-arrow { font-size: 10px; line-height: 1; }
/* Reserve the arrow's width on every heading so sorting does not shift the
   column widths as the marker moves between them. */
.th-sort:not(.is-sorted) .th-arrow::before { content: "\25B4"; visibility: hidden; }
tbody tr:last-child td { border-bottom: none; }
tbody tr:hover { background: #fafbfc; }
td.num, th.num { text-align: right; font-variant-numeric: tabular-nums; }
.table-wrap { overflow-x: auto; }

/* The outer columns line up with the card's own gutter, so a heading and the
   column under it share one left edge instead of missing it by six pixels. */
.card-tight th:first-child,
.card-tight td:first-child { padding-left: var(--card-pad); }
.card-tight th:last-child,
.card-tight td:last-child { padding-right: var(--card-pad); }

.muted { color: var(--muted); }
.small { font-size: 13px; }
.nowrap { white-space: nowrap; }

/* ---------- forms ---------- */

label { display: block; font-size: 13px; font-weight: 600; margin-bottom: 4px; }
.field { margin-bottom: 14px; }
.field-help { color: var(--muted); font-size: 12px; margin-top: 4px; font-weight: 400; }

input[type=text], input[type=email], input[type=password], input[type=number],
input[type=date], select, textarea {
    width: 100%;
    padding: 8px 10px;
    border: 1px solid var(--border);
    border-radius: var(--radius);
    font: inherit;
    background: #fff;
    color: var(--text);
}

input:focus, select:focus, textarea:focus {
    outline: 2px solid var(--accent);
    outline-offset: -1px;
    border-color: var(--accent);
}

textarea { min-height: 72px; resize: vertical; }

/* The member-facing sentence. Tall enough to read a 25-word summary whole:
   a single-line input hides the end of it behind a cursor, which is no way to
   check a line that is about to go on somebody's bill. Grows as you type. */
.summary-box {
    min-height: 0;
    height: auto;
    field-sizing: content;
    line-height: 1.45;
}

input[type=file] {
    width: 100%;
    padding: 8px 10px;
    border: 1px dashed var(--border);
    border-radius: var(--radius);
    background: #fafbfc;
    font: inherit;
    cursor: pointer;
}
input[type=file]:hover { border-color: var(--accent); }

/* Generated SQL and config snippets: monospace, and obviously copyable. */
textarea[readonly] {
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-size: 12.5px;
    line-height: 1.55;
    background: #f7f8f9;
    color: #1f2933;
    cursor: text;
}
textarea[readonly]:focus { outline-color: var(--accent); }

code {
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-size: .92em;
    background: #f0f2f5;
    padding: 1px 5px;
    border-radius: 3px;
}

/* A server path meant to be read and copied, so it wraps rather than truncates. */
code.path {
    display: inline-block;
    padding: 7px 10px;
    word-break: break-all;
    user-select: all;
}

.row { display: flex; gap: 14px; flex-wrap: wrap; }
.row > * { flex: 1 1 180px; }

button, .button {
    display: inline-block;
    padding: 9px 16px;
    border: 1px solid transparent;
    border-radius: var(--radius);
    background: var(--accent);
    color: #fff;
    font: inherit;
    font-weight: 500;
    cursor: pointer;
    text-decoration: none;
}
button:hover, .button:hover { background: var(--accent-dark); color: #fff; }

.button-secondary { background: #fff; color: var(--text); border-color: var(--border); }
.button-secondary:hover { background: #f0f2f5; color: var(--text); }

.button-danger { background: var(--bad); }
.button-danger:hover { background: #8f1e17; }

.button-small { padding: 5px 11px; font-size: 13px; }

.link-button {
    background: none;
    border: none;
    color: #b9c4d0;
    cursor: pointer;
    padding: 0;
    font: inherit;
    text-decoration: underline;
}
.link-button:hover { background: none; color: #fff; }

.inline { display: inline; }
.actions { display: flex; gap: 10px; flex-wrap: wrap; align-items: center; margin-top: 8px; }

/* ---------- flashes and callouts ---------- */

.flash, .callout {
    padding: 12px 16px;
    border-radius: var(--radius);
    margin-bottom: 16px;
    border: 1px solid transparent;
}

.flash-success, .callout-success { background: var(--good-bg); border-color: #b5e0c8; color: #10502f; }
.flash-warning, .callout-warning { background: var(--warn-bg); border-color: #f0d9a8; color: #7a4e00; }
.flash-error,   .callout-error   { background: var(--bad-bg);  border-color: #f2c0bc; color: #8f1e17; }
.callout-info { background: #eaf2fd; border-color: #c2d9f7; color: #123f80; }

/* ---------- badges ---------- */

.badge {
    display: inline-block;
    padding: 2px 8px;
    border-radius: 10px;
    font-size: 11px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: .04em;
    white-space: nowrap;
}
.badge-monthly { background: #e7edff; color: #2a3f86; }
.badge-prepaid { background: #eaf5ec; color: #1d6b3e; }
.badge-draft { background: #fdf3e0; color: #7a4e00; }
.badge-applied { background: var(--good-bg); color: #10502f; }
.badge-discarded { background: #eef0f2; color: #5b636b; }
.badge-reversed { background: var(--bad-bg); color: #8f1e17; }
.badge-overage { background: var(--bad-bg); color: #8f1e17; }
.badge-inactive { background: #eef0f2; color: #5b636b; }
.badge-internal { background: #e7edff; color: #2a3f86; }

/* Hours on hold, beside a member's name. `cursor: help` is the affordance that
   tells somebody the title tooltip is there to be hovered. */
.hold-marker {
    display: inline-flex;
    align-items: center;
    gap: 3px;
    cursor: help;
    white-space: nowrap;
    font-size: 12px;
    vertical-align: baseline;
}
.hold-marker-hours { color: var(--warn); font-weight: 600; }
.badge-none { background: #eef0f2; color: #5b636b; }
.badge-advanced { background: #e7edff; color: #2a3f86; }
.badge-premium { background: #f0e7ff; color: #55308c; }

/* ---------- meters ---------- */

.meter { height: 7px; background: #e8ebee; border-radius: 4px; overflow: hidden; margin-top: 6px; }
.meter-fill { height: 100%; background: var(--accent); }
.meter-fill.is-low { background: var(--warn); }
.meter-fill.is-empty { background: var(--bad); }

/* ---------- reports ---------- */

/* A label and a control on one line, for the window pickers. Submits on
   change; the Go button ships visible and app.js removes it once the change
   handler is attached, so a script that never loads leaves a working form
   rather than a control that looks live and does nothing. */
.inline-form { display: flex; align-items: center; gap: 10px; }
.inline-form label { margin: 0; }
.inline-form select { width: auto; }
.inline-form.is-inline-cell { display: inline-flex; }

.btn-small { padding: 4px 10px; font-size: 0.85rem; }

/* A Toggl-audit difference somebody has reviewed and accepted. Shown only when
   asked for, and receding rather than hidden: it is still a real difference,
   just one that has been answered. */
tr.is-acknowledged { opacity: 0.62; }
tr.is-acknowledged td { background: var(--surface-muted, #f7f8fa); }

/* A submit button that reads as a link, for a single low-ceremony action
   sitting in a table cell. It still has to be a button: the action is a POST
   carrying a CSRF token, and an anchor could not be. */
.btn-link {
    background: none;
    border: 0;
    padding: 0;
    color: var(--link, #2563eb);
    font: inherit;
    text-decoration: underline;
    cursor: pointer;
}
.btn-link:hover { color: var(--bad); }

/* A row that names the group the rows beneath it belong to. */
.group-row td { background: #f5f7f9; border-top: 1px solid var(--border); }

/* ---------- import audit ---------- */

.client-group { border: 1px solid var(--border); border-radius: var(--radius); background: var(--surface); margin-bottom: 18px; }
.client-group.is-unmapped { border-color: #f0c9a8; }
/* Our own time. Distinct from unmapped on purpose -- one is a job to do before
   applying, the other is a thing to glance over. */
.client-group.is-internal { border-color: #c9d4ea; background: #fbfcfe; }

/* One gutter down the whole import screen -- the group heading, the day
   heading, the summary box and the first column of every entry all share the
   card inset, so the eye reads a single left edge from top to bottom. */
.client-group-head {
    padding: 14px var(--card-pad);
    border-bottom: 1px solid var(--border);
    display: flex;
    justify-content: space-between;
    gap: 16px;
    align-items: center;
    flex-wrap: wrap;
}

.day-block { border-bottom: 1px solid #eef1f4; }
.day-block:last-child { border-bottom: none; }

.day-head {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    gap: 16px;
    padding: 10px var(--card-pad);
    background: #fafbfc;
    font-size: 13px;
    flex-wrap: wrap;
}

/* The member-facing sentence and its label, inset like everything else -- it
   had no padding of its own at all and sat hard against the border. */
.day-summary { padding: 10px var(--card-pad) 12px; }
.day-summary .stat-label { display: block; margin-bottom: 4px; }

.day-charge { font-weight: 600; }

/* A day that rounds to nothing. Deliberately not alarming -- it is the rule
   working, not a problem, and the entries below stay ticked to bill. */
.day-charge.is-free { font-weight: 500; font-style: italic; }
.entry-row td { padding: 8px 14px; }
.entry-row td:first-child { padding-left: var(--card-pad); }
.entry-row td:last-child { padding-right: var(--card-pad); }
.entry-row.is-excluded { opacity: .55; }
.entry-row.is-excluded .entry-desc { text-decoration: line-through; }

/* Yellow means "not settled yet".
   `is-generated` -- a summary Claude wrote that nobody has read and approved.
   `is-unsaved`   -- something typed on this page that is not in the database.
   Both are things an operator should notice before applying, and both stop
   being yellow the moment they are dealt with. */
.is-generated,
.is-unsaved {
    background: #fffbe6;
    border-color: #e6d48a;
}
.is-unsaved:focus { background: #fffdf2; }

.unsaved-flag {
    display: none;
    color: var(--warn);
    font-weight: 600;
}
.has-unsaved .unsaved-flag { display: inline; }

.check { display: flex; align-items: center; gap: 8px; font-weight: 400; margin: 0; }
.check input { width: auto; }

.sticky-actions {
    position: sticky;
    bottom: 0;
    background: var(--surface);
    border-top: 1px solid var(--border);
    padding: 14px var(--card-pad);
    display: flex;
    gap: 12px;
    align-items: center;
    flex-wrap: wrap;
    box-shadow: 0 -2px 8px rgba(0,0,0,.05);
}

/* ---------- login ---------- */

.login-wrap { max-width: 380px; margin: 12vh auto; }
.login-wrap h1 { text-align: center; margin-bottom: 20px; }

/* ---------- misc ---------- */

.empty { padding: 32px 20px; text-align: center; color: var(--muted); }
.pagination { display: flex; gap: 12px; align-items: center; margin-top: 16px; }
details.detail-json summary { cursor: pointer; color: var(--muted); font-size: 13px; }
details.detail-json pre {
    background: #f7f8f9;
    padding: 10px;
    border-radius: var(--radius);
    overflow-x: auto;
    font-size: 12px;
    margin: 8px 0 0;
}
