/* Shared fleet styles — one home for the DW-001 system every fleet
 * frontend consumes. Extracted verbatim from the blog's app.css (itself
 * ported from the retired Rails app); per-app stylesheets now hold only
 * what differs. Rules here change once, everywhere. */

:root {
    /* Tells the browser to render built-in controls appropriately for the
       active color scheme. */
    color-scheme: light dark;

    --bg: #f7f2e9;            /* warm cream paper */
    --fill: #fffdf8;          /* slightly lighter surface for fields and code */
    --text: #1f1a12;          /* warm near-black ink */
    --accent: #0066b1;
    --accent-contrast: #ffffff;
    --ink: #1f1a12;           /* button outlines and hard shadows */
    --good: #2f6f3e;

    /* These two DEPART from the shared fleet palette, deliberately.
       DW-001 §2 records that the canonical light-mode values fail WCAG AA for
       normal text — --text-muted #7a7264 is 4.26:1 on the paper and --danger
       #dc2626 is 4.33:1, both under the 4.5 threshold, both carrying real
       content at 0.7rem. The other three apps are read over the tailnet by one
       person who can pick his own lighting; this one is read by strangers on
       whatever screen they have. So it ships the corrected values the guide
       recommends: 5.00:1 and 5.14:1, same hue family, no visible change in
       character.

       This makes a fourth palette variant in a system whose guide already
       complains about drift (DW-001 §9). The intended repair is to move the
       other three apps onto these values, not to keep a special case here. */
    --text-muted: #6f675a;
    --danger: #c81e1e;

    --mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, monospace;
}

@media (prefers-color-scheme: dark) {
    :root {
        --bg: #121316;         /* cool charcoal, blue undertone, not warm */
        --fill: #1c1e22;       /* same family, one step lighter */
        --text: #eceef1;       /* neutral paper-white, drop the warm tint */
        --text-muted: #8b9199; /* cool gray — already 5.85:1, unchanged */
        --accent: #4d9de0;     /* dashboard backlight blue */
        --accent-contrast: #141311;
        --danger: #f87171;     /* already 6.72:1, unchanged */
        --ink: #000000;
        --good: #5cb974;
    }
}
/* Shared fleet styles — one home for the DW-001 system every fleet
 * frontend consumes. Extracted verbatim from the blog's app.css (itself
 * ported from the retired Rails app); per-app stylesheets now hold only
 * what differs. Rules here change once, everywhere. */

/* Document shell: base, headings, lists, prose. */

body {
    margin: 0;
    font-family: system-ui, sans-serif;
    font-size: 1rem;
    line-height: 1.6;
    color: var(--text);
    background: var(--bg);
}

/* This app is prose end to end, so every page keeps the notes app's original
   40rem measure via .measure — there is no table here that needs the room.

   G1 (guide §11): the masthead opens with a tall empty rise — the nave's
   first void. */
.container {
    max-width: 64rem;
    margin: 0 auto;
    padding: 6rem 1.25rem 5rem;
}

.measure {
    max-width: 40rem;
}
/* --- Headings --- */

h1 {
    font-family: ui-serif, Georgia, serif;
    font-size: 2rem;
    letter-spacing: -0.02em; /* tight tracking on large headings */
    line-height: 1.4;        /* G1: tall vertical rhythm */
    margin: 0;
}

/* G11 blackletter masthead (guide §11) — the voice for the title that opens a
   page. Fraktur is nearly unreadable at length, so the discipline is
   self-enforcing; it degrades to the serif if the font cannot load. */
.incipit {
    max-width: 26rem;
    font-family: "UnifrakturCook", "UnifrakturMaguntia", "Old English Text MT", ui-serif, Georgia, serif;
}

/* G5 illuminated initial (extension of rule 1) — the page's one ornament.
   Ink on a fill panel, never accent: this is not a control. */
.incipit::first-letter {
    float: left;
    font-size: 4.75rem;
    line-height: 0.8;
    padding: 0.3rem 0.55rem;
    margin: 0.1rem 0.6rem 0 0;
    background: var(--fill);
    border-radius: 10px;
    color: var(--text);
}

.incipit::after { content: ""; display: block; clear: both; }

h2 {
    font-family: ui-serif, Georgia, serif;
    font-size: 1.35rem;
    font-weight: 600;
    letter-spacing: -0.01em;
    line-height: 1.4;
    margin: 6rem 0 0.75rem;  /* G1: the tall void between major sections */
}

/* --- Prose (extension of rule 1) ---
   The rendered body of a post. Everything here is separated by space and a
   shift in type; nothing gets a box, a rule, or a border. */

.prose {
    font-size: 1.05rem;
    line-height: 1.7;
}

.prose > *:first-child { margin-top: 0; }
.prose > *:last-child { margin-bottom: 0; }

.prose p {
    margin: 0 0 1.25rem;  /* G1: prose gaps widened */
}

.prose h2 {
    font-size: 1.35rem;
    margin: 2.5rem 0 0.75rem;
}

.prose h3 {
    font-family: ui-serif, Georgia, serif;
    font-size: 1.1rem;
    font-weight: 600;
    margin: 2rem 0 0.5rem;
}

.prose a {
    color: var(--accent);
}

.prose ul,
.prose ol {
    margin: 0 0 1.15rem;
    padding-left: 1.2rem;
}

.prose li + li {
    margin-top: 0.4rem;
}

/* A quotation, set off by space and a change of voice rather than the
   conventional left border — rule 1 does not make an exception for
   blockquotes. The indent and the muted serif do the work a rule would have.

   `border: none` is load-bearing: without it the quotation grows a divider
   the design does not allow. */
.prose blockquote {
    margin: 1.5rem 0 1.5rem 1.25rem;
    padding-left: 0;
    border: none;
    font-family: ui-serif, Georgia, serif;
    font-size: 1.05rem;
    color: var(--text-muted);
}

/* Rule 1 again: an <hr> in markdown is a section break, and here it is spelled
   as space rather than as a line. */
.prose hr {
    border: none;
    margin: 3rem 0;
}

/* Prose tables follow the DW-001 table treatment: no rules, no zebra striping,
   headers as instrumentation, figures tabular. */
.prose table,
.prose tr,
.prose th,
.prose td {
    border: none;
}

.prose table {
    width: 100%;
    border-collapse: collapse;
    margin: 0 0 1.15rem;
    font-variant-numeric: tabular-nums;
    font-size: 0.95rem;
}

/* Plain `th`, not `thead th`: it matches header cells whether or not a
   `<thead>` wrapper survives the sanitizer, where a `thead th` selector
   would silently never match a thead-less table. */
.prose th {
    font-size: 0.7rem;
    font-weight: 600;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--text-muted);
    text-align: left;
    padding: 0 1rem 0.6rem 0;
    white-space: nowrap;
}

.prose td {
    padding: 0.4rem 1rem 0.4rem 0;
    vertical-align: top;
}

/* Inline code takes the monospace face and nothing else — no fill, no border,
   no pill. Rule 1: a background here would box a phrase mid-sentence.
   The `:not(pre code)` keeps the block treatment below intact. */
.prose code:not(pre code) {
    font-family: var(--mono);
    font-size: 0.9em;
    background: none;
    padding: 0;
    border: none;
    border-radius: 0;
}
/* --- Code blocks (extension of rule 1) ---
   Program text is a distinct plane, marked by the fill surface and the
   monospace face rather than by a border. It scrolls itself; the page never
   scrolls sideways. */

.prose pre {
    margin: 0 0 1.15rem;
    padding: 1rem;
    background: var(--fill);
    border-radius: 10px;
    overflow-x: auto;
    font-size: 0.8rem;
    line-height: 1.55;
}

.prose pre code {
    font-size: inherit;
}
/* Shared fleet styles — one home for the DW-001 system every fleet
 * frontend consumes. Extracted verbatim from the blog's app.css (itself
 * ported from the retired Rails app); per-app stylesheets now hold only
 * what differs. Rules here change once, everywhere. */

/* --- Masthead --- */

.masthead {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 1rem;
    flex-wrap: wrap;
    margin-bottom: 3rem;
}

.wordmark {
    font-family: ui-serif, Georgia, serif;
    font-size: 1.1rem;
    font-style: italic;
    color: var(--text);
    text-decoration: none;
}

.masthead-nav {
    display: flex;
    align-items: baseline;
    gap: 1rem;
    font-size: 0.85rem;
}

.masthead-nav a { color: var(--text-muted); }
.masthead-nav a:hover { color: var(--accent); }
/* Shared fleet styles — one home for the DW-001 system every fleet
 * frontend consumes. Extracted verbatim from the blog's app.css (itself
 * ported from the retired Rails app); per-app stylesheets now hold only
 * what differs. Rules here change once, everywhere. */

/* --- Buttons: the only elements with depth (rule 2) --- */

.button,
input[type="submit"] {
    display: inline-block;
    padding: 0.6rem 1.1rem;  /* yields ~44px height: a proper touch target */
    font: inherit;
    font-weight: 600;
    color: var(--accent-contrast);
    background: var(--accent);
    border: 2px solid var(--ink);
    border-radius: 10px;
    box-shadow: 3px 3px 0 var(--ink);  /* solid offset shadow: a physical key */
    cursor: pointer;
    text-decoration: none;   /* links are underlined by default */
    transition: transform 0.15s cubic-bezier(0.2, 0.8, 0.2, 1),  /* damped (rule 6) */
                box-shadow 0.15s cubic-bezier(0.2, 0.8, 0.2, 1);
}

.button:hover,
input[type="submit"]:hover {
    filter: brightness(0.95);
}

/* The press: the key slides down into its own shadow. */
.button:active,
input[type="submit"]:active {
    transform: translate(3px, 3px);
    box-shadow: 0 0 0 var(--ink);
    filter: none;
}

.button--secondary {
    color: var(--accent);
    background: var(--fill);
}

.button--danger {
    color: var(--danger);
    background: var(--fill);
}

/* button_to wraps its input in its own <form class="button_to">, which is
   block-level by default and would break an inline action row. */
.button_to {
    display: inline;
    margin: 0;
}

.actions {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 0.75rem;
    margin-top: 2rem;
}
/* --- Back link --- */

.back-link {
    display: inline-block;
    color: var(--accent);
    text-decoration: none;
    font-weight: 600;
}

.back-link:hover {
    text-decoration: underline;
}
/* Shared fleet styles — one home for the DW-001 system every fleet
 * frontend consumes. Extracted verbatim from the blog's app.css (itself
 * ported from the retired Rails app); per-app stylesheets now hold only
 * what differs. Rules here change once, everywhere. */

/* --- Flash messages: text in flow, never a box (rule 1) --- */

.flash {
    margin: 0 0 1.5rem;
    font-weight: 600;
}

.flash--alert { color: var(--danger); }
.flash--notice { color: var(--text-muted); }
/* Shared fleet styles — one home for the DW-001 system every fleet
 * frontend consumes. Extracted verbatim from the blog's app.css (itself
 * ported from the retired Rails app); per-app stylesheets now hold only
 * what differs. Rules here change once, everywhere. */

/* --- Forms: filled fields, no borders (rule 2) --- */

.field {
    margin-bottom: 1.25rem;
}

label {
    display: block;          /* labels are inline by default */
    font-weight: 600;
    margin-bottom: 0.25rem;
}

input[type="text"],
textarea {
    width: 100%;
    padding: 0.75rem 1rem;
    font: inherit;           /* form elements don't inherit the page font */
    color: var(--text);
    background: var(--fill);
    border: none;
    border-radius: 10px;
}

input[type="text"]:focus,
textarea:focus {
    outline: 2px solid var(--accent);
    outline-offset: 1px;
}

.field-hint {
    margin: 0.35rem 0 0;
    font-size: 0.8rem;
    color: var(--text-muted);
}

.form-errors {
    list-style: none;
    padding: 0;
    margin: 0 0 1.25rem;
    color: var(--danger);
    font-weight: 600;
}
/* Shared fleet styles — one home for the DW-001 system every fleet
 * frontend consumes. Extracted verbatim from the blog's app.css (itself
 * ported from the retired Rails app); per-app stylesheets now hold only
 * what differs. Rules here change once, everywhere. */

/* Metadata as instrumentation (rule 5). Styles for the Meta component. */

.meta {
    margin: 0.2rem 0 0;
    font-size: 0.7rem;
    font-weight: 600;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    font-variant-numeric: tabular-nums;
    color: var(--text-muted);
}

.meta a { color: var(--text-muted); }
.meta a:hover { color: var(--accent); }
/* Shared fleet styles — one home for the DW-001 system every fleet
 * frontend consumes. Extracted verbatim from the blog's app.css (itself
 * ported from the retired Rails app); per-app stylesheets now hold only
 * what differs. Rules here change once, everywhere. */

/* Empty states say it plainly (rule 1). Styles for the EmptyState component. */

.empty-state {
    margin-top: 4rem;
    text-align: center;
    font-size: 1.1rem;
    color: var(--text-muted);
}
/* Shared fleet styles — one home for the DW-001 system every fleet
 * frontend consumes. Extracted verbatim from the blog's app.css (itself
 * ported from the retired Rails app); per-app stylesheets now hold only
 * what differs. Rules here change once, everywhere. */

/* Short machine ids: the monospace voice, and nothing else. Alignment
 * carries meaning, so an id never sets in prose type. Styles for the Mono
 * component. */

.mono {
    font-family: var(--mono);
    font-size: 0.85em;
}
/* Shared fleet styles — one home for the DW-001 system every fleet
 * frontend consumes. Rules here change once, everywhere. */

/* Status badges (extension of rule 5). The label carries the meaning on its
   own — colour is a second signal, never the only one, so this still reads
   correctly in monochrome and to a colourblind reader. Moved here from the
   blog's app.css with the markup; the selectors are renamed from `.state*`
   to the generic badge. */

.badge {
    font-weight: 700;
    letter-spacing: 0.1em;
}

.badge--muted { color: var(--text-muted); }
.badge--good { color: var(--good); }
/* Shared fleet styles — one home for the DW-001 system every fleet
 * frontend consumes. Rules here change once, everywhere. */

/* A page title beside its primary action. */
.page-header {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 1rem;
    margin-bottom: 1.5rem;
}
/* --- Index lists: whitespace-only separation (rule 1) --- */

.posts {
    list-style: none;
    padding: 0;
    margin: 0;
}

.post-item {
    padding: 0.75rem 0;
}

/* The gap between entries replaces the divider. */
.post-item + .post-item {
    margin-top: 1.25rem;
}

.post-item-title {
    display: block;
    font-family: ui-serif, Georgia, serif;
    font-size: 1.2rem;
    font-weight: 600;
    letter-spacing: -0.01em;
    color: var(--text);
    text-decoration: none;
}

.post-item-title:hover {
    color: var(--accent);
    text-decoration: underline;
}

.post-item-summary {
    margin: 0.3rem 0 0;
    color: var(--text-muted);
}
/* Shared fleet styles — one home for the DW-001 system every fleet
 * frontend consumes. Rules here change once, everywhere. */

/* --- The article --- */

.post-header {
    margin-bottom: 2rem;
}

.post-footer {
    margin-top: 3.5rem;
}
/* Shared fleet styles — one home for the DW-001 system every fleet
 * frontend consumes. Rules here change once, everywhere. */

/* Plain-text bodies keep their paragraphs (rule 1: space, not markup). */
.pre-line {
    white-space: pre-line;
}
/* Shared fleet styles — one home for the DW-001 system every fleet
 * frontend consumes. Rules here change once, everywhere. */

/* The gargoyle (DW-001 §7): the 404's one allowed moment of playfulness — a
   stone grotesque drawn in muted ink, the only ornament on the page. Moved
   here from the retired static 404 (docs/deepwater-404.html, DW-404) with
   the markup. He sits left-aligned with the honest line under him; the old
   page centered the whole composition, but this body renders inside the
   normal page flow beside admin content, where centering would read as an
   accident. */
.gargoyle {
    margin: 2.5rem 0;
    width: 10rem;
    height: 10rem;
    color: var(--text-muted);
}
.gargoyle svg {
    width: 100%;
    height: 100%;
    fill: none;
    stroke: currentColor;
    stroke-width: 2.5;
    stroke-linecap: round;
    stroke-linejoin: round;
}
/* Shared fleet styles — one home for the DW-001 system every fleet
 * frontend consumes. Rules here change once, everywhere. */

/* Recorded component trees: components only, no bullets, no dividers — the
   indent does the work (rule 1). Names ride the monospace voice; props sit
   beside them as instrumentation. */
ul.tree {
    list-style: none;
    padding: 0;
    margin: 0;
}
ul.tree ul.tree {
    padding-left: 1.5rem;
    margin-top: 0.25rem;
}
.tree-node {
    padding: 0.15rem 0;
}
.tree-node .meta {
    margin-left: 0.5rem;
}

/* App diagrams: static SVG, so every paint rule lives here rather than in
   attributes. Boxes sit on the fill surface; names set in the page voices
   (serif pages, monospace components); usage curves are muted ink. The page
   boxes link to their drill-down and take the accent voice on hover, like
   any other link. */
.tgraph {
    max-width: 100%;
    height: auto;
    display: block;
    margin: 1.5rem 0;
}
.tgraph .node-box {
    fill: var(--fill);
}
.tgraph text {
    fill: var(--text);
    font-size: 12.5px;
}
.tgraph .page-label {
    font-family: ui-serif, Georgia, serif;
}
.tgraph .comp-label {
    font-family: var(--mono);
}
.tgraph .edge {
    fill: none;
    stroke: var(--text-muted);
    stroke-width: 2;
}
.tgraph .tnode-link {
    text-decoration: none;
}
.tgraph .tnode-link:hover .node-box {
    stroke: var(--accent);
    stroke-width: 2;
}
.tgraph .tnode-link:hover text {
    fill: var(--accent);
}
