/* ==========================================================================
   ZENDAY UI — one moment, at full size
   ==========================================================================
   This file has been through two wrong versions and it is worth writing down
   why, because the mistake is easy to make twice.

   The first was inherited: mock interfaces authored at 5-9px and scaled up
   with `zoom`, so nothing was the size it claimed to be. The second was mine —
   twelve complete application screens, rail and stat tiles and record rows and
   agent bar, each rendered at about 40% into a card. That one was internally
   consistent and still wrong, because a whole dashboard shrunk into a 400px
   box shows everything and demonstrates nothing. The eye cannot read it, so it
   files it as texture and moves on.

   THE BRIEF NOW
   One card, one moment, at the size you would really see it, cropped by the
   card so it reads as a detail of something larger. The animation is not
   decoration laid over a picture — it IS the demonstration. A transaction
   arrives and gets coded. Two records find each other. A close fills up. If a
   fragment needs a caption to explain what it is showing, it is showing the
   wrong thing.

   RULES
   1. One focal object. Two at the most, and only when the point is the
      relationship between them.
   2. No window chrome. No rail, no toolbar, no fake buttons, no tab bars.
      This is a detail view, not a screenshot of an app.
   3. Everything sets at real interface size: 17px on a record title, 13px
      under it. If it will not fit at that size, the fragment is too busy.
   4. One animation, and it must show the product working. It plays on entry
      and replays on hover. Under `prefers-reduced-motion` every fragment holds
      its finished state, which is always the meaningful one.
   ========================================================================== */

:root{
  /* The fragments sit directly on the page's own paper, so they run on the
     page's own ramp. They had a near-white and a cool grey of their own back
     when they were framed panels pretending to be a separate application;
     with the frame gone that only put two greys and two whites on one card. */
  --ui-bg:var(--paper);
  --ui-sunken:#F0EFEA;      /* one warm step under paper, for fills */
  --ui-line:rgba(23,22,28,.09);
  --ui-line-soft:rgba(23,22,28,.055);

  --ui-ink:var(--ink);
  --ui-muted:var(--muted);
  --ui-quiet:var(--quiet);

  /* Two colours, and each one means exactly one thing.
     INDIGO says the agent did this. AMBER says a person still has to.
     Everything else in a fragment is ink, muted, quiet or a hairline — which
     is what makes the two that are left carry any weight at all. Green is
     gone: it used to mean "done", but done is the default state of every
     screen here, so colouring it spent the eye's attention on the one thing
     that never needed it. A tick is enough. */
  --ui-brand:var(--indigo);
  --ui-brand-deep:var(--indigo-deep);
  --ui-brand-soft:var(--indigo-soft);
  --ui-brand-line:rgba(29,27,196,.18);

  --ui-warn:var(--amber);
  --ui-warn-soft:var(--amber-soft);
  --ui-warn-line:rgba(138,90,30,.42);

  /* a settled tick, a matched pair, a balanced entry: shape, not colour */
  --ui-done:var(--muted);
  --ui-done-soft:#EDECE7;

  --ui-r:15px;
  --ui-r-sm:10px;
}

/* --------------------------------------------------------------------------
   The stage
   --------------------------------------------------------------------------
   Transparent. The card is the ground and the fragment sits on it. Giving the
   stage a panel of its own is exactly what made the last version read as a
   small application instead of a close-up.
   -------------------------------------------------------------------------- */

.ui{
  position:relative;
  display:grid;
  align-content:center;
  gap:16px;
  height:100%;
  padding:8px 4px;
  overflow:hidden;
  font-family:var(--font-sans);
  color:var(--ui-ink);
  font-variant-numeric:tabular-nums;
  word-spacing:.03em;
  -webkit-font-smoothing:antialiased;
}

/* A surface object: a record, an entry, a compose window — anything that is a
   piece of interface rather than a bare figure. */
.ui-obj{
  padding:16px 18px;
  border:1px solid var(--ui-line);
  border-radius:var(--ui-r);
  background:var(--ui-bg);
}

/* The one line under a fragment. It says who did the work — never what the
   picture is already showing. */
.ui-note{
  display:flex;
  align-items:center;
  gap:9px;
  padding:0 4px;
  color:var(--ui-quiet);
  font-size:12.5px;
  line-height:1.45;
}
.ui-note>i{
  flex:0 0 auto;
  display:grid;place-items:center;
  width:19px;height:19px;
  border:1px solid var(--ui-brand-line);
  border-radius:50%;
  color:var(--ui-brand);
  font-size:10px;font-style:normal;
}
.ui-note b{color:var(--ui-muted);font-weight:600}
.ui-note.needs-you>i{border-color:rgba(138,90,30,.42);color:var(--ui-warn)}
.ui-note.needs-you b{color:var(--ui-warn)}

/* --------------------------------------------------------------------------
   A record, at the size a record is
   -------------------------------------------------------------------------- */

.ui-rec{display:grid;grid-template-columns:42px minmax(0,1fr) auto;align-items:center;gap:14px}
.ui-mk{
  display:grid;place-items:center;
  width:42px;height:42px;
  border-radius:12px;
  background:var(--ui-sunken);
  color:var(--ui-muted);
  font-size:14px;font-weight:700;font-style:normal;
}
.ui-mk.is-round{border-radius:50%}
.ui-mk.is-brand{background:var(--ui-brand-soft);color:var(--ui-brand-deep)}
.ui-mk.is-warn{background:var(--ui-warn-soft);color:var(--ui-warn)}
.ui-mk.is-doc{background:transparent;border:1px solid var(--ui-line);color:var(--ui-quiet);font-size:11.5px}

.ui-t{display:grid;gap:3px;min-width:0}
.ui-t>strong{
  font-size:17px;font-weight:600;letter-spacing:-.018em;line-height:1.2;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
}
.ui-t>small{
  color:var(--ui-quiet);font-size:13px;line-height:1.3;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
}
.ui-amt{font-size:17px;font-weight:600;letter-spacing:-.022em;white-space:nowrap}

.ui-chip{
  display:inline-flex;align-items:center;gap:6px;
  padding:4px 10px;
  border-radius:999px;
  background:var(--ui-sunken);
  color:var(--ui-quiet);
  font-size:12.5px;font-weight:600;font-style:normal;white-space:nowrap;
}
.ui-chip.is-ok{background:var(--ui-done-soft);color:var(--ui-done)}
.ui-chip.is-warn{background:var(--ui-warn-soft);color:var(--ui-warn)}
.ui-chip.is-brand{background:var(--ui-brand-soft);color:var(--ui-brand-deep)}
.ui-chips{display:flex;flex-wrap:wrap;gap:7px;margin-top:14px;padding-top:13px;border-top:1px solid var(--ui-line-soft)}

/* --------------------------------------------------------------------------
   Display figures
   -------------------------------------------------------------------------- */

.ui-fig{display:grid;gap:5px;justify-items:start}
.ui-fig>small{color:var(--ui-quiet);font-size:13px;font-weight:500}
.ui-fig>strong{font-size:42px;font-weight:600;line-height:1;letter-spacing:-.042em}
.ui-fig>span{display:flex;align-items:center;gap:8px;color:var(--ui-done);font-size:13px;font-weight:600}

/* --------------------------------------------------------------------------
   The ask — one question, one answer
   -------------------------------------------------------------------------- */

.ui-ask{
  display:flex;align-items:center;gap:11px;
  padding:13px 16px;
  border:1px solid var(--ui-brand-line);
  border-radius:999px;
  background:var(--ui-bg);
  box-shadow:0 4px 14px rgba(29,27,196,.06);
}
.ui-ask>i{color:var(--ui-brand);font-size:14px;font-style:normal}
.ui-ask>span{flex:1;min-width:0;font-size:14.5px;white-space:nowrap;overflow:hidden}
.ui-ask>b{color:var(--ui-quiet);font-size:12.5px;font-weight:600}
/* The question types itself with a clip on the text, not a per-character
   script — one transition, and it degrades to the finished sentence. */
.ui-typed{display:inline-block;overflow:hidden;white-space:nowrap;vertical-align:bottom}
.ui-reply{padding:0 4px;font-size:15px;line-height:1.6;color:var(--ui-muted)}
.ui-reply b{color:var(--ui-ink);font-weight:600}


/* --------------------------------------------------------------------------
   Ring — a close filling up
   -------------------------------------------------------------------------- */

.ui-ring{position:relative;display:grid;place-items:center;width:152px;height:152px;margin-inline:auto}
.ui-ring svg{position:absolute;inset:0;width:100%;height:100%;transform:rotate(-90deg)}
/* The two arcs say the same two things the rest of the set says: indigo is
   what the agents prepared, amber is what is still a person's. Grey for the
   prepared arc was tried when green came out and it read as a heavy dark
   ring — a 9px stroke in a muted grey is a lot of ink for the part of the
   picture that is meant to be settled. Thinner, and coloured by who did it. */
.ui-ring .track{fill:none;stroke:var(--ui-sunken);stroke-width:7}
.ui-ring .done{fill:none;stroke:var(--ui-brand);stroke-width:7;stroke-linecap:round}
.ui-ring .todo{fill:none;stroke:var(--ui-warn);stroke-width:7;stroke-linecap:round}
/* How far round each arc goes is the whole point of the fragment, so it is set
   here and not in the motion block. Without this the dash offset defaults to
   zero and both arcs draw the full circle. */
.ui-ring .done,.ui-ring .todo{stroke-dashoffset:var(--o)}
.ui-ring>div{display:grid;justify-items:center;gap:2px;text-align:center}
.ui-ring strong{font-size:34px;font-weight:600;letter-spacing:-.04em;line-height:1}
.ui-ring small{color:var(--ui-quiet);font-size:12.5px}

/* --------------------------------------------------------------------------
   Strip — one tile per book
   -------------------------------------------------------------------------- */

.ui-strip{display:grid;grid-template-columns:repeat(12,1fr);gap:5px}
.ui-strip>i{
  height:26px;border-radius:5px;
  background:var(--ui-sunken);
  transition:background .45s var(--ease);
}
/* Which books are current is state, not motion. It belongs out here so a
   visitor who has asked for less motion sees the answer rather than an
   undifferentiated grey row. The motion block below only takes it away for
   long enough to give it back. */
.ui-strip>i.ok{background:var(--ui-done)}
.ui-strip>i.warn{background:var(--ui-warn)}

/* --------------------------------------------------------------------------
   Plots
   -------------------------------------------------------------------------- */

.ui-plot{position:relative;width:100%;height:106px}
.ui-plot svg{position:absolute;inset:0;width:100%;height:100%}
.ui-plot .area{fill:rgba(29,27,196,.09)}
.ui-plot .line{fill:none;stroke:var(--ui-brand);stroke-width:2.2;stroke-linecap:round}
.ui-plot .plan{fill:none;stroke:var(--ui-quiet);stroke-width:1.5;stroke-dasharray:3 4;opacity:.45}
.ui-plot .dot{fill:var(--ui-brand);stroke:#FFF;stroke-width:2.5}

/* --------------------------------------------------------------------------
   Bars — a report, three lines
   -------------------------------------------------------------------------- */

.ui-bars{display:grid;gap:14px}
.ui-bar{display:grid;grid-template-columns:minmax(0,1fr) auto;align-items:baseline;gap:12px}
.ui-bar>span{font-size:13.5px;color:var(--ui-muted)}
.ui-bar>b{font-size:15px;font-weight:600;letter-spacing:-.02em}
.ui-bar>u{
  grid-column:1/-1;text-decoration:none;
  display:block;height:6px;border-radius:3px;background:var(--ui-sunken);overflow:hidden;
}
.ui-bar>u>i{display:block;height:100%;width:var(--p);border-radius:3px;background:var(--ui-brand)}


/* ==========================================================================
   THE DEMONSTRATIONS
   ==========================================================================
   One per fragment, keyed off `.is-live` on the stage. Every end state below
   is the state a visitor should be left looking at, which is also what
   `prefers-reduced-motion` gets: this block only ever adds motion to a page
   that is already correct without it.
   ========================================================================== */

@media (prefers-reduced-motion:no-preference){

  /* Anything marked simply arrives. */
  .ui [data-in]{opacity:0;transform:translateY(7px)}
  .ui.is-live [data-in]{
    opacity:1;transform:none;
    transition:opacity .55s var(--ease),transform .6s var(--settle);
    transition-delay:calc(var(--i,0) * 110ms);
  }

  /* --- the ask: the question types, then the answer lands --------------- */
  .ui .ui-typed{max-width:0}
  .ui.is-live .ui-typed{max-width:30ch;transition:max-width 1.2s steps(30,end) .3s}
  .ui .ui-reply{opacity:0;transform:translateY(6px)}
  .ui.is-live .ui-reply{opacity:1;transform:none;transition:opacity .5s var(--ease) 1.6s,transform .55s var(--settle) 1.6s}

  /* --- coding: the account and the verdict snap onto the transaction ---- */
  .ui .ui-chips>*{opacity:0;transform:scale(.86)}
  .ui.is-live .ui-chips>*{
    opacity:1;transform:none;
    transition:opacity .4s var(--ease),transform .5s var(--settle);
    transition-delay:calc(.8s + var(--n,0) * 170ms);
  }

  /* --- matching: two records close the gap, then the seam confirms ------ */

  /* --- the close fills: what the agents did, then what is left ---------- */
  .ui:not(.is-live) .ui-ring .done,.ui:not(.is-live) .ui-ring .todo{stroke-dashoffset:1}
  .ui.is-live .ui-ring .done{transition:stroke-dashoffset 1.1s var(--ease) .35s}
  .ui.is-live .ui-ring .todo{transition:stroke-dashoffset .55s var(--ease) 1.3s}

  /* --- the books turn over, one after another --------------------------- */
  .ui:not(.is-live) .ui-strip>i{background:var(--ui-sunken)}
  .ui.is-live .ui-strip>i{transition-delay:calc(var(--n,0) * 42ms)}

  /* --- plots draw ------------------------------------------------------- */
  .ui .ui-plot .line{stroke-dasharray:1;stroke-dashoffset:1}
  .ui.is-live .ui-plot .line{stroke-dashoffset:0;transition:stroke-dashoffset 1.5s var(--ease) .3s}
  .ui .ui-plot .area,.ui .ui-plot .plan,.ui .ui-plot .dot{opacity:0}
  .ui.is-live .ui-plot .area{opacity:1;transition:opacity 1s var(--ease) .8s}
  .ui.is-live .ui-plot .plan{opacity:.45;transition:opacity .8s var(--ease) 1s}
  .ui.is-live .ui-plot .dot{opacity:1;transition:opacity .4s var(--ease) 1.6s}

  /* --- bars grow -------------------------------------------------------- */
  .ui .ui-bar>u>i{width:0}
  .ui.is-live .ui-bar>u>i{width:var(--p);transition:width 1s var(--ease);transition-delay:calc(.4s + var(--n,0) * 140ms)}

}

/* --------------------------------------------------------------------------
   Narrow — the fragment keeps its scale; only the frame tightens.
   -------------------------------------------------------------------------- */

@media (max-width:620px){
  .ui{gap:14px;padding:6px 0}
  .ui-obj{padding:14px 15px}
  .ui-rec{grid-template-columns:38px minmax(0,1fr) auto;gap:11px}
  .ui-mk{width:38px;height:38px;border-radius:11px;font-size:13px}
  .ui-t>strong{font-size:16px}
  .ui-amt{font-size:16px}
  .ui-fig>strong{font-size:36px}
  .ui-ring{width:134px;height:134px}
  .ui-ring strong{font-size:30px}
  .ui-strip{grid-template-columns:repeat(8,1fr)}
}

/* ==========================================================================
   Two floors the fragments stand on
   ==========================================================================
   Both were found by rendering the fragments into the real slot and measuring,
   not by reading the sheet.

   The caption is a <p> and its user-agent margin was never cleared. As a grid
   child of .ui that is 12.5px top and bottom charged silently to every one of
   the twelve fragment boxes on this page — 25px the layout never budgeted.

   And the reveal had no floor without JavaScript. Every [data-in] node starts
   at opacity 0 and is lifted by .is-live, which only app.js ever adds; with
   scripts off and motion allowed, all fifteen of them stayed at zero and the
   fragments rendered as empty rectangles under their captions. The reduced-
   motion path was always safe because the whole motion block sits inside
   prefers-reduced-motion:no-preference — which is exactly why the earlier
   no-JS check, run in that mode, never saw it. The root element is stamped
   data-js by an inline script before paint, so its absence is the signal.
   ========================================================================== */
.ui-note{margin:0}
html:not([data-js]) .ui [data-in]{opacity:1;transform:none}


/* ==========================================================================
   THE PRACTICE FRAGMENTS
   ==========================================================================
   Four close-ups of one product, so they are built from one set of decisions
   rather than four. What they share:

   A ROW IS TWO LINES AND A RIGHT COLUMN. Line one is the name and the moment;
   line two is the thing itself. The right edge is a column, not a place to
   hang a pill — time above, state below, aligned down the whole fragment.
   That column is what makes a list read as software instead of as a stack of
   labelled boxes.

   ONE ACCENT PER FRAGMENT. Everything else is ink, muted, quiet. The eye
   should land somewhere on purpose.

   THE PANEL IS THE APP. Each fragment sits in a white pane with a hairline
   and a soft shadow, so the surrounding paper reads as the desk it is on.
   -------------------------------------------------------------------------- */

/* A hairline and a radius. No shadow, and nothing under it: the fragment used
   to sit in a sunken window inside a card, which is three framed surfaces deep
   before any content, each with its own border and its own shading. One is
   enough, and flat reads cleaner than layered at this size. */
.ui-pane{
  border:1px solid var(--ui-line);
  border-radius:var(--ui-r);
  background:var(--ui-bg);
  overflow:hidden;
}

/* The title bar every pane carries: what you are looking at, and its scope. */
.ui-head{
  display:flex;align-items:center;gap:9px;
  padding:var(--head-pad,10px 15px);
  border-bottom:1px solid var(--ui-line-soft);
  background:var(--ui-sunken);
}
.ui-head>b{
  min-width:0;
  font-size:13px;font-weight:600;letter-spacing:-.012em;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
}
.ui-head>small{
  margin-left:auto;flex:0 0 auto;
  color:var(--ui-quiet);font-size:12.5px;font-weight:500;white-space:nowrap;
}
.ui-head>small em{color:var(--ui-ink);font-style:normal;font-weight:600}

/* --------------------------------------------------------------------------
   A row — the unit all three list fragments are made of
   -------------------------------------------------------------------------- */

.ui-rows{display:grid}
.ui-row{
  display:grid;
  grid-template-columns:var(--row-lead,34px) minmax(0,1fr) auto;
  align-items:center;
  column-gap:var(--row-gap,12px);
  padding:var(--row-pad,10px 15px);
  border-top:1px solid var(--ui-line-soft);
}
.ui-row:first-child{border-top:0}

/* the two stacked lines in the middle column */
.ui-line{display:grid;gap:2px;min-width:0}
.ui-line>b{
  font-size:14.5px;font-weight:600;letter-spacing:-.014em;line-height:1.25;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
}
.ui-line>span{
  color:var(--ui-quiet);font-size:13px;line-height:1.3;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
}
.ui-line>span i{color:var(--ui-muted);font-style:normal}
.ui-line>b em{color:var(--ui-quiet);font-weight:400;font-style:normal}

/* the right column: a moment above, a state below, both right-aligned */
.ui-end{display:grid;justify-items:end;gap:3px;text-align:right}
.ui-end>time{color:var(--ui-quiet);font-size:12.5px;line-height:1.25;white-space:nowrap}
.ui-end>span{
  color:var(--ui-quiet);font-size:12.5px;font-weight:500;line-height:1.25;white-space:nowrap;
}
.ui-end>span.is-warn{color:var(--ui-warn);font-weight:600}
.ui-end>span.is-ok{color:var(--ui-done);font-weight:600}

/* --------------------------------------------------------------------------
   Inbox — a mail list, drawn the way mail is drawn
   --------------------------------------------------------------------------
   Two lines, never three. The unread mark is a dot in a narrow gutter rather
   than a bolded row shouting at the reader, and the right column carries the
   time over the book the message was filed to — which is the one column a
   mail client does not have, and the whole reason this is a Zenday screen.
   -------------------------------------------------------------------------- */
.ui-inbox{--row-lead:8px}
.ui-inbox .ui-row{--row-pad:11px 15px}
.ui-dot{
  justify-self:center;
  width:7px;height:7px;border-radius:50%;
  background:transparent;
}
.ui-row.is-unread .ui-dot{background:var(--ui-brand)}
.ui-row.is-unread .ui-line>b{color:var(--ui-ink)}
.ui-row.is-unread .ui-line>span i{color:var(--ui-ink)}
.ui-inbox .ui-line>b{color:var(--ui-ink)}
.ui-inbox .ui-row:not(.is-unread) .ui-line>b{color:var(--ui-muted);font-weight:500}

/* --------------------------------------------------------------------------
   Checklist — what the agents worked down, and the one row they cannot
   --------------------------------------------------------------------------
   A single column of ticks carries the picture, so nothing else in the frame
   is allowed a shape: no avatars, no pills. The right column says when each
   was done, and on the last row says whose it is instead.
   -------------------------------------------------------------------------- */
.ui-check{--row-lead:20px;--row-gap:13px}
.ui-check .ui-row{--row-pad:9px 15px}
.ui-tick{
  display:grid;place-items:center;
  width:20px;height:20px;
  border:1.5px solid var(--ui-line);border-radius:6px;
  background:var(--ui-bg);color:var(--ui-bg);
}
.ui-row.is-done .ui-tick{border-color:var(--ui-done);background:var(--ui-done)}
.ui-row.is-open .ui-tick{border-style:dashed;border-color:var(--ui-warn)}
.ui-task{
  min-width:0;font-size:14.5px;line-height:1.3;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
}
.ui-row.is-done>.ui-task{color:var(--ui-muted)}
.ui-row.is-open>.ui-task{color:var(--ui-ink);font-weight:600}

/* --------------------------------------------------------------------------
   Dashboard — every book, and how far through it is
   --------------------------------------------------------------------------
   The only fragment with a measure in it. The bar and the figure are the
   shape; the initials tile on the left is square where the inbox's is a
   circle, so the two lists never read as the same list.
   -------------------------------------------------------------------------- */
.ui-board{--row-lead:34px}
.ui-board .ui-row{--row-pad:11px 15px;grid-template-columns:34px minmax(0,1fr) var(--meter,116px) auto}
.ui-tile{
  display:grid;place-items:center;
  width:34px;height:34px;
  border:1px solid var(--ui-line);border-radius:9px;
  background:var(--ui-sunken);
  color:var(--ui-muted);
  font-size:11.5px;font-weight:700;font-style:normal;letter-spacing:.02em;
}
.ui-meter{height:6px;border-radius:3px;background:var(--ui-sunken);overflow:hidden}
.ui-meter>i{display:block;height:100%;width:var(--p);border-radius:3px;background:var(--ui-brand)}
/* One bar material, three lengths. Colouring the bars by state was tried and
   looked worse than it read: the amber is a TEXT colour, dark and warm, and at
   6px of solid fill it goes to mud against the track. The lengths carry the
   quantity; the figure beside them carries the state. One accent per
   fragment, which is the rule the other three keep. */
.ui-pct{
  min-width:42px;
  font-size:14.5px;font-weight:600;letter-spacing:-.022em;text-align:right;
  font-variant-numeric:tabular-nums;
}
.ui-row.is-behind .ui-pct{color:var(--ui-warn)}

/* --------------------------------------------------------------------------
   Files — the one fragment that is not a list
   -------------------------------------------------------------------------- */
.ui-files{
  display:grid;grid-template-columns:repeat(2,minmax(0,1fr));
  gap:var(--file-gap,8px);
  padding:var(--files-pad,12px);
}
.ui-file{
  display:grid;grid-template-columns:auto minmax(0,1fr);align-items:center;
  gap:var(--file-gap-x,10px);
  padding:var(--file-pad,9px 11px);
  border:1px solid var(--ui-line-soft);
  border-radius:var(--ui-r-sm);
  background:var(--ui-bg);
}
.ui-page{width:var(--page-w,24px);height:auto;flex:0 0 auto}
.ui-page .sheet{fill:var(--ui-brand-soft);stroke:var(--ui-brand-line);stroke-width:1.1}
.ui-page .fold{fill:var(--ui-brand-line)}
.ui-page .rule{fill:none;stroke:var(--ui-brand-deep);stroke-width:1.6;stroke-linecap:round;opacity:.32}
.ui-file-t{display:grid;gap:2px;min-width:0}
.ui-file-t>b{
  font-size:13.5px;font-weight:600;letter-spacing:-.012em;line-height:1.2;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
}
.ui-file-t>small{
  color:var(--ui-quiet);font-size:12.5px;line-height:1.2;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
}
.ui-file-t>small b{color:var(--ui-muted);font-weight:600}

/* --------------------------------------------------------------------------
   Motion — additive only
   --------------------------------------------------------------------------
   Declared only while .is-live is on the stage, so a page with no JavaScript
   has no start state to be stranded in: it simply renders finished.
   -------------------------------------------------------------------------- */
@keyframes ui-row-in{from{opacity:0;transform:translateY(6px)}}
@keyframes ui-tick-in{from{border-color:var(--ui-line);background:var(--ui-bg)}}
@keyframes ui-meter-in{from{width:0}}
@keyframes ui-fade-in{from{opacity:0}}

@media (prefers-reduced-motion:no-preference){
  .ui.is-live .ui-row,.ui.is-live .ui-file{
    animation:ui-row-in .5s var(--ease) backwards;
    animation-delay:calc(.15s + var(--i,0) * 95ms);
  }
  .ui.is-live .ui-row.is-done .ui-tick{
    animation:ui-tick-in .3s var(--ease) backwards;
    animation-delay:calc(.45s + var(--i,0) * 140ms);
  }
  .ui.is-live .ui-meter>i{
    animation:ui-meter-in .9s var(--ease) backwards;
    animation-delay:calc(.4s + var(--i,0) * 110ms);
  }
  .ui.is-live .ui-head>small,.ui.is-live .ui-end{
    animation:ui-fade-in .5s var(--ease) backwards .7s;
  }
}

@media (max-width:620px){
  .ui-head{--head-pad:9px 12px}
  .ui-row{--row-pad:9px 12px;--row-gap:10px}
  .ui-inbox .ui-row{--row-pad:10px 12px}
  /* the meter goes; the row must lose its track too, or the figure lands in a
     zero-width column and overflows it */
  .ui-board .ui-row{--row-pad:10px 12px;column-gap:10px;
    grid-template-columns:var(--row-lead,34px) minmax(0,1fr) auto}
  .ui-board .ui-meter{display:none}
  .ui-files{--files-pad:10px;--file-gap:7px}
  .ui-file{--file-pad:8px 9px;--file-gap-x:8px;--page-w:21px}
  .ui-line>b{font-size:14px}
  .ui-task{font-size:14px}
}
@media (max-width:380px){
  .ui-file{--page-w:18px;--file-gap-x:6px;--file-pad:8px}
  .ui-tile{width:30px;height:30px}
  .ui-board{--row-lead:30px}
  .ui-board .ui-row{grid-template-columns:30px minmax(0,1fr) auto}
}

/* ==========================================================================
   Mockups that perform the change, not mockups of the finished state
   ==========================================================================
   Every product fragment showed an outcome: two numbers already equal with a
   tick beside them, a transaction already coded, a ring already at 92%. Cover
   the copy and none of them said what the feature does — the words carried the
   meaning and the picture decorated it.

   So each one now does the thing. A bank line arrives as the bank wrote it and
   resolves. Two sides of a reconciliation find each other and the one that
   cannot is written up. The motion is the explanation, and it replays on hover
   for anyone who scrolled past the first run.
   -------------------------------------------------------------------------- */

/* --- a row that resolves from one state into another ---------------------- */
.ui-swap{display:grid}
.ui-swap>*{grid-area:1/1}
.ui-raw,.ui-done{transition:opacity .5s var(--ease),transform .6s var(--settle)}

.ui-raw{
  display:grid;grid-template-columns:20px minmax(0,1fr) auto;
  align-items:center;gap:12px;
  padding:11px 15px;
}
.ui-raw>code{
  min-width:0;
  font:500 12.5px/1.3 var(--font-mono,ui-monospace,SFMono-Regular,Menlo,monospace);
  color:var(--ui-quiet);letter-spacing:.01em;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
}
.ui-raw>b{font-size:14px;font-weight:600;color:var(--ui-muted);white-space:nowrap}
.ui-pip{
  justify-self:center;
  width:9px;height:9px;border-radius:50%;
  border:1.5px dashed var(--ui-line);
}
.ui-done{opacity:0;transform:translateY(5px)}
.ui.is-live .ui-swap .ui-raw{opacity:0;transform:translateY(-5px);transition-delay:1.15s}
.ui.is-live .ui-swap .ui-done{opacity:1;transform:none;transition-delay:1.25s}

/* --- two sides finding each other ---------------------------------------- */
.ui-match{display:grid;gap:7px;padding:11px 14px}
.ui-mrow{
  display:grid;grid-template-columns:minmax(0,1fr) 30px minmax(0,1fr);
  align-items:center;gap:0;
}
.ui-side{
  display:flex;align-items:baseline;justify-content:space-between;gap:8px;
  padding:8px 11px;
  border:1px solid var(--ui-line-soft);border-radius:8px;
  background:var(--ui-bg);
  font-size:13px;
  transition:transform .75s var(--settle),border-color .5s var(--ease);
}
.ui-side small{color:var(--ui-quiet);font-size:11.5px;font-weight:600;letter-spacing:.04em;text-transform:uppercase}
.ui-side b{font-size:13.5px;font-weight:600;letter-spacing:-.015em;white-space:nowrap}
.ui-join{
  display:grid;place-items:center;
  color:var(--ui-quiet);font-size:11px;
  opacity:0;transition:opacity .4s var(--ease);
}
.ui-join>i{
  display:grid;place-items:center;width:19px;height:19px;border-radius:50%;
  background:var(--ui-done-soft);color:var(--ui-done);
}
/* apart, then together — the gap closes and the tick lands */
.ui-mrow.is-match .ui-side:first-child{transform:translateX(-9px)}
.ui-mrow.is-match .ui-side:last-child{transform:translateX(9px)}
.ui.is-live .ui-mrow.is-match .ui-side{transform:none;transition-delay:calc(.35s + var(--i,0)*.32s)}
.ui.is-live .ui-mrow.is-match .ui-join{opacity:1;transition-delay:calc(1s + var(--i,0)*.32s)}
.ui-mrow.is-open .ui-side{border-color:var(--ui-warn-line,rgba(138,90,30,.42))}
.ui-mrow.is-open .ui-join{color:var(--ui-warn);opacity:1}
.ui-mrow.is-open .ui-side:last-child{background:var(--ui-warn-soft);color:var(--ui-warn)}
.ui-mrow.is-open .ui-side:last-child b{color:var(--ui-warn)}

/* the one that could not be matched, written up */
.ui-writeup{
  display:flex;align-items:flex-start;gap:8px;
  margin:9px 14px 0;padding-top:9px;
  border-top:1px solid var(--ui-line-soft);
  color:var(--ui-quiet);font-size:12.5px;line-height:1.4;
  opacity:0;transition:opacity .5s var(--ease);
}
.ui-writeup b{color:var(--ui-muted);font-weight:600}
.ui.is-live .ui-writeup{opacity:1;transition-delay:2.1s}

/* hover replays the whole explanation for anyone who scrolled past it */
.library-card:hover .ui.is-live .ui-swap .ui-raw,
.library-card:hover .ui.is-live .ui-mrow.is-match .ui-side{transition-duration:0s;transition-delay:0s}

@media (prefers-reduced-motion:reduce){
  .ui-raw{display:none}
  .ui-done{opacity:1;transform:none}
  .ui-mrow.is-match .ui-side{transform:none}
  .ui-join,.ui-writeup{opacity:1}
}
html:not([data-js]) .ui-raw{display:none}
html:not([data-js]) .ui-done,
html:not([data-js]) .ui-join,
html:not([data-js]) .ui-writeup{opacity:1;transform:none}

/* --------------------------------------------------------------------------
   A table, with column headers
   --------------------------------------------------------------------------
   The thing that was missing. Every fragment here was a record, a chip, a
   figure or a ring — interface furniture — and none of it was the object an
   accountant actually looks at all day, which is a table with named columns
   and right-aligned money. A header row saying Date · Description · Account ·
   Amount does more to say "this is accounting software" than any amount of
   styling on a card, and it does it before a word is read.

   Wide and short: four or five rows filling the column, not a square panel.
   Hairlines between rows, nothing around them.
   -------------------------------------------------------------------------- */
/* Fixed layout, because an auto table cannot shrink below the sum of its
   nowrap cells — which is why these overflowed their card at nine widths out
   of nine. Fixed lets width:100% actually mean it and pushes the give into
   the one column that can ellipsis.

   And the columns that drop are chosen by the CARD's width, not the window's:
   the same card is 599px wide in a two-up row and 403px in a three-up at the
   same viewport, so a media query was always going to be wrong for one of
   them. A container query asks the only question that matters. */
.ui-pane{container-type:inline-size}
.ui-tbl{width:100%;table-layout:fixed;border-collapse:collapse;font-variant-numeric:tabular-nums}
.ui-tbl th.num,.ui-tbl td.num{width:var(--num-w,122px)}
/* The account code rides inside the name cell rather than holding a column
   of its own: four columns need about 420px and these panes top out at 390. */
.ui-tbl .code{
  margin-right:6px;font-style:normal;
  color:var(--ui-quiet);
  font:500 11px/1 var(--font-mono,ui-monospace,SFMono-Regular,Menlo,monospace);
}
@container (max-width:430px){
  .ui-tbl .hide-sm{display:none}
  .ui-tbl{--num-w:112px;--tbl-pad:10px 12px;--tbl-hpad:8px 12px}
}
@container (max-width:330px){
  .ui-tbl .hide-xs{display:none}
  .ui-tbl{--num-w:96px;--tbl-pad:9px 9px;--tbl-hpad:8px 9px}
  .ui-tbl td{font-size:12.75px}
  .ui-tbl th{font-size:11.5px}
}
/* Measured: these panes run 216px to 390px, not the ~440 the layout implied.
   Below 290 the account-code column goes too — there is not room for a code,
   a name and two money columns. */
@container (max-width:290px){
  .ui-tbl .code{display:none}
  .ui-tbl{--num-w:100px;--tbl-pad:9px 6px;--tbl-hpad:8px 6px}
  .ui-tbl td,.ui-tbl tfoot td{font-size:12.25px}
}
.ui-tbl th{
  padding:var(--tbl-hpad,9px 14px);
  border-bottom:1px solid var(--ui-line);
  background:var(--ui-sunken);
  color:var(--ui-quiet);
  font-size:12px;font-weight:600;letter-spacing:.01em;text-align:left;
  white-space:nowrap;
}
.ui-tbl td{
  padding:var(--tbl-pad,10px 14px);
  border-top:1px solid var(--ui-line-soft);
  font-size:13.5px;line-height:1.3;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
}
.ui-tbl tbody tr:first-child td{border-top:0}
.ui-tbl .num{text-align:right;font-weight:600;letter-spacing:-.015em}
.ui-tbl th.num{text-align:right}
.ui-tbl .dim{color:var(--ui-quiet);font-size:12.5px}
.ui-tbl .key{color:var(--ui-ink);font-weight:600}
/* the total line closes the table off the way a statement does */
.ui-tbl tfoot td{
  border-top:1px solid var(--ui-line);
  padding-top:11px;
  font-size:13.5px;font-weight:600;
}
.ui-tbl tfoot .lbl{color:var(--ui-quiet);font-weight:500}
/* cents a shade back from dollars, so the eye reads the magnitude first */
.ui-cents{color:var(--ui-quiet);font-weight:500}
.ui-tbl tr.is-you td{background:var(--ui-warn-soft)}
.ui-tbl tr.is-you .num{color:var(--ui-warn)}

/* the strip under a table: what the agents did, and what is left */
.ui-foot{
  display:flex;align-items:center;gap:10px;
  padding:10px 14px;
  border-top:1px solid var(--ui-line);
  color:var(--ui-quiet);font-size:12.5px;
}
.ui-foot>b{color:var(--ui-muted);font-weight:600}
.ui-foot>span{margin-left:auto;white-space:nowrap}
.ui-foot .is-warn{color:var(--ui-warn);font-weight:600}

@media (max-width:620px){
  .ui-tbl{--tbl-pad:9px 11px;--tbl-hpad:8px 11px}
  .ui-foot{padding:9px 11px}
}

/* --------------------------------------------------------------------------
   A time column, and a row that resolves inside a table cell
   --------------------------------------------------------------------------
   The agents work overnight, so the clock is the product: a row that says
   02:14 next to what was done at 02:14 explains autonomy in a way no amount
   of copy about autonomy does. And the bank feed's raw memo resolves in
   place, inside the cell, so the table never reflows.
   -------------------------------------------------------------------------- */
.ui-tbl th.t,.ui-tbl td.t{
  width:var(--t-w,80px);
  color:var(--ui-quiet);
  font:500 12px/1.3 var(--font-mono,ui-monospace,SFMono-Regular,Menlo,monospace);
}
.ui-tbl td.who{color:var(--ui-quiet);font-size:12.5px}
.ui-tbl .tag{
  display:inline-block;padding:1px 7px;border-radius:5px;
  background:var(--ui-brand-soft);color:var(--ui-brand-deep);
  font-size:11.5px;font-weight:600;white-space:nowrap;
}
.ui-tbl .tag.is-warn{background:var(--ui-warn-soft);color:var(--ui-warn)}
.ui-tbl .tag.is-plain{background:var(--ui-sunken);color:var(--ui-quiet)}

/* the description cell holds both states of one transaction */
.ui-cell-swap{display:grid}
.ui-cell-swap>*{grid-area:1/1;min-width:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.ui-cell-swap>code{
  font:500 12px/1.4 var(--font-mono,ui-monospace,SFMono-Regular,Menlo,monospace);
  color:var(--ui-quiet);
}
.ui-cell-swap>span{opacity:0}
@media (prefers-reduced-motion:no-preference){
  .ui.is-live .ui-cell-swap>code{opacity:0;transition:opacity .45s var(--ease) 1.1s}
  .ui.is-live .ui-cell-swap>span{opacity:1;transition:opacity .5s var(--ease) 1.2s}
}
html:not([data-js]) .ui-cell-swap>code,
.ui-cell-swap>span{}
@media (prefers-reduced-motion:reduce){
  .ui-cell-swap>code{opacity:0}
  .ui-cell-swap>span{opacity:1}
}
html:not([data-js]) .ui-cell-swap>code{opacity:0}
html:not([data-js]) .ui-cell-swap>span{opacity:1}

/* the ask, given the chrome of a thing you type into */
.ui-ask-body{padding:14px 15px 4px;color:var(--ui-muted);font-size:14px;line-height:1.6}
.ui-ask-body b{color:var(--ui-ink);font-weight:600}

@container (max-width:430px){ .ui-tbl{--t-w:72px} }
@container (max-width:330px){ .ui-tbl{--t-w:64px} .ui-tbl th.t,.ui-tbl td.t{font-size:11px} }

/* ==========================================================================
   Air, and a white sheet
   ==========================================================================
   Put side by side at the same scale, the difference was not structure — by
   this point both are hairline grids full of tables — it was six things, and
   all six were mine.

   1. THE PANEL WAS GREY ON GREY. Mine sat at --paper over a --paper-soft
      ground: three values of separation, so it read as a muddy slab. Theirs
      is a white sheet on a near-white page, separated by the hairline alone.
      Value does the work, not fill.

   2. THE HEADER WAS A FILLED STRIP. A grey bar across the top of every
      fragment, and another grey bar for every table's column heads — two
      bands of fill before a single number. Theirs are plain text with a rule
      under them.

   3. THE ROWS WERE CRAMPED. 10px of vertical padding against their 16. At
      13.5px type that is a row barely taller than its text, which is what
      makes a table feel like a spreadsheet instead of a document.

   4. THE TYPE WAS A SHADE LARGE for the space it sat in, so nothing had room
      around it.

   5. EVERY CAPTION CARRIED A BADGE — a sparkle in a bordered circle, twelve
      times down the page. Theirs is small grey text and nothing else.

   6. NO LABEL ABOVE THE SHEET. Theirs names the thing you are looking at in
      small grey type before you look at it.
   -------------------------------------------------------------------------- */

/* 1 — a white sheet, held by its edge */
.ui-pane{
  background:#FFFEFC;
  border-color:rgba(23,22,28,.10);
}

/* 2 — no filled bands */
.ui-head{
  background:none;
  padding:var(--head-pad,13px 16px 12px);
  border-bottom:1px solid var(--ui-line);
}
.ui-head>b{font-size:13.5px;font-weight:600}
.ui-tbl th{
  background:none;
  padding:var(--tbl-hpad,4px 16px 9px);
  border-bottom:1px solid var(--ui-line);
  font-size:11.5px;font-weight:500;letter-spacing:.04em;text-transform:uppercase;
  color:var(--ui-quiet);
}

/* 3 + 4 — air around the type */
.ui-tbl{--tbl-pad:15px 16px}
.ui-tbl td{font-size:13px;line-height:1.35}
.ui-tbl tfoot td{padding-top:15px;font-size:13px}
.ui-foot{padding:14px 16px;font-size:12px}
.ui-ask-body{padding:16px 16px 6px;font-size:13.5px}
.ui-ask{margin:16px 16px 0 !important;padding:12px 15px}
.ui-ask>span{font-size:13.5px}

/* 5 — the caption is a caption */
.ui-note{
  gap:0;
  padding:0 2px;
  color:var(--ui-quiet);
  font-size:12px;
  line-height:1.5;
}
.ui-note>i{display:none}
.ui-note.needs-you>span{color:var(--ui-warn)}
.ui-note.needs-you b{color:var(--ui-warn)}

/* 6 — and a name above it */
.ui-label{
  margin:0 0 10px;
  padding:0 2px;
  color:var(--ui-quiet);
  font-size:12px;
  line-height:1.4;
}
.ui{gap:12px}

@container (max-width:330px){
  .ui-tbl{--tbl-pad:13px 12px;--tbl-hpad:4px 12px 8px}
  .ui-head{--head-pad:12px 12px 11px}
  .ui-foot{padding:12px}
}

/* The date column does not need the full row padding on both sides — it is a
   fixed narrow column, and the 16px gutters the wider rows gained were coming
   straight out of "12 Aug". */
.ui-tbl th.t,.ui-tbl td.t{padding-right:8px}
@container (max-width:430px){ .ui-tbl{--t-w:76px} }
@container (max-width:330px){ .ui-tbl{--t-w:70px} }

/* --------------------------------------------------------------------------
   An ownership tree
   --------------------------------------------------------------------------
   Multi-entity is not a list of names. It is a parent, the companies it owns,
   what share of each, what currency each keeps its books in, and one set of
   consolidated figures over the top. A flat table of book names shows none of
   that — it shows a table. So this draws the structure: the parent at full
   weight, the children indented off a rule with their ownership and currency
   on the line, and three figures under each so the consolidation is visible
   rather than asserted.
   -------------------------------------------------------------------------- */
.ui-tree{padding:4px 16px 2px}
.ui-ent{padding:13px 0 12px;border-top:1px solid var(--ui-line-soft)}
.ui-ent:first-child{border-top:0}

/* a child hangs off the parent on a rule, the way an org chart does */
.ui-ent.is-child{position:relative;padding-left:var(--ent-in,20px)}
.ui-ent.is-child:before{
  content:"";position:absolute;left:5px;top:0;bottom:0;
  border-left:1px solid var(--ui-line);
}
.ui-ent.is-child:after{
  content:"";position:absolute;left:5px;top:24px;width:9px;
  border-top:1px solid var(--ui-line);
}
.ui-ent.is-child:last-child:before{bottom:auto;height:24px}

.ui-ent-hd{display:flex;align-items:baseline;gap:9px;min-width:0}
.ui-ent-hd>b{
  min-width:0;font-size:13.5px;font-weight:600;letter-spacing:-.014em;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
}
.ui-ent-hd>small{flex:0 0 auto;color:var(--ui-quiet);font-size:12px}
.ui-ent-hd>em{
  flex:0 0 auto;margin-left:auto;
  padding:1px 7px;border-radius:5px;
  background:var(--ui-sunken);color:var(--ui-quiet);
  font-size:11px;font-weight:600;font-style:normal;white-space:nowrap;
}
.ui-ent-hd>em.is-parent{background:var(--ui-brand-soft);color:var(--ui-brand-deep)}

/* three figures under each entity — the consolidation, shown */
.ui-figs{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:0 12px;margin-top:9px}
.ui-figs>span{display:grid;gap:2px;min-width:0}
.ui-figs small{color:var(--ui-quiet);font-size:11px;letter-spacing:.03em;text-transform:uppercase}
.ui-figs b{
  font-size:13.5px;font-weight:600;letter-spacing:-.018em;
  font-variant-numeric:tabular-nums;white-space:nowrap;
}
.ui-ent.is-child .ui-figs b{font-size:12.5px;font-weight:500;color:var(--ui-muted)}

@container (max-width:330px){
  .ui-tree{padding:4px 12px 2px}
  .ui-ent{--ent-in:17px}
  .ui-figs{gap:0 9px}
  .ui-figs b{font-size:12.5px}
  .ui-ent.is-child .ui-figs b{font-size:12px}
}

/* the indented adjusting lines of a reconciliation */
.ui-tbl td.sub{padding-left:30px;color:var(--ui-quiet);font-size:12.5px}
.ui-tbl tfoot td.lbl{color:var(--ui-ink);font-weight:600}

/* --------------------------------------------------------------------------
   The Zenday inbox
   --------------------------------------------------------------------------
   Recreated from the product file (Firm View / Email -> My Email), not from
   an idea of what an inbox looks like. The values below are the ones the
   design actually uses:

     row            1040x56, and — the thing that makes it read clean —
                    no rule between rows at all, only a hover fill
     unread dot     6px  #2e90fa
     sender         600/14  #181d27
     client badge   #fafafa on #e9eaeb, r4, 500/12 #414651
     reminder       #fffaeb on #fedf89, r6, 500/12 #b54708, icon #dc6803
     comment        #eff8ff on #b2ddff, r6, 500/12 #175cd3, icon #2e90fa
     subject        500/14  #181d27   snippet 400/14 #414651, same line
     date           400/14  #181d27   clip icon #a4a7ae
     day header     600/12  #717680

   Sizes are stepped down for the card, proportions and hierarchy are not.
   -------------------------------------------------------------------------- */
.zi{
  --zi-ink:#181d27; --zi-body:#414651; --zi-quiet:#717680; --zi-icon:#a4a7ae;
  --zi-line:#e9eaeb; --zi-surface:#fafafa;
  --zi-blue:#2e90fa; --zi-blue-bg:#eff8ff; --zi-blue-bd:#b2ddff; --zi-blue-tx:#175cd3;
  --zi-amb-bg:#fffaeb; --zi-amb-bd:#fedf89; --zi-amb-tx:#b54708; --zi-amb-ic:#dc6803;
  container-type:inline-size;
}

/* the tab strip, with its counts and the single active underline */
.zi-tabs{
  display:flex; align-items:center; gap:20px;
  padding:0 14px; border-bottom:1px solid var(--zi-line);
}
.zi-tabs>span{
  padding:11px 0 10px; border-bottom:1px solid transparent;
  color:var(--zi-quiet); font-size:12.5px; font-weight:600; white-space:nowrap;
}
.zi-tabs>span.is-on{color:var(--zi-ink); border-bottom-color:var(--zi-ink)}
.zi-tabs em{margin-left:5px; color:var(--zi-quiet); font-size:11px; font-weight:400; font-style:normal}
.zi-tabs>span.is-on em{color:#535862}

.zi-list{padding:6px 0 8px}
.zi-day{
  margin:8px 0 2px; padding:0 14px;
  color:var(--zi-quiet); font-size:11px; font-weight:600; letter-spacing:.005em;
}

/* the row.  no border-bottom — that is the whole trick */
.zi-row{
  display:flex; align-items:center; gap:9px;
  padding:9px 14px; min-width:0;
  color:var(--zi-body); font-size:12.5px; line-height:1.35;
}
.zi-dot{
  flex:0 0 auto; width:5px; height:5px; border-radius:50%;
  background:transparent;
}
.zi-row.is-unread .zi-dot{background:var(--zi-blue)}
.zi-from{
  flex:0 0 auto; width:var(--from-w,92px);
  color:var(--zi-ink); font-size:12.5px; font-weight:600; letter-spacing:-.006em;
  white-space:nowrap; overflow:hidden; text-overflow:ellipsis;
}
.zi-tags{flex:0 0 auto; display:flex; align-items:center; gap:5px}
.zi-tag{
  display:inline-flex; align-items:center; gap:3px;
  padding:1px 6px; border:1px solid var(--zi-line); border-radius:4px;
  background:var(--zi-surface); color:var(--zi-body);
  font-size:10.5px; font-weight:500; white-space:nowrap;
}
.zi-tag.is-amber{border-radius:6px; border-color:var(--zi-amb-bd); background:var(--zi-amb-bg); color:var(--zi-amb-tx)}
.zi-tag.is-amber svg{color:var(--zi-amb-ic)}
.zi-tag.is-blue{border-radius:6px; border-color:var(--zi-blue-bd); background:var(--zi-blue-bg); color:var(--zi-blue-tx)}
.zi-tag.is-blue svg{color:var(--zi-blue)}

/* subject and snippet share one line, the snippet taking whatever is left */
.zi-sub{flex:1 1 auto; min-width:0; white-space:nowrap; overflow:hidden; text-overflow:ellipsis}
.zi-sub>b{color:var(--zi-ink); font-weight:500}
.zi-sub>span{margin-left:7px; color:var(--zi-body)}

.zi-meta{
  flex:0 0 auto; display:flex; align-items:center; gap:5px;
  color:var(--zi-ink); font-size:11.5px; font-variant-numeric:tabular-nums;
}
.zi-clip{display:inline-flex; color:var(--zi-icon)}

/* what the card drops as it narrows, in the order the design would drop it */
@container (max-width:520px){ .zi-sub>span{display:none} }
@container (max-width:430px){
  .zi-tags{display:none}
  .zi-tabs>span.hide-sm{display:none}
  .zi-from{--from-w:80px}
}
@container (max-width:330px){
  .zi-row{gap:7px; padding:8px 11px}
  .zi-day,.zi-tabs{padding-left:11px}
  .zi-from{--from-w:68px}
  .zi-meta{font-size:11px}
}

/* The product's own sheet: pure white on its own hairline, not the warm
   paper the rest of the page uses. This fragment is a screenshot, so it
   should look like the screen. */
.ui-pane.zi{background:#fff; border-color:var(--zi-line)}

/* --------------------------------------------------------------------------
   The Zenday client portal
   --------------------------------------------------------------------------
   Read from Client Portal / Home. This is the client's own view, not the
   firm's — which is what I had wrong: a list of books and their close
   progress is the firm side. The client sees their numbers.

     page              #fafafa, cards white on #f5f5f5 at r12
     section label     600/12 UPPER #535862
     metric label      500/12 UPPER #a4a7ae
     metric value      600/24 #181d27
     delta %           600/12 #181d27  — ink, NOT green or red
     "From last month" 400/12 #a4a7ae
     delta tile        32x32 r4, #f3fee7 with a #4ca30d arrow,
                       or #fef3f2 with a #d92d20 one
     action count      #d92d20 on 400/12 #535862 label

   The restraint in that fifth line is the whole character of the screen:
   only the little tile carries colour, so the numbers stay black and the
   page never lights up like a dashboard demo.
   -------------------------------------------------------------------------- */
.zp{
  --zp-ink:#181d27; --zp-mid:#535862; --zp-quiet:#a4a7ae;
  --zp-line:#f5f5f5; --zp-page:#fafafa;
  --zp-up-bg:#f3fee7; --zp-up:#4ca30d;
  --zp-dn-bg:#fef3f2; --zp-dn:#d92d20;
  display:grid; gap:9px; padding:10px;
  container-type:inline-size;
}
.ui-pane.zp{background:var(--zp-page); border-color:var(--zp-line)}

.zp-card{
  padding:12px 13px 13px;
  border:1px solid var(--zp-line); border-radius:12px; background:#fff;
}
.zp-cap{
  display:flex; align-items:baseline; justify-content:space-between; gap:10px;
  margin:0 0 11px;
  color:var(--zp-mid); font-size:10px; font-weight:600;
  letter-spacing:.06em; text-transform:uppercase;
}
.zp-cap em{color:var(--zp-quiet); font-style:normal; font-weight:500}

/* three metrics, divided by hairlines the way the screen divides them */
.zp-mets{display:grid; grid-template-columns:repeat(3,minmax(0,1fr))}
.zp-met{padding:0 12px; min-width:0; border-left:1px solid var(--zp-line)}
.zp-met:first-child{padding-left:0; border-left:0}
.zp-met:last-child{padding-right:0}
.zp-k{
  margin:0 0 5px; color:var(--zp-quiet);
  font-size:9.5px; font-weight:500; letter-spacing:.055em; text-transform:uppercase;
  white-space:nowrap; overflow:hidden; text-overflow:ellipsis;
}
.zp-v{
  margin:0; color:var(--zp-ink);
  font-size:clamp(15px,1.35cqi + 10px,19px); font-weight:600; letter-spacing:-.026em;
  font-variant-numeric:tabular-nums; white-space:nowrap;
}
.zp-v span{color:var(--zp-quiet)}
.zp-d{display:flex; align-items:center; gap:6px; margin:8px 0 0; min-width:0}
.zp-d i{
  flex:0 0 auto; display:grid; place-items:center;
  width:21px; height:21px; border-radius:4px;
}
.zp-d i.is-up{background:var(--zp-up-bg); color:var(--zp-up)}
/* colour says good or bad; the arrow says which way the number went, so a
   rise in expenses is a red tile with the arrow still pointing up */
.zp-d i.is-bad{background:var(--zp-dn-bg); color:var(--zp-dn)}
.zp-d i.is-fall svg{transform:rotate(180deg)}
.zp-d b{color:var(--zp-ink); font-size:10.5px; font-weight:600; white-space:nowrap}
.zp-d span{
  color:var(--zp-quiet); font-size:10px; font-weight:400;
  white-space:nowrap; overflow:hidden; text-overflow:ellipsis;
}

/* --------------------------------------------------------------------------
   Profit & loss, as Insights draws it
   --------------------------------------------------------------------------
   A waterfall against a dashed zero line: what came in runs left of the
   line in the brand indigo, what went out runs right of it in grey, and
   each bar carries its figure on the outside. Row labels 400/10 #535862,
   values 400/10 #181d27, bars #444ce7 / #a4bcfd / #d5d7da — the palette
   the charts on that page actually use.
   -------------------------------------------------------------------------- */
.zp-wf{display:grid; gap:7px}
.zp-wrow{display:flex; align-items:center; gap:9px; min-width:0}
.zp-wlab{
  flex:0 0 auto; width:var(--wlab,96px);
  color:var(--zp-mid); font-size:10px;
  white-space:nowrap; overflow:hidden; text-overflow:ellipsis;
}
/* the track is split by the zero line; bars grow outward from it */
.zp-wtrack{
  position:relative; flex:1 1 auto; min-width:0; height:15px;
}
.zp-wtrack:before{
  content:""; position:absolute; left:var(--zero,58%); top:-2px; bottom:-2px;
  border-left:1px dashed #d5d7da;
}
.zp-wtrack>i{
  position:absolute; top:1px; bottom:1px;
  width:calc(var(--w) * var(--side,.58));
}
.zp-wtrack>i.is-pos{right:calc(100% - var(--zero,58%)); background:var(--zp-bar,#444ce7)}
.zp-wtrack>i.is-neg{left:var(--zero,58%); --side:.42; background:#d5d7da}
.zp-wrow:nth-child(3) .zp-wtrack>i.is-pos{background:#a4bcfd}
.zp-wrow:nth-child(5) .zp-wtrack>i.is-pos{background:#c7d7fe}
/* The figure sits just past the bar's outer end — right of the zero line
   for what came in, right of the bar itself for what went out. That is
   what makes each row read as a step rather than a table cell. */
.zp-wval{
  position:absolute; top:50%; transform:translateY(-50%);
  left:calc(var(--zero,58%) + 7px);
  color:var(--zp-ink); font-size:10px; font-variant-numeric:tabular-nums;
  white-space:nowrap;
}
.zp-wrow.is-out .zp-wval{left:calc(var(--zero,58%) + (var(--w) * .42) + 7px)}

@container (max-width:430px){
  .zp-mets{grid-template-columns:repeat(2,minmax(0,1fr))}
  .zp-met.hide-sm{display:none}
  .zp-d span{display:none}
  .zp-wf{--wlab:78px; --wval:44px}
}
@container (max-width:330px){
  .zp{padding:8px; gap:8px}
  .zp-card{padding:10px 11px 11px}
  .zp-met{padding:0 9px}
  .zp-wf{--wlab:66px; --wval:40px}
  .zp-wrow{gap:7px}
}

/* --------------------------------------------------------------------------
   Tasks, as the table view draws them
   --------------------------------------------------------------------------
   From Firm View / Tasks / Table. Work is grouped under the client it
   belongs to, and each task carries a row of stage pills — 20x6 at r16,
   one per stage, coloured while reached and #e9eaeb while not — with the
   count beside it. That pill track is the signature of the screen and the
   reason it reads as a checklist rather than a to-do list.

     task name   600/14 #414651      month chip 600/12 #535862
     count       600/12 #535862      client group 600/12 #717680
     stages      #b692f6 #f7b27a #f97066 #8098f9 #3ccb7f, unreached #e9eaeb
   -------------------------------------------------------------------------- */
.zt{
  --zt-ink:#181d27; --zt-name:#414651; --zt-mid:#535862; --zt-quiet:#717680;
  --zt-line:#e9eaeb; --zt-page:#fafafa;
  container-type:inline-size;
}
.ui-pane.zt{background:#fff; border-color:var(--zt-line)}
.zt-bar{
  display:flex; align-items:center; gap:7px;
  padding:10px 13px; border-bottom:1px solid var(--zt-line);
}
.zt-bar>b{color:var(--zt-ink); font-size:12.5px; font-weight:500}
.zt-bar>em{color:var(--zt-quiet); font-size:12.5px; font-style:normal}
.zt-tog{
  display:flex; margin-left:auto; padding:2px;
  border:1px solid var(--zt-line); border-radius:7px; background:var(--zt-page);
}
.zt-tog i{
  padding:2px 8px; border:1px solid transparent; border-radius:5px;
  color:var(--zt-quiet); font-size:10.5px; font-weight:500; font-style:normal;
}
.zt-tog i.is-on{border-color:var(--zt-line); background:#fff; color:var(--zt-mid)}

.zt-list{padding:8px 10px 10px; background:var(--zt-page)}
.zt-grp{
  display:flex; align-items:center; gap:6px; margin:9px 0 6px; padding:0 3px;
  color:var(--zt-quiet); font-size:10.5px; font-weight:600;
}
.zt-grp:first-child{margin-top:2px}
.zt-av{width:11px; height:11px; border-radius:50%; background:var(--zt-quiet)}
.zt-av.is-ol{background:#e0349a} .zt-av.is-nw{background:#0f9d58}

/* each task is its own white card on the grey page, the way the screen has it */
.zt-row{
  display:flex; align-items:center; gap:8px;
  margin-bottom:5px; padding:8px 11px;
  border:1px solid var(--zt-line); border-radius:8px; background:#fff;
  min-width:0;
}
.zt-name{
  flex:1 1 auto; min-width:0;
  color:var(--zt-name); font-size:12px; font-weight:600;
  white-space:nowrap; overflow:hidden; text-overflow:ellipsis;
}
.zt-mo{
  flex:0 0 auto; padding:1px 6px;
  border:1px solid var(--zt-line); border-radius:5px;
  color:var(--zt-mid); font-size:10px; font-weight:600;
}
.zt-seg{flex:0 0 auto; display:flex; gap:3px}
.zt-seg i{width:15px; height:5px; border-radius:16px; background:var(--zt-line)}
.zt-seg i.spur{background:#b692f6} .zt-seg i.sora{background:#f7b27a}
.zt-seg i.sred{background:#f97066} .zt-seg i.sblu{background:#8098f9}
.zt-seg i.sgrn{background:#3ccb7f} .zt-seg i.sgry{background:#e9eaeb}
.zt-cnt{
  position:relative;
  flex:0 0 auto; width:24px; text-align:right;
  color:var(--zt-mid); font-size:10.5px; font-weight:600; font-variant-numeric:tabular-nums;
}

@container (max-width:430px){ .zt-mo{display:none} .zt-seg i{width:11px} }
@container (max-width:330px){ .zt-list{padding:6px 7px 8px} .zt-row{gap:6px; padding:7px 9px} .zt-seg i{width:8px} }

/* --------------------------------------------------------------------------
   Documents
   --------------------------------------------------------------------------
   From the Documents screen. The breadcrumb is the point: a file is not
   somewhere, it is in a year, a month and an account — and the last crumb
   sits in an indigo pill because that is where you are.

     crumb 600/14 #535862, current #444ce7 on a tint
     folder header 500/14 #181d27 with a #444ce7 folder
     file name 500/14 #535862, rules between rows, download in #a4a7ae
   -------------------------------------------------------------------------- */
.zd{
  --zd-ink:#181d27; --zd-mid:#535862; --zd-quiet:#a4a7ae;
  --zd-line:#e9eaeb; --zd-brand:#444ce7;
  container-type:inline-size;
}
.ui-pane.zd{background:#fff; border-color:var(--zd-line)}
.zd-crumb{
  display:flex; align-items:center; gap:6px; flex-wrap:nowrap;
  margin:0; padding:10px 13px; border-bottom:1px solid var(--zd-line);
  color:var(--zd-mid); font-size:11px; font-weight:600; min-width:0;
}
.zd-crumb>span{white-space:nowrap; overflow:hidden; text-overflow:ellipsis}
.zd-crumb>i{color:var(--zd-quiet); font-style:normal; font-weight:400}
.zd-crumb>em{
  flex:0 1 auto; padding:2px 8px; border-radius:6px;
  background:#eef0ff; color:var(--zd-brand); font-style:normal;
  white-space:nowrap; overflow:hidden; text-overflow:ellipsis;
}
.zd-body{padding:11px 13px 4px}
.zd-fold{
  display:flex; align-items:center; gap:7px; margin:0 0 4px;
  color:var(--zd-ink); font-size:12px; font-weight:500;
}
.zd-fold svg{flex:0 0 auto; color:var(--zd-brand)}
.zd-file{
  display:flex; align-items:center; gap:9px;
  padding:9px 1px; border-top:1px solid var(--zd-line); min-width:0;
}
.zd-file>span{
  flex:1 1 auto; min-width:0; color:var(--zd-mid); font-size:12px;
  white-space:nowrap; overflow:hidden; text-overflow:ellipsis;
}
.zd-pdf{flex:0 0 auto}
.zd-dl{flex:0 0 auto; display:inline-flex; color:var(--zd-quiet)}

@container (max-width:430px){ .zd-crumb .hide-sm{display:none} }
@container (max-width:330px){ .zd-crumb,.zd-body{padding-left:10px; padding-right:10px} }

/* ==========================================================================
   THE FOUR PRACTICE FRAGMENTS, WORKING
   ==========================================================================
   Same contract as the demonstrations above: the natural state of every
   element below is the finished one, and the keyframes only ever run
   forward into it. So a page with no JavaScript — where `.is-live` is
   never added — shows the completed screen, and this block adds motion to
   something already correct rather than depending on it.

   Each card performs the one thing it is for, once, when it arrives:
   mail lands, a task finishes, the bars grow, a document is filed.
   ========================================================================== */
@media (prefers-reduced-motion:no-preference){

  /* ---- Email: the inbox fills, newest first, and unread stays unread ---- */
  .ui.is-live .zi-row{
    animation:zi-land .5s var(--settle) both;
    animation-delay:calc(var(--i,0) * 85ms + 140ms);
  }
  @keyframes zi-land{from{opacity:0;transform:translateY(-8px)}to{opacity:1;transform:none}}

  /* the blue dot breathes twice, so the card is alive without being busy */
  .ui.is-live .zi-row.is-unread .zi-dot{
    animation:zi-ping 1.5s var(--ease) calc(var(--i,0) * 85ms + 900ms) 2;
  }
  @keyframes zi-ping{
    0%,100%{box-shadow:0 0 0 0 rgba(46,144,250,0)}
    45%{box-shadow:0 0 0 3.5px rgba(46,144,250,.22)}
  }

  /* ---- Tasks: every stage fills, and the top one finishes as you watch ---- */
  .zt-seg i{transform-origin:left center}
  .ui.is-live .zt-seg i{animation:zt-fill .32s var(--settle) both}
  @keyframes zt-fill{from{transform:scaleX(0)}to{transform:none}}
  .ui.is-live .zt-row:nth-of-type(1) .zt-seg i:nth-child(1){animation-delay:.30s}
  .ui.is-live .zt-row:nth-of-type(1) .zt-seg i:nth-child(2){animation-delay:.38s}
  .ui.is-live .zt-row:nth-of-type(1) .zt-seg i:nth-child(3){animation-delay:.46s}
  .ui.is-live .zt-row:nth-of-type(2) .zt-seg i{animation-delay:.50s}
  .ui.is-live .zt-row:nth-of-type(3) .zt-seg i{animation-delay:.62s}
  .ui.is-live .zt-row:nth-of-type(4) .zt-seg i{animation-delay:.74s}



  /* ---- Client dashboard: the waterfall grows out of the zero line ---- */
  .zp-wtrack>i.is-pos{transform-origin:right center}
  .zp-wtrack>i.is-neg{transform-origin:left center}
  .ui.is-live .zp-wtrack>i{animation:zp-grow .6s var(--settle) both}
  @keyframes zp-grow{from{transform:scaleX(0)}to{transform:none}}
  .ui.is-live .zp-wrow:nth-child(1) .zp-wtrack>i{animation-delay:.30s}
  .ui.is-live .zp-wrow:nth-child(2) .zp-wtrack>i{animation-delay:.42s}
  .ui.is-live .zp-wrow:nth-child(3) .zp-wtrack>i{animation-delay:.54s}
  .ui.is-live .zp-wrow:nth-child(4) .zp-wtrack>i{animation-delay:.66s}
  .ui.is-live .zp-wrow:nth-child(5) .zp-wtrack>i{animation-delay:.78s}

  /* each figure appears once its own bar has finished travelling */
  .ui.is-live .zp-wval{animation:zp-say .34s var(--ease) both}
  .ui.is-live .zp-wrow:nth-child(1) .zp-wval{animation-delay:.72s}
  .ui.is-live .zp-wrow:nth-child(2) .zp-wval{animation-delay:.84s}
  .ui.is-live .zp-wrow:nth-child(3) .zp-wval{animation-delay:.96s}
  .ui.is-live .zp-wrow:nth-child(4) .zp-wval{animation-delay:1.08s}
  .ui.is-live .zp-wrow:nth-child(5) .zp-wval{animation-delay:1.20s}
  @keyframes zp-say{from{opacity:0;transform:translateY(-50%) translateX(-4px)}
                    to{opacity:1;transform:translateY(-50%)}}

  /* ---- Documents: the paper arrives and the folder it landed in lights ---- */
  .ui.is-live .zd-file{animation:zd-land .46s var(--settle) both}
  .ui.is-live .zd-file:nth-of-type(1){animation-delay:.34s}
  .ui.is-live .zd-file:nth-of-type(2){animation-delay:.46s}
  .ui.is-live .zd-file:nth-of-type(3){animation-delay:.58s}
  @keyframes zd-land{from{opacity:0;transform:translateY(-6px)}to{opacity:1;transform:none}}

  .ui.is-live .zd-crumb>em{animation:zd-here 1.1s var(--ease) .9s 1}
  @keyframes zd-here{
    0%,100%{background:#eef0ff}
    35%{background:#dfe3ff}
  }
}

/* ==========================================================================
   THE FRAGMENTS AT WORK
   ==========================================================================
   The block above is an arrival: it plays once and stops. This one is the
   product running — mail landing and being filed, stages completing,
   figures rebuilding, paper arriving — driven by scenes.js.

   Every initial state here is gated behind `.is-scened`, which only exists
   while a scene is actually playing. Nothing below can leave a fragment
   incomplete on a page without JavaScript: there, none of it applies.
   ========================================================================== */
@media (prefers-reduced-motion:no-preference){

  /* ---- Email ---- */
  .zi-row{transition:max-height .42s var(--settle),opacity .34s var(--ease),transform .42s var(--settle)}
  .zi-row.is-arriving{max-height:0;opacity:0;transform:translateY(-10px);overflow:hidden;transition:none}
  .zi-row{max-height:70px}

  /* the client chip is Zenday's filing, so it appears when the filing does */
  .zi-list.is-scened .zi-tag{transition:opacity .4s var(--ease),transform .4s var(--settle)}
  .zi-list.is-scened .zi-row:not(.is-filed) .zi-tag{opacity:0;transform:scale(.9)}

  .zi-dot{transition:background .5s var(--ease),box-shadow .4s var(--ease)}

  /* ---- Tasks ---- */
  .zt-seg i{transition:background .45s var(--ease),scale .3s var(--settle)}
  /* `transform` is pinned by the arrival animation's fill, so the pop rides
     the independent `scale` property instead — otherwise it never shows. */
  .zt-seg i.is-just{scale:1 1.75}
  .zt-row{transition:border-color .5s var(--ease),background .5s var(--ease)}
  .zt-row.is-moved{border-color:#c7d7fe}
  .zt-cnt{transition:color .4s var(--ease)}

  /* ---- Client dashboard ---- */
  .zp-wtrack>i{transition:width .85s var(--settle)}
  .zp-wval{transition:left .85s var(--settle),opacity .3s var(--ease)}
  .zp-v,.zp-d b{transition:opacity .3s var(--ease)}
  .zp-met.is-fresh .zp-v,.zp-met.is-fresh .zp-d b{
    animation:zp-refresh .85s var(--ease) 1;
  }
  @keyframes zp-refresh{
    0%{opacity:.25;transform:translateY(-3px)}
    100%{opacity:1;transform:none}
  }
  .zp-d i{transition:background .4s var(--ease),color .4s var(--ease)}
  .zp-cap em{transition:color .4s var(--ease)}

  /* ---- Documents ---- */
  .zd-file{transition:max-height .44s var(--settle),opacity .34s var(--ease),
                      transform .44s var(--settle),background .6s var(--ease);
           max-height:60px}
  .zd-file.is-arriving{max-height:0;opacity:0;transform:translateY(-8px);overflow:hidden;transition:none}
  .zd-file.is-new{background:#f5f6ff}
}

/* --------------------------------------------------------------------------
   The close, as an exception queue
   --------------------------------------------------------------------------
   From Client / Close -> Transactions review. The close in this product is
   not a checklist of chores; it is a list of exceptions, each carrying what
   it is waiting on. The status chips are the whole content of the screen:

     Not started    #fafafa on #e9eaeb, #414651
     Awaiting client #fffaeb on #fedf89, #b54708
     Internal hold  #f4f3ff on #d9d6fe, #5925dc
     In progress    #eff8ff on #b2ddff, #175cd3
   -------------------------------------------------------------------------- */
.zc{--zc-ink:#181d27; --zc-mid:#535862; --zc-quiet:#717680; --zc-line:#e9eaeb;
    container-type:inline-size}
.ui-pane.zc{background:#fff; border-color:var(--zc-line)}
.zc-tabs{display:flex; gap:15px; padding:9px 13px 8px; border-bottom:1px solid var(--zc-line)}
.zc-tabs>b{color:var(--zc-ink); font-size:12px; font-weight:600; white-space:nowrap}
.zc-tabs>span{color:var(--zc-quiet); font-size:12px; font-weight:500; white-space:nowrap}
.zc-mon{margin:0; padding:9px 13px 5px; color:var(--zc-quiet); font-size:10.5px; font-weight:600;
        background:#fafafa}
.zc-list{background:#fafafa; padding:0 10px 10px}
.zc-row{
  display:flex; align-items:center; gap:9px; min-width:0;
  margin-bottom:5px; padding:8px 11px;
  border:1px solid var(--zc-line); border-radius:8px; background:#fff;
}
.zc-t{flex:1 1 auto; min-width:0; color:#414651; font-size:11.5px; font-weight:600;
      white-space:nowrap; overflow:hidden; text-overflow:ellipsis}
.zc-n{flex:0 0 auto; padding:0 6px; border:1px solid var(--zc-line); border-radius:20px;
      color:var(--zc-mid); font-size:10px; font-weight:600}
.zc-s{flex:0 0 auto}
.zc-s>em{display:inline-block; padding:1px 7px; border-radius:6px; border:1px solid var(--zc-line);
         background:#fafafa; color:#414651; font-size:10px; font-weight:500; font-style:normal;
         white-space:nowrap}
.is-cli  .zc-s>em{background:#fffaeb; border-color:#fedf89; color:#b54708}
.is-hold .zc-s>em{background:#f4f3ff; border-color:#d9d6fe; color:#5925dc}
.is-run  .zc-s>em{background:#eff8ff; border-color:#b2ddff; color:#175cd3}
.zc-when{flex:0 0 auto; color:var(--zc-quiet); font-size:10.5px; white-space:nowrap}
.zc-go{flex:0 0 auto; padding:2px 8px; border:1px solid var(--zc-line); border-radius:6px;
       color:var(--zc-mid); font-size:10px; font-weight:600; font-style:normal}

@container (max-width:470px){ .zc-row .hide-sm,.zc-tabs .hide-sm{display:none} .zc-go{display:none} }
@container (max-width:340px){ .zc-n{display:none} .zc-row{gap:7px; padding:7px 9px} }

/* --------------------------------------------------------------------------
   The summary, against last month
   --------------------------------------------------------------------------
   From the portal's P&L summary. Period, prior period, and a variance that
   carries its own direction — red down, and up in red too when the line is
   an expense, because rising costs are not good news.
   -------------------------------------------------------------------------- */
.zs{--zs-ink:#181d27; --zs-mid:#535862; --zs-quiet:#717680; --zs-line:#e9eaeb;
    container-type:inline-size}
.ui-pane.zs{background:#fff; border-color:var(--zs-line)}
.zs-cap{display:flex; align-items:baseline; justify-content:space-between; gap:10px;
        margin:0; padding:11px 13px 9px; color:var(--zs-mid); font-size:10px; font-weight:600;
        letter-spacing:.06em; text-transform:uppercase}
.zs-cap em{color:var(--zs-quiet); font-style:normal; font-weight:500}
.zs-tbl{width:100%; table-layout:fixed; border-collapse:collapse}
.zs-tbl th{padding:5px 8px; background:#f5f5f5; border-top:1px solid var(--zs-line);
           border-bottom:1px solid var(--zs-line); color:var(--zs-quiet);
           font-size:9.5px; font-weight:600; text-align:right; white-space:nowrap}
.zs-tbl th.zs-k{text-align:left}
.zs-tbl td{padding:8px; border-bottom:1px solid var(--zs-line); font-size:11.5px;
           white-space:nowrap; overflow:hidden; text-overflow:ellipsis}
.zs-tbl tr:last-child td{border-bottom:0}
.zs-k{color:var(--zs-mid); font-weight:500; padding-left:13px}
.zs-n{width:var(--sn,82px); text-align:right; color:var(--zs-ink); font-weight:500;
      font-variant-numeric:tabular-nums}
.zs-var{width:var(--sv,86px); text-align:right; padding-right:13px;
        font-weight:500; font-variant-numeric:tabular-nums}
.zs-var.is-dn{color:#d92d20} .zs-var.is-up{color:#d92d20}
.zs-var:before{content:"↓ "; font-size:.85em}
.zs-var.is-up:before{content:"↑ "}
@container (max-width:470px){ .zs-tbl .hide-sm{display:none} .zs-tbl{--sn:78px; --sv:82px} }
@container (max-width:340px){ .zs-tbl{--sn:72px; --sv:76px}
  .zs-tbl td,.zs-tbl th{padding-left:6px; padding-right:6px} }

/* --------------------------------------------------------------------------
   Burn, and how long the cash lasts
   --------------------------------------------------------------------------
   From Insights -> Net burn rate. Three figures, then the year in and out
   of the bank across a zero line: money in above it in the brand indigo,
   money out below it in grey.
   -------------------------------------------------------------------------- */
.zf{--zf-ink:#181d27; --zf-quiet:#717680; --zf-line:#e9eaeb;
    container-type:inline-size}
.ui-pane.zf{background:#fff; border-color:var(--zf-line)}
.zf-cap{display:flex; align-items:baseline; justify-content:space-between; gap:10px;
        margin:0; padding:11px 13px 10px; color:#535862; font-size:10px; font-weight:600;
        letter-spacing:.06em; text-transform:uppercase}
.zf-cap em{color:var(--zf-quiet); font-style:normal; font-weight:500}
.zf-figs{display:grid; grid-template-columns:repeat(3,minmax(0,1fr)); gap:8px; margin:0 13px 13px}
.zf-figs>span{display:grid; gap:3px; min-width:0; padding:9px 10px;
              border:1px solid var(--zf-line); border-radius:9px}
.zf-figs small{color:var(--zf-quiet); font-size:9.5px; font-weight:500;
               letter-spacing:.04em; text-transform:uppercase;
               white-space:nowrap; overflow:hidden; text-overflow:ellipsis}
.zf-figs b{color:var(--zf-ink); font-size:clamp(11.5px,1cqi + 8px,13.5px); font-weight:600;
           letter-spacing:-.02em; font-variant-numeric:tabular-nums; white-space:nowrap}
.zf-figs b>i{color:var(--zf-quiet); font-size:.82em; font-style:normal; font-weight:500}
/* the year across a zero line */
.zf-chart{position:relative; display:flex; align-items:center; gap:var(--fg,5px);
          height:88px; margin:0 13px; padding-bottom:14px}
.zf-zero{position:absolute; left:0; right:0; top:calc(50% - 7px); border-top:1px dashed #d5d7da}
.zf-col{position:relative; flex:1 1 0; min-width:0; height:100%}
/* a bar is anchored to the axis, so only its far end is rounded — rounding
   all four corners makes it float free of the line it is measured from */
.zf-col>i{position:absolute; left:0; right:0}
.zf-col>i.is-in {bottom:50%; height:calc(var(--h) * .75); background:#444ce7;
                 border-radius:3px 3px 0 0}
.zf-col>i.is-out{top:50%;    height:calc(var(--h) * .75); background:#d5d7da;
                 border-radius:0 0 3px 3px}
.zf-col>small{position:absolute; left:0; right:0; bottom:-14px; text-align:center;
              color:var(--zf-quiet); font-size:8.5px}
@container (max-width:470px){ .zf-figs{gap:6px} .zf-chart{--fg:4px} }
@container (max-width:430px){
  .zf-figs{grid-template-columns:minmax(0,1fr); gap:5px}
  .zf-figs>span{grid-template-columns:1fr auto; align-items:baseline; gap:10px; padding:7px 10px}
  .zf-figs small{overflow:visible}
}
@container (max-width:340px){ .zf-chart{--fg:3px; height:76px} .zf-col>small{font-size:7.5px} }

@media (prefers-reduced-motion:no-preference){
  /* an account, a period, a month: the figures under them rebuild */
  .zs.is-fresh .zs-n,.zs.is-fresh .zs-var,.zs.is-fresh .zs-k{animation:zq-fresh .7s var(--ease) 1}
  @keyframes zq-fresh{0%{opacity:.2;transform:translateY(-2px)}100%{opacity:1;transform:none}}
  .zs-var{transition:color .4s var(--ease)}
  .zf-figs b{transition:opacity .3s var(--ease)}
  /* the year re-tweens rather than jumping */
  .zf-col>i{transition:height .8s var(--settle),background .5s var(--ease),border-radius .3s var(--ease)}
}

/* --------------------------------------------------------------------------
   The portfolio, as the firm sees it
   --------------------------------------------------------------------------
   From the firm dashboard. One row per book, each carrying what is waiting
   in it — mail, tasks, queries, exceptions — and who is on it. The count
   that matters is the last one, so it is the only one that is not grey.
   -------------------------------------------------------------------------- */
.zn{--zn-ink:#181d27; --zn-mid:#535862; --zn-quiet:#717680; --zn-line:#e9eaeb;
    container-type:inline-size}
.ui-pane.zn{background:#fff; border-color:var(--zn-line)}
.zn-bar{display:flex; align-items:center; gap:7px; padding:10px 13px 9px;
        border-bottom:1px solid var(--zn-line)}
.zn-bar>b{color:var(--zn-ink); font-size:12.5px; font-weight:600}
.zn-bar>em{color:var(--zn-quiet); font-size:12px; font-style:normal}
.zn-add{margin-left:auto; padding:2px 8px; border:1px solid var(--zn-line); border-radius:6px;
        color:var(--zn-mid); font-size:10px; font-weight:600; font-style:normal; white-space:nowrap}
.zn-list{padding:2px 0}
.zn-row{display:flex; align-items:center; gap:9px; min-width:0;
        padding:9px 13px; border-bottom:1px solid var(--zn-line)}
.zn-row:last-child{border-bottom:0}
.zn-av{flex:0 0 auto; width:13px; height:13px; border-radius:50%; background:var(--zn-quiet)}
.zn-av.is-nl{background:#e0349a} .zn-av.is-ol{background:#7f56d9}
.zn-av.is-nw{background:#0f9d58} .zn-av.is-gw{background:#12b76a}
.zn-av.is-bf{background:#f79009}
.zn-c{flex:1 1 auto; min-width:0; color:var(--zn-ink); font-size:11.5px; font-weight:600;
      white-space:nowrap; overflow:hidden; text-overflow:ellipsis}
.zn-m{flex:0 0 auto; display:inline-flex; align-items:center; gap:3px;
      color:var(--zn-quiet); font-size:10.5px; font-variant-numeric:tabular-nums}
.zn-m>em{font-style:normal; font-size:10px}
.zn-m.is-warn{color:#b54708}
/* who is on the book, drawn rather than photographed */
.zn-who{flex:0 0 auto; display:inline-flex}
.zn-who>i{width:14px; height:14px; border-radius:50%; background:#e9eaeb;
          border:1.5px solid #fff; margin-left:-5px}
.zn-who>i:first-child{margin-left:0; background:#d5d7da}
.zn-who>i:nth-child(2){background:#c7d7fe}
@container (max-width:430px){ .zn-row .hide-sm{display:none} .zn-who{display:none} }
@container (max-width:330px){ .zn-row{gap:7px; padding:8px 10px} }

@media (prefers-reduced-motion:no-preference){

  /* a count moving on one book */
  .zn-row{transition:background .5s var(--ease)}
  .zn-row.is-live-row{background:#fafbff}
  .zn-m{transition:color .35s var(--ease)}
  .zn-m.is-bump{color:#3538cd; font-weight:600}
}

/* --------------------------------------------------------------------------
   Three accounting artifacts, not three tables with chips
   --------------------------------------------------------------------------
   The bank feed, the ledger and the reconciliation were all the same shape:
   rows with a status pill on the right. That is a product-manager's view of
   three features, not an accountant's. Each does a different job, so each
   gets the artifact that job produces:

     bank feed      a coding decision — the bank's raw string on top, the
                    payee and GL account it resolved to underneath
     general ledger debit, credit, and the balance after each entry
     reconciliation two sides set against each other, ending in a difference

   Column counts came down as well. Invaro's categorisation table runs four
   columns and reads instantly; mine ran six plus a tab strip plus a banner,
   and truncated in every one of them.
   -------------------------------------------------------------------------- */
.zb,.zg,.zr{
  --k-ink:#181d27; --k-mid:#535862; --k-quiet:#717680; --k-faint:#a4a7ae;
  --k-line:#e9eaeb; --k-strip:#f5f5f5;
  container-type:inline-size;
}
.ui-pane.zb,.ui-pane.zg,.ui-pane.zr{background:#fff; border-color:var(--k-line)}
.zb-tbl,.zg-tbl{width:100%; table-layout:fixed; border-collapse:collapse}
.zb-tbl th,.zg-tbl th{
  padding:7px 8px; background:var(--k-strip); border-bottom:1px solid var(--k-line);
  color:var(--k-quiet); font-size:9.5px; font-weight:600; text-align:left; white-space:nowrap;
}
.zb-tbl td,.zg-tbl td{
  padding:9px 8px; border-bottom:1px solid var(--k-line);
  color:var(--k-mid); font-size:11.5px; white-space:nowrap; overflow:hidden; text-overflow:ellipsis;
}
.zb-tbl tbody tr:last-child td{border-bottom:0}

/* ---- bank feed: the raw string, and what it became ---- */
.zb-tbl .zb-d{width:var(--bd,62px); padding-left:13px; color:var(--k-quiet)}
.zb-tbl .zb-g{width:var(--bg,110px)}
.zb-tbl .zb-m{width:var(--bm,102px); text-align:right; padding-right:13px;
              color:var(--k-ink); font-weight:500; font-variant-numeric:tabular-nums}
.zb-tbl th.zb-m{text-align:right}
.zb-n{min-width:0}
/* the bank's own text, kept as evidence above the reading of it */
.zb-raw{display:block; color:var(--k-faint); font-size:9.5px; font-weight:500;
        letter-spacing:.02em; font-family:var(--font-mono,ui-monospace,monospace);
        white-space:nowrap; overflow:hidden; text-overflow:ellipsis}
.zb-n>b{display:block; margin-top:1px; color:var(--k-ink); font-size:11.5px; font-weight:500;
        white-space:nowrap; overflow:hidden; text-overflow:ellipsis}
.zb-g>em{display:inline-flex; align-items:baseline; gap:5px; font-style:normal;
         color:var(--k-mid); font-size:11px; white-space:nowrap}
.zb-g>em>i{color:var(--k-faint); font-size:10px; font-style:normal;
           font-family:var(--font-mono,ui-monospace,monospace)}
.zb-g>u{color:#b54708; font-size:10.5px; text-decoration:none}
.zb-tbl tr.is-open .zb-m{color:#b54708}
.zb-foot{display:flex; align-items:baseline; gap:8px; margin:0;
         padding:9px 13px; border-top:1px solid var(--k-line); background:#fafafa}
.zb-foot>b{color:var(--k-ink); font-size:10.5px; font-weight:600}
/* amber means something wants a person. When nothing does, the line must
   not keep wearing the colour that says it does. */
.zb-foot>span{margin-left:auto; color:#b54708; font-size:10.5px}
.zb-foot>span.is-clear{color:var(--k-quiet)}

/* ---- general ledger: debit, credit, balance ---- */
.zg-head{display:flex; align-items:baseline; gap:9px; padding:11px 13px 9px; min-width:0}
.zg-head b{color:var(--k-ink); font-size:12.5px; font-weight:600;
           white-space:nowrap; overflow:hidden; text-overflow:ellipsis}
.zg-head small{margin-left:auto; flex:0 0 auto; color:var(--k-quiet); font-size:10.5px}
.zg-tbl th{border-top:1px solid var(--k-line)}
.zg-tbl .zg-d{width:var(--gd,64px); padding-left:13px; color:var(--k-quiet)}
.zg-tbl .zg-x{width:var(--gx,74px); text-align:right; font-variant-numeric:tabular-nums}
.zg-tbl .zg-b{width:var(--gb,88px); text-align:right; padding-right:13px;
              color:var(--k-ink); font-weight:500; font-variant-numeric:tabular-nums}
.zg-tbl th.zg-x,.zg-tbl th.zg-b{text-align:right}
.zg-tbl tr.is-open td{color:var(--k-quiet); font-style:italic}
/* the closing line is the account's answer, so it is ruled off like one */
.zg-tbl tfoot td{padding:9px 8px; border-top:1.5px solid var(--k-ink); border-bottom:0;
                 color:var(--k-ink); font-size:11.5px; font-weight:600;
                 font-variant-numeric:tabular-nums; white-space:nowrap}
.zg-tbl tfoot .zg-d{padding-left:13px} .zg-tbl tfoot .zg-b{padding-right:13px}

@container (max-width:470px){
  .zb-tbl{--bd:60px; --bg:88px; --bm:98px}
  .zg-tbl{--gd:62px; --gx:64px; --gb:78px}
}
/* At 288px a five-column ledger cannot hold its own entry names, and an
   entry you cannot read is worse than a column you dropped. Date and credit
   go; entry, debit and balance are what make it a ledger. */
@container (max-width:360px){
  .zb-tbl .zb-g{display:none}
  .zg-tbl .zg-d{display:none}
  .zg-tbl td.zg-x:nth-of-type(4),.zg-tbl th.zg-x:nth-of-type(4){display:none}
  .zg-tbl .zg-e{padding-left:13px}
  .zb-tbl td,.zb-tbl th,.zg-tbl td,.zg-tbl th{padding-left:6px; padding-right:6px}
}

@media (prefers-reduced-motion:no-preference){
  /* a line getting coded */
  .zb-tbl tr{transition:background .5s var(--ease)}
  .zb-tbl tr.is-just{background:#f7f8ff}
  .zb-g>em,.zb-g>u{animation:zk-set .4s var(--ease)}
  @keyframes zk-set{from{opacity:0;transform:translateY(-3px)}to{opacity:1;transform:none}}
  .zb-foot>b,.zb-foot>span{transition:color .4s var(--ease)}

  /* an entry posting, and every balance under it moving */
  .zg-tbl tr{transition:max-height .4s var(--settle),opacity .3s var(--ease),background .5s var(--ease);
             max-height:56px}
  .zg-tbl tr.is-arriving{max-height:0;opacity:0;overflow:hidden;transition:none}
  .zg-tbl tr.is-posted{background:#f7f8ff}
  .zg-b,.zg-tbl tfoot td{transition:color .4s var(--ease)}

}

/* ---- general ledger: the split rides under the memo ----
   A GL detail report is defined by its Split column — the account the other
   half of the entry landed in. Without it you cannot see the double entry at
   all. Nine columns will not fit a card, so the reference and the split sit
   under the memo rather than beside it; the accounting survives, the width
   does not have to. */
.zg-tbl td.zg-e{white-space:normal}
.zg-e>b{display:block; color:var(--k-ink); font-size:11.5px; font-weight:500;
        white-space:nowrap; overflow:hidden; text-overflow:ellipsis}
.zg-e>small{display:block; margin-top:1px; color:var(--k-faint); font-size:9.5px;
            white-space:nowrap; overflow:hidden; text-overflow:ellipsis}
.zg-e>small>em{color:var(--k-quiet); font-style:normal}
.zg-e>small>u{text-decoration:none}
/* When the column is too tight for both, the reference goes and the split
   stays: the split is the other half of the entry, the reference is only a
   way to look the entry up. */
@container (max-width:480px){ .zg-e>small>u{display:none}
  .zg-e>small>u + *{margin-left:0}
  .zg-e>small{text-indent:0} }
.zg-tbl tr.is-open td{color:var(--k-quiet)}
.zg-tbl tr.is-open .zg-e>b{color:var(--k-quiet); font-weight:500}
.zg-tbl tfoot tr.is-tot td{border-top:1px solid var(--k-line); border-bottom:0}
.zg-tbl tfoot tr:last-child td{border-top:1.5px solid var(--k-ink)}

/* ---- bank reconciliation: two sides that must meet ----
   Not a list of matched lines — that is transaction matching. A bank
   reconciliation is a statement: the bank's balance adjusted for what the
   books already knew (deposits in transit, outstanding checks), the books'
   balance adjusted for what only the bank knew (charges, returned items),
   and the two arriving at the same figure. The zero at the bottom is the
   proof, and it is the first thing anyone looks at. */
/* the header lost its rules when the old matching block came out */
.zr-top{display:flex; align-items:baseline; gap:10px; padding:11px 13px 9px; min-width:0}
.zr-top b{color:var(--k-ink); font-size:12.5px; font-weight:600;
          white-space:nowrap; overflow:hidden; text-overflow:ellipsis}
.zr-top small{margin-left:auto; flex:0 0 auto; color:var(--k-quiet); font-size:10.5px; white-space:nowrap}
.zr-side{padding:9px 13px 10px}
.zr-side + .zr-side{border-top:1px solid var(--k-line)}
.zr-line{display:flex; align-items:baseline; gap:7px; margin:0; padding:3px 0; min-width:0}
.zr-line>i{flex:0 0 auto; width:30px; color:var(--k-faint); font-size:9.5px;
           font-style:normal; text-transform:uppercase; letter-spacing:.04em}
.zr-line>span{flex:1 1 auto; min-width:0; color:var(--k-mid); font-size:11.5px;
              white-space:nowrap; overflow:hidden; text-overflow:ellipsis}
.zr-line>b{flex:0 0 auto; color:var(--k-ink); font-size:11.5px; font-weight:500;
           font-variant-numeric:tabular-nums; white-space:nowrap}
.zr-line.is-lead>span{color:var(--k-ink); font-weight:600}
.zr-line.is-lead>b{font-weight:600}
/* the sub-total is ruled off, because that is what a rule means here */
.zr-line.is-sub{margin-top:4px; padding-top:6px; border-top:1px solid var(--k-line)}
.zr-line.is-sub>span{color:var(--k-ink); font-weight:600; padding-left:37px}
.zr-line.is-sub>b{font-weight:600}

@container (max-width:360px){
  .zr-side{padding-left:10px; padding-right:10px}
  .zr-line>i{width:26px}
  .zr-line.is-sub>span{padding-left:33px}
}

@media (prefers-reduced-motion:no-preference){
  /* an unrecorded item being found and the difference closing on it */
  .zr-line>b,.zr-diff>span{transition:color .4s var(--ease)}
  .zr-side.is-fresh .zr-line>b{animation:zq-fresh .7s var(--ease) 1}
}

/* --------------------------------------------------------------------------
   Zenday's own chrome on both
   --------------------------------------------------------------------------
   The two cards above were right as accounting and wrong as Zenday. The
   product has a signature and neither carried it: a #fafafa panel on
   #e9eaeb at r8 holding the figures that matter, labels 500/12 #717680
   over 600/18 figures, a sub-tab strip with the live one ruled underneath,
   and entry types as chips rather than words. All of that is measured off
   Financial's drill-down, not invented to match.
   -------------------------------------------------------------------------- */
.zg-head{display:flex; align-items:baseline; gap:9px; padding:11px 13px 10px; min-width:0}
.zg-head b{color:var(--k-ink); font-size:12.5px; font-weight:600;
           white-space:nowrap; overflow:hidden; text-overflow:ellipsis}
.zg-head small{flex:0 0 auto; color:var(--k-quiet); font-size:10.5px; white-space:nowrap}
.zg-exp{flex:0 0 auto; margin-left:auto; padding:2px 8px; border:1px solid var(--k-line);
        border-radius:8px; color:#414651; font-size:10px; font-weight:500; font-style:normal}

/* the panel Zenday puts its key figures in */
.zg-sums,.zr-sums{
  display:grid; grid-template-columns:repeat(3,minmax(0,1fr)); gap:10px;
  margin:0 13px 11px; padding:10px 12px;
  background:#fafafa; border:1px solid var(--k-line); border-radius:8px;
}
.zg-sums>span,.zr-sums>span{display:grid; gap:3px; min-width:0}
.zg-sums small,.zr-sums small{color:var(--k-quiet); font-size:10px; font-weight:500;
                              white-space:nowrap; overflow:hidden; text-overflow:ellipsis}
.zg-sums b,.zr-sums b{color:var(--k-ink); font-size:clamp(12px,1.2cqi + 8px,15px);
                      font-weight:600; letter-spacing:-.02em;
                      font-variant-numeric:tabular-nums; white-space:nowrap}
.zg-sums b>i{color:var(--k-quiet); font-size:.72em; font-style:normal; font-weight:500}
.zr-sums>span.is-zero b{color:#067647}

/* the sub-tab strip, live tab ruled underneath */
.zr-tabs{display:flex; gap:16px; padding:9px 13px 0; border-bottom:1px solid var(--k-line); margin-bottom:11px}
.zr-tabs>span,.zr-tabs>b{padding-bottom:8px; border-bottom:1px solid transparent;
                         font-size:11.5px; white-space:nowrap}
.zr-tabs>span{color:var(--k-quiet); font-weight:500}
.zr-tabs>b{color:var(--k-ink); font-weight:600; border-bottom-color:var(--k-ink)}

/* entry types as chips, the way Financial shows them */
/* the chip rides on the split line, not beside the memo: at card width the
   two together cannot both be read, and the memo is what identifies the
   entry. JE and Bill are what an accountant writes anyway. */
.zg-e>small>em{display:inline-block; margin-right:5px; padding:0 4px; border-radius:5px;
               font-size:8.5px; font-weight:500; font-style:normal}
.zg-e>small>em.is-je{background:#f4f3ff; border:1px solid #d9d6fe; color:#5925dc}
.zg-e>small>em.is-bill{background:#eef4ff; border:1px solid #c7d7fe; color:#3538cd}
.zg-tbl tfoot tr td{border-top:1.5px solid var(--k-ink); border-bottom:0}
.zr-side:first-of-type{border-top:0}
/* the difference footer went out with the summary panel that briefly held it */
.zr-diff{display:flex; align-items:baseline; margin:0; padding:10px 13px;
         border-top:1.5px solid var(--k-ink); background:#fafafa}
.zr-diff>b{color:var(--k-ink); font-size:11px; font-weight:600}
.zr-diff>span{margin-left:auto; color:#067647; font-size:14px; font-weight:600;
              letter-spacing:-.02em; font-variant-numeric:tabular-nums}

@container (max-width:430px){
  .zg-sums,.zr-sums{grid-template-columns:minmax(0,1fr); gap:6px; padding:8px 10px}
  .zg-sums>span,.zr-sums>span{grid-template-columns:1fr auto; align-items:baseline; gap:10px}
  .zg-sums small,.zr-sums small{overflow:visible}
  .zr-tabs .hide-sm{display:none}
  .zg-exp{display:none}
}
