/*
 * Application styles.
 *
 * NFR-9: Bootstrap 5 grid, utilities, and components only, with minimal bespoke CSS.
 * Anything here should be something Bootstrap genuinely does not provide.
 */

/* NFR-8: 44x44px minimum touch targets with 8px spacing. Bootstrap's small buttons
   are below that, so coarse pointers get a floor applied rather than a separate
   mobile stylesheet. */
@media (pointer: coarse) {
    .btn,
    .form-select,
    .form-control {
        min-height: 44px;
    }

    .btn + .btn {
        margin-left: 8px;
    }
}

/* NFR-7: tables become cards below 768px rather than scrolling sideways. Applied by
   adding .table-cards to a table whose cells carry data-label attributes. */
@media (max-width: 767.98px) {
    .table-cards thead {
        display: none;
    }

    .table-cards tbody tr {
        display: block;
        margin-bottom: 1rem;
        border: 1px solid var(--bs-border-color);
        border-radius: var(--bs-border-radius);
        padding: 0.5rem 0.75rem;
    }

    .table-cards tbody td {
        display: flex;
        justify-content: space-between;
        gap: 1rem;
        border: 0;
        padding: 0.25rem 0;
    }

    .table-cards tbody td::before {
        content: attr(data-label);
        font-weight: 600;
        color: var(--bs-secondary-color);
    }
}

/* Wide content scrolls inside its own container, never the page body. */
.scroll-x {
    overflow-x: auto;
}

/* ---------------------------------------------------------------------------
 * Timer widget (TIME-3, NFR-5)
 *
 * NFR-5: "sticky bottom bar, stop control at least 44x44px, never scrolling away,
 * never overlapping the primary action beneath it." The bar is fixed and the body
 * gets matching bottom padding, so the last control on a page stays reachable
 * rather than sitting underneath the bar.
 * ------------------------------------------------------------------------ */

.timer-bar {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 1030;
    padding: 0.5rem 1rem;
    background: var(--bs-body-bg);
    border-top: 1px solid var(--bs-border-color);
    box-shadow: 0 -2px 8px rgb(0 0 0 / 8%);
}

/* Keep the bar from covering whatever is at the foot of the page. */
body:has(.timer-bar) main {
    padding-bottom: 5rem;
}

.timer-widget .timer-stop {
    min-width: 44px;
    min-height: 44px;
}

.timer-labels {
    display: flex;
    flex-direction: column;
    line-height: 1.2;
    min-width: 0;
}

.timer-title {
    max-width: 18rem;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* A quiet indication that something is running, without animation that would
   distract on every page. Reduced-motion users get the dot without the pulse. */
.timer-pulse {
    width: 0.6rem;
    height: 0.6rem;
    border-radius: 50%;
    background: var(--bs-danger);
    flex: 0 0 auto;
    animation: timer-pulse 2s ease-in-out infinite;
}

@keyframes timer-pulse {
    0%, 100% { opacity: 1; }
    50% { opacity: 0.35; }
}

@media (prefers-reduced-motion: reduce) {
    .timer-pulse {
        animation: none;
    }
}

/* ---------------------------------------------------------------------------
 * Idle prompt (TIME-8)
 *
 * A dialogue rather than a banner: the question is about time that is already
 * accumulating, and answering it should not compete with the page behind it.
 * ------------------------------------------------------------------------ */

.idle-prompt {
    position: fixed;
    inset: 0;
    z-index: 1055;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 1rem;
    background: rgb(0 0 0 / 45%);
}

.idle-prompt[hidden] {
    display: none;
}

.idle-prompt-panel {
    width: 100%;
    max-width: 32rem;
}

/* ---------------------------------------------------------------------------
 * Kanban board (KAN-1 to KAN-8, NFR-6)
 *
 * One markup, two shapes. Desktop lays the columns out side by side and cards are
 * dragged; NFR-6 gives a phone one visible column with horizontal snap scrolling,
 * because a board squeezed to 360px is unreadable and a vertical stack of five
 * columns is not a board at all.
 * ------------------------------------------------------------------------ */

.board-track {
    display: flex;
    gap: 0.75rem;
    overflow-x: auto;
    padding-bottom: 0.5rem;
    /* NFR-6: one column at a time, snapped, on a phone. */
    scroll-snap-type: x mandatory;
}

.board-column {
    flex: 0 0 88%;
    scroll-snap-align: start;
    min-width: 0;
    background: var(--bs-tertiary-bg);
    border-radius: var(--bs-border-radius);
    padding: 0.5rem;
}

@media (min-width: 992px) {
    .board-track {
        /* Snapping is a phone affordance; on desktop it fights the drag. */
        scroll-snap-type: none;
    }

    .board-column {
        flex: 0 0 20rem;
    }
}

.board-column-header {
    padding: 0.25rem 0.25rem 0.5rem;
}

/* KAN-3. The limit is a warning, not a block: the team decides, the board tells them. */
.board-column-over-wip {
    box-shadow: inset 0 0 0 2px var(--bs-danger-border-subtle);
}

.board-wip-warning {
    color: var(--bs-danger-text-emphasis);
    background: var(--bs-danger-bg-subtle);
    border-radius: var(--bs-border-radius-sm);
    padding: 0.25rem 0.5rem;
}

.board-column-cards {
    min-height: 3rem;
}

.board-lane-heading {
    margin-top: 0.5rem;
    text-transform: uppercase;
    letter-spacing: 0.03em;
}

.board-card-title {
    font-size: 0.9rem;
    line-height: 1.3;
}

/* The card being dragged leaves a placeholder rather than a hole, so the column
   below it does not jump while the pointer is still down. */
.board-card-ghost {
    opacity: 0.35;
}

/* KAN-8. Parked cards stay where they are, dimmed. Not hidden and not moved: the
   information that this was started is exactly what must not be destroyed. */
.board-card-parked {
    opacity: 0.6;
    border-style: dashed;
}

.board-card-parked:hover,
.board-card-parked:focus-within {
    opacity: 1;
}

.board-park-marker {
    color: var(--bs-secondary-color);
    line-height: 1.25;
}

/* The card whose timer is running, so the board agrees with the widget (TIME-3). */
.board-card-timing {
    box-shadow: inset 0 0 0 2px var(--bs-primary-border-subtle);
}

.board-avatar {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 1.75rem;
    height: 1.75rem;
    border-radius: 50%;
    background: var(--bs-secondary-bg);
    color: var(--bs-body-color);
    font-size: 0.7rem;
    font-weight: 600;
    flex: 0 0 auto;
}

.board-avatar-empty {
    color: var(--bs-secondary-color);
    border: 1px dashed var(--bs-border-color);
    background: transparent;
}

/* The pill selector scrolls rather than wrapping, so the track keeps its height
   however many columns a board has. */
.board-pills {
    overflow-x: auto;
}

.board-move-sheet {
    height: auto;
    max-height: 70vh;
}

/* ---------------------------------------------------------------------------
 * Interaction model (§6)
 *
 * NFR-9 keeps this to Bootstrap components and minimal bespoke CSS, so what is
 * here is the handful of things Bootstrap has no component for: the inline-edit
 * states, the metric hint, and the focus ring that makes UX-6's j/k navigation
 * visible.
 * ------------------------------------------------------------------------ */

/* UX-3's "unobtrusive indicator". A border colour, not a spinner: the save takes
   a moment and a spinner on every blur is noise. */
.inline-field {
    position: relative;
}

.inline-field .form-control,
.inline-field .form-select {
    transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
}

.inline-saving .form-control,
.inline-saving .form-select,
.inline-saving .form-check-input {
    border-color: var(--bs-primary-border-subtle);
    box-shadow: inset 0 0 0 1px var(--bs-primary-border-subtle);
}

.inline-saved .form-control,
.inline-saved .form-select,
.inline-saved .form-check-input {
    border-color: var(--bs-success);
}

/* A field this person may not change is shown as a value, never as a disabled
   input: a greyed-out control invites clicking and then does nothing. */
.inline-field-locked .inline-edit-value {
    color: var(--bs-body-color);
}

/* UX-9. Hover OR tap, so the explanation is reachable on a phone -- a title
   attribute alone is invisible there. */
.metric-label {
    cursor: help;
    border-bottom: 1px dotted var(--bs-secondary-color);
}

.metric-label-hint {
    font-size: 0.7em;
    vertical-align: super;
    color: var(--bs-secondary-color);
}

/* UX-6's j/k focus. NFR-12 needs it visible at AA contrast, and the browser's
   default outline disappears against a card border. */
[data-focusable]:focus,
[data-focusable]:focus-visible {
    outline: 3px solid var(--bs-primary);
    outline-offset: 2px;
}

/* UX-1's preview. Wraps rather than scrolls: the whole point is that the parsed
   interpretation is readable at a glance. */
.quick-capture-preview {
    min-height: 4.5rem;
}

/* UX-5's bulk bar. Fixed above the timer bar so neither covers the other
   (NFR-5 promises the stop control never gets overlapped). */
.bulk-bar {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 1035;
    padding: 0.5rem 1rem;
    background: var(--bs-body-bg);
    border-top: 1px solid var(--bs-border-color);
    box-shadow: 0 -2px 8px rgb(0 0 0 / 8%);
}

body:has(.timer-bar) .bulk-bar {
    bottom: 4.5rem;
}

/* The slide-over is wide enough for the panel's two-column field grid without
   becoming a page in disguise. */
@media (min-width: 768px) {
    .detail-panel {
        width: 32rem;
    }
}

/* NFR-11: usable at 200% zoom. At that magnification a 1440px viewport behaves
   like 720px, so the board's fixed column width has to give way -- otherwise a
   single column fills the screen and the pill selector is the only way to move. */
@media (max-width: 1400px) and (min-resolution: 1.5dppx) {
    .board-column {
        flex-basis: 18rem;
    }
}

/* ---------------------------------------------------------------------------
 * Sprint planning (SPRT-2, INT-12)
 * ------------------------------------------------------------------------ */

.sprint-list {
    min-height: 6rem;
    max-height: 32rem;
    overflow-y: auto;
}

/* INT-12: "unplanned work entering an active sprint is flagged and rendered
   distinctly." Distinctly, not subtly -- the value of the flag is that someone
   reading the sprint can see at a glance how much of it was not planned. */
.sprint-task-unplanned {
    border-left: 3px solid var(--bs-warning);
}

.sprint-task:focus,
.sprint-task:focus-visible {
    outline: 3px solid var(--bs-primary);
    outline-offset: -3px;
}

/* ---------------------------------------------------------------------------
 * Daily planning (PLAN-1 to PLAN-5, INT-9)
 * ------------------------------------------------------------------------ */

/* PLAN-1's groups are labelled rather than merely sorted: an ordering nobody can
   see the rule for is an ordering the next person will "fix". */
.day-group-heading {
    background-color: var(--bs-tertiary-bg);
    letter-spacing: .05em;
}

/* Pinned to the top and coloured like the thing it is. INT-4's P1 and P2 are the
   incident severities, and an incident that reads like every other row is one
   somebody scrolls past. */
.day-row-incident {
    border-left: 3px solid var(--bs-danger);
}

/* KAN-8's dimming, on the row rather than the card. Same rule, same reason: the
   work is still where it was, and that it was started must stay visible. */
.day-row-parked {
    opacity: .65;
    border-left: 3px dashed var(--bs-secondary);
}

.day-row-parked:hover,
.day-row-parked:focus-within {
    opacity: 1;
}

.day-row-done {
    background-color: var(--bs-success-bg-subtle);
}

/* The row whose timer is running, so My Day agrees with the widget (TIME-3). */
.day-row-timing {
    border-left: 3px solid var(--bs-primary);
}

/* UX-3's optimistic move: the row is already where it was dropped while the
   server catches up, and it says so rather than looking finished. */
.day-row-pending {
    opacity: .5;
}

.day-row:focus,
.day-row:focus-visible,
.day-pool-row:focus,
.day-pool-row:focus-visible {
    outline: 3px solid var(--bs-primary);
    outline-offset: -3px;
}

.day-pool {
    max-height: 32rem;
    overflow-y: auto;
}

/* PLAN-4's column per member, the same shape as the board's -- and for the same
   reason SPRT-2 gives: never a single team number, because two people with the
   same total have entirely different days. */
.team-day-column {
    flex-basis: 20rem;
    min-width: 16rem;
    max-height: 40rem;
    overflow-y: auto;
}

@media (max-width: 575.98px) {
    .team-day-column {
        flex-basis: 85vw;
    }
}

/* ---------------------------------------------------------------------------
 * Leave and holidays (§5.10, §5.11)
 * ------------------------------------------------------------------------ */

/* LEAVE-22's month grid. The name column stays put while the dates scroll,
   because a row of dots with no name attached is unreadable. */
.leave-calendar-name {
    position: sticky;
    left: 0;
    background-color: var(--bs-body-bg);
    min-width: 10rem;
    z-index: 1;
}

.leave-cell {
    min-width: 1.6rem;
    line-height: 1.1;
}

/* LEAVE-22: "half days rendered distinctly". Distinct in shape as well as
   colour -- a colour-only distinction fails NFR-12's contrast expectations
   and is invisible to a good share of readers. */
.leave-cell-full {
    background-color: var(--bs-primary-bg-subtle);
    color: var(--bs-primary-text-emphasis);
}

.leave-cell-half {
    background-color: var(--bs-primary-bg-subtle);
    color: var(--bs-primary-text-emphasis);
    opacity: .75;
}

/* HOL-14: holidays overlaid, visually distinct from leave. Different colour and
   a different glyph, for the same reason. */
.leave-cell-holiday {
    background-color: var(--bs-warning-bg-subtle);
    color: var(--bs-warning-text-emphasis);
}

.leave-key {
    display: inline-block;
    width: 1rem;
    height: 1rem;
    vertical-align: text-bottom;
    border: 1px solid var(--bs-border-color);
}

/* HOL-14 on the planning screens: a day nobody is expected to work reads
   differently from an empty one. */
.day-holiday {
    background-color: var(--bs-warning-bg-subtle);
}
