/*
 * The old SGAtools look, carried onto Bootstrap 5.
 *
 * Every colour and font here is lifted from the Play app's public/stylesheets/style.css
 * so the site stays recognisable to the people who used it: PT Sans for text, Open Sans
 * for the brand and headings, a light #f7f7f7 bar under a 5px #e0e8ea rule, #0081cc for
 * anything you click, and a grey strip along the bottom.
 *
 * What is deliberately not carried over: the fixed 940px navbar (it does not fit a
 * phone), the negative-margin sticky footer (flexbox does it without the magic numbers),
 * and the gradient/filter stack on .btn-primary (six vendor prefixes for one flat
 * colour).
 */

:root {
    --sga-blue: #0081cc;
    --sga-blue-dark: #006aa8;
    --sga-bar: #f7f7f7;
    --sga-rule: #e0e8ea;
    --sga-ink: #222;

    --bs-primary: var(--sga-blue);
    --bs-primary-rgb: 0, 129, 204;
    --bs-link-color: var(--sga-blue);
    --bs-link-color-rgb: 0, 129, 204;
    --bs-link-hover-color: var(--sga-blue-dark);
    --bs-body-font-family: 'PT Sans', -apple-system, 'Segoe UI', Roboto, sans-serif;
    --bs-body-font-size: 1rem;
    --bs-border-radius: .375rem;
}

/* Sticky footer, the 2026 way: the old #wrap/#push pair needed the footer's height
   written down in three places, and they had to agree. */
html, body {
    height: 100%;
}

body {
    display: flex;
    flex-direction: column;
    min-height: 100%;
    border-top: 5px solid var(--sga-rule);
    color: #333;

    /* The original's tile: near-white colony spots, faint enough that you notice it
       only once it is gone. It sat on #wrap, which was everything above the footer, so
       the footer strip below stays flat grey. */
    background-image: url("../img/dots-bg.aa49be789f30.jpg");
    background-repeat: repeat;
}

main {
    flex: 1 0 auto;
    padding-top: 2rem;
    padding-bottom: 2.5rem;
}

h1, h2, h3, h4, h5, h6, .navbar-brand {
    font-family: 'Open Sans', var(--bs-body-font-family);
    color: var(--sga-ink);
}

h1, h2, h3 {
    font-weight: 600;
    letter-spacing: -.01em;
}

.heavy {
    font-family: 'Open Sans', sans-serif;
    font-weight: 700;
}

/* --- Navigation ---------------------------------------------------------------- */

.navbar {
    background: var(--sga-bar);
    box-shadow: inset 0 0 3px rgba(0, 0, 0, .2);
    padding-top: 0;
    padding-bottom: 0;
}

.navbar-brand {
    font-size: 28px;
    font-weight: 400;
    color: var(--sga-ink);
    padding: .6rem 0;
}

/* The gap is in em, not px, so the one rule serves both the 28px navbar wordmark and
   the 68px one on the home page. The original got this gap from the newline between its
   <img> and <b> tags collapsing to a space, which is too fragile to rely on. */
.navbar-brand img {
    height: 32px;
    margin-top: -6px;
    margin-right: .3em;
}

.navbar .nav-link {
    color: #444;
    font-size: 1.1em;
    padding: 1.35rem .65rem;
    text-shadow: 0 -1px 0 rgba(255, 255, 255, .25);
}

.navbar .nav-link:hover,
.navbar .nav-link:focus {
    color: var(--sga-ink);
}

/* The original marked the current page by making it bold and dark. Underlining it too
   is the small modern addition -- weight alone is easy to miss at a glance. */
.navbar .nav-link.active {
    color: var(--sga-ink);
    font-weight: 700;
    box-shadow: inset 0 -3px 0 var(--sga-blue);
}

@media (max-width: 991.98px) {
    .navbar .nav-link {
        padding: .5rem 0;
    }

    .navbar .nav-link.active {
        box-shadow: inset 3px 0 0 var(--sga-blue);
        padding-left: .65rem;
    }

    .navbar-collapse {
        padding-bottom: .75rem;
    }
}

/* --- Buttons and links --------------------------------------------------------- */

/* Bootstrap 5 underlines every link; the old site, on Bootstrap 2, underlined on hover.
   The old way is also the current one, and it is most of the difference on a page of
   file names. */
a {
    text-decoration: none;
}

a:hover, a:focus-visible {
    text-decoration: underline;
}

.btn:hover, .nav-link:hover, .navbar-brand:hover, .dropdown-item:hover,
.badge:hover, .page-link:hover {
    text-decoration: none;
}

.btn-primary {
    --bs-btn-bg: var(--sga-blue);
    --bs-btn-border-color: var(--sga-blue);
    --bs-btn-hover-bg: var(--sga-blue-dark);
    --bs-btn-hover-border-color: var(--sga-blue-dark);
    --bs-btn-active-bg: var(--sga-blue-dark);
    --bs-btn-active-border-color: var(--sga-blue-dark);
    --bs-btn-disabled-bg: var(--sga-blue);
    --bs-btn-disabled-border-color: var(--sga-blue);
}

.btn-outline-primary {
    --bs-btn-color: var(--sga-blue);
    --bs-btn-border-color: var(--sga-blue);
    --bs-btn-hover-bg: var(--sga-blue);
    --bs-btn-hover-border-color: var(--sga-blue);
    --bs-btn-active-bg: var(--sga-blue-dark);
    --bs-btn-active-border-color: var(--sga-blue-dark);
}

.form-check-input:checked {
    background-color: var(--sga-blue);
    border-color: var(--sga-blue);
}

.form-control:focus, .form-select:focus, .form-check-input:focus {
    border-color: #7fc0e6;
    box-shadow: 0 0 0 .2rem rgba(0, 129, 204, .2);
}

/* --- Cards and form chrome ------------------------------------------------------ */

/* The settings box. The grey-capped panel it was is the one Bootstrap 3 tell left on the
   page; this is the same box with the cap taken off -- a heading inside a white card,
   lifted a hair off the dotted background. */
.card {
    --bs-card-border-color: #e4e7e9;
    --bs-card-border-radius: .5rem;
    --bs-card-inner-border-radius: calc(.5rem - 1px);
    --bs-card-cap-bg: transparent;
    --bs-card-cap-padding-y: .9rem;
    box-shadow: 0 1px 2px rgba(0, 0, 0, .04), 0 2px 8px rgba(0, 0, 0, .03);
}

.card-header {
    font-family: 'Open Sans', var(--bs-body-font-family);
    font-weight: 600;
    color: var(--sga-ink);
    border-bottom-color: #eef1f2;
}

.form-label {
    margin-bottom: .3rem;
}

.form-text {
    color: #747474;
}

/* Switches: a touch larger than Bootstrap's, and the help text tucked close under the
   label so a row reads as one setting. */
.form-switch .form-check-input {
    width: 2.25em;
    height: 1.25em;
    margin-top: .12em;
    cursor: pointer;
}

.form-switch .form-check-label {
    cursor: pointer;
}

.form-check .form-text {
    margin-top: .05rem;
}

/* The end of a form: the button sits on its own rule, tied to the fields above it rather
   than floating in the space under the shorter column. */
.form-actions {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: .75rem 1rem;
    margin-top: 1.5rem;
    padding-top: 1.25rem;
    border-top: 1px solid #e6eaec;
}

/* --- The three columns on the home page ---------------------------------------- */

.home-columns .bi,
.home-columns svg {
    color: #bfbfbf;
    width: 42px;
    height: 42px;
}

.home-columns h2 {
    color: #3b3b3b;
    font-size: 1.5rem;
    margin: .75rem 0 .35rem;
}

.home-columns p {
    color: #747474;
}

/* --- Footer -------------------------------------------------------------------- */

#footer {
    flex-shrink: 0;
    background-color: #f5f5f5;
    border-top: 1px solid #e5e5e5;
    padding: 1.25rem 0;
    text-align: center;
}

#footer p {
    margin: 0;
    color: #777;
    font-size: .9rem;
}

#footer a {
    color: #777;
    text-decoration: none;
}

#footer a:hover {
    color: var(--sga-blue);
    text-decoration: underline;
}

/* --- Small things the pages lean on -------------------------------------------- */

.page-header {
    margin-top: 0;
    padding-bottom: .5rem;
    border-bottom: 1px solid #eee;
    margin-bottom: 1.5rem;
}

.page-header p {
    color: #747474;
    margin: .35rem 0 0;
}

code, pre, .font-monospace {
    font-size: .875em;
}

/* Nearly every <code> on the site is a file name or an ORF, and a page of them in
   Bootstrap 3's raspberry is what dated the result pages most. Monospace and a shade
   darker than the text is enough to set them apart. */
code {
    color: #3b3b3b;
}

.alert code {
    color: inherit;
}

/* The partial-screen confirmation, under the array picker. Compact: it is one sentence
   and a checkbox, not a banner. */
.partial-array {
    padding: .6rem .8rem;
    margin: .25rem 0 .75rem;
    font-size: .875rem;
}

.partial-array .form-check {
    margin-bottom: 0;
}

.partial-array .form-text {
    margin-top: .15rem;
}

/* Tables: a light rule between rows, a firmer one under the heading, none after the
   last row -- the box the table sits in already ends it. */
.table {
    --bs-table-border-color: #e6eaec;
    --bs-table-color: #333;
}

.table > thead > tr > th {
    border-bottom-color: #cfd6da;
    font-weight: 700;
    color: #3b3b3b;
}

.table > tbody > tr:last-child > * {
    border-bottom-width: 0;
}

.table-sm td, .table-sm th {
    padding: .35rem .5rem;
}

/* Long output listings and logs: scroll the block, never the page. */
.scroll-box {
    max-height: 20rem;
    overflow: auto;
}

/* --- Result pages -------------------------------------------------------------- */

/* Facts about a run: a two-column list, keys muted, no rules. A ruled table said
   "data" about what is a caption. */
.facts {
    display: grid;
    grid-template-columns: max-content minmax(0, 1fr);
    gap: .4rem 1.25rem;
    margin: 0;
}

.facts dt {
    font-weight: 400;
    color: #747474;
}

.facts dd {
    margin: 0;
    overflow-wrap: anywhere;
}

/* Where else a run leads -- back to its photographs, on to its plate maps. One row of
   the same buttons, so they read as navigation and not as two more downloads. */
.run-nav {
    display: flex;
    flex-wrap: wrap;
    gap: .5rem;
    margin-bottom: 1rem;
}

/* The downloads, in one box: a heading that says what and how much, the zip beside it,
   and under them every file as a row that is itself the link. Each row carries the
   download arrow, since a bare monospace name in a list did not look clickable. */
.downloads {
    background: #fff;
    border: 1px solid #e4e7e9;
    border-radius: .5rem;
}

.downloads-head {
    display: flex;
    flex-wrap: wrap;
    justify-content: space-between;
    align-items: center;
    gap: .5rem 1rem;
    padding: .75rem 1rem;
}

.downloads-head .btn svg {
    vertical-align: -.15em;
    margin-right: .15em;
}

.btn-note {
    margin-left: .5em;
    padding-left: .5em;
    border-left: 1px solid rgba(255, 255, 255, .4);
    font-variant-numeric: tabular-nums;
    opacity: .85;
}

/* A table in the same box -- the plates of an image run -- flush with its edges, the
   first and last cells padded to the heading's margin, and a note under it. */
.downloads > .table-responsive {
    border-top: 1px solid #e4e7e9;
}

.downloads .table {
    margin: 0;
}

.downloads .table > :not(caption) > * > :first-child {
    padding-left: 1rem;
}

.downloads .table > :not(caption) > * > :last-child {
    padding-right: 1rem;
}

.downloads .table > thead > tr > th {
    background: #f7f9fa;
    color: #747474;
    font-size: .8em;
    font-weight: 600;
    letter-spacing: .02em;
    text-transform: uppercase;
    border-bottom-color: #e4e7e9;
}

.downloads-note {
    margin: 0;
    padding: .6rem 1rem;
    border-top: 1px solid #e4e7e9;
    color: #747474;
    font-size: .85em;
}

/* A file at the end of a table row: the arrow says which of the two is the download. */
.file-link {
    display: inline-flex;
    align-items: center;
    gap: .2em;
    text-decoration: none;
}

.file-link + .file-link {
    margin-left: .75em;
}

.file-link:hover {
    text-decoration: underline;
}

.file-list {
    list-style: none;
    margin: 0;
    padding: 0;
    border-top: 1px solid #e4e7e9;
}

.file-list li + li {
    border-top: 1px solid #eef1f2;
}

.file-list li:last-child > a {
    border-radius: 0 0 .5rem .5rem;
}

/* A group's label: a slim band, muted, the note after it smaller still. */
.file-group {
    padding: .35rem 1rem;
    background: #f7f9fa;
    color: #747474;
    font-size: .8em;
    font-weight: 600;
    letter-spacing: .02em;
    text-transform: uppercase;
}

.file-group span {
    margin-left: .5em;
    font-weight: 400;
    text-transform: none;
    letter-spacing: 0;
}

.file-list li > a {
    display: grid;
    grid-template-columns: minmax(0, max-content) minmax(0, 1fr) max-content max-content;
    align-items: center;
    column-gap: .75rem;
    padding: .45rem 1rem;
    color: #333;
    text-decoration: none;
}

.file-list li > a:hover,
.file-list li > a:focus-visible {
    background: rgba(0, 129, 204, .06);
    color: var(--sga-blue);
}

.file-name {
    font-family: var(--bs-font-monospace);
    font-size: .875em;
    overflow-wrap: anywhere;
}

.file-about {
    color: #747474;
    font-size: .85em;
}

/* No description on a per-plate row: the name is the description. The size then takes
   the empty column, so every size still lines up on the right. */
.file-list li > a > .size {
    grid-column: 3;
    color: #747474;
    font-size: .85em;
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

.file-list li > a > .file-get {
    grid-column: 4;
    color: #b0b8bc;
}

.file-list li > a:hover > .file-get,
.file-list li > a:hover > .size,
.file-list li > a:focus-visible > .file-get {
    color: inherit;
}

@media (max-width: 575.98px) {
    .file-list li > a {
        grid-template-columns: minmax(0, 1fr) max-content max-content;
    }

    .file-about {
        grid-column: 1 / -1;
        grid-row: 2;
    }
}

/* Plates read under a corrected name: the same box as the files above it, one plate per
   row, the uploaded name over the one it was read as. Only the fields the correction
   changed are tinted -- everything else in the two names is, by construction, the same. */
.renames {
    list-style: none;
    margin: 0;
    padding: 0;
    background: #fff;
    border: 1px solid #e4e7e9;
    border-radius: .5rem;
}

.renames li {
    display: grid;
    grid-template-columns: max-content minmax(0, 1fr);
    gap: .15rem .75rem;
    align-items: baseline;
    padding: .5rem .85rem;
    border-top: 1px solid #eef1f2;
}

.renames li:first-child {
    border-top: 0;
}

.renames-label {
    color: #747474;
    font-size: .85em;
}

.renames code {
    overflow-wrap: anywhere;
}

.renames li > code:first-of-type {
    color: #747474;
}

.renames mark {
    padding: 0 .15em;
    border-radius: .2em;
    color: var(--sga-blue-dark);
    background: rgba(0, 129, 204, .12);
    font-weight: 700;
}

/* Folded sections: the log on a result page, the advanced settings on the form. The
   marker is drawn, because the browser's triangle cannot be styled and sits on a line of
   its own in some of them. Open on click -- or by default when there is a reason to look. */
details.fold > summary {
    list-style: none;
    cursor: pointer;
    display: flex;
    align-items: baseline;
    gap: .5rem;
}

details.fold > summary::-webkit-details-marker {
    display: none;
}

details.fold > summary::before {
    content: '';
    flex: none;
    align-self: center;
    width: .5em;
    height: .5em;
    border: solid #747474;
    border-width: 0 2px 2px 0;
    transform: rotate(-45deg);
    transition: transform .15s;
}

details.fold[open] > summary::before {
    transform: rotate(45deg);
}

details.fold > summary h5 {
    margin: 0;
}

details.log pre {
    margin-top: .75rem;
    border-radius: .5rem;
}

/* --- The scoring form ----------------------------------------------------------- */

/* A section heading with something beside it on the same baseline: the count, or the
   "Correct names" switch. */
.plates-head {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: .25rem .75rem;
    margin-bottom: .5rem;
}

/* The plates live in the same white box the result pages put their files in, so the
   left column is a solid thing on the dotted background rather than a table floating
   on it. The picker gets the box too, so the column has a shape before any file is
   picked. */
.plate-box {
    background: #fff;
    border: 1px solid #e4e7e9;
    border-radius: .5rem;
    overflow: auto;
}

.plate-box > .table {
    margin-bottom: 0;
}

.plate-box .table > thead > tr > th {
    background: #f7f9fa;
    border-bottom-color: #e4e7e9;
    font-size: .9em;
    padding: .5rem .85rem;
}

.plate-box .table > tbody > tr > td {
    padding: .4rem .85rem;
}

.plate-box .table > tbody > tr:last-child > td {
    border-bottom-width: 0;
}

.plate-box-picker {
    padding: 1rem 1.25rem;
}

/* Two short fields on one line, where the card would otherwise stack a four-item dropdown
   over a two-item one. Each keeps bootstrap_field's own bottom margin. */
.field-pair {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    gap: 0 1rem;
}

.field-pair-linkage {
    grid-template-columns: minmax(0, 9.5rem) minmax(0, 1fr);
}

/* The array picker with the replicates buttons beside it: the picker takes what the
   buttons leave, which is most of the row. */
.field-pair-array {
    grid-template-columns: minmax(0, 1fr) auto;
}

/* A two-way choice drawn as buttons, the height of the select next to it. The chosen
   one is filled; the other is an outline, so the pair reads as one control. */
.segmented {
    display: flex;
}

.segmented .btn {
    flex: 0 0 auto;
    min-width: 2.5rem;
    padding-left: .6rem;
    padding-right: .6rem;
}

.segmented .btn-check:checked + .btn {
    background-color: var(--sga-blue);
    border-color: var(--sga-blue);
    color: #fff;
}

.field-pair > p {
    grid-column: 1 / -1;
}

@media (max-width: 575.98px) {
    .field-pair, .field-pair-linkage {
        grid-template-columns: minmax(0, 1fr);
    }
}

/* The advanced settings, folded into the card. Its summary reads as a field label, since
   that is the line it sits among. */
details.advanced > summary .fold-title {
    font-weight: 600;
    color: var(--sga-ink);
    white-space: nowrap;
}

details.advanced > summary .text-muted {
    flex: 1 1 10rem;
}

details.advanced .advanced-body {
    margin-top: 1rem;
}

.form-select:disabled {
    color: #747474;
    background-color: #f2f4f5;
}

/* --- The plate table on the two forms ----------------------------------------- */

/* Read out of the file names, shown as text; the controls under each value appear only
   when the "Correct names" switch is on. Both are in the row all the time so that the
   switch is a class flip and nothing is rebuilt. */
.plate-table .plate-text {
    display: inline-block;
    padding: .25rem 0;
}

.plate-table .plate-text.missing {
    color: var(--bs-danger);
}

.plate-table:not(.editing) .form-control,
.plate-table:not(.editing) .form-select {
    display: none;
}

.plate-table.editing .plate-text {
    display: none;
}

.plate-table td {
    vertical-align: middle;
}

/* The switch itself, made hard to miss: it sits on the table's heading line, blue. */
.plate-edit .form-check-label {
    color: var(--sga-blue);
    font-weight: 700;
}

/* --- Choosing files ------------------------------------------------------------ */

/* _file_picker.html. The <input type=file> is still in the page and still focusable, so
   the keyboard, the change event and the drop all reach it; it is only kept out of sight
   behind the button, which is a <label for> pointing at it. Not display:none -- a hidden
   input cannot be tabbed to, and the focus ring drawn on the button below would never
   show. */
.file-picker-control {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: .35rem .75rem;
    min-height: 2.375rem;
}

.file-picker input[type="file"] {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    padding: 0;
    overflow: hidden;
    clip: rect(0 0 0 0);
    opacity: 0;
}

.file-picker-button {
    display: inline-flex;
    align-items: center;
    gap: .45rem;
    margin: 0;
    background: #fff;
    cursor: pointer;
}

.file-picker input[type="file"]:focus-visible + .file-picker-button {
    box-shadow: 0 0 0 .2rem rgba(0, 129, 204, .3);
}

.file-picker-status {
    color: #747474;
    font-size: .95em;
}

.file-picker-filled .file-picker-status {
    color: var(--sga-ink);
}

.file-picker.is-invalid .file-picker-button {
    --bs-btn-color: var(--bs-danger);
    --bs-btn-border-color: var(--bs-danger);
}

/* --- Dropping files ------------------------------------------------------------ */

/* The whole field is the target -- the picker above, label and help text included -- not
   the input hidden inside it. The border is drawn into a margin the field already had, so
   nothing moves when it appears -- and it appears as soon as a file is dragged anywhere
   over the page, because the place to let go should be visible before anyone has to look
   for it. */
.drop-zone {
    position: relative;
    border: 1px dashed transparent;
    border-radius: var(--bs-border-radius);
    margin: -.5rem -.5rem .5rem;
    padding: .5rem;
    /* Only the tint fades. The class is added by script after the page has painted, and a
       border-color transition then plays from the default (dark grey) to transparent -- a
       dashed box flashing round every file field on load. */
    transition: background-color .15s;
}

body.dragging-files .drop-zone {
    border-color: #7fc0e6;
    background-color: rgba(0, 129, 204, .04);
}

body.dragging-files .drop-zone::after {
    content: 'Drop files here';
    position: absolute;
    top: .35rem;
    right: .6rem;
    font-size: .8rem;
    color: var(--sga-blue);
    pointer-events: none;
}

.drop-zone.drop-zone-over {
    border-color: var(--sga-blue);
    background-color: rgba(0, 129, 204, .09);
}

/* --- The analysis page ---------------------------------------------------------- */

/* Three tabs over the whole page. Bootstrap's tabs, on the old site's colours: the
   active one is white on the dotted ground with the rule broken under it. */
.viz-tabs {
    --bs-nav-tabs-border-color: #d9dfe2;
    --bs-nav-tabs-link-active-bg: #fff;
    --bs-nav-tabs-link-active-border-color: #d9dfe2 #d9dfe2 #fff;
    margin-top: .5rem;
}

.viz-tabs .nav-link {
    color: #444;
    font-family: 'Open Sans', var(--bs-body-font-family);
    font-weight: 600;
    padding: .55rem 1rem;
}

.viz-tabs .nav-link.active {
    color: var(--sga-ink);
    box-shadow: inset 0 3px 0 var(--sga-blue);
}

.viz-tabs .nav-link .badge {
    font-weight: 600;
    color: #747474;
    border: 1px solid #e4e7e9;
    vertical-align: .05em;
}

.viz-content {
    padding-top: 1rem;
}

/* The controls, in one white strip: label over control, compact, wrapping on a phone. */
.viz-toolbar {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-end;
    gap: .6rem 1rem;
    padding: .65rem .85rem;
    margin-bottom: .75rem;
    background: #fff;
    border: 1px solid #e4e7e9;
    border-radius: .5rem;
}

.viz-field > label {
    display: block;
    margin-bottom: .2rem;
    font-size: .8em;
    font-weight: 600;
    color: #747474;
    letter-spacing: .02em;
    text-transform: uppercase;
}

.viz-field-plate {
    flex: 1 1 22rem;
    min-width: 0;
}

.viz-field-plate .form-select {
    min-width: 0;
}

.viz-field-scale .input-group {
    width: auto;
}

.viz-field-scale .form-control {
    width: 5.5rem;
    flex: 0 0 auto;
    font-variant-numeric: tabular-nums;
}

.viz-field-scale .form-control:disabled {
    background: #f2f4f5;
    color: #999;
}

.viz-switches {
    display: flex;
    flex-direction: column;
    gap: .1rem;
    padding-bottom: .1rem;
}

.viz-switches .form-check {
    margin-bottom: 0;
    font-size: .9em;
}

/* A white card per chart: a heading row, the chart, an optional foot. */
.viz-card {
    background: #fff;
    border: 1px solid #e4e7e9;
    border-radius: .5rem;
    padding: .85rem 1rem 1rem;
}

.viz-card-head {
    display: flex;
    flex-wrap: wrap;
    justify-content: space-between;
    align-items: center;
    gap: .35rem .75rem;
    margin-bottom: .5rem;
}

.viz-card-head h5 {
    font-size: 1.05rem;
}

.viz-card-foot {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: .25rem .75rem;
    margin-top: .5rem;
    font-size: .875em;
}

.viz-card-foot .btn-link {
    font-size: inherit;
    text-decoration: none;
}

.viz-card-foot .btn-link:hover {
    text-decoration: underline;
}

.viz-scroll {
    overflow-x: auto;
}

.viz-heatmap {
    display: block;
    width: 100%;
    height: auto;
}

.viz-heatmap rect.cell {
    cursor: crosshair;
}

.viz-heatmap rect.cell.hover {
    stroke: #222;
    stroke-width: 1.5;
}

.viz-heatmap rect.cell.pinned {
    stroke: var(--sga-blue);
    stroke-width: 2.5;
}

.viz-heatmap.selecting rect.cell:not(.selected) {
    opacity: .18;
}

.viz-heatmap .flag {
    pointer-events: none;
}

.viz-chart {
    display: block;
    width: 100%;
    height: auto;
    max-height: 22rem;
    user-select: none;
    -webkit-user-select: none;
}

/* The histogram is dragged across; the browser must not scroll or select instead. */
#histogram {
    touch-action: none;
    cursor: col-resize;
}

.viz-chart .axis text, .viz-heatmap .axis text {
    fill: #747474;
    font-size: 11px;
    font-family: 'PT Sans', sans-serif;
}

.viz-chart .axis line, .viz-chart .axis path {
    stroke: #ddd;
}

.viz-chart .grid line {
    stroke: #eee;
}

.viz-chart .label {
    fill: #444;
    font-size: 11px;
    font-family: 'PT Sans', sans-serif;
}

.viz-chart .brush {
    fill: rgba(0, 129, 204, .12);
    stroke: var(--sga-blue);
    stroke-width: 1;
}

.viz-chart .pt {
    cursor: pointer;
}

.viz-chart .pt.dim {
    opacity: .12;
}

.viz-chart .pt.hover {
    stroke: #222;
    stroke-width: 1.5;
}

.viz-legend {
    display: flex;
    align-items: center;
    gap: .4rem;
    font-size: .8rem;
    color: #747474;
    font-variant-numeric: tabular-nums;
}

.viz-legend .ramp {
    display: flex;
}

.viz-legend .ramp span {
    width: 14px;
    height: 12px;
}

.viz-legend .ramp span:first-child {
    border-radius: 2px 0 0 2px;
}

.viz-legend .ramp span:last-child {
    border-radius: 0 2px 2px 0;
}

.viz-tooltip {
    position: fixed;
    z-index: 20;
    pointer-events: none;
    background: #222;
    color: #fff;
    padding: .4rem .55rem;
    border-radius: .25rem;
    font-size: .8rem;
    line-height: 1.4;
    max-width: 17rem;
}

.viz-tooltip b {
    color: #fff;
}

.viz-tooltip .muted {
    color: #aaa;
}

/* The photograph: a canvas kept at the plate's own proportions, with its controls in
   the card's head. */
.viz-photo-tools {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: .4rem;
}

.viz-photo-wrap {
    position: relative;
    background: #1b1b1b;
    border-radius: .35rem;
    overflow: hidden;
}

.viz-photo {
    display: block;
    width: 100%;
    aspect-ratio: 3 / 2;
    cursor: crosshair;
    touch-action: none;
}

.viz-photo.panning {
    cursor: grabbing;
}

.viz-photo-empty {
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 1.5rem;
    text-align: center;
    color: #bbb;
    font-size: .9rem;
}

/* The pinned colony: close-up on the left, facts on the right. */
.viz-colony {
    display: flex;
    flex-wrap: wrap;
    gap: 1rem 1.5rem;
    align-items: flex-start;
}

.viz-crop {
    flex: 0 0 auto;
    border: 1px solid #e4e7e9;
    border-radius: .35rem;
    background: #1b1b1b;
}

.viz-colony .facts {
    flex: 1 1 16rem;
    grid-template-columns: max-content minmax(0, 1fr) max-content minmax(0, 1fr);
}

@media (max-width: 575.98px) {
    .viz-colony .facts {
        grid-template-columns: max-content minmax(0, 1fr);
    }
}

/* Replicate positions: the 2x2 block, each cell its mean. */
.viz-quadrants {
    display: grid;
    grid-template-columns: repeat(2, 4.5rem);
    gap: 3px;
    margin: .25rem 0 .75rem;
}

.viz-quadrants div {
    padding: .5rem 0;
    text-align: center;
    font-variant-numeric: tabular-nums;
    font-size: .95em;
    border-radius: .25rem;
    color: #222;
}

.viz-quadrants small {
    display: block;
    font-size: .7em;
    color: #555;
    text-transform: uppercase;
    letter-spacing: .03em;
}

/* Tables of colonies and hits. */
.viz-table .table {
    margin-bottom: 0;
    font-size: .9em;
}

.viz-table th {
    white-space: nowrap;
}

.viz-table td.num, .viz-table th.num {
    text-align: right;
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

.viz-table td .status {
    display: inline-block;
    padding: 0 .35em;
    border-radius: .25em;
    background: #f0efec;
    color: #555;
    font-size: .85em;
    font-family: var(--bs-font-monospace);
}

.viz-table a code {
    color: var(--sga-blue);
}

/* The quality table: a tinted bar behind numbers that are a ratio to 1. */
.viz-qc td.bar {
    position: relative;
    background-image: linear-gradient(to right, rgba(0, 129, 204, .16), rgba(0, 129, 204, .16));
    background-repeat: no-repeat;
    background-size: var(--w, 0) 100%;
}

.viz-qc tr.control td:first-child {
    color: #747474;
}

.viz-qc td.codes {
    white-space: nowrap;
}

.viz-qc td.codes .status {
    margin-right: .25em;
}

.viz-qc td.type {
    font-size: .8em;
    text-transform: uppercase;
    letter-spacing: .03em;
    color: #747474;
}

.viz-defs {
    display: grid;
    grid-template-columns: max-content minmax(0, 1fr);
    gap: .15rem .75rem;
    margin: .75rem 0 0;
}

.viz-defs dt {
    font-weight: 600;
}

.viz-defs dd {
    margin: 0;
}

/* Every plate at once. */
.viz-multiples {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(11rem, 1fr));
    gap: .75rem;
}

.viz-multiples a {
    display: block;
    color: inherit;
    text-decoration: none;
    border: 1px solid #eef1f2;
    border-radius: .35rem;
    padding: .4rem;
}

.viz-multiples a:hover {
    border-color: var(--sga-blue);
    text-decoration: none;
}

.viz-multiples svg {
    display: block;
    width: 100%;
    height: auto;
}

.viz-multiples .name {
    display: block;
    margin-top: .3rem;
    font-family: var(--bs-font-monospace);
    font-size: .72em;
    color: #444;
    overflow-wrap: anywhere;
}

.viz-multiples .sub {
    display: block;
    font-size: .75em;
    color: #747474;
}

/* Headline numbers on the interactions tab. */
.viz-kpis {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr));
    gap: .75rem;
    margin-bottom: .75rem;
}

.viz-kpi {
    background: #fff;
    border: 1px solid #e4e7e9;
    border-radius: .5rem;
    padding: .6rem .85rem;
}

.viz-kpi b {
    display: block;
    font-family: 'Open Sans', var(--bs-body-font-family);
    font-size: 1.5rem;
    font-weight: 600;
    line-height: 1.2;
    color: var(--sga-ink);
    font-variant-numeric: tabular-nums;
}

.viz-kpi span {
    font-size: .8em;
    color: #747474;
}

.viz-kpi.neg b { color: #1c5cab; }
.viz-kpi.pos b { color: #9e3432; }

.viz-gp-note {
    flex: 1 1 100%;
    line-height: 1.35;
}

.viz-downloads .btn {
    font-family: var(--bs-font-monospace);
    font-size: .8em;
}

/* --- Keyboard and motion ------------------------------------------------------- */

/* Invisible until tabbed to, then a small button over the top-left corner. */
.skip-link {
    position: absolute;
    top: -100%;
    left: .5rem;
    z-index: 1100;
    padding: .5rem .75rem;
    background: #fff;
    border: 1px solid var(--sga-blue);
    border-radius: var(--bs-border-radius);
}

.skip-link:focus {
    top: .5rem;
}

@media (prefers-reduced-motion: reduce) {
    .progress-bar-animated {
        animation: none;
    }

    .drop-zone {
        transition: none;
    }
}
