/* Invocing NG base styles */
*, *::before, *::after { box-sizing: border-box; }
/* The header gradient is shared: the wordmark knocks its text out of the same
   gradient so the header background shows through the letterforms. */
:root {
    /* A dark scrim over the gradient lifts contrast for the white nav text. */
    --header-gradient:
        linear-gradient(rgba(16, 26, 38, 0.35), rgba(16, 26, 38, 0.35)),
        linear-gradient(0deg, rgba(67, 94, 116, 0.5), rgba(67, 94, 116, 0)),
        linear-gradient(70deg, rgba(229, 125, 133, 1), rgba(229, 125, 133, 0)),
        linear-gradient(120deg, rgba(53, 158, 177, 0.2), rgba(53, 158, 177, 1));
}
body { font-family: "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, monospace; margin: 0; padding: 0; line-height: 1.6; }
header {
    /* The header uses Open Sans while the rest of the app is IBM Plex Mono. */
    font-family: "Open Sans", system-ui, sans-serif;
    background: var(--header-gradient);
    /* Fixed attachment so the wordmark's knocked-out letters sample the exact
       same gradient pixels as the header behind it. */
    background-attachment: fixed;
    color: #fff;
    padding: 1rem 2rem 0;
}
header nav a { color: #fff; text-decoration: none; margin-right: 1rem; font-weight: 600; }
/* Nav labels sit on a variable gradient, so a soft dark shadow keeps them legible
   over the lighter areas. */
header nav.nav-primary a:not(.brand) { text-shadow: 0 1px 2px rgba(0, 0, 0, 0.45); }
header nav.nav-primary { display: flex; align-items: center; flex-wrap: wrap; padding-bottom: 1.5rem; }
/* The brand link is the app wordmark, so it reads larger and heavier than the
   surrounding navigation links. */
header nav.nav-primary .brand {
    margin-right: 2rem;
    padding: 0.25rem 0.75rem;
    border-radius: 8px;
    /* A mostly opaque plate still lets the gradient tint through, but gives the
       knocked-out letters enough contrast to read. */
    background: rgba(255, 255, 255, 0.82);
    box-shadow:
        inset 0 1px 0 rgba(255, 255, 255, 0.9),
        inset 0 -1px 0 rgba(0, 0, 0, 0.08),
        0 1px 3px rgba(0, 0, 0, 0.2);
    font-family: "Open Sans", system-ui, sans-serif;
    font-size: 1.35rem;
    font-weight: 700;
    letter-spacing: -0.01em;
}
/* The letterforms are filled with a deepened version of the header gradient so
   they stay on-brand while reading clearly against the light plate. The white
   drop shadow underneath gives the embossed feel. */
header nav.nav-primary .brand .wordmark {
    background:
        linear-gradient(0deg, rgba(20, 32, 44, 0.45), rgba(20, 32, 44, 0.45)),
        var(--header-gradient);
    background-attachment: fixed;
    -webkit-background-clip: text;
    background-clip: text;
    color: transparent;
    -webkit-text-fill-color: transparent;
    filter: drop-shadow(0 1px 0 rgba(255, 255, 255, 0.8));
}
header nav.nav-primary .brand:hover { text-decoration: none; background: rgba(255, 255, 255, 0.95); }
/* The log out form is pushed to the far right of the primary nav. */
header nav.nav-primary form.logout { margin-left: auto; }
header nav.nav-primary a.active { text-decoration: underline; text-underline-offset: 4px; }
header nav.nav-tabs { display: flex; flex-wrap: wrap; gap: 0.25rem; }
/* Inactive tabs use a dark translucent fill rather than a light one, so the white
   label keeps contrast wherever the gradient is bright. */
header nav.nav-tabs a { margin-right: 0; padding: 0.5rem 1rem; border-radius: 6px 6px 0 0; background: rgba(16, 26, 38, 0.4); font-weight: 600; text-shadow: 0 1px 2px rgba(0, 0, 0, 0.45); }
header nav.nav-tabs a:hover { background: rgba(16, 26, 38, 0.55); }
header nav.nav-tabs a.active { background: #fff; color: #16222f; font-weight: 700; text-shadow: none; }
main { max-width: 960px; margin: 2rem auto; padding: 0 1rem; }
label { display: block; margin-top: 1rem; font-weight: 500; }
input, select, textarea, button { font-family: inherit; }
input, select, textarea { display: block; width: 100%; padding: 0.5rem; margin-top: 0.25rem; border: 1px solid #ccc; border-radius: 4px; }
button { margin-top: 1.5rem; padding: 0.5rem 1.5rem; background: #1a1a2e; color: #fff; border: none; border-radius: 4px; cursor: pointer; }
button:hover { background: #16213e; }
table { width: 100%; border-collapse: collapse; margin-top: 1rem; }
th, td { text-align: left; padding: 0.5rem; border-bottom: 1px solid #eee; }
/* Header cells carry no fill: an underline is enough to set them off. */
th { background: none; }
thead th { text-decoration: underline; }

main { max-width: 1100px; }
form.inline { display: inline; }
form.inline button { margin-top: 0; }

/* Row actions read alike whether they are links or single-button forms, and sit
   side by side rather than stacking. The cell stays a table cell, so the column
   still lines up: the actions flow inline within it.
   REQ 42.17: Row actions within a listing read alike and sit side by side. */
td.row-actions { white-space: nowrap; }
td.row-actions > * { margin-right: 0.75rem; }
td.row-actions > *:last-child { margin-right: 0; }
button.link {
    margin: 0;
    padding: 0;
    background: none;
    border: none;
    color: #1a1a2e;
    font: inherit;
    text-decoration: underline;
    cursor: pointer;
}
button.link:hover { background: none; color: #16213e; }
button.link.destructive { color: #b00020; }
.flash { background: #e7f5e7; border: 1px solid #b6dfb6; padding: 0.75rem 1rem; border-radius: 4px; }
.error { color: #b00020; font-size: 0.9rem; }
.empty { color: #666; font-style: italic; }
.total { font-weight: 600; margin-top: 1rem; }
.badge { display: inline-block; padding: 0.1rem 0.5rem; border-radius: 999px; font-size: 0.75rem; font-weight: 600; }
/* An unsigned contract is a fact, not a problem, so it reads as neutral gray.
   Expiry is a caution in yellow and overage the severe signal in red. */
.badge-unsigned { background: #eef0f2; color: #444; }
/* An expired contract is a caution, so it takes the yellow warning treatment. */
.badge-expired { background: #fff8d6; color: #7a5c00; }
.badge-warning { background: #fdecea; color: #b00020; }
.badge-status-draft { background: #eef0f2; color: #444; }
.badge-status-sent { background: #e7f5e7; color: #1b6b1b; }
.badge-status-void { background: #fdecea; color: #b00020; }
/* Voided invoice rows are de-emphasized when shown, while the status badge stays
   fully legible. */
tr.voided { opacity: 0.55; }
tr.voided .badge { opacity: 1; }
table.line-items caption { text-align: left; font-weight: 600; padding: 0.5rem 0; }
/* Task-level layout: the entry notes read as one sentence below the task title. */
.entry-notes { color: #444; margin-top: 0.25rem; }
.cap-note { display: block; margin-top: 0.25rem; }
/* A disclosure whose summary reads as a plain text link, used to keep secondary
   forms collapsed until requested. */
details.disclosure { margin-top: 1.5rem; }
details.disclosure > summary {
    display: inline-block;
    cursor: pointer;
    color: #1a1a2e;
    font-size: 0.9rem;
    font-weight: 500;
    text-decoration: underline;
    list-style: none;
}
details.disclosure > summary::-webkit-details-marker { display: none; }
details.disclosure > summary:hover { color: #16213e; }

/* Task metadata sits on its own line beneath the task title, inside the bullet. */
.task-meta { color: #444; font-size: 0.9rem; margin-top: 0.15rem; }
.task-meta form.inline button { margin-top: 0; padding: 0.15rem 0.6rem; font-size: 0.85rem; }

/* Layout switcher: the active invoice layout is emphasized, the other is a link. */
.layout-switcher { color: #666; font-size: 0.875rem; margin-bottom: 0.5rem; }
.layout-switcher strong { color: #222; }
table.line-items tr.total-row th { background: #fff4cc; font-weight: 700; border-top: 2px solid #e0c96b; }
/* A subtotal row is shaded across every cell, not just its heading cells. */
tr.subtotal-row th, tr.subtotal-row td { background: #f5f5f5; font-weight: 600; }
fieldset { margin-top: 1.5rem; }
small { color: #666; }

/* Filter bar: three controls side by side, stacking on narrow screens.
   REQ 18.11: The client, contract and user filters sit in a single row of three
   controls, stacking into one column on narrow viewports. */
/* A collapsed filter set keeps its own margin, so the form inside does not add a
   second one. The caret alone says the region expands, so the summary carries no
   fill or border. */
details.filters-disclosure { margin-top: 1rem; }
details.filters-disclosure > form.filters { margin-top: 0.5rem; }
details.filters-disclosure > summary {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    color: #333;
    text-decoration: none;
}
details.filters-disclosure > summary::before {
    content: "\25b8";
    font-size: 0.95rem;
    color: #666;
    transition: transform 120ms ease-in-out;
}
details.filters-disclosure[open] > summary::before { transform: rotate(90deg); }
details.filters-disclosure > summary:hover { color: #16213e; }
details.filters-disclosure > summary:focus-visible { outline: 2px solid #1a1a2e; outline-offset: 2px; }
form.filters { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 0 1rem; align-items: end; margin-top: 1rem; }
form.filters .field label { margin-top: 0; }
form.filters .filter-actions { display: flex; align-items: center; gap: 1rem; grid-column: 1 / -1; }
form.filters .filter-actions button { margin-top: 1rem; }
@media (max-width: 640px) {
    form.filters { grid-template-columns: minmax(0, 1fr); gap: 0.75rem; }
}

/* A locked field group states that its value does not apply to the selected
   contract type.
   REQ 3.14: An unbillable contract has no hourly rate, no hour cap and no signed
   status. */
.field.locked { opacity: 0.55; }
.field.locked input { background: #f0f0f0; cursor: not-allowed; }

/* Time entry list: each weekday heads its group of rows, and the contract name
   sits above the task title as secondary text. */
tr.group th { background: #eceef4; border-top: 2px solid #d5d9e6; text-decoration: none; }
a.meta, .meta { display: block; color: #666; font-size: 0.8rem; }

/* Contract notes are free text, so line breaks in them are kept. */
p.notes { white-space: pre-wrap; color: #333; }

/* Inline hint: a small info icon paired with a popover (see partials/hint.tmpl).
   The trigger's footprint matches the icon, so what is hoverable is what is
   clickable, and it sits on the text baseline beside the label it explains. */
.hint-trigger {
    display: inline-flex;
    margin: 0 0 0 0.25rem;
    padding: 0;
    border: 0;
    background: none;
    color: #666;
    line-height: 0;
    vertical-align: middle;
    cursor: pointer;
}
.hint-trigger:hover, .hint-trigger:focus-visible { background: none; color: #1a1a2e; }

.hint-popover {
    /* A popover carries inset: 0 and margin: auto from the browser, which centres
       it; clearing both hands placement back to this rule. The anchor() insets pin
       the hint under the start edge of its own trigger, which the trigger names in
       an inline style so that hints on one page do not collide. A browser without
       anchor positioning ignores them and, with the insets auto, a fixed element
       sits where it would have sat in flow, which is beside the trigger. */
    position: fixed;
    inset: auto;
    inset-block-start: anchor(end);
    inset-inline-start: anchor(start);
    position-try-fallbacks: flip-block, flip-inline;
    margin: 0.25rem 0 0;
    max-width: min(90vw, 18rem);
    padding: 0.5rem 0.75rem;
    border: 1px solid #d5d9e6;
    border-radius: 4px;
    background: #fff;
    box-shadow: 0 2px 8px rgb(0 0 0 / 12%);
    color: #333;
    font-size: 0.85rem;
    font-weight: 400;
    line-height: 1.4;
}
