/*
 * TireFire Industries — content-site livery ("bubbles").
 *
 * The variant of the TFI livery used by the prose/marketing Jekyll sites
 * (arrdem.com, flowmetal.io, tirefireind.us, joe.tirefireind.us) — as opposed
 * to the dashboard/service livery in tfi.css. This is the single source for
 * that look: it's a faithful port of what those sites used to vendor
 * per-repo under public/css/ (tfi.css + colors + fonts + bubbles-border +
 * rouge; bubbles-shadow.css and notes.css were dead files and are dropped).
 *
 * Fonts are served from cairn (ns/tfi) instead of the old hard-coded
 * https://flowmetal.io/public/font/ references, so every site loads one
 * immutable, CORS-enabled copy. Published via tools/publish.py.
 *
 * Load a content site with a single <link> to this file's cairn URL; it pulls
 * its own fonts. Keep this file's palette in sync with tokens/colors.css — the
 * values are the same TFI brand colors, duplicated here so the sheet is
 * self-contained (a content page never loads the dashboard tfi.css).
 */

/* ── palette (mirrors tokens/colors.css; kept inline so the sheet is standalone) */
:root {
    /* The base lives here so rem == the base. It used to sit on body while html
     * stayed at the 16px default, which quietly made every rem in this file
     * ~11% small against the text beside it: 0.85rem nav rendered 13.6px next
     * to 18px prose — a banner smaller than the body it heads. tfi.css's ramp
     * only works because there body and rem are both 16px; porting its numbers
     * onto an 18px body without moving the base is what broke the ratios. */
    font-size: 18px;

    /* Hanken for prose. Aaux Next is a condensed display face — narrow and
     * pinched at reading sizes, though it earns its keep in headings and the
     * wordmark, which is why it stays on --content-display. tokens/
     * typography.css already calls Hanken the "Aaux Next substitute". */
    --content-body: 'Hanken Grotesk', system-ui, -apple-system, 'Segoe UI', sans-serif;
    --content-display: 'aaux next', 'Hanken Grotesk', system-ui, sans-serif;

    /* primary */
    --tfi-black: #171426;
    --tfi-red: #bb2d2e;
    --tfi-orange: #ca4f1f;
    --tfi-orange-dark: #a8401a; /* pressed; also th text — 5.35:1 */
    --tfi-yellow: #eeb922;
    --tfi-white: #f4f8ee;

    /* Status tokens — mirrors tokens/colors.css exactly. A Badge is 12px bold,
     * so these are text colours and must clear WCAG AA (4.5:1). The raw brand
     * hues don't on light surfaces (green 1.85:1, yellow 1.68:1), so ok/warn
     * are ink-grade variants; the hues stay pure for fills and piping. The
     * dashboards had the same bug — it was never yellow-as-text, which the
     * livery only ever uses for decoration. */
    --status-ok:   #657817;              /* ink-grade --tfi-green  — 4.59:1 */
    --status-warn: #8c6d14;              /* ink-grade --tfi-yellow — 4.52:1 */
    --status-fail: var(--tfi-red);       /* 5.53:1 as-is */
    --status-idle: var(--tfi-dark-grey); /* 4.56:1 now that grey is fixed */
    --status-info: var(--tfi-blue);      /* 4.53:1 now that blue is fixed */

    /* secondary */
    /* Darkened from #288bc2 — 3.28:1 as link text, now 4.53:1. */
    --tfi-blue: #2173a1;
    --tfi-green: #a5c426;
    --tfi-light-grey: #cacbca;
    /* Darkened from #9a9a9a. Its documented job is muted text, and it failed at
     * it: 2.45:1 on --bg-zebra, against the 4.5:1 WCAG AA needs for the small
     * text it's always used for. ~290 instances across the dashboards alone —
     * scoreboard labels, shas, "queued", "✓ deployed", every tagline. Now
     * 4.56:1. Only ever a text colour; hairlines are --tfi-light-grey. */
    --tfi-dark-grey: #6c6c6c;
    --tfi-cream: #f5f9ef;
}

/* ── fonts (Aaux Next display, Hanken Grotesk body, Berkeley Mono code;
 *    all self-hosted on cairn) ─────────────────────────────────────────────── */
@font-face {
    font-family: aaux next;
    src: url('https://cairn.tirefireind.us/v1/ns/tfi/fonts/aaux-next-regular.woff2') format('woff2');
    font-weight: light;
    font-style: normal;
}
@font-face {
    font-family: aaux next;
    src: url('https://cairn.tirefireind.us/v1/ns/tfi/fonts/aaux-next-semibold.woff2') format('woff2');
    font-weight: normal;
    font-style: normal;
}
@font-face {
    font-family: aaux next;
    src: url('https://cairn.tirefireind.us/v1/ns/tfi/fonts/aaux-next-black.woff2') format('woff2');
    font-weight: bold;
    font-style: normal;
}
@font-face {
    font-family: 'Hanken Grotesk';
    font-style: normal;
    font-weight: 400 800;
    font-display: swap;
    src: url('https://cairn.tirefireind.us/v1/ns/tfi/fonts/hanken-grotesk-latin.woff2') format('woff2');
}

@font-face {
    font-family: monospace;
    src: url('https://cairn.tirefireind.us/v1/ns/tfi/fonts/berkeley-mono-regular.woff2') format('woff2');
    font-weight: normal;
    font-style: normal;
}
@font-face {
    font-family: monospace;
    src: url('https://cairn.tirefireind.us/v1/ns/tfi/fonts/berkeley-mono-bold.woff2') format('woff2');
    font-weight: bold;
    font-style: normal;
}
@font-face {
    font-family: monospace;
    src: url('https://cairn.tirefireind.us/v1/ns/tfi/fonts/berkeley-mono-italic.woff2') format('woff2');
    font-weight: normal;
    font-style: italic;
}

/* ── base / prose ────────────────────────────────────────────────────────── */
body {
    color: var(--tfi-black);
    background-color: var(--tfi-cream);
    align-content: center;

    font-family: var(--content-body);
    font-weight: normal;
    font-style: normal;
    font-size-adjust: from-font;
    line-height: 1.6;

    padding: 0;
}

.wrapper {
    min-height: calc(100vh - 12em);
    margin: auto;
    max-width: 72ch;
    /* No min-width: it forced horizontal overflow on phones. Vertical padding
     * so the masthead doesn't jam against the top of the viewport — tfi.css
     * gets this from body { padding: 2rem }, which this sheet doesn't set. */
    padding: 2rem 1rem;
}

/* ── Type scale ─────────────────────────────────────────────────────────────
 * Mirrors tfi.css: h1 1.8rem bold (no underline — it's the loudest thing on the
 * page already), a muted <small> tagline beside it, h2 stepping down with a
 * yellow underline. Prose needs rhythm, so unlike tfi.css we keep block margins
 * instead of a `* { margin: 0 }` reset. */
h1 {
    font-family: var(--content-display);
    font-size: 1.8rem;
    font-weight: bold;
    line-height: 1.2;
    margin: 0 0 0.5rem;
}

h1 small {
    color: var(--tfi-dark-grey);
    font-size: 0.62em;
    font-weight: normal;
    text-decoration: none;
    display: inline-block;
}

h2 {
    font-family: var(--content-display);
    font-size: 1.25rem;
    font-weight: bold;
    line-height: 1.3;
    margin: 2rem 0 0.5rem;
    text-decoration: underline;
    text-decoration-color: var(--tfi-yellow);
    text-underline-offset: 0.15em;
}

h3, h4, h5 {
    font-family: var(--content-display);
    font-size: 1.05rem;
    font-weight: bold;
    margin: 1.5rem 0 0.4rem;
}

p, ul, ol, blockquote, pre {
    margin: 0 0 1rem;
}

/* ── Tables ─────────────────────────────────────────────────────────────────
 * Mirrors tfi.css, because a keyed list with a status column is a table and
 * reads like one — the dashboards have known this all along. Hover is
 * --tfi-white rather than tfi.css's --tfi-cream: the content body *is* cream,
 * so that hover would be invisible here. */
table {
    width: 100%;
    border-collapse: collapse;
    margin: 0 0 1.5rem;
}

th {
    text-align: left;
    /* 13.6px bold needs 4.5:1; --tfi-orange is 3.92. Use the palette's darker
     * orange rather than moving --tfi-orange, which is the piping. */
    color: var(--tfi-orange-dark);
    border-bottom: 2px solid var(--tfi-orange);
    padding: 0.4rem 0.6rem;
    font-weight: bold;
}

td {
    padding: 0.3rem 0.6rem;
    border-bottom: 1px solid var(--tfi-light-grey);
    color: var(--tfi-black);
}

tr:hover td {
    background: var(--tfi-white);
}

/* A decommissioned row is struck, so mute it too — a line through it at full
 * contrast still shouts. */
td del {
    color: var(--tfi-dark-grey);
}

/* ── Badge ──────────────────────────────────────────────────────────────────
 * The compact status pill from the component library — tone-coloured outline,
 * faint wash, leading glyph. Mirrors components/core/Badge.jsx: the tone names
 * (ok/warn/fail/idle/info/neutral) and the glyphs are canon's, so a status cell
 * here means the same thing it means on a scoreboard. Drop one in a table cell
 * rather than writing bare status text. */
.badge {
    display: inline-flex;
    align-items: center;
    gap: 0.35em;
    font-size: 0.75rem;
    font-weight: bold;
    color: var(--status-idle);
    background: color-mix(in srgb, currentColor 10%, transparent);
    border: 1px solid color-mix(in srgb, currentColor 45%, transparent);
    border-radius: 3px;
    padding: 0.15em 0.5em;
    line-height: 1.3;
    white-space: nowrap;
}

/* The glyph auto-selects from the tone, as it does in Badge.jsx. */
.badge::before {
    content: "—";
}

.badge-ok { color: var(--status-ok); }
.badge-ok::before { content: "✓"; }

.badge-warn { color: var(--status-warn); }
.badge-warn::before { content: "●"; }

.badge-fail { color: var(--status-fail); }
.badge-fail::before { content: "✗"; }

.badge-info { color: var(--status-info); }
.badge-info::before { content: "i"; }

.badge-neutral { color: var(--tfi-dark-grey); }
.badge-neutral::before { content: none; }

/* Dashes, not dots — the marker the dashboards use. */
ul {
    list-style: none;
    padding-left: 1rem;
}

ul li::before {
    content: "- ";
    color: var(--tfi-orange);
    font-weight: bold;
}

b, strong { font-weight: bold; }

h1 + hr, h2 + hr, h3 + hr, h4 + hr, h5 + hr {
    margin-top: -0.75rem;
}

.wrapper .main img {
    max-width: 100%;
}

a {
    color: var(--tfi-blue);
    text-decoration: none;
}

a:hover {
    color: var(--tfi-orange);
    text-decoration: underline;
}

/* ── Masthead ───────────────────────────────────────────────────────────────
 * See design-system/components/core/Masthead.prompt.md.
 *
 * [project icon] [project title] by TireFire Ind. — the shape the old Flowmetal
 * site used, and it's the right one: the *project* leads and TireFire is
 * attribution beside it. The dashboards' icon + wordmark + muted subtitle reads
 * fine on a dashboard, where the subtitle says what the tool does; on a project
 * site it's just a second, quieter title competing with the first.
 *
 * "by TireFire Ind." is text, not logo.svg. Flowmetal's lockup works because
 * it's ~300px wide with room to breathe; at a masthead's height its
 * "INDUSTRIES" lands ~3px tall. A lockup needs the room or it needs to not be
 * there. The parent site (tirefireind.us) has no by-line — it *is* TireFire. */
.masthead {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    flex-wrap: wrap;
    margin: 0 0 1.5rem;
}

/* The masthead wears no casing. Every dashboard we run — doorman, deliverator,
 * rubberstamp — puts its header plain at the top-left and spends its one casing
 * on the scoreboard, and they read better for it. A casing round the header
 * plus a casing round the content is two, which is how the loudest device in
 * the livery stops meaning "look here" and starts meaning wallpaper. Spend it
 * once, on .lede. */

/* Groups [icon][title] with the by-line so the pair sits together and the nav
 * still pushes to the far edge. Baseline-aligned: "by" rides the title's
 * baseline the way it does on the Flowmetal banner, not its centre. */
.masthead-lockup {
    display: flex;
    align-items: baseline;
    gap: 0.5rem;
    flex-wrap: wrap;
}

/* Attribution — subordinate to the title, but never under the body size. The
 * muted grey is the "by"; the credit itself is a link and gets no color
 * override, so it takes the sheet's ordinary link treatment (blue, orange on
 * hover) like every other link on the page. */
.masthead-by {
    color: var(--tfi-dark-grey);
    font-size: 1rem;
    font-weight: normal;
    white-space: nowrap;
}

.masthead-by a {
    font-weight: bold;
}

/* ── Lede ───────────────────────────────────────────────────────────────────
 * The page's topline, and the only thing on it worth a casing. The dashboards
 * spend their single casing on the scoreboard — the numbers you came for — and
 * on nothing else, which is exactly what makes it read as "look here". A prose
 * page's equivalent is the lede: what this is, in one breath.
 *
 * Exactly one per page. If you want a second, you want an h2. */
.lede {
    font-size: 1.15rem;
    /* Clears the yellow ::before, which bleeds 18px past the box. */
    margin-bottom: 3.5rem;
}

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

/* Deliberately NOT a flex container. A flex box takes its baseline from its
 * first flex item, and an <img>'s baseline is its bottom edge — so with the
 * mark in here, .masthead-lockup's baseline alignment lined "by" up with the
 * bottom of the icon and knocked it onto a second line. Inline layout keeps the
 * anchor's baseline on the *text*, which is what "by" needs to ride. */
.masthead-brand {
    display: block;
    text-decoration: none;
    color: var(--tfi-black);
    font-family: var(--content-display);
    font-weight: bold;
    /* Larger than the body, not level with it — this is a banner. */
    font-size: 1.4rem;
    line-height: 1;
    white-space: nowrap;
}

.masthead-brand:hover {
    color: var(--tfi-black);
    text-decoration: none;
}

.masthead-brand img {
    height: 1.9em;
    width: auto;
    vertical-align: middle;
    margin-right: 0.4rem;
}

.masthead-brand small {
    color: var(--tfi-dark-grey);
    font-size: 0.72em;
    font-weight: normal;
}

.masthead-nav {
    display: flex;
    align-items: center;
    gap: 1rem;
    /* Body size. Never under it. */
    font-size: 1rem;
}

.masthead-nav a {
    font-weight: bold;
}

/* The one destination that leaves the public site (e.g. an authed console). */
.masthead-nav a[data-external]::after {
    content: " ↗";
    color: var(--tfi-dark-grey);
}

.subheading {
    background: var(--tfi-grey);
    border-left: solid;
    font-style: italic;
    padding: 1em;
}

.mb-1 {
    margin-bottom: 1em;
}

code {
    font-family: monospace;
    font-size-adjust: 0.4;
}

@supports (-webkit-touch-callout: none) {
    /* iOS renders Berkeley Mono larger; nudge it down. */
    code {
        font-size-adjust: 0.3;
    }
}

pre code {
    min-width: 40em;
}

blockquote {
    border-left: var(--tfi-dark-grey) 0.5em solid;
    background: var(--tfi-light-grey);
    padding: 0.25em 1em;
    font-style: italic;
}

path {
    stroke-width: 15px;
    fill: transparent;
}

/* .yellow carries both a text color (from colors.css) and an SVG stroke (from
 * the old tfi.css) — the loading-spinner's yellow arcs rely on the stroke. */
.yellow {
    color: var(--tfi-yellow);
    stroke: var(--tfi-yellow);
}

.orange {
    color: var(--tfi-orange);
}

.blue {
    color: var(--tfi-blue);
}

.red {
    stroke: var(--tfi-orange);
}

/* ── Loader ─────────────────────────────────────────────────────────────────
 * See design-system/components/core/Loader.prompt.md. The mark draws itself in
 * on a dark scrim — a title card, not a spinner.
 *
 * It used to blur the cream page behind it and leave the mark to fend for
 * itself: --tfi-yellow (#eeb922) on --tfi-cream (#f5f9ef) is nearly no contrast,
 * so half the mark vanished. The scrim is now --tfi-black, which is what the
 * brand's yellow and orange are actually built to sit on. */
#loading-overlay,
.loader-overlay {
    position: fixed;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;

    display: flex;
    justify-content: center;
    align-items: center;
    z-index: 9999;

    background: var(--tfi-black);
    /* Fade out rather than cutting — a hard cut from a dark scrim to a cream
     * page is a flashbang. Paired with .loader-done below. */
    transition: opacity 0.4s ease;
}

/* The old rule pinned this wrapper to 50x50 while the mark inside is 220 wide,
 * so the SVG overflowed its own box and the flex centring centred the *box* —
 * the mark sat visibly off-centre. Size to the mark and let it scale. */
.spinner,
.loader-mark {
    width: min(240px, 45vw);
    height: auto;
    display: block;
}

.spinner svg,
.loader-mark svg {
    width: 100%;
    height: auto;
    display: block;
    overflow: visible;
}

.loader-done {
    opacity: 0;
    pointer-events: none;
}

/* Played already this session. The title card is a front-door flourish, and a
 * static site reloads on every internal link — without this, each click pays
 * the toll again and the flourish becomes an obstacle. The consumer sets
 * html.loader-seen from <head>, before first paint, so a repeat view never
 * flashes the scrim; a body-end script would be too late. */
html.loader-seen #loading-overlay,
html.loader-seen .loader-overlay {
    display: none;
}

.hidden {
    display: none !important;
}

/* ── bubbles (nested-border "double bubble" casing) ──────────────────────────
 * The piping is 8px on a -18px inset — the same casing .tfi-box draws in
 * tfi.css, so a content page and a dashboard read as the same machine. It used
 * to be 1em (18px at this base) on a -2.25em inset: more than double the
 * apps' weight, which is what made the content sites look heavy-handed next to
 * every service we run. Weight is the piping's only job; keep it at 8px. */
.bubble-orange {
    border-radius: 1em;
    border: 8px solid var(--tfi-orange);
    padding: 1em;
}

.bubble-yellow {
    border-radius: 1em;
    border: 8px solid var(--tfi-yellow);
    padding: 1em;
}

.bubble-double, .double-bubble-yo {
    position: relative;
    border: 8px solid var(--tfi-orange);
    border-radius: 1em;
    padding: 1.5rem;
    margin: 1.25em;
}

.bubble-double:before, .double-bubble-yo:before {
    content: '';
    position: absolute;
    inset: -18px;
    border: 8px solid var(--tfi-yellow);
    border-radius: 1.5em;
    z-index: -1; /* sit behind, not over, the content */
}

.double-bubble-oy {
    position: relative;
    border: 8px solid var(--tfi-yellow);
    border-radius: 1em;
    padding: 1.5rem;
    margin: 1.25em;
}

.double-bubble-yo:before {
    content: '';
    position: absolute;
    inset: -18px;
    border: 8px solid var(--tfi-orange);
    border-radius: 1.5em;
    z-index: -1;
}

.no-top-border {
    margin-top: 0;
}
.no-top-border, .no-top-border:before {
    border-top: none;
    border-top-left-radius: 0 !important;
    border-top-right-radius: 0 !important;
}
.no-top-border:before {
    top: 0em !important;
}

.no-bottom-border {
    margin-bottom: 0;
}
.no-bottom-border, .no-bottom-border:before {
    border-bottom: none;
    border-bottom-right-radius: 0 !important;
    border-bottom-left-radius: 0 !important;
}
.no-bottom-border:before {
    bottom: 0em !important;
}

.no-right-border {
    /* Half of .bubble-double's 1.25em: the casing runs most of the way out
     * without going flush to the edge. */
    margin-right: 0.625em;
}
.no-right-border, .no-right-border:before {
    border-right: none;
    border-top-right-radius: 0 !important;
    border-bottom-right-radius: 0 !important;
}
.no-right-border:before {
    right: 0em !important;
}

.no-left-border {
    margin-left: 0;
}
.no-left-border, .no-left-border:before {
    border-left: none;
    border-top-left-radius: 0 !important;
    border-bottom-left-radius: 0 !important;
}
.no-left-border:before {
    left: 0em !important;
}

/* ── code highlighting (Jekyll rouge tokens) ─────────────────────────────── */
.highlight {
    background: var(--tfi-cream);
}

.highlight .k {
    color: var(--tfi-orange);
    font-weight: bold;
}

.highlight .nf {
    color: var(--tfi-blue);
}

.highlight .n {
    color: var(--tfi-black);
}

.highlight .c,
.highlight .c1 {
    color: var(--tfi-dark-grey);
    font-style: italic;
}
.highlight .c::first-letter,
.highlight .c1::first-letter {
    color: var(--tfi-black);
}

/* constants like None */
.highlight .bp {
    color: color-mix(in srgb, var(--tfi-blue), black 30%);
}

.highlight .s,
.highlight .sh {
    color: color-mix(in srgb, var(--tfi-green), black 15%);
}
