/* Task log
   ---------------------------------------------------------------------------
   A reusable progress + log viewer for long running operations (client
   onboarding, syncs, imports, bulk jobs). Anything that runs long enough that
   the operator needs to know it is alive, what it is doing now, and what it did
   afterwards.

   Deliberately global rather than scoped to a page: the same three problems
   turn up all over the CMS, and a log that looks different on every screen is a
   log people learn to ignore.

   Structure:
     .task-log
       .task-log-head      current step + counts, always visible
         .task-log-status
         .task-log-count
       .task-log-bar       progress track
         .task-log-fill    determinate width, or .indeterminate
       .task-log-toggle    show/hide the detail
            .task-log-entries   scrollable detail, one .task-log-entry per line

          For a job that blocks the page, wrap the whole thing in .task-log-modal
          inside a .task-log-scrim. See the scrim section at the foot of this file.
       */

.task-log {
    border: 1px solid var(--color-lighter);
    border-radius: 10px;
    background-color: var(--color-lightest);
    overflow: hidden;
}

/* Head ------------------------------------------------------------------- */

.task-log-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 15px;
    padding: 15px;
}

.task-log-status {
    display: flex;
    align-items: center;
    gap: 10px;
    min-width: 0;
    font-size: .9rem;
    color: var(--text-color);
}

    /* The message is the part that changes constantly, so it gets the ellipsis
       rather than wrapping and shifting everything below it on every entry. */
    .task-log-status .task-log-message {
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
    }

    .task-log-status .task-log-category {
        font-weight: 600;
        flex: none;
    }

.task-log-count {
    flex: none;
    font-size: .8rem;
    color: var(--sub-text-color);
    font-variant-numeric: tabular-nums;
}

/* Spinner shown while the run is active. */
.task-log-spinner {
    flex: none;
    width: 14px;
    height: 14px;
    border: 2px solid var(--color-lighter);
    border-top-color: var(--color-2);
    border-radius: 50%;
    animation: task-log-spin .8s linear infinite;
}

@keyframes task-log-spin {
    to {
        transform: rotate(360deg);
    }
}

.task-log-icon {
    flex: none;
    width: 14px;
    text-align: center;
}

    .task-log-icon.success {
        color: var(--color-success);
    }

    .task-log-icon.warning {
        color: var(--color-star);
    }

    .task-log-icon.error {
        color: var(--color-error);
    }

/* Progress bar ------------------------------------------------------------ */

.task-log-bar {
    position: relative;
    height: 4px;
    background-color: var(--color-lighter);
    overflow: hidden;
}

.task-log-fill {
    height: 100%;
    background-color: var(--color-2);
    transition: width .3s ease;
}

    .task-log-fill.success {
        background-color: var(--color-success);
    }

    .task-log-fill.warning {
        background-color: var(--color-star);
    }

    .task-log-fill.error {
        background-color: var(--color-error);
    }

    /* Used when the total number of steps is not known up front. A moving band
       says "still working" without claiming a percentage that would be a guess. */
    .task-log-fill.indeterminate {
        width: 35% !important;
        position: absolute;
        animation: task-log-slide 1.4s ease-in-out infinite;
    }

@keyframes task-log-slide {
    0% {
        left: -35%;
    }

    100% {
        left: 100%;
    }
}

/* Toggle ------------------------------------------------------------------ */

.task-log-toggle {
    display: block;
    width: 100%;
    padding: 8px 15px;
    border: 0;
    border-top: 1px solid var(--color-lighter);
    background-color: transparent;
    color: var(--link-color);
    font-size: .8rem;
    text-align: left;
    cursor: pointer;
}

    .task-log-toggle:hover {
        background-color: var(--color-4);
    }

/* Entries ----------------------------------------------------------------- */

.task-log-entries {
    max-height: 320px;
    overflow-y: auto;
    border-top: 1px solid var(--color-lighter);
    font-size: .8rem;
}

.task-log-entry {
    display: grid;
    grid-template-columns: 70px 60px 100px 1fr;
    gap: 10px;
    padding: 8px 15px;
    border-bottom: 1px solid var(--color-4);
    align-items: start;
}

    .task-log-entry:last-child {
        border-bottom: 0;
    }

    .task-log-entry .task-log-time {
        color: var(--sub-text-color);
        font-variant-numeric: tabular-nums;
    }

    .task-log-entry .task-log-level {
        font-weight: 600;
        font-size: .7rem;
        text-transform: uppercase;
    }

    .task-log-entry .task-log-entry-category {
        color: var(--sub-text-color);
    }

    /* Server messages can be long (a failed remote command includes its whole
       script), so the detail wraps and preserves the breaks git and PowerShell
       put in it rather than collapsing to one unreadable line. */
    .task-log-entry .task-log-detail {
        white-space: pre-wrap;
        word-break: break-word;
        min-width: 0;
    }

    .task-log-entry.info .task-log-level {
        color: var(--sub-text-color);
    }

    .task-log-entry.success {
        background-color: var(--color-success-light);
    }

        .task-log-entry.success .task-log-level {
            color: var(--color-success);
        }

    .task-log-entry.warning {
        background-color: var(--color-warning-light);
    }

        .task-log-entry.warning .task-log-level {
            color: var(--color-star);
        }

    .task-log-entry.error {
        background-color: var(--color-error-light);
    }

        .task-log-entry.error .task-log-level {
            color: var(--color-error);
        }

@media (max-width: 700px) {
    .task-log-entry {
        grid-template-columns: 60px 1fr;
    }

        .task-log-entry .task-log-entry-category {
            grid-column: 2;
        }

        .task-log-entry .task-log-detail {
            grid-column: 1 / -1;
        }
}

/* Draft restore prompt
   ---------------------------------------------------------------------------
   Shown when a page finds a cached copy of a form the operator had not yet
   submitted, typically because the CMS was republished underneath them. Global
   for the same reason as the log: any long form can lose its contents this way.
*/

.draft-prompt {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 15px;
    flex-wrap: wrap;
    margin: 20px 0;
    padding: 15px;
    border: 1px solid var(--color-star);
    border-radius: 10px;
    background-color: var(--color-warning);
    color: var(--color-warning-reverse);
    font-size: .9rem;
}

.draft-prompt-actions {
    display: flex;
    gap: 10px;
    flex: none;
}

/* Blocking scrim
   ---------------------------------------------------------------------------
   For jobs the operator must not interact around: onboarding creates a
   database, an IIS site and a GitHub repository, so wandering off and pressing
   something else mid-run is how you get a half-provisioned client.

   The scrim whites out the page rather than dimming it. This is not a dialog
   asking for a decision, it is the page saying nothing else is available yet,
   and washing the background out says that more plainly than a dark overlay.

   Kept here rather than in modal.css because it comes as a pair with the task
   log and has different semantics from a normal modal: no close affordance,
   nothing dismissable behind it.
*/

.task-log-scrim {
    position: fixed;
    inset: 0;
    z-index: 999;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 20px;
    background-color: rgba(255, 255, 255, .85);
    backdrop-filter: blur(2px);
    /* Swallows clicks aimed at the page underneath. */
    pointer-events: auto;
}

.task-log-modal {
    width: 100%;
    max-width: 720px;
    max-height: 85vh;
    display: flex;
    flex-direction: column;
    padding: 25px;
    border-radius: 15px;
    background: var(--modal-background-color);
    box-shadow: var(--shadow-very-far);
}

    .task-log-modal h2 {
        margin: 0 0 5px;
    }

    /* The log itself is the part that grows, so the heading and any footer
       buttons stay put and only the entry list scrolls. */
    .task-log-modal .task-log {
        flex: 1 1 auto;
        min-height: 0;
        display: flex;
        flex-direction: column;
    }

    .task-log-modal .task-log-entries {
        flex: 1 1 auto;
        max-height: none;
    }

@media (max-width: 767px) {
    .task-log-scrim {
        padding: 10px;
    }

    .task-log-modal {
        max-height: 92vh;
        padding: 20px 15px;
    }
}

