/*
 * Production-side pages: the job register, the sheet record, share links.
 * Extends foundation.css, which must be linked first.
 */

/* ---- finder ------------------------------------------------------------------------------ */

/*
 * Finding one job among all of them. Set like an instrument rather than like a form field: what is
 * typed here is an identifier being matched against the plates and order numbers in the register
 * below, so it is mono for the same reason those are. The registration mark that opens every
 * eyebrow on the site opens this too — the one motif, doing the one thing its name describes.
 */
.finder {
    display: flex;
    align-items: center;
    gap: 0.625rem;
    margin-bottom: 1.25rem;
}

.finder__field {
    position: relative;
    display: flex;
    align-items: center;
    flex: 1 1 auto;
    min-width: 0;
}

/* The mark takes the ink of the field it opens: marginalia grey at rest, cyan while it has focus. */
.finder__mark {
    position: absolute;
    left: 0.625rem;
    width: 13px;
    height: 13px;
    background: var(--type-faint);
    -webkit-mask: var(--regmark) center / contain no-repeat;
    mask: var(--regmark) center / contain no-repeat;
    pointer-events: none;
    transition: background-color 140ms var(--ease);
}

.finder__field:focus-within .finder__mark { background: var(--cyan); }

.finder__field input[type='search'] {
    padding-left: 2.0625rem;
    font-family: var(--mono);
    font-size: 0.8125rem;
    letter-spacing: 0.04em;
}

/*
 * How much of the register is on screen. A measured quantity, so mono and tabular — and shown only
 * while a search is narrowing the list, because "148 of 148" is not information.
 */
.finder__count {
    flex: 0 0 auto;
    margin-left: auto;
    font-family: var(--mono);
    font-size: 0.6875rem;
    font-variant-numeric: tabular-nums;
    letter-spacing: 0.08em;
    color: var(--type-faint);
    white-space: nowrap;
}

/* Narrow: the field keeps the full width and its controls drop beneath it rather than shrinking. */
@media (max-width: 620px) {
    .finder { flex-wrap: wrap; }
    .finder__field { flex: 1 0 100%; }
}

/* ---- 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;
}

/* Deleting a project is confirmed by typing its order number, which is eight or nine characters
   rather than two digits. Sized to that instead, for the same reason the rule above is sized to a
   version number: a field the reader has to scroll inside cannot be compared with the label. */
.danger--text input { width: 11rem; }

/* ---- 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;
}

/* The proofing timeline under a share link. Marginalia: it is evidence, not a headline. */
.issue__timeline {
    margin: 0 1.125rem 1rem;
    padding-top: 0.75rem;
    border-top: 1px solid var(--rule);
}

/* Cyan carries the affirmative, as it does in the register. No green. */
.issue__approved { margin: 0 0 .5rem; color: var(--cyan); font-size: .8125rem; }

.issue__quiet {
    margin: 0;
    font-size: 0.8125rem;
    color: var(--type-faint);
}

.trace {
    list-style: none;
    margin: 0.25rem 0 0;
    padding: 0;
}

.trace__row {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 0.5rem 1rem;
    padding: 0.25rem 0;
    font-size: 0.8125rem;
    color: var(--type-dim);
}

.trace__span { color: var(--type); }
.trace__place { color: var(--type); }
.trace__ip { color: var(--type-faint); }

/* ---- 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. */

/* ---- register: proof state ---------------------------------------------------------------- */

/*
 * Which jobs are waiting on a client, at a glance down the column. No green in this palette —
 * cyan carries the affirmative (approved), grey carries everything still unsettled — the opposite
 * of the annotation palette on purpose: there, chromatic ink means outstanding on the sheet itself;
 * here the question is only whether a job still needs chasing. The mark is `.num` (foundation.css),
 * the same mono/tabular-nums treatment as every other measured figure in the register, so the marks
 * line up down the column even though the words beside them do not.
 */
.proof { display: inline-flex; gap: 0.4rem; align-items: baseline; white-space: nowrap; }
.proof--approved { color: var(--cyan); }
.proof--opened   { color: var(--type); }
.proof--waiting  { color: var(--type-dim); }
.proof--none     { color: var(--type-faint); }
