/* ====================================================================
   depth.css - the depth system, and the three shared components built
   out of it.

   Linked from views/partials/head.php immediately AFTER app.css, and
   from views/partials/head_public.php immediately AFTER public.css --
   the two layouts a member and a stranger actually read. Order matters:
   section 1 overrides three declarations app.css makes about the ground,
   and same-specificity selectors are decided by which sheet came last.

   It is not linked from layouts/print.php (that surface is paper and
   wants none of this) and it is not linked from layouts/landing.php,
   which a later pass handles deliberately and alone.

   It is the only sheet on this site allowed to paint the ground, and it
   is where the shared instruments (threat readout, classification stamp,
   redaction) keep their styles -- each of those is ONE partial consumed
   by several pages, so its CSS must not end up inside any one page's
   stylesheet.

   The reference implementation is docs/design/direction.html. It is a
   WORKING PAGE, not a description. Open it before you change anything
   in here.


   ####################################################################
   ##                                                                ##
   ##                       THE FOUR RULES                           ##
   ##                                                                ##
   ##  Every one of these was arrived at by getting it wrong first.   ##
   ##  Break one and the whole surface stops reading as a place and   ##
   ##  starts reading as a stack of divs with effects on them.        ##
   ##                                                                ##
   ####################################################################

   1. LIGHT COMES FROM ABOVE AND BEHIND.

      Every raised surface gets a LIT TOP EDGE (an `inset 0 1px 0`
      highlight plus a brighter border-top-color), a DIMMER BOTTOM EDGE,
      and a shadow that FALLS AWAY BELOW it. One light source, never
      moved, never negotiated. A surface lit from the front has no
      thickness; a surface lit from below looks like a mistake.

   2. ELEVATION MOVES FOUR PROPERTIES TOGETHER.

      Shadow spread, surface brightness, border luminance, backdrop
      blur. All four, in step, every time. Depth that is only a shadow
      looks like a sticker; depth that is only a lighter background
      looks like a hover state that got stuck. The .e1/.e2/.e3 classes
      below exist precisely so that nobody has to remember all four --
      use them instead of hand-rolling a box-shadow.

   3. ONLY THREE THINGS GLOW.

        * LIVE SYSTEM DATA        phosphor green  (--glow-phosphor)
          Counts, timers, measurements, state the server just computed.
          Not labels. Not headings. Not "it looked nice there".
        * CLASSIFICATION          red             (--glow-classify)
        * THREAT LEVEL 4 AND 5    amber / red     (--glow-threat)

      Nothing else. If everything glows, nothing is urgent and the page
      reads as a screensaver. The one sanctioned extra is --glow-chrome
      on a FOCUS RING or a hover lift, which is feedback, not state.

   4. GLASS HAS A BUDGET.

      backdrop-filter is expensive and it COMPOUNDS: each blurred pane
      re-samples everything behind it, so the cost is not linear in the
      number of panes. It belongs on elevated, bounded surfaces -- cards,
      panels, the publish sheet, a drawer. It does NOT go on long
      scrolling lists, table rows, comment threads, or anything that
      repeats a hundred times down a page. Six blurred panes on screen,
      not sixty. When you need the look on something repeated, use
      .pane--flat, which is the same material with the blur removed and
      an opaque surface substituted.

   And one that is not a rule about depth but is not negotiable either:
   PREFERS-REDUCED-MOTION KILLS EVERYTHING IN THIS FILE. See the block
   at the bottom. If you add an animation or a transition here, it is
   already covered; if you add one that a wildcard cannot reach (a JS
   loop, a scroll effect), it is on you to guard it.
   ==================================================================== */


/* ====================================================================
   1. THE GROUND

   Three overrides and two pseudo-elements. Together they are the room
   every pane in this file sits in.
   ==================================================================== */

/*
 * THE THREE OVERRIDES.
 *
 * They live here rather than in app.css because they are one decision --
 * "the page has a room now" -- and splitting a single decision across
 * two files owned by different people is how it half-lands.
 *
 *   .dash-content had an OPAQUE radial gradient (#151515 -> --c-bg)
 *   painted across the whole content column. Opaque is the operative
 *   word: it sat on top of the body bloom and hid it completely, so on
 *   every member page the room would have existed and been invisible.
 *   Transparent, and the bloom below shows through. The vignette it
 *   used to provide is now the bloom's job and the bloom does it in
 *   colour.
 *
 *   .dash-nav was #080808 -- neutral near-black, hard-coded, against a
 *   blue-black ground. A neutral column on a cool ground reads as a
 *   dirty patch, and any glow crossing the seam changes hue halfway.
 *   This is a hue correction and nothing else: same darkness, right
 *   family.
 *
 *   .dash-content::after was the old CRT film -- a 4px gradient at 0.6
 *   opacity, a scanline you SEE. It is retired here rather than in
 *   app.css so that the replacement and the retirement are one hunk in
 *   one diff. body::after below is the same idea done at 1px/4px and
 *   0.012 alpha: felt, not seen. Two films stacked would be neither.
 */
.dash-content { background: transparent; }
.dash-nav     { background: var(--deep); }
.dash-content::after { content: none; }

/*
 * THE AMBIENT BLOOM. Three soft lights in the room, none of them on
 * anything.
 *
 * Chrome-blue from above and to the right (the machine), a smaller
 * phosphor wash top-left (the artifact), and a classification red
 * bleeding up from below the fold. All three are enormous and very
 * faint; the point is that the ground is never one flat value, so a
 * pane's translucency has something to be translucent ABOUT.
 *
 * z-index -1, not 0: the reference gives its wrapper z-index 2 to sit
 * above this, and there is no single wrapper on this site to give one
 * to. A negative index puts the bloom above the canvas background and
 * below every element in the document, which needs no cooperation from
 * any page. body must not become a stacking context or this disappears
 * behind the canvas -- so nothing in here gives body a transform, a
 * filter, an opacity or a z-index.
 */
body::before {
    content: "";
    position: fixed;
    inset: 0;
    z-index: -1;
    pointer-events: none;
    background:
        radial-gradient(58vw 42vw at 72% -8%,  rgba(var(--chrome-rgb),   0.10), transparent 60%),
        radial-gradient(46vw 38vw at 12%  8%,  rgba(var(--phosphor-rgb), 0.055), transparent 62%),
        radial-gradient(70vw 50vw at 50% 108%, rgba(var(--classify-rgb), 0.05), transparent 65%);
}

/*
 * THE PHOSPHOR ARTIFACT. 1px on, 3px off, at 1.2% white, blended in
 * overlay.
 *
 * overlay rather than normal is what makes it a property of the SURFACE
 * instead of a sheet of gauze over it: it darkens what is already dark
 * and brightens what is already light, so a glowing readout gets a
 * texture and the black around it stays black. At this alpha it is
 * invisible in a screenshot and obvious the moment you remove it.
 *
 * It sits at --z-scanlines (900), exactly where the old film sat, which
 * preserves the one documented ordering fact in tokens.css: the mobile
 * drawer (1100) and the notification dropdown (1200) are the two
 * surfaces on the site that rise above the texture.
 */
body::after {
    content: "";
    position: fixed;
    inset: 0;
    z-index: var(--z-scanlines);
    pointer-events: none;
    mix-blend-mode: overlay;
    background: repeating-linear-gradient(
        0deg,
        rgba(255, 255, 255, 0.012) 0 1px,
        transparent 1px 4px
    );
}


/* ====================================================================
   2. THE ELEVATION LADDER

   .pane is elevation 1. .pane.e2 and .pane.e3 raise it. .pane.e1 is
   spelled out too, so a template can write the level it means rather
   than relying on a default.

   FOUR PROPERTIES MOVE AT EVERY STEP -- surface, border, gloss, blur --
   plus the shadow. That is rule 2, and it is the whole reason these are
   classes instead of a mixin nobody would use.

   WHAT ELEVATION MEANS, so the three levels do not become decoration:

     e1   the page's own furniture. Cards in a grid, sidebar modules,
          a form section. The resting state of almost everything.
     e2   something that has been raised deliberately: the focused
          record, a summary panel, an open disclosure.
     e3   something over the page. A sheet, a dialog, a drawer, a
          menu. If it does not overlap other content, it is not e3.

   Two levels of separation is a lot. If you find yourself wanting a
   fourth, what you want is contrast, not altitude.
   ==================================================================== */

.pane {
    position: relative;
    border-radius: var(--radius-pane);
    background: var(--pane-1);
    border: 1px solid var(--edge-dim);
    border-top-color: var(--edge-e1);            /* the light is above */
    -webkit-backdrop-filter: blur(var(--blur-e1)) saturate(1.2);
    backdrop-filter: blur(var(--blur-e1)) saturate(1.2);
    box-shadow:
        inset 0 1px 0 var(--gloss-e1),           /* specular top edge  */
        var(--shadow-e1);                        /* and it falls below */
}

.pane.e1 {
    background: var(--pane-1);
    border-top-color: var(--edge-e1);
    -webkit-backdrop-filter: blur(var(--blur-e1)) saturate(1.2);
    backdrop-filter: blur(var(--blur-e1)) saturate(1.2);
    box-shadow: inset 0 1px 0 var(--gloss-e1), var(--shadow-e1);
}

.pane.e2 {
    background: var(--pane-2);
    border-top-color: var(--edge-e2);
    -webkit-backdrop-filter: blur(var(--blur-e2)) saturate(1.25);
    backdrop-filter: blur(var(--blur-e2)) saturate(1.25);
    box-shadow: inset 0 1px 0 var(--gloss-e2), var(--shadow-e2);
}

.pane.e3 {
    background: var(--pane-3);
    border-top-color: var(--edge-e3);
    -webkit-backdrop-filter: blur(var(--blur-e3)) saturate(1.3);
    backdrop-filter: blur(var(--blur-e3)) saturate(1.3);
    box-shadow: inset 0 1px 0 var(--gloss-e3), var(--shadow-e3);
}

/*
 * RULE 4, AS A CLASS.
 *
 * Same material, blur removed, opaque surface substituted at the
 * matching luminance. This is what a table row, a comment, a search
 * result or anything else that repeats down a page gets. It is not a
 * downgrade -- composited over --void the solid surfaces land on the
 * same value the translucent ones do, so a flat row sitting next to a
 * glass card reads as the same stuff.
 */
.pane--flat {
    -webkit-backdrop-filter: none;
    backdrop-filter: none;
    background: var(--pane-1-solid);
}
.pane--flat.e2 { -webkit-backdrop-filter: none; backdrop-filter: none; background: var(--pane-2-solid); }
.pane--flat.e3 { -webkit-backdrop-filter: none; backdrop-filter: none; background: var(--pane-3-solid); }

/*
 * THE FALLBACK, and it is not hypothetical: backdrop-filter is off by
 * default in a few configurations and is disabled outright by some
 * privacy extensions. Without this a pane there is a nearly transparent
 * rectangle with a shadow -- the surface disappears and the text lands
 * on the bloom. Opaque surfaces, same luminance, everything else intact.
 */
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
    .pane    { background: var(--pane-1-solid); }
    .pane.e1 { background: var(--pane-1-solid); }
    .pane.e2 { background: var(--pane-2-solid); }
    .pane.e3 { background: var(--pane-3-solid); }
}

/* Padding that matches the radius. A 14px corner with 8px of padding
   inside it reads as a mistake. */
.pane-pad    { padding: var(--s3); }
.pane-pad-lg { padding: var(--s4); }

/*
 * THE LIFT. Opt-in, for a pane that is a link to somewhere.
 *
 * 6px and 2.2 degrees. Depth you NOTICE is depth done wrong -- the
 * reference is explicit about this and it is the easiest thing in the
 * system to overcook. The lift moves all four properties as well: it
 * rises, brightens, its edge lights up and its shadow lengthens.
 * Requires `perspective` on the parent for the rotation to be worth
 * anything; without one it degrades to a straight rise, which is fine.
 */
.pane--lift {
    transition:
        transform  var(--dur-lift) var(--ease-lift),
        box-shadow var(--dur-lift) var(--ease-lift),
        background var(--dur-lift) var(--ease-lift),
        border-color var(--dur-lift) var(--ease-lift);
    transform-style: preserve-3d;
}
.pane--lift:hover,
.pane--lift:focus-within {
    transform: translateY(-6px) rotateX(2.2deg);
    background: var(--pane-2);
    border-top-color: var(--edge-e2);
    box-shadow:
        inset 0 1px 0 var(--gloss-e3),
        0 48px 90px -30px rgba(0, 0, 0, 0.96),
        0 0 60px -26px rgba(var(--chrome-rgb), 0.5);
}

/* A grid that gives its children somewhere to rotate into. */
.pane-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(290px, 1fr));
    gap: var(--s4);
    perspective: 1200px;
}


/* ====================================================================
   3. THE SMALL SHARED ATOMS

   Four things the components below and the three page workstreams all
   need, defined once so they cannot drift into four versions.
   ==================================================================== */

/*
 * .hud - the machine talking to itself. Mono, tiny, wide-tracked,
 * uppercase. It is CHROME by default; .hud--live is the phosphor variant
 * and IT IS FOR LIVE DATA ONLY (rule 3). If the string is a label rather
 * than a value the server just computed, it does not get --live.
 */
.hud {
    font-family: var(--font-mono);
    font-size: var(--fs-100);
    letter-spacing: 0.2em;
    text-transform: uppercase;
    color: var(--chrome);
}
.hud--dim  { color: var(--text-faint); }
.hud--live { color: var(--phosphor); text-shadow: var(--glow-phosphor); }

/* .eyebrow - a section marker with a rule trailing off it. */
.eyebrow {
    display: flex;
    align-items: center;
    gap: var(--s2);
    margin-bottom: var(--s2);
    font-family: var(--font-mono);
    font-size: var(--fs-100);
    letter-spacing: 0.34em;
    text-transform: uppercase;
    color: var(--chrome);
}
.eyebrow::after {
    content: "";
    flex: 1;
    height: 1px;
    background: linear-gradient(90deg, rgba(var(--chrome-rgb), 0.4), transparent);
}

/* .rule - a horizontal rule that starts as classification and cools off
   into chrome before it fades. Used between major sections. */
.rule {
    height: 1px;
    border: 0;
    margin: var(--s5) 0;
    background: linear-gradient(
        90deg,
        rgba(var(--classify-rgb), 0.55),
        rgba(var(--chrome-rgb), 0.28),
        transparent
    );
}

/*
 * .chip - a small fact attached to something. Neutral by default. The
 * three modifiers are the three signals and nothing else may be added:
 * .chip--live for a measured value, .chip--hot for classification, and
 * the threat chip, which is not a modifier here at all -- it is the
 * component in section 4, because a threat level is not a fact, it is a
 * reading.
 */
.chip {
    display: inline-block;
    padding: 0.28em 0.6em;
    border-radius: var(--radius-1);
    border: 1px solid var(--edge-dim);
    background: rgba(255, 255, 255, 0.02);
    color: var(--text-dim);
    font-family: var(--font-mono);
    font-size: var(--fs-100);
    letter-spacing: 0.13em;
    text-transform: uppercase;
    white-space: nowrap;
}
.chip--live {
    border-color: rgba(var(--phosphor-rgb), 0.35);
    background: rgba(var(--phosphor-rgb), 0.07);
    color: var(--phosphor);
    text-shadow: var(--glow-phosphor);
}
.chip--hot {
    border-color: rgba(var(--classify-rgb), 0.45);
    background: rgba(var(--classify-rgb), 0.09);
    color: #FF8FA1;
    box-shadow: 0 0 20px -8px rgba(var(--classify-rgb), 0.6);
}
.chips {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-1);
}


/* ====================================================================
   4. THREAT READOUT              views/partials/threat_readout.php

   The heat scale as a casualty report. This is the site's most
   characterful data and it used to render as five grey chilli glyphs
   that read as noise -- decoration where an instrument belonged.

   Two forms, one partial:

     .threat--inline   a chip. Goes on a card, in a list, in a table.
                       No glass, no blur: it appears a hundred times a
                       page (rule 4).
     .threat--ladder   the full five-rung scale with the record's own
                       level marked. Goes on a recipe, once.

   THE COLOUR LADDER, and why it is not the reference's exactly:
   direction.html paints level 4 in classification red because its
   ladder is a LEGEND -- all five rungs lit at once, and it needs five
   distinguishable rows. Here the ladder is an INSTRUMENT: the rungs
   that are not the answer are dimmed, so the run is
   phosphor - phosphor - amber - amber - red, and the only rows that
   GLOW are 4 and 5 (rule 3). Level 5 is the only one that gets the
   gradient and the full bloom, and that is deliberate: it is one record
   in a hundred and sixteen, and arriving at it should feel like
   something happening.
   ==================================================================== */

.threat {
    --threat-hue: var(--phosphor-rgb);   /* rebound per level below */
    --threat-ink: var(--phosphor);
}

.threat[data-threat="3"],
.threat[data-threat="4"] { --threat-hue: var(--threat-rgb);   --threat-ink: var(--threat); }
.threat[data-threat="5"] { --threat-hue: var(--classify-rgb); --threat-ink: var(--classify); }

/* -- inline form ---------------------------------------------------- */

.threat--inline {
    display: inline-flex;
    align-items: baseline;
    gap: 0.5em;
    padding: 0.28em 0.65em;
    border-radius: var(--radius-1);
    border: 1px solid rgba(var(--threat-hue), 0.3);
    background: rgba(var(--threat-hue), 0.07);
    font-family: var(--font-mono);
    font-size: var(--fs-100);
    letter-spacing: 0.13em;
    text-transform: uppercase;
    color: var(--threat-ink);
    white-space: nowrap;
}
/* The word THREAT. Deliberately the faintest thing in the chip: it is
   the only part that is the same on all 116 records. */
.threat--inline .threat-lead { color: var(--text-faint); }
.threat--inline .threat-num {
    font-family: var(--font-ui);
    font-size: var(--fs-ui-200);
    font-variant-numeric: tabular-nums;
    line-height: 1;
}
.threat--inline .threat-shu {
    color: var(--phosphor);
    letter-spacing: 0.1em;
}

/* Only 4 and 5 glow. */
.threat--inline[data-threat="4"] {
    border-color: rgba(var(--threat-rgb), 0.5);
    box-shadow: 0 0 20px -8px rgba(var(--threat-rgb), 0.65);
    text-shadow: var(--glow-threat);
}
.threat--inline[data-threat="5"] {
    border-color: rgba(var(--classify-rgb), 0.55);
    background: rgba(var(--classify-rgb), 0.12);
    color: #fff;
    box-shadow: 0 0 24px -8px rgba(var(--classify-rgb), 0.75);
    text-shadow: 0 0 16px rgba(var(--classify-rgb), 0.9);
}
.threat--inline[data-threat="5"] .threat-shu { color: #FFD7DE; text-shadow: none; }

/* -- ladder form ---------------------------------------------------- */

.threat--ladder { display: block; }

.threat-head {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--s2);
    margin-bottom: var(--s2);
}
.threat-head-label {
    font-family: var(--font-mono);
    font-size: var(--fs-100);
    letter-spacing: 0.2em;
    text-transform: uppercase;
    color: var(--text-faint);
}
/* A measured value: live data, therefore phosphor, therefore it glows. */
.threat-scoville {
    font-family: var(--font-ui);
    font-size: var(--fs-ui-400);
    line-height: 1;
    color: var(--phosphor);
    text-shadow: var(--glow-phosphor);
    font-variant-numeric: tabular-nums;
}
.threat-scoville .threat-band {
    font-family: var(--font-mono);
    font-size: var(--fs-100);
    letter-spacing: 0.2em;
    color: var(--text-dim);
    text-shadow: none;
    margin-left: 0.5em;
}

.threat-rows {
    display: grid;
    gap: 2px;
    margin: 0;
    padding: 0;
    list-style: none;
}

/*
 * A ladder rung. NO backdrop-filter, by rule 4 -- five of these appear
 * together and they are rows in a list, not panes. They borrow the
 * pane's LIGHTING (lit top edge, dim elsewhere, shadow below) without
 * borrowing its cost.
 */
.threat-row {
    display: grid;
    grid-template-columns: 2.6rem 10.5rem 1fr;
    gap: var(--s3);
    align-items: center;
    padding: var(--space-2) var(--space-3);
    border-radius: var(--radius-2);
    background: var(--pane-1-solid);
    border: 1px solid var(--edge-dim);
    border-top-color: var(--edge-e1);
    opacity: 0.42;
    transition:
        opacity    var(--dur-base) var(--ease),
        background var(--dur-base) var(--ease);
}
.threat-row:hover { opacity: 0.75; background: var(--pane-2-solid); }

.threat-row .threat-num {
    font-family: var(--font-ui);
    font-size: var(--fs-ui-500);
    line-height: 1;
    text-align: center;
    color: var(--text-faint);
    font-variant-numeric: tabular-nums;
}
.threat-row .threat-code {
    font-family: var(--font-mono);
    font-size: var(--fs-200);
    font-weight: 700;
    letter-spacing: 0.15em;
    text-transform: uppercase;
    color: var(--text-dim);
}
.threat-row .threat-say  { font-size: var(--fs-300); color: var(--text-dim); }
.threat-row .threat-say em { color: var(--text); font-style: normal; }

/*
 * THE ANSWER. Full opacity, its own hue, and a lit top edge that says it
 * is the raised one. Everything above is at 42% so this needs no extra
 * decoration to be found.
 */
.threat-row.is-current {
    opacity: 1;
    background: var(--pane-2-solid);
    border-color: rgba(var(--threat-hue), 0.34);
    border-top-color: rgba(var(--threat-hue), 0.5);
    box-shadow: inset 0 1px 0 var(--gloss-e2), var(--shadow-e1);
}
.threat-row.is-current .threat-num  { color: var(--threat-ink); }
.threat-row.is-current .threat-code { color: var(--threat-ink); }
.threat-row.is-current .threat-say  { color: var(--text-2); }

/* Levels 4 and 5 -- and ONLY when they are the answer -- glow. */
.threat[data-threat="4"] .threat-row.is-current {
    box-shadow:
        inset 0 1px 0 var(--gloss-e2),
        0 0 40px -16px rgba(var(--threat-rgb), 0.5),
        var(--shadow-e1);
}
.threat[data-threat="4"] .threat-row.is-current .threat-num,
.threat[data-threat="4"] .threat-row.is-current .threat-code {
    text-shadow: var(--glow-threat);
}
.threat[data-threat="5"] .threat-row.is-current {
    background: linear-gradient(90deg, rgba(var(--classify-rgb), 0.16), var(--pane-1-solid) 62%);
    border-color: rgba(var(--classify-rgb), 0.4);
    box-shadow:
        inset 0 1px 0 var(--gloss-e2),
        0 0 44px -14px rgba(var(--classify-rgb), 0.5),
        var(--shadow-e1);
}
.threat[data-threat="5"] .threat-row.is-current .threat-code {
    color: #fff;
    text-shadow: 0 0 16px rgba(var(--classify-rgb), 0.9);
}
.threat[data-threat="5"] .threat-row.is-current .threat-num {
    color: var(--classify);
    text-shadow: var(--glow-classify);
}

@media (max-width: 640px) {
    .threat-row { grid-template-columns: 2.2rem 1fr; row-gap: var(--space-1); }
    .threat-row .threat-say { grid-column: 1 / -1; }
}


/* ====================================================================
   5. CLASSIFICATION STAMP        views/partials/classification_stamp.php

   Three visibility states that the query layer has enforced since
   0009_visibility.sql, and that the interface has only ever rendered as
   words in a dropdown. As stamps they glow like ink under a lamp and
   cost nothing but CSS.

   The rotation is FIXED PER STATE, never random: a stamp that sits at a
   different angle on every page load is a stamp that draws the eye every
   time, which is the opposite of what a stamp is for. currentColor does
   the work -- one colour declaration per state paints the text, the
   border, the text-shadow and the bloom together, which is why the three
   states are four lines each.
   ==================================================================== */

.stamp {
    display: inline-block;
    padding: 0.32em 0.75em;
    border-radius: var(--radius-1);
    border: 2.5px solid currentColor;
    font-family: var(--font-ui);
    font-size: var(--fs-ui-400);
    font-weight: 700;
    letter-spacing: 0.07em;
    line-height: 1.1;
    text-transform: uppercase;
    text-shadow: 0 0 18px currentColor;
    box-shadow:
        0 0 30px -8px currentColor,
        0 18px 40px -22px rgba(0, 0, 0, 0.9);
}
.stamp-word { display: block; }
.stamp-sub {
    display: block;
    margin-top: 0.15em;
    font-family: var(--font-mono);
    font-size: var(--fs-100);
    letter-spacing: 0.2em;
    opacity: 0.85;
    text-shadow: none;
}

.stamp--classified   { color: var(--classify); transform: rotate(-4deg);   }
.stamp--eyes-only    { color: var(--threat);   transform: rotate(-1deg);   }
.stamp--declassified { color: var(--phosphor); transform: rotate(2.5deg);  }

/* The small variant, for a card corner or a list row. Same ink, less
   shout, and it does not rotate -- a 4-degree tilt on a 12px stamp is
   just a crooked chip. */
.stamp--sm {
    padding: 0.22em 0.5em;
    border-width: 1.5px;
    font-size: var(--fs-ui-100);
    letter-spacing: 0.12em;
    transform: none;
    box-shadow: 0 0 18px -8px currentColor;
}
.stamp--sm .stamp-sub { display: none; }

/* A row of them, as on the design reference. */
.stamps {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--s4);
    padding: var(--s4) 0;
}


/* ====================================================================
   6. REDACTION                   views/partials/redaction.php

   THE BARS ARE RENDERED FROM A COUNT. There is no withheld row behind
   them, there is no title in an attribute, there is nothing in the DOM
   that a withheld record could leak through -- the partial is handed an
   integer and draws that many bars. The widths are picked from a fixed
   cycle indexed by POSITION, not by anything about the data, so the
   ragged edge that makes it look like a real redaction is generated by
   the loop counter and nothing else.

   The absence is more interesting than a shorter list would be, and it
   is TRUE: the archive really does hold records it will not show you.

   The sweep is the only place on this site the 80s artifact is allowed
   to MOVE. It is 5.5 seconds long and idle for the first 70% of that,
   because it has to be something you catch rather than something you
   watch. prefers-reduced-motion removes it entirely, and the bar without
   it is still a bar.
   ==================================================================== */

.redact-list {
    display: grid;
    gap: var(--space-2);
    margin: 0;
    padding: 0;
    list-style: none;
    font-family: var(--font-mono);
    font-size: var(--fs-300);
}
.redact-row {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: var(--space-2);
}
.redact-mark { color: var(--text-faint); }
.redact-note {
    font-size: var(--fs-100);
    letter-spacing: 0.15em;
    text-transform: uppercase;
    color: var(--text-faint);
}

/*
 * THE BAR. Not a black rectangle -- a hole. The two inset shadows are
 * what sell it: a 1px lip catching the light at the edge of the cut, and
 * a deep inner shadow so the middle reads as depth rather than as fill.
 * Width comes from --redact-w, set inline by the partial from an integer
 * it computed itself.
 */
.redact-bar {
    position: relative;
    display: inline-block;
    min-width: var(--redact-w, 11ch);
    height: 1.15em;
    vertical-align: baseline;
    border-radius: 2px;
    background: #0D0F18;
    box-shadow:
        inset 0 0 0 1px rgba(255, 255, 255, 0.05),
        inset 0 0 24px rgba(0, 0, 0, 0.9);
    overflow: hidden;
}
.redact-bar::after {
    content: "";
    position: absolute;
    inset: 0;
    background: linear-gradient(
        90deg,
        transparent,
        rgba(var(--chrome-rgb), 0.16),
        transparent
    );
    transform: translateX(-100%);
    animation: fb-redact-sweep var(--dur-sweep) ease-in-out infinite;
    animation-delay: var(--redact-d, 0s);
}
@keyframes fb-redact-sweep {
    0%, 70%   { transform: translateX(-100%); }
    85%, 100% { transform: translateX(100%);  }
}

/* The count line above a set of bars. The two numbers are live system
   data — counts the server just computed — and they are the only thing
   in this whole component that is allowed to glow. Released is phosphor
   because it is a measurement; withheld is classification red because it
   is a classification. */
.redact-tally {
    margin: 0 0 var(--s3);
    font-family: var(--font-mono);
    font-size: var(--fs-100);
    letter-spacing: 0.2em;
    text-transform: uppercase;
    color: var(--text-faint);
}
.redact-tally .redact-released {
    color: var(--phosphor);
    text-shadow: var(--glow-phosphor);
}
.redact-tally .redact-held {
    color: var(--classify);
    text-shadow: var(--glow-classify);
}
.redact-more {
    margin: var(--space-2) 0 0;
    font-family: var(--font-mono);
    font-size: var(--fs-100);
    letter-spacing: 0.15em;
    text-transform: uppercase;
    color: var(--text-faint);
}

/* The block wrapper the partial emits. It is not a pane: whether these
   bars sit on glass is the calling page's decision, and most of the time
   they belong INSIDE one. */
.redaction { display: block; }


/* ====================================================================
   7. FOCUS

   NOT a global :focus-visible rule -- app.css already owns exactly one
   of those and a second would be two people solving the same problem in
   two files. This is the ring as a TOKEN plus one opt-in class, for the
   controls in this system whose shape the outline cannot follow: a
   rounded pane, a rotated stamp, a clipped chip.

   There is no outline:none anywhere in this file and there must never
   be one. 025_theme_contract_test.php fails the build over it.
   ==================================================================== */

.focus-ring:focus-visible,
.pane a:focus-visible,
.pane button:focus-visible {
    box-shadow: var(--ring);
    border-radius: var(--radius-1);
}


/* ====================================================================
   8. MOTION

   Every animation and every transition in this file, off. A wildcard,
   precisely because a wildcard cannot be forgotten: a rule added above
   this block on a Tuesday is covered on the Tuesday it is added, by
   somebody who never read this section.

   Two things need more than the wildcard and get it below -- the
   redaction sweep, which must be REMOVED rather than made instant, and
   the hover lift, whose transform is movement even with no transition
   attached to it.
   ==================================================================== */

@media (prefers-reduced-motion: reduce) {
    /*
     * The wildcard, restated. app.css and public.css each carry one of
     * these already, and this is the third copy ON PURPOSE rather than by
     * oversight: depth.css is loaded by both surfaces and would be
     * relying on a guard that lives in a file it does not control, in a
     * sheet that loads BEFORE it. A sheet that animates carries its own
     * guard, full stop. The duplication costs four declarations and
     * removes an ordering dependency nobody would notice breaking.
     */
    *, *::before, *::after {
        animation-duration: 0.01ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: 0.01ms !important;
        scroll-behavior: auto !important;
    }

    /* The sweep is removed rather than sped up. At 0.01ms the wildcard
       above leaves a single instantaneous pass, which on a redaction bar
       is a flash — the one thing a reduced-motion reader most wants gone. */
    .redact-bar::after { display: none; }

    /* And the hover LIFT itself, not only its transition. Reduced motion
       is a request about movement, and a card that snaps six pixels
       upward with no transition has still moved. The brightness, the lit
       edge and the shadow all stay, so the affordance survives: you can
       still see what you are pointing at. */
    .pane--lift:hover,
    .pane--lift:focus-within { transform: none; }
}


/* ====================================================================
   9. PRINT

   Both films are position:fixed, so without this they print on every
   single page of a long recipe -- which is exactly the bug app.css
   already carries a guard for on the film this file retired.

   Everything else in here degrades on its own: a pane that loses its
   backdrop-filter and its shadow is a bordered box, which is what a
   printed panel should be anyway.
   ==================================================================== */

@media print {
    body::before,
    body::after { display: none !important; }

    .pane, .pane.e1, .pane.e2, .pane.e3 {
        background: none;
        border: 1px solid #ccc;
        box-shadow: none;
        -webkit-backdrop-filter: none;
        backdrop-filter: none;
    }
    .stamp   { color: #000; text-shadow: none; box-shadow: none; transform: none; }
    .threat-row { opacity: 1; background: none; border: 1px solid #ccc; box-shadow: none; }
    .redact-bar { background: #000; box-shadow: none; }
    .redact-bar::after { display: none; }
}
