/* ⛔ THE APP'S OWN SCOPE, NOT THE SHELL'S — see os-vault.css for the whole
   reasoning. Rules hang off `.osApp`, the app root, so the app draws the same
   wherever the shell mounts it. Header, gutter and tokens come from
   os-app.css; the tabs are the shell's and are NOT re-declared here — the
   Passwords app duplicated them once and the two sheets drifted apart the
   first time either was touched.
   ⛔ EVERY COLOUR IS A TOKEN. This app renders on a light page and a dark one
   from the same rules; a hard-coded hex is how a screen ends up looking
   broken in the theme its author was not using. */
body.os.inthread.osAppOpen #osMain .thread,
body.os.inthread.osAppOpen #osMain .composer,
body.os.inthread.osAppOpen #osMain .rhead{visibility:hidden}

/* ⛔ THE BAR IS THE SHELL'S — `.osAppBar.withTabs` in os-app.css. It was
   `.mlBar` + `.mlQ` here, a third private copy of the same frame beside
   Passwords' and Site X-ray's, and three copies drift three ways. Same for the
   waiting and refusal boxes, now `.osAppEmpty` and `.osAppRefuse`.
   SPEC-APP-STANDARD §2. */

/* ── the list ─────────────────────────────────────────────────────────────
   ⚡ ONE GRID FOR THE HEADER AND EVERY ROW, declared once as a custom property.
   The column head and the rows were two grids with two sets of numbers in the
   concept, and they drifted by 4px the first time either changed — a table
   whose header does not sit over its own column reads as broken long before
   anyone can say why.
   ⛔ The token lives on the APP ROOT, not on .mlList — no such element ever
   rendered, so the token resolved nowhere and every grid fell back to one
   column. Four columns: the checkbox owns the first 22px, hence the head and
   the shelf offset their first child past it. */
.osApp[data-app="mail"]{--mlGrid:22px minmax(0,178px) minmax(0,1fr) 62px}
.osApp[data-app="mail"] .mlHead,
.osApp[data-app="mail"] .mlRow{
  display:grid;grid-template-columns:var(--mlGrid);gap:18px;align-items:baseline;
  width:100%;text-align:left;padding:13px 14px}
.osApp[data-app="mail"] .mlHead>span:first-child,
.osApp[data-app="mail"] .mlShelf>span:first-child{grid-column:2}
.osApp[data-app="mail"] .mlHead{
  font:500 9.5px/1 var(--body);letter-spacing:.14em;text-transform:uppercase;
  color:var(--faint);padding-bottom:9px;border-bottom:1px solid var(--line)}
.osApp[data-app="mail"] .mlHead span:last-child{text-align:right}

.osApp[data-app="mail"] .mlRow{
  border:0;border-bottom:1px solid var(--line);background:transparent;position:relative;
  color:var(--ink);cursor:pointer;transition:background .12s}
.osApp[data-app="mail"] .mlRow:hover{background:var(--surf)}
.osApp[data-app="mail"] .mlRow.on{background:var(--surf2)}
/* Thread children indent in every scope — the shell already does; the base
   sheet left them flush, so an opened thread read as duplicate rows. */
.osApp[data-app="mail"] .mlRow.sub{padding-left:32px}
/* A selected row keeps its tint outside the shell too. */
.osApp[data-app="mail"] .mlRow.sel{background:color-mix(in srgb,var(--violet) 10%,transparent)}
/* ⛔ §5: opening is a STRETCHED BUTTON under the text (the house pattern from
   mail-page.mjs's .rowopen), the checkbox and thread count sit above it.
   Siblings, never nested — both natively keyboard-operable. */
.osApp[data-app="mail"] .mlOpen{position:absolute;inset:0;z-index:0;border:0;
  border-radius:var(--r-s);background:transparent;padding:0;cursor:pointer}
.osApp[data-app="mail"] .mlOpen:focus-visible{outline:1px solid var(--line2);outline-offset:-1px}
.osApp[data-app="mail"] .mlCk,
.osApp[data-app="mail"] .mlThreadN{position:relative;z-index:1}
/* The checkbox is a real button: strip the native chrome, keep the 16px box
   the shell already draws. The tick uses the shared on-fill token, never a
   hard-coded white — this sheet renders on a light page and a dark one. */
.osApp[data-app="mail"] .mlCk{width:16px;height:16px;flex:none;align-self:center;
  justify-self:start;padding:0;border:1.5px solid var(--line2);border-radius:var(--r-s);
  background:transparent;cursor:pointer}
.osApp[data-app="mail"] .mlRow:hover .mlCk{border-color:var(--violet)}
.osApp[data-app="mail"] .mlCk.on{background:var(--violet);border-color:transparent}
.osApp[data-app="mail"] .mlCk.on::after{content:'';position:absolute;left:5px;top:2px;
  width:4px;height:8px;border:solid var(--onfill,#fff);border-width:0 2px 2px 0;transform:rotate(45deg)}
.osApp[data-app="mail"] .mlFrom{
  font:400 13px/1.4 var(--body);color:var(--muted);
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
/* ⚡ UNREAD IS WEIGHT, NOT A DOT. A dot is a second thing to look at; the
   sender's own name carrying the weight says the same in the place the eye
   already is. Colour is not used — it is reserved for state that matters more. */
.osApp[data-app="mail"] .mlRow.unseen .mlFrom{color:var(--ink);font-weight:600}
/* ⛔ A PAPERCLIP, NOT A CHIP (owner, 2026-09-06): no plate, no background —
   the glyph alone in the muted ink, the word lives in the title. */
.osApp[data-app="mail"] .mlAttMark{margin-right:6px;font:400 12px/1 var(--body);color:var(--faint)}
/* ⚡ ONE LINE, said once (§4 a6e6e21b). Two stylesheets disagreed about whether
   a row is one line or two: this sheet drew the sub-line as a second BLOCK
   line while the shell overrode it to inline — so the row's geometry depended
   on which sheet won, and the snippet crossed the date column. The shell is
   where the app actually runs, so the shell wins and this sheet agrees with
   it: subject and sub-line share one flex line, the subject shrinks, the
   sub-line never does, and .mlMid carries overflow:hidden so nothing — not
   even a hostile 200-character snippet — can paint past the date column. */
.osApp[data-app="mail"] .mlMid{min-width:0;display:flex;align-items:baseline;gap:8px;overflow:hidden}
.osApp[data-app="mail"] .mlSub{
  display:block;flex:1 1 auto;min-width:0;font:500 13.5px/1.4 var(--disp,inherit);color:var(--ink);
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.osApp[data-app="mail"] .mlSubline{
  display:inline-flex;gap:6px;flex:none;font:400 11.5px/1.5 var(--body);color:var(--muted);
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.osApp[data-app="mail"] .mlWhen{
  font:400 11px/1.4 var(--mono);color:var(--faint);text-align:right;white-space:nowrap}

/* ── the shelf: folded, named, never hidden ───────────────────────────── */
.osApp[data-app="mail"] .mlShelf{
  display:grid;grid-template-columns:var(--mlGrid);gap:18px;align-items:baseline;
  width:100%;text-align:left;padding:13px 14px;border:0;background:transparent;
  color:var(--faint);cursor:pointer;font:400 12.5px/1.4 var(--body)}
.osApp[data-app="mail"] .mlShelf:hover{background:var(--surf);color:var(--muted)}
.osApp[data-app="mail"] .mlShelfWhat{
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.osApp[data-app="mail"] .mlShelfMark{text-align:right;font:400 13px var(--mono)}

/* ── absence, said out loud ───────────────────────────────────────────── */
.osApp[data-app="mail"] .mlEmpty,
/* ⚠ The Sent caveat is part of the list, not a footnote to it: it is the
   difference between "nobody answered" and "we hold no copy". */
.osApp[data-app="mail"] .mlCaveat{
  display:block;margin-top:7px;font:400 11.5px/1.55 var(--body);color:var(--muted);max-width:62ch}

/* ── the app body becomes a two-column when a letter is open ─────────────
   ⚡ THE LIST AND THE READING PANE ARE SIBLINGS, NOT OVERLAYS. A drawer
   painted with `position:fixed` is a strip the dock is allowed to eat
   (the previous failure), and the list behind it stays full-width, so a
   person is reading a 430px letter on a 1178px pane — half the page is
   the list they have to look past.
   The two are flex siblings inside the same card. When a letter is open
   the list collapses to a narrow SENDER STRIP (the column the eye already
   scans for the next letter), and the drawer takes the rest. The same
   flexbox also has a `data-reading="false"` mode where the drawer
   vanishes and the list takes 100% — no JavaScript, just an attribute. */
/* ⛔ THE ACTION BAR SAT BELOW THE WHOLE LETTER, AND IT WAS A HEIGHT CHAIN, NOT
   A POSITIONING BUG (owner, 2026-09-08: "why action button are soooo down the
   list? cant use app normally in this way").
   `.mlRead` is `height:100%`, `.mlReadScroll` is `flex:1;min-height:0;overflow:auto`
   and `.mlReadFoot` is `flex:none` — a correctly built pinned footer. It never
   worked because no DEFINITE height ever reached it. Measured:
       .osApp              fixed, top→bottom, overflow:auto   ← the real scroller
       .wrap.wide          padding/width/max-width only       → auto
       .osAppCard          border/radius/overflow only        → auto
       .osAppBody.mlBody2  min-height:420px only              → auto
   `height:100%` against an auto parent resolves to auto, so `overflow:auto`
   never engaged, the letter expanded the card, and the footer painted after it.
   ⚡ The fix is to let the CARD be the scroller: give every link in the chain a
      grow and `min-height:0`. Both panes then scroll independently, which is
      what a two-pane mail client is.
   ⛔ `min-height:0` is not optional on any of them — a flex item's default
      `min-height:auto` refuses to shrink below its content, which is exactly
      how a chain like this fails silently.
   ⛔ And never `position:fixed` on the footer: `.osApp` carries a
      `backdrop-filter`, which makes it a containing block for fixed
      descendants — it would look right today and break the day that filter
      moves. Sticky is the belt-and-braces, below. */
.osApp[data-app="mail"] .wrap.wide,
.osApp[data-app="mail"] .osAppCard{
  flex:1 1 auto;min-height:0;display:flex;flex-direction:column}
.osApp[data-app="mail"] .osAppBar{flex:none}
.osApp[data-app="mail"] .osAppBody.mlBody2{
  display:flex;gap:0;padding:0;align-items:stretch;
  flex:1 1 auto;min-height:min(420px,60vh)}
.osApp[data-app="mail"] .osAppBody.mlBody2 .mlListPane{
  flex:1 1 auto;min-width:0;display:flex;flex-direction:column;
  border-right:1px solid var(--line);overflow:hidden;transition:flex-basis .18s ease}
.osApp[data-app="mail"] .osAppBody.mlBody2[data-reading="true"] .mlListPane{
  flex:0 0 220px;max-width:32%;border-right:1px solid var(--line);background:var(--surf)}
/* ⚡ THE STRIP IS CAPPED BY THE PANE, NOT THE WINDOW. Measured 2026-09-08 at a
   1280px window: rail and dock leave a 684px pane, the 220px strip left the
   letter 345px and a nine-word subject ran to three lines — while a 1100px
   window (dock hidden, 800px pane) had MORE room. A share of the card cannot
   be wrong that way; 220 stays the ceiling. */
/* ⚡ A 220px strip beside a 576px card is a third of the letter's width gone;
   under the existing 1100 breakpoint the strip gives some of it back. */
@media (max-width:1100px){
  .osApp[data-app="mail"] .osAppBody.mlBody2[data-reading="true"] .mlListPane{flex-basis:176px}
}
.osApp[data-app="mail"] .osAppBody.mlBody2[data-reading="true"] .mlListPane .mlHead,
.osApp[data-app="mail"] .osAppBody.mlBody2[data-reading="true"] .mlListPane .mlShelf{display:none}
.osApp[data-app="mail"] .osAppBody.mlBody2[data-reading="true"] .mlRow{
  gap:2px 8px;padding:9px 12px;align-items:baseline}
/* The strip redefines the token rather than carrying its own columns — head,
   row and shelf stay one rule, and the suite pins exactly that. */
.osApp[data-app="mail"] .osAppBody.mlBody2[data-reading="true"]{--mlGrid:minmax(0,1fr) auto}
/* The strip is ONE line per letter: sender, then the date on the same row.
   The checkbox hides (selection survives as .sel), subject and subline hide —
   the open pane beside it already says what they are. */
.osApp[data-app="mail"] .osAppBody.mlBody2[data-reading="true"] .mlRow .mlCk{display:none}
.osApp[data-app="mail"] .osAppBody.mlBody2[data-reading="true"] .mlRow .mlFrom{grid-column:1;grid-row:1}
.osApp[data-app="mail"] .osAppBody.mlBody2[data-reading="true"] .mlRow .mlWhen{grid-column:2;grid-row:1}
.osApp[data-app="mail"] .osAppBody.mlBody2[data-reading="true"] .mlRow .mlSub,
.osApp[data-app="mail"] .osAppBody.mlBody2[data-reading="true"] .mlRow .mlSubline,
.osApp[data-app="mail"] .osAppBody.mlBody2[data-reading="true"] .mlRow .mlSlot{display:none}
.osApp[data-app="mail"] .osAppBody.mlBody2[data-reading="true"] .mlRow .mlWhen{
  font-size:10.5px;opacity:.7}
.osApp[data-app="mail"] .osAppBody.mlBody2[data-reading="true"] .mlRow .mlMid{display:none}

/* ⛔ THE READER SITS BESIDE THE LIST, NEVER BELOW IT. The pane is a flex
   child; the dock does not own any of its space. */
.osApp[data-app="mail"] .osAppBody.mlBody2 .mlReadPane{
  flex:1 1 auto;min-width:0;display:flex;flex-direction:column;
  background:var(--panel,var(--bg));overflow:hidden}
.osApp[data-app="mail"] .osAppBody.mlBody2[data-reading="false"] .mlReadPane{display:none}
.osApp[data-app="mail"] .osAppBody.mlBody2[data-reading="true"] .mlListPane .osAppFoot{display:none}
.osApp[data-app="mail"] .osAppBody.mlBody2 .mlListScroll{
  flex:1 1 auto;min-height:0;overflow:auto}
.osApp[data-app="mail"] .osAppBody.mlBody2 .mlListScroll-foot{
  flex:0 0 auto;border-top:1px solid var(--line);padding:9px 14px;background:var(--glass);
  font:400 11px var(--osMono);color:var(--faint)}

/* ── the reading pane ────────────────────────────────────────────────────
   The pane is a child of the card now — no `position:fixed`, no
   collision with the dock. The vault pattern (os-vault.css:112) is
   reserved for app shells that mount over a conversation; the mail
   shell has its own card, so the right answer is the same flexbox
   the rest of the card uses. */
.osApp[data-app="mail"] .mlRead{
  display:flex;flex-direction:column;height:100%;
  background:var(--panel,var(--bg))}

/* ⚡ READING MODE FOLDS THE CHROME (owner, 2026-09-08: "too much % from
   vertical details view is taken by general info, summaries, buttons").
   Measured on the real signed-in app before this block existed: the app
   header (105px desktop / 181px phone) and the tabs row (76 / 158) together
   said which mailbox and which folder you are in TWICE, directly above a
   reader whose own head said it a third time; on a phone that was 339px —
   40% of the screen — before the letter's subject, and the letter itself was
   given 92px of 844. The first word sat at 56% / 89% of the viewport.
   ⚡ `data-reading` on the ROOT (set beside the card's own attribute in paint)
      lets the two headers above the card answer to the same state the panes
      already answer to. Nothing is removed from the markup — the standard's
      gate reads the source, and the crumb, picker and refresh stay live on a
      desktop — the identity block (icon + name + scope) folds, because the
      picker in the crumb row already names the mailbox and the letter names
      itself. On a phone the list is hidden while reading, so the tabs have
      nothing to switch and the header has nothing to head: both fold, and
      `.mlBack` in the reader is the way home. */
.osApp[data-app="mail"][data-reading="true"] .rhead{padding:7px 0}
.osApp[data-app="mail"][data-reading="true"] .rhead .rhId{display:none}
.osApp[data-app="mail"][data-reading="true"] .rhead .rhTop{margin-bottom:0}
.osApp[data-app="mail"][data-reading="true"] .wrap.wide{padding-top:10px}
.osApp[data-app="mail"][data-reading="true"] .osAppBar.withTabs{padding-bottom:6px}
/* ⚡ While reading, the search field gives up its 240px floor so the tabs and
   the field share ONE line on a 684px pane instead of stacking to two (that
   stack cost 50px at 1280). Written from body.os to outrank work-screen.css's
   `.mlQ{min-width:240px}`. */
/* ⛔ `.osAppBar` — THE SEARCH FIELD, NOT EVERY .mlQ. `.mlQ` names two different
   things in this app: the search input in the bar, and a QUOTED BLOCK inside a
   letter. This rule was written for the first and matched the second, so every
   quoted reply in a read letter became a 200px-wide flex item with a minimum
   width — the enormous empty rounded blob the owner photographed on
   2026-09-10, swallowing the quote it was supposed to render.
   ⛔ Two meanings for one class name is the defect; scoping the rule to the bar
      is the fix that does not require renaming a class three files use. */
body.os .osApp[data-app="mail"][data-reading="true"] .osAppCard .osAppBar .mlQ{min-width:150px;flex:0 1 200px}

/* ⚡ THE TOOLS SIT IN THE LETTER'S MARGIN, NOT ABOVE IT. `.mlReadHd` was a
   54px strip — back, ‹ ›, a keyboard hint, an "Aizvērt" pill — at full weight
   above every subject. It is now a small cluster FLOATED right inside the
   scroller, so the subject wraps around it and the row costs nothing on a
   desktop. The hint is gone (the keys are on each button's tooltip); the close
   is a glyph carrying its name; `.mlBack` is the phone's control and the ×
   the desktop's — they are one act (`data-close-letter`), so only one shows. */
.osApp[data-app="mail"] .mlReadHd{
  float:right;display:flex;align-items:center;gap:6px;
  margin:0 0 8px 14px;padding:0;border:0;background:none}
.osApp[data-app="mail"] .mlReadHd .mlBack{display:none}
.osApp[data-app="mail"] .mlBack{
  flex:none;padding:6px 12px 6px 9px;border:1px solid var(--line);border-radius:var(--r-pill);
  background:var(--surf);color:var(--muted);cursor:pointer;font:500 12.5px var(--body);
  display:inline-flex;align-items:center;gap:5px;box-shadow:var(--lift)}
.osApp[data-app="mail"] .mlBack:hover{color:var(--ink);border-color:var(--line2)}
.osApp[data-app="mail"] .mlReadNav{display:flex;gap:6px;flex:none}
.osApp[data-app="mail"] .mlNav,
.osApp[data-app="mail"] .mlClosePill{
  width:30px;height:30px;padding:0;border:1px solid var(--line);border-radius:var(--r-pill);
  background:var(--surf);color:var(--muted);cursor:pointer;font:500 16px/1 var(--body);
  display:inline-flex;align-items:center;justify-content:center;box-shadow:var(--lift);
  transition:transform .14s,border-color .14s,color .14s}
.osApp[data-app="mail"] .mlNav:hover,
.osApp[data-app="mail"] .mlClosePill:hover{color:var(--ink);border-color:var(--line2);transform:translateY(-1px)}
.osApp[data-app="mail"] .mlClosePill{margin-left:4px;font-size:18px}

/* ── the scrollable body — subject, meta, attachments, body ─────────── */
.osApp[data-app="mail"] .mlReadScroll{
  flex:1;min-height:0;overflow:auto;padding:12px clamp(16px,4%,32px) 28px}
.osApp[data-app="mail"] .mlSubject{
  margin:0 0 10px;font:700 22px/1.25 var(--disp,inherit);color:var(--ink);
  letter-spacing:-.015em;overflow-wrap:anywhere}
/* ⚡ ONE LINE FOR TWO FACTS. Three label rows (NO / ADRESĒTS / KAD, ~80px)
   became: initial · who ‹address› · when — and the two on-demand chips, why
   the letter is here and load-images, sit at the line's end instead of each
   owning a row. "Addressed to" appears only when it is news (os-mail.js). */
.osApp[data-app="mail"] .mlReadMeta{
  display:flex;flex-wrap:wrap;align-items:center;gap:5px 10px;
  padding-bottom:10px;margin-bottom:14px;border-bottom:1px solid var(--line);
  font:400 13px/1.4 var(--body);color:var(--muted)}
.osApp[data-app="mail"] .mlMetaAv{
  flex:none;width:26px;height:26px;border-radius:var(--r-pill);
  display:inline-grid;place-items:center;font:700 12px/1 var(--disp,inherit);font-style:normal;
  color:var(--violet);background:color-mix(in srgb,var(--violet) 14%,var(--surf));
  box-shadow:var(--lift)}
.osApp[data-app="mail"] .mlMetaFrom{min-width:0;overflow-wrap:anywhere}
.osApp[data-app="mail"] .mlMetaWho{color:var(--ink);font-weight:700}
.osApp[data-app="mail"] .mlMetaWhoAddr{
  color:var(--faint);font-weight:400;margin-left:6px;font-size:12px}
.osApp[data-app="mail"] .mlMetaSep{color:var(--faint)}
.osApp[data-app="mail"] .mlMetaWhen{font:400 12px var(--osMono);color:var(--faint);white-space:nowrap}
.osApp[data-app="mail"] .mlMetaTo{
  flex:0 1 auto;min-width:0;font-size:12px;color:var(--muted);overflow-wrap:anywhere}
.osApp[data-app="mail"] .mlMetaTo i{
  font-style:normal;color:var(--faint);font:400 10.5px var(--osMono);
  letter-spacing:.04em;text-transform:uppercase;margin-right:8px}
.osApp[data-app="mail"] .mlWhyBtn{
  margin:0;border:0;background:none;cursor:pointer;padding:0;
  font:400 11.5px var(--body);color:var(--faint);
  display:inline-flex;align-items:center;gap:5px;min-width:0}
.osApp[data-app="mail"] .mlWhyBtn span{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.osApp[data-app="mail"] .mlWhyBtn:hover{color:var(--muted)}
.osApp[data-app="mail"] .mlWhyBtn i{
  display:inline-flex;align-items:center;justify-content:center;width:14px;height:14px;
  border:1px solid var(--line);border-radius:var(--r-pill);font:500 9.5px/1 var(--osMono);
  color:var(--faint);flex:none}
.osApp[data-app="mail"] .mlWhy{
  flex:1 1 100%;margin:2px 0 0;padding:10px 12px;border:1px solid var(--line);border-radius:var(--r-s);
  background:var(--surf);font:400 12px/1.5 var(--body);color:var(--muted)}
.osApp[data-app="mail"] .mlWhy b{color:var(--ink);font-weight:500}
.osApp[data-app="mail"] .mlWhy code{
  font:400 11px var(--osMono);background:var(--bg);border:1px solid var(--line);
  border-radius:var(--r-s);padding:1px 5px;color:var(--ink)}

/* ── attachments — one card per file, with inline image preview ─────── */
/* ⚠ one quiet line, under the letter — the pictures a letter is MADE of
   are not attachments and must never be twelve cards (2026-09-11) */
.osApp[data-app="mail"] .mlAttNote{margin:14px 0 0;font-size:12.5px;color:var(--muted)}
.osApp[data-app="mail"] .mlAtts{margin-bottom:18px}
.osApp[data-app="mail"] .mlAtts h3{
  margin:0 0 10px;font:400 11px var(--body);letter-spacing:.02em;
  color:var(--faint);font-weight:500}
.osApp[data-app="mail"] .mlAttGrid{
  display:grid;grid-template-columns:repeat(auto-fill,minmax(160px,1fr));gap:9px}
.osApp[data-app="mail"] .mlAtt{
  display:flex;flex-direction:column;gap:0;text-align:left;
  border:1px solid var(--line);border-radius:var(--r-s);background:var(--bg);
  color:var(--ink);cursor:pointer;font:inherit;overflow:hidden}
.osApp[data-app="mail"] .mlAtt:hover{border-color:var(--line2)}
.osApp[data-app="mail"] .mlAtt.on{opacity:.6;pointer-events:none}
.osApp[data-app="mail"] .mlAttImg{
  background:var(--surf);aspect-ratio:16/10;display:flex;align-items:center;justify-content:center;
  font:400 11px var(--osMono);color:var(--faint);overflow:hidden;position:relative}
.osApp[data-app="mail"] .mlAttImg img{
  max-width:100%;max-height:100%;object-fit:cover;display:block}
.osApp[data-app="mail"] .mlAttImg .mlAttExt{
  font:500 18px var(--disp);color:var(--muted);letter-spacing:.04em}
.osApp[data-app="mail"] .mlAttMeta{
  padding:8px 10px;display:flex;flex-direction:column;gap:2px;min-width:0}
.osApp[data-app="mail"] .mlAttName{
  font:500 12.5px/1.35 var(--body);color:var(--ink);
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.osApp[data-app="mail"] .mlAttSize{
  font:400 10.5px var(--osMono);color:var(--faint)}

/* ── the body itself — stranger's text, never as markup ──────────────── */
/* ⛔ THE CHAT WIDGET'S CARD CAP LEAKED INTO THE WHOLE APP. mail-ui.js injects a
   global <style> whose selectors are UNSCOPED, and one of them is
   `.mlBody{max-height:280px;overflow:auto}` — right for a card inside a chat
   bubble, catastrophic for a full-pane letter. Measured 2026-09-09 at 1280x950:
   the reading pane was 758px tall and the letter was clamped to 280 with its
   OWN inner scrollbar, so a letter scrolled inside a pane that also scrolled
   and half the pane sat empty. The app carries the same class name, so the
   only thing separating them was specificity, and the app had no rule at all.
   ⛔ Do not "fix" this by renaming the app's class — the leak would simply find
   the next shared name. The widget's sheet wants scoping (task filed). */
.osApp[data-app="mail"] .mlBody{
  font:400 14.5px/1.7 var(--body);color:var(--ink);
  white-space:pre-wrap;overflow-wrap:anywhere;max-width:70ch;
  max-height:none;overflow:visible}
/* ⚡ THE RICH BODY (2026-09-08): the letter rebuilt from its parsed blocks —
   headings, lists, quotes, real anchors, image slots. Every string was
   escaped by the renderer; these rules only give the STRUCTURE its shape.
   ⛔ white-space:pre-wrap is OFF here — the renderer supplies the paragraphs,
   and pre-wrap would double every break the parser already made. */
.osApp[data-app="mail"] .mlBodyRich{
  white-space:normal;max-width:70ch;display:flex;flex-direction:column;gap:10px}
.osApp[data-app="mail"] .mlBodyRich .mlP{
  margin:0;font:400 14.5px/1.65 var(--body);color:var(--ink);overflow-wrap:anywhere}
.osApp[data-app="mail"] .mlBodyRich .mlH4{
  margin:8px 0 0;font:550 16px/1.35 var(--body);color:var(--ink)}
.osApp[data-app="mail"] .mlBodyRich .mlLi{
  position:relative;margin:0;padding-left:18px;
  font:400 14.5px/1.6 var(--body);color:var(--ink);overflow-wrap:anywhere}
.osApp[data-app="mail"] .mlBodyRich .mlLi::before{
  content:'•';position:absolute;left:2px;color:var(--violet)}
.osApp[data-app="mail"] .mlBodyRich .mlQ{
  margin:2px 0;padding:6px 14px;border-left:3px solid var(--line2);
  color:var(--muted);background:color-mix(in srgb,var(--surf) 60%,transparent);
  border-radius:0 var(--r-s) var(--r-s) 0}
.osApp[data-app="mail"] .mlBodyRich .mlP-raw{white-space:pre-wrap}
/* ── links, wearing the verdict the platform stored ──────────────────── */
.osApp[data-app="mail"] .mlLn{
  color:var(--violet);text-decoration:underline;
  text-decoration-color:color-mix(in srgb,var(--violet) 40%,transparent);
  text-underline-offset:2px;cursor:pointer}
.osApp[data-app="mail"] .mlLn:hover{
  text-decoration-color:var(--violet);
  background:color-mix(in srgb,var(--violet) 8%,transparent);border-radius:var(--r-s)}
.osApp[data-app="mail"] .mlLn-uzmanigi{color:var(--ink)}
.osApp[data-app="mail"] .mlLn-bistami{color:var(--bad)}
.osApp[data-app="mail"] .mlLnDot{
  display:inline-block;width:7px;height:7px;margin-right:5px;border-radius:var(--r-pill);
  background:var(--bad);vertical-align:1px}
/* ── inline image slots — a person's click turns them into pictures ──── */
.osApp[data-app="mail"] .mlImgBar{
  display:flex;align-items:center;gap:8px;margin:0;min-width:0}
.osApp[data-app="mail"] .mlImgBar[hidden]{display:none}
.osApp[data-app="mail"] .mlImgLoad{
  padding:4px 11px;border:1px solid var(--line);border-radius:var(--r-pill);
  background:var(--surf);color:var(--ink);cursor:pointer;font:500 12px var(--body);white-space:nowrap}
.osApp[data-app="mail"] .mlImgLoad:hover{border-color:var(--line2);background:var(--surf2)}
.osApp[data-app="mail"] .mlImgNote{font:400 11.5px var(--body);color:var(--faint);
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0}
.osApp[data-app="mail"] .mlImgSlot{
  display:flex;align-items:center;gap:8px;align-self:flex-start;
  padding:9px 14px;border:1px dashed var(--line2);border-radius:var(--r-s);
  background:var(--surf);color:var(--muted);cursor:pointer;
  font:400 12.5px var(--body);max-width:100%}
.osApp[data-app="mail"] .mlImgSlot i{font-style:normal}
.osApp[data-app="mail"] .mlImgSlot span{
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.osApp[data-app="mail"] .mlImgSlot:hover{border-color:var(--violet);color:var(--ink)}
.osApp[data-app="mail"] .mlImgSlot.on{
  padding:0;border:0;background:none;cursor:default;max-width:100%}
.osApp[data-app="mail"] .mlImgSlot.on img{
  display:block;max-width:100%;height:auto;border-radius:var(--r-s);
  border:1px solid var(--line)}
.osApp[data-app="mail"] .mlBodyNone{
  color:var(--faint);font:400 13px/1.55 var(--body);
  white-space:normal;display:flex;flex-direction:column;gap:4px;max-width:62ch}
.osApp[data-app="mail"] .mlBodyNone b{color:var(--ink);font-weight:500}

/* ── the viewer ───────────────────────────────────────────────────────────
   ⛔ It mounts INSIDE the app root, not on <body>: an app never leaves the
      shell, and a veil on the body would cover the rail and the dock too. */
.osApp[data-app="mail"] .mlVeil{
  position:absolute;inset:0;z-index:30;display:grid;place-items:center;padding:26px;
  background:rgba(24,20,34,.42);-webkit-backdrop-filter:blur(4px);backdrop-filter:blur(4px)}
.osApp[data-app="mail"] .mlVBox{
  display:flex;flex-direction:column;width:min(1100px,100%);height:min(88%,900px);
  background:var(--panel);border:1px solid var(--line2);border-radius:var(--r);
  box-shadow:0 34px 80px -30px rgba(20,14,40,.62);overflow:hidden}
.osApp[data-app="mail"] .mlVHd{
  display:flex;align-items:center;gap:9px;flex:none;padding:11px 14px;
  border-bottom:1px solid var(--line);background:var(--glass)}
.osApp[data-app="mail"] .mlVHd .nm{
  font:600 13.5px var(--body);color:var(--ink);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:52%}
.osApp[data-app="mail"] .mlVHd .sz{font:400 11px var(--mono);color:var(--faint)}
.osApp[data-app="mail"] .mlVHd .sp{flex:1}
/* the body centres one thing and scrolls if it is taller than the box */
.osApp[data-app="mail"] .mlVBd{
  flex:1 1 auto;min-height:0;overflow:auto;display:grid;place-items:center;padding:16px;background:var(--surf)}
.osApp[data-app="mail"] .mlVBd img{max-width:100%;max-height:100%;object-fit:contain;border-radius:var(--r-s)}
/* ⛔ a PDF gets the whole box and no padding — a page inset by 16px reads as a
   photograph of a page rather than the page */
.osApp[data-app="mail"] .mlVBd.pdf{padding:0;display:block}
.osApp[data-app="mail"] .mlVBd.pdf iframe{width:100%;height:100%;border:0;display:block}
/* ⚠ `mlVSay`, not `mlVWait`. The app-standard gate forbids an app's own sheet
   defining `*Wait{` — it exists because apps kept re-implementing the shared
   frame's empty/wait/refuse states. ⛔ The fix is the rename, NOT an entry in
   the exemption list: widening a guard to get past it is how the guard stops
   guarding. And the name is truer — this line says what the viewer is showing,
   which is a caption more often than it is a wait. */
.osApp[data-app="mail"] .mlVSay{font:400 13px var(--body);color:var(--muted);text-align:center;max-width:44ch;line-height:1.55}
/* ⛔ THE CAPTION IS NOT DECORATION. It carries whether this is the file or a
   card built from it, and it must stay legible — never a faint footnote. */
.osApp[data-app="mail"] .mlVFt{
  flex:none;padding:10px 14px;border-top:1px solid var(--line);background:var(--glass);
  font:400 12px/1.5 var(--body);color:var(--muted)}
@media (max-width:720px){
  .osApp[data-app="mail"] .mlVeil{padding:0;place-items:stretch}
  .osApp[data-app="mail"] .mlVBox{width:100%;height:100%;border-radius:0;border:0}
}

/* ── compose: the same pane, a third mode ─────────────────────────────────
   ⛔ NO NEW FRAME CLASS. This is `.mlReadPane`'s content under
      `data-mode="write"`, so it inherits the sender-strip collapse and the
      ≤720 full-width rule that the reader already earns. A `.mlComposeBar`
      would have to reimplement both — `.osRegBar` cost a ship for that. */
.osApp[data-app="mail"] .mlWrite{
  display:flex;flex-direction:column;min-height:0;height:100%;gap:9px;
  padding:14px clamp(16px,4%,32px) 0}
.osApp[data-app="mail"] .mlWriteHd{display:flex;align-items:center;gap:10px}
.osApp[data-app="mail"] .mlWriteTitle{
  flex:1;min-width:0;font:600 15px/1.25 var(--disp);letter-spacing:-.01em;color:var(--ink)}
.osApp[data-app="mail"] .mlWriteX{
  flex:none;width:28px;height:28px;border-radius:var(--r-pill);border:1px solid var(--line2);
  background:transparent;color:var(--muted);font:400 16px/1 var(--body);cursor:pointer}
.osApp[data-app="mail"] .mlWriteX:hover{color:var(--ink);background:var(--surf2)}
/* label + field on one line: the labels are short and a stacked form makes a
   four-field letter look like a tax return */
.osApp[data-app="mail"] .mlF{
  display:grid;grid-template-columns:64px minmax(0,1fr);align-items:center;gap:10px}
.osApp[data-app="mail"] .mlF>i{
  font:400 10px/1 var(--mono);letter-spacing:.1em;text-transform:uppercase;color:var(--faint);font-style:normal}
.osApp[data-app="mail"] .mlF input,
.osApp[data-app="mail"] .mlWriteBody{
  width:100%;box-sizing:border-box;border:1px solid var(--line2);border-radius:var(--r-s);
  background:var(--surf);color:var(--ink);font:400 13.5px/1.5 var(--body);padding:8px 11px}
.osApp[data-app="mail"] .mlF input:focus,
.osApp[data-app="mail"] .mlWriteBody:focus{outline:none;border-color:var(--fill-2);background:var(--surf2)}
.osApp[data-app="mail"] .mlWriteBody{flex:1 1 auto;min-height:120px;resize:none}
/* ══ the rich composer (2026-09-11, SPEC-MAIL-RICH-COMPOSE) ══════════════════
   ⛔ THE HEIGHT CHAIN IS THE CONSTRAINT (lines 126-145 above). The form is a
      flex column at `height:100%`; the editor is the ONE thing that grows and
      it scrolls INSIDE itself (`overflow:auto` + `min-height:0`), so a
      60-line letter never pushes the footer below the pane. Every other row
      here is `flex:none` with a fixed height. The form also scrolls as the
      belt-and-braces: on a short phone viewport the fixed rows alone can
      exceed the pane, and the sticky footer then rides the form's own
      scroller instead of being clipped by `.mlReadPane{overflow:hidden}`. */
.osApp[data-app="mail"] .mlWrite{overflow:auto}
.osApp[data-app="mail"] .mlRich{
  flex:1 1 auto;min-height:120px;overflow:auto;outline:none;
  font:400 13.5px/1.55 var(--body);color:var(--ink);cursor:text;
  overflow-wrap:anywhere}
.osApp[data-app="mail"] .mlRich:focus{border-color:var(--fill-2);background:var(--surf2)}
/* the placeholder is copy from the table (data-placeholder), drawn only while
   the editor holds nothing but an empty paragraph */
.osApp[data-app="mail"] .mlRich:empty::before,
.osApp[data-app="mail"] .mlRich:has(> p:only-child > br:only-child)::before{
  content:attr(data-placeholder);color:var(--faint);pointer-events:none;position:absolute}
.osApp[data-app="mail"] .mlRich{position:relative}
.osApp[data-app="mail"] .mlRich p{margin:0 0 .6em}
.osApp[data-app="mail"] .mlRich p:last-child{margin-bottom:0}
.osApp[data-app="mail"] .mlRich ul,.osApp[data-app="mail"] .mlRich ol{margin:0 0 .6em;padding-left:1.5em}
.osApp[data-app="mail"] .mlRich blockquote{
  margin:0 0 .6em;padding:2px 0 2px 12px;border-left:2px solid var(--line2);color:var(--ink2,var(--muted))}
.osApp[data-app="mail"] .mlRich a{color:var(--violet);text-decoration:underline}
/* a picture in the letter: bounded by the column, never by its pixels; a
   selected one shows its edge so backspace is aimed at something visible */
.osApp[data-app="mail"] .mlRich img{max-width:100%;height:auto;border-radius:6px;display:inline-block;vertical-align:bottom}
.osApp[data-app="mail"] .mlRich img:hover{outline:2px solid var(--fill-2)}

/* ── the toolbar: ONE fixed row, never a second ───────────────────────────
   Seven 30px pictograms and two gaps are 244px wide; a 380px phone leaves
   348px inside the form's padding, so nothing is hidden at any width the
   product serves. `nowrap` + `overflow-x:auto` is the guarantee, not a hope:
   a width nobody measured gets a sideways scroll, never a taller bar. The
   link bar re-uses the SAME box and the same height. */
.osApp[data-app="mail"] .mlTools{
  flex:none;height:34px;display:flex;align-items:center;gap:3px;
  padding:0 2px;white-space:nowrap;overflow-x:auto;overflow-y:hidden;
  scrollbar-width:none}
.osApp[data-app="mail"] .mlTools::-webkit-scrollbar{display:none}
.osApp[data-app="mail"] .mlTool{
  flex:none;width:30px;height:30px;box-sizing:border-box;display:inline-flex;align-items:center;justify-content:center;
  border:1px solid transparent;border-radius:var(--r-s);background:transparent;
  color:var(--muted);cursor:pointer;padding:0;font:400 16px/1 var(--body)}
.osApp[data-app="mail"] .mlTool:hover{color:var(--ink);background:var(--surf)}
.osApp[data-app="mail"] .mlTool.on{color:var(--violet);background:var(--fill-2);border-color:transparent}
.osApp[data-app="mail"] .mlToolGap{flex:none;width:1px;height:16px;margin:0 3px;background:var(--line)}
.osApp[data-app="mail"] .mlToolsLink{gap:6px;color:var(--muted)}
.osApp[data-app="mail"] .mlToolsLink>svg{flex:none;margin-left:4px}
.osApp[data-app="mail"] .mlLinkIn{
  flex:1 1 auto;min-width:0;height:28px;box-sizing:border-box;padding:0 10px;
  border:1px solid var(--line2);border-radius:var(--r-s);background:var(--surf);
  color:var(--ink);font:400 12.5px var(--body)}
.osApp[data-app="mail"] .mlLinkIn:focus{outline:none;border-color:var(--fill-2)}
.osApp[data-app="mail"] .mlToolWord{
  flex:none;height:28px;padding:0 11px;border:1px solid var(--line2);border-radius:var(--r-pill);
  background:var(--surf);color:var(--ink);cursor:pointer;font:500 12px var(--body)}
.osApp[data-app="mail"] .mlToolWord:hover{background:var(--surf2)}

/* ── To, with Cc / Bcc folded behind two words at its right end ──────────── */
.osApp[data-app="mail"] .mlToWrap{display:flex;align-items:center;gap:6px;min-width:0}
.osApp[data-app="mail"] .mlToWrap input{flex:1 1 auto;min-width:0}
.osApp[data-app="mail"] .mlCcBtns{flex:none;display:inline-flex;gap:4px}
.osApp[data-app="mail"] .mlCcBtns button{
  padding:3px 8px;border:1px solid transparent;border-radius:var(--r-pill);background:transparent;
  color:var(--faint);cursor:pointer;font:500 11px var(--body);letter-spacing:.02em}
.osApp[data-app="mail"] .mlCcBtns button:hover{color:var(--ink);border-color:var(--line2);background:var(--surf)}

/* ── in the letter vs with the letter: one glance ─────────────────────────
   A picture IN the letter shows a thumbnail of itself and the word for
   "in the letter"; a file WITH it shows a paperclip and its size. Two chips
   in the same row, two different first glyphs — that is the whole tell. */
.osApp[data-app="mail"] .mlWriteFile>svg{flex:none;width:14px;height:14px;color:var(--faint)}
.osApp[data-app="mail"] .mlWriteFile.inl{padding-left:4px;border-color:var(--fill-2);background:color-mix(in srgb,var(--violet) 6%,var(--surf))}
.osApp[data-app="mail"] .mlWriteFile.inl>img{
  flex:none;width:22px;height:22px;object-fit:cover;border-radius:4px;background:var(--surf2)}
.osApp[data-app="mail"] .mlWriteFile.inl>i{color:var(--violet)}

/* ── the signature panel: opens ABOVE the footer, closes back into it ────── */
.osApp[data-app="mail"] .mlSigEdit{
  flex:none;display:flex;flex-direction:column;gap:6px;padding:10px 12px;
  border:1px solid var(--line2);border-radius:var(--r-s);background:var(--surf)}
.osApp[data-app="mail"] .mlSigEdit>b{font:600 12px var(--body);color:var(--ink)}
.osApp[data-app="mail"] .mlSigEdit textarea{
  width:100%;box-sizing:border-box;resize:vertical;min-height:58px;
  border:1px solid var(--line2);border-radius:var(--r-s);background:var(--bg);
  color:var(--ink);font:400 13px/1.45 var(--body);padding:7px 10px}
.osApp[data-app="mail"] .mlSigEdit textarea:focus{outline:none;border-color:var(--fill-2)}
.osApp[data-app="mail"] .mlSigRow{display:flex;align-items:center;gap:8px;flex-wrap:wrap}
.osApp[data-app="mail"] .mlSigRow small{font:400 11px/1.4 var(--body);color:var(--muted);flex:1 1 200px}
.osApp[data-app="mail"] .mlAttachBtn.on{color:var(--violet);border-color:var(--fill-2)}
.osApp[data-app="mail"] .mlWriteFiles{display:flex;flex-wrap:wrap;gap:6px}
.osApp[data-app="mail"] .mlWriteFile{
  display:inline-flex;align-items:center;gap:7px;padding:4px 6px 4px 10px;
  border:1px solid var(--line2);border-radius:var(--r-pill);background:var(--surf);
  font:400 12px var(--body);color:var(--ink);max-width:100%}
.osApp[data-app="mail"] .mlWriteFile b{font-weight:500;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.osApp[data-app="mail"] .mlWriteFile i{font-style:normal;font:400 10.5px var(--mono);color:var(--faint);flex:none}
.osApp[data-app="mail"] .mlWriteFile button{
  flex:none;width:18px;height:18px;border:0;border-radius:var(--r-pill);
  background:var(--surf2);color:var(--muted);cursor:pointer;font:400 12px/1 var(--body)}
.osApp[data-app="mail"] .mlWriteFile button:hover{color:var(--rose)}
/* the send row shares .mlReadFoot's grammar — two rows of actions in one app
   must read as one family */
.osApp[data-app="mail"] .mlWriteFoot{
  display:flex;align-items:center;gap:10px;flex:none;
  position:sticky;bottom:0;z-index:2;margin:0 clamp(-32px,-4%,-16px);
  padding:11px clamp(16px,4%,32px);border-top:1px solid var(--line);
  background:var(--glass);-webkit-backdrop-filter:blur(14px);backdrop-filter:blur(14px)}
.osApp[data-app="mail"] .mlAttachBtn{
  padding:6px 13px;border:1px solid var(--line);border-radius:var(--r-pill);
  background:var(--surf);color:var(--muted);cursor:pointer;font:400 12.5px var(--body)}
.osApp[data-app="mail"] .mlAttachBtn:hover{color:var(--ink);border-color:var(--line2)}
/* ⛔ The verdict is a SENTENCE, not a colour. A red dot beside "sending" tells
   nobody which of nine refusals happened; the words are the product. */
.osApp[data-app="mail"] .mlWriteSay{
  flex:1;min-width:0;font:400 12px/1.4 var(--body);color:var(--muted)}
.osApp[data-app="mail"] .mlWriteSay.bad{color:var(--amber)}
.osApp[data-app="mail"] .mlWrite[data-compose] .mlActGo{margin-left:0}
.osApp[data-app="mail"] .mlWrite{background:transparent}
/* the compose entry sits in the shared bar, beside the search */
/* ⚠ `.mlWriteBtn`, not `.mlWrite` — the FORM is `.mlWrite`, and one class on
   both a 28px pill and a full-height flex column is two designs fighting. */
.osApp[data-app="mail"] .mlWriteBtn{
  flex:none;padding:6px 14px;border:0;border-radius:var(--r-pill);
  background:var(--fill);color:var(--onfill);cursor:pointer;font:500 12.5px var(--body)}
.osApp[data-app="mail"] .mlWriteBtn:hover{filter:brightness(1.08)}
@media (max-width:720px){
  .osApp[data-app="mail"] .mlWrite{padding:12px 16px 0;gap:8px}
  .osApp[data-app="mail"] .mlF{grid-template-columns:52px minmax(0,1fr);gap:8px}
  .osApp[data-app="mail"] .mlWriteFoot{margin:0 -16px;padding:10px 16px;flex-wrap:wrap}
  .osApp[data-app="mail"] .mlWriteSay{flex:1 1 100%;order:3}
}

/* ── the drawer's footer — a row of soft actions pinned to the bottom ── */
/* ── the bulk bar: what you do to the letters you ticked ──────────────────
   ⛔ THIS ELEMENT HAD NO CSS AT ALL, AT ANY WIDTH. It replaces the column-head
      row the moment a letter is ticked, so unstyled it rendered as browser
      default buttons in the middle of a clay app — the one place in Mail that
      looked broken rather than plain. Same grammar as .mlAct so the two rows of
      actions in this app read as one family. */
.osApp[data-app="mail"] .mlBulk{
  display:flex;align-items:center;gap:7px;flex-wrap:wrap;
  padding:9px 14px;border-bottom:1px solid var(--line);
  background:color-mix(in srgb,var(--violet) 7%,var(--surf))}
.osApp[data-app="mail"] .mlBulk .n{
  font:600 12px var(--body);color:var(--ink);margin-right:2px}
.osApp[data-app="mail"] .mlBulk button{
  padding:5px 12px;border:1px solid var(--line2);border-radius:var(--r-pill);
  background:var(--surf);color:var(--ink);cursor:pointer;font:400 12.5px var(--body)}
.osApp[data-app="mail"] .mlBulk button:hover{background:var(--surf2)}
/* ⛔ Destructive reads as destructive, and it uses the state token — never a
   literal red. It is still not the loudest thing in the row: nothing here is
   filled, because a filled button beside a count invites a mis-tap. */
.osApp[data-app="mail"] .mlBulk button.danger{color:var(--rose);border-color:color-mix(in srgb,var(--rose) 40%,var(--line2))}
.osApp[data-app="mail"] .mlBulk button.danger:hover{background:color-mix(in srgb,var(--rose) 10%,transparent)}
.osApp[data-app="mail"] .mlBulk button.quiet{border-color:transparent;background:none;color:var(--muted)}
.osApp[data-app="mail"] .mlBulk button.quiet:hover{color:var(--ink);background:var(--surf)}
.osApp[data-app="mail"] .mlBulk .note{
  flex:1 1 100%;font:400 11px/1.45 var(--mono);color:var(--faint)}
/* ⚠ four buttons, a count and a note do not fit at 390px — the count owns its
   own line and the verbs wrap under it rather than truncating. */
@media (max-width:720px){
  .osApp[data-app="mail"] .mlBulk{padding:8px 12px;gap:6px}
  .osApp[data-app="mail"] .mlBulk .n{flex:1 1 100%}
  .osApp[data-app="mail"] .mlBulk button{padding:5px 10px;font-size:12px}
}
/* ⚡ STICKY IS THE BELT, THE CHAIN ABOVE IS THE BRACES. The chain is what makes
   the pane scroll at all; this keeps the bar on screen even if some future
   ancestor breaks it. ⛔ Sticky ALONE would leave the list unscrollable — it is
   not a substitute for the chain. */
.osApp[data-app="mail"] .mlReadFoot{
  display:flex;align-items:center;gap:8px;flex:none;
  position:sticky;bottom:0;z-index:2;
  padding:12px 16px;border-top:1px solid var(--line);
  background:var(--glass);-webkit-backdrop-filter:blur(14px);backdrop-filter:blur(14px)}
/* ⚠ ON A PHONE THE PRIMARY ACTION IS THE ONE THAT FALLS OFF. Three pills plus
   `margin-left:auto` on Reply, in a nowrap flex row, had no rule at ANY width. */
@media (max-width:720px){
  .osApp[data-app="mail"] .mlReadFoot{flex-wrap:wrap;gap:7px;padding:10px 14px}
  .osApp[data-app="mail"] .mlActGo{margin-left:0;order:-1;flex:1 1 100%;text-align:center}
}
/* ⚡ ONE LINE OF TEXT PER BUTTON (owner, 2026-09-09: "lets compact somehow so
   that buttons coudl be on onel line text"). Measured before this rule: at
   1280px the five pills wrapped to two and three lines each — "Select this
   letter" was 65px tall against a 31px pill — and the footer stood 267px, a
   third of the reading pane. Labels are DATA (they arrive translated), so the
   fix is nowrap plus a note that can shrink, never a shorter English string. */
.osApp[data-app="mail"] .mlAct{
  padding:6px 13px;border:1px solid var(--line);border-radius:var(--r-pill);
  background:var(--surf);color:var(--muted);cursor:pointer;white-space:nowrap;flex:none;
  font:400 12.5px var(--body);text-decoration:none;display:inline-block}
/* ⛔ THE NOTE HAD NO RULE AT ALL — a bare <span> in a nowrap flex row, so it was
   squeezed to 59px wide and grew 242px TALL, which is what actually made the
   footer eat the pane. It is the one thing here that may shrink and truncate:
   it explains a state, the buttons DO something. Full text stays in `title`. */
.osApp[data-app="mail"] .mlReadSay{
  flex:1 1 12ch;min-width:12ch;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
  font:400 12px var(--body);color:var(--muted)}
/* ⚠ AND THE FOOTER MAY WRAP AT ANY WIDTH, not only on a phone. Measured: with
   the note shrinking freely it was squeezed to the single letter "T" beside the
   buttons — narrower than its own ellipsis, which tells a person nothing and
   looks like a bug. A `min-width` plus `wrap` sends it to its own line when the
   pane is tight instead, so it is either READABLE or on the next row, never a
   stump. Two compact rows is still a fraction of the 267px it used to cost. */
.osApp[data-app="mail"] .mlReadFoot{flex-wrap:wrap;row-gap:7px}
.osApp[data-app="mail"] .mlAct:hover{color:var(--ink);border-color:var(--line2)}
.osApp[data-app="mail"] .mlAct.on,
.osApp[data-app="mail"] .mlAct[aria-pressed="true"]{color:var(--ink);background:color-mix(in srgb,var(--violet) 14%,transparent);
  border-color:color-mix(in srgb,var(--violet) 45%,var(--line2))}
.osApp[data-app="mail"] .mlActGo{
  margin-left:auto;background:var(--fill);color:var(--onfill);border-color:transparent}
.osApp[data-app="mail"] .mlActGo:hover{filter:brightness(1.08);color:var(--onfill)}

/* ── the skeleton — three regions of a real body, while we wait ─────── */
.osApp[data-app="mail"] .mlSk{
  padding:30px 32px;display:flex;flex-direction:column;gap:11px}
.osApp[data-app="mail"] .mlSkHd{display:flex;flex-direction:column;gap:6px;padding-bottom:14px;
  border-bottom:1px solid var(--line);margin-bottom:8px}
.osApp[data-app="mail"] .osAppSkLine{
  height:11px;border-radius:var(--r-s);background:linear-gradient(90deg,
    var(--surf) 0%,var(--surf2) 50%,var(--surf) 100%);
  background-size:200% 100%;animation:mlShimmer 1.4s ease-in-out infinite}
.osApp[data-app="mail"] .osAppSkLine.w60{width:60%}.osApp[data-app="mail"] .osAppSkLine.w70{width:70%}
.osApp[data-app="mail"] .osAppSkLine.w80{width:80%}.osApp[data-app="mail"] .osAppSkLine.w100{width:100%}
.osApp[data-app="mail"] .osAppSkLine.w30{width:30%}
.osApp[data-app="mail"] .osAppSkLine.lg{height:18px}
@keyframes mlShimmer{ 0%{background-position:200% 0} 100%{background-position:-200% 0} }

/* ── the slot chip on each row — the inbox the letter was delivered to ── */
.osApp[data-app="mail"] .mlSlot{
  display:inline-block;margin-left:8px;padding:1px 6px;border:1px solid var(--line);
  border-radius:var(--r-s);font:400 10.5px var(--osMono);color:var(--faint);vertical-align:1px}
.osApp[data-app="mail"] .mlSlot i{font-style:normal;color:var(--faint)}

/* ── per-tab unread badge — same vocabulary the dock uses, just smaller ── */
.osApp[data-app="mail"] .osAppCard .tabs .tabN{
  display:inline-block;min-width:18px;padding:0 6px;margin-left:6px;
  border-radius:var(--r-pill);background:var(--surf2);color:var(--muted);
  font:500 10.5px/16px var(--osMono);text-align:center;vertical-align:1px}
.osApp[data-app="mail"] .osAppCard .tabs .tabN.hot{
  background:var(--fill);color:var(--onfill)}

/* ⌨ the keyboard-focused row — a hairline that survives every theme ─── */
.osApp[data-app="mail"] .mlRow.focus{outline:1px solid var(--line2);outline-offset:-1px}
.osApp[data-app="mail"] .mlListSkel{padding:30px 18px;color:var(--faint);text-align:center;font-size:13px}

@media (max-width:720px){
  .osApp[data-app="mail"] .osAppBody.mlBody2[data-reading="true"] .mlListPane{display:none}
  /* ⚡ On a phone the reader IS the screen: the app header and the tabs fold
     (the list they head and switch is hidden), the card takes the pane from
     its top edge, and the tool cluster becomes one slim row with the back
     button as its first word. Measured: the letter's first word moved from
     y=752 to under 45% of 844. */
  /* ⛔ `body.os.inthread .osApp .osAppBar{display:flex}` (work-screen.css) and
     `body.os.inthread .osApp{padding}` outrank a bare `.osApp[…]` rule, so the
     fold is written from `body.os` — one class more than the sheet it has to
     beat, no !important. On the phone the letter IS the screen: the pane's
     own 20px side padding and the card's frame go too, the card runs edge to
     edge and the action bar sits on the screen's bottom edge. */
  body.os .osApp[data-app="mail"][data-reading="true"] .rhead,
  body.os .osApp[data-app="mail"][data-reading="true"] .osAppCard .osAppBar{display:none}
  body.os .osApp[data-app="mail"][data-reading="true"],
  :root body.os.workHomeOpen .osApp[data-app="mail"][data-reading="true"]{padding:0}
  /* (the second line outranks work-clay.css's `:root[data-theme] body.os.workHomeOpen .osApp{padding:14px 20px 24px}`) */
  body.os .osApp[data-app="mail"][data-reading="true"] .wrap.wide{padding:0}
  body.os .osApp[data-app="mail"][data-reading="true"] .osAppCard{
    border-radius:0;border-left:0;border-right:0;border-top:0;box-shadow:none}
  .osApp[data-app="mail"] .mlReadHd{float:none;margin:0 0 10px}
  .osApp[data-app="mail"] .mlReadHd .mlBack{display:inline-flex;margin-right:auto}
  .osApp[data-app="mail"] .mlReadHd .mlClosePill{display:none}
  .osApp[data-app="mail"] .mlReadScroll{padding:12px 16px 24px}
  .osApp[data-app="mail"] .mlSubject{font-size:19px;margin-bottom:8px}
  .osApp[data-app="mail"] .mlReadMeta{gap:5px 8px;padding-bottom:10px;margin-bottom:12px}
  .osApp[data-app="mail"] .mlImgNote{display:none}
  /* ⛔ The phone grid keeps the checkbox column: four children in a two-track
     grid stacked the date under the sender. Sender and subject share the wide
     track, the date keeps its own. */
  .osApp[data-app="mail"]{--mlGrid:22px minmax(0,1fr) 54px}
  .osApp[data-app="mail"] .mlRow{grid-template-columns:var(--mlGrid)}
  .osApp[data-app="mail"] .mlHead span:first-child{display:none}
  .osApp[data-app="mail"] .mlHead span:nth-child(2){grid-column:2}
  .osApp[data-app="mail"] .mlHead span:nth-child(3){grid-column:3}
  .osApp[data-app="mail"] .mlRow .mlCk{grid-column:1;grid-row:1;align-self:start;margin-top:2px}
  .osApp[data-app="mail"] .mlFrom{grid-column:2;order:-1;font-size:11.5px}
  .osApp[data-app="mail"] .mlMid{grid-column:2}
  .osApp[data-app="mail"] .mlWhen{grid-column:3;grid-row:1}
  /* The shelf has no checkbox: its count and its mark share the phone row,
     what it holds drops to a full-width second line. */
  .osApp[data-app="mail"] .mlShelf{grid-template-columns:minmax(0,1fr) 54px}
  .osApp[data-app="mail"] .mlShelf>span:first-child{grid-column:1;grid-row:1}
  .osApp[data-app="mail"] .mlShelfWhat{grid-column:1;grid-row:2}
  .osApp[data-app="mail"] .mlShelfMark{grid-column:2;grid-row:1}
}

/* ── the address picker ───────────────────────────────────────────────────
   ⛔ NOT A NATIVE SELECT. A catch-all domain accepts every address, so a list
   of the eight named mailboxes we happen to hold looks complete and is not —
   and the native control can carry neither a filter nor the sentence that says
   so. Drawn here, in the product's own rendering rather than the operating
   system's. */
.osApp[data-app="mail"] .mlPick{position:relative;flex:none}
.osApp[data-app="mail"] .mlPickBtn{
  display:inline-flex;align-items:center;gap:7px;max-width:min(300px,42vw);
  padding:5px 11px;border:1px solid var(--line);border-radius:var(--r-pill);
  background:var(--surf);color:var(--ink);font:400 12.5px/1.3 var(--body);cursor:pointer}
.osApp[data-app="mail"] .mlPickBtn:hover{border-color:var(--line2)}
.osApp[data-app="mail"] .mlPickBtn span{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.osApp[data-app="mail"] .mlPickBtn i{font-style:normal;color:var(--faint);font-size:10px}
.osApp[data-app="mail"] .mlPickMenu{
  position:absolute;top:calc(100% + 6px);left:0;z-index:20;width:min(340px,80vw);
  padding:7px;border:1px solid var(--line);border-radius:var(--r-s);background:var(--panel,var(--bg));
  box-shadow:0 22px 60px -30px var(--shadow);max-height:min(60vh,420px);overflow:auto}
.osApp[data-app="mail"] .mlPickQ{
  width:100%;padding:8px 11px;margin-bottom:6px;border:1px solid var(--line);
  border-radius:var(--r-s);background:var(--surf);color:var(--ink);font:13px var(--body)}
.osApp[data-app="mail"] .mlPickQ:focus{outline:none;border-color:var(--line2)}
.osApp[data-app="mail"] .mlPickHead{
  padding:9px 10px 5px;font:500 9.5px/1 var(--body);letter-spacing:.13em;
  text-transform:uppercase;color:var(--faint)}
.osApp[data-app="mail"] .mlPickRow{
  display:flex;align-items:baseline;gap:9px;width:100%;text-align:left;
  padding:8px 10px;border:0;border-radius:var(--r-s);background:none;color:var(--ink);
  font:400 13px/1.35 var(--body);cursor:pointer}
.osApp[data-app="mail"] .mlPickRow:hover{background:var(--surf)}
.osApp[data-app="mail"] .mlPickRow.on{background:var(--surf2);font-weight:550}
.osApp[data-app="mail"] .mlPickWhole{margin-left:auto;font:400 11px var(--mono);color:var(--faint)}
.osApp[data-app="mail"] .mlPickUse b{font-weight:600}
/* ⚠ The sentence is part of the control, not a footnote to it: without it the
   named list reads as the whole truth about which addresses exist. */
.osApp[data-app="mail"] .mlPickNote{
  padding:9px 10px 4px;margin-top:5px;border-top:1px solid var(--line);
  font:400 11.5px/1.5 var(--body);color:var(--muted)}

/* ⚡ The restored-draft notice (2026-09-10). Quiet, beside the title, with the
   one press that empties the form — a person who expected a blank page must be
   able to see WHY it is not blank and leave in one move. */
.osApp[data-app="mail"] .mlDraftKept{
  display:inline-flex;align-items:center;gap:8px;margin-left:10px;
  font:400 11.5px var(--body);color:var(--muted)}
.osApp[data-app="mail"] .mlDraftKept button{
  padding:2px 9px;border:1px solid var(--line);border-radius:999px;
  background:var(--surf);color:var(--ink);font:400 11px var(--body);cursor:pointer}
.osApp[data-app="mail"] .mlDraftKept button:hover{border-color:var(--line2);background:var(--surf2)}

/* ⛔ THE NAMED REFUSAL, NOT AN EMPTY LIST (owner, 2026-09-10). A person for
   whom no address has been claimed sees a sentence explaining that, not a
   blank mailbox — the two states look identical and only one is true. Toned
   as guidance rather than as an error: nobody has done anything wrong. */
body.os .osApp[data-app="mail"] .mlNoClaim {
  max-width: 46ch;
  margin: 32px auto;
  text-align: center;
  line-height: 1.5;
}
body.os .osApp[data-app="mail"] .mlNoClaim b { display: block; margin-bottom: 6px; }

/* ══ selection: the head slot, select-all, and the bulk action ══════════════
   Owner, 2026-09-10: ticking a checkbox threw the list back to the top, and
   the action bar was four bare buttons with no default and no statement of
   what would happen. The bar and the column header share ONE slot so a
   selection redraws that element alone — the rows below never move. */
.osApp[data-app="mail"] .mlHeadSlot{display:block}

/* The tri-state select-all. ⛔ "Some" must not read as "all": a person who
   cannot tell them apart will press Do it on a selection they did not make. */
.osApp[data-app="mail"] .mlCkAll{margin-right:2px}
.osApp[data-app="mail"] .mlCkAll.some{
  background:color-mix(in srgb,var(--violet) 22%,var(--surf));
  border-color:color-mix(in srgb,var(--violet) 55%,var(--line2));
  position:relative}
.osApp[data-app="mail"] .mlCkAll.some::after{
  content:"";position:absolute;inset:auto 3px;height:2px;border-radius:1px;
  top:50%;transform:translateY(-50%);background:var(--violet)}

/* The action itself: what will happen, to how many, before it is pressed. */
.osApp[data-app="mail"] .mlBulkPick{
  padding:5px 10px;border:1px solid var(--line2);border-radius:var(--r-pill);
  background:var(--surf);color:var(--ink);font:400 12.5px var(--body);cursor:pointer}
.osApp[data-app="mail"] .mlBulkGo{font-weight:600}
.osApp[data-app="mail"] .mlBulkSay{
  font:400 12px var(--body);color:var(--ink3,var(--ink2));margin-left:2px}
@media (max-width:720px){
  /* The sentence is the first thing to wrap, never the control it describes. */
  .osApp[data-app="mail"] .mlBulkSay{flex:1 1 100%;margin-left:0}
}

/* ⚡ The compose form as a DROP TARGET (owner, 2026-09-10: drag & drop and
   Ctrl+V did not attach). The whole form lights up, not a strip, because a
   person aiming at a small zone with a file in hand misses it. */
.osApp[data-app="mail"] .mlWrite.drop{
  outline:2px dashed color-mix(in srgb,var(--violet) 60%,transparent);
  outline-offset:-6px;background:color-mix(in srgb,var(--violet) 5%,transparent)}

/* ⚡ The next page — a button, never infinite scroll: a person hunting an old
   letter decides when to spend the wait, and an auto-fetch on scroll moves the
   list under them, which is the defect this pass removed. */
.osApp[data-app="mail"] .mlMore{
  margin-left:auto;padding:4px 12px;border:1px solid var(--line2);
  border-radius:var(--r-pill);background:var(--surf);color:var(--ink);
  font:500 12px var(--body);cursor:pointer}
.osApp[data-app="mail"] .mlMore:hover{background:var(--surf2)}
.osApp[data-app="mail"] .mlMore[disabled]{opacity:.6;cursor:progress}

/* ══ the bin (owner, 2026-09-10) ══════════════════════════════════════════
   Read live off the mail server, so it says so — and the Empty button is the
   only control in this app that destroys anything. */
.osApp[data-app="mail"] .mlTrashBar{
  display:flex;align-items:center;gap:9px;flex-wrap:wrap;
  padding:9px 14px;border-bottom:1px solid var(--line);background:var(--glass)}
.osApp[data-app="mail"] .mlTrashBar .n{font:600 12px var(--body);color:var(--ink)}
.osApp[data-app="mail"] .mlTrashLive{font:400 12px var(--body);color:var(--ink3,var(--ink2))}
.osApp[data-app="mail"] .mlTrashBar button{
  margin-left:auto;padding:5px 12px;border:1px solid var(--line2);
  border-radius:var(--r-pill);background:var(--surf);color:var(--ink);
  cursor:pointer;font:500 12.5px var(--body)}
.osApp[data-app="mail"] .mlTrashNote{
  padding:28px 16px;text-align:center;line-height:1.5;
  font:400 13px var(--body);color:var(--ink2)}
/* A trashed row does not open — there is no body route for a folder we do not
   mirror — so it must not wear the pointer that promises one. */
.osApp[data-app="mail"] .mlTrashRow{cursor:default}
.osApp[data-app="mail"] .mlRestore{
  padding:3px 10px;border:1px solid var(--line2);border-radius:var(--r-pill);
  background:var(--surf);color:var(--ink);cursor:pointer;font:400 12px var(--body)}
.osApp[data-app="mail"] .mlRestore:hover{background:var(--surf2)}
.osApp[data-app="mail"] .mlRestore[disabled]{opacity:.6;cursor:progress}

/* The hidden-technical line in the foot (task f818c5c2).
   ⛔ It reads as a link, not a warning: nothing is wrong, the letters are just
      somewhere better. Amber here would say "a problem" about working software. */
/* .mlHidden removed 2026-09-11 — the inbox footer carries ONE control */
