/**
 * Scout Dependency Engine - application stylesheet.
 *
 * Layered on top of Bootstrap 5 rather than replacing it. Rules here cover
 * only what Bootstrap does not: the skip link, dependency-graph presentation
 * and print output.
 *
 * Conventions
 * -----------
 *  - Custom classes are prefixed `sde-` so they never collide with Bootstrap.
 *  - Colours are declared as CSS custom properties in one place, so a future
 *    theme change is a single edit.
 *  - Nothing here relies on colour alone to convey meaning (WCAG 1.4.1);
 *    node and edge types also differ in shape or line style.
 */

:root {
    /* Node semantics. Contrast ratios against white are >= 4.5:1. */
    --sde-node-required: #0a58ca;
    --sde-node-optional: #6c757d;
    --sde-node-blocked: #b02a37;
    --sde-node-complete: #146c43;

    /* Edge semantics. */
    --sde-edge-requires: #495057;
    --sde-edge-alternative: #6f42c1;
    --sde-edge-parallel: #0f766e;

    --sde-focus-ring: #0d6efd;
}

/* ---------------------------------------------------------------------------
   Focus and keyboard navigation

   A visible focus indicator is required by WCAG 2.4.7. Bootstrap supplies one
   for its own components; this guarantees it everywhere else, including SVG
   graph nodes, which browsers style inconsistently.
   --------------------------------------------------------------------------- */

:focus-visible {
    outline: 3px solid var(--sde-focus-ring);
    outline-offset: 2px;
}

.skip-link {
    position: absolute;
    top: 0.5rem;
    left: 0.5rem;
    z-index: 1080;
    padding: 0.5rem 1rem;
    background-color: #fff;
    border: 2px solid var(--sde-focus-ring);
    border-radius: 0.25rem;
    text-decoration: none;
}

/* The skip link targets <main>, which needs tabindex="-1" to receive focus.
   Suppress the resulting outline on the container itself. */
main:focus {
    outline: none;
}

/* ---------------------------------------------------------------------------
   The brand mark

   The logo is dark green and gold on transparency, so it is illegible against
   the dark navbar it sits in. It is given a white plate of its own rather than
   a lightened navbar, because the navbar's contrast with its links and the
   logo's contrast with its own background are two separate requirements and
   solving them together means compromising both.

   Height is set rather than width: the file is 459 x 100, and at full height
   it is wider than a small phone. Below the md breakpoint it drops to 44px
   (202px wide), which fits alongside the toggler on a 320px screen. `width:
   auto` with both HTML attributes present keeps the aspect ratio while still
   reserving the right box before the image arrives.
   --------------------------------------------------------------------------- */

.brand-plate {
    display: inline-flex;
    padding: 0.25rem 0.5rem;
    background-color: #fff;
    border-radius: 0.375rem;
}

.brand-logo {
    height: 44px;
    width: auto;
}

@media (min-width: 768px) {
    .brand-logo {
        height: 100px;
    }
}

/* ---------------------------------------------------------------------------
   Rendered prose

   The output of Scout\Domain\Content\Prose: a topic's body, a step's
   description. Only the tags on that class's allowlist can appear here, so
   this is a short and closed set of rules rather than a defensive one.

   The measure is capped because long lines are hard to track back to the start
   of, and this is content people read rather than scan. Bootstrap sets no
   maximum, so it is set here.
   --------------------------------------------------------------------------- */

.topic-body {
    max-width: 42rem;
}

.topic-body > :last-child {
    margin-bottom: 0;
}

.topic-body h2,
.topic-body h3,
.topic-body h4 {
    margin-top: 1.75rem;
    margin-bottom: 0.5rem;
    font-size: 1.15rem;
    font-weight: 600;
}

.topic-body blockquote {
    padding-left: 1rem;
    border-left: 3px solid var(--bs-border-color);
    color: var(--bs-secondary-color);
}

.topic-body pre {
    padding: 0.75rem;
    border-radius: 0.375rem;
    background: var(--bs-tertiary-bg);
    overflow-x: auto;
}

.topic-body table {
    width: 100%;
    margin-bottom: 1rem;
}

.topic-body th,
.topic-body td {
    padding: 0.4rem 0.6rem;
    border: 1px solid var(--bs-border-color);
    text-align: left;
    vertical-align: top;
}

/* ---------------------------------------------------------------------------
   Print

   Every dependency graph generates a printable checklist. Printing is a
   first-class output, not an afterthought: troops carry paper into the field,
   and a plan pinned to a noticeboard is read by more people than the page ever
   will be.

   This is also the whole of the PDF story. A plan resolved against a reader's
   answers is a self-contained document that the browser already knows how to
   turn into a PDF, and adding a rendering library to produce a second, slightly
   different copy of the same page would be a dependency, a memory ceiling on
   shared hosting, and a layout to keep in step with this one. What was owed
   instead was making the printed page genuinely good, which is what the rules
   below are for.
   --------------------------------------------------------------------------- */

/* ---------------------------------------------------------------------------
   Reading a diagram that is bigger than the page

   GraphLayout draws at a fixed scale — 220px boxes on a 260px pitch — so width
   is decided by the widest stage and not by the window. A plan of forty-four
   steps whose widest stage holds thirteen of them is 3,388px across. Fitted to
   a 700px reading column that is 21% of full size, and the 13px labels are
   drawn at under three pixels: present, printable, and unreadable.

   Fitting stays the default, because a page that scrolls sideways the moment it
   loads is worse than one that starts small. What is added is a way out of it:
   four sizes, and the scroll box the diagram already sat in does the rest.

   All of this is CSS. The chosen size is a radio button and the rules below key
   off `:has()`, which means it needs no JavaScript, survives scripting being
   turned off, and keeps the browser as the only thing that knows how big the
   picture currently is. A browser without `:has()` ignores these rules and
   shows the fitted diagram it showed before.
   --------------------------------------------------------------------------- */

/* Fitting the column is a stylesheet rule rather than an inline style on the
   <svg>, because an inline style wins against a stylesheet and the zoom levels
   below have to be able to switch the fit off. */
.graph-diagram > svg {
    max-width: 100%;
    height: auto;
}

/* The layout's own width in pixels, set inline by the diagram partial. The
   fallback is relative to the box rather than absolute, so that a viewer which
   somehow arrives without it still scales by something sane — 100% of the
   column, then half as much again — instead of collapsing to nothing. */
.graph-diagram-viewer {
    --diagram-width: 100%;
}

.graph-diagram-viewer:has(.graph-diagram-zoom[value="1"]:checked) > .graph-diagram > svg,
.graph-diagram-viewer:has(.graph-diagram-zoom[value="1.5"]:checked) > .graph-diagram > svg,
.graph-diagram-viewer:has(.graph-diagram-zoom[value="2"]:checked) > .graph-diagram > svg {
    max-width: none;
    height: auto;
}

.graph-diagram-viewer:has(.graph-diagram-zoom[value="1"]:checked) > .graph-diagram > svg {
    width: var(--diagram-width);
}

.graph-diagram-viewer:has(.graph-diagram-zoom[value="1.5"]:checked) > .graph-diagram > svg {
    width: calc(var(--diagram-width) * 1.5);
}

.graph-diagram-viewer:has(.graph-diagram-zoom[value="2"]:checked) > .graph-diagram > svg {
    width: calc(var(--diagram-width) * 2);
}

/* Zoomed in, the box is capped and scrolls in both directions — otherwise
   choosing 200% on a tall plan pushes everything after the diagram five
   thousand pixels down the page and the reader has lost the checklist. It is
   left resizable, because how much screen to give a diagram is a judgement
   nobody but the reader can make. */
.graph-diagram-viewer:has(.graph-diagram-zoom:not([value="fit"]):checked) > .graph-diagram {
    max-height: 75vh;
    resize: vertical;
}

/* Dragging to pan. The cursor is the only thing that says the box can be moved
   by hand, and it is applied by the script rather than by the server so that it
   never appears where nothing will happen. */
.graph-diagram[data-graph-pan].is-pannable {
    cursor: grab;
}

.graph-diagram[data-graph-pan].is-panning {
    cursor: grabbing;
    user-select: none;
}

/* ---------------------------------------------------------------------------
   Full screen

   Keyed off a class the script sets on `fullscreenchange` rather than off
   `:fullscreen`, because the prefixed and unprefixed selectors cannot share a
   rule — a browser that does not understand one of them discards the whole
   block — and one class avoids writing everything twice.

   A full-screen element has no page behind it, so it has to paint its own
   background or the diagram is drawn on black.
   --------------------------------------------------------------------------- */

.graph-diagram-viewer.is-fullscreen {
    padding: 1rem;
    background-color: var(--bs-body-bg, #fff);
    overflow: auto;
}

.graph-diagram-viewer.is-fullscreen > .graph-diagram {
    max-height: calc(100vh - 7rem);
}

/* ---------------------------------------------------------------------------
   The visual editor's canvas

   Only ever seen on the arrange screen, and only when the signed-in account may
   edit the version — the classes below are attached by the server for a canvas
   that can be changed, and the handle is created by the script that does the
   dragging. A diagram nobody may rearrange carries none of this, so it never
   offers a grab cursor for something that will not move.
   --------------------------------------------------------------------------- */

.graph-diagram[data-graph-canvas] .graph-diagram-node {
    cursor: grab;
}

.graph-diagram[data-graph-canvas] .graph-diagram-node.is-dragging {
    cursor: grabbing;
}

.graph-diagram[data-graph-canvas] .graph-diagram-node.is-dragging .graph-diagram-box {
    /* The box being moved has to be distinguishable from the forty it is being
       moved past, and it is being looked at directly, so a slight shadow is
       enough. */
    filter: drop-shadow(0 2px 4px rgba(0, 0, 0, 0.35));
}

/* Focus has to be visible on a box reached with the Tab key, because the arrow
   keys then move that box and nothing else says which one they will move. */
.graph-diagram[data-graph-canvas] .graph-diagram-node a:focus-visible .graph-diagram-box {
    outline: 3px solid var(--bs-focus-ring-color, #0d6efd);
    outline-offset: 2px;
}

/* The handle a connection is dragged from. Faint until the box is approached,
   so forty of them do not read as part of the diagram. */
.graph-diagram-handle {
    fill: var(--bs-primary, #0d6efd);
    opacity: 0;
    cursor: crosshair;
    transition: opacity 0.12s ease-in-out;
}

.graph-diagram-node:hover .graph-diagram-handle,
.graph-diagram-node:focus-within .graph-diagram-handle {
    opacity: 0.85;
}

/* The line being dragged out of a handle: dashed, because it is not a
   relationship yet. */
.graph-diagram-linking {
    stroke: var(--bs-primary, #0d6efd);
    stroke-width: 2;
    stroke-dasharray: 6 4;
    fill: none;
    pointer-events: none;
}

@media print {
    header nav,
    footer,
    .skip-link,
    .breadcrumb,
    .pagination,
    .sde-no-print {
        display: none !important;
    }

    body {
        font-size: 11pt;
    }

    a[href]::after {
        /* Printed links are useless without their destination. */
        content: " (" attr(href) ")";
        font-size: 9pt;
        word-break: break-all;
    }

    /* Except where the destination is already on the paper, or is this page. */
    a[href^="#"]::after,
    .list-group a[href]::after,
    nav a[href]::after {
        content: "";
    }

    /* Keep a task and its prerequisites on the same sheet where possible. */
    .sde-checklist-item,
    .card,
    li {
        break-inside: avoid;
    }

    h1, h2, h3 {
        break-after: avoid;
    }

    /* A card's frame is chrome on screen and ink on paper. */
    .card {
        border: 0 !important;
        background: transparent !important;
    }

    /* A tick box has to be printable as an empty box somebody can pen in. */
    .bi-square::before {
        content: "\2610";
    }

    .bi-check-square-fill::before {
        content: "\2611";
    }

    /* Badges are colour on screen; on paper they need an outline to read as
       labels rather than as stray words. */
    .badge {
        border: 1pt solid #666 !important;
        background: transparent !important;
        color: #000 !important;
    }

    /* The diagram is the reason this stylesheet exists. It must not be cut in
       half by a page break, and it must fit the width of the sheet.

       The overrides are heavy-handed on purpose. Zoom is a screen affordance:
       whatever size the reader chose to read it at, and whatever height the box
       was capped or dragged to, paper gets the whole diagram fitted to the
       sheet. A printout cropped at 75% of the viewport height would lose the
       last stages of the plan without saying so. */
    .graph-diagram {
        break-inside: avoid;
        max-width: 100%;
        max-height: none !important;
        overflow: visible !important;
        resize: none !important;
    }

    .graph-diagram svg {
        width: auto !important;
        max-width: 100% !important;
        height: auto !important;
    }

    /* The connection handles are a pointer affordance and nothing else. On
       paper they are forty blue dots with no meaning. */
    .graph-diagram-handle,
    .graph-diagram-linking {
        display: none !important;
    }
}

/* ---------------------------------------------------------------------------
   Reduced motion

   Graph layout animation is decorative; honour the user's preference.
   --------------------------------------------------------------------------- */

@media (prefers-reduced-motion: reduce) {
    *,
    *::before,
    *::after {
        animation-duration: 0.01ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: 0.01ms !important;
    }
}
