/* wireframes/_wf.css  ---  the ONE home of how a wireframe looks.
 *
 * Grey contract. No colour, no brand, no icons, no shadows, no images: those are stages 06 to 08,
 * and they arrive on COPIES of these screens under design/, never by painting this file.
 *
 * The palette below is inherited verbatim from the black and white mockups of stage 03b
 * (ia/*.html, the .wf block). The type scale is deliberately NOT inherited: those mockups were
 * miniatures inside a document at 10.5px mono, and these are live screens at 1440.
 *
 * Inline CSS on a screen is TRANSPORT, not a home. Two rules decide every declaration:
 *   1. a token VALUE never lives inline, not even once. It is a variable here and a var() there
 *   2. a rule that appears on two or more screens moves here
 * Anything genuinely one-off stays inline, still written through var(), and opens with
 *   /* INLINE: <screen> :: for reconcile into _wf.css *\/
 * so the parent finds every one of them mechanically at step 8 rather than by reading twenty files.
 */

:root{
  /* ---- palette, inherited from the stage 03b mockups ---- */
  --ink:#16181a;        /* text, borders that carry structure */
  --soft:#6a6f76;       /* secondary text, disabled, placeholders. Was #9aa0a6, which measured
                           2.64:1 on the paper and 2.38:1 on the fill, and 46 of this value's
                           48 uses are TEXT. The one line of this folder stage 07 edited after
                           the freeze, on the user's decision: grey to grey, no structure, no
                           string and no state touched. Grounds in docs/decisions.md. */
  --hair:#d4d6d8;       /* hairlines inside a component */
  --fill:#f2f3f3;       /* filled band: table head, resting pane, selected row */
  --paper:#ffffff;      /* surface a component sits on */
  --bg:#fbfbfa;         /* the page behind everything */

  /* ---- type. Density is design principle 5, so the base is small and the scale is short ---- */
  --ui:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif;
  --mono:ui-monospace,SFMono-Regular,"SF Mono",Menlo,Consolas,monospace;
  --t-xs:11px;          /* column heads, chips, meta */
  --t-sm:12.5px;        /* dense data: rows, tables */
  --t-md:14px;          /* body, controls */
  --t-lg:17px;          /* screen title, pane title */
  --t-xl:21px;          /* the one heading a page is allowed */
  --lh:1.45;
  --lh-tight:1.25;

  /* ---- space. One scale, used everywhere ---- */
  --s1:4px; --s2:8px; --s3:12px; --s4:16px; --s5:24px; --s6:32px; --s7:48px;

  /* ---- structure ---- */
  --radius:2px;         /* almost square. A wireframe is not soft */
  --line:1px solid var(--hair);
  --line-ink:1px solid var(--ink);
  --z-top:56px;         /* Z1 top bar */
  --z-strip:26px;       /* Z2 connection strip */
  --pane:380px;         /* Z5 detail pane, resizable in the product */
  --focus:2px solid var(--ink);
}

*,*::before,*::after{box-sizing:border-box}
html,body{margin:0;padding:0}
body{
  background:var(--bg);color:var(--ink);
  font:var(--t-md)/var(--lh) var(--ui);
  -webkit-font-smoothing:antialiased;
}
:focus-visible{outline:var(--focus);outline-offset:1px}

/* A link takes the ink of whatever it sits in. Written here, at the lowest specificity in the
   file and above every component, so any class that wants its own colour still wins.
   Without it this stage shipped 151 browser blue links across 27 screens, in the one stage
   whose contract at the top of this file says there is no colour. Nothing declared them,
   so nothing flagged them: the defect was an ABSENT rule, and the grep instrument can only
   see rules that exist. It took asking the browser for a computed colour to find it. */
a{color:inherit;text-decoration-color:var(--soft);text-underline-offset:2px}
a:hover{text-decoration-color:var(--ink)}

/* ============================================================
   SHELL. The five zones of node 0.1, and the stage panel beside them
   ============================================================ */
.wf-shell{display:flex;min-height:100vh;align-items:stretch}
#sidebar{flex:0 0 var(--nav-w);border-right:var(--line);background:var(--paper);min-width:0}
.wf-screen{flex:1 1 auto;min-width:0;display:flex;flex-direction:column;background:var(--bg)}

/* Z1 top bar */
.z1{flex:0 0 auto;min-height:var(--z-top);display:flex;align-items:center;gap:var(--s5);
    padding:0 var(--s4);border-bottom:var(--line-ink);background:var(--paper)}
.z1 .wordmark{font:600 var(--t-lg)/1 var(--ui);letter-spacing:-.01em}
.z1 nav{display:flex;gap:var(--s4)}
.z1 nav a{color:var(--soft);text-decoration:none;font-size:var(--t-md);padding:var(--s2) 0;
          border-bottom:2px solid transparent}
.z1 nav a[aria-current="page"]{color:var(--ink);font-weight:600;border-bottom-color:var(--ink)}
.z1 nav span{color:var(--soft);font-size:var(--t-md);padding:var(--s2) 0;
             border-bottom:2px solid transparent;text-decoration:line-through;
             text-decoration-color:var(--hair)}
.z1 .spacer{flex:1 1 auto}

/* Z3 annunciator, node 0.3. A fixed slot, read only in the MVP */
/* IT TAKES THE ROOM IT NEEDS AND WRAPS BETWEEN ITS PARTS, NEVER INSIDE ONE. `nowrap` on
   the strip made it overflow at 1280, the declared minimum, and `normal` on the strip broke
   `contain / endpoint` and `DECIDED by / you` in the middle. The strip wraps and the part
   does not. */
.annun{display:flex;align-items:center;gap:var(--s3);font-family:var(--mono);font-size:var(--t-xs);
       border:var(--line-ink);padding:var(--s1) var(--s3);
       white-space:normal;flex:1 1 22rem;min-width:0;flex-wrap:wrap}
.annun .part{white-space:nowrap}
.annun b{font-weight:700}
.annun .sep{color:var(--hair)}
.annun .ovrd{background:var(--ink);color:var(--paper);padding:0 var(--s1);font-weight:700}

/* The keyboard map trigger, 0.2 section 4. One character, so it costs the bar nothing, and
   it is the only way into 0.5 from inside the product. */
.z1 .kmap{font:600 var(--t-sm)/1 var(--mono);color:var(--soft);text-decoration:none;
          border:var(--line);border-radius:var(--radius);padding:var(--s1) var(--s2)}
.z1 .kmap:hover{color:var(--ink);border-color:var(--ink)}

/* Z2 connection strip, node 0.4. Never silent when connected */
/* THE STRIP IS A `<p>` AND NOTHING TOOK THE BROWSER'S MARGIN OFF IT, so Z2 floated with 11px
   of air above and below on all 55 screens that carry it, and the top of every one of them
   sat 22px lower than the coloured screen it is paired with. Nothing in this file declared a
   margin, so nothing flagged one: the defect was an ABSENT rule, the same class as the 151
   blue links, and it was found by putting the two renderings side by side rather than by
   reading either. design/system/components/z2.css has carried margin:0 since stage 08. */
.z2{flex:0 0 auto;min-height:var(--z-strip);display:flex;align-items:center;gap:var(--s3);
    margin:0;
    padding:0 var(--s4);border-bottom:var(--line);background:var(--fill);
    font-family:var(--mono);font-size:var(--t-xs);color:var(--soft)}
.z2 b{color:var(--ink);font-weight:700}
.z2.is-degraded{border-bottom:var(--line-ink);background:var(--paper)}
.z2.is-degraded b{text-decoration:underline;text-underline-offset:2px}

/* Z4 + Z5, the split */
.z45{position:relative;flex:1 1 auto;display:flex;min-height:0;align-items:stretch}
.z4{flex:1 1 auto;min-width:0;display:flex;flex-direction:column;border-right:var(--line-ink);background:var(--paper)}
.z5{flex:0 0 var(--pane);min-width:0;display:flex;flex-direction:column;background:var(--fill);overflow-y:auto}
.z5.is-paper{background:var(--paper)}

/* ============================================================
   SCOPE BAR, node 3.6
   ============================================================ */
.scopebar{display:flex;flex-wrap:wrap;gap:var(--s2);align-items:center;
          padding:var(--s3) var(--s4);border-bottom:var(--line)}
/* The same absent rule as .z2 above: the browser's margin resolved to 8.375px here, where the
   product's own component file says var(--space-2), which is 8. */
.readout{margin:var(--s2) 0;padding:var(--s2) var(--s4);border-bottom:var(--line);font-size:var(--t-sm)}
.readout b{font-weight:700}
.readout .dim{color:var(--soft)}

.chip{display:inline-flex;align-items:center;gap:var(--s1);border:var(--line-ink);
      border-radius:var(--radius);padding:var(--s1) var(--s2);font-size:var(--t-xs);
      background:var(--paper);color:var(--ink);cursor:pointer;font-family:var(--ui);white-space:nowrap}
.chip--ghost{border-style:dashed;color:var(--soft)}
.chip--solid{background:var(--ink);color:var(--paper)}
.chip--state{font-family:var(--mono);text-transform:uppercase;letter-spacing:.04em;cursor:default}

/* ============================================================
   THE ROW, node 3.1 section 4. Canonical: 5.1 reuses it or says why not
   ============================================================ */
/* THE TRACKS BELONG TO THE LIST, NOT TO EACH ROW. Written on the row, every row computes
   its own set and they line up by luck of the pixel; the head row is mono and the body is
   not, so a column head sitting over its column is not something either row can guarantee
   alone. The list declares them once and every row is a `subgrid` of them, keeping its own
   padding, its own rule and its own states. Everything in the list that is NOT a row spans
   them all: making the list a grid makes a grid item of the empty state, the arriving bar
   and the annotation, and each of them would otherwise be placed in the 90px first track.
   Same structure as design/system/components/rows.css. */
.rows{flex:1 1 auto;overflow-y:auto;min-height:0;
      display:grid;grid-template-columns:var(--row-tracks);align-content:start}
.rows > *{grid-column:1 / -1}
.rows > .row{grid-template-columns:subgrid}
.row{display:grid;grid-template-columns:var(--row-tracks);gap:var(--s2);align-items:start;
     padding:var(--s2) var(--s4);border-bottom:var(--line);font-size:var(--t-sm);
     text-decoration:none;color:inherit;line-height:1.35}
/* TO CHECK WAS 72px AND `14 signals` DID NOT FIT IN IT, so the count broke away from its
   noun and the cell read as two lines where the product reads as one. The seven tracks are
   the product's, converted from its rem to this folder's px. */
:root{--row-tracks:90px 120px minmax(104px,1fr) minmax(120px,1fr) 80px 112px 40px}
.row > *{min-width:0;overflow-wrap:anywhere}
.row--head{position:sticky;top:0;background:var(--fill);border-bottom:var(--line-ink);
           font:600 var(--t-xs)/1.2 var(--mono);letter-spacing:.08em;text-transform:uppercase;
           color:var(--soft);padding-top:var(--s2);padding-bottom:var(--s2)}
.row.is-selected{background:var(--fill);box-shadow:inset 3px 0 0 var(--ink)}
.row:hover:not(.row--head){background:var(--fill)}
.row .sev{display:flex;align-items:center;gap:var(--s2);white-space:nowrap;font-family:var(--mono)}
.row .client{font-weight:600}
.row .concluded b{font-weight:600}
.row .dim{color:var(--soft)}
/* A COUNT NEVER LEAVES ITS NOUN. `14 signals` on two lines is not a wrap, it is two
   readings, and the cell exists to be scanned down a column. */
.row .cost,.row .age{font-family:var(--mono);color:var(--soft);text-align:right;
                     white-space:nowrap}
/* THE CHIPS STACK TOWARDS THE COLUMN THEY BELONG TO. Two that did not fit side by side
   wrapped, and the second sat left aligned under a right aligned first, crossing the rule
   under the row. They are one cell and they read as one. */
.row .states{display:flex;flex-wrap:wrap;gap:var(--s1);justify-content:flex-end;
             align-items:flex-start}
/* THE MEASURE ON THE TWO CELLS THAT ARE READ RATHER THAN SCANNED. Neither carries a
   border, so capping the cell IS capping the text and the track stays free to fill the
   row. Same two numbers in design/system/components/row.css. */
.row > span:nth-child(3){max-width:32ch}
.row .concluded{max-width:36ch}

/* severity, node 0.8: glyph plus the word, three levels */
.bars{display:inline-flex;gap:2px;align-items:flex-end;height:12px}
.bars i{display:block;width:3px;border:1px solid var(--ink);background:var(--paper)}
.bars i.on{background:var(--ink)}
.bars i:nth-child(1){height:5px} .bars i:nth-child(2){height:8.5px} .bars i:nth-child(3){height:12px}

/* ============================================================
   PANE CONTENT: fleet 3.5, case file 4.1, log entry 5.4
   ============================================================ */
.pane-head{position:sticky;top:0;background:inherit;padding:var(--s4);border-bottom:var(--line-ink)}
.pane-head h2{margin:0;font:600 var(--t-lg)/var(--lh-tight) var(--ui)}
/* The same subline under a pane title and under a dialog title. It was one rule in _wf.css
   and an identical inline copy on three dialog generators. */
.pane-head .sub,.dialog > header .sub{margin:var(--s1) 0 0;color:var(--soft);font-size:var(--t-sm)}
.pane-body{padding:var(--s4);display:flex;flex-direction:column;gap:var(--s5)}
.pane-foot{margin-top:auto;position:sticky;bottom:0;background:inherit;
           padding:var(--s3) var(--s4);border-top:var(--line-ink);display:flex;gap:var(--s2);flex-wrap:wrap}

/* THE VERDICT FOOT, AND FOUR EQUAL BUTTONS MADE THE MAIN ACTION A QUARTER OF ITS OWN FOOT.
   Accept carries the main job named in CLAUDE.md; the three that qualify it stand under it and
   share the row. Conditioned on the COUNT, not written as a variant: a foot with one, two or
   three actions is a row and stays a row, because nothing there competes. Block priority is
   what this folder owns, so this is a contradiction carried back rather than a stage boundary.
   The same arrangement in the coloured product is design/system/components/pane-foot.css. */
.pane-foot:has(> .btn:nth-of-type(4)){padding:var(--s4);gap:var(--s3)}
.pane-foot:has(> .btn:nth-of-type(4)) > .btn--primary{flex:1 0 100%;justify-content:center;
                                                     padding-block:var(--s3)}
.pane-foot:has(> .btn:nth-of-type(4)) > .btn:not(.btn--primary){flex:1 1 0;justify-content:center}

.block{display:flex;flex-direction:column;gap:var(--s2)}
.block > h3{margin:0;font:600 var(--t-xs)/1 var(--mono);letter-spacing:.1em;text-transform:uppercase;color:var(--soft)}

/* evidence, node 4.1 section 4. The chip expands in place, never over the block */
.claim{border-top:var(--line);padding:var(--s2) 0;display:flex;gap:var(--s2);align-items:baseline}
.claim:first-of-type{border-top:none}
.claim .txt{flex:1 1 auto;min-width:0}
.claim .src{flex:0 0 auto;font-family:var(--mono);font-size:var(--t-xs)}
.claim--absence{color:var(--soft)}
/* THE TAG USED TO BE A `content:` STRING AND IT IS NOW MARKUP. `not found` and `points the
   other way` are interface strings, and stage 05 owns every interface string in this product.
   A string that lives in a stylesheet is one that cannot be found by reading the screens,
   cannot be rewritten without touching the shared file, and is read by no screen reader that
   the analyst might use. Two strings, both on the one surface the whole product exists for.
   It sits INSIDE `.txt`, not beside it. As a sibling it became a flex item of `.claim` and
   took a line of its own at 360, one line per claim on the densest surface in the product.
   A move is supposed to change where a thing is declared and nothing about how it looks. */
.claim .tag{font-family:var(--mono);font-size:var(--t-xs);
            letter-spacing:.06em;text-transform:uppercase;color:var(--soft)}
.claim .tag::after{content:"\00a0\00a0"}
.expand{margin:var(--s2) 0 var(--s3) var(--s4);border-left:2px solid var(--hair);
        padding:var(--s2) var(--s3);font-size:var(--t-sm);background:var(--fill)}
/* THE EXPANSION HAS A HEAD, AND WITHOUT ONE IT PROMISED A CONTROL THAT WAS NOT THERE. The body
   was always open, so design principle 2, "cheapest correct thing first, depth one key away",
   was drawn as depth already spent. 21 of the 27 expansions are a `details` with a `summary`;
   the other six HOLD A LINK and stay a `div`, because a head that is itself a control cannot
   also carry one. The caret is a glyph and not an icon, which is the same licence the scope
   chips already use for their `&#9662;`, so the grey contract at the top of this file holds. */
.expand > summary{cursor:pointer;list-style:none;font-weight:600}
.expand > summary::-webkit-details-marker{display:none}
.expand > summary::before{content:"\25B8\00a0";color:var(--soft)}
.expand[open] > summary::before{content:"\25BE\00a0"}
/* the gap belongs to the OPEN state and not to the head, or a closed expansion carries the air
   of an open one under a line of text */
.expand[open] > summary{margin-bottom:var(--s2)}

.prov{font-family:var(--mono);font-size:var(--t-xs);color:var(--soft);
      border-top:var(--line);padding-top:var(--s2)}
.prov b{color:var(--ink)}

/* fleet, node 3.5 */
.frow{display:grid;grid-template-columns:minmax(0,1fr) 118px 92px;gap:var(--s2);
      padding:var(--s2) var(--s4);border-bottom:var(--line);font-size:var(--t-sm);
      text-decoration:none;color:inherit}
.frow--head{background:var(--fill);border-bottom:var(--line-ink);
            font:600 var(--t-xs)/1.2 var(--mono);letter-spacing:.08em;text-transform:uppercase;color:var(--soft)}
.frow:hover:not(.frow--head){background:var(--paper)}
.frow .rec{font-family:var(--mono);text-align:right}
.frow .was{display:block;color:var(--soft);font-size:var(--t-xs)}

/* ============================================================
   CONTROLS. Real elements, draft strings. Stage 05 owns the words
   ============================================================ */
.btn{font:var(--t-md)/1 var(--ui);border:var(--line-ink);border-radius:var(--radius);
     background:var(--paper);color:var(--ink);padding:var(--s2) var(--s3);cursor:pointer;
     display:inline-flex;align-items:center;gap:var(--s2);text-decoration:none}
.btn--primary{background:var(--ink);color:var(--paper);font-weight:600}
.btn--quiet{border-color:var(--hair);color:var(--soft)}
.btn[disabled],.btn[aria-disabled="true"]{border-color:var(--hair);color:var(--soft);
     background:var(--paper);cursor:not-allowed}
/* A SHORTCUT IS ONE GLYPH IN A BOX AND IT NEVER BREAKS. */
.btn .key{font-family:var(--mono);font-size:var(--t-xs);border:1px solid currentColor;
          border-radius:var(--radius);padding:0 var(--s1);opacity:.75;white-space:nowrap}
.btn--primary .key{opacity:.85}

.field{display:flex;flex-direction:column;gap:var(--s1)}
.field > label{font:600 var(--t-xs)/1 var(--mono);letter-spacing:.08em;text-transform:uppercase;color:var(--soft)}
.field input,.field textarea,.field select{
  font:var(--t-md)/var(--lh) var(--ui);color:var(--ink);background:var(--paper);
  border:var(--line-ink);border-radius:var(--radius);padding:var(--s2) var(--s3);width:100%}
.field .hint{font-size:var(--t-xs);color:var(--soft)}

/* radio list, node 4.4. A radio group, not a menu */
.optlist{display:flex;flex-direction:column;border:var(--line-ink);border-radius:var(--radius)}
/* An option is a link once the state it produces exists as a page: choosing a reason is
   something she DOES, so step 7 gives it a real href rather than a dead label. */
.opt{display:flex;gap:var(--s3);align-items:baseline;padding:var(--s2) var(--s3);
     text-decoration:none;color:inherit;cursor:pointer;
     border-bottom:var(--line);cursor:pointer}
.opt:last-child{border-bottom:none}
.opt.is-chosen{background:var(--fill)}
a.opt:hover{background:var(--fill)}
.opt .key{font-family:var(--mono);font-size:var(--t-xs);border:var(--line-ink);
          border-radius:var(--radius);padding:0 var(--s1);flex:0 0 auto;white-space:nowrap}
.opt.is-chosen .key{background:var(--ink);color:var(--paper)}
.opt .lbl{flex:1 1 auto;min-width:0}
/* THE SAME ZONE STANDS INSIDE `.locked` ON THE SECOND AXIS and had no rule there, so where
   the reason was a locked statement rather than a choice it lost its column. */
.opt .routes,.locked .routes{margin-left:auto;font-family:var(--mono);font-size:var(--t-xs);
             color:var(--soft);flex:0 0 auto;text-align:right}
.dialog > header h2{margin:0;font:600 var(--t-lg)/var(--lh-tight) var(--ui)}

/* ============================================================
   STATE FURNITURE. Every state page uses these, so they live here
   ============================================================ */
.banner{border:var(--line-ink);border-radius:var(--radius);background:var(--paper);
        padding:var(--s3);display:flex;gap:var(--s2) var(--s3);align-items:baseline;flex-wrap:wrap;
        font-size:var(--t-sm)}
.banner > *{min-width:0;overflow-wrap:anywhere}
.banner b{font-weight:700}
.banner--quiet{border-color:var(--hair);color:var(--soft)}
.banner .act{margin-left:auto;flex:0 0 auto}

.empty{padding:var(--s7) var(--s4);text-align:center;color:var(--soft);font-size:var(--t-sm)}
.empty b{display:block;color:var(--ink);font-size:var(--t-md);margin-bottom:var(--s2)}

/* loading is a shape that fills, never a spinner and never a skeleton of the whole screen */
.arriving{height:2px;background:var(--hair);position:relative;overflow:hidden}
.arriving::after{content:"";position:absolute;inset:0 60% 0 0;background:var(--ink)}

.tomb{border:1px dashed var(--soft);border-radius:var(--radius);padding:var(--s3);
      color:var(--soft);font-size:var(--t-sm)}
.tomb b{color:var(--ink)}

/* dialog, nodes 4.4 and 4.6. Over the pane, never over the evidence being decided on */
/* 4.4 says it in its own words: the dialog is ANCHORED, not centred, and it never covers the
   evidence block, because the thing being disagreed with is the least acceptable thing to hide.
   So it is held clear of the pane by the pane's own width, and lands over the list instead. */
.scrim{position:fixed;inset:0;background:var(--scrim);display:flex;
       align-items:flex-start;justify-content:flex-end;
       padding:var(--s6) calc(var(--pane) + var(--s5)) var(--s6) var(--s5)}
.dialog{background:var(--paper);border:var(--line-ink);border-radius:var(--radius);
        width:min(var(--dialog-w),100%);max-height:calc(100vh - var(--s7));overflow:hidden;
        display:flex;flex-direction:column}
.dialog > header{padding:var(--s4);border-bottom:var(--line-ink);flex:0 0 auto}
.dialog > header h2{margin:0;font:600 var(--t-lg)/var(--lh-tight) var(--ui)}
/* The BODY scrolls, not the dialog. Found on 4.6, where the handover is long enough to push
   the footer past the fold: a primary action you have to scroll to find is a primary action
   that is not there, and on the no recipient state the sentence saying WHY it is disabled
   went with it. Header and footer are pinned; only the middle moves. */
.dialog > .body{padding:var(--s4);display:flex;flex-direction:column;gap:var(--s4);
                flex:1 1 auto;overflow-y:auto;min-height:0}
.dialog > footer{padding:var(--s3) var(--s4);border-top:var(--line-ink);flex:0 0 auto;
                 display:flex;gap:var(--s2);align-items:center;flex-wrap:wrap}
.dialog > footer .grow{flex:1 1 auto}
.dialog > footer .btn{flex:0 0 auto;white-space:nowrap}
/* 4.4 settled that reject is not exposed at 360: it needs the taxonomy AND the evidence in
   view, and neither fits on a phone. So the dialog is a desk action and the narrow rendering
   says what the exits are instead of pretending to offer one that was ruled out. */
.only-narrow{display:none}

/* ------------------------------------------------------------
   The prototype carries a 244px stage panel that the product does not, so at the product's
   own minimum of 1280 the screen is running in about 1040. The pane is resizable in the
   product, so narrowing it here is the honest move rather than letting the list scroll
   sideways, which is the one thing the density argument cannot afford.
   ------------------------------------------------------------ */
@media (max-width:1560px){
  :root{--pane:320px}
  .row{padding-left:var(--s3);padding-right:var(--s3)}
  .row--head{padding-left:var(--s3);padding-right:var(--s3)}
}

/* ============================================================
   NARROW. One live screen, narrowed. Not a second file and not a second frame.
   The stance is desktop first: this is the 360 rendering being PROVED, not the origin
   ============================================================ */
@media (max-width:900px){
  .wf-shell{display:block}
  #sidebar{border-right:none;border-bottom:var(--line-ink);flex:none;width:auto}
  .z1{gap:var(--s3);flex-wrap:wrap;min-height:auto;padding:var(--s2) var(--s3)}
  /* 0.3 stays on a phone, so it wraps rather than being dropped or clipped */
  .annun{white-space:normal;flex-wrap:wrap;width:100%;order:2;font-size:10.5px;gap:var(--s2)}
  .annun .sep{display:none}
  .z1 nav{order:3;width:100%;gap:var(--s3);overflow-x:auto}
  .annun{order:2}
  .z2{padding:0 var(--s3)}
  .z45{display:block}
  .z4{border-right:none}
  /* The pane is a desk instrument, so with the fleet at rest it is simply not rendered:
     3.1 settled that. A pane holding a CASE is different. 3.1 says selecting at 360 opens
     the standalone rendering rather than a pane covering the list, so that is what happens
     here: the case becomes the whole page and the list steps aside. Two routes, one
     rendering, which is what 4.2 has always been. */
  .z5{display:none}
  .z5.is-paper{display:flex;flex:1 1 auto}
  .z45:has(> .z5.is-paper) > .z4{display:none}
  .z5.is-standalone{display:flex;flex:1 1 auto}
  :root{--row-tracks:1fr}
  .row{gap:var(--s1);padding:var(--s3)}
  .row--head{display:none}     /* column heads mean nothing once the row is two lines */
  .row .cost{display:none}     /* on a phone she is not choosing what to read next */
  .row .age,.row .sev{text-align:left}
  .scopebar{padding:var(--s2) var(--s3)}
  .pane-body,.pane-head{padding:var(--s3)}
  .scrim{padding:0;align-items:stretch}  /* no pane at 360, so nothing to stay clear of */
  .scrim--desk-only{display:none}
  .only-narrow{display:block}
  .dialog{width:100%;max-height:100vh;border:none}
  .frow{grid-template-columns:minmax(0,1fr) 96px;padding:var(--s2) var(--s3)}
  .frow .rec{grid-column:2}
}

/* utility, and the list is short on purpose */
.dim{color:var(--soft)}
.mono{font-family:var(--mono)}
.vh{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap}

/* ============================================================
   MOVED HERE AT STEP 8, VERBATIM. Nothing below was edited on the way: a rule that is
   tidied while it moves is a change of appearance wearing the clothes of a refactor,
   and it cannot be caught afterwards. The improvements the reconcile found are a
   separate list and a separate decision.
   ============================================================ */

/* The shell chrome, and the queue foot. On all 58 pages. */
#sidebar{padding:var(--s5) var(--s3)}
.qfoot{flex:0 0 auto;padding:var(--s2) var(--s4);border-top:var(--line);
       font-family:var(--mono);font-size:var(--t-xs);color:var(--soft);
       display:flex;gap:var(--s4);flex-wrap:wrap}
.qfoot kbd{font:inherit;border:1px solid var(--hair);border-radius:var(--radius);padding:0 var(--s1)}
.fleet-more{padding:var(--s3) var(--s4);font-family:var(--mono);font-size:var(--t-xs);color:var(--soft)}
/* AND IT HAS AIR UNDER IT. The bottom margin was zero, so the banner's border sat directly on
   the sticky header of the table below it: two rules touching, and the banner read as part of
   the table rather than as a notice about it. The same sentence stands in
   design/system/components/z4.css, because it is the same finding, made on the coloured side
   first and carried back here rather than rediscovered. */
.qbanner{margin:var(--s3) var(--s4)}

/* The case pane: the narrative, the latitude ladder and the snapshot stamp. On 44 pages. */
.nar{margin:0;font-size:var(--t-sm)}
.nar + .nar{margin-top:var(--s2)}
.nar b{font-weight:600}
.nar .when{font-family:var(--mono);font-size:var(--t-xs);color:var(--soft);
           display:inline-block;min-width:46px}
/* THE MEASURE, AND IT HAD NO READER. `--measure` was declared at the bottom of this file
   and `var(--measure)` appears nowhere else in it, so a token stood here for two stages
   reading nothing. The width sweep measured the consequence: 46 page and class pairs run
   past 66 characters, up to 96ch on a shift note. It is ON THE TEXT and not on its
   container, because the container holds blocks that should fill the pane and a measure is
   a property of a line somebody reads ACROSS. The same fourteen selectors carry it in
   design/system/base.css. */
.nar,.gnote,.prov,.cons,.sub,.hint,.empty,.sa-fresh,.sa-route,.chips-hd,.meta,.anote,
.contact p,.toast p{max-width:var(--measure)}
/* THE BAND MUST NOT TAKE THE MEASURE AND ITS SENTENCE MUST. Capping `.rail` would stop the
   inverted band at half the framed record, which is the one thing that band may never do;
   without a cap the sentence ran 148 characters. `.soft` is where the sentence already
   lives. The rest of `.rail` is still page local on five screens under an INLINE marker
   that names this file as its destination, and that debt is not paid here. */
.rail .soft{max-width:var(--measure)}

/* THE LADDER IS A TABLE, AND AS A FLEX ROW IT ONLY LOOKED LIKE ONE. `margin-left:auto` on
   the reason right-aligns it against whatever space is left, so the reason moved with the
   length of the action beside it and the rows stopped sharing a column. `subgrid` is what
   makes every row land on one set of three tracks while each keeps its own padding and its
   own rule. The third track is CAPPED rather than `auto`: as `auto` it asks for the
   reason's max-content and leaves the action names nothing. */
.lat{display:grid;grid-template-columns:auto 1fr fit-content(22ch);
     border:var(--line);border-radius:var(--radius)}
.lat > div{display:grid;grid-column:1 / -1;grid-template-columns:subgrid;
           gap:var(--s2);align-items:baseline;padding:var(--s2) var(--s3);
           border-bottom:var(--line);font-size:var(--t-sm)}
.lat > div > *{min-width:0}
.lat > div:last-child{border-bottom:none}
.lat .mark{font-family:var(--mono);font-size:var(--t-xs)}
.lat .off{color:var(--soft)}
.lat .off .why{grid-column:3;text-align:right;font-size:var(--t-xs)}
.lat .ceiling{background:var(--fill);font-weight:600}

/* THE ROTA HAD MARKUP ON SEVEN PAGES AND NOT ONE RULE IN THIS FILE, so three spans that
   are a row stacked into three lines. Same shape as the ladder above it, and the same
   rules stand in design/system/components/rota.css. */
.rota{display:flex;flex-direction:column;border:var(--line);border-radius:var(--radius)}
.rota > div{display:flex;gap:var(--s2);align-items:baseline;padding:var(--s2) var(--s3);
            border-bottom:var(--line);font-size:var(--t-sm)}
.rota > div:last-child{border-bottom:none}
.rota .who{font-weight:600;white-space:nowrap}
.rota .role{color:var(--soft);font-size:var(--t-xs)}
.rota .when{margin-left:auto;white-space:nowrap;font-family:var(--mono);
            font-size:var(--t-xs);color:var(--soft)}
.rota .is-now{background:var(--fill)}
.stamp{font-family:var(--mono);font-size:var(--t-xs);color:var(--soft);
       border-top:var(--line);padding-top:var(--s2);overflow-wrap:anywhere}
.chips-hd{display:flex;gap:var(--s1);flex-wrap:wrap;margin-top:var(--s2)}

/* The consequence box. Reject and escalate, 9 pages. */
.cons{border:var(--line);border-radius:var(--radius);padding:var(--s2) var(--s3);
      font-size:var(--t-sm);min-height:58px}
.cons.is-empty{border-style:dashed;color:var(--soft)}
.cons b{font-weight:600}

/* The log row, three slots changed, and the pane that states what a view covers. 7 pages. */
/* The canonical row, three slots changed. 5.1 section 4 owns the list of changes; this owns
   only the widths they need. `To check` is gone and `Age` became an absolute UTC stamp, and
   an ISO 8601 string does not fit in the 40px the relative age used.
   Everything except the stamp is FLUID, for the same reason the queue's row is: the pane is
   resizable, and the prototype's own 244px panel means the screen runs in about 1040 at 1280. */
/* THE SECOND TRACK WAS 96px AND THE PRODUCT GIVES IT 124. Same class as the queue's TO
   CHECK: a fixed column this folder never widened. The three flexible tracks stay in `fr`
   rather than the product's `ch`, on purpose: `ch` resolves against the element's own font
   and would put a type dependence into a grey table. */
.rows--log{--row-tracks:74px 124px minmax(0,1fr) minmax(0,1.6fr) minmax(0,1fr) 106px 138px}
/* The stamp never breaks mid string: half an ISO 8601 on each line is not the grammar
   0.8 defined. If it stops fitting, that is a measurable failure rather than an ugly wrap. */
.rows--log .when{font-family:var(--mono);font-size:var(--t-xs);color:var(--soft);
                 white-space:nowrap;overflow-wrap:normal}
.rows--log .states{display:flex;flex-wrap:wrap;gap:var(--s1);align-content:start}
.rows--log .states .chip{white-space:normal;text-align:center}
.rows--log .why{font-size:var(--t-xs);color:var(--soft)}
.row.is-superseded{background:repeating-linear-gradient(135deg,transparent 0 7px,var(--fill) 7px 8px)}
.row .sup{display:block;font-size:var(--t-xs);color:var(--soft);margin-top:var(--s1)}
.covers{display:flex;flex-direction:column;gap:var(--s1);font-size:var(--t-sm)}
.covers .k{font:600 var(--t-xs)/1 var(--mono);letter-spacing:.08em;text-transform:uppercase;
           color:var(--soft)}
.covers .v{margin-bottom:var(--s2)}
.seek{display:flex;flex-direction:column;gap:var(--s3)}

/* The log pane, and 5.6 inside it. 7 pages. */
/* The log's pane is white like 4.1's, but it deliberately does NOT take `is-paper`.
   That class is what tells _wf.css to swap the pane in for the list at 360, and on this
   screen there is no swap to make: nothing here renders on a phone. Same paint, no swap. */
.z5--paper{background:var(--paper)}

/* ============================================================
   FOUND BY THE FAN-OUT, FIXED AFTER THE RECONCILE PAIRS MATCHED.
   Seven agents drew seven screens against this sheet at the same time, which is the only
   way these could have surfaced: four of them were found independently by more than one
   agent, and the same missing utility was privately re-invented three times.
   Nothing here was folded into the move itself. The move proved zero pixels changed; this
   section is the part that deliberately changes them.
   ============================================================ */

/* 1. THE BANNER WAS A FLEX CONTAINER, so every run of plain text between two bold runs became
   its own flex item with a 12px gap and its own wrap point. 30 pages carry a banner with two
   or more bold runs. Found by four agents separately, and best said by one of them: every
   screen built before the fan-out happened to put its bold only at the start, so it never
   showed. Text flows as a paragraph now and the action floats clear of it. */
/* `:not(.only-narrow)` is load bearing. Plain `.banner{display:flow-root}` has the same
   specificity as `.only-narrow{display:none}` above and sits later in the file, so it won
   the cascade and showed the 360 only block at 1440 on twenty two pages. */
.banner:not(.only-narrow){display:flow-root}
/* THE ACTION ZONE SOMETIMES CARRIES A REASON, AND THERE WAS NO RULE BETWEEN THEM. On the
   reconnecting queue it holds a disabled control and the sentence saying why there is
   nothing for it to do; the two were siblings with nothing declared between them, so the
   sentence began on the control's border at a zero pixel gap. THE CONTROL IS ON TOP AND ITS
   REASON IS UNDER IT, not beside it. The float stays: it is this folder's own cure for the
   bold runs splitting into flex items, and the arrangement INSIDE the zone is independent
   of it. Same arrangement in design/system/components/banner.css. */
/* THE BANNER THAT CARRIES AN ACTION IS TWO DECLARED TRACKS, NOT A FLOAT AND NOT A
   WRAPPING ROW. The reason under the control, right aligned in 22 characters, was the
   arrangement this folder was given on 2026-08-26 and the owner rejected it on sight: a
   ragged right column hanging under a button is a stranded paragraph, not a qualifier.
   It is a SECOND LINE OF THE NOTICE. It leaves the action zone in the markup, takes its
   place under the sentence at the same left edge in the secondary ink, and carries no
   cap of its own, because `.say` above it has none and a shorter right edge on the line
   underneath reads as the sentence having been chopped. The control then holds one
   thing and centres against the whole block. Same arrangement, and the same reasons, in
   design/system/components/banner.css. */
/* `:not(.only-narrow)` IS LOAD BEARING, and it is the trap the coloured file already
   paid for: `:has()` takes the specificity of its argument, so `.banner:has(> .act)` is
   0,2,0 and beats the 0,1,0 utility that hides a narrow-only banner at the desk. Without
   it this rule turns `display:none` back into `display:grid`. */
.banner:not(.only-narrow):has(> .act){display:grid;grid-template-columns:minmax(0,1fr) auto;
                    align-items:start;gap:var(--s2) var(--s4);float:none}
.banner:not(.only-narrow):has(> .act) > .say,
.banner:not(.only-narrow):has(> .act) > .why{grid-column:1}
.banner:not(.only-narrow):has(> .act) > .act{grid-column:2;grid-row:1 / -1;align-self:center;
                           float:none;margin:0;display:flex;align-items:center}
.banner .why{color:var(--soft)}

/* 2. `.only-narrow` HAD NO COMPLEMENT, so three generators privately invented `.esc-desk`,
   `.out-desk` and `.only-desk` for the identical job. Three names for one utility is not
   three mistakes, it is one hole in this file found three times. */
.only-desk{display:block}
.only-desk-i{display:inline}
/* A UTILITY SAYS WHETHER SOMETHING SHOWS, NOT WHAT IT IS. `display:block` on a button
   overrode the button's own `inline-flex`, so its label and its key stopped being flex items
   and the key rendered as a block under the word. The utility keeps its job and the
   component keeps its display.
   IT IS SCOPED TO THE ONE THAT IS SHOWN HERE, and that is not a detail. Written as
   `.btn.only-desk,.btn.only-narrow` it outranked `.only-narrow{display:none}` above and put
   the 360 only button on the desk on eight screens. The complement is restored inside the
   narrow block, where `.only-narrow` is the one that shows. */
.btn.only-desk{display:inline-flex}

/* 3. `.pane-foot` IS STICKY AND NEVER STUCK. `min-height:100vh` let the shell grow with its
   content, so there was no scroll container for `bottom:0` to resolve against and the pane's
   own `overflow-y:auto` never engaged either. The columns scroll, the page does not. */
.wf-shell{height:100vh}
.z4{overflow-y:auto}

/* 4. THE ANNUNCIATOR OVERFLOWED AT 1280 on every page carrying three parts: `nowrap` plus a
   third part pushed the analyst's name past the right edge, measured at 1344 in a 1280 window.
   1280 is the product's declared minimum, so this was a defect at the width the spec names.
   It wraps inside its own border rather than losing a part or clipping the bar. */
@media (max-width:1400px){
  .z1{flex-wrap:wrap;padding-top:var(--s2);padding-bottom:var(--s2)}
  .annun{white-space:normal}
}

/* 5. AN EMPTY KEY SLOT RENDERED AS AN EMPTY BOX. `foot()` always emits the span, so a control
   with no shortcut wore a bordered blank that reads as a key nobody can press. */
.btn .key:empty{display:none}

/* 6. THE CLAIM'S SOURCE CHIP PARKED AT THE TOP RIGHT at 360 and the claim wrapped underneath
   it, so a two line claim read as two columns. It takes its own line now. */
@media (max-width:900px){
  .wf-shell{height:auto}
  .z4{overflow-y:visible}
  /* 13. THE PANE FOOT WAS STICKY AND NEVER STUCK AT 360. The shell stops scrolling here and
     the document takes over, but .z5 kept its own `overflow-y:auto`, so `bottom:0` pinned to
     a box that never scrolls and the decision sat two viewports down. 4.2 had found this and
     fixed it privately for one screen; every other pane kept the defect. */
  .z5,.z5.is-paper{overflow:visible}
  .pane-foot{padding:var(--s2) var(--s3)}
  /* 14. AND THE VERDICT ARRANGEMENT GOES BACK AT 360, NOT JUST THE PADDING. `:nth-of-type`
     counts what is in the DOM, so on a pane where three of the four are `.only-desk` and not
     rendered the selector still matches and `flex:1 0 100%` turns the one remaining action
     into a full bleed bar. All three declarations revert. */
  .pane-foot:has(> .btn:nth-of-type(4)){padding:var(--s2) var(--s3);gap:var(--s2)}
  .pane-foot:has(> .btn:nth-of-type(4)) > .btn--primary{flex:0 1 auto;padding-block:var(--s2)}
  .pane-foot:has(> .btn:nth-of-type(4)) > .btn:not(.btn--primary){flex:0 1 auto}
  .claim{flex-wrap:wrap}
  .claim .src{margin-left:auto}
  /* AND THE BUTTON'S OWN DISPLAY HAS TO BE HIDDEN AT ITS OWN SPECIFICITY. Restoring
     `inline-flex` to a `.btn.only-desk` above is 0,2,0 and this line was 0,1,0, so the
     desk-only control stayed on six reject screens at 360 and R8 reported every one.
     Third time this session that `:has()` or a compound beat a bare utility class. */
  .only-desk,.only-desk-i,.btn.only-desk{display:none}
  .btn.only-narrow{display:inline-flex}
}

/* 7. A SECTION HEADING IS NOT ALWAYS AN h3. In the pane it sits under the pane's h2, so h3 is
   right; in Z4 with no pane title above it the same heading has to be an h2, and the shift
   brief had to restyle it to the same face by hand. */
.block > :is(h2,h3){margin:0;font:600 var(--t-xs)/1 var(--mono);letter-spacing:.1em;
                    text-transform:uppercase;color:var(--soft)}

/* 8. THE SMALL SOFT NOTE UNDER A BLOCK is the most duplicated thing in the stage: it was
   written as a literal style attribute in three generators and on a dozen blocks. */
.gnote{margin:var(--s2) 0 0;font-size:var(--t-xs);color:var(--soft)}

/* 9. THE ADDRESS BLOCK EXISTED TWICE, once on 5.4 and once on 8.1, same hairline and same
   wrapping. One component, and the sign in page can adopt it instead of a bare code chip. */
.addr{display:flex;gap:var(--s2);align-items:center;flex-wrap:wrap;
      border:var(--line);border-radius:var(--radius);padding:var(--s2) var(--s3);background:var(--fill)}
.addr code{font-size:var(--t-sm);overflow-wrap:anywhere;flex:1 1 220px}

/* 10. A COLUMN WITH NO PANE BESIDE IT was written twice under two names, `.z4--entry` on 5.4
   and `.z4--solo` on 8.1. It is one situation: the screen IS the page. */
.z4--solo,.z4--entry{border-right:none;overflow-y:auto;align-items:center}

/* 11. THE FLEET ROW REUSED INSIDE A PADDED PANE BODY doubles the padding, which the shift
   brief hit first because it is the first screen to put the rota inside a pane. */
.pane-body .frow{padding-left:0;padding-right:0}

/* 12. TOKENS THE STAGE NEEDED AND DID NOT HAVE. Four different measures were written as raw
   lengths on four screens, a space token was doing a tap target's job, and the dialog width
   was a literal in one place and a second literal in another. */
:root{
  --scrim:rgba(22,24,26,.28);  /* --ink at 28 per cent. The dialog ground */
  --measure:66ch;       /* a single column of prose. 8.1 */
  --sheet:760px;        /* a case sitting on its own with no list beside it. 4.2 */
  --frame:820px;        /* the framed record. 5.4 */
  --dialog-w:520px;     /* 4.4, 4.6 */
  --dialog-w-wide:720px;/* 0.5, which is a reference table rather than a decision */
  --toast:400px;        /* 8.4, the notice stack. Narrow enough that it never reaches --pane */
  --z6-clear:180px;     /* what the page ends above at 360 so one notice hides no content */
}


/* ---------------------------------------------------------------------------------------
   14. THE AUTHOR'S NOTE IS NOT PRODUCT COPY, AND UNTIL STEP 9 IT LOOKED EXACTLY LIKE IT.
   The prototype argues for its own decisions in the same typographic slots as real hints,
   in the second person, addressed to a reviewer. Two instruments found it independently:
   the browser pass called it the dominant defect of the stage, and the clean context reader
   could not tell which sentences stage 05 was supposed to rewrite.
   Settled by the user: the argument stays on the screen and takes a register of its own.
   A dotted rail and a WHY label, so it reads as a margin note at a glance and can never be
   mistaken for a string the analyst is meant to act on. Stage 05 owns everything that is
   NOT `.anote`; `.anote` belongs to whoever is presenting the case.

   The class is `.anote` and not `.why`, which was the first name written here: `.why` is
   already owned three times over, by the latitude ladder, the log row and the outage line.
   The fifth collision of this stage and the first one caught before it shipped.
   --------------------------------------------------------------------------------------- */
.anote{margin:var(--s3) 0 0;padding:0 0 0 var(--s3);border-left:1px dotted var(--soft);
       font-size:var(--t-xs);line-height:1.55;color:var(--soft)}
.anote::before{content:"WHY";display:block;margin-bottom:var(--s1);
               font:700 9.5px/1 var(--mono);letter-spacing:.14em;color:var(--soft)}
.anote b,.anote strong{color:var(--soft);font-weight:700}
.anote code{font-size:inherit}
.anote + .anote{margin-top:var(--s2)}


/* ---------------------------------------------------------------------------------------
   15. THE NARROW OVERFLOW RESETS LIVE AT THE END OF THE FILE ON PURPOSE.
   Written first inside the narrow block at section 13, they lost to `.z4--solo,.z4--entry`
   at section 10, which is later in the file and carries the same specificity. A media query
   adds no weight, so the only thing that decides it is order. Anything overriding a rule
   from the moved sections belongs below them.
   --------------------------------------------------------------------------------------- */
@media (max-width:900px){
  .z4--solo,.z4--entry{overflow:visible}

  /* 4.4 AT 360 HAD NO PRIMARY AT ALL, and its own banner names the exit in as many words:
     "on a phone the exit is escalate, and only escalate". Accept is `only-desk` there, so the
     footer lost its primary with it and the one action the screen names rendered as an
     ordinary outlined button. Found by the final sweep, which counted primaries per page and
     flags zero as loudly as it flags two.

     `.btn--primary-narrow` WAS THE FIX AND IT ONLY DID HALF THE JOB. It repainted the button
     at 360 and never hid it at the desk, so on all six reject screens a second action sat in
     the pane foot while the dialog was open, and 4.4 says the dialog owns the primary there.
     It is gone: the markup uses `btn--primary only-narrow`, which is display driven, does hide
     at the desk, and is the utility this file already had. Two names for one job was the
     defect `.only-desk` was consolidated out of at step 2 of this same section. */
}


/* ---------------------------------------------------------------------------------------
   16. ZONE Z6, THE TOAST STACK. 8.4 was specified as MVP with three notices marked `yes`
   and not one of them was drawn: step 9 found a whole node living only in prose.

   It is anchored to `.z45`, not to the viewport, and that is the node's rule rather than a
   preference: 8.4 says bottom left, above the list and NEVER over the detail pane. A fixed
   element would sit over the pane at 1440, which is where the evidence being decided on is.
   `--toast` is 400px, and the list is never narrower than that plus the pane, so the two
   cannot meet.

   No drop shadow. The grey contract at the top of this file says no shadows, so the lift
   comes from an ink border against the hairlines of the list, and the assertive notice is
   separated by border weight rather than by colour, which is the only lever a B/W stage has.
   --------------------------------------------------------------------------------------- */
.z6{position:absolute;left:var(--s4);bottom:var(--s4);z-index:5;
    width:min(var(--toast),calc(100% - var(--s6)));display:flex;flex-direction:column;gap:var(--s2)}
.z6-more{margin:0;align-self:flex-start;border:var(--line);border-radius:var(--radius);
         background:var(--fill);padding:var(--s1) var(--s2);
         font:var(--t-xs)/1.4 var(--ui);color:var(--soft)}
.toast{display:flex;gap:var(--s3);align-items:flex-start;background:var(--paper);
       border:var(--line-ink);border-radius:var(--radius);padding:var(--s3);
       font-size:var(--t-sm);line-height:1.45}
.toast p{margin:0;flex:1 1 auto;min-width:0}
.toast .role{display:block;margin-bottom:var(--s1);font:700 9.5px/1 var(--mono);
             letter-spacing:.14em;color:var(--soft)}
.toast .t-x{flex:0 0 auto;width:var(--s5);height:var(--s5);display:flex;align-items:center;
            justify-content:center;border:var(--line);border-radius:var(--radius);
            color:var(--soft);text-decoration:none;font-size:var(--t-md);line-height:1}
.toast .t-x:hover{border-color:var(--ink);color:var(--ink)}
/* The failure notice is not dismissible while the verdict is unrecorded, so it carries no
   close control at all. A disabled X would be a control that argues with itself. */
/* THE HOLD IS A NOTE ABOUT THE ALERT, SO IT GOES UNDER IT AND READS IN ONE LINE. Beside the
   message it broke into two right aligned ragged lines in the space the dismiss used to
   occupy, and `max-width:9em` was the width that produced them. */
.toast--alert{border-width:2px;flex-wrap:wrap}
.toast--alert .t-hold{flex:1 1 100%;font-size:var(--t-xs);color:var(--soft);text-align:right}

@media (max-width:900px){
  /* 8.4 state matrix, the 360 column: bottom, full width, above the safe area. There is no
     `.z45` box to hang off at this width because the layout is `display:block`, so this is
     the one place the stack is fixed to the viewport. It still never covers the pane,
     because at 360 there is no pane: the case takes the whole screen. */
  .z6{position:fixed;left:0;right:0;bottom:0;width:auto;
      padding:var(--s2) var(--s3) calc(var(--s3) + env(safe-area-inset-bottom));
      background:var(--bg);border-top:var(--line-ink)}

  /* THE CAP IS ONE HERE, NOT THREE, AND DRAWING IT IS WHAT SETTLED THAT. Measured at 360:
     three notices took 408 of 760 pixels, so the notice layer covered more of the phone than
     the case did. 8.4 says the cap is a layout decision and the persistence is the compliance
     one, so the cap may differ per width and the persistence may not. The one that stays is
     the failure, because it is the one that cannot be dismissed; with no failure present the
     newest stays. The rest join the count, which is why every stack carries a second count
     line for this width. */
  .z6:has(.toast--alert) .toast:not(.toast--alert){display:none}
  .z6:not(:has(.toast--alert)) .toast:not(:last-of-type){display:none}

  /* And the page has to end above it, or the last row is unreachable at the bottom of a scroll. */
  .z45:has(> .z6){padding-bottom:var(--z6-clear)}
}


/* ---------------------------------------------------------------------------------------
   17. WHAT WAS STILL INLINE, AND THE THREE NAMES FOR ONE UTILITY THAT SECTION 2 ONLY
   HALF FIXED.

   Section 2 above says `.esc-desk`, `.out-desk` and `.only-desk` were three names for one
   job and declares `.only-desk` as the survivor. It declared the survivor and never migrated
   the other two: both generators still shipped their own private copy, and `genunavailable`
   still carried the comment "the complement of _wf.css's .only-narrow, which has no
   complement", which had stopped being true the moment section 2 was written.
   **A comment that describes a fix is not the fix**, and a file that asserts something false
   about itself is worse than one that says nothing. Both callers now use `.only-desk` and
   `.only-desk-i`, and the two private definitions are gone.

   The rest below is the same criterion applied honestly: a rule on two or more screens lives
   here. Everything that stayed inline after this is genuinely used once.
   --------------------------------------------------------------------------------------- */
.dialog > footer .hint{font-size:var(--t-xs);color:var(--soft);flex:1 1 0;min-width:0}
.nar--sep{margin-top:var(--s3)}
.cons--gap{margin-top:var(--s2)}
.empty--tight{padding:var(--s5)}
/* `.empty b` is display:block, because in an empty state the first line is a heading.
   Two screens needed a bold that stays inside the sentence instead. */
.empty b.run{display:inline;font-size:inherit}
.wrapline{margin:var(--s2) 0 0;display:flex;gap:var(--s2);flex-wrap:wrap}

/* 5.1 section 8: the log is NOT rendered at 360. `CLAUDE.md` gives the phone one scenario,
   a paged case read and escalated, and answering an auditor is not it. So the narrowed
   rendering says that instead of squeezing seven columns into a phone. 5.4 is the boundary
   case and it is different: a permalink can arrive anywhere. Five pages carried these three
   rules inline. */
@media (max-width:900px){
  .z4--log .scopebar,.z4--log .rows,.z4--log .qfoot,
  /* AND UNDER THE POINT IT IS ONE COLUMN, with the action back under the sentence.
     Two declared tracks do not negotiate: at 360 the text column took what was left
     after a button. It ranges left, because on one column there is nothing to range
     against and a lone control on the right edge of a phone reads as a mistake. */
  .banner:not(.only-narrow):has(> .act){grid-template-columns:minmax(0,1fr)}
  .banner:not(.only-narrow):has(> .act) > .act{grid-column:1;grid-row:auto;align-self:start;justify-self:start}
  .z4--log .banner:not(.only-narrow){display:none}
  .z4--log .only-narrow{margin:var(--s4)}
}
