/*
 * Production-side pages: the job register, the sheet record, share links.
 * Extends foundation.css, which must be linked first.
 */

/* ---- register ---------------------------------------------------------------------------- */

/*
 * The list of jobs in the shop. Not cards: a production user scans this looking for one sheet
 * among many, so it is a dense ruled register with the measured columns set in mono.
 */
.register {
    width: 100%;
    border-collapse: collapse;
}

.register thead th {
    padding: 0 0.75rem 0.625rem;
    border-bottom: 1px solid var(--rule);
    font-family: var(--mono);
    font-size: 0.625rem;
    font-weight: 500;
    letter-spacing: 0.16em;
    text-transform: uppercase;
    color: var(--type-faint);
    text-align: left;
}

/*
 * The whole row opens the record, not just its name — the row lights on hover, so it has to be
 * clickable to match. Done by stretching the name link's own box over the row rather than with a
 * click handler, so it stays a real link: middle-click, ctrl-click, "copy link" and the keyboard
 * all keep working, and there is no JavaScript to load on a page that otherwise needs none.
 */
.register tbody tr {
    position: relative;
    border-bottom: 1px solid var(--rule);
    transition: background-color 140ms var(--ease);
}

.register tbody tr:hover { background: var(--ink-raise); }

.register tbody tr:has(.register__name) { cursor: pointer; }

.register__name::after {
    content: '';
    position: absolute;
    inset: 0;
}

/* Later in the row than the overlay and positioned, so it paints above it and keeps its own
   hover — the sheet plate is a link too, to the same place. */
.register tbody a:not(.register__name) { position: relative; }

.register td {
    padding: 0.875rem 0.75rem;
    vertical-align: middle;
}

/*
 * The left gutter carries a cyan tick on hover — the only movement in the table.
 *
 * Deliberately NOT position: relative. The tick positions against the row instead, which puts it
 * in exactly the same place (this is the first cell, so the row's left edge is its left edge) and
 * leaves the row as the one containing block in here. Make this cell a containing block again and
 * the row-wide click overlay collapses into it — which is how the sheets list silently stopped
 * being clickable while the projects list, whose name sits in the second cell, kept working.
 */
.register td:first-child {
    padding-left: 1.25rem;
    width: 1%;
    white-space: nowrap;
}

.register td:first-child::before {
    content: '';
    position: absolute;
    left: 0;
    top: 50%;
    width: 3px;
    height: 0;
    background: var(--cyan);
    transform: translateY(-50%);
    transition: height 160ms var(--ease);
}

.register tbody tr:hover td:first-child::before { height: 22px; }

/* The name column takes the slack so the measured columns stay together on the right. */
.register__stretch { width: 100%; }
.register th:not(.register__stretch), .register td:not(:nth-child(2)) { white-space: nowrap; }

/* A plate sits beside the name, so that cell is a row of its own. */
.register__id {
    display: flex;
    align-items: center;
    gap: 0.875rem;
}

.register__plate {
    font-family: var(--mono);
    font-size: 0.75rem;
    font-variant-numeric: tabular-nums;
    color: var(--type-faint);
}

.register__name {
    font-family: var(--display);
    font-weight: 700;
    font-size: 1.0625rem;
    letter-spacing: 0.03em;
    text-transform: uppercase;
    color: var(--type);
}

a.register__name:hover { color: var(--paper); text-decoration: none; }

.register__sub {
    display: block;
    font-family: var(--body);
    font-size: 0.8125rem;
    font-weight: 400;
    letter-spacing: 0;
    text-transform: none;
    color: var(--type-dim);
}

.register td.right { text-align: right; }
.register .cell-dim { color: var(--type-faint); font-size: 0.8125rem; }

/* ---- sheet plate ------------------------------------------------------------------------- */

/*
 * The sheet showing itself, cut from level 0 of its own pyramid. Paper white is otherwise
 * reserved for the map, which is exactly what this is.
 */
.plate {
    position: relative;
    display: block;
    overflow: hidden;
    flex: 0 0 auto;
    /* Ink, not paper: the image supplies the paper, and until it arrives a white box reads as a
       blank sheet rather than as one still loading. */
    background: var(--ink-sunk);
    border: 1px solid var(--rule-lit);
    border-radius: var(--radius);
    box-shadow: 0 2px 10px rgba(0, 0, 0, 0.45);
}

.plate img {
    position: absolute;
    top: 0;
    left: 0;
    display: block;
    max-width: none;
}

/* A fixed height, not a fixed width: sheets differ in orientation, and a register wants even
   rows more than it wants even plates. */
.plate--sm { height: 48px; }
.plate--lg { width: 168px; }

/*
 * A version with no pyramid — queued, rendering, or failed — has no sheet to show. Left plain it
 * reads as a hole punched in the page, or worse as a proof that came back black, which on a
 * colour-critical tool is alarming. Dashed edge and a faint hatch instead: empty stock, nothing
 * printed on it yet.
 */
.plate--blank {
    aspect-ratio: 4 / 3;
    background: repeating-linear-gradient(
        -45deg,
        transparent 0 5px,
        rgba(255, 255, 255, 0.035) 5px 6px);
    background-color: var(--ink-sunk);
    border-style: dashed;
    border-color: var(--rule);
    box-shadow: none;
}

a:hover > .plate { border-color: var(--paper); }

/* ---- pipeline ---------------------------------------------------------------------------- */

/*
 * Numbering earns its place here: rasterising really is an ordered run of stages, and which one
 * the sheet is sitting in is the question the page exists to answer.
 */
.pipeline {
    display: grid;
    grid-template-columns: repeat(4, minmax(0, 1fr));
    gap: 0.5rem;
    margin-bottom: 1.5rem;
}

.pipeline li { padding-top: 0.5rem; border-top: 2px solid var(--rule); }

.pipeline__stage {
    display: block;
    font-family: var(--mono);
    font-size: 0.5625rem;
    letter-spacing: 0.16em;
    color: #545e6c;
}

.pipeline__name {
    display: block;
    margin-top: 0.125rem;
    font-size: 0.8125rem;
    color: var(--type-faint);
}

/* A finished stage is history, so its rule is a muted cyan; only the live one is full strength. */
.pipeline li.is-done { border-top-color: rgba(0, 166, 218, 0.4); }
.pipeline li.is-done .pipeline__name { color: var(--type-dim); }
.pipeline li.is-done .pipeline__stage { color: var(--type-faint); }

.pipeline li.is-current { border-top-color: var(--cyan); }
.pipeline li.is-current .pipeline__name { color: var(--type); font-weight: 500; }
.pipeline li.is-current .pipeline__stage { color: var(--cyan); }
.pipeline li.is-current { animation: stage-breathe 1.8s ease-in-out infinite; }

.pipeline li.is-failed { border-top-color: var(--magenta); }
.pipeline li.is-failed .pipeline__name { color: var(--magenta); }
.pipeline li.is-failed .pipeline__stage { color: var(--magenta); }

@keyframes stage-breathe {
    0%, 100% { border-top-color: var(--cyan); }
    50% { border-top-color: rgba(0, 166, 218, 0.3); }
}

/* ---- live progress ---------------------------------------------------------------------- */

/* The stage rule is already the indicator, so it fills rather than a second bar being added
   beside it. The li's own border stays as the unfilled track underneath. */
.pipeline li { position: relative; }

.pipeline__fill {
    position: absolute;
    top: -2px;                 /* sits exactly on the 2px border it is filling */
    left: 0;
    height: 2px;
    width: 0;
    background: var(--cyan);
    transition: width 0.4s linear;
}

/* Only the running stage carries a fill, and while it does the breathing rule underneath would
   fight it — a bar that fades is a bar that looks like it is losing progress. */
.pipeline li.is-current:has(.pipeline__fill) { animation: none; }

/* Nothing can measure or predict this stage, so the bar reports motion and not position: a
   short segment travelling the width of the stage, which is honest about knowing nothing. */
.pipeline__fill.is-indeterminate {
    width: 30%;
    transition: none;
    animation: stage-sweep 1.6s ease-in-out infinite;
}

@keyframes stage-sweep {
    0% { left: 0; }
    50% { left: 70%; }
    100% { left: 0; }
}

/* A reading off the job, so mono, like every other measured quantity here. */
.progress-read {
    font-family: var(--mono);
    font-size: 0.75rem;
    color: var(--type-dim);
    margin: -1rem 0 1.5rem;
}

.progress-read strong { color: var(--cyan); font-weight: 500; }

.progress-read .sep { color: var(--rule); padding: 0 0.4rem; }

@media (prefers-reduced-motion: reduce) {
    .pipeline__fill { transition: none; }
    .pipeline__fill.is-indeterminate { animation: none; width: 100%; opacity: 0.35; }
}

/* ---- deleting a version ------------------------------------------------------------------ */

/* Magenta, like revoke and every other correction: this is the ink that means "something is
   being taken away". Bordered rather than filled, so it reads as a bounded area of the page
   rather than an alarm the reader has to dismiss before doing anything else. */
.danger {
    border: 1px solid rgba(214, 0, 122, 0.45);
    border-left-width: 3px;
    padding: 1.25rem 1.5rem;
    background: rgba(214, 0, 122, 0.04);
}

.danger__loss {
    font-size: 0.9375rem;
    color: var(--type);
    margin-bottom: 0.5rem;
}

/* Someone may have the map open right now. That outranks everything else in the block. */
.danger__live {
    font-family: var(--mono);
    font-size: 0.75rem;
    color: var(--magenta);
    margin-bottom: 0.5rem;
}

.danger__confirm {
    display: block;
    margin: 1.25rem 0 0.5rem;
    font-size: 0.8125rem;
    color: var(--type-dim);
}

.danger__confirm strong {
    font-family: var(--mono);
    color: var(--type);
}

/* Mono because it is a number being matched, and the eye should be able to compare the two.
   Sized to the number rather than stretched by the row: a field the width of the page invites
   a sentence, and this wants two or three digits. */
.danger input {
    font-family: var(--mono);
    text-align: center;
    width: 5rem;
    flex: 0 0 auto;
}

/* ---- spec grid -------------------------------------------------------------------------- */

/* Everything the pipeline measured. Mono, because these are readings. */
/* Fixed at four so the eight readings fall into two complete rows; auto-fit left ragged gaps. */
.spec {
    display: grid;
    grid-template-columns: repeat(4, minmax(0, 1fr));
    border: 1px solid var(--rule);
    border-radius: var(--radius);
    background: var(--ink-raise);
}

@media (max-width: 1000px) { .spec { grid-template-columns: repeat(2, minmax(0, 1fr)); } }

.spec > div {
    padding: 1rem 1.25rem 1.125rem;
    border-left: 1px solid var(--rule);
    border-top: 1px solid var(--rule);
}

.spec > div:first-child { border-left: 0; }
.spec > div:nth-child(-n + 4) { border-top: 0; }

@media (max-width: 1000px) {
    .spec > div:nth-child(-n + 4) { border-top: 1px solid var(--rule); }
    .spec > div:nth-child(-n + 2) { border-top: 0; }
    .spec > div:nth-child(odd) { border-left: 0; }
}

@media (max-width: 560px) {
    .spec { grid-template-columns: minmax(0, 1fr); }
    .spec > div { border-left: 0; border-top: 1px solid var(--rule); }
    .spec > div:first-child { border-top: 0; }
}

.spec dt {
    font-family: var(--mono);
    font-size: 0.625rem;
    font-weight: 500;
    letter-spacing: 0.16em;
    text-transform: uppercase;
    color: var(--type-faint);
}

.spec dd {
    margin-top: 0.375rem;
    font-family: var(--mono);
    font-size: 0.9375rem;
    font-variant-numeric: tabular-nums;
    color: var(--type);
}

.spec dd .unit { color: var(--type-faint); font-size: 0.8125rem; }
.spec dd.pending { color: var(--rule-lit); }

/* ---- issues (share links) --------------------------------------------------------------- */

/* One row per link handed out — an issue of the proof, with who it went to and its state. */
.issues { display: grid; gap: 0.75rem; }

.issue {
    border: 1px solid var(--rule);
    border-radius: var(--radius);
    background: var(--ink-raise);
    transition: border-color 140ms var(--ease);
}

.issue:hover { border-color: var(--rule-lit); }
.issue.is-dead { opacity: 0.55; }

.issue__head {
    display: flex;
    align-items: center;
    gap: 0.875rem;
    flex-wrap: wrap;
    padding: 0.875rem 1.125rem;
}

.issue__label {
    font-family: var(--display);
    font-weight: 700;
    font-size: 1rem;
    letter-spacing: 0.04em;
    text-transform: uppercase;
}

.issue__perm {
    font-family: var(--mono);
    font-size: 0.6875rem;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: var(--type-faint);
}

.issue__spacer { flex: 1 1 auto; }

.issue__url {
    display: flex;
    align-items: stretch;
    gap: 0.5rem;
    padding: 0 1.125rem 1rem;
}

.issue__url code {
    flex: 1 1 auto;
    min-width: 0;
    padding: 0.4375rem 0.625rem;
    background: var(--ink-sunk);
    border: 1px solid var(--rule);
    border-radius: var(--radius);
    font-size: 0.75rem;
    color: var(--type-dim);
    overflow-x: auto;
    white-space: nowrap;
}

/* ---- upload ----------------------------------------------------------------------------- */

.gauge {
    height: 2px;
    background: var(--rule);
    overflow: hidden;
}

.gauge__fill {
    height: 100%;
    width: 0;
    background: var(--cyan);
    transition: width 200ms linear;
}

.gauge-readout {
    margin-top: 0.4375rem;
    font-family: var(--mono);
    font-size: 0.6875rem;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: var(--type-faint);
}

/* ---- failure ---------------------------------------------------------------------------- */

.stderr {
    margin: 0.75rem 0 1rem;
    padding: 0.875rem 1rem;
    max-height: 16rem;
    overflow: auto;
    background: var(--ink-sunk);
    border: 1px solid rgba(229, 0, 126, 0.3);
    border-radius: var(--radius);
    font-size: 0.75rem;
    line-height: 1.6;
    color: #ff8ec4;
    white-space: pre-wrap;
    overflow-wrap: anywhere;
}

/* The dead-end page lives in foundation.css: a client meeting an expired link sees it on the
   viewer layout, which does not load this file. */
