/* ============================================================================
   fdc-ui — the FDC design system
   ----------------------------------------------------------------------------
   One stylesheet, no build step, no framework. Everything below is either a
   TOKEN (a design decision, named once) or a COMPONENT (a token arrangement,
   named once). Screens never invent colours, spacing or shadows — they compose
   these classes. That is what keeps a screen written next year looking like a
   screen written today.

   Sections
     1. Tokens          — colour, type, space, radius, shadow, motion
     2. Reset + base    — the small amount of normalisation we actually need
     3. Layout          — app shell, nav, grid
     4. Primitives      — button, field, pill, icon
     5. Surfaces        — card, stat, sheet, toast
     6. Data            — table, list row, chart, bill viewer
     7. States          — skeleton, empty, error
     8. Utilities       — the deliberately short list
   ========================================================================= */

/* ---------------------------------------------------------------- 1. TOKENS */
:root{
  /* Brand. Sky + orange are the FDC marks; they are used sparingly and never
     as a background for body text. */
  --sky:#11C0EA;      --sky-deep:#0C8FB8;   --sky-bg:#E6F8FD;
  --orange:#E3591E;   --orange-deep:#C8491A; --orange-bg:#FCEBE1;

  /* Ink ramp — five steps, enough to build a hierarchy, few enough to remember. */
  --ink:#1B2430; --slate:#566071; --muted:#8B95A4; --faint:#AEB6C2; --line:#E9E6DE;
  --line-soft:#F2F0EA;

  /* Ground */
  --page:#F6F7F4; --card:#FFFFFF; --raised:#FFFFFF; --overlay:rgba(27,36,48,.45);

  /* Semantic. Each has a text colour and a matching wash for its own background,
     so a status never has to be improvised from opacity. */
  --green:#137A57; --green-bg:#E7F7F0; --green-line:#BFE7D5;
  --amber:#8A5709; --amber-bg:#FBEFDD; --amber-line:#F0D6A8;
  --red:#A32A1E;   --red-bg:#FBEAE7;   --red-line:#F2C4BC;
  --blue:#245C93;  --blue-bg:#E7F0FB;  --blue-line:#C3D9F7;

  /* Type. One family, a Devanagari face ahead of it so हिंदी does not fall back
     to something that fights the Latin. Sizes are a 1.2 ratio, rounded. */
  --font:"Hind","Inter",-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif;
  --mono:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;
  /* FLUID TYPE. Every size scales with the viewport between a floor and a ceiling, so
     the app fits a 320px phone and a 430px one without a breakpoint for each.
     Owner: "so everythign must be dynamic" — this is that, for text.

     Why clamp() and not more breakpoints: a breakpoint is a cliff. At 359px the layout is
     one thing and at 361px another, and the phones people actually own are scattered
     across that line — 320, 360, 375, 390, 393, 412, 428. Sizing against the viewport
     itself means no phone sits on a cliff edge.

     The FLOOR is what protects the small phone: nothing shrinks below the value it has
     today, because this app is read in shop light by people who are not looking for small
     print. The ceiling stops a tablet rendering it comic-book large. */
  --t-xs:clamp(11.5px, 0.7rem + 0.25vw, 13px);
  --t-sm:clamp(13px,   0.78rem + 0.3vw,  15px);
  --t-md:clamp(14.5px, 0.85rem + 0.35vw, 16.5px);
  --t-lg:clamp(17px,   1rem + 0.45vw,    19.5px);
  --t-xl:clamp(21px,   1.2rem + 0.6vw,   25px);
  --t-2xl:clamp(27px,  1.5rem + 0.8vw,   32px);
  --t-3xl:clamp(34px,  1.9rem + 1vw,     40px);

  /* Space — a 4px grid. Everything is one of these. */
  /* Spacing scales too, but far less than type — gaps that grow with the screen waste a
     small phone's height, which is the scarcest thing on it. Only the larger steps move. */
  --s-1:4px; --s-2:8px; --s-3:12px; --s-4:clamp(14px, 0.8rem + 0.4vw, 18px);
  --s-5:clamp(18px, 1rem + 0.5vw, 22px); --s-6:clamp(20px, 1.15rem + 0.6vw, 28px);
  --s-8:clamp(26px, 1.5rem + 0.8vw, 36px); --s-10:40px; --s-12:48px;

  --r-sm:9px; --r:13px; --r-lg:18px; --r-pill:999px;

  --shadow-sm:0 1px 2px rgba(27,36,48,.05), 0 1px 3px rgba(27,36,48,.04);
  --shadow-md:0 4px 12px rgba(27,36,48,.07), 0 2px 4px rgba(27,36,48,.04);
  --shadow-lg:0 16px 40px rgba(27,36,48,.14), 0 4px 12px rgba(27,36,48,.06);

  --ease:cubic-bezier(.2,.7,.3,1);
  --fast:120ms; --base:200ms; --slow:320ms;

  /* The minimum comfortable target. The shop uses this on a tablet with a thumb,
     so it is 44px, not whatever a mouse could hit. */
  --tap:44px;
  --shell-max:1180px;
}

/* Dark theme. The shop floor is dim in the evening and the stockroom has one
   tube light; a white screen at 100% is genuinely unpleasant there. Only the
   tokens change — not one component rule below knows this exists. */
/* NO AUTOMATIC DARK MODE — owner ruling 2026-08-20 ("remember our colour preference").
   Every other FDC surface is light-only: the dashboard, the counter till, the staff app,
   FDC Mail and the CA portal all ship the house palette and nothing else. This app was
   the ONLY one that flipped to dark off the phone's system setting, so a worker moving
   between the ERP and Expenses saw two different-looking products — and the owner opened
   it on a dark-mode phone and said so.

   The dark tokens are NOT lost: the explicit `:root[data-theme="dark"]` block below still
   defines the whole palette, so dark remains one attribute away if it is ever asked for.
   What is gone is the media query that applied it without anyone choosing it. */
:root[data-theme="dark"]{
  --ink:#EDF0F4; --slate:#B4BDCA; --muted:#8992A1; --faint:#69727F;
  --line:#2C3542; --line-soft:#232B36;
  --page:#141A22; --card:#1C232D; --raised:#222A35; --overlay:rgba(0,0,0,.6);
  --green:#5FD3A6; --green-bg:#12332A; --green-line:#1E5544;
  --amber:#E8B45F; --amber-bg:#33280F; --amber-line:#5A4419;
  --red:#F09085;   --red-bg:#361C19;   --red-line:#5C2C25;
  --blue:#8CBBEB;  --blue-bg:#16273A;  --blue-line:#274563;
  --sky-bg:#0E2B36; --orange-bg:#3A2013;
  --shadow-sm:0 1px 2px rgba(0,0,0,.4);
  --shadow-md:0 4px 12px rgba(0,0,0,.45);
  --shadow-lg:0 16px 40px rgba(0,0,0,.55);
}

/* ------------------------------------------------------ 2. RESET + BASE */
*,*::before,*::after{box-sizing:border-box}
html,body{margin:0;padding:0}
html{-webkit-text-size-adjust:100%}
body{
  font-family:var(--font); font-size:var(--t-md); line-height:1.5;
  color:var(--ink); background:var(--page);
  -webkit-font-smoothing:antialiased; text-rendering:optimizeLegibility;
  /* iOS: keep the app clear of the notch and the home indicator. */
  padding:env(safe-area-inset-top) env(safe-area-inset-right) 0 env(safe-area-inset-left);
}
h1,h2,h3,p,figure{margin:0}
button,input,select,textarea{font:inherit;color:inherit}
img,svg{display:block;max-width:100%}
table{border-collapse:collapse;width:100%}
[hidden]{display:none !important}

/* Focus. Visible, always, and never removed — the till is driven by keyboard as
   often as by thumb. :focus-visible so a mouse click does not draw a ring. */
:focus-visible{outline:3px solid var(--sky);outline-offset:2px;border-radius:var(--r-sm)}
:focus:not(:focus-visible){outline:none}

/* …EXCEPT the screen region itself, which is not a control anybody focuses on purpose.
   Every app here moves focus to <main class="shell" tabindex="-1"> after a navigation so
   a screen reader starts reading the NEW screen instead of staying where the old one
   was. That is right and stays. What was wrong is what it looked like: Chrome treats a
   programmatic focus on a non-interactive element as focus-visible, so the rule above
   drew a 3px sky outline around the entire page after every single click — reported as
   "cards are not properly aligned", because a rounded cyan box around the content reads
   as a border somebody meant to be there.
   Suppressed only here, and it costs no accessibility: .shell has tabindex="-1", so it
   is not in the tab order and no keyboard user can ever focus it deliberately. Every
   real control keeps its ring. */
.shell:focus,.shell:focus-visible{outline:none}

/* Anyone who has asked their OS to stop animating things gets no animation.
   Not reduced — none. */
@media (prefers-reduced-motion: reduce){
  *,*::before,*::after{animation-duration:.01ms !important;animation-iteration-count:1 !important;
    transition-duration:.01ms !important;scroll-behavior:auto !important}
}

/* Visible only to a screen reader. */
.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;
  clip:rect(0 0 0 0);white-space:nowrap;border:0}
/* …until it is focused — this is the skip link. */
.skip:focus{position:fixed;top:var(--s-3);left:var(--s-3);width:auto;height:auto;clip:auto;
  z-index:120;background:var(--card);color:var(--ink);padding:var(--s-3) var(--s-4);
  border-radius:var(--r-sm);box-shadow:var(--shadow-lg)}

/* ------------------------------------------------------------ 3. LAYOUT */
.app{min-height:100vh;min-height:100dvh;display:flex;flex-direction:column}

/* THE BAR IS FULL WIDTH; WHAT IS IN IT IS NOT.
   .shell below is capped at --shell-max and centred, so on any window wider than that
   the title, the tabs and the cards each started at a different place — the header hard
   against the window edge, the cards floating in the middle. The fix is padding rather
   than a wrapper element: the background and the bottom rule still span the whole
   window (which is the point of a sticky bar), while the first thing inside it begins on
   the same vertical line as the first thing in a card. `max()` keeps the ordinary
   padding on a narrow screen, where there is no gap to close. */
.topbar{
  position:sticky;top:0;z-index:40;background:var(--card);border-bottom:1px solid var(--line);
  display:flex;align-items:center;gap:var(--s-3);
  padding:var(--s-3) max(var(--s-4), calc((100% - var(--shell-max)) / 2 + var(--s-4)));
}
.topbar__brand{display:flex;align-items:center;gap:var(--s-3);min-width:0}
/* The wrapper around the title + subtitle needs this too, or the flex child refuses to
   shrink and the title is clipped BEHIND the actions instead of ellipsising. */
.topbar__brand > div{min-width:0}
.topbar__logo{width:30px;height:30px;border-radius:8px;flex:none}
.topbar__title{font-size:var(--t-lg);font-weight:700;letter-spacing:-.3px;white-space:nowrap;
  overflow:hidden;text-overflow:ellipsis}
.topbar__sub{font-size:var(--t-xs);color:var(--muted);white-space:nowrap}
.who{display:flex;flex-direction:column;justify-content:center;line-height:1.15;
  padding:0 var(--s-2);max-width:9.5rem}
.who__name{font-size:var(--t-sm);font-weight:600;color:var(--ink);
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.who__role{font-size:var(--t-xs);color:var(--muted);white-space:nowrap}
.topbar__spacer{flex:1 1 auto}
.topbar__actions{display:flex;align-items:center;gap:var(--s-2);flex:none}

/* On a phone the header has to give something up rather than overlap. The name of the
   signed-in user is the least load-bearing thing on it, and the language picker only
   needs to be wide enough for "हिंदी". */
@media (max-width:560px){
  .topbar{padding:var(--s-3) var(--s-3);gap:var(--s-2)}
  .topbar__sub{display:none}
  /* .who is NOT hidden here on purpose — it is the only thing on a phone that says which
     of the five logins you are holding, and the self-approval rule turns on that. */
  .who__role{display:none}
  .topbar__actions select{max-width:96px}
}
@media (max-width:400px){
  /* Narrower still: the dashboard link becomes its arrow alone. */
  .topbar__actions .btn span:last-child:not(:first-child){display:none}
}

.shell{flex:1 1 auto;width:100%;max-width:var(--shell-max);margin:0 auto;
  padding:var(--s-5) var(--s-4) var(--s-12)}

/* Primary navigation. A row of tabs on a wide screen; a fixed bottom bar on a
   phone, where the top of the screen is out of thumb reach. */
/* Same alignment as .topbar and .shell — see the note above. */
.nav{display:flex;gap:var(--s-1);
  padding:0 max(var(--s-4), calc((100% - var(--shell-max)) / 2 + var(--s-4)));background:var(--card);
  border-bottom:1px solid var(--line);overflow-x:auto;scrollbar-width:none;position:sticky;top:57px;z-index:35}
.nav::-webkit-scrollbar{display:none}
.nav__item{
  appearance:none;background:none;border:0;cursor:pointer;white-space:nowrap;
  padding:var(--s-3) var(--s-3) calc(var(--s-3) - 2px);margin-bottom:-1px;
  font-size:var(--t-md);font-weight:600;color:var(--muted);
  border-bottom:2.5px solid transparent;transition:color var(--fast) var(--ease);
  display:flex;align-items:center;gap:var(--s-2);min-height:var(--tap);
}
.nav__item:hover{color:var(--ink)}
.nav__item[aria-current="page"]{color:var(--ink);border-bottom-color:var(--orange)}
.nav__badge{background:var(--amber);color:#fff;font-size:var(--t-xs);font-weight:700;
  min-width:19px;height:19px;padding:0 5px;border-radius:var(--r-pill);
  display:inline-flex;align-items:center;justify-content:center;line-height:1}

.grid{display:grid;gap:var(--s-4)}
.grid--stats{grid-template-columns:repeat(auto-fit,minmax(190px,1fr))}
.grid--split{grid-template-columns:minmax(0,1.4fr) minmax(0,1fr)}
@media (max-width:860px){ .grid--split{grid-template-columns:1fr} }

.stack{display:flex;flex-direction:column;gap:var(--s-4)}
.row{display:flex;align-items:center;gap:var(--s-3);flex-wrap:wrap}
.row--tight{gap:var(--s-2)}
.row--end{justify-content:flex-end}
.row--between{justify-content:space-between}

/* -------------------------------------------------------- 4. PRIMITIVES */
.btn{
  --btn-bg:var(--card); --btn-fg:var(--ink); --btn-line:var(--line);
  appearance:none;cursor:pointer;display:inline-flex;align-items:center;justify-content:center;
  gap:var(--s-2);min-height:var(--tap);padding:0 var(--s-4);
  font-size:var(--t-md);font-weight:650;line-height:1;
  color:var(--btn-fg);background:var(--btn-bg);border:1px solid var(--btn-line);
  border-radius:var(--r-sm);
  transition:transform var(--fast) var(--ease), filter var(--fast) var(--ease),
             background var(--fast) var(--ease);
  -webkit-tap-highlight-color:transparent; user-select:none; text-decoration:none;
}
.btn:hover:not(:disabled){filter:brightness(.97)}
.btn:active:not(:disabled){transform:translateY(1px)}
.btn:disabled,.btn[aria-disabled="true"]{opacity:.5;cursor:not-allowed;transform:none;filter:none}
.btn--primary{--btn-bg:var(--orange); --btn-fg:#fff; --btn-line:var(--orange)}
.btn--accent{--btn-bg:var(--sky-deep); --btn-fg:#fff; --btn-line:var(--sky-deep)}
.btn--good{--btn-bg:var(--green); --btn-fg:#fff; --btn-line:var(--green)}
.btn--danger{--btn-bg:var(--red); --btn-fg:#fff; --btn-line:var(--red)}
.btn--quiet{--btn-bg:transparent; --btn-line:transparent; --btn-fg:var(--slate)}
.btn--quiet:hover:not(:disabled){--btn-bg:var(--line-soft)}
.btn--sm{min-height:34px;padding:0 var(--s-3);font-size:var(--t-sm)}
.btn--lg{min-height:56px;padding:0 var(--s-6);font-size:var(--t-lg)}
.btn--block{width:100%}
.btn--icon{padding:0;width:var(--tap);flex:none}

/* A button that is working. The label stays (so the width does not jump) and a
   spinner replaces the icon slot; aria-busy tells a screen reader. */
.btn[aria-busy="true"]{pointer-events:none;opacity:.75}
.spinner{width:15px;height:15px;flex:none;border-radius:50%;
  border:2px solid currentColor;border-top-color:transparent;animation:spin .7s linear infinite}
@keyframes spin{to{transform:rotate(360deg)}}

/* The big capture buttons — the three ways a bill gets in. */
.capture{display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:var(--s-3)}
.capture__btn{
  display:flex;flex-direction:column;align-items:center;justify-content:center;gap:var(--s-2);
  min-height:112px;padding:var(--s-4);cursor:pointer;text-align:center;
  background:var(--card);border:1.5px dashed var(--line);border-radius:var(--r);
  transition:border-color var(--fast) var(--ease), background var(--fast) var(--ease);
}
.capture__btn:hover,.capture__btn:focus-visible{border-color:var(--orange);background:var(--orange-bg)}
.capture__btn[data-drag="over"]{border-color:var(--orange);border-style:solid;background:var(--orange-bg)}
.capture__icon{font-size:28px;line-height:1}
.capture__label{font-weight:650;font-size:var(--t-md)}
.capture__hint{font-size:var(--t-xs);color:var(--muted);line-height:1.35}

/* Fields */
.field{display:flex;flex-direction:column;gap:6px;min-width:0}
.field__label{font-size:var(--t-sm);font-weight:650;color:var(--slate);display:flex;
  align-items:center;gap:var(--s-2)}
.field__req{color:var(--red);font-weight:700}
.field__hint{font-size:var(--t-xs);color:var(--muted)}
.field__error{font-size:var(--t-xs);color:var(--red);font-weight:600;display:flex;
  align-items:center;gap:5px}
.input{
  width:100%;min-height:var(--tap);padding:0 var(--s-3);
  background:var(--card);color:var(--ink);
  border:1px solid var(--line);border-radius:var(--r-sm);
  transition:border-color var(--fast) var(--ease), box-shadow var(--fast) var(--ease);
}
textarea.input{padding:var(--s-3);min-height:84px;line-height:1.5;resize:vertical}
select.input{cursor:pointer;appearance:none;padding-right:var(--s-8);
  background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8'%3E%3Cpath d='M1 1l5 5 5-5' stroke='%238B95A4' stroke-width='2' fill='none' stroke-linecap='round'/%3E%3C/svg%3E");
  background-repeat:no-repeat;background-position:right var(--s-3) center}
.input:hover{border-color:var(--faint)}
.input:focus{border-color:var(--sky);box-shadow:0 0 0 3px var(--sky-bg);outline:none}
.input[aria-invalid="true"]{border-color:var(--red)}
.input[aria-invalid="true"]:focus{box-shadow:0 0 0 3px var(--red-bg)}
.input:disabled{background:var(--line-soft);color:var(--muted);cursor:not-allowed}
/* A money field: right-aligned, tabular so digits line up, and a rupee sign that
   is part of the field rather than the value. */
.money{position:relative;display:flex;align-items:center}
.money__sign{position:absolute;left:var(--s-3);color:var(--muted);font-weight:600;pointer-events:none}
.money .input{padding-left:26px;text-align:right;font-variant-numeric:tabular-nums;
  font-weight:650;letter-spacing:-.2px}

.check{display:flex;align-items:flex-start;gap:var(--s-3);cursor:pointer;
  padding:var(--s-3);border:1px solid var(--line);border-radius:var(--r-sm);min-height:var(--tap)}
.check:hover{background:var(--line-soft)}
.check input{width:20px;height:20px;flex:none;margin-top:1px;accent-color:var(--orange)}
.check__text{font-size:var(--t-sm);line-height:1.45}

/* Status pills. Colour is never the only signal — each carries a glyph too, for
   the 1-in-12 men who cannot separate the green from the amber. */
.pill{display:inline-flex;align-items:center;gap:5px;padding:3px 9px;border-radius:var(--r-pill);
  font-size:var(--t-xs);font-weight:700;letter-spacing:.1px;white-space:nowrap;
  border:1px solid transparent}
.pill--paid{background:var(--green-bg);color:var(--green);border-color:var(--green-line)}
.pill--due{background:var(--blue-bg);color:var(--blue);border-color:var(--blue-line)}
.pill--review{background:var(--amber-bg);color:var(--amber);border-color:var(--amber-line)}
.pill--rejected{background:var(--red-bg);color:var(--red);border-color:var(--red-line)}
.pill--neutral{background:var(--line-soft);color:var(--slate);border-color:var(--line)}

/* ---------------------------------------------------------- 5. SURFACES */
.card{background:var(--card);border:1px solid var(--line);border-radius:var(--r);
  box-shadow:var(--shadow-sm)}
.card__head{display:flex;align-items:center;gap:var(--s-3);padding:var(--s-4) var(--s-4) 0;
  flex-wrap:wrap}
.card__title{font-size:var(--t-md);font-weight:700;letter-spacing:-.1px}
.card__sub{font-size:var(--t-xs);color:var(--muted)}
.card__body{padding:var(--s-4)}
.card__foot{padding:var(--s-3) var(--s-4);border-top:1px solid var(--line-soft);
  display:flex;gap:var(--s-2);align-items:center;flex-wrap:wrap}

.stat{background:var(--card);border:1px solid var(--line);border-radius:var(--r);
  padding:var(--s-4);box-shadow:var(--shadow-sm);display:flex;flex-direction:column;gap:5px}
.stat__label{font-size:var(--t-xs);color:var(--muted);font-weight:650;text-transform:uppercase;
  letter-spacing:.4px}
.stat__value{font-size:var(--t-2xl);font-weight:750;letter-spacing:-.8px;
  font-variant-numeric:tabular-nums;line-height:1.15}
.stat__foot{font-size:var(--t-xs);color:var(--slate)}
.stat--alert{border-color:var(--amber-line);background:var(--amber-bg)}
.stat--alert .stat__value,.stat--alert .stat__label,.stat--alert .stat__foot{color:var(--amber)}

/* Sheet — a dialog on a wide screen, a bottom sheet on a phone. One component;
   the difference is a media query, not a second implementation. */
.scrim{position:fixed;inset:0;background:var(--overlay);z-index:60;
  animation:fade var(--base) var(--ease);backdrop-filter:blur(2px)}
@keyframes fade{from{opacity:0}}
.sheet{
  position:fixed;z-index:61;background:var(--card);box-shadow:var(--shadow-lg);
  display:flex;flex-direction:column;
  left:50%;top:50%;transform:translate(-50%,-50%);
  width:min(640px,calc(100vw - var(--s-8)));max-height:min(86vh,880px);
  border-radius:var(--r-lg);border:1px solid var(--line);
  animation:pop var(--base) var(--ease);
}
@keyframes pop{from{opacity:0;transform:translate(-50%,-46%) scale(.97)}}
.sheet--wide{width:min(940px,calc(100vw - var(--s-8)))}
.sheet__head{display:flex;align-items:center;gap:var(--s-3);padding:var(--s-4);
  border-bottom:1px solid var(--line);flex:none}
.sheet__title{font-size:var(--t-lg);font-weight:700;letter-spacing:-.3px;flex:1 1 auto;min-width:0}
.sheet__body{padding:var(--s-4);overflow-y:auto;flex:1 1 auto;-webkit-overflow-scrolling:touch}
.sheet__foot{padding:var(--s-4);border-top:1px solid var(--line);display:flex;gap:var(--s-2);
  justify-content:flex-end;flex:none;flex-wrap:wrap}
@media (max-width:640px){
  .sheet{left:0;top:auto;bottom:0;transform:none;width:100vw;max-width:100vw;
    max-height:92dvh;border-radius:var(--r-lg) var(--r-lg) 0 0;animation:rise var(--base) var(--ease)}
  .sheet__foot{padding-bottom:calc(var(--s-4) + env(safe-area-inset-bottom))}
  .sheet__foot .btn{flex:1 1 auto}
}
@keyframes rise{from{transform:translateY(14%);opacity:.6}}

/* A STACK OF BUTTONS IN A SHEET'S FOOTER SPANS THE SHEET. Owner, 2026-09-23, on the
   Inquiries app's order sheet: *"Can these be on the left side rather tha right.. make
   it look more professional"*. `.sheet__foot` right-aligns its children — right for two
   buttons side by side in a dialog — and a sheet that hands it ONE wrapper of stacked
   buttons (`div.stack`: the order sheet, the advance sheet) got that wrapper sized to its
   longest label and pushed against the right edge, every button in it half the width of
   the phone. Buttons placed straight in the footer are untouched. The gap tightens to
   the footer's own: 16px between stacked buttons read as unrelated islands. */
.sheet__foot > .stack{flex:1 1 100%;min-width:0;gap:var(--s-2)}

/* THE ACTION LIST — the rest of a sheet's actions as one tidy menu, under its main
   button (same request). Left-aligned rows, one divider between them, a › that says
   "this opens something": how a phone's own settings lists read, and a sheet of six
   same-sized outlined buttons did not. Rows are ordinary .btn, so busy/disabled work. */
.actlist{display:flex;flex-direction:column;background:var(--card);
  border:1px solid var(--line);border-radius:var(--r);overflow:hidden}
.actlist > .btn{width:100%;justify-content:flex-start;text-align:left;min-height:52px;
  border:0;border-radius:0;border-bottom:1px solid var(--line-soft);font-weight:600}
.actlist > .btn:last-child{border-bottom:0}
.actlist > .btn::after{content:"\203A";margin-left:auto;padding-left:var(--s-3);
  color:var(--faint);font-size:22px;font-weight:400;line-height:1}
.actlist > .btn:active:not(:disabled){transform:none;background:var(--line-soft)}

/* Toasts. aria-live region; they stack and never cover the primary action. */
.toasts{position:fixed;z-index:80;left:50%;transform:translateX(-50%);
  bottom:calc(var(--s-5) + env(safe-area-inset-bottom));
  display:flex;flex-direction:column;gap:var(--s-2);width:min(460px,calc(100vw - var(--s-6)));
  pointer-events:none}
.toast{display:flex;align-items:flex-start;gap:var(--s-3);padding:var(--s-3) var(--s-4);
  border-radius:var(--r-sm);box-shadow:var(--shadow-lg);background:var(--ink);color:#fff;
  font-size:var(--t-sm);font-weight:600;pointer-events:auto;animation:rise var(--base) var(--ease)}
.toast--good{background:var(--green)}
.toast--bad{background:var(--red)}
.toast__x{margin-left:auto;background:none;border:0;color:inherit;opacity:.7;cursor:pointer;
  font-size:16px;line-height:1;padding:2px 4px}
.toast__x:hover{opacity:1}

.banner{display:flex;gap:var(--s-3);padding:var(--s-3) var(--s-4);border-radius:var(--r-sm);
  font-size:var(--t-sm);line-height:1.45;border:1px solid transparent;align-items:flex-start}
.banner--info{background:var(--blue-bg);color:var(--blue);border-color:var(--blue-line)}
.banner--good{background:var(--green-bg);color:var(--green);border-color:var(--green-line)}
.banner--warn{background:var(--amber-bg);color:var(--amber);border-color:var(--amber-line)}
.banner--bad{background:var(--red-bg);color:var(--red);border-color:var(--red-line)}
.banner__icon{flex:none;font-size:16px;line-height:1.3}

/* -------------------------------------------------------------- 6. DATA */
.tbl{font-size:var(--t-sm)}
.tbl th{text-align:left;font-size:var(--t-xs);text-transform:uppercase;letter-spacing:.4px;
  color:var(--muted);font-weight:700;padding:var(--s-2) var(--s-3);border-bottom:1px solid var(--line);
  white-space:nowrap;background:var(--card);position:sticky;top:0;z-index:1}
.tbl td{padding:var(--s-3);border-bottom:1px solid var(--line-soft);vertical-align:middle}
.tbl tbody tr:last-child td{border-bottom:0}
.tbl tbody tr{cursor:pointer;transition:background var(--fast) var(--ease)}
.tbl tbody tr:hover{background:var(--line-soft)}
.tbl tbody tr:focus-visible{background:var(--sky-bg);outline:2px solid var(--sky);outline-offset:-2px}
.tbl__num{text-align:right;font-variant-numeric:tabular-nums;font-weight:650;white-space:nowrap}
.tbl__sort{background:none;border:0;padding:0;font:inherit;color:inherit;cursor:pointer;
  display:inline-flex;align-items:center;gap:4px;text-transform:inherit;letter-spacing:inherit}
.tbl__sort:hover{color:var(--ink)}
.tbl-wrap{overflow-x:auto;-webkit-overflow-scrolling:touch}

/* Under 720px the table becomes a list of cards. Nine columns of a table on a
   380px screen is not a table, it is a horizontal scroll nobody uses. */
/* A row works at ANY width — the overview uses it on a wide screen for "bills coming
   up", and the table falls back to it on a narrow one. Defining it inside the media
   query meant the wide-screen use rendered completely unstyled. */
.listrow{display:flex;gap:var(--s-3);width:100%;text-align:left;align-items:center;
  padding:var(--s-3);background:var(--card);border:1px solid var(--line);
  border-radius:var(--r-sm);min-height:var(--tap)}
.listrow[role="button"],.listrow[tabindex]{cursor:pointer}
.listrow[role="button"]:hover,.listrow[tabindex]:hover{background:var(--line-soft)}
.listrow__main{flex:1 1 auto;min-width:0;display:flex;flex-direction:column;gap:3px}
.listrow__title{font-weight:650;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.listrow__meta{font-size:var(--t-xs);color:var(--muted);display:flex;gap:var(--s-2);
  align-items:center;flex-wrap:wrap}
.listrow__num{font-variant-numeric:tabular-nums;font-weight:700;font-size:var(--t-lg);
  white-space:nowrap;flex:none;margin-left:var(--s-3)}

/* Which of the two layouts is on show is a separate decision from how either looks. */
.only-narrow{display:none}
@media (max-width:720px){
  .tbl-wrap{display:none}
  .only-narrow{display:block}
}

/* The pending card puts the bill beside the figure on a wide screen and above it on a
   phone. In CSS, not in a matchMedia read that never updates on resize. */
.pending-grid{display:grid;gap:var(--s-4);grid-template-columns:minmax(0,1fr) 180px}
@media (max-width:720px){ .pending-grid{grid-template-columns:1fr} }

/* The per-line decision control (the shop's standing rule: a decision button
   asks once, in place, and the line then leaves). */
.decide{display:flex;gap:var(--s-2);flex-wrap:wrap;align-items:center}
.decide__confirm{display:flex;gap:var(--s-2);align-items:center;flex-wrap:wrap;
  padding:var(--s-2) var(--s-3);background:var(--amber-bg);border:1px solid var(--amber-line);
  border-radius:var(--r-sm);font-size:var(--t-sm);font-weight:650;color:var(--amber)}
.line--leaving{animation:leave var(--slow) var(--ease) forwards;overflow:hidden}
@keyframes leave{to{opacity:0;transform:translateX(24px);max-height:0;padding-top:0;
  padding-bottom:0;margin-top:0;margin-bottom:0}}

/* Charts. Deliberately small and hand-drawn in SVG: a charting library would be
   larger than this entire app, and the two shapes we need are a ring and bars. */
.chart{display:flex;gap:var(--s-5);align-items:center;flex-wrap:wrap}
.legend{display:flex;flex-direction:column;gap:7px;font-size:var(--t-sm);min-width:0;flex:1 1 190px}
.legend__row{display:flex;align-items:center;gap:var(--s-2);min-width:0}
.legend__sw{width:11px;height:11px;border-radius:3px;flex:none}
.legend__name{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;flex:1 1 auto;color:var(--slate)}
.legend__val{font-variant-numeric:tabular-nums;font-weight:650;flex:none}
.bars{display:flex;align-items:flex-end;gap:var(--s-2);height:132px;width:100%}
.bars__col{flex:1 1 0;display:flex;flex-direction:column;align-items:center;gap:6px;min-width:0}
.bars__bar{width:100%;max-width:44px;background:var(--sky);border-radius:5px 5px 0 0;
  min-height:3px;transition:height var(--slow) var(--ease)}
.bars__bar--now{background:var(--orange)}
.bars__label{font-size:var(--t-xs);color:var(--muted);white-space:nowrap;overflow:hidden;
  text-overflow:ellipsis;max-width:100%}

/* Bill viewer */
.bill{position:relative;background:var(--line-soft);border:1px solid var(--line);
  border-radius:var(--r-sm);overflow:hidden;display:flex;align-items:center;justify-content:center;
  min-height:180px;cursor:zoom-in}
.bill img{width:100%;height:auto;display:block}
.bill__pdf{display:flex;flex-direction:column;align-items:center;gap:var(--s-2);
  padding:var(--s-6);color:var(--slate);font-size:var(--t-sm);text-align:center}
/* Nothing said the thumbnail was tappable, so the larger view went unfound for months. */
.bill__hint{position:absolute;right:var(--s-2);bottom:var(--s-2);pointer-events:none;
  padding:2px var(--s-2);border-radius:999px;background:rgba(15,23,42,.72);color:#fff;
  font-size:var(--t-xs);line-height:1.7;letter-spacing:.01em}

/* THE FULL-SCREEN BILL. Owner, 2026-09-17: "I am unable to zoom or download the jpeg
   or pdf file of the image." A column — a bar that stays put, and a scroller that owns
   the panning — rather than the single centred <img> this used to be, because zooming a
   letterboxed image gives you nowhere to pan to. */
.lightbox{position:fixed;inset:0;z-index:90;background:#0b1220;display:flex;
  flex-direction:column}
.lightbox__bar{flex:0 0 auto;display:flex;align-items:center;gap:var(--s-1);
  padding:var(--s-2) var(--s-2) var(--s-2) var(--s-1);background:var(--card);
  border-bottom:1px solid var(--line);padding-top:max(var(--s-2),env(safe-area-inset-top))}
.lightbox__name{flex:1 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;
  white-space:nowrap;font-size:var(--t-sm);color:var(--slate)}
.lightbox__pctbtn{border:0;background:none;padding:var(--s-1) 0;cursor:pointer;
  min-width:52px;min-height:44px}
.lightbox__pct{font-size:var(--t-xs);color:var(--muted);font-variant-numeric:tabular-nums}
/* pan-x pan-y leaves one-finger panning to the browser; two fingers are the page's,
   which is what the pinch handler in fdc-ui.js relies on. */
.lightbox__stage{flex:1 1 auto;overflow:auto;-webkit-overflow-scrolling:touch;
  touch-action:pan-x pan-y;padding:var(--s-2);padding-bottom:max(var(--s-2),env(safe-area-inset-bottom))}
.lightbox__img{display:block;width:100%;max-width:none;height:auto;margin:0 auto;
  border-radius:var(--r-sm);background:#fff}
/* The gallery's ‹ › and "2 of 5" (owner, 2026-09-25: "move back and forward to next
   image infinitly"). Over the picture's edges, big enough for a thumb. */
.lightbox__nav{position:absolute;top:50%;transform:translateY(-50%);width:48px;height:64px;
  border:0;border-radius:12px;background:rgba(15,23,42,.62);color:#fff;font-size:34px;
  line-height:1;cursor:pointer;display:grid;place-items:center;z-index:1}
.lightbox__nav--prev{left:8px}
.lightbox__nav--next{right:8px}
.lightbox__count{position:absolute;left:50%;transform:translateX(-50%);
  bottom:max(14px,env(safe-area-inset-bottom));background:rgba(15,23,42,.75);color:#fff;
  font-size:var(--t-sm);padding:4px 12px;border-radius:999px;font-variant-numeric:tabular-nums}

/* ------------------------------------------------------------ 7. STATES */
/* Skeletons mirror the SHAPE of what is loading, so the page does not jump when
   the data lands. A spinner tells you to wait; this tells you what for. */
.sk{background:linear-gradient(90deg,var(--line-soft) 25%,var(--line) 37%,var(--line-soft) 63%);
  background-size:400% 100%;animation:shimmer 1.3s ease-in-out infinite;border-radius:6px}
@keyframes shimmer{from{background-position:100% 0}to{background-position:-100% 0}}
.sk--text{height:11px;margin:5px 0}
.sk--title{height:17px;width:42%}
.sk--stat{height:30px;width:66%}
.sk--row{height:var(--tap)}
.sk--block{height:132px}

.state{display:flex;flex-direction:column;align-items:center;gap:var(--s-3);
  padding:var(--s-10) var(--s-5);text-align:center;color:var(--slate)}
.state__icon{font-size:38px;line-height:1;opacity:.65}
.state__title{font-size:var(--t-lg);font-weight:700;color:var(--ink)}
.state__body{font-size:var(--t-sm);max-width:44ch;line-height:1.55}
.state--error .state__icon{opacity:1}

/* --------------------------------------------------------- 8. UTILITIES */
.u-num{font-variant-numeric:tabular-nums}
.u-muted{color:var(--muted)}
.u-slate{color:var(--slate)}
.u-sm{font-size:var(--t-sm)}
.u-xs{font-size:var(--t-xs)}
.u-bold{font-weight:700}
.u-truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0}
.u-right{text-align:right}
.u-grow{flex:1 1 auto;min-width:0}
.u-nowrap{white-space:nowrap}
/* Long free text from a bill (a vendor name can be 90 characters) has to be
   allowed to break, or it pushes the layout sideways. */
.u-break{overflow-wrap:anywhere;word-break:break-word}

/* ── Progress: a real bar for the parts we can measure ──────────────────────
   Deliberately TWO modes. Sending the photo has a true byte count, so that bar
   is determinate and the number means something. The AI read that follows has
   no measurable progress at all, so rather than invent a percentage that
   creeps to 90% and stalls — the thing every user has learned to distrust —
   it switches to an indeterminate sweep and says what it is doing. A progress
   bar that lies is worse than no progress bar. */
.prog{display:block;width:100%}
.prog__label{display:flex;justify-content:space-between;gap:8px;font-size:12.5px;
  color:var(--slate);margin:0 0 6px}
.prog__track{position:relative;height:8px;border-radius:999px;background:var(--line-soft);
  overflow:hidden}
.prog__fill{position:absolute;inset:0 auto 0 0;width:0;border-radius:999px;
  background:var(--orange);transition:width .18s ease-out}
.prog--indeterminate .prog__fill{width:40%;animation:prog-sweep 1.15s ease-in-out infinite}
@keyframes prog-sweep{
  0%{left:-40%}
  100%{left:100%}
}
@media (prefers-reduced-motion: reduce){
  /* No sweeping bar for anyone who has asked the system for stillness. It becomes a
     calm, static band — the text beside it is what carries the meaning anyway. */
  .prog--indeterminate .prog__fill{animation:none;left:0;width:100%;opacity:.35}
  .prog__fill{transition:none}
}

/* ── Cropper: trim to the bill before it is read ────────────────────────────
   The dashboard's own help text has always told people to "crop to the bill
   first on a photo", which meant leaving the app to do it. Doing it here makes
   the instruction actionable — and a tighter crop measurably helps the read,
   because the model stops spending attention on a table top. */
.crop{--handle:28px;position:relative;touch-action:none;user-select:none;
  background:var(--ink);border-radius:var(--r-md);overflow:hidden;display:block}
.crop__img{display:block;width:100%;height:auto;pointer-events:none}
.crop__shade{position:absolute;inset:0;background:rgba(12,16,22,.55);pointer-events:none}
.crop__win{position:absolute;box-shadow:0 0 0 9999px rgba(12,16,22,.55);
  border:2px solid #fff;border-radius:4px;cursor:move}
.crop__grid{position:absolute;inset:0;pointer-events:none;
  background:
    linear-gradient(to right,transparent 33.33%,rgba(255,255,255,.35) 33.33%,rgba(255,255,255,.35) calc(33.33% + 1px),transparent calc(33.33% + 1px)),
    linear-gradient(to right,transparent 66.66%,rgba(255,255,255,.35) 66.66%,rgba(255,255,255,.35) calc(66.66% + 1px),transparent calc(66.66% + 1px)),
    linear-gradient(to bottom,transparent 33.33%,rgba(255,255,255,.35) 33.33%,rgba(255,255,255,.35) calc(33.33% + 1px),transparent calc(33.33% + 1px)),
    linear-gradient(to bottom,transparent 66.66%,rgba(255,255,255,.35) 66.66%,rgba(255,255,255,.35) calc(66.66% + 1px),transparent calc(66.66% + 1px))}
/* Handles are 28px because this is used one-handed on a phone, often by someone
   holding the paper bill in the other hand. Anything smaller cannot be grabbed. */
.crop__h{position:absolute;width:var(--handle);height:var(--handle);border-radius:50%;
  background:#fff;box-shadow:0 1px 4px rgba(0,0,0,.45);touch-action:none}
.crop__h--nw{left:calc(var(--handle) / -2);top:calc(var(--handle) / -2);cursor:nwse-resize}
.crop__h--ne{right:calc(var(--handle) / -2);top:calc(var(--handle) / -2);cursor:nesw-resize}
.crop__h--sw{left:calc(var(--handle) / -2);bottom:calc(var(--handle) / -2);cursor:nesw-resize}
.crop__h--se{right:calc(var(--handle) / -2);bottom:calc(var(--handle) / -2);cursor:nwse-resize}

/* The bee, working, while the readers read — the shop's own mark, animated the way the
   dashboard's scanner does it. Not decoration: a 20-second wait with nothing moving is
   indistinguishable from a hang, and this is the one screen where people wait. */
.prog__bee{display:flex; justify-content:center; padding:6px 0 12px}
.prog__bee img{width:64px; height:64px; display:block; animation:bee-hover 2.6s ease-in-out infinite}
@keyframes bee-hover{
  0%,100%{transform:translateY(0) rotate(-3deg)}
  50%{transform:translateY(-9px) rotate(3deg)}
}
/* Landed. The bar turns from the working orange to the settled green, and the shine stops. */
.prog--done .prog__fill{background:var(--green)}
.prog--done .prog__fill::after{display:none}
/* A shine travelling the filled portion, so a slow read still looks alive. */
.prog__fill::after{content:""; position:absolute; inset:0;
  background:linear-gradient(90deg,transparent,rgba(255,255,255,.55),transparent);
  transform:translateX(-100%); animation:prog-shine 1.15s linear infinite}
.prog--done .prog__label{color:var(--green)}
@keyframes prog-shine{to{transform:translateX(100%)}}
@media (prefers-reduced-motion: reduce){
  .prog__bee img{animation:none}
  .prog__fill::after{animation:none; display:none}
}

/* ── Check this one: per-field confirmation ────────────────────────────────
   Owner rule: mark what could not be read, and let the person settle it one
   field at a time — green tick if the figure is right, edit if it is not.
   The same shape as the decision rows elsewhere in the app: confirm per line,
   on tap, and the doubt disappears when it is answered.

   The caution is on the FIELD, not in a list above it. A warning that names a
   field the reader then has to go and find is a warning most people skip. */
.checkme{display:block; border-radius:10px; padding:11px 12px 12px;
  border:1px solid var(--amber-line); background:var(--amber-bg)}
.checkme--settled{border-color:var(--green-line); background:var(--green-bg)}
.checkme--edited{border-color:var(--blue-line); background:var(--blue-bg)}
.checkme__head{display:flex; align-items:flex-start; gap:8px; margin:0 0 9px}
.checkme__mark{flex:none; font-size:15px; line-height:1.35}
.checkme__why{flex:1; min-width:0; font-size:13px; line-height:1.5; color:var(--amber)}
.checkme--settled .checkme__why{color:var(--green)}
.checkme--edited .checkme__why{color:var(--blue)}
.checkme__actions{display:flex; gap:8px; flex-wrap:wrap; margin-top:10px}
/* Big enough for a thumb on a phone held in one hand while the other holds the bill. */
.checkme__actions .btn{min-height:40px}

/* ── The customer's sizes ──────────────────────────────────────────────────
   Owner ask 2026-09-10: the measurements shown in the Orders app and on the
   enquiry, not only on the ERP screen. Rendered by measurements.js, which the
   Orders app and the Enquiry app both import — one table, so the two cannot
   drift apart the way two copies of a list always do.

   A GRID, not a table: the number, the name and the two units are four columns
   that must line up down the page, and on a 320px phone a real <table> either
   scrolls sideways or squeezes the name to nothing. `1fr` on the label lets it
   be the part that gives, because "Shoulder" wrapping is survivable and "63.5"
   wrapping is not.

   Centimetres are the shop's unit and read first; inches are dimmer and second,
   for the customer who measured in inches and is checking their own number. */
/* THE CHILD, NOT THE GARMENT (owner, 2026-09-11). Above every list of sizes, on
   screen and on paper alike — a parent who measured a costume the child owns and a
   tailor who cuts to the body exactly make opposite mistakes from the same numbers,
   and neither is visible in "77cm". Quiet green rather than a warning banner: it is
   a standing fact about what these numbers ARE, not an alert about this one row. */
.msz__what{font-size:var(--t-sm); color:#1D9E75; font-weight:600; margin-bottom:6px}
.msz{display:grid; grid-template-columns:auto 1fr auto auto; gap:2px 10px;
  align-items:baseline; font-size:14px}
.msz__row{display:contents}
/* The same 1-15 the customer saw beside the figure on their own form, so a
   phone call about "number 6" means one thing in both places. */
.msz__no{font-size:11px; font-weight:700; color:var(--muted); text-align:right;
  min-width:1.4em; font-variant-numeric:tabular-nums}
.msz__label{min-width:0; overflow-wrap:break-word; color:var(--ink)}
/* Inches lead (owner ruling 2026-09-10: "i want primary be in inches than cm").
   The shop's tape is an inch tape and the customer says "twenty-eight chest";
   cm is what the database stores and stays beside it, small, for a metric
   pattern. Tabular figures on both so the decimal points line up down the page —
   a column of measurements that does not align is a column that gets misread. */
.msz__in{font-weight:700; font-size:15px; font-variant-numeric:tabular-nums;
  white-space:nowrap}
.msz__cm{font-size:12px; color:var(--muted); font-variant-numeric:tabular-nums;
  white-space:nowrap}
.msz__row + .msz__row .msz__no,
.msz__row + .msz__row .msz__label,
.msz__row + .msz__row .msz__cm,
.msz__row + .msz__row .msz__in{padding-top:5px; border-top:1px solid var(--line)}

/* The one-line answer on a card: are the sizes in or not. Colour is never the
   only signal — the words say it too — for the same reason the dispatch bands
   print "Late" rather than trusting the red. */
.szline{display:inline-flex; align-items:center; gap:5px; font-size:12px;
  font-weight:700; border-radius:999px; padding:2px 9px; white-space:nowrap}
.szline--yes{background:var(--green-bg); color:var(--green); border:1px solid var(--green-line)}
.szline--no{background:var(--amber-bg); color:var(--amber); border:1px solid var(--amber-line)}

/* The measuring figure beside a set of sizes. Capped rather than full-width: at
   390px an uncapped figure is 15 numbered discs on a body the size of a thumb,
   and the list underneath is the part being read anyway. */
.mfig{display:flex; justify-content:center; margin:2px 0 10px}
.mfig svg{width:100%; max-width:190px; height:auto; max-height:44vh}

/* ── the printed measuring sheet ───────────────────────────────────────────
   Owner ask 2026-09-10: "it should be printable and should have order number on
   the top with a qr of the order number". The sheet is printed, pinned to the
   cloth and carried round the workshop; the square is how it says which order it
   belonged to without anybody re-typing thirty characters off a creased page. */
.msheet{display:flex; align-items:center; justify-content:space-between; gap:12px;
  padding:10px 12px; border:1px solid var(--line); border-radius:10px;
  background:var(--card)}
.msheet__label{font-size:11px; font-weight:700; letter-spacing:.06em;
  text-transform:uppercase; color:var(--muted)}
/* The number is the heading of the page, so it is set as one — and it breaks
   anywhere, because a 30-character order number has no spaces in it and would
   otherwise push the QR off the paper. */
.msheet__no{font-family:var(--mono); font-size:15px; font-weight:700; line-height:1.25;
  overflow-wrap:anywhere; margin-top:1px}
.msheet__who{font-size:13px; color:var(--slate); margin-top:2px}
.msheet__qr{flex:none; width:88px; height:88px; image-rendering:pixelated}

/* ── printing ──────────────────────────────────────────────────────────────
   Owner ask 2026-09-10: "it should be printable".

   ONE PATH FOR BOTH PLACES. The sizes are read in two shapes — a modal sheet in
   the Orders app and an inline card on the enquiry — and printing "whatever is on
   screen" would need two sets of rules that drift. Instead `printSizes()` clones
   the block into `#fdc-print-root` at the top of the document and marks <html>;
   from here it is always the same node in the same place.

   Cloned rather than moved: moving it would tear the live card out of the page
   the person is still looking at, and putting it back after a print dialog the
   browser may never report closing is not something to rely on. */
html.fdc-printing > body > *{display:none !important}
html.fdc-printing > body > #fdc-print-root{display:block !important}
#fdc-print-root{display:none}

@media print{
  /* A card must not be split down the middle by a page break — half a person's
     measurements at the bottom of page one is how the wrong sizes get cut. */
  #fdc-print-root .card{break-inside:avoid; page-break-inside:avoid;
    box-shadow:none !important; border:1px solid #999 !important}
  #fdc-print-root .msheet{break-after:avoid; page-break-after:avoid;
    border:1px solid #999 !important}
  /* No screen height cap on paper, and the figure is set to a printable width
     rather than a share of a viewport that no longer exists. */
  #fdc-print-root .mfig svg{max-height:none !important; max-width:150px !important}
  /* Backgrounds are not printed by default in most browsers, so a flagged banner
     would come out as invisible text on white. Keep the ink instead. */
  #fdc-print-root .banner{border:1px solid #999 !important; color:#000 !important}
  /* Anything the screen wants and the paper does not — the line repeating the
     customer's name above a heading that already carries it, and the buttons. */
  .u-noprint, #fdc-print-root .btn{display:none !important}
}
