/* ---- Interface layouts A–E ("Beyond the Chat Box", 11 Aug 2026) ------------------------
   Alternate HOME surfaces, chosen in Settings → Display → Interface layout. 'Default' uses
   none of this file. Every value below is a design-system token — no literal colours — so
   all five layouts follow the current theme (unison / night / enclave) with no per-theme
   rules of their own. Registered in scripts/cds-lint.mjs (DS_STYLESHEETS); every class is
   declared at top level so the style gate accepts it (the office-suite.css precedent).

   Also holds the two controls that came with them (2026-08-12): the Settings layout SLIDER,
   and the floating Settings WINDOW (main.js SettingsWindow) that replaced the settings page. */

/* shared ------------------------------------------------------------------------------- */
.yb-lay{
  flex:1;min-height:0;overflow-y:auto;
  padding:20px clamp(16px,4vw,40px) 40px;
  background:var(--bg);color:var(--ink);font-family:var(--ui)
}
/* Half the margin on the tile board, and only there. A board is a wall you fill; the
   space around the outside of it is the one part that can never hold anything. The other
   layouts keep theirs -- the briefing one is a reading column and wants its margins. */
.yb-f{padding:10px clamp(8px,2vw,20px) 20px}

/* the briefing is a reading column, not a full-bleed board — it stays narrow on wide screens */
.yb-lay-briefing{max-width:760px;margin:0 auto;width:100%}
.yb-lay-note{
  display:flex;align-items:center;gap:12px;flex-wrap:wrap;
  margin:0 0 20px;padding:8px 12px;
  border:1px dashed var(--line);border-radius:var(--r-sm);
  background:var(--surface-2);color:var(--mute);font-size:12.5px
}
.yb-lay-note-text{flex:1;min-width:180px}
.yb-lay-hd{display:flex;align-items:baseline;gap:12px;flex-wrap:wrap;margin:0 0 20px}
.yb-lay-sub{margin:0;flex:1;min-width:220px;color:var(--mute);font-size:14px}
.yb-lay-meta{color:var(--faint);font-size:12.5px;font-variant-numeric:tabular-nums}
.yb-lay-empty{margin:8px 2px;color:var(--mute);font-size:13.5px}

.yb-btn{
  appearance:none;cursor:pointer;
  padding:7px 13px;border:1px solid var(--line);border-radius:var(--r-pill);
  background:var(--surface);color:var(--ink-2);
  font-family:inherit;font-size:13px;font-weight:500;line-height:1.2;
  transition:background .12s ease,border-color .12s ease
}
.yb-btn:hover{background:var(--surface-2);border-color:var(--faint)}
.yb-btn:disabled{opacity:.5;cursor:default}
.yb-btn-primary{background:var(--accent);border-color:var(--accent);color:var(--on-accent)}
.yb-btn-primary:hover{background:var(--accent);border-color:var(--ink-2)}
.yb-btn-ghost{background:transparent;border-color:transparent;color:var(--accent);padding:6px 8px}
.yb-btn-ghost:hover{background:var(--accent-soft);border-color:transparent}

.yb-privacy{
  display:flex;align-items:center;gap:6px;margin:8px 0 0;
  color:var(--faint);font-size:11.5px;line-height:1.3
}
.yb-ask{
  appearance:none;cursor:pointer;display:block;width:100%;margin-top:22px;
  padding:13px 16px;text-align:left;
  border:1px solid var(--line);border-radius:var(--r-pill);
  background:var(--surface);color:var(--mute);font-family:inherit;font-size:14px
}
.yb-ask:hover{border-color:var(--accent);color:var(--ink-2)}

.yb-back-pill{
  position:fixed;left:50%;bottom:86px;transform:translateX(-50%);z-index:70;cursor:pointer;
  padding:7px 15px;border:1px solid var(--line);border-radius:var(--r-pill);
  background:var(--surface);color:var(--ink-2);box-shadow:var(--shadow-sm);
  font-family:var(--ui);font-size:12.5px;font-weight:500
}
.yb-back-pill:hover{border-color:var(--accent);color:var(--accent)}

/* cards (B · Briefing, and A's Today room) --------------------------------------------- */
.yb-lay-cards{display:flex;flex-direction:column;gap:14px}
.yb-card{
  padding:16px 18px;border:1px solid var(--line);border-radius:var(--r);
  background:var(--surface);box-shadow:var(--shadow-sm)
}
.yb-card.yb-is-done{opacity:.62}
.yb-card-title{margin:0 0 6px;font-family:var(--display);font-size:16.5px;font-weight:600}
.yb-card-body{margin:0;color:var(--ink-2);font-size:14px;line-height:1.5}
.yb-card-acts{display:flex;align-items:center;gap:8px;flex-wrap:wrap;margin-top:14px}
.yb-card-said{color:var(--mute);font-size:13px}
.yb-wb-item.yb-is-on{background:var(--surface);color:var(--ink);font-weight:600;box-shadow:var(--shadow-sm)}
.yb-step.yb-is-done{background:var(--accent-soft);border-color:var(--accent-soft);color:var(--accent)}
.yb-step.yb-is-on{border-color:var(--accent);color:var(--ink);font-weight:600}

/* C · The Co-Editor -------------------------------------------------------------------- */
.yb-ce{display:grid;grid-template-columns:minmax(0,1fr) 300px;gap:0;padding:0;overflow:hidden}
.yb-ce-doc{min-width:0;padding:18px clamp(16px,3vw,44px) 40px;overflow-y:auto}
.yb-ce-head{margin-bottom:18px}
.yb-ce-tools{display:flex;align-items:center;gap:8px;margin-top:10px}
.yb-ce-tool{
  min-width:26px;padding:3px 7px;text-align:center;
  border:1px solid var(--line);border-radius:var(--r-xs);
  background:var(--surface);color:var(--mute);font-size:12px
}
.yb-ce-status{margin-left:auto;color:var(--accent);font-size:12px}
.yb-ce-sec{margin-bottom:20px}
.yb-ce-h{margin:0 0 8px;font-family:var(--display);font-size:15px;font-weight:600}
.yb-ce-p{margin:0;max-width:64ch;color:var(--ink-2);font-family:var(--font-body-serif);font-size:14.5px;line-height:1.65}
.yb-ce-margin{
  display:flex;flex-direction:column;gap:12px;padding:18px 14px 30px;
  border-left:1px solid var(--line);background:var(--bg-2);overflow-y:auto
}
.yb-ce-mhead{display:flex;align-items:center;gap:7px;color:var(--mute);font-size:12.5px;font-weight:600}
.yb-note{padding:12px 14px;border:1px solid var(--line);border-radius:var(--r-sm);background:var(--surface)}
.yb-note.yb-is-done{border-color:var(--accent-soft);background:var(--surface-2)}
.yb-note-head{margin:0 0 5px;font-size:13px;font-weight:600}
.yb-note-body{margin:0 0 10px;color:var(--ink-2);font-size:12.5px;line-height:1.5}

/* D · The Job Board -------------------------------------------------------------------- */
.yb-board-wrap{display:flex;flex-direction:column}
.yb-board-hd{align-items:center}
.yb-board{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:12px;align-items:start}
.yb-col{display:flex;flex-direction:column;min-width:0;border:1px solid var(--line);border-radius:var(--r);background:var(--bg-2)}
.yb-col-head{
  display:flex;align-items:center;gap:8px;margin:0;padding:11px 14px;
  border-bottom:1px solid var(--line);
  color:var(--mute);font-size:11px;font-weight:700;letter-spacing:.09em
}
.yb-col-count{margin-left:auto;color:var(--faint);font-weight:600;letter-spacing:0}
.yb-col-body{display:flex;flex-direction:column;gap:8px;padding:10px;min-height:70px}
.yb-board-composer{display:flex;gap:8px;margin-top:16px}
.yb-board-input{
  flex:1;min-width:0;padding:11px 15px;
  border:1px solid var(--line);border-radius:var(--r-pill);
  background:var(--surface);color:var(--ink);font-family:inherit;font-size:14px
}
.yb-board-input:focus{outline:none;border-color:var(--accent)}

/* E · The Ambient Layer ---------------------------------------------------------------- */
.yb-amb-hint{
  position:fixed;right:18px;bottom:96px;z-index:70;cursor:pointer;
  display:flex;align-items:center;gap:9px;padding:8px 14px;
  border:1px solid var(--line);border-radius:var(--r-pill);
  background:var(--surface);color:var(--mute);box-shadow:var(--shadow-sm);
  font-family:var(--ui);font-size:12px
}
.yb-amb-hint:hover{border-color:var(--accent);color:var(--ink-2)}
.yb-amb-kbd{
  padding:2px 7px;border:1px solid var(--line);border-radius:var(--r-xs);
  background:var(--surface-2);color:var(--faint);font-family:var(--mono);font-size:10.5px
}
.yb-amb-scrim{
  position:fixed;inset:0;z-index:120;display:flex;justify-content:center;align-items:flex-start;
  padding-top:14vh;background:rgba(14,17,22,.42);backdrop-filter:blur(2px)
}
.yb-amb-box{
  width:min(560px,92vw);padding:10px;
  border:1px solid var(--line);border-radius:var(--r);
  background:var(--surface);box-shadow:var(--shadow)
}
.yb-amb-top{display:flex;align-items:center;gap:10px;padding:8px 10px 12px;border-bottom:1px solid var(--line-soft)}
.yb-amb-input{flex:1;min-width:0;color:var(--ink);font-size:14.5px}
.yb-amb-row{
  appearance:none;cursor:pointer;display:flex;align-items:baseline;gap:10px;width:100%;
  padding:10px 12px;margin-top:4px;border:0;border-radius:var(--r-sm);
  background:transparent;color:var(--ink);font-family:inherit;text-align:left
}
.yb-amb-row.yb-is-on{background:var(--accent-soft)}
.yb-amb-verb{font-size:14px;font-weight:500}
.yb-amb-sub{flex:1;color:var(--mute);font-size:12px}
.yb-amb-enter{color:var(--accent);font-family:var(--mono);font-size:12px}
.yb-amb-said{margin:8px 12px 0;color:var(--accent);font-size:12.5px}
.yb-amb-foot{
  display:flex;align-items:center;gap:12px;flex-wrap:wrap;
  margin-top:10px;padding:10px 12px 4px;border-top:1px solid var(--line-soft)
}
.yb-amb-foot .yb-privacy{margin:0;flex:1;min-width:160px}
.yb-amb-footacts{display:flex;align-items:center;gap:10px;margin-left:auto}
.yb-amb-esc{color:var(--faint);font-size:11.5px}

/* Settings → Interface layout picker --------------------------------------------------- */
/* Two layouts, so this is a plain choice and not a slider: each row IS the radio. The
   slider's ticks and read-out are gone with it; .range-slider itself stays in
   app-extensions.css because the text-scale control still uses it. */
.yb-pick{display:flex;flex-direction:column;gap:6px}
.yb-pick-row{
  display:flex;align-items:flex-start;gap:12px;width:100%;
  padding:12px 13px;text-align:left;cursor:pointer;font:inherit;color:inherit;
  border:1px solid var(--line);border-radius:var(--r-sm);background:var(--surface-2)
}
.yb-pick-row:hover{background:var(--surface)}
.yb-pick-row:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
.yb-pick-row.yb-is-on{border-color:var(--accent)}
.yb-pick-row.yb-is-on .yb-pick-name{color:var(--accent)}
/* The dot: a ring that fills with the accent on the chosen row. */
.yb-pick-mark{
  flex:none;width:16px;height:16px;margin-top:2px;
  border:1px solid var(--line);border-radius:var(--r-pill);background:var(--surface)
}
.yb-pick-row.yb-is-on .yb-pick-mark{
  border-color:var(--accent);
  background:radial-gradient(circle at 50% 50%, var(--accent) 0 4.5px, var(--surface) 4.5px)
}
.yb-pick-main{flex:1;min-width:0}
/* name/desc are spans, not <p> — a <button> may only contain phrasing content. */
.yb-pick-name{display:block;font-size:14px;font-weight:600}
.yb-pick-kicker{color:var(--faint);font-family:var(--mono);font-size:11px}
.yb-pick-desc{display:block;margin-top:3px;color:var(--mute);font-size:12.5px;line-height:1.5}

/* Settings as a centred overlay dialog (main.js SettingsWindow) ------------------------
   A dimmed backdrop over the live page, not a window: no drag, no resize, no minimise.
   z-index 40 is deliberately BELOW the DS modal layer (.modal) so a modal opened from
   inside Settings — feedback, confirms, the file viewer — still lands on top of it. */
.yb-winlayer{
  position:fixed;inset:0;z-index:40;display:flex;align-items:center;justify-content:center;
  padding:20px;background:rgba(8,10,14,.55)
}
.yb-win{
  width:min(760px,92vw);height:min(76vh,780px);
  display:flex;flex-direction:column;overflow:hidden;
  background:var(--surface);border:1px solid var(--line);border-radius:var(--r);
  box-shadow:var(--shadow)
}
.yb-win-head{
  display:flex;align-items:center;gap:8px;flex:none;padding:8px 10px;
  border-bottom:1px solid var(--line);background:var(--surface-2);user-select:none
}
.yb-win-title{font-size:12.5px;font-weight:650;color:var(--ink)}
.yb-win-spacer{flex:1}
.yb-win-btn{
  width:26px;height:26px;flex:none;cursor:pointer;
  display:inline-flex;align-items:center;justify-content:center;
  border:1px solid var(--line);border-radius:var(--r-xs);
  background:var(--surface);color:var(--ink-2);font-size:14px;line-height:1
}
.yb-win-btn:hover{border-color:var(--accent);color:var(--accent)}
/* MMDB fix 2026-08-25: dialog body was overflow:hidden, delegating the actual scroll to
   the nested .yarnball-main two levels down. At 1280x720 the Display/Sign-out rows sat past
   the dialog's bottom edge with no reachable scrollbar -- the delegated child scroll never
   surfaced past this ancestor's hard clip. The dialog BODY itself now owns the scroll
   (bounded height still comes from flex:1;min-height:0 inside .yb-win's fixed height), same
   pattern as .mcard{max-height:86vh;overflow-y:auto} elsewhere in this file. */
.yb-win-body{flex:1;min-height:0;display:flex;flex-direction:column;overflow-y:auto}
/* Settings sizes itself to its content as a page (yarnball-main-compact). In the dialog it
   fills it and scrolls inside instead. */
.yb-win-body .yarnball-main{flex:1;min-height:0;overflow-y:auto}

/* the shell modifiers — one per layout, so a surface can reach the chrome around it ----- */
.yb-layout-a .yarnball-main,
.yb-layout-c .yarnball-main{min-height:0}
.yb-layout-b,.yb-layout-d,.yb-layout-e{min-height:0}

/* narrow screens: the three- and two-column layouts stack ------------------------------- */
@media (max-width:900px){
  .yb-wb{grid-template-columns:1fr}
  .yb-wb-rail{flex-direction:row;overflow-x:auto;border-right:0;border-bottom:1px solid var(--line)}
  .yb-wb-railtop{display:none}
  .yb-wb-ask{border-left:0;border-top:1px solid var(--line)}
  .yb-ce{grid-template-columns:1fr}
  .yb-ce-margin{border-left:0;border-top:1px solid var(--line)}
  .yb-board{grid-template-columns:repeat(2,minmax(0,1fr))}
}
@media (max-width:760px){
  /* on a phone Settings takes the whole screen, as before — no inset dialog, no backdrop */
  .yb-winlayer{padding:0}
  .yb-win{width:100vw;height:100dvh;border:0;border-radius:0}
  .yb-win-head{display:none}
}
@media (max-width:560px){
  .yb-board{grid-template-columns:1fr}
  .yb-back-pill{bottom:120px}
  .yb-amb-hint{bottom:130px;right:10px}
}

/* ---- Layout F · The Ambient Workspace (SB, 14 Aug 2026) --------------------------------
   Ctrl+Y opens a dispatcher; each intent becomes a tiled window running whatever the model
   built for it. The tiler owns placement — no drag, no resize, no z-order — so nothing
   below sets position on a window. Tokens only, like A–E, so night and enclave follow for
   free. Every class is declared at top level for the cds-lint style gate. */

/* The dispatcher used to be position:fixed at the bottom centre while the tool rail sat at
   the bottom of normal flow — two different layout systems, missing each other by arithmetic.
   Reserving a fixed strip of floor worked on a tall window and failed on a short one. Both
   are in flow now, the rail and then the dispatcher beneath it, so they cannot overlap at any
   size and no magic number is holding it together. */
.yb-f{display:flex;flex-direction:column;gap:12px;overflow:hidden;padding-bottom:16px}
/* Centred, so a rail that wraps stays balanced rather than ragged. */
.yb-f-tools{justify-content:center;flex:none}

/* tab strip — separate contexts; a window cannot move between them ---------------------- */
.yb-f-tabs{display:flex;align-items:center;gap:6px;flex-wrap:wrap;min-height:34px}
.yb-f-tab{
  display:inline-flex;align-items:center;gap:7px;
  padding:6px 14px;border:1px solid transparent;border-radius:var(--r-pill);
  background:transparent;color:var(--ink-2);font:inherit;font-size:13.5px;cursor:pointer
}
.yb-f-tab:hover{background:var(--surface-2)}
.yb-f-tab.yb-is-on{background:var(--accent);border-color:var(--accent);color:var(--on-accent);font-weight:600}
.yb-f-tabcount{font-family:var(--mono);font-size:11px;opacity:.75}
.yb-f-tabadd{border:1px dashed var(--line);color:var(--mute);padding:6px 12px}
.yb-f-knows{
  margin-left:auto;display:inline-flex;align-items:center;gap:6px;
  color:var(--faint);font-size:12px;max-width:46ch;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap
}

/* the board — the unit grid from the widget-skeleton library ---------------------------
   Six units across on desktop, four on tablet, two on phone. One cell is nominally
   160x120 with a 12px gutter; the columns take their share of the stage so the board fills
   the width, and the rows hold their height so a tile is the size it asked for.

   FOOTPRINTS, NOT SHARES (SB, 2026-08-20). The old tiler sized a window by how MANY were
   open -- one filled the stage, two halved it, four quartered it -- so the same weather
   tile was a wall one moment and a quarter the next, and a lone window was always
   fullscreen. A window now takes the footprint it declared and the board leaves the gap.
   One tile alone is one small tile.

   The library those footprints come from is app/widget-skeletons.json; the page it was
   lifted from is design/widget-skeletons.html. */
.yb-f-stage{
  position:relative;
  /* The narrowest the stage may be squeezed to, and the only place it is written down: the
     panels read it when working out how wide they are allowed to get. Four columns of one
     cell (4 x 160 + 3 gutters), which is the squeezed shape at its smallest useful size. */
  --yb-stage-min:640px;
  /* 6px, not 12. The gutter is dead space repeated once per tile in both directions, so
     halving it gives a board of a dozen tiles most of a column back (SB, 2026-08-23). It
     is still a gutter -- tiles read as separate objects, which is the only job it has. */
  flex:1;min-height:0;display:grid;gap:6px;align-content:start;
  grid-template-columns:repeat(6,minmax(0,1fr));
  grid-auto-rows:120px;
  /* A whiteboard, and modules are magnets on it (SB, 2026-08-21): one that does not fit
     the space left on a row drops to the next, and a later smaller one fills the hole
     rather than the board keeping a gap open for nothing. */
  grid-auto-flow:row dense;
  overflow-y:auto;overflow-x:hidden;scrollbar-width:none
}
.yb-f-stage::-webkit-scrollbar{width:0;height:0}

/* The sixteen footprints the library declares, columns x rows. Literal classes rather than
   a computed span: cds-lint rejects undocumented dynamic class prefixes, and sixteen known
   values are clearer than registering a prefix for them (same reasoning as
   LAYOUT_SHELL_CLASS). SIZE_CLASS in layout-ambient.js is the other half of this map, and
   testrig/cases/tiles-footprint-table.mjs now asserts the three lists agree -- the library,
   SIZES, and the rules below. It exists because they had already drifted: widget-skeletons
   .json declared 3x4, 4x1 and 6x2, none of which had a rule or an entry, so eight skeletons
   (chat-thread, document-viewer, feed-stream, timeline-events, heatmap-calendar,
   header-strip, weather-forecast-days, ticker-tape) silently lost a footprint and were
   clamped to the 2x2 default instead. 1x2 was the opposite error: a rule no skeleton asks
   for. Nothing announced either, which is why the check is here rather than a comment. */
.yb-f-s1x1{grid-column:span 1;grid-row:span 1}
.yb-f-s1x2{grid-column:span 1;grid-row:span 2}
.yb-f-s2x1{grid-column:span 2;grid-row:span 1}
.yb-f-s3x1{grid-column:span 3;grid-row:span 1}
.yb-f-s4x1{grid-column:span 4;grid-row:span 1}
.yb-f-s6x1{grid-column:span 6;grid-row:span 1}
.yb-f-s2x2{grid-column:span 2;grid-row:span 2}
.yb-f-s3x2{grid-column:span 3;grid-row:span 2}
.yb-f-s4x2{grid-column:span 4;grid-row:span 2}
.yb-f-s6x2{grid-column:span 6;grid-row:span 2}
.yb-f-s1x3{grid-column:span 1;grid-row:span 3}
.yb-f-s2x3{grid-column:span 2;grid-row:span 3}
.yb-f-s3x3{grid-column:span 3;grid-row:span 3}
.yb-f-s4x3{grid-column:span 4;grid-row:span 3}
.yb-f-s3x4{grid-column:span 3;grid-row:span 4}
.yb-f-s4x4{grid-column:span 4;grid-row:span 4}
.yb-f-s6x3{grid-column:span 6;grid-row:span 3}
.yb-f-s6x4{grid-column:span 6;grid-row:span 4}

/* "Anything wider than the canvas clamps and reflows to its next smallest footprint."
   Four units at tablet, two at phone -- the rows keep their height, so a tile loses
   columns rather than shrinking its type. */
@media (max-width:1100px){
  .yb-f-stage{grid-template-columns:repeat(4,minmax(0,1fr))}
  .yb-f-s6x1,.yb-f-s6x2,.yb-f-s6x3,.yb-f-s6x4{grid-column:span 4}
}

/* a window: an enclosure, not a card ---------------------------------------------------
   No wall. A widget is not a bordered box with a title bar -- it is a quiet surface that
   lifts slightly off the board and grows a hairline ring only while it is pointed at.
   position:relative because the two hover controls hang off it without taking any
   layout space. */
.yb-f-win{
  position:relative;
  display:flex;flex-direction:column;min-width:0;min-height:0;overflow:hidden;
  background:var(--surface);border:0;border-radius:var(--r-sm);
  box-shadow:var(--shadow-sm);transition:box-shadow .15s ease
}
.yb-f-win:hover,.yb-f-win:focus-within{box-shadow:var(--shadow)}
.yb-f-win.yb-is-pinned{box-shadow:var(--shadow-sm)}

/* The ring is drawn INSIDE the window, on a pseudo-element above the module, because the
   stage scrolls: overflow-y:auto with overflow-x:hidden clips anything that spreads
   outward, so a ring made of an outward box-shadow came out cut along the right-hand
   column and the bottom edge. An inset ring on a child of the window itself cannot be
   clipped by anything, and it follows the same border-radius. */
.yb-f-win::after{
  content:"";position:absolute;inset:0;border-radius:inherit;
  pointer-events:none;z-index:3;
  box-shadow:inset 0 0 0 0 transparent;transition:box-shadow .15s ease
}
.yb-f-win:hover::after,.yb-f-win:focus-within::after{box-shadow:inset 0 0 0 1px var(--line)}
.yb-f-win.yb-is-pinned::after{box-shadow:inset 0 0 0 1px var(--accent-soft)}
/* Off this page but still mounted and still running — a monitor or a tracker must not be
   killed just because you looked at another page. */
.yb-f-win.yb-is-off{display:none}

/* selected: the one Ctrl+Y is pointed at (SB, 2026-08-21). Click a module to select it;
   Escape, or a click on the board, puts the pointer down. It is a highlight and NOTHING
   else -- no reflow, no promotion, and nothing happens to any other module. */
.yb-f-win.yb-is-selected,
.yb-f-win.yb-is-selected:hover{box-shadow:var(--shadow)}
.yb-f-win.yb-is-selected::after,
.yb-f-win.yb-is-selected:hover::after{box-shadow:inset 0 0 0 2px var(--accent)}

/* a catalogue module's box. The module paints inside a sealed shadow root within this
   element, so nothing here reaches in and nothing in there reaches out. */
.yb-f-module{flex:1;min-height:0;min-width:0;display:block;overflow:hidden}

/* expanded: a mode, not a size. It lifts over the board, the tile keeps its footprint
   underneath, and minimising puts it back. One open at a time, never written to the
   layout. Inside the stage, so it covers the board and nothing else. */
.yb-f-expanded{
  position:absolute;inset:0;z-index:6;display:flex;flex-direction:column;
  background:var(--surface);border-radius:var(--r);box-shadow:var(--shadow);overflow:hidden
}
.yb-f-expandedhead{
  flex:0 0 auto;display:flex;align-items:center;gap:10px;
  padding:10px 14px;border-bottom:1px solid var(--line-soft)
}
.yb-f-expandedhead b{font:600 13px/1 var(--ui);color:var(--ink)}
.yb-f-expandedhead .yb-f-footbtn{margin-left:auto}
.yb-f-expandedbody{flex:1;min-height:0;display:flex;overflow:auto;padding:14px}

/* Opened from what it said last time, and not yet confirmed. Dimmed rather than hidden:
   an old reading is worth more than an empty rectangle, and pretending it is current is
   the only thing that would be dishonest. */
.yb-f-module[data-stale]{opacity:.62;transition:opacity .25s ease}

/* the dock, when it is pointed at something rather than at the board */
.yb-f-hint.yb-is-pointed{border-color:var(--accent);color:var(--ink)}


/* the widget store ----------------------------------------------------------------------
   A place you open over the board, not a tile on it (SB, 2026-08-24). Same shape as
   Settings: it comes forward, the room behind it dims, and a click out there closes it.

   Every card holds a LIVE module -- the panel is board-level, outside the module sandbox,
   so the picture of a widget is the widget. The card is therefore a landscape box of a
   fixed height: a tile fills the room it is given, and a preview that grew with its content
   would make a grid of forty different heights. */
.yb-f-storescrim{
  position:fixed;inset:0;z-index:130;display:flex;align-items:center;justify-content:center;
  padding:5vh 4vw;
  background:rgba(14,17,22,.5);backdrop-filter:blur(3px);
  animation:yb-f-storein .14s ease
}
@keyframes yb-f-storein{from{opacity:0}to{opacity:1}}
@media (prefers-reduced-motion:reduce){.yb-f-storescrim{animation:none}}

.yb-f-storepanel{
  display:flex;flex-direction:column;min-height:0;
  width:min(1180px,100%);height:min(820px,100%);
  background:var(--surface);border:1px solid var(--line);border-radius:var(--r);
  box-shadow:var(--shadow);overflow:hidden
}

.yb-f-storehead{
  flex:none;display:flex;align-items:center;gap:12px;
  padding:14px 16px;border-bottom:1px solid var(--line-soft)
}
.yb-f-storehead b{font:600 15px/1 var(--ui);color:var(--ink)}
.yb-f-storecount{color:var(--mute);font-size:12px}
.yb-f-storesearch{
  margin-left:auto;width:min(280px,40%);
  padding:7px 10px;border:1px solid var(--line);border-radius:var(--r-sm);
  background:var(--surface-2);color:var(--ink);font:inherit;font-size:13px
}
.yb-f-storesearch:focus{outline:2px solid var(--accent-soft);outline-offset:1px}

.yb-f-storegroups{
  flex:none;display:flex;flex-wrap:wrap;gap:6px;
  padding:10px 16px;border-bottom:1px solid var(--line-soft)
}
.yb-f-storegroup{
  appearance:none;cursor:pointer;
  padding:4px 10px;border:1px solid var(--line);border-radius:999px;
  background:transparent;color:var(--mute);
  font:600 10.5px/1 var(--ui);letter-spacing:.06em;text-transform:uppercase
}
.yb-f-storegroup:hover{color:var(--ink)}
.yb-f-storegroup.yb-is-on{background:var(--accent);border-color:var(--accent);color:var(--on-accent,#fff)}

.yb-f-storegrid{
  flex:1;min-height:0;overflow-y:auto;overscroll-behavior:contain;
  display:grid;grid-template-columns:repeat(auto-fill,minmax(248px,1fr));
  gap:14px;padding:16px;align-content:start
}

.yb-f-storecard{
  appearance:none;cursor:pointer;text-align:left;
  display:flex;flex-direction:column;gap:4px;min-width:0;
  padding:10px;border:1px solid var(--line);border-radius:var(--r-sm);
  background:var(--surface);color:var(--ink);font:inherit
}
.yb-f-storecard:hover{border-color:var(--accent);box-shadow:var(--shadow)}
.yb-f-storecard:focus-visible{outline:2px solid var(--accent);outline-offset:2px}

/* The live tile. A fixed box because a tile fills what it is given -- 2x1 is 320x120, and
   at 228px across that is 86 tall, so the box is that shape and the module draws into it. */
.yb-f-storeface{
  display:block;height:104px;border-radius:7px;overflow:hidden;
  background:var(--surface-2);border:1px solid var(--line-soft)
}
/* Before it has drawn: a quiet ground rather than an empty hole, so a grid mid-scroll does
   not read as a page of broken cards. */
.yb-f-storeface[data-state=waiting],
.yb-f-storeface[data-state=drawing]{
  background:linear-gradient(100deg,var(--surface-2) 30%,var(--surface) 50%,var(--surface-2) 70%);
  background-size:300% 100%;animation:yb-f-storewait 1.2s linear infinite
}
@keyframes yb-f-storewait{from{background-position:100% 0}to{background-position:-100% 0}}
@media (prefers-reduced-motion:reduce){.yb-f-storeface[data-state=drawing]{animation:none}}

.yb-f-storename{font:600 13px/1.3 var(--ui);color:var(--ink)}
.yb-f-storesum{
  color:var(--mute);font-size:11.5px;line-height:1.4;
  display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden
}
.yb-f-storeadd{
  margin-top:2px;color:var(--accent);font:600 10.5px/1 var(--ui);
  letter-spacing:.06em;text-transform:uppercase;opacity:0;transition:opacity .12s ease
}
.yb-f-storecard:hover .yb-f-storeadd,
.yb-f-storecard:focus-visible .yb-f-storeadd{opacity:1}

.yb-f-storenone{grid-column:1/-1;margin:24px 0;color:var(--mute);text-align:center}
.yb-f-storefoot{
  flex:none;margin:0;padding:10px 16px;border-top:1px solid var(--line-soft);
  color:var(--faint);font-size:11.5px
}

/* A phone has no room for a grid of previews, so the cards go to one column and the panel
   takes the whole screen. */
@media (max-width:640px){
  .yb-f-storescrim{padding:0}
  .yb-f-storepanel{width:100%;height:100%;border-radius:0;border:0}
  .yb-f-storesearch{width:100%;margin-left:0}
  .yb-f-storehead{flex-wrap:wrap}
}

/* the grip: pin and close, and nothing else --------------------------------------------
   No refresh, no fullscreen, no expand, no menu, no cog. The host owns the timer, so a
   refresh button is chrome that eats the space the content needed, and the rest never
   existed in the product. The two that remain fade in at the corner while the pointer is
   on the tile and never occupy layout space, so at rest a window is content and nothing
   else.

   Retry and Stop are NOT here. They belong to the error and thinking states inside the
   tile, where the tile can say what actually failed (SB, 2026-08-20). */
.yb-f-grip{
  position:absolute;top:3px;right:3px;z-index:2;
  display:inline-flex;gap:5px;
  /* Its own ground. The 3px of padding puts the buttons back where they were. */
  padding:3px;border-radius:999px;
  background:var(--surface);box-shadow:0 0 0 1px var(--line);
  opacity:0;transition:opacity .12s ease;pointer-events:none
}
.yb-f-win:hover>.yb-f-grip,
.yb-f-win:focus-within>.yb-f-grip{opacity:1;pointer-events:auto}
.yb-f-gripbtn{
  width:19px;height:19px;padding:0;border:0;border-radius:50%;cursor:pointer;
  display:inline-flex;align-items:center;justify-content:center;
  background:var(--surface-2);color:var(--mute);
  box-shadow:0 0 0 1px var(--line);
  font:inherit;font-size:10px;line-height:1
}
.yb-f-gripbtn:hover{color:var(--ink)}
.yb-f-gripbtn.yb-is-on{color:var(--accent)}
/* Touch has no hover, so the pair sits quietly visible rather than being unreachable. */
@media (hover:none){
  .yb-f-grip{opacity:.55;pointer-events:auto}
}
/* direct manipulation: drag to move, drag the corner to shrink -------------------------
   SB, 2026-08-23. Both gestures apply LIVE — the board reflows under the pointer, so the
   preview and the result are the same thing. The reflow itself is animated in JS (a FLIP
   in layout-ambient.js) because a CSS grid does not animate its own reflow; what lives
   here is only what a tile LOOKS like while it is being handled. */

/* Lifted, and out from under everything else. The tile keeps its grid cell while it is
   being carried, which is what makes the slot below honest. */
.yb-f-win.yb-is-drag{
  z-index:20;cursor:grabbing;opacity:.94;
  box-shadow:0 12px 28px rgba(0,0,0,.22);
  /* Carrying a tile is not reading it. Without this the drag paints a text selection
     across whatever it passes over, inside the module's shadow root included. */
  user-select:none
}
.yb-f-win.yb-is-drag::after,
.yb-f-win.yb-is-sizing::after{box-shadow:inset 0 0 0 2px var(--accent)}

/* Where it will land: the cell the carried tile still owns. Positioned from JS against
   the stage, which is already the containing block. */
.yb-f-slot{
  position:absolute;z-index:1;pointer-events:none;
  border-radius:var(--r-sm);
  background:var(--accent-soft);opacity:.5;
  box-shadow:inset 0 0 0 2px var(--accent)
}

/* The corner grip. Bottom-right, opposite the pin/close pair, and like them it fades in
   under the pointer and never takes layout space. touch-action:none because a drag from
   the corner is a resize and never a scroll of the board. */
.yb-f-rsz{
  position:absolute;right:0;bottom:0;z-index:4;
  width:20px;height:20px;padding:0;border:0;background:transparent;
  cursor:nwse-resize;touch-action:none;
  opacity:0;transition:opacity .12s ease;pointer-events:none
}
.yb-f-win:hover>.yb-f-rsz,
.yb-f-win:focus-within>.yb-f-rsz,
.yb-f-win.yb-is-sizing>.yb-f-rsz{opacity:1;pointer-events:auto}
.yb-f-rsz::after{
  content:"";position:absolute;right:5px;bottom:5px;width:8px;height:8px;
  border-right:2px solid var(--mute);border-bottom:2px solid var(--mute);
  border-bottom-right-radius:3px
}
.yb-f-rsz:hover::after{border-color:var(--accent)}
/* Touch has no hover, so the grip sits quietly visible rather than being unreachable. */
@media (hover:none){
  .yb-f-rsz{opacity:.55;pointer-events:auto}
}
@media (prefers-reduced-motion:reduce){
  .yb-f-rsz{transition:none}
}

.yb-f-body{flex:1;min-height:0;overflow:auto;padding:6px}

/* The scroll pill. Thin, rounded, ink-toned rather than the OS default grey slab, and
   drawn with a transparent border so the track shows through as breathing room. Applied
   to everything in layout F that can scroll; generated apps are told to match it. */
.yb-f{scrollbar-width:thin;scrollbar-color:var(--faint) transparent}
.yb-f ::-webkit-scrollbar{width:10px;height:10px}
.yb-f ::-webkit-scrollbar-track{background:transparent}
.yb-f ::-webkit-scrollbar-thumb{
  background:var(--faint);border-radius:var(--r-pill);
  border:3px solid transparent;background-clip:content-box
}
.yb-f ::-webkit-scrollbar-thumb:hover{background:var(--mute);background-clip:content-box}
.yb-f ::-webkit-scrollbar-corner{background:transparent}
/* a generated app owns its whole pane — no padding, no scrollbar of ours */
.yb-f-body.yb-is-app{padding:0;overflow:hidden}

/* .yb-f-foot, .yb-f-zoom and .yb-f-footpct are gone with the title bar: a footer bar of
   Chat / Keys / zoom is the "no borrowed furniture" rule broken on every single tile.
   Ctrl+wheel still zooms -- that is a gesture, not chrome -- and Escape still hands the
   keyboard back from a generated app.

   .yb-f-footbtn stays: it is the general small button this layout uses everywhere else --
   the office browser, the app rail, chat send, and the retry inside an error state. */
.yb-f-footbtn{
  border:1px solid var(--line);background:var(--surface);color:var(--ink-2);
  font:inherit;font-size:11.5px;padding:3px 8px;border-radius:var(--r-xs);cursor:pointer;line-height:1.3
}
.yb-f-footbtn:hover:enabled{background:var(--surface-2);color:var(--ink)}
.yb-f-footbtn:disabled{opacity:.4;cursor:default}
.yb-f-footbtn.yb-is-on{background:var(--accent);border-color:var(--accent);color:var(--on-accent)}

/* highlight-to-explain: a small panel inside the window, never a trip elsewhere --------- */
.yb-f-explain{
  flex:none;display:flex;flex-direction:column;gap:6px;align-items:flex-start;
  margin:0 8px 8px;padding:8px 10px;
  background:var(--accent-soft);border:1px solid var(--accent-soft);
  border-left:2px solid var(--accent);border-radius:var(--r-xs)
}
.yb-f-explainq{margin:0;font-size:11.5px;color:var(--accent);font-style:italic;overflow-wrap:anywhere}
.yb-f-explainbody{margin:0;font-size:12.5px;line-height:1.5;color:var(--ink-2)}

/* the per-window chat ------------------------------------------------------------------- */
.yb-f-chat{
  flex:none;display:flex;flex-direction:column;gap:6px;max-height:45%;
  padding:8px;border-top:1px solid var(--line-soft);background:var(--surface-2)
}
.yb-f-thread{display:flex;flex-direction:column;gap:5px;overflow-y:auto;min-height:0}
.yb-f-msg{
  margin:0;font-size:12.5px;line-height:1.45;color:var(--ink-2);
  background:var(--surface);border:1px solid var(--line-soft);
  border-radius:var(--r-xs);padding:5px 8px;white-space:pre-wrap;overflow-wrap:anywhere
}
.yb-f-msg.yb-is-you{background:var(--accent-soft);border-color:var(--accent-soft);color:var(--ink)}
.yb-f-chatform{display:flex;align-items:center;gap:6px}
.yb-f-chatinput{
  flex:1;min-width:0;border:1px solid var(--line);border-radius:var(--r-xs);
  background:var(--surface);color:var(--ink);font:inherit;font-size:12.5px;padding:5px 8px;outline:none
}
.yb-f-chatinput:focus{border-color:var(--accent)}

/* what the model built ----------------------------------------------------------------- */
.yb-f-frame{display:block;width:100%;height:100%;border:0;background:var(--surface)}
.yb-f-answer{max-width:70ch;display:flex;flex-direction:column;gap:11px}
.yb-f-para{margin:0;font-size:13.5px;color:var(--ink-2);line-height:1.6;white-space:pre-wrap}
/* Prose arrives as markdown whether or not it was asked for, so it is rendered as real
   elements — these are the styles that stop an answer looking like source. */
.yb-f-head{margin:0;font-size:14.5px;font-weight:650;color:var(--ink);line-height:1.35;letter-spacing:.005em}
.yb-f-list{margin:0;padding-left:20px;display:flex;flex-direction:column;gap:6px}
.yb-f-list li{font-size:13.5px;color:var(--ink-2);line-height:1.6}
.yb-f-list li::marker{color:var(--ink-soft)}
.yb-f-answer strong{font-weight:650;color:var(--ink)}
.yb-f-answer em{font-style:italic}
.yb-f-code{
  font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:12.5px;
  background:color-mix(in srgb,var(--ink) 7%,transparent);border-radius:4px;padding:1px 5px
}

/* one question, asked before building, only when it changes the shape ------------------- */
.yb-f-ask{display:flex;flex-direction:column;gap:10px;height:100%;justify-content:center}
.yb-f-askq{margin:0;font-size:14.5px;font-weight:600;color:var(--ink);max-width:42ch;line-height:1.4}
.yb-f-askopts{display:flex;flex-wrap:wrap;gap:6px}
.yb-f-askopt{
  border:1px solid var(--line);background:var(--surface);color:var(--ink);
  font:inherit;font-size:12.5px;padding:6px 12px;border-radius:var(--r-pill);cursor:pointer;
  transition:background 140ms ease,border-color 140ms ease
}
.yb-f-askopt:hover{background:var(--accent-soft);border-color:var(--accent);color:var(--accent)}
@media (prefers-reduced-motion:reduce){ .yb-f-askopt{transition:none} }

/* office: your real documents, opened in the app's real editor --------------------------- */
.yb-f-office{display:flex;flex-direction:column;gap:10px;align-items:flex-start}
.yb-f-officenote{margin:0;font-size:12px;color:var(--accent)}
.yb-f-doclist{list-style:none;margin:0;padding:0;width:100%;display:flex;flex-direction:column}
.yb-f-docrow{
  display:flex;align-items:center;gap:10px;padding:7px 2px;
  border-bottom:1px solid var(--line-soft);font-size:13px
}
.yb-f-dockind{
  flex:none;font-family:var(--mono);font-size:10px;letter-spacing:.06em;
  color:var(--accent);background:var(--accent-soft);
  border-radius:var(--r-xs);padding:2px 6px;min-width:44px;text-align:center
}
.yb-f-docname{
  flex:1;min-width:0;color:var(--ink);
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap
}
.yb-f-docacts{flex:none;display:inline-flex;gap:5px}

/* building ----------------------------------------------------------------------------- */
.yb-f-waiting{display:flex;flex-direction:column;gap:6px;height:100%;justify-content:center;align-items:center;text-align:center}
.yb-f-waitlabel{margin:0;font-size:14px;font-weight:600;color:var(--ink-2)}
.yb-f-waitsub{margin:0;font-size:12.5px;color:var(--mute);font-variant-numeric:tabular-nums}
.yb-f-waithint{margin:8px 0 0;font-size:11.5px;color:var(--faint);max-width:34ch}
.yb-f-offline{
  position:fixed;left:50%;top:12px;transform:translateX(-50%);z-index:70;margin:0;
  padding:7px 14px;border-radius:var(--r-pill);
  background:var(--accent);color:var(--on-accent);
  font-size:12.5px;box-shadow:var(--shadow-sm)
}
.yb-f-waitbar{
  display:block;width:120px;height:3px;margin-top:6px;border-radius:2px;
  background:var(--accent-soft);position:relative;overflow:hidden
}
.yb-f-waitbar::after{
  content:'';position:absolute;inset:0;width:40%;border-radius:2px;
  background:var(--accent);animation:yb-f-slide 1.1s ease-in-out infinite
}
@keyframes yb-f-slide{0%{transform:translateX(-100%)}100%{transform:translateX(250%)}}
@media (prefers-reduced-motion:reduce){
  .yb-f-waitbar::after{animation:none;width:100%;opacity:.5}
}

/* it went wrong ------------------------------------------------------------------------ */
.yb-f-err{display:flex;flex-direction:column;gap:6px;height:100%;justify-content:center;align-items:center;text-align:center;padding:12px}
.yb-f-errtitle{margin:0;font-size:14px;font-weight:600;color:var(--ink)}
.yb-f-errbody{margin:0;font-size:12.5px;color:var(--mute);max-width:44ch;line-height:1.5}

/* empty surface ------------------------------------------------------------------------ */
.yb-f-empty{
  display:flex;flex-direction:column;align-items:center;justify-content:center;
  gap:8px;height:100%;min-height:220px;text-align:center
}
.yb-f-emptybig{margin:0;font-size:19px;font-weight:600;color:var(--ink-2)}
/* What you kept, under the empty board's line. Rows rather than a grid of cards: a kept
   tile is a name you are scanning for, and eight names read faster in a column than eight
   boxes do. */
.yb-f-kept{margin-top:26px;text-align:left;display:inline-block;max-width:420px}
.yb-f-keptlabel{
  margin:0 0 8px;font-family:var(--mono);font-size:10px;letter-spacing:.13em;
  text-transform:uppercase;color:var(--faint)
}
.yb-f-keptlist{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:2px}
.yb-f-keptlist li{display:flex;align-items:center;gap:4px}
.yb-f-keptone{
  flex:1;min-width:0;text-align:left;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
  border:0;background:none;padding:6px 8px;border-radius:var(--r-xs);
  font:inherit;font-size:14px;color:var(--ink);cursor:pointer
}
.yb-f-keptone:hover{background:var(--surface-2)}
/* Forget stays out of the way until the row is under the pointer: the common act is opening
   one, and a delete button beside every name invites the rare act by mistake. */
.yb-f-keptx{
  flex:none;width:22px;height:22px;border:0;border-radius:50%;background:none;
  color:var(--faint);font:inherit;font-size:15px;line-height:1;cursor:pointer;
  opacity:0;transition:opacity .12s ease
}
.yb-f-keptlist li:hover .yb-f-keptx,.yb-f-keptx:focus-visible{opacity:1}
.yb-f-keptx:hover{background:var(--surface-2);color:var(--ink)}
.yb-f-emptysub{margin:0 0 6px;font-size:13.5px;color:var(--mute);max-width:52ch}

/* the tools — nothing is removed, everything stays one press away ---------------------- */
.yb-f-tools{display:flex;align-items:center;gap:6px;flex-wrap:wrap}
.yb-f-tool{
  display:inline-flex;align-items:center;gap:7px;
  padding:6px 12px;border:1px solid var(--line);border-radius:var(--r-pill);
  background:var(--surface);color:var(--ink-2);font:inherit;font-size:12.5px;cursor:pointer
}
.yb-f-tool:hover{background:var(--surface-2);color:var(--ink);border-color:var(--accent-soft)}

/* one-question answer card ------------------------------------------------------------- */
/* An answer is a note, not a modal. It used to sit dead centre at 38% of the height and
   520px wide, which is on top of whatever you were looking at and in front of the tile you
   just asked about — you cannot read the answer and the thing it is about at the same time.
   It goes in the bottom-left corner instead, clear of the dispatcher in the middle and the
   chatbot on the right, and it is capped: a long answer scrolls inside the note rather than
   growing up the screen. */
.yb-f-card{
  position:fixed;left:16px;bottom:16px;z-index:60;
  width:min(400px,calc(100vw - 32px));
  max-height:min(46vh,420px);display:flex;flex-direction:column;
  background:var(--surface);border:1px solid var(--line);border-radius:var(--r);
  padding:14px 16px;box-shadow:var(--shadow)
}
/* The answer itself is the part that scrolls; the question and the controls stay put. */
.yb-f-card>.yb-f-carda,.yb-f-card>.yb-f-cardera{
  overflow-y:auto;min-height:0;overscroll-behavior:contain
}
.yb-f-cardq{margin:0 0 8px;font-size:12.5px;color:var(--mute)}
.yb-f-carda{margin:0 0 12px;font-size:15px;color:var(--ink);line-height:1.55}
.yb-f-carda.yb-is-waiting{color:var(--mute)}
.yb-f-cardera{margin:0 0 12px;font-size:13.5px;color:var(--accent)}
.yb-f-esc{font-family:var(--mono);font-size:11px;color:var(--faint);margin-left:auto}

/* routing flash ------------------------------------------------------------------------ */
/* The notice can carry one action — see say() in layout-ambient.js. A link rather than a
   button shape: it sits inside a sentence, and a second pill floating over the board would
   read as chrome. */
.yb-f-flashact{
  margin-left:8px;border:0;background:none;padding:0;font:inherit;
  color:var(--accent);text-decoration:underline;text-underline-offset:2px;cursor:pointer
}
.yb-f-flashact:hover{filter:brightness(1.15)}
.yb-f-flash{
  position:fixed;left:50%;bottom:96px;transform:translateX(-50%);z-index:60;margin:0;
  padding:7px 14px;border-radius:var(--r-pill);
  background:var(--accent-soft);color:var(--accent);border:1px solid var(--accent);
  font-size:12.5px;box-shadow:var(--shadow-sm)
}

/* the dispatcher ----------------------------------------------------------------------- */
.yb-f-dock{
  /* In flow, under the rail. Fixed positioning is what let it land on top of the tools. */
  flex:none;align-self:center;width:min(760px,100%);
  display:flex;align-items:center;gap:10px;padding:9px 10px 9px 14px;
  background:var(--surface);border:1px solid var(--line);border-radius:var(--r);
  box-shadow:var(--shadow)
}
.yb-f-dockinput{
  flex:1;min-width:0;border:0;background:transparent;color:var(--ink);
  font:inherit;font-size:14.5px;outline:none
}
.yb-f-mic{
  flex:none;display:inline-flex;align-items:center;justify-content:center;
  width:32px;height:32px;border-radius:50%;cursor:pointer;
  border:1px solid var(--line);background:var(--surface);color:var(--mute)
}
.yb-f-mic:hover{color:var(--ink);border-color:var(--accent-soft)}
.yb-f-mic.yb-is-on{background:var(--accent);border-color:var(--accent);color:var(--on-accent)}
.yb-f-hint{
  flex:none;align-self:center;
  display:inline-flex;align-items:center;gap:10px;padding:8px 16px;
  background:var(--surface);border:1px solid var(--line);border-radius:var(--r-pill);
  color:var(--mute);font:inherit;font-size:13px;cursor:pointer;box-shadow:var(--shadow-sm)
}
.yb-f-hint:hover{color:var(--ink);border-color:var(--accent-soft)}
.yb-f-kbd{
  font-family:var(--mono);font-size:11px;color:var(--accent);
  border:1px solid var(--accent-soft);border-radius:var(--r-xs);padding:2px 6px
}

/* phones: the tiler stops tiling — one column, scrolled, because four panes on a 375px
   screen is four unreadable panes (mobile-ergonomics.css makes the same call elsewhere) */
@media (max-width:720px){
  .yb-f-stage{grid-template-columns:repeat(2,minmax(0,1fr));grid-auto-rows:130px}
  .yb-f-s3x1,.yb-f-s3x2,.yb-f-s3x3,.yb-f-s3x4,
  .yb-f-s4x1,.yb-f-s4x2,.yb-f-s4x3,.yb-f-s4x4,
  .yb-f-s6x1,.yb-f-s6x2,.yb-f-s6x3,.yb-f-s6x4{grid-column:span 2}
  .yb-f-knows{display:none}
}

/* ---- Mail and the chatbot: two windows Yarnball draws itself ------------------------
   The mailbox is a reading surface, not a dashboard — one column of senders, one message
   at a time, and the reply where the message is. */
.yb-f-footbtn-go{background:var(--accent);border-color:var(--accent);color:var(--on-accent);font-weight:600}
.yb-f-footbtn-go:hover:enabled{background:var(--accent);filter:brightness(1.06)}

.yb-m{display:flex;flex-direction:column;height:100%;min-height:0}
.yb-m-bar{
  display:flex;align-items:center;gap:8px;padding:8px 12px;flex:0 0 auto;
  border-bottom:1px solid var(--line);background:var(--surface)
}
.yb-m-bartitle{font-size:13px;font-weight:650;color:var(--ink);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.yb-m-spacer{flex:1 1 auto}

.yb-m-list{list-style:none;margin:0;padding:0;overflow:auto;flex:1 1 auto;min-height:0}
.yb-m-row{
  display:flex;gap:10px;width:100%;text-align:left;padding:10px 12px;cursor:pointer;
  background:transparent;border:0;border-bottom:1px solid var(--line);font:inherit
}
.yb-m-row:hover{background:var(--surface-2)}
.yb-m-row.is-unread .yb-m-subj{font-weight:650;color:var(--ink)}
.yb-m-av{
  flex:0 0 auto;width:28px;height:28px;border-radius:999px;display:grid;place-items:center;
  background:var(--surface-2);color:var(--ink-2);font-size:11px;font-weight:650;letter-spacing:.02em
}
.yb-m-rowmain{display:flex;flex-direction:column;gap:2px;min-width:0;flex:1 1 auto}
.yb-m-rowtop{display:flex;align-items:baseline;gap:8px}
.yb-m-from{font-size:12.5px;font-weight:600;color:var(--ink);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.yb-m-date{margin-left:auto;font-size:11px;color:var(--ink-soft);flex:0 0 auto}
.yb-m-subj{font-size:12.5px;color:var(--ink-2);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.yb-m-prev{font-size:11.5px;color:var(--ink-soft);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}

.yb-m-read{padding:12px;overflow:auto;flex:1 1 auto;min-height:0;display:flex;flex-direction:column;gap:10px}
.yb-m-who{display:flex;align-items:center;gap:9px}
.yb-m-sum{
  margin:0;font-size:13px;line-height:1.6;color:var(--ink-2);
  border-left:2px solid var(--accent-soft);padding-left:11px
}
.yb-m-body{
  margin:0;font:inherit;font-size:13px;line-height:1.65;color:var(--ink-2);
  white-space:pre-wrap;word-break:break-word
}
.yb-m-note{margin:0;font-size:12px;color:var(--ink-soft)}
.yb-m-err{margin:0;font-size:12.5px;color:var(--accent)}
.yb-m-actions{display:flex;gap:8px;padding:8px 12px;border-top:1px solid var(--line);flex:0 0 auto}

.yb-m-form{display:flex;flex-direction:column;gap:8px;padding:12px;flex:1 1 auto;min-height:0}
.yb-m-input,.yb-m-text{
  font:inherit;font-size:13px;color:var(--ink);background:var(--surface);
  border:1px solid var(--line);border-radius:var(--r-xs);padding:7px 10px;width:100%
}
.yb-m-text{flex:1 1 auto;min-height:120px;resize:none;line-height:1.6}
.yb-m-input:focus,.yb-m-text:focus{outline:2px solid var(--accent-soft);outline-offset:-1px}
.yb-m-send{display:flex;align-items:center;gap:8px;padding:8px 12px;border-top:1px solid var(--line);flex:0 0 auto}

.yb-m-load{display:flex;flex-direction:column;gap:10px;padding:14px}
.yb-m-skel{
  height:30px;border-radius:var(--r-xs);
  background:linear-gradient(90deg,var(--line) 25%,var(--surface) 37%,var(--line) 63%);
  background-size:400% 100%;animation:yb-m-sk 1.4s ease infinite
}
@keyframes yb-m-sk{0%{background-position:100% 50%}100%{background-position:0 50%}}

/* The chatbot: a conversation that happens to be tiled. */
.yb-b{display:flex;flex-direction:column;height:100%;min-height:0}
.yb-b-thread{flex:1 1 auto;min-height:0;overflow:auto;padding:12px;display:flex;flex-direction:column;gap:10px}
.yb-b-msg{max-width:88%;display:flex;flex-direction:column;gap:5px}
.yb-b-you{align-self:flex-end;align-items:flex-end}
.yb-b-yarnball{align-self:flex-start}
.yb-b-text{
  margin:0;font-size:13px;line-height:1.6;padding:8px 11px;border-radius:var(--r-sm);
  background:var(--surface-2);color:var(--ink-2);white-space:pre-wrap
}
.yb-b-you .yb-b-text{background:var(--accent);color:var(--on-accent)}
.yb-b-think{color:var(--ink-soft)}
.yb-b-did{margin:0;padding-left:16px;font-size:11.5px;color:var(--ink-soft);display:flex;flex-direction:column;gap:3px}
/* Four controls in a 240px column do not fit on one line, so the row wraps rather than
   running off the edge — the panel is resizable now, and the composer has to hold at the
   narrowest width it can be dragged to, not just at the width it shipped with. */
.yb-b-form{display:flex;flex-wrap:wrap;gap:8px;padding:9px 12px;border-top:1px solid var(--line);flex:0 0 auto}
/* On one line it already sits last; on two it stays on the right instead of drifting left. */
.yb-b-form .yb-f-footbtn-go{margin-left:auto}
/* min-width:0 is load-bearing. An <input> carries an intrinsic minimum width from its size
   attribute — about twenty characters — and a flex item will not shrink below that on its
   own, so in a 296px panel the input held its ground and pushed Send off the edge instead.
   This is the rail's composer wherever it appears, Work's panel included. The 150px basis is
   what lets it drop to a second line instead of squeezing when the panel is dragged narrow. */
.yb-b-input{
  flex:1 1 150px;min-width:0;font:inherit;font-size:13px;color:var(--ink);background:var(--surface);
  border:1px solid var(--line);border-radius:999px;padding:7px 13px
}
.yb-b-input:focus{outline:2px solid var(--accent-soft);outline-offset:-1px}

/* Calendar and Drive share the mailbox's bones — a bar, a list of rows, a reading pane —
   because they are the same kind of surface: a list of things, one of them open. */
.yb-m-day{
  margin:0;padding:10px 12px 4px;font-size:11px;font-weight:650;letter-spacing:.06em;
  text-transform:uppercase;color:var(--ink-soft);position:sticky;top:0;background:var(--surface)
}
.yb-m-at{
  flex:0 0 auto;width:44px;font-size:12px;font-variant-numeric:tabular-nums;
  color:var(--ink-2);padding-top:1px
}
.yb-m-find{
  flex:1 1 auto;min-width:0;font:inherit;font-size:12.5px;color:var(--ink);
  background:var(--surface);border:1px solid var(--line);border-radius:var(--r-pill);padding:5px 11px
}
.yb-m-find:focus{outline:2px solid var(--accent-soft);outline-offset:-1px}

/* Honest about a cached list rather than pretending it is live. */
.yb-m-cached{
  font-family:var(--mono);font-size:10.5px;color:var(--faint);
  padding:2px 7px;border:1px solid var(--line);border-radius:var(--r-pill)
}

/* ===================== the mailbox switcher ============================================
   Only drawn when there is more than one mailbox, so most people never see it at all. */
.yb-m-inboxes{display:flex;align-items:center;gap:4px;flex-wrap:wrap;padding:0 2px 8px}
.yb-m-inbox{
  display:inline-flex;align-items:center;gap:6px;
  padding:5px 11px;border:1px solid var(--line);border-radius:var(--r-pill);
  background:var(--surface);color:var(--ink-2);font:inherit;font-size:12.5px;cursor:pointer;
  max-width:220px
}
.yb-m-inbox:hover{background:var(--surface-2);color:var(--ink)}
.yb-m-inbox.yb-is-on{border-color:var(--accent);background:var(--surface-2);font-weight:600;color:var(--ink)}
.yb-m-inboxname{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}

/* ===================== the two edge panels ============================================
   Notifications open on the left, the chatbot on the right, and both take their room FROM
   the stage rather than sitting on top of it. Opening one makes the windows narrower, which
   is what a sidebar is supposed to do — an overlay covered the workspace tabs and read as a
   rendering bug. Closed, they take no floor at all and the stage is full width. */
/* The shell pads every layout by 40px a side. The stage wants that breathing room; the
   panels want the actual wall, and 40px of empty page beside a panel just reads as a gap
   nobody chose. So the padding comes off the row and goes onto the stage instead. */
.yb-f{flex-direction:row;gap:0;align-items:stretch;position:relative;overflow:hidden;
  padding-left:0;padding-right:0}
/* `order` rather than DOM order, so the rail can render both panels after the stage and
   still put the left one on the left. */
.yb-f-main{
  order:0;
  display:flex;flex-direction:column;gap:12px;
  flex:1;min-width:0;min-height:0;overflow:hidden;
  padding:0 40px 16px
}

.yb-edge{
  order:1;
  flex:0 0 0px;min-width:0;min-height:0;overflow:hidden;
  display:flex;align-items:stretch;
  transition:flex-basis .34s cubic-bezier(.32,.72,0,1)
}
/* The contents fade in over the last of the slide. Opening on a hard cut was the jump that
   made the whole movement read as a snap however smooth the width itself was. */
.yb-edge .yb-edge-panel{opacity:0;transition:opacity .18s ease .1s}
.yb-edge.yb-is-open .yb-edge-panel{opacity:1}
.yb-edge-left{order:-1}
/* Each panel's width, and the only place either is written down. The drag handle sets one of
   these two properties; both the column and the panel inside it read it. */
.yb-f{--yb-edge-w-left:296px;--yb-edge-w-right:296px}
.yb-edge-left.yb-is-open{flex-basis:var(--yb-edge-w-left)}
.yb-edge-right.yb-is-open{flex-basis:var(--yb-edge-w-right)}
/* While a drag is in flight the slide transition has to be off, or the panel chases the
   pointer a third of a second behind it. */
.yb-f.yb-is-sizing .yb-edge,
.yb-f.yb-is-sizing .yb-edge .yb-edge-panel{transition:none}
.yb-f.yb-is-sizing{cursor:col-resize;user-select:none}
/* Fixed width whatever the opening is doing, so the contents slide into view instead of
   reflowing through every frame. The border lives here so a shut panel shows no hairline. */
.yb-edge-panel{
  position:relative;
  flex:none;min-height:0;
  display:flex;flex-direction:column;background:var(--surface)
}
.yb-edge-left .yb-edge-panel{width:var(--yb-edge-w-left)}
.yb-edge-right .yb-edge-panel{width:var(--yb-edge-w-right)}
/* The drag handle: the panel's own inner edge, widened to something a mouse can actually
   hit. Invisible until it is wanted — a permanent bar down the middle of the screen is a
   piece of furniture nobody asked for. */
.yb-edge-grip{
  position:absolute;top:0;bottom:0;width:9px;z-index:3;
  cursor:col-resize;touch-action:none
}
.yb-edge-grip-right{left:0}
.yb-edge-grip-left{right:0}
.yb-edge-grip::before{
  content:'';position:absolute;top:0;bottom:0;width:1px;
  background:var(--accent);opacity:0;transition:opacity .15s ease
}
.yb-edge-grip-right::before{left:4px}
.yb-edge-grip-left::before{right:4px}
.yb-edge-grip:hover::before,.yb-edge-grip:focus-visible::before{opacity:1}
.yb-edge-grip:focus-visible{outline:none}
.yb-edge-left .yb-edge-panel{border-right:1px solid var(--line)}
.yb-edge-right .yb-edge-panel{border-left:1px solid var(--line)}

.yb-edge-bar{
  display:flex;align-items:center;gap:8px;flex:none;
  padding:11px 12px;border-bottom:1px solid var(--line)
}
.yb-edge-title{
  flex:1;font-size:12.5px;font-weight:650;
  text-transform:uppercase;letter-spacing:.03em;color:var(--ink-2)
}
/* The chatbot panel puts the arrow first and the name last (SB, 2026-08-20), so the name
   sits against the outer wall and the arrow against the workspace it folds away towards.
   Notifications keeps the usual order — it is the mirror side, and the same order there
   would put its arrow in the middle of the screen. */
.yb-edge-right .yb-edge-title{text-align:right}
.yb-edge-close{
  flex:none;width:26px;height:26px;border:0;border-radius:var(--r-xs);
  background:transparent;color:var(--mute);
  font:inherit;font-size:18px;line-height:1;cursor:pointer
}
.yb-edge-close:hover{background:var(--surface-2);color:var(--ink)}
.yb-edge-body{
  flex:1;min-height:0;overflow:auto;
  padding:10px 12px 14px;display:flex;flex-direction:column
}
/* The chatbot fills its panel; a chat that only uses the top third is wasted room. */
.yb-edge-body .yb-b{flex:1;min-height:0}

/* The handle. ONE control per panel, halfway down its edge, and it both opens and closes
   (SB, 2026-08-20). It used to be a round button near the top that only opened, with the
   closing done by a separate arrow in the panel's header — so the way out was never where
   the way in had been, and with the panel open there was nothing on the edge at all.

   It rides the edge as the panel slides, which is what the transition on left/right is for:
   shut, it sits against the wall; open, it sits against the panel's inner border, on the
   same easing and duration as the panel itself so the two move as one thing. Rounded on the
   stage side only, in both states, so it always reads as a tab pulled out of that wall. */
.yb-tab{
  position:absolute;top:50%;transform:translateY(-50%);z-index:6;
  width:20px;height:58px;
  display:inline-flex;align-items:center;justify-content:center;
  border:1px solid var(--line);background:var(--surface);color:var(--mute);
  box-shadow:0 2px 10px rgba(0,0,0,.10);
  cursor:pointer;font:inherit;font-size:13px;line-height:1;padding:0;
  transition:background .15s ease,color .15s ease,
    left .34s cubic-bezier(.32,.72,0,1),right .34s cubic-bezier(.32,.72,0,1)
}
.yb-tab:hover{background:var(--surface-2);color:var(--ink)}
.yb-tab:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
.yb-tab-left{left:0;border-left:0;border-radius:0 var(--r) var(--r) 0}
.yb-tab-right{right:0;border-right:0;border-radius:var(--r) 0 0 var(--r)}
/* Open, it stands on the panel's inner border. 296px is .yb-edge-panel's own width — the
   two numbers have to agree or the handle floats off the edge it belongs to. */
.yb-tab-left.yb-is-open{left:296px}
.yb-tab-right.yb-is-open{right:296px}

@media (prefers-reduced-motion:reduce){
  .yb-edge,.yb-edge .yb-edge-panel,.yb-f-win{transition:none}
}

/* ===================== notifications =================================================== */
.yb-n{display:flex;flex-direction:column;gap:8px}
.yb-n-head{display:flex;align-items:center;gap:8px}
.yb-n-count{flex:1;font-size:12.5px;color:var(--mute)}
.yb-n-sample{
  margin:0;padding:8px 10px;border:1px dashed var(--line);border-radius:var(--r);
  font-size:11.5px;line-height:1.45;color:var(--mute)
}
.yb-n-list{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:2px}
.yb-n-item{border-radius:var(--r)}
.yb-n-btn{
  display:flex;align-items:flex-start;gap:9px;width:100%;
  padding:9px 10px;border:0;border-radius:var(--r);
  background:transparent;color:inherit;font:inherit;text-align:left;cursor:pointer
}
.yb-n-btn:hover{background:var(--surface-2)}
.yb-n-icon{display:inline-flex;padding-top:2px;color:var(--mute);flex:none}
.yb-n-item.yb-is-unread .yb-n-icon{color:var(--accent)}
.yb-n-text{display:flex;flex-direction:column;gap:2px;flex:1;min-width:0}
.yb-n-title{font-size:13px;color:var(--ink-2)}
.yb-n-item.yb-is-unread .yb-n-title{font-weight:650;color:var(--ink)}
.yb-n-body{
  font-size:12px;line-height:1.4;color:var(--mute);
  display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden
}
.yb-n-when{font-family:var(--mono);font-size:10.5px;color:var(--faint);flex:none;padding-top:2px}

/* ===================== what used to live here ==========================================
   A second .yb-f-stage that made the board a wrapped flex row of half-width tiles, plus
   the n1-n4 minimum heights and the fullscreen override. All of it decided a window's
   size from how many were open, which is the thing the unit grid above replaces -- so it
   is gone rather than left to fight the grid from further down the file.

   Minimise went with the title bar it lived in. A tile is content or it is closed; a
   collapsed bar was chrome standing in for a window, and the board no longer needs the
   room back because a tile only ever takes the footprint it declared. */

/* ===================== renaming and deleting a workspace ============================== */
.yb-f-tabwrap{display:inline-flex;align-items:center;gap:2px}
.yb-f-tabedit{
  padding:6px 12px;border:1px solid var(--accent);border-radius:var(--r-pill);
  background:var(--surface);color:var(--ink);font:inherit;font-size:13.5px;
  font-weight:600;width:9rem;min-width:0
}
.yb-f-tabkill{
  display:inline-flex;align-items:center;justify-content:center;
  min-width:22px;height:22px;padding:0 6px;
  border:0;border-radius:var(--r-pill);
  background:transparent;color:var(--faint);
  font:inherit;font-size:13px;line-height:1;cursor:pointer
}
.yb-f-tabkill:hover{background:var(--surface-2);color:var(--ink)}
/* Armed. It says what the next press does rather than just looking dangerous. */
.yb-f-tabkill.yb-is-on{
  background:var(--accent);color:var(--on-accent);
  font-size:11px;font-weight:600;padding:0 9px
}

/* ===================== the layout picker's Experiment tag ============================
   Lives in this block because the block is replaced wholesale when the panels change,
   and a rule sitting after it gets taken with it. */
.yb-pick-tag{
  display:inline-block;vertical-align:middle;
  font-family:var(--mono);font-size:10px;font-weight:600;
  text-transform:uppercase;letter-spacing:.06em;
  padding:2px 7px;border-radius:var(--r-pill);
  background:var(--accent);color:var(--on-accent)
}

/* ===================== both panels out: a narrower canvas =============================
   One panel still leaves room for two up and two down, and the tiles keep it (SB,
   2026-08-19). TWO panels take about 600px, so the board is dividing what is left.

   It used to answer that by collapsing every window but one down to its title bar. That
   made sense while a window WAS a title bar plus a body; with the bar gone, a collapsed
   window is a blank card -- worse than a small one, and it read as a rendering fault.
   Footprints have a better answer: narrow the canvas exactly as the tablet breakpoint does
   and let every tile keep its own shape at a smaller cell. Nothing is hidden, nothing is
   unmounted, and closing a panel puts the last two columns back.

   The media queries below cannot do this job -- they measure the VIEWPORT, and the viewport
   has not changed. The stage has. */
.yb-f.yb-f-squeeze .yb-f-stage{grid-template-columns:repeat(4,minmax(0,1fr))}
.yb-f.yb-f-squeeze .yb-f-s6x1,
.yb-f.yb-f-squeeze .yb-f-s6x2,
.yb-f.yb-f-squeeze .yb-f-s6x3,
.yb-f.yb-f-squeeze .yb-f-s6x4{grid-column:span 4}


/* ============ WhatsApp channel panel (ported from feat/sb-whatsapp-preview, 2026-08-25) ============ */


/* ===================== WhatsApp (preview) =============================================
   Removable in one block: delete this section with whatsapp.js. */
.yb-w-note{
  border:1px dashed var(--line);border-radius:var(--r-md);
  padding:12px 14px;margin:0 2px 10px;display:flex;flex-direction:column;gap:8px
}
.yb-w-title{margin:0;font-size:13px;font-weight:650;color:var(--ink-1)}
.yb-w-body{margin:0;font-size:12px;line-height:1.5;color:var(--mute)}
.yb-w-reqs{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:7px}
.yb-w-req{display:flex;align-items:flex-start;gap:9px}
.yb-w-who{
  flex:none;min-width:56px;text-align:center;
  font-family:var(--mono);font-size:10px;text-transform:uppercase;letter-spacing:.04em;
  padding:2px 6px;border-radius:var(--r-pill);
  background:var(--surface-2);color:var(--mute)
}
.yb-w-who.yb-is-person{background:var(--accent);color:var(--on-accent)}
.yb-w-reqtext{display:flex;flex-direction:column;gap:1px;min-width:0}
.yb-w-reqwhat{font-size:12.5px;color:var(--ink-2)}
.yb-w-reqnote{font-size:11.5px;line-height:1.45;color:var(--faint)}

.yb-w-threads{list-style:none;margin:0;padding:0;display:flex;flex-direction:column}
.yb-w-thread{display:flex;align-items:center;gap:11px;padding:10px 6px;border-bottom:1px solid var(--line)}
.yb-w-thread:last-child{border-bottom:0}
.yb-w-avatar{
  flex:none;width:34px;height:34px;border-radius:50%;
  display:inline-flex;align-items:center;justify-content:center;
  background:var(--surface-2);color:var(--ink-2);font-weight:650;font-size:14px
}
.yb-w-main{display:flex;flex-direction:column;gap:2px;flex:1;min-width:0}
.yb-w-top{display:flex;align-items:baseline;gap:8px}
.yb-w-name{font-size:13.5px;color:var(--ink-1);flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.yb-w-when{font-family:var(--mono);font-size:10.5px;color:var(--faint);flex:none}
.yb-w-last{font-size:12px;color:var(--mute);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.yb-w-unread{
  flex:none;min-width:20px;text-align:center;
  font-family:var(--mono);font-size:11px;font-weight:600;padding:2px 6px;
  border-radius:var(--r-pill);background:var(--accent);color:var(--on-accent)
}
.yb-w-foot{margin:10px 2px 0;font-size:11.5px;line-height:1.45;color:var(--faint)}

/* ------------------------------------------------------------------------- */
/* Mail rework (SPEC-ben-email-rework-20260826, stream B).                    */
/* Items 1.3/1.4: the four-column mail list. Header and rows share one grid  */
/* template; the two fixed columns take their width from custom properties    */
/* the pane sets from localStorage 'yarnball.mailCols'.                       */
.mail-cols {
  display: grid;
  grid-template-columns: var(--mail-col-sender, 168px) minmax(0, 1fr) 26px var(--mail-col-date, 84px);
  align-items: center;
  gap: 6px;
  padding: 0 10px;
  border-bottom: 1px solid var(--line);
  background: var(--surface);
}
.mail-col {
  position: relative;
  display: flex;
  align-items: center;
  gap: 4px;
  min-width: 0;
  padding: 4px 2px;
  border: 0;
  background: transparent;
  text-align: left;
  font-family: var(--mono);
  font-size: 9.5px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--mute);
  cursor: pointer;
  overflow: hidden;
  white-space: nowrap;
}
.mail-col:hover { color: var(--ink); }
.mail-col.is-sorted { color: var(--accent); }
.mail-col-attach { justify-content: center; cursor: default; }
.mail-col-date { justify-content: flex-end; }
.mail-col-handle {
  position: absolute;
  top: 0;
  right: -5px;
  width: 9px;
  height: 100%;
  cursor: col-resize;
  touch-action: none;
}
.mail-col-handle::after {
  content: '';
  position: absolute;
  top: 15%;
  bottom: 15%;
  left: 4px;
  width: 1px;
  background: var(--line);
}
.mail-col-handle:hover::after, .mail-col-handle:focus-visible::after { background: var(--accent); }
/* The rows keep the .mail-item hover/active/unread treatment and lay their four cells on
   the same template as the header. */
.mail-item.mail-cols-row {
  display: grid;
  grid-template-columns: var(--mail-col-sender, 168px) minmax(0, 1fr) 26px var(--mail-col-date, 84px);
  align-items: center;
  gap: 6px;
  padding: 4px 10px;
}
.mail-col-cell { display: flex; align-items: center; gap: 5px; min-width: 0; overflow: hidden; white-space: nowrap; }
.mail-col-cell.mail-col-sender { display: block; text-overflow: ellipsis; }
.mail-col-cell.mail-col-subject { flex-wrap: nowrap; }
.mail-col-cell.mail-col-attach { justify-content: center; color: var(--mute); }
.mail-col-cell.mail-col-date { justify-content: flex-end; }

/* Item 1.5: the attachment-only filter chip on the list toolbar. */
.mail-filter-chip {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  /* G02 (SB 2026-09-04): the chips are icons now, so they are as wide as an icon. */
  padding: 4px 7px;
  border: 1px solid var(--line);
  border-radius: var(--r-pill);
  background: var(--surface-2);
  color: var(--mute);
  font-family: var(--ui);
  font-size: 11.5px;
  cursor: pointer;
  white-space: nowrap;
}
.mail-filter-chip:hover { color: var(--ink); }
.mail-filter-chip.active {
  color: var(--accent);
  border-color: color-mix(in srgb, var(--accent) 40%, transparent);
  background: var(--accent-soft);
}
.mail-filter-chip-label { white-space: nowrap; }

/* Items 1.7/1.8: ONE action row in the reading pane — icon verbs, a divider, then the
   AI-proposed actions as labelled chips. */
.mail-actionbar {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 4px;
  /* G02 (SB 2026-09-04): tighter row, and the verbs on it are one size smaller. */
  padding: 4px 12px;
  border-bottom: 1px solid var(--line-soft);
  background: var(--surface);
}
.mail-actionbar .btn { padding: 4px 10px; font-size: 12px; }
.mail-actionbar-divider {
  width: 1px;
  align-self: stretch;
  margin: 2px 4px;
  background: var(--line);
}
.mail-actionbar .icbtn.is-on {
  color: var(--accent);
  border-color: color-mix(in srgb, var(--accent) 45%, transparent);
  background: var(--accent-soft);
}
.mail-actionbar-chip {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 3px 9px;
  border: 1px solid color-mix(in srgb, var(--accent) 35%, transparent);
  border-radius: var(--r-pill);
  background: var(--accent-soft);
  color: var(--accent);
  font-family: var(--ui);
  font-size: 11.5px;
  cursor: pointer;
  white-space: nowrap;
}
.mail-actionbar-chip:hover { border-color: var(--accent); }

/* Item 1.9: who the reply is going TO, by name, resolved through the server. */
.mail-recipient-chips { display: flex; flex-wrap: wrap; gap: 4px; margin: 6px 0 2px; }
.mail-recipient-chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  padding: 2px 8px;
  border: 1px solid var(--line);
  border-radius: var(--r-pill);
  background: var(--surface-2);
  color: var(--ink-2);
  font-family: var(--ui);
  font-size: 11.5px;
}
/* CR-3 (Ben, change requests 2026-08-26): the chips above become the field itself —
   removable with an x, addable by typing — on reply, reply-all and new mail alike (see
   RecipientChips in work.js, which replaces both the plain <input> this used to sit above
   AND new mail's own separate, non-chip RecipientField). One boxed container standing in
   for what used to be a plain <input>: the committed chips and the text still being typed
   share a single field the way any chip-input reads. */
.mail-recipient-field {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 4px;
  width: 100%;
  padding: 4px 6px;
  border: 1px solid var(--line);
  border-radius: var(--r-xs);
  background: var(--surface-2);
}
.mail-recipient-input {
  flex: 1 1 90px;
  min-width: 90px;
  border: 0;
  background: transparent;
  color: var(--ink);
  font-family: var(--ui);
  font-size: 12.5px;
  padding: 3px 4px;
}
.mail-recipient-input:focus { outline: none; }

/* Item 1.10: Bcc stays out of the way until asked for. */
.mail-compose-field-row { display: flex; align-items: center; gap: 6px; }
.mail-compose-field-row .mail-compose-field { flex: 1; min-width: 0; }
.mail-compose-bcc-link {
  flex: none;
  padding: 2px 7px;
  border: 1px solid var(--line);
  border-radius: var(--r-pill);
  background: transparent;
  color: var(--mute);
  font-family: var(--mono);
  font-size: 9.5px;
  letter-spacing: 0.08em;
  cursor: pointer;
}
.mail-compose-bcc-link:hover { color: var(--accent); border-color: color-mix(in srgb, var(--accent) 40%, transparent); }

/* Item 1.14: the created Teams meeting, in its window. */
.mail-meeting-result { display: flex; flex-direction: column; gap: 10px; padding: 4px 2px; }
.mail-meeting-line { margin: 0; font-size: 13px; line-height: 1.5; color: var(--ink-2); }
.mail-meeting-link { font-size: 13px; color: var(--accent); text-decoration: underline; }

/* Items 1.13/1.15: the one-line confirmation toast, bottom of the Work frame. */
.mail-toast {
  position: absolute;
  left: 50%;
  bottom: 18px;
  transform: translateX(-50%);
  z-index: 80;
  display: flex;
  align-items: center;
  gap: 10px;
  max-width: min(520px, 80%);
  padding: 8px 14px;
  border: 1px solid var(--line);
  border-radius: var(--r-pill);
  background: var(--ink);
  color: var(--bg);
  box-shadow: var(--shadow-sm);
  font-family: var(--ui);
  font-size: 12.5px;
}
.mail-toast-text { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.mail-toast-link { color: inherit; text-decoration: underline; white-space: nowrap; }
