/* ============================================================================
   Docolect - styles.css
   Document collection and reconciliation portal for Portuguese certified
   accountants. Clickable prototype, no build step, runs from file://.

   Rules this file keeps:
   - Ring first (box-shadow: 0 0 0 1px var(--line)). Real shadow only on the
     dev toggle, the modal and the toast.
   - --accent is fill / progress / focus / wash. Never white text on it.
     Primary buttons use --accent-deep.
   - "Missing" carries no colour. Hollow spine in --line.
   - Bricolage Grotesque only at large sizes. Never inside a table.
   - Containers never carry padding. Only their __head / __body / __foot do,
     so .card and .panel can never fight over the same box.
   ========================================================================== */


/* === TOKENS ============================================================== */

:root{
  /* core six, all lifted from uniplaces.com */
  --ink:#224251;          /* text, table numbers */
  --slate:#5A6B77;        /* labels, secondary text. Uniplaces ships #647A85, which is
                             exactly 4.50 on pure white and fails on every ground this app
                             actually uses (canvas 4.15, field 4.28, ok-wash 4.02).
                             Darkened one step, same hue family: 5.09 / 5.15 / 4.92. */
  --line:#BCC6CA;         /* hairlines and rings */
  --accent:#00ADEF;       /* fills, progress, focus ring, washes. WHITE-LABEL VARIABLE */
  --accent-deep:#005879;  /* accent text, primary buttons */
  --wash:#ECF7FC;         /* selected and active surfaces */

  /* surfaces */
  --paper:#FFFFFF;
  --canvas:#F4F6F7;       /* accountant canvas, cool */
  --field:#F0FBFF;        /* client portal field, pale cyan. NEVER cream */
  --seg-well:#D9EEF7;     /* the switch's ground, a step darker than --field (D-137) */
  --seg-pill:var(--field); /* the pill behind the pressed option: the page's own blue */

  /* status */
  --ok:#16785F;      --ok-wash:#E8F5EF;    /* matched, complete */
  --soon:#8F5410;    --soon-wash:#FBF0E0;  /* deadline approaching, needs review.
                             Was #B4553A, a red-orange only dE 15 from --late, so overdue
                             and approaching read as one colour in a 3px spine. Moved to
                             amber so red means overdue and nothing else. 5.9 on white. */
  --late:#B3352C;    --late-wash:#FBE9E7;  /* genuinely overdue only */
  /* A field that refuses what was typed (docs/M19P-DATE-ERROR-PLAN.md D1).
     Amber, like the other refusals (.settings__msg--bad, .toast--warn), so
     red keeps meaning overdue and nothing else. Change it here, two lines. */
  --bad:var(--soon); --bad-wash:var(--soon-wash);
  /* "missing" has NO colour. hollow spine in --line. this is deliberate. */

  /* shape */
  --r-chip:4px; --r-ctl:8px; --r-card:12px; --r-soft:18px; --r-pill:999px;

  /* elevation. ring first, shadow is a whisper */
  --ring:0 0 0 1px var(--line);
  --lift:0 3px 10px rgba(34,66,81,.10);
  --lift-lg:0 10px 34px rgba(34,66,81,.16);

  /* type scale */
  --t-xs:11px; --t-sm:12.5px; --t-base:14px; --t-md:15px;
  --t-lg:18px; --t-xl:23px; --t-2xl:30px; --t-3xl:44px;

  /* rhythm, 4px base, 20/30 from their --gap */
  --s1:4px; --s2:8px; --s3:12px; --s4:16px; --s5:20px; --s6:30px; --s7:44px;
}

/* Non-colour helpers. Not part of the spec token set, added so the two font
   stacks and the two fixed layout widths are stated once. */
:root{
  --f-display:'Bricolage Grotesque', 'Instrument Sans', system-ui, sans-serif;
  --f-body:'Instrument Sans', system-ui, -apple-system, sans-serif;
  --f-mono:ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  --rail-w:232px;
  --cli-w:560px;
  --ease-out:cubic-bezier(.22,1,.36,1);
  --ease-back:cubic-bezier(.34,1.56,.64,1);
  /* The approved switch's spring (D-137): motion-primitives AnimatedBackground,
     bounce .2 over .3 s. */
  --spring:cubic-bezier(.3,1.35,.5,1);
  /* room for a 2.5px outline at 2px offset inside a scroll container */
  --focus-pad:6px;
}


/* === RESET AND BASE ====================================================== */

*,*::before,*::after{box-sizing:border-box;}

html{-webkit-text-size-adjust:100%;}

body{
  margin:0;
  background:var(--canvas);
  color:var(--ink);
  font-family:var(--f-body);
  font-size:var(--t-base);
  line-height:1.5;
  font-variant-numeric:tabular-nums;
  -webkit-font-smoothing:antialiased;
  -moz-osx-font-smoothing:grayscale;
}

[hidden]{display:none !important;}

img,svg{max-width:100%;}
svg{display:block;}

button,input,select,textarea{font-family:inherit; color:inherit;}
button{cursor:pointer;}

a{color:var(--accent-deep); text-decoration-thickness:1px; text-underline-offset:2px;}

ul,ol{margin:0; padding:0; list-style:none;}
p,h1,h2,h3,h4,dl,dd{margin:0;}

/* Focus is always visible. Nothing in this file puts overflow:hidden on an
   ancestor of a focusable element. The three scroll containers that exist
   (.clist, .recon__table, .rail on mobile) each pad by --focus-pad. */
/* --accent-deep, not --accent. A focus indicator is non-text content and needs
   3:1 (WCAG 1.4.11); --accent is 2.55:1 on paper and 2.34:1 on --wash. It also
   matters more here than on the marketing site, because --accent is the
   white-label variable: a client could set a pale brand colour and erase the
   focus ring entirely. --accent-deep is derived to clear contrast, so it holds
   whatever accent is chosen. */
:focus-visible{outline:2.5px solid var(--accent-deep); outline-offset:2px;}
:focus:not(:focus-visible){outline:none;}

::selection{background:var(--wash); color:var(--accent-deep);}

.sr-only{
  position:absolute; width:1px; height:1px; padding:0; margin:-1px;
  overflow:hidden; clip:rect(0 0 0 0); clip-path:inset(50%); white-space:nowrap; border:0;
}


/* === TYPOGRAPHY ========================================================== */

/* Bricolage Grotesque only at large sizes: page titles, the big counts,
   the client greeting, the completion state. Never inside a table. */
.page__title,
.summary__val,
.celebrate__title{
  font-family:var(--f-display);
  font-weight:650;
  letter-spacing:-.02em;
  text-wrap:balance;
}

/* Every numeric surface is explicitly tabular. */
.crow__nif,.crow__count,.crow__deadline,.crow__days,.crow__legal,
.drow__nif,.drow__date,.drow__base,.drow__vat,.drow__total,
.idcard__meta,.summary__val,
.tl__when,.wstep__num,.ring__label,.parsed__summary{
  font-variant-numeric:tabular-nums;
  font-feature-settings:"tnum" 1;
}


/* === SHARED COMPONENTS =================================================== */

/* ---- status spine, the signature element --------------------------------
   Same object in the client list, the reconciliation table and the missing
   list. State reads down the leading edge with no text. */

.spine-host{position:relative;}

.spine-host::before{
  content:"";
  position:absolute;
  left:0; top:0; bottom:0;
  width:3px;
  border-radius:var(--r-card) 0 0 var(--r-card);
  background:var(--line);
  transition:background .25s ease;
}

.spine-host.s-ok::before{background:var(--ok);}
.spine-host.s-none::before{background:var(--line);}
.spine-host.s-soon::before{background:var(--soon);}
.spine-host.s-late::before{background:var(--late);}

/* ---- buttons ------------------------------------------------------------ */

.btn{
  display:inline-flex; align-items:center; justify-content:center;
  gap:var(--s2);
  font:inherit; font-size:var(--t-base); font-weight:600; line-height:1.2;
  padding:10px 18px;
  border:0; border-radius:var(--r-ctl);
  background:var(--paper); color:var(--ink);
  box-shadow:var(--ring);
  text-decoration:none; white-space:nowrap;
  -webkit-appearance:none; appearance:none;
  transition:background .18s ease, color .18s ease, box-shadow .18s ease,
             filter .18s ease, transform .12s ease;
}
.btn:hover{background:var(--wash); color:var(--accent-deep);}
.btn:active{transform:translateY(1px);}
.btn[disabled],.btn[aria-disabled="true"]{opacity:.45; pointer-events:none;}
.btn svg{width:16px; height:16px; flex:none;}

.btn--primary{background:var(--accent-deep); color:var(--paper); box-shadow:none;}
.btn--primary:hover{background:var(--accent-deep); color:var(--paper); filter:brightness(1.14);}

.btn--ghost{background:transparent; color:var(--slate); font-weight:500; box-shadow:var(--ring);}
.btn--ghost:hover{background:var(--wash); color:var(--accent-deep);}

.btn--quiet{background:transparent; color:var(--slate); font-weight:500; box-shadow:none; padding-inline:var(--s3);}
.btn--quiet:hover{background:var(--wash); color:var(--accent-deep);}

.btn--sm{padding:6px 12px; font-size:var(--t-sm); gap:6px;}
.btn--sm svg{width:14px; height:14px;}

.btn--block{display:flex; width:100%;}

/* ---- tags --------------------------------------------------------------- */

.tag{
  display:inline-flex; align-items:center; gap:5px;
  padding:3px 9px;
  border-radius:var(--r-pill);
  font-size:var(--t-xs); font-weight:600; letter-spacing:.02em; line-height:1.5;
  white-space:nowrap;
  background:var(--canvas); color:var(--slate);
  box-shadow:var(--ring);
}
.tag svg{width:11px; height:11px; flex:none;}

.tag--ok{background:var(--ok-wash); color:var(--ok); box-shadow:none;}
.tag--none{background:transparent; color:var(--slate); box-shadow:var(--ring);}
.tag--soon{background:var(--soon-wash); color:var(--soon); box-shadow:none;}
.tag--late{background:var(--late-wash); color:var(--late); box-shadow:none;}

/* ---- chips (filters) ---------------------------------------------------- */

.chip{
  display:inline-flex; align-items:center; gap:6px;
  padding:6px 13px;
  border:0; border-radius:var(--r-pill);
  font:inherit; font-size:var(--t-sm); font-weight:600; line-height:1.3;
  white-space:nowrap;
  background:var(--paper); color:var(--slate);
  box-shadow:var(--ring);
  -webkit-appearance:none; appearance:none;
  transition:background .16s ease, color .16s ease, box-shadow .16s ease;
}
.chip:hover{color:var(--ink);}
.chip--on{background:var(--wash); color:var(--accent-deep); box-shadow:0 0 0 1.5px var(--accent);}
.chip--on:hover{color:var(--accent-deep);}

/* ---- fields and controls ------------------------------------------------ */

.field{display:flex; flex-direction:column; gap:6px; min-width:0;}
.field__label{
  font-size:var(--t-xs); font-weight:600; letter-spacing:.08em;
  text-transform:uppercase; color:var(--slate);
}
.field__hint{font-size:var(--t-sm); color:var(--slate); line-height:1.45;}
/* A link that leaves the app in a new tab (the AT agenda, D-114): the glyph
   says so, and an sr-only phrase says it to a screen reader. */
.extlink{color:var(--accent-deep); text-decoration:underline; text-underline-offset:2px; white-space:nowrap;}
.extlink svg{display:inline-block; vertical-align:-1px; margin-left:3px;}
.field--half{flex:1 1 220px;}
.field--row{
  flex-direction:row; align-items:center; justify-content:space-between;
  gap:var(--s4); flex-wrap:wrap;
}
.field--row .field__label{flex:1 1 auto;}

.input,.select,.textarea{
  width:100%; min-width:0;
  font:inherit; font-size:var(--t-base); line-height:1.4;
  color:var(--ink); background:var(--paper);
  border:0; border-radius:var(--r-ctl);
  box-shadow:var(--ring);
  padding:9px 12px;
  transition:box-shadow .16s ease, background .16s ease;
}
.input:hover,.select:hover,.textarea:hover{box-shadow:0 0 0 1px var(--slate);}
/* An invalid typed date (M19P.6, D2): the ring turns --bad and stays so on
   hover and focus, so the state never flickers away under the pointer. */
.input[aria-invalid="true"],
.input[aria-invalid="true"]:hover,
.input[aria-invalid="true"]:focus-visible{box-shadow:0 0 0 1.5px var(--bad);}
/* The line under it says why, with an icon, so it is not colour alone
   (WCAG 1.4.1). In the page from the start and toggled by datepicker.js. */
.field__error{font-size:var(--t-sm); color:var(--bad); line-height:1.45;}
.field__error svg{display:inline-block; vertical-align:-2px; margin-right:5px;}
.input::placeholder,.textarea::placeholder{color:var(--slate); opacity:.8;}
.textarea{min-height:96px; resize:vertical; line-height:1.55;}
.select{padding-block:8px;}

.switch{
  position:relative; flex:none;
  display:inline-flex; align-items:center;
  width:44px; height:26px; padding:3px;
  border:0; border-radius:var(--r-pill);
  background:color-mix(in srgb, var(--line) 62%, transparent);
  box-shadow:var(--ring);
  -webkit-appearance:none; appearance:none;
  transition:background .2s ease;
}
.switch__knob{
  width:20px; height:20px; border-radius:var(--r-pill);
  background:var(--paper); box-shadow:var(--lift);
  transition:transform .22s var(--ease-out);
}
/* Styled on `:checked` rather than a server-rendered class (R-034, F6):
   the picked state has to follow the click, not the last save. Before this,
   pressing the toggle changed the hidden checkbox and moved nothing, which
   read as a dead control. */
.switch:has(input:checked){background:var(--accent);}
.switch:has(input:checked) .switch__knob{transform:translateX(18px);}
/* A switch and its sentence on one line (S4: Novo cliente's reminders box,
   A2's reminders toggle, the Lembretes card). */
.check-row{display:inline-flex; align-items:center; gap:10px; cursor:pointer;}
/* A switch that only shows a saved state (A2's toggle is a POST button, so
   there is no checkbox inside it to follow). */
.switch--on{background:var(--accent);}
.switch--on .switch__knob{transform:translateX(18px);}
.check-row:has(input:disabled){cursor:default; opacity:.6;}

/* ---- card ---------------------------------------------------------------
   The container itself never carries padding. Only its parts do. */

.card{
  background:var(--paper);
  border-radius:var(--r-card);
  box-shadow:var(--ring);
  display:flex; flex-direction:column;
  min-width:0;
}
.card__head{
  display:flex; align-items:center; justify-content:space-between;
  gap:var(--s3); flex-wrap:wrap;
  padding:var(--s4) var(--s5);
  border-bottom:1px solid var(--line);
}
.card__title{font-size:var(--t-md); font-weight:650; letter-spacing:-.01em; line-height:1.3;}
.card__body{padding:var(--s5); display:flex; flex-direction:column; gap:var(--s4); min-width:0;}
.card__foot{
  display:flex; align-items:center; justify-content:flex-end;
  gap:var(--s3); flex-wrap:wrap;
  padding:var(--s3) var(--s5) var(--s4);
  border-top:1px solid var(--line);
}

/* ---- toast (one of the three real shadows) ------------------------------ */

.toast{
  position:fixed; z-index:80;
  left:50%; bottom:var(--s5);
  transform:translate(-50%,14px);
  opacity:0; pointer-events:none;
  max-width:min(440px, calc(100vw - 2 * var(--s4)));
  display:flex; align-items:center; gap:var(--s3);
  padding:12px 18px;
  border-radius:var(--r-ctl);
  background:var(--paper); color:var(--ink);
  font-size:var(--t-base); font-weight:500; line-height:1.4;
  box-shadow:var(--lift-lg), var(--ring);
  transition:opacity .22s ease, transform .3s var(--ease-out);
}
.toast svg{width:16px; height:16px; flex:none;}
.toast--ok{color:var(--ok);}
.toast--info{color:var(--accent-deep);}
.toast--warn{color:var(--soon);}
.toast--on{opacity:1; transform:translate(-50%,0); pointer-events:auto;}

/* ---- the client's outcome toast (D-088) ---------------------------------
   A second toast, and deliberately not the one above: that one is the
   dashboard's, single, centred and dismissed by the next action. This one
   stacks bottom-right, carries a title and an instruction, and leaves on its
   own after twelve seconds because a client with five bad scans would otherwise
   have five things to dismiss.

   No retry button. They send another file by sending another file, and a
   "retry" control would promise a retry the pipeline cannot honour: what was
   refused was refused on the bytes, and the same bytes refuse again.

   The stack ignores the pointer so the page under its empty area stays
   clickable; each toast takes it back, or a button inside one could never be
   pressed. */

.rtoasts{
  position:fixed; z-index:80;
  right:var(--s5); bottom:var(--s5);
  left:var(--s5);
  display:flex; flex-direction:column; align-items:flex-end; gap:var(--s2);
  pointer-events:none;
}
.rtoast{
  position:relative; overflow:hidden;
  width:min(360px, 100%);
  padding:12px 14px;
  border-radius:var(--r-card);
  background:var(--paper); color:var(--ink);
  box-shadow:var(--lift-lg), var(--ring);
  animation:rtoast-in .32s var(--ease-out) both;
  pointer-events:auto;
}
.rtoast__t{
  font-size:var(--t-md); font-weight:650; line-height:1.35;
  color:var(--soon); margin-bottom:2px;
  overflow-wrap:anywhere;
}
.rtoast__s{font-size:var(--t-sm); color:var(--slate); line-height:1.45;}
.rtoast--info .rtoast__t{color:var(--accent-deep);}
/* The draining bar is the only thing that says "this will leave by itself".
   Without it a toast with no buttons reads as stuck. */
.rtoast__bar{position:absolute; left:0; right:0; bottom:0; height:3px;
  background:rgba(34,66,81,.06);}
.rtoast__bar i{display:block; height:100%; width:100%;
  transform-origin:left; background:var(--soon);
  animation:rtoast-drain linear forwards;}
.rtoast--info .rtoast__bar i{background:var(--accent-deep);}
.rtoast--out{animation:rtoast-out .4s ease forwards;}
/* The progress toast (D-098): the same bar filling with the bytes sent,
   never draining, so it cannot be read as a result on its way out. */
.rtoast--progress .rtoast__bar i{animation:none; transform:scaleX(0);
  transition:transform .2s linear;}

@keyframes rtoast-in{from{opacity:0; transform:translateY(10px);} to{opacity:1;}}
@keyframes rtoast-out{to{opacity:0; transform:translateY(6px);}}
@keyframes rtoast-drain{to{transform:scaleX(0);}}

@media (prefers-reduced-motion:reduce){
  .rtoast{animation:none;}
  .rtoast--out{animation:none; opacity:0;}
  /* The bar still has to say how long is left, so it holds its full width
     rather than sliding: the toast leaves on the same timer either way. */
  .rtoast__bar i{animation:none;}
}

/* ---- modal (two of the three real shadows) ------------------------------ */

.modal{
  position:fixed; inset:0; z-index:90;
  display:flex; align-items:center; justify-content:center;
  padding:var(--s5);
}
.modal__backdrop{
  position:absolute; inset:0;
  background:color-mix(in srgb, var(--ink) 44%, transparent);
  animation:conf-fade .2s ease both;
}
.modal__panel{
  position:relative;
  width:min(600px, 100%);
  max-height:calc(100vh - 2 * var(--s5));
  display:flex; flex-direction:column;
  background:var(--paper);
  border-radius:var(--r-card);
  box-shadow:var(--lift-lg);
  animation:conf-rise .28s var(--ease-out) both;
}
.modal__head{
  display:flex; align-items:flex-start; justify-content:space-between;
  gap:var(--s3);
  padding:var(--s4) var(--s5);
  border-bottom:1px solid var(--line);
}
.modal__title{font-size:var(--t-md); font-weight:650; letter-spacing:-.01em; line-height:1.35;}
/* The one way out since F34: the foot's Fechar is gone, so the X earns
   real size and a visible ring. */
.modal__close{
  flex:none;
  width:44px; height:44px;
  display:grid; place-items:center;
  border:0; border-radius:var(--r-ctl);
  background:transparent; color:var(--slate);
  box-shadow:var(--ring);
  font-size:var(--t-lg); line-height:1;
  cursor:pointer;
  transition:background .16s ease, color .16s ease;
}
.modal__close:hover{background:var(--canvas); color:var(--ink);}
.modal__close svg{width:15px; height:15px;}
.modal__body{
  padding:var(--s5);
  overflow-y:auto;
  display:flex; flex-direction:column; gap:var(--s4);
  min-height:0;
}
.modal__foot{
  display:flex; align-items:center; justify-content:flex-end;
  gap:var(--s3); flex-wrap:wrap;
  padding:var(--s3) var(--s5) var(--s4);
  border-top:1px solid var(--line);
}

/* ---- empty state -------------------------------------------------------- */

.empty{
  display:flex; flex-direction:column; align-items:center;
  gap:var(--s2); text-align:center;
  padding:var(--s7) var(--s5);
  background:var(--paper);
  border-radius:var(--r-card);
  box-shadow:var(--ring);
}
.empty__title{font-size:var(--t-md); font-weight:650; letter-spacing:-.01em;}
.empty__body{font-size:var(--t-base); color:var(--slate); max-width:44ch; line-height:1.5;}

/* ---- completeness ring (svg) -------------------------------------------- */

.ring{
  position:relative; flex:none;
  width:56px; height:56px;
  display:inline-grid; place-items:center;
}
.ring svg{width:100%; height:100%; transform:rotate(-90deg);}
.ring__track{fill:none; stroke:color-mix(in srgb, var(--line) 60%, transparent); stroke-width:4;}
.ring__fill{
  fill:none; stroke:var(--accent); stroke-width:4; stroke-linecap:round;
  transition:stroke-dashoffset .5s var(--ease-out), stroke .25s ease;
}
.ring__label{
  position:absolute;
  font-size:var(--t-sm); font-weight:700; letter-spacing:-.01em;
  line-height:1; color:var(--ink);
}
.ring.s-ok .ring__fill{stroke:var(--ok);}
.ring.s-none .ring__fill{stroke:var(--accent);}
.ring.s-soon .ring__fill{stroke:var(--soon);}
.ring.s-late .ring__fill{stroke:var(--late);}

/* ---- linear progress ---------------------------------------------------- */

.bar{
  display:block;
  width:100%;
  height:6px;
  border-radius:var(--r-pill);
  background:color-mix(in srgb, var(--line) 55%, transparent);
  overflow:hidden;
}
.bar__fill{
  display:block; height:100%; width:0;
  border-radius:var(--r-pill);
  background:var(--accent);
  transition:width .5s var(--ease-out), background .25s ease;
}
.s-ok .bar__fill{background:var(--ok);}
.s-soon .bar__fill{background:var(--soon);}
.s-late .bar__fill{background:var(--late);}


/* === DEV TOGGLE ==========================================================
   Deliberately not product styling. Dark slab, monospace-ish label,
   the third and last real shadow. */

.devbar{
  position:fixed; z-index:100;
  right:var(--s4); bottom:var(--s4);
  max-width:calc(100vw - 2 * var(--s4));
  display:flex; align-items:center; gap:var(--s2); flex-wrap:wrap;
  padding:7px 9px;
  border-radius:var(--r-ctl);
  background:var(--ink); color:var(--canvas);
  box-shadow:var(--lift-lg);
  font-family:var(--f-mono);
  font-size:var(--t-xs); letter-spacing:.02em; line-height:1;
}
.devbar__label{
  padding-inline:3px;
  text-transform:uppercase; letter-spacing:.12em;
  color:color-mix(in srgb, var(--canvas) 58%, transparent);
}
.devbar__seg{
  display:flex; gap:2px;
  padding:2px;
  border-radius:6px;
  background:color-mix(in srgb, var(--canvas) 12%, transparent);
}
.devbar__btn{
  border:0; border-radius:4px;
  padding:5px 9px;
  font:inherit; font-size:var(--t-xs); line-height:1;
  background:transparent;
  color:color-mix(in srgb, var(--canvas) 70%, transparent);
  transition:background .14s ease, color .14s ease;
}
.devbar__btn:hover{color:var(--canvas);}
.devbar__btn--on{background:var(--canvas); color:var(--ink);}
.devbar__btn--on:hover{color:var(--ink);}
.devbar__reset{
  border:0; border-radius:4px;
  padding:5px 9px;
  font:inherit; font-size:var(--t-xs); line-height:1;
  background:transparent;
  color:color-mix(in srgb, var(--canvas) 55%, transparent);
  box-shadow:0 0 0 1px color-mix(in srgb, var(--canvas) 22%, transparent);
  transition:color .14s ease, box-shadow .14s ease;
}
.devbar__reset:hover{
  color:var(--canvas);
  box-shadow:0 0 0 1px color-mix(in srgb, var(--canvas) 45%, transparent);
}
.devbar :focus-visible{outline-color:var(--canvas); outline-offset:1px;}


/* === ACCOUNTANT SHELL ====================================================
   Cool --canvas ground, dense, ledger-like. Fixed 232px left rail. */

body.side-accountant{background:var(--canvas); font-size:var(--t-base);}

.acc{min-height:100vh; padding-left:var(--rail-w);}

.rail{
  position:fixed; z-index:40;
  left:0; top:0; bottom:0;
  width:var(--rail-w);
  display:flex; flex-direction:column; gap:var(--s5);
  padding:var(--s5) var(--s4);
  background:var(--paper);
  box-shadow:inset -1px 0 0 var(--line);
  overflow-y:auto;
}

/* The product's own mark, quiet, at the very top of the rail (F16). The
   page heads belong to the screen titles now; her brand stays where it
   was, right below. The approved logo since D-115, 24px high (97px wide, fixed so the
   collapsed top bar cannot shrink it to nothing); the file
   carries its own colours, so nothing here recolours it. */
.rail__product{ padding-bottom:var(--s3); line-height:0; }
.rail__logo{ display:block; width:97px; height:24px; }

.rail__brand{
  display:grid;
  grid-template-columns:auto minmax(0,1fr);
  gap:1px var(--s3);
  align-items:center;
  padding-bottom:var(--s4);
  border-bottom:1px solid var(--line);
}
.rail__mark{
  grid-row:1 / -1;
  width:34px; height:34px;
  display:grid; place-items:center;
  border-radius:var(--r-ctl);
  background:var(--accent-deep); color:var(--paper);
  font-family:var(--f-display); font-weight:700; font-size:var(--t-base);
  letter-spacing:-.02em; line-height:1;
  overflow:hidden;
}
.rail__mark img{width:100%; height:100%; object-fit:cover;}
.rail__name{
  grid-column:2; font-size:var(--t-sm); font-weight:600; line-height:1.2;
  overflow-wrap:anywhere; letter-spacing:-.005em;
  display:-webkit-box; -webkit-box-orient:vertical; -webkit-line-clamp:2; overflow:hidden;
}
.rail__role{grid-column:2; font-size:var(--t-xs); color:var(--slate); line-height:1.3;}

.rail__nav{display:flex; flex-direction:column; gap:2px; min-width:0;}
.rail__item{
  display:flex; align-items:center; gap:var(--s3);
  width:100%; padding:9px var(--s3);
  border:0; border-radius:var(--r-ctl);
  background:transparent; color:var(--slate);
  font:inherit; font-size:var(--t-base); font-weight:500; text-align:left;
  -webkit-appearance:none; appearance:none;
  transition:background .15s ease, color .15s ease;
}
.rail__item:hover{background:var(--canvas); color:var(--ink);}
.rail__item--on{background:var(--wash); color:var(--accent-deep); font-weight:600;}
/* The "Por verificar" count (M8). A link's underline cannot be cancelled from
   inside a child, so an item carrying the badge drops its own underline and
   only the label text is underlined - otherwise the underline runs through
   the circle under the number and it reads high. */
.rail__item:has(.rail__badge){text-decoration:none;}
.rail__item:has(.rail__badge) .rail__label{
  text-decoration:underline; text-decoration-thickness:1px;}
/* `line-height` equal to the height, not 1: with a shorter text box the number
   rounds a pixel high on standard (1x) screens. Measured centred at 1x and 2x.
   `--badge` is the practice's colour, or the deep accent when it has none. */
.rail__badge{
  display:inline-flex; align-items:center; justify-content:center; flex:none;
  min-width:20px; height:20px; padding:0 6px; margin-left:-4px;
  border-radius:var(--r-pill);
  background:var(--badge, var(--accent-deep)); color:#fff;
  font-size:11.5px; font-weight:700; line-height:20px;
  font-variant-numeric:tabular-nums;
}
.rail__item--on:hover{background:var(--wash); color:var(--accent-deep);}
.rail__icon{width:18px; height:18px; flex:none; display:grid; place-items:center; color:currentColor;}
.rail__icon svg{width:100%; height:100%;}

.rail__foot{
  margin-top:auto;
  display:flex; flex-direction:column; gap:var(--s2);
  padding-top:var(--s4);
  border-top:1px solid var(--line);
  font-size:var(--t-xs); color:var(--slate); line-height:1.45;
}

.main{min-width:0;}
.main__inner{
  max-width:1220px; margin:0 auto;
  padding:var(--s6) var(--s6) 130px;
  display:flex; flex-direction:column; gap:var(--s5);
}

.page{display:flex; flex-direction:column; gap:var(--s5); min-width:0;}
.page__head{
  display:flex; align-items:flex-end; justify-content:space-between;
  gap:var(--s4); flex-wrap:wrap;
}
.page__eyebrow{
  display:block;
  font-size:var(--t-xs); font-weight:600; letter-spacing:.13em;
  text-transform:uppercase; color:var(--slate);
}
.page__title{font-size:var(--t-xl); line-height:1.12; margin-top:2px;}
.page__sub{font-size:var(--t-base); color:var(--slate); max-width:64ch; line-height:1.5;}
.page__actions{display:flex; align-items:center; gap:var(--s2); flex-wrap:wrap;}
/* Why `Lembrar agora` is disabled (D-122), on its own line under the head. */
.remind__why{ flex-basis:100%; text-align:right; }


/* === A1 CLIENT LIST ======================================================
   The summary strip is typographic and sits directly on the canvas.
   No metric cards, no shadows. */

.summary{
  display:flex; flex-wrap:wrap;
  gap:var(--s4) var(--s6);
  padding:var(--s1) 0 var(--s3);
}
.summary__item{
  display:flex; flex-direction:column; gap:1px;
  min-width:0;
  padding-left:var(--s5);
  border-left:1px solid var(--line);
}
.summary__item:first-child{padding-left:0; border-left:0;}
/* The ignored-rows count opens a card naming them (M5). Hover on a desk, and
   focus for the keyboard and for a tap, where there is no hover. */
.summary__item--tip{position:relative; cursor:help; outline:none;}
.summary__item--tip .summary__label{
  text-decoration:underline dotted; text-underline-offset:3px;}
.summary__item--tip:focus-visible .summary__label{color:var(--ink);}
.tipcard{
  display:none; position:absolute; top:100%; left:var(--s5); z-index:20;
  margin-top:var(--s2); width:max-content; max-width:min(440px, 80vw);
  padding:var(--s3) var(--s4);
  background:var(--paper); border:1px solid var(--line);
  border-radius:var(--r-card); box-shadow:0 8px 24px rgba(34,66,81,.14);
  cursor:auto;
}
.summary__item--tip:hover .tipcard,
.summary__item--tip:focus-within .tipcard{display:block;}
.tipcard__title{
  font-size:var(--t-xs); font-weight:600; letter-spacing:.12em;
  text-transform:uppercase; color:var(--slate); margin-bottom:var(--s2);}
.tipcard__list{list-style:none; margin:0; padding:0;
  font-size:var(--t-base); line-height:1.5; color:var(--ink);}
.tipcard__list li + li{margin-top:var(--s1);}
.summary__val{font-size:var(--t-2xl); font-weight:700; line-height:1.02;}
.summary__label{
  font-size:var(--t-xs); font-weight:600; letter-spacing:.12em;
  text-transform:uppercase; color:var(--slate); line-height:1.4;
}
.summary__item.s-ok .summary__val{color:var(--ok);}
.summary__item.s-soon .summary__val{color:var(--soon);}
.summary__item.s-late .summary__val{color:var(--late);}

.toolbar{
  display:flex; align-items:center; gap:var(--s3);
  flex-wrap:wrap;
}
.toolbar__filters{display:flex; align-items:center; gap:var(--s2); flex-wrap:wrap; min-width:0;}
.toolbar__search{
  margin-left:auto;
  flex:0 1 280px; min-width:180px;
  display:flex; align-items:center; gap:var(--s2);
}
.toolbar__search .input{background:var(--paper);}

/* .clist is the horizontal scroll container for the wide client table.
   It pads by --focus-pad so a row focus ring is never clipped, and pulls the
   padding back with a matching negative margin so the grid still lines up. */
.clist{
  display:flex; flex-direction:column;
  gap:var(--s2);
  overflow-x:auto;
  padding:var(--focus-pad);
  margin:calc(var(--focus-pad) * -1);
  /* The last column is Actividade. At `minmax(118px,1.2fr)` it resolved to
     143px against 148px of content on a 1440 viewport - clipped by five
     pixels, with an ellipsis, on the *shortest* string it ever holds
     ("1 documento enviado, 17 ago"). Two documents makes it longer, so the
     column never once showed what it said. Measured in the browser at
     160px/1.6fr: 177px shown against 177px needed. The min sum is 866, still
     inside `--clist-min`, so nothing else moves. */
  /* Two deadline columns since D-082: what the client was asked for, and
     the AT's own date beside it. */
  --clist-cols:32px minmax(150px,2.4fr) 106px 86px minmax(124px,1.4fr) 112px 96px 96px minmax(150px,1.5fr);
  --clist-min:920px;
}

.clist__head{
  display:grid;
  grid-template-columns:var(--clist-cols);
  gap:var(--s3);
  align-items:end;
  min-width:var(--clist-min);
  padding:0 var(--s4) var(--s1) var(--s5);
}
.clist__hcell{
  display:flex; align-items:center; gap:5px;
  font-size:var(--t-xs); font-weight:600; letter-spacing:.1em;
  text-transform:uppercase; color:var(--slate); line-height:1.3;
  min-width:0;
}
.clist__hcell--num{justify-content:flex-end; text-align:right;}
.clist__hcell--sort{
  border:0; padding:0;
  background:transparent; color:var(--slate);
  font:inherit; font-size:var(--t-xs); font-weight:600; letter-spacing:.1em;
  text-transform:uppercase;
  -webkit-appearance:none; appearance:none;
  transition:color .15s ease;
}
.clist__hcell--sort:hover{color:var(--ink);}
.clist__hcell--sort::after{
  content:"";
  width:7px; height:5px; flex:none;
  background:currentColor;
  clip-path:polygon(50% 0, 100% 100%, 0 100%);
  opacity:0;
  transition:opacity .15s ease;
}
.clist__hcell.is-asc{color:var(--ink);}
.clist__hcell.is-asc::after{opacity:.8;}
.clist__hcell.is-desc{color:var(--ink);}
.clist__hcell.is-desc::after{opacity:.8; clip-path:polygon(0 0, 100% 0, 50% 100%);}

.crow{
  display:grid;
  grid-template-columns:var(--clist-cols);
  grid-template-rows:auto auto;
  gap:2px var(--s3);
  align-items:center;
  min-width:var(--clist-min);
  min-height:52px;
  width:100%;
  padding:var(--s2) var(--s4) var(--s2) var(--s5);
  border:0; border-radius:var(--r-card);
  background:var(--paper); color:inherit;
  box-shadow:var(--ring);
  font:inherit; font-size:var(--t-sm); text-align:left;
  -webkit-appearance:none; appearance:none;
  transition:background .16s ease, box-shadow .16s ease, transform .12s ease;
}
.crow:hover{background:var(--wash);}
.crow:active{transform:translateY(1px);}
.crow > *{grid-row:1 / -1; min-width:0;}

.crow__id{
  grid-column:1;
  width:26px; height:26px;
  display:grid; place-items:center;
  border-radius:var(--r-chip);
  background:var(--wash); color:var(--accent-deep);
  font-size:var(--t-xs); font-weight:700; letter-spacing:-.01em; line-height:1;
}
.crow__name{
  grid-column:2; grid-row:1;
  font-size:var(--t-base); font-weight:600; line-height:1.25;
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
}
/* The sub line carries two things now: the progress phrase, and - when nobody
   is assigned - the ownership marker. It moved here from `Estado` on 20 Aug,
   where it was a second pill in a 75px track and wrapped under the status
   instead of beside it, taking the row from 52px to 61px (R-027). */
.crow__sub{
  grid-column:2; grid-row:2;
  display:flex; align-items:center; gap:var(--s2); min-width:0;
  font-size:var(--t-xs); color:var(--slate); line-height:1.3;
}
.crow__subtext{overflow:hidden; text-overflow:ellipsis; white-space:nowrap;}
/* Sized to the line it sits on. At the tag's own 3px/16.5px it stood 23px
   against the sub line's 14px and pushed the row to 58px - the same broken
   rhythm the move was supposed to end, 6px instead of 9px. */
.crow__owner{
  flex:none;
  padding:0 6px;
  font-size:10px; line-height:1.45; letter-spacing:.01em;
}
.crow__nif{grid-column:3; font-size:var(--t-sm); color:var(--slate); letter-spacing:.01em;}
.crow__period{grid-column:4; font-size:var(--t-sm); color:var(--slate);}
.crow__prog{grid-column:5; grid-row:1; align-self:end; padding-bottom:3px;}
.crow__count{grid-column:5; grid-row:2; font-size:var(--t-xs); color:var(--slate); line-height:1.3;}
.crow__deadline{grid-column:6; grid-row:1; font-size:var(--t-sm); line-height:1.3;}
.crow__days{grid-column:6; grid-row:2; font-size:var(--t-xs); color:var(--slate); line-height:1.3;}
.crow__legal{grid-column:7; font-size:var(--t-sm); color:var(--slate); align-self:center;}
.crow__status{grid-column:8; justify-self:start;}
.crow__activity{
  grid-column:9;
  font-size:var(--t-xs); color:var(--slate); line-height:1.35;
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
}
.crow.s-soon .crow__days{color:var(--soon); font-weight:600;}
.crow.s-late .crow__days{color:var(--late); font-weight:600;}


/* === A2 CLIENT DETAIL ==================================================== */

.detail{display:flex; flex-direction:column; gap:var(--s6); min-width:0;}
.detail__top{
  display:grid;
  grid-template-columns:minmax(0,1.4fr) minmax(0,1fr);
  gap:var(--s4);
  align-items:start;
}

.idcard{
  display:grid;
  grid-template-columns:auto minmax(0,1fr);
  gap:var(--s1) var(--s4);
  align-items:start;
  padding:var(--s5);
  background:var(--paper);
  border-radius:var(--r-card);
  box-shadow:var(--ring);
}
.idcard__mark{
  grid-row:1 / -1;
  width:46px; height:46px;
  display:grid; place-items:center;
  border-radius:var(--r-ctl);
  background:var(--wash); color:var(--accent-deep);
  font-size:var(--t-md); font-weight:700; letter-spacing:-.01em; line-height:1;
}
.idcard__name{
  grid-column:2;
  font-size:var(--t-lg); font-weight:650; letter-spacing:-.01em; line-height:1.2;
}
.idcard__meta{
  grid-column:2;
  display:flex; flex-wrap:wrap; gap:var(--s1) var(--s3);
  font-size:var(--t-sm); color:var(--slate);
}
.idcard__contact{
  grid-column:2;
  display:flex; flex-wrap:wrap; gap:var(--s1) var(--s3);
  font-size:var(--t-sm); color:var(--slate);
  padding-top:var(--s2);
}

.notes{
  display:flex; flex-direction:column; gap:var(--s2);
  padding:var(--s5);
  background:var(--paper);
  border-radius:var(--r-card);
  box-shadow:var(--ring);
}
.notes__label{
  display:flex; align-items:center; justify-content:space-between;
  gap:var(--s3);
  font-size:var(--t-xs); font-weight:600; letter-spacing:.08em;
  text-transform:uppercase; color:var(--slate);
}
.notes__input{
  width:100%; min-height:88px; resize:vertical;
  font:inherit; font-size:var(--t-sm); line-height:1.55;
  color:var(--ink); background:var(--canvas);
  border:0; border-radius:var(--r-ctl);
  box-shadow:var(--ring);
  padding:10px 12px;
  transition:box-shadow .16s ease, background .16s ease;
}
.notes__input:hover{box-shadow:0 0 0 1px var(--slate);}
.notes__input:focus{background:var(--paper);}
.notes__saved{font-size:var(--t-xs); font-weight:600; color:var(--ok); letter-spacing:.02em;}

.periodsel{
  display:inline-flex; flex-wrap:wrap; gap:2px;
  padding:3px;
  border-radius:var(--r-ctl);
  background:var(--canvas);
  box-shadow:var(--ring);
}
.periodsel__btn{
  border:0; border-radius:calc(var(--r-ctl) - 2px);
  padding:6px 13px;
  background:transparent; color:var(--slate);
  font:inherit; font-size:var(--t-sm); font-weight:600; line-height:1.3;
  white-space:nowrap;
  -webkit-appearance:none; appearance:none;
  transition:background .16s ease, color .16s ease, box-shadow .16s ease;
}
.periodsel__btn:hover{color:var(--ink);}
.periodsel__btn--on{background:var(--paper); color:var(--accent-deep); box-shadow:var(--ring);}
.periodsel__btn--on:hover{color:var(--accent-deep);}

.recon{display:flex; flex-direction:column; gap:var(--s3); min-width:0;}
.recon__toolbar{display:flex; align-items:center; gap:var(--s3); flex-wrap:wrap;}
.recon__legend{
  display:flex; align-items:center; gap:var(--s3); flex-wrap:wrap;
  margin-left:auto;
  font-size:var(--t-xs); color:var(--slate);
}
.recon__legend > *{display:inline-flex; align-items:center; gap:6px;}
.recon__legend > *::before{
  content:"";
  width:3px; height:13px; flex:none;
  border-radius:2px;
  background:var(--line);
}
.recon__legend > .s-ok::before{background:var(--ok);}
.recon__legend > .s-none::before{background:var(--line);}
.recon__legend > .s-soon::before{background:var(--soon);}
.recon__legend > .s-late::before{background:var(--late);}

/* Wide table, own horizontal scroller, padded so focus rings survive. */
.recon__table{
  display:flex; flex-direction:column; gap:6px;
  overflow-x:auto;
  padding:var(--focus-pad);
  margin:calc(var(--focus-pad) * -1);
  --recon-cols:minmax(158px,2fr) minmax(128px,1.2fr) 92px 86px 76px 96px 104px;
  --recon-min:800px;
}
.recon__head{
  display:grid;
  grid-template-columns:var(--recon-cols);
  gap:var(--s3);
  align-items:end;
  min-width:var(--recon-min);
  padding:0 var(--s4) var(--s1) var(--s5);
}
.recon__hcell{
  font-size:var(--t-xs); font-weight:600; letter-spacing:.1em;
  text-transform:uppercase; color:var(--slate); line-height:1.3;
  min-width:0;
}
.recon__hcell--num{text-align:right;}

.drow{
  display:grid;
  grid-template-columns:var(--recon-cols);
  gap:var(--s3);
  align-items:center;
  min-width:var(--recon-min);
  min-height:46px;
  width:100%;
  padding:var(--s2) var(--s4) var(--s2) var(--s5);
  border:0; border-radius:var(--r-card);
  background:var(--paper); color:inherit;
  box-shadow:var(--ring);
  font:inherit; font-size:var(--t-sm); text-align:left;
  -webkit-appearance:none; appearance:none;
  transition:background .16s ease, box-shadow .16s ease;
}
.drow:hover{background:var(--wash);}
.drow > *{min-width:0;}
.drow.s-ok{background:var(--ok-wash);}
.drow.s-ok:hover{background:var(--ok-wash);}
.drow.s-soon{background:var(--soon-wash);}
.drow.s-soon:hover{background:var(--soon-wash);}

.drow__sup{grid-column:1; display:flex; flex-direction:column; gap:1px; min-width:0;}
.drow__supname{
  font-size:var(--t-sm); font-weight:600; line-height:1.3;
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
}
.drow__nif{font-size:var(--t-xs); color:var(--slate); letter-spacing:.01em; line-height:1.3;}
.drow__doc{
  grid-column:2;
  display:flex; align-items:center; gap:var(--s2); min-width:0;
  font-size:var(--t-sm);
}
.drow__type{
  flex:none;
  padding:2px 6px;
  border-radius:var(--r-chip);
  background:var(--canvas); color:var(--slate);
  box-shadow:var(--ring);
  font-size:var(--t-xs); font-weight:700; letter-spacing:.06em; line-height:1.4;
}
.drow__date{grid-column:3; font-size:var(--t-sm); color:var(--slate);}
.drow__base{grid-column:4; text-align:right; font-size:var(--t-sm); color:var(--slate);}
.drow__vat{grid-column:5; text-align:right; font-size:var(--t-sm); color:var(--slate);}
.drow__total{grid-column:6; text-align:right; font-size:var(--t-sm); font-weight:600;}
.drow__status{grid-column:7; justify-self:start;}
.drow.s-ok .drow__total{color:var(--ok);}

.docview{display:flex; flex-direction:column; gap:var(--s4); min-width:0;}
.docview__paper{
  position:relative;
  display:flex; flex-direction:column; gap:var(--s3);
  min-height:180px;
  padding:var(--s5);
  background:var(--paper);
  border-radius:var(--r-ctl);
  box-shadow:var(--ring);
}
.docview__stamp{
  position:absolute; top:var(--s4); right:var(--s4);
  transform:rotate(-7deg);
  padding:4px 11px;
  border-radius:var(--r-chip);
  background:var(--paper); color:var(--slate);
  box-shadow:0 0 0 1.5px currentColor;
  font-size:var(--t-xs); font-weight:700; letter-spacing:.12em;
  text-transform:uppercase; line-height:1.5;
}
.docview.s-ok .docview__stamp{color:var(--ok); background:var(--ok-wash);}
.docview.s-soon .docview__stamp{color:var(--soon); background:var(--soon-wash);}
.docview.s-late .docview__stamp{color:var(--late); background:var(--late-wash);}
.docview__meta{
  display:grid;
  grid-template-columns:auto minmax(0,1fr);
  gap:6px var(--s4);
  font-size:var(--t-sm);
  font-variant-numeric:tabular-nums;
}
.docview__meta > *:nth-child(odd){
  color:var(--slate);
  font-size:var(--t-xs); font-weight:600; letter-spacing:.06em;
  text-transform:uppercase; align-self:center;
}

.control{display:flex; flex-direction:column; gap:var(--s4); min-width:0;}
.control__grid{
  display:grid;
  grid-template-columns:repeat(auto-fit, minmax(230px,1fr));
  gap:var(--s4) var(--s5);
  align-items:start;
}

.cadence{display:flex; flex-direction:column; gap:var(--s3); min-width:0;}
.cadence__preview{
  display:flex; flex-wrap:wrap; align-items:center;
  gap:var(--s2) var(--s3);
  padding:var(--s3) var(--s4);
  border-radius:var(--r-ctl);
  background:var(--wash); color:var(--accent-deep);
  font-size:var(--t-sm); line-height:1.45;
}

.timeline{
  display:flex; flex-direction:column;
  --tl-when:86px; --tl-dot:12px;
}
.tl__item{
  position:relative;
  display:grid;
  grid-template-columns:var(--tl-when) var(--tl-dot) minmax(0,1fr);
  column-gap:var(--s3); row-gap:2px;
  padding-bottom:var(--s4);
}
.tl__item::before{
  content:"";
  position:absolute;
  left:calc(var(--tl-when) + var(--s3) + var(--tl-dot) / 2 - .5px);
  top:14px; bottom:0;
  width:1px;
  background:var(--line);
}
.tl__item:last-child{padding-bottom:0;}
.tl__item:last-child::before{display:none;}
.tl__dot{
  grid-column:2; grid-row:1;
  justify-self:center; align-self:start;
  width:9px; height:9px; margin-top:5px;
  border-radius:var(--r-pill);
  background:var(--accent);
  box-shadow:0 0 0 3px var(--canvas);
  position:relative; z-index:1;
}
.tl__when{
  grid-column:1; grid-row:1 / 3;
  text-align:right;
  font-size:var(--t-xs); color:var(--slate); line-height:1.5;
}
.tl__what{grid-column:3; grid-row:1; font-size:var(--t-sm); font-weight:600; line-height:1.4;}
.tl__meta{grid-column:3; grid-row:2; font-size:var(--t-xs); color:var(--slate); line-height:1.45;}
.tl__item--scheduled .tl__dot{
  background:var(--paper);
  box-shadow:0 0 0 1.5px var(--line), 0 0 0 3px var(--canvas);
}
.tl__item--scheduled .tl__what{color:var(--slate); font-weight:500;}
/* M9 (D-110): under an event's title, at most a reason and one line of the
   values read. Grid column 3, auto rows, so they stack under the title. */
.tl__line{grid-column:3; font-size:var(--t-sm); color:var(--ink); line-height:1.45; font-variant-numeric:tabular-nums;}
.tl__line--muted,.tl__note{color:var(--slate);}
/* The document drawer: two sizes and two weights, nothing else. The title
   and the event titles carry the weight; everything else reads at base. */
.modal--trail .page__sub,.modal--trail .ev__row,.modal--trail .tl__when,
.modal--trail .tl__what,.modal--trail .tl__line,.modal--trail .modal__close,
.modal--trail .btn{font-size:var(--t-base);}
.modal--trail .modal__title,.modal--trail .tl__what,.modal--trail .btn{font-weight:650;}
.modal--trail .modal__close{font-weight:400;}


/* === A3 ADD CLIENT ======================================================= */

.wizard{
  display:flex; flex-direction:column;
  background:var(--paper);
  border-radius:var(--r-card);
  box-shadow:var(--ring);
  min-width:0;
}
.wizard__steps{
  display:flex; flex-wrap:wrap;
  gap:var(--s3) var(--s5);
  padding:var(--s4) var(--s5);
  border-bottom:1px solid var(--line);
}
.wstep{
  display:flex; align-items:center; gap:var(--s2);
  font-size:var(--t-sm); font-weight:500; color:var(--slate);
  min-width:0;
}
.wstep__num{
  flex:none;
  width:24px; height:24px;
  display:grid; place-items:center;
  border-radius:var(--r-pill);
  background:var(--paper); color:var(--slate);
  box-shadow:var(--ring);
  font-size:var(--t-xs); font-weight:700; line-height:1;
}
.wstep__label{overflow:hidden; text-overflow:ellipsis; white-space:nowrap;}
.wstep--on{color:var(--ink); font-weight:600;}
.wstep--on .wstep__num{
  background:var(--wash); color:var(--accent-deep);
  box-shadow:0 0 0 1.5px var(--accent);
}
.wstep--done{color:var(--ink);}
.wstep--done .wstep__num{
  background:var(--ok-wash); color:var(--ok);
  box-shadow:0 0 0 1px var(--ok);
}

.wizard__body{
  display:flex; flex-direction:column; gap:var(--s5);
  padding:var(--s6) var(--s5);
  min-width:0;
}
.wizard__foot{
  display:flex; align-items:center; justify-content:space-between;
  gap:var(--s3); flex-wrap:wrap;
  padding:var(--s3) var(--s5) var(--s4);
  border-top:1px solid var(--line);
}

.radiocards{
  display:grid;
  grid-template-columns:repeat(auto-fit, minmax(220px,1fr));
  gap:var(--s3);
}
.radiocard{
  display:flex; flex-direction:column; gap:4px;
  width:100%;
  padding:var(--s4);
  border:0; border-radius:var(--r-card);
  background:var(--paper); color:inherit;
  box-shadow:var(--ring);
  font:inherit; text-align:left;
  -webkit-appearance:none; appearance:none;
  transition:background .16s ease, box-shadow .16s ease, transform .12s ease;
}
.radiocard:hover{background:var(--wash);}
.radiocard:active{transform:translateY(1px);}
.radiocard--on{background:var(--wash); box-shadow:0 0 0 1.5px var(--accent);}
.radiocard--on:hover{background:var(--wash);}
.radiocard__title{font-size:var(--t-base); font-weight:600; line-height:1.3;}
.radiocard__body{font-size:var(--t-sm); color:var(--slate); line-height:1.45;}

.dropzone{
  display:flex; flex-direction:column; align-items:center;
  gap:var(--s3); text-align:center;
  padding:var(--s6) var(--s5);
  border:1.5px dashed color-mix(in srgb, var(--accent) 45%, transparent);
  border-radius:var(--r-card);
  background:var(--paper);
  transition:background .2s ease, border-color .2s ease;
}
.dropzone--over{background:var(--wash); border-color:var(--accent);}
/* A picked file is acknowledged in place (F7): solid border, the file's name
   as the title. */
.dropzone--chosen{background:var(--wash); border-style:solid; border-color:var(--accent);}
.dropzone__mark{
  width:44px; height:44px; flex:none;
  display:grid; place-items:center;
  border-radius:var(--r-pill);
  background:var(--wash); color:var(--accent-deep);
}
.dropzone__mark svg{width:20px; height:20px;}
.dropzone__title{font-size:var(--t-md); font-weight:600; line-height:1.35;}
.dropzone__hint{font-size:var(--t-sm); color:var(--slate); max-width:44ch; line-height:1.5;}

.parsed{display:flex; flex-direction:column; gap:var(--s3); min-width:0;}
.parsed__summary{
  display:flex; flex-wrap:wrap; align-items:baseline;
  gap:var(--s2) var(--s4);
  font-size:var(--t-sm); color:var(--slate);
}
.parsed__summary b{color:var(--ink); font-weight:600;}
.parsed__table{
  overflow:auto;
  max-height:320px;
  border-radius:var(--r-ctl);
  background:var(--paper);
  box-shadow:var(--ring);
}
.parsed__table table{border-collapse:collapse; width:100%; min-width:560px; font-size:var(--t-sm);}
.parsed__table th{
  position:sticky; top:0; z-index:1;
  background:var(--paper);
  padding:9px 12px;
  border-bottom:1px solid var(--line);
  text-align:left; white-space:nowrap;
  font-size:var(--t-xs); font-weight:600; letter-spacing:.1em;
  text-transform:uppercase; color:var(--slate);
}
.parsed__table td{
  padding:7px 12px;
  border-bottom:1px solid color-mix(in srgb, var(--line) 50%, transparent);
  white-space:nowrap;
  font-variant-numeric:tabular-nums;
}
.parsed__table tr:last-child td{border-bottom:0;}
.parsed__table td:nth-child(n+3){text-align:right;}
.parsed__split{
  display:flex; flex-wrap:wrap; align-items:center;
  gap:var(--s2) var(--s3);
  padding:var(--s3) var(--s4);
  border-radius:var(--r-ctl);
  background:var(--wash); color:var(--accent-deep);
  box-shadow:var(--ring);
  font-size:var(--t-sm); line-height:1.5;
}
.parsed__split code{
  font-family:var(--f-mono); font-size:var(--t-sm);
  padding:2px 7px;
  border-radius:var(--r-chip);
  background:var(--paper); color:var(--ink);
  box-shadow:var(--ring);
}


/* === A4 SETTINGS ========================================================= */

.settings{
  display:grid;
  grid-template-columns:repeat(auto-fit, minmax(330px,1fr));
  gap:var(--s4);
  align-items:start;
}

/* .panel is a sibling of .card, never a replacement for it. Neither carries
   padding, so putting both classes on one node cannot double up the box. */
.panel{
  display:flex; flex-direction:column;
  background:var(--paper);
  border-radius:var(--r-card);
  box-shadow:var(--ring);
  min-width:0;
}
.panel__head{
  display:flex; align-items:center; justify-content:space-between;
  gap:var(--s3); flex-wrap:wrap;
  padding:var(--s4) var(--s5);
  border-bottom:1px solid var(--line);
}
.panel__title{font-size:var(--t-md); font-weight:650; letter-spacing:-.01em; line-height:1.3;}
.panel__body{
  display:flex; flex-direction:column; gap:var(--s4);
  padding:var(--s5);
  min-width:0;
}

.brand{display:flex; flex-wrap:wrap; align-items:center; gap:var(--s4);}
.brand__preview{
  display:flex; align-items:center; gap:var(--s3);
  min-width:0;
  padding:var(--s3) var(--s4);
  border-radius:var(--r-ctl);
  background:var(--wash);
  box-shadow:var(--ring);
  font-size:var(--t-sm); font-weight:600; color:var(--accent-deep);
}
.brand__mark{
  width:40px; height:40px; flex:none;
  display:grid; place-items:center;
  border-radius:var(--r-ctl);
  background:var(--accent-deep); color:var(--paper);
  font-family:var(--f-display); font-weight:700; font-size:var(--t-md);
  letter-spacing:-.02em; line-height:1;
  overflow:hidden;
}
.brand__mark img{width:100%; height:100%; object-fit:cover;}
.brand__upload{display:flex; flex-direction:column; gap:6px; min-width:0;}
.brand__upload input[type="file"]{font-size:var(--t-sm); color:var(--slate); max-width:100%;}
/* D-107: a quiet link, not a button, and a POST underneath. */
.brand__remove{align-self:flex-start; padding:0; border:0; background:none; cursor:pointer;
  font:inherit; font-size:var(--t-sm); color:var(--slate);
  text-decoration:underline; text-underline-offset:2px;}
.brand__remove:hover{color:var(--ink);}

.swatches{display:flex; flex-wrap:wrap; gap:var(--s2);}
.swatch{
  width:32px; height:32px;
  padding:0; border:0;
  border-radius:var(--r-pill);
  box-shadow:var(--ring);
  -webkit-appearance:none; appearance:none;
  transition:transform .14s var(--ease-out), box-shadow .16s ease;
}
.swatch:hover{transform:scale(1.07);}
/* Same reason as `.switch` above (F6): the ring follows `:checked`. */
.swatch:has(input:checked){box-shadow:0 0 0 2px var(--paper), 0 0 0 4px var(--ink);}

.tplcard{
  display:flex; flex-direction:column; gap:var(--s3);
  padding:var(--s4);
  border-radius:var(--r-ctl);
  background:var(--canvas);
  box-shadow:var(--ring);
  min-width:0;
}
.tplcard__subject{font-size:var(--t-base); font-weight:600; line-height:1.35;}
.tplcard__body{
  font-size:var(--t-sm); color:var(--slate); line-height:1.6;
  white-space:pre-wrap; overflow-wrap:anywhere;
}
.tplcard__sig{
  padding-top:var(--s3);
  border-top:1px solid var(--line);
  font-size:var(--t-xs); color:var(--slate); line-height:1.5;
}

.rulegrid{
  display:grid;
  grid-template-columns:repeat(auto-fit, minmax(240px,1fr));
  gap:var(--s4) var(--s5);
  align-items:start;
}


/* === CLIENT PORTAL B1 ====================================================
   --field ground, one centred column, roughly 25 percent larger type,
   18px radii, generous padding, one action visible at a time.
   Same product, calmer room. */

body.side-client{
  background:var(--field);
  font-size:var(--t-lg);
  line-height:1.5;
}

/* larger register for the shared components inside the portal */
body.side-client .btn:not(.btn--sm){
  font-size:var(--t-md);
  padding:14px 24px;
  border-radius:var(--r-card);
}
body.side-client .input,
body.side-client .select,
body.side-client .textarea{
  font-size:var(--t-lg);
  padding:13px 16px;
  border-radius:var(--r-card);
}
body.side-client .tag{font-size:var(--t-sm); padding:5px 12px;}
body.side-client .card,
body.side-client .panel{border-radius:var(--r-soft);}
body.side-client .empty{border-radius:var(--r-soft); padding:var(--s7) var(--s5);}
body.side-client .empty__title{font-size:var(--t-xl);}
body.side-client .empty__body{font-size:var(--t-lg);}
body.side-client .toast{font-size:var(--t-md);}

/* the spine is taller and rounder on this side */
body.side-client .spine-host::before{border-radius:var(--r-soft) 0 0 var(--r-soft);}

/* The practice's brand line on the sign-in and set-password pages. The
   checklist has its own, smaller one (`.cl-brand`, D-101). */
.cli__brand{display:flex; align-items:center; gap:var(--s3); min-width:0;}
.cli__brandmark{
  width:38px; height:38px; flex:none;
  display:grid; place-items:center;
  border-radius:var(--r-ctl);
  background:var(--accent-deep); color:var(--paper);
  font-family:var(--f-display); font-weight:700; font-size:var(--t-base);
  letter-spacing:-.02em; line-height:1;
  overflow:hidden;
}
.cli__brandmark img{width:100%; height:100%; object-fit:cover;}
.cli__brandname{
  font-size:var(--t-md); font-weight:600; color:var(--slate); line-height:1.3;
  overflow-wrap:anywhere;
}

/* ---- M12: the client list (D-100, D-101, D-102, D-103) -----------------
   Ana's one-line table with the upload window beside the headline. Every rule
   is scoped under `.cli`, `.cl-page` or a `cl-` class: the upload and brand
   classes the old page used are shared with the import screen and the login
   page, and restyling them would have changed those too.

   Three type sizes and no others (D-101), as tokens so a stray size is
   greppable and `test_portal_layout.py` can refuse a fourth.

   The list is a box that fills the window to its bottom edge, with nothing
   below it: the page does not scroll on a desktop (D-103). Under
   `html.cl-phone` - set by `portal.js` when the window is 760px wide or less,
   or the box would be under 270px - the page scrolls and the window becomes
   a fixed button. Those are the only two layouts. */

.cl-page{ --t-cli-lg:34px; --t-cli:16px; --t-cli-sm:13px; }

body.cl-page{ height:100dvh; overflow:hidden; }
.cli{
  max-width:1120px; height:100dvh;
  margin:0 auto; padding:var(--s6) var(--s5) 0;
  display:flex; flex-direction:column;
  min-width:0;
  font-size:var(--t-cli); line-height:1.5;
}

.cli .cl-top{
  display:flex; align-items:center; justify-content:space-between;
  gap:var(--s3) var(--s4); flex-wrap:wrap;
}
.cli .cl-brand{
  display:flex; align-items:center; gap:10px; min-width:0;
  font-size:var(--t-cli-sm); color:var(--slate);
}
.cli .cl-mark{
  width:32px; height:32px; flex:none;
  display:grid; place-items:center; overflow:hidden; object-fit:cover;
  border-radius:var(--r-ctl);
  background:var(--accent-deep); color:var(--paper);
  font-size:var(--t-cli-sm); font-weight:700; letter-spacing:-.02em; line-height:1;
}
.cli .cl-brandname{ overflow-wrap:anywhere; }
.cli .cl-links{ display:flex; align-items:center; gap:var(--s4); }
.cli .cl-signout{ margin:0; display:flex; }
.cli .cl-link{
  font:inherit; font-size:var(--t-cli-sm); line-height:1.4;
  color:var(--slate); background:none; border:0; padding:0; cursor:pointer;
}
.cli .cl-link:hover{ color:var(--ink); text-decoration:underline; }
.cli .cl-link--accent{
  color:var(--accent-deep);
  text-decoration:underline; text-underline-offset:2px;
}
.cli .cl-link--accent:hover{ color:var(--ink); }
.cli .langtoggle__opt{ font-size:var(--t-cli-sm); font-weight:400; letter-spacing:0; }

/* The headline block and the upload window, tops level (D-100). */
.cli .cl-lead{
  display:grid; grid-template-columns:minmax(0,1fr) minmax(340px,420px);
  gap:var(--s6); align-items:start;
  margin-top:var(--s7);
}
.cli .cl-hero{ display:flex; flex-direction:column; min-width:0; }
.cli .cl-got{ font-size:var(--t-cli-sm); color:var(--slate); }
.cli .cl-h1{
  font-family:var(--f-display);
  font-size:var(--t-cli-lg); font-weight:700; line-height:1.1; letter-spacing:-.02em;
  text-wrap:balance; margin-top:6px;
}
/* The deadline line. Plain text while far off; the amber and red pills keep
   the chip's colours and its calendar glyph, so the state is never carried by
   colour alone (D-083, D-084). */
.cli .cl-by{
  align-self:flex-start; gap:var(--s2);
  margin-top:var(--s2);
  font-size:var(--t-cli); font-weight:400;
}
.cli .cl-by b{ font-weight:600; color:var(--accent-deep); }
.cli .dchip--calm{ padding:0; background:none; color:var(--ink); }
.cli .dchip--soon,
.cli .dchip--late{ padding:5px 12px; font-weight:600; }
.cli .dchip--soon b,
.cli .dchip--late b{ color:inherit; }
.cli .cl-bar{ margin-top:var(--s4); }

.cli .cl-up{
  display:flex; flex-direction:column; align-items:center; justify-content:center;
  gap:6px; text-align:center;
  min-height:190px; padding:18px 22px;
  border:2px dashed color-mix(in srgb, var(--accent-deep) 45%, transparent);
  border-radius:var(--r-soft);
  background:var(--paper);
  cursor:pointer;
  transition:background .2s ease, border-color .2s ease;
}
.cli .cl-up__mark{
  width:46px; height:46px; flex:none; margin-bottom:4px;
  display:grid; place-items:center;
  border-radius:var(--r-pill);
  background:var(--wash); color:var(--accent-deep);
}
.cli .cl-up__title{ font-size:var(--t-cli); color:var(--ink); }
.cli .cl-up__pick{ font-size:var(--t-cli); font-weight:600; }
.cli .cl-up__hint{ font-size:var(--t-cli-sm); color:var(--slate); }
/* Continuous uploads (D-142, CU.9): the window in every state. The state
   block takes the hero's slot and the window keeps column 2, so the header
   keeps its two columns whether rows are missing, all in, or not asked yet. */
.cli .cl-lead--always > .cl-hero,
.cli .cl-lead--always > .celebrate{ grid-column:1; grid-row:1; }
.cli .cl-lead--always > .cl-up{ grid-column:2; grid-row:1; }
.cli .cl-lead--always > .celebrate{ margin-top:0; align-self:stretch; justify-content:center; }
html.cl-phone .cli .cl-lead--always > .cl-up{ grid-column:auto; }
/* No list yet (CU.9c): no state block, so the window takes the whole width,
   over the list of what was sent. */
.cli .cl-lead--solo{ grid-template-columns:minmax(0,1fr); }
.cli .cl-lead--solo > .cl-up{ grid-column:1; }

/* A file held over the page (D-100): a copy of the window, fixed on its
   rectangle, grows to fill the viewport in 0.3 s and shrinks back. The box
   animates, never a scale, so the words inside keep their size and place;
   only "ou escolha os ficheiros" fades, keeping its space. The global
   reduced-motion rule makes both moves instant. */
.cli .cl-fly{
  --fly-ease:cubic-bezier(.2,.8,.2,1);
  position:fixed; z-index:90; margin:0;
  box-shadow:0 20px 60px rgba(34,66,81,.18);
  transition:top .3s var(--fly-ease), left .3s var(--fly-ease),
             width .3s var(--fly-ease), height .3s var(--fly-ease),
             border-color .3s ease;
}
.cli .cl-fly.is-open{
  top:12px !important; left:12px !important;
  width:calc(100vw - 24px) !important; height:calc(100dvh - 24px) !important;
  border-color:var(--accent-deep);
}
.cli .cl-fly .cl-up__mark{ transition:transform .3s var(--fly-ease); }
.cli .cl-fly.is-open .cl-up__mark{ transform:scale(1.35); }
.cli .cl-fly .cl-up__pick{ transition:opacity .2s ease; }
.cli .cl-fly.is-open .cl-up__pick{ opacity:0; }

/* The completion and waiting states. No rule under the title (D-100). */
.cli .celebrate{
  display:flex; flex-direction:column; align-items:center;
  gap:var(--s3); text-align:center;
  margin-top:var(--s5); padding:var(--s7) var(--s5);
  background:var(--paper);
  border-radius:var(--r-soft);
  box-shadow:var(--ring);
  animation:conf-pop .5s var(--ease-out) both;
}
.cli .celebrate__mark{
  width:64px; height:64px; flex:none;
  display:grid; place-items:center;
  border-radius:var(--r-pill);
  background:var(--ok-wash); color:var(--ok);
}
.cli .celebrate__mark svg{ width:30px; height:30px; }
.cli .celebrate__title{
  font-size:var(--t-cli-lg); font-weight:700; letter-spacing:-.02em;
  color:var(--ok); line-height:1.1;
}
.cli .celebrate__body{ color:var(--slate); max-width:46ch; line-height:1.5; }
.cli .celebrate__foot{ font-size:var(--t-cli); color:var(--slate); line-height:1.5; }

.cli .cl-tools{ display:flex; align-items:center; flex-wrap:wrap; gap:var(--s2) var(--s5); margin-top:var(--s5); }
/* `Receber lembretes por email` (D-122): the tools row's shape, at the foot. */
.cli .cl-remind{ display:flex; align-items:center; flex-wrap:wrap; gap:var(--s2) var(--s5); margin-top:var(--s5); }
.cli .cl-check{
  display:inline-flex; align-items:center; gap:10px;
  padding:6px 0;
  font-size:var(--t-cli-sm); color:var(--slate);
  cursor:pointer; user-select:none;
}
/* Transparent only while unticked: the shared tick rule must still fill it. */
.cli .cl-check input:not(:checked) + .cb{ background:transparent; }
.cli .cl-check:hover .cb{ border-color:var(--accent-deep); }
.cli .cl-print{
  font-size:var(--t-cli-sm); color:var(--accent-deep);
  text-decoration:underline; text-underline-offset:2px;
}
/* "Em falta" / "Ja enviados" (D-137): a darker blue well with no outline,
   and a pill in the page's own blue behind the pressed option that slides on
   the spring (Hugo, 12 Oct: not white, no outlines).
   13 px for the label and the count, because D-101 allows three sizes; the
   mockup's 14 and 12 would be a fourth and a fifth. `font:inherit` comes
   before the size, so no shorthand can carry a pixel size past the guard.
   The grey pill is invisible until `portal.js` has placed it (`is-placed`),
   and slides only once it is live (`is-live`, a frame after), so a load or a
   resize puts it in place without a slide. */
.cli .cl-seg{
  position:relative; display:inline-flex; padding:4px;
  border-radius:12px; background:var(--seg-well);
}
.cli .cl-seg__bg{
  position:absolute; top:4px; bottom:4px; left:0; width:0;
  border-radius:var(--r-ctl); background:var(--seg-pill); opacity:0;
}
.cli .cl-seg.is-placed .cl-seg__bg{ opacity:1; }
.cli .cl-seg.is-live .cl-seg__bg{
  transition:transform .3s var(--spring), width .3s var(--spring);
}
.cli .cl-seg__opt{
  position:relative; z-index:1; display:inline-flex;
  align-items:center; gap:6px; padding:9px 14px; border:0;
  border-radius:var(--r-ctl); background:none; cursor:pointer;
  font:inherit; font-size:var(--t-cli-sm); font-weight:600; line-height:1;
  color:var(--slate); transition:color .1s; white-space:nowrap;
}
.cli .cl-seg__opt[aria-pressed="true"]{ color:var(--ink); }
.cli .cl-seg__ct{
  font-size:var(--t-cli-sm); font-weight:600;
  color:var(--slate); background:color-mix(in srgb, var(--ink) 6%, transparent);
  border-radius:var(--r-pill); padding:2px 7px;
  font-variant-numeric:tabular-nums;
}
/* "Ja enviados" at zero: one line in the box (D-137). */
.cli .cl-none{
  margin:0; padding:var(--s4) var(--s5);
  font-size:var(--t-cli-sm); color:var(--slate);
}

/* The box: the only scroller, and the last thing on the page. Its bottom is
   the window's bottom; min-height is the column headers plus about five rows. */
.cli .cl-box{
  flex:1 1 0; min-height:270px;
  overflow:auto; overscroll-behavior:contain;
  margin-top:var(--s3);
  border-top:1px solid color-mix(in srgb, var(--ink) 14%, transparent);
  /* One grid for the whole list: the header, the groups and every row are
     subgrids of it, so each column is as wide as its widest cell and the
     Categoria column lines up across rows (D-112). Supplier, Categoria,
     Data, Valor, action; without any category the column is not there. */
  display:grid; align-content:start; column-gap:var(--s4);
  grid-template-columns:minmax(0,1fr) max-content minmax(96px,max-content) minmax(96px,max-content) max-content;
}
.cli .cl-box[data-nocat]{
  grid-template-columns:minmax(0,1fr) minmax(96px,max-content) minmax(96px,max-content) max-content;
}
.cli .cl-box > *{ grid-column:1 / -1; }
.cli .cl-box > .missing,
.cli .cl-box > .cl-group{ display:grid; grid-template-columns:subgrid; }
.cli .cl-cols,
.cli .mrow{
  display:grid; grid-column:1 / -1;
  grid-template-columns:subgrid;
  gap:0 var(--s4); align-items:center;
  padding:0 var(--s5);
}
.cli [data-nocat] .cl-catcol,
.cli [data-nocat] .mrow__cat{ display:none; }
.cli .cl-cols{
  position:sticky; top:0; z-index:1;
  padding-top:var(--s3); padding-bottom:var(--s2);
  background:var(--field);
  border-bottom:1px solid var(--line);
  font-size:var(--t-cli-sm); color:var(--slate);
}
/* The files' header (D-143): a `.cl-cols` like section 1's, so as tall, as
   opaque and as sticky, inside its own group. Scrolled into the files it
   sticks over section 1's header, which sticks for the whole box: one layer
   up, so it covers it rather than relying on the order of the tree. */
.cli .cl-cols--files{ z-index:2; }
.cli .cl-num{ text-align:right; }
.cli .missing,
.cli .cl-group{ min-width:0; }
.cli .mrow{
  min-height:48px; padding-top:10px; padding-bottom:10px;
  border-bottom:1px solid color-mix(in srgb, var(--ink) 10%, transparent);
  transition:background .35s ease, opacity .3s ease;
}
.cli .mrow__sup{
  min-width:0;
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
  font-size:var(--t-cli); font-weight:600; color:var(--ink);
}
.cli .mrow__legal{ margin-left:8px; font-size:var(--t-cli-sm); font-weight:400; color:var(--slate); }
.cli .mrow__cat{ font-size:var(--t-cli-sm); color:var(--slate); white-space:nowrap; }
.cli .mrow__date{ font-size:var(--t-cli); color:var(--slate); white-space:nowrap; }
.cli .mrow__amt{
  text-align:right; white-space:nowrap;
  font-size:var(--t-cli); font-weight:600;
  font-variant-numeric:tabular-nums; font-feature-settings:"tnum" 1;
}
.cli .mrow__act{
  justify-self:end; margin:0;
  display:flex; align-items:center; gap:4px; white-space:nowrap;
  font-size:var(--t-cli-sm);
}
.cli .mrow__note{
  grid-column:1 / -1; justify-self:start;
  margin:6px 0 2px; padding:3px 8px;
  border-radius:6px; background:var(--wash);
  font-size:var(--t-cli-sm); color:var(--ink);
}
/* Received, held and lost: muted, regular weight, no fill (D-099, D-101). */
.cli .mrow--done .mrow__sup,
.cli .mrow--done .mrow__amt{ color:var(--slate); font-weight:400; }
.cli .mrow__got{ color:var(--ok); }
.cli .mrow__lost{ color:var(--slate); }
.cli .mrow.is-matching{ background:var(--ok-wash); }
.cli .mrow.is-matching .mrow__sup{ color:var(--ok); }
.cli .mrow.is-leaving{ opacity:0; }
/* An undone row, back in its place: washed for 0.9 s so the eye finds it. */
.cli .mrow.is-back{ background:var(--wash); }

.cli .cl-dock{ display:none; }

.cl-page .rtoast__t{ font-size:var(--t-cli); }
.cl-page .rtoast__s{ font-size:var(--t-cli-sm); }
/* The lost toast's undo (D-099): a real button, styled as the row's link. */
.cl-page .rtoast--lost .rtoast__undo{
  margin-top:4px; padding:0; border:0; background:none; cursor:pointer;
  font:inherit; font-size:var(--t-cli-sm); font-weight:600;
  color:var(--accent-deep); text-decoration:underline; text-underline-offset:2px;
}
.cl-page .rtoast--lost .rtoast__undo:hover{ color:var(--ink); }
.cl-page .rtoast--lost .rtoast__undo:disabled{ opacity:.5; cursor:default; }

/* The phone layout, the only other one. */
html.cl-phone body.cl-page{ height:auto; overflow:visible; }
html.cl-phone .cli{
  height:auto; min-height:100dvh;
  padding:var(--s4) var(--s4) calc(96px + env(safe-area-inset-bottom, 0px));
}
html.cl-phone .cli .cl-lead{ grid-template-columns:minmax(0,1fr); margin-top:var(--s5); }
html.cl-phone .cli .cl-up{ display:none; }
html.cl-phone .cli .cl-box{ flex:none; min-height:0; overflow:visible; }
html.cl-phone .cli .cl-box,
html.cl-phone .cli .cl-box > .missing,
html.cl-phone .cli .cl-box > .cl-group{ display:block; }
html.cl-phone .cli .cl-cols{ display:none; }
html.cl-phone .cli .cl-none{ padding:var(--s3) var(--s4); }
/* A phone row is two lines that wrap on their own, not a shared grid: the
   name and the amount, then the date and the action. The action no longer
   sets the width of the name's column, and when it does not fit beside the
   date it drops to its own line, right-aligned, instead of overlapping it.
   The category, when the client may see one, is a small line under the name.
   Each cell has its own `order`, so no two can land in the same place. */
html.cl-phone .cli .mrow{
  display:flex; flex-wrap:wrap; align-items:baseline;
  column-gap:12px; row-gap:2px; padding:var(--s3) var(--s4);
}
html.cl-phone .cli .mrow::after{ content:""; order:4; flex:0 0 100%; height:0; }
html.cl-phone .cli .mrow__sup{ order:1; flex:1 1 0; }
html.cl-phone .cli .mrow__legal{ display:block; margin:0; overflow:hidden; text-overflow:ellipsis; }
html.cl-phone .cli .mrow__amt{ order:2; flex:none; }
html.cl-phone .cli .mrow__cat{ order:3; flex:0 0 100%; }
html.cl-phone .cli .mrow__cat:empty{ display:none; }
html.cl-phone .cli .mrow__date{ order:5; flex:none; }
html.cl-phone .cli .mrow__act{ order:6; flex:none; margin-left:auto; }
html.cl-phone .cli .mrow__note{ order:7; flex:0 1 auto; max-width:100%; }
html.cl-phone .cli .cl-dock{
  display:block;
  position:fixed; z-index:45; left:0; right:0; bottom:0;
  padding:var(--s3) var(--s4) calc(var(--s3) + env(safe-area-inset-bottom, 0px));
  background:linear-gradient(transparent, var(--field) 35%);
}
html.cl-phone .cli .cl-dock .btn{
  display:flex; align-items:center; justify-content:center; gap:var(--s2);
  width:100%; min-height:52px;
  font-size:var(--t-cli); font-weight:600;
}
/* Toasts sit above the fixed button, never on top of it (D-098). */
html.cl-phone .cl-page .rtoasts{ bottom:calc(92px + env(safe-area-inset-bottom, 0px)); }

.prog__bar{
  display:block;
  width:100%;
  height:10px;
  border-radius:var(--r-pill);
  background:color-mix(in srgb, var(--line) 50%, transparent);
  overflow:hidden;
}
.prog__fill{
  display:block; height:100%; width:0;
  border-radius:var(--r-pill);
  background:var(--accent);
  transition:width .5s var(--ease-out);
}
.prog__note{font-size:var(--t-md); color:var(--slate); line-height:1.5;}

.up__zone{
  display:flex; flex-direction:column; align-items:center; justify-content:center;
  gap:var(--s3); text-align:center;
  /* A fixed floor, so the box cannot resize under a file the accountant is
     already holding (F41). */
  min-height:232px;
  padding:var(--s6) var(--s5);
  border:2px dashed color-mix(in srgb, var(--accent) 42%, transparent);
  border-radius:var(--r-soft);
  background:var(--paper);
  transition:background .2s ease, border-color .2s ease;
}
/* No transform here on purpose: the zone must not grow or shift while a
   dragged file hovers over it. */
.up__zone--over{background:var(--wash); border-color:var(--accent);}
/* A file is chosen (the import card, import.js): solid rather than dashed, so
   it reads as filled and not still waiting. */
.up__zone--chosen{background:var(--wash); border-style:solid; border-color:var(--accent);}
.up__pick{color:var(--accent-deep); text-decoration:underline;
  text-decoration-style:dotted; text-underline-offset:4px;}
.up__mark{
  width:52px; height:52px; flex:none;
  display:grid; place-items:center;
  border-radius:var(--r-pill);
  background:var(--wash); color:var(--accent-deep);
}
.up__mark svg{width:24px; height:24px;}
.up__title{font-size:var(--t-xl); font-weight:600; letter-spacing:-.01em; line-height:1.25;}
.up__hint{font-size:var(--t-md); color:var(--slate); max-width:36ch; line-height:1.5;}

.fpill{
  display:flex; align-items:flex-start; gap:var(--s3);
  padding:var(--s4);
  border-radius:var(--r-card);
  font-size:var(--t-md); font-weight:500; line-height:1.45;
  animation:conf-pop .35s var(--ease-out) both;
}
/* The sign-in and set-password notices. The portal's own pills went with
   D-098, and their `dup` and `wait` variants with them. */
.fpill--ok{background:var(--ok-wash); color:var(--ok);}
.fpill--review{background:var(--soon-wash); color:var(--soon);}
.fpill__mark{
  flex:none;
  width:20px; height:20px;
  display:grid; place-items:center;
  margin-top:1px;
}
.fpill__mark svg{width:100%; height:100%;}
.fpill__text{min-width:0; overflow-wrap:anywhere;}


/* === KEYFRAMES =========================================================== */

@keyframes conf-pop{
  from{opacity:0; transform:translateY(5px);}
  to{opacity:1; transform:none;}
}
@keyframes conf-rise{
  from{opacity:0; transform:translateY(10px) scale(.985);}
  to{opacity:1; transform:none;}
}
@keyframes conf-fade{
  from{opacity:0;}
  to{opacity:1;}
}


/* === RESPONSIVE ========================================================== */

/* the rail collapses to a top bar */
@media (max-width:900px){
  .acc{display:flex; flex-direction:column; padding-left:0;}
  .rail{
    position:sticky; z-index:40;
    left:auto; top:0; bottom:auto;
    width:auto;
    flex-direction:row; align-items:center; gap:var(--s3);
    padding:var(--s2) var(--s4);
    box-shadow:inset 0 -1px 0 var(--line);
    overflow-x:auto; overflow-y:hidden;
  }
  .rail__product{flex:none; padding-bottom:0;}
  .rail__brand{
    flex:none;
    padding-bottom:0;
    border-bottom:0;
    gap:1px var(--s2);
  }
  .rail__mark{width:30px; height:30px;}
  .rail__role{display:none;}
  .rail__name{font-size:var(--t-xs); white-space:nowrap;}
  .rail__nav{flex-direction:row; gap:2px; flex:none;}
  .rail__item{width:auto; padding:7px var(--s3); white-space:nowrap; font-size:var(--t-sm);}
  .rail__foot{
    margin-top:0; margin-left:auto;
    flex:none; flex-direction:row; align-items:center; gap:var(--s3);
    padding-top:0;
    border-top:0;
    white-space:nowrap;
  }
  .main__inner{padding:var(--s5) var(--s5) 130px;}
  .detail__top{grid-template-columns:1fr;}
  .settings{grid-template-columns:1fr;}
}

/* the client list becomes stacked cards */
@media (max-width:760px){
  .clist{overflow-x:visible; padding:0; margin:0; gap:var(--s3);}
  .clist__head{display:none;}

  /* switching to flex makes every grid-column / grid-row above inert */
  .crow{
    display:flex; flex-wrap:wrap; align-items:center;
    gap:var(--s2) var(--s3);
    min-width:0; min-height:0;
    padding:var(--s4) var(--s4) var(--s4) var(--s5);
    font-size:var(--t-base);
  }
  .crow__id{order:0;}
  .crow__name{order:1; flex:1 1 auto; font-size:var(--t-md); white-space:normal;}
  .crow__status{order:2; flex:none;}
  .crow__sub{order:3; flex:1 1 100%; font-size:var(--t-sm);}
  .crow__subtext{white-space:normal;}
  .crow__prog{order:4; flex:1 1 120px; padding-bottom:0;}
  .crow__count{order:5; flex:none; font-size:var(--t-sm);}
  .crow__deadline{order:6; flex:none;}
  .crow__days{order:7; flex:none; font-size:var(--t-sm);}
  .crow__nif{order:8; flex:none;}
  .crow__period{order:9; flex:none;}
  .crow__activity{order:10; flex:1 1 100%; white-space:normal;}

  /* the toast would sit under the dev toggle at this width */
  .toast{top:var(--s4); bottom:auto; transform:translate(-50%,-14px);}
  .toast--on{transform:translate(-50%,0);}

  .timeline{--tl-when:64px;}
  .control__grid,.rulegrid{grid-template-columns:1fr;}
  .modal{padding:var(--s3);}
  .modal__panel{max-height:calc(100vh - 2 * var(--s3));}
}

@media (max-width:560px){
  .main__inner{padding:var(--s4) var(--s4) 140px;}
  .page__head{align-items:flex-start;}
  .page__title{font-size:var(--t-lg);}
  .up__title{font-size:var(--t-lg);}

  .summary{gap:var(--s3) var(--s4);}
  .summary__item{padding-left:var(--s4);}
  .summary__val{font-size:var(--t-xl);}

  .toolbar__search{margin-left:0; flex:1 1 100%;}

  .idcard{padding:var(--s4); gap:var(--s1) var(--s3);}
  .idcard__mark{width:38px; height:38px;}
  .notes{padding:var(--s4);}
  .wizard__body{padding:var(--s5) var(--s4);}
  .wizard__steps,.wizard__foot{padding-inline:var(--s4);}
  .card__head,.card__foot,.card__body,
  .panel__head,.panel__body,.modal__head,.modal__body,.modal__foot{padding-inline:var(--s4);}

  .up__zone{padding:var(--s5) var(--s4);}

  .devbar{
    right:var(--s2); bottom:var(--s2);
    max-width:calc(100vw - 2 * var(--s2));
    gap:6px; padding:6px 7px;
  }
  .devbar__label{display:none;}
}

/* 360px: nothing may push the body sideways */
@media (max-width:400px){
  .brand{gap:var(--s3);}
  .brand__preview{width:100%;}
}


/* === REDUCED MOTION ====================================================== */

@media (prefers-reduced-motion: reduce){
  *,*::before,*::after{
    animation-duration:.01ms !important;
    animation-iteration-count:1 !important;
    transition-duration:.01ms !important;
    transition-delay:0ms !important;
    scroll-behavior:auto !important;
  }
}

/* ============================================================================
   Additions for the built product. Everything above this line is the prototype
   stylesheet and carries over untouched (D-051): the token block, the
   ring-before-shadow rule, the status colours and the "missing has no colour"
   decision are load-bearing and were validated in front of Ana.
   
   The client list itself was rebuilt in M12 (D-100 to D-103) and now lives
   in its own scoped block under CLIENT PORTAL B1. The not-available control
   and the portal footer that used to be described here went with it.
   ========================================================================= */


/* A `.crow` is a link here rather than the donor's div-with-handler, so the
   whole row is keyboard reachable without a script. Link decoration would
   underline every cell in it, which is what happened first. */
a.crow, a.crow:hover, a.crow:focus{ text-decoration:none; color:inherit; }

/* A2's reconciliation grid loses two columns.
   
   `--recon-cols` above is seven: supplier, document, date, Valor liquido, IVA,
   total, status. PROTOTYPE-DELTA drops the two money columns - her second ask
   on the call, and forced by the data regardless, because the TOConline export
   carries only `Total do documento` so those columns have nothing to show.
   
   Scoped to `.recon` so the token itself is untouched and anything else using
   it keeps the original seven.

   Six tracks since F9: the category left the supplier cell for a column of
   its own, which is what makes it scannable down the page. */
.recon{ --recon-cols:minmax(170px,2fr) minmax(96px,.9fr) minmax(140px,1.3fr) 96px 104px 110px 60px; }
.recon__hcell--num{ text-align:right; }


/* The confirm sentence sits with the button that asked (F29), spanning
   the row's grid under its own controls. */
.supmap__confirm{
  grid-column:1 / -1;
  font-size:var(--t-sm); color:var(--late);
  padding-top:var(--s1);
  text-align:right;
}

/* The one-shot password dialog (F27). */
.pwmodal{
  border:0; border-radius:var(--r-card);
  padding:var(--s5) var(--s6);
  max-width:min(440px, calc(100vw - 2 * var(--s4)));
  box-shadow:var(--lift-lg), var(--ring);
  background:var(--paper); color:var(--ink);
}
.pwmodal::backdrop{ background:rgba(34,66,81,.45); }
.pwmodal__title{ font-size:var(--t-lg); margin-bottom:var(--s3); }
.pwmodal__secret{
  font-size:var(--t-xl); font-weight:700; letter-spacing:.02em;
  font-variant-numeric:tabular-nums; margin-bottom:var(--s3);
}
.pwmodal__foot{ display:flex; justify-content:flex-end; margin-top:var(--s4); }

/* The stop-asking cluster holds one line (F25): the reason select flexes,
   its button sits beside it, and Fechar keeps the right edge - instead of a
   full-width dropdown over a stray button. */
.modal__foot .field--row{ flex:1 1 auto; min-width:0; flex-wrap:nowrap; margin-right:auto; }
.modal__foot .field--row .select{ flex:1 1 200px; min-width:0; width:auto; }
.modal__foot .field--row .btn{ flex:none; white-space:nowrap; }

/* The category popover (F37, design B): one floating panel shared by every
   pill on the table. The pill itself is a control now and says so on hover. */
.tag--cat{ cursor:pointer; font:inherit; font-size:var(--t-xs); }
.tag--cat:hover{ border-color:var(--accent); box-shadow:0 0 0 1px var(--accent); color:var(--accent-deep); }
.catpick{
  position:absolute; z-index:70; width:270px;
  background:var(--paper); border-radius:var(--r-ctl);
  box-shadow:var(--ring), var(--lift-lg);
  padding:var(--s2);
}
.catpick__search{ width:100%; margin-bottom:var(--s2); }
.catpick__list{ max-height:220px; overflow:auto; }
.catpick__opt{
  display:block; width:100%; text-align:left;
  padding:var(--s2) var(--s3);
  font:inherit; font-size:var(--t-sm); color:var(--ink);
  background:transparent; border:0; border-radius:6px; cursor:pointer;
}
.catpick__opt:hover{ background:var(--wash); }
.catpick__create{
  display:block; width:100%; text-align:left;
  padding:var(--s2) var(--s3); margin-top:2px;
  font:inherit; font-size:var(--t-sm); font-weight:600; color:var(--accent-deep);
  background:transparent; border:0; border-top:1px solid var(--line);
  border-radius:0; cursor:pointer;
}
.catpick__create:hover{ background:var(--wash); }
.catpick__form{ display:none; }
.catpick__note{
  margin-top:var(--s2); padding:var(--s2) var(--s3);
  font-size:var(--t-xs); color:var(--slate); background:var(--chip, #eef2f1);
  border-radius:6px;
}

/* F23, design D: the reason leads the held card in ink weight, and the one
   card in another NIF's name also wears the badge - attaching that one puts
   someone else's expense in this client's books. Every other reason stays
   badge-free so the queue never reads as an error list (D-049). */
.held__lead{ font-size:var(--t-md); font-weight:650; color:var(--ink); margin-bottom:var(--s3); }
.held__lead small{ display:block; font-weight:500; color:var(--slate); font-size:var(--t-sm); margin-top:3px; max-width:64ch; }
.hbadge{
  font-size:var(--t-xs); font-weight:700; letter-spacing:.05em;
  text-transform:uppercase; border-radius:var(--r-pill); padding:3px 10px;
  background:var(--late-wash); color:var(--late); white-space:nowrap;
}
.card__side{ display:flex; align-items:center; gap:var(--s3); flex:none; }
/* Files early for their list (D-142, CU.8): one quiet line and its fold on
   Por verificar, no colour (not work, not late), ring like a card. */
.held-wait{
  background:var(--paper); border-radius:var(--r-card); box-shadow:var(--ring);
  padding:var(--s3) var(--s4); margin-bottom:var(--s4);
}
.held-wait__line{ cursor:pointer; color:var(--slate); font-size:var(--t-sm); }
.held-wait__list{ list-style:none; margin:var(--s3) 0 0; padding:0; }
.held-wait__item{
  display:flex; flex-wrap:wrap; align-items:center; gap:var(--s2) var(--s3);
  padding:var(--s2) 0; border-top:1px solid var(--line); min-width:0;
}
.held-wait__client{ font-weight:650; color:var(--ink); }
.held-wait__file{ color:var(--ink); overflow-wrap:anywhere; min-width:0; }
.held-wait__day{ color:var(--slate); font-size:var(--t-sm); }
.held-wait__item .btn{ margin-left:auto; }

/* The searchable attach combobox (F22). The list floats over the card in
   the paper surface; options are buttons so they are keyboard-reachable. */
.combo{ position:relative; flex:1; min-width:0; }
.combo__search{ width:100%; }
.combo__list{
  position:absolute; top:calc(100% + 4px); left:0; right:0; z-index:30;
  max-height:260px; overflow:auto;
  background:var(--paper); border-radius:var(--r-ctl);
  box-shadow:var(--ring), var(--lift);
}
.combo__opt{
  display:block; width:100%; text-align:left;
  padding:var(--s2) var(--s3);
  font:inherit; font-size:var(--t-sm); color:inherit;
  background:transparent; border:0; cursor:pointer;
}
.combo__opt:hover{ background:var(--wash); }

/* The never-asked state is quiet (F19): the green title belongs to a list
   that was actually delivered. */
.cli .celebrate--waiting .celebrate__title{ color:var(--slate); }

/* The just-added row flashes and fades (F14). The transition does the
   fading: the class is removed after 3s and the background eases back. */
.drow{ transition:background-color .9s ease; }
.drow--new{ background:var(--wash); }

/* The add-a-line summary is clickable and says so (F13). */
summary.card__head{ cursor:pointer; }

/* Every header sorts (F10): click for ascending, again for descending. */
.recon__hcell{ cursor:pointer; user-select:none; }
.recon__hcell:hover{ color:var(--accent-deep); }
.recon__hcell--asc::after{ content:" \2191"; }
.recon__hcell--desc::after{ content:" \2193"; }

/* Every cell placed explicitly, because the donor's 6 and 7 placements are
   how defect seven happened: cells landing in implicit columns off the end
   of the grid while declared columns sat empty, and nothing looking broken.
   `test_grid_cells_land_in_columns_that_exist` reads these now. */
.recon .drow__cat{ grid-column:2; min-width:0; }
.recon .drow__doc{ grid-column:3; }
.recon .drow__date{ grid-column:4; }
.recon .drow__total{ grid-column:5; }
.recon .drow__status{ grid-column:6; }
.recon .drow__act{ grid-column:7; justify-self:end; }

/* ---- selecting several rows to stop asking (D-090) ---------------------
   A checkbox column, scoped to the client table with `.recon--pick` and NOT to
   `.recon`. The import preview renders `.drow` rows inside `.recon` too, so a
   track added to the token itself would move that screen as well.

   Every cell restated, because `test_grid_cells_land_in_columns_that_exist`
   reads the last declaration for each and asserts no track is left without a
   cell. `.drow__sup` is here for that reason alone: its only other declaration
   is the base block's `grid-column:1`, which the new column now occupies. */
.recon--pick{
  --recon-cols:34px minmax(170px,2fr) minmax(96px,.9fr) minmax(140px,1.3fr) 96px 104px 110px 60px;
}
.recon--pick .drow__pick{ grid-column:1; display:flex; align-items:center; }
.recon--pick .drow__sup{ grid-column:2; }
.recon--pick .drow__cat{ grid-column:3; }
.recon--pick .drow__doc{ grid-column:4; }
.recon--pick .drow__date{ grid-column:5; }
.recon--pick .drow__total{ grid-column:6; }
.recon--pick .drow__status{ grid-column:7; }
.recon--pick .drow__act{ grid-column:8; justify-self:end; }
.recon--pick .rpick{ display:flex; align-items:center; }

/* The import preview's own four columns (M4). It shares `.recon` with the
   client table, and F9's `.recon .drow__*` placements put its cells at 1, 3,
   4 and 5 under headers at 1 to 4. Two deliberate choices, both so
   `test_grid_cells_land_in_columns_that_exist` keeps reading the client
   table's values rather than these: the tracks are set directly instead of
   redefining `--recon-cols`, and cells are pinned by position rather than by
   `.drow__` class name. The mobile block turns `.drow` into flex, which makes
   all of this inert there, as intended. */
.recon--import .recon__head,
.recon--import .drow{
  grid-template-columns:minmax(200px,2.2fr) minmax(160px,1.6fr) 110px 110px;
}
.recon--import .recon__hcell:nth-child(1), .recon--import .drow > :nth-child(1){ grid-column:1; }
.recon--import .recon__hcell:nth-child(2), .recon--import .drow > :nth-child(2){ grid-column:2; }
.recon--import .recon__hcell:nth-child(3), .recon--import .drow > :nth-child(3){ grid-column:3; }
.recon--import .recon__hcell:nth-child(4), .recon--import .drow > :nth-child(4){ grid-column:4; }
/* F10 gave every `.recon__hcell` a sort cursor, but the preview does not load
   recon.js, so its headers looked clickable and were not. */
.recon--import .recon__hcell{ cursor:default; }
.recon--import .recon__hcell:hover{ color:var(--slate); }
/* The preview's row list is a flex column capped at 320px that scrolls. Below
   760px the mobile block sets `.drow{min-height:0}` so a row can wrap to two
   lines, which also let flexbox squash every row to fit the cap: only the
   supplier showed and rows overlapped. Rows keep their own height; the box
   scrolls instead. */
.parsed__table .drow{ flex-shrink:0; }

/* The box is a label for a real checkbox that is only visually hidden, so the
   keyboard and the accessibility tree get the native control and the drawing
   is ours. */
.cb{
  width:17px; height:17px; flex:none;
  border:1.5px solid var(--line); border-radius:5px;
  background:var(--paper);
  display:grid; place-items:center;
  cursor:pointer;
  transition:background .12s ease, border-color .12s ease;
}
.drow:hover .cb{ border-color:var(--accent-deep); }
input:checked + .cb,
input[data-pick-some] + .cb{ background:var(--accent-deep); border-color:var(--accent-deep); }
input:checked + .cb::after{
  content:""; width:4px; height:9px;
  border:solid var(--paper); border-width:0 2px 2px 0;
  transform:rotate(42deg) translate(-1px,-1px);
}
/* Partial selection: a dash, never a tick. A tick here claims every row. */
input[data-pick-some] + .cb::after{
  content:""; width:9px; height:2px; border-radius:1px; background:var(--paper);
}
input:focus-visible + .cb{ outline:2px solid var(--accent-deep); outline-offset:2px; }
.drow.is-picked{ background:var(--wash); }
.drow.is-picked:hover{ background:var(--wash); }

/* Static by default, so with no script it is simply the last thing in the
   list and the checkboxes still post to it. `bulkstop.js` promotes it to
   `--float` on load, which is what makes the rise possible: the transition
   needs the floating state to have been applied in an earlier frame than the
   class that reverses it, and a requestAnimationFrame does not reliably give
   that in a background tab. */
.selbar{
  display:flex; align-items:center; gap:var(--s3); flex-wrap:wrap;
  margin-top:var(--s4);
  padding:var(--s3) var(--s3) var(--s3) var(--s5);
  border-radius:var(--r-card);
  background:var(--ink); color:var(--paper);
  box-shadow:var(--lift-lg);
}
.selbar--float{
  position:fixed; left:50%; bottom:var(--s5); z-index:60;
  width:min(680px, calc(100vw - 2 * var(--s5)));
  margin-top:0;
  transform:translate(-50%, 120%); opacity:0; pointer-events:none;
  transition:transform .36s var(--ease-out), opacity .24s ease;
}
.selbar--float.selbar--on{
  transform:translate(-50%, 0); opacity:1; pointer-events:auto;
}
.selbar__n{ font-size:var(--t-base); font-weight:650; white-space:nowrap; }
.selbar__n b{
  font-family:var(--f-display); font-size:var(--t-lg);
  font-variant-numeric:tabular-nums; margin-right:5px;
}
.selbar__reason{
  font:inherit; font-size:var(--t-sm); font-weight:550;
  margin-left:auto; padding:8px 10px;
  border:0; border-radius:var(--r-ctl);
  background:#3b5666; color:var(--paper); cursor:pointer;
}
.selbar__reason option{ color:var(--ink); background:var(--paper); }
.selbar .btn--quiet{ color:#c3d2da; }
.undo{ margin-top:var(--s3); }

/* Estado is one pill everywhere (R-034, F8): Em falta red, Recebido green,
   Nao pedir neutral, identical wherever a status is written. The wash pairs,
   not --late itself - the loud red stays reserved for the overdue spine, so
   an always-red pill dilutes that signal as little as it can.

   The cell is still a button when there is a decision behind it: clicking
   what the row says is how you find out why it says that (D-028). The old
   dotted underline existed because a quiet button was pixel-identical to
   plain text; a button-pill gets the pointer and a hover ring instead. */
.stpill{
  display:inline-flex; align-items:center;
  border-radius:var(--r-pill); padding:3px 11px;
  font:inherit; font-size:var(--t-xs); font-weight:700; letter-spacing:.05em;
  text-transform:uppercase; white-space:nowrap; border:0;
}
.stpill--missing{background:var(--late-wash); color:var(--late);}
.stpill--matched{background:var(--ok-wash); color:var(--ok);}
.stpill--stopped{background:var(--wash); color:var(--slate);}
/* The kebab is the way into a row since F35: the pill is just a pill
   again, and the affordance lives in its own Accoes column. */
.drow__kebab{
  width:36px; height:36px;
  display:grid; place-items:center;
  border:0; border-radius:var(--r-pill);
  background:transparent; color:var(--slate);
  cursor:pointer;
}
.drow__kebab:hover{ background:var(--wash); color:var(--accent-deep); }
.material-symbols-outlined{ font-size:20px; line-height:1; }
.recon__hcell--noop{ cursor:default; }

/* The compared values in the trail drawer (D-028). Flex rather than a grid,
   because these rows carry two cells or three depending on whether there is
   another side to compare against, and a fixed track count would leave one of
   them with an empty column - see `.drow__total` above for how that reads. */
.ev{ display:flex; flex-direction:column; gap:2px; margin-top:var(--s2); }
.ev__row{
  display:flex; align-items:baseline; gap:var(--s3);
  font-size:var(--t-xs); line-height:1.5;
}
.ev__label{ flex:0 0 132px; color:var(--slate); }
.ev__val{ flex:1 1 0; min-width:0; font-variant-numeric:tabular-nums; }
.ev__row--head .ev__val{
  font-weight:600; letter-spacing:.08em; text-transform:uppercase;
  color:var(--slate);
}
.ev__row--agrees .ev__val{ color:var(--ok); }

/* The held queue's attach control. The select is capped rather than left to
   fill the card: a 1100px dropdown for a 40-character row label reads as a
   layout accident, and the button belongs beside it rather than under it. */
.card__body .field--row{ justify-content:flex-start; }
.card__body .field--row .field__label{ flex:0 0 auto; }
.card__body .field--row .select{ flex:1 1 320px; max-width:34rem; width:auto; }
/* SE.9b. Once held.js has built its combobox the select is `sr-only`, but the
   rule above is more specific and gave it back its natural width (496px), so
   the absolutely placed select pushed the page sideways on a phone. The
   combobox takes the select's place in the row, wrapping under the label when
   the card is narrow rather than squeezing beside it (it was 30-85px at 320
   and 375). */
.card__body .field--row .select.sr-only{ width:1px; }
.card__body .field--row .combo{ flex:1 1 320px; max-width:34rem; }

/* The supplier map. One line per supplier, because a practice has dozens and
   the stacked `.field--row` fallback gave each of them 150px of screen. */
/* The screen answering back, added with the categories (D-074). Before them
   the only control on A4 that could refuse was the logo, which 404s, and
   everything else silently succeeded - so there was nowhere for a refusal to
   appear. Amber rather than red: "you already have one of those" is not a
   failure, it is an answer. */
.settings__msg{
  padding:10px 14px; border-radius:var(--r-card);
  font-size:var(--t-sm); font-weight:600; line-height:1.4;
  background:var(--ok-wash); color:var(--ok);
}
.settings__msg--bad{ background:var(--soon-wash); color:var(--soon); }

.supmap{ display:flex; flex-direction:column; gap:var(--s2); }
.supmap__row{
  display:grid;
  grid-template-columns:minmax(180px,1.4fr) minmax(160px,1.6fr) 170px auto;
  gap:var(--s3);
  align-items:center;
}
.supmap__who{
  display:flex; flex-direction:column; gap:1px; min-width:0;
  font-size:var(--t-sm); font-weight:600; line-height:1.3;
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
}
/* The team rows on A4 reuse the supplier map's grid: same shape, four cells,
   one line per client. Two cells it does not have - the tags showing who
   covers the client, and the pair of buttons, which need a gap between them
   and would otherwise sit as one run-together control. */
.supmap__tags{ display:flex; flex-wrap:wrap; gap:var(--s2); align-items:center; min-width:0; }
.supmap__acts{ display:flex; gap:var(--s2); align-items:center; }

/* A team row places its cells explicitly, because its DOM order is not its
   layout order. `Atribuir` is written first so it is the form's default submit
   and Enter in the select cannot fire a removal; it belongs last on screen.
   `grid-row:1` on all four is load-bearing, not tidiness: with only the column
   pinned, auto-placement puts `acts` in column 4 and then has nowhere left in
   row 1 for the cells written after it, so the tags and the select dropped to a
   second row and every client's assignees read as belonging to the client
   below. Caught by looking at the deployed page (D-071), not by the tests,
   which were happy because the columns were right. */
.supmap__row--team .supmap__who{ grid-column:1; grid-row:1; }
.supmap__row--team .supmap__tags{ grid-column:2; grid-row:1; }
.supmap__row--team .select{ grid-column:3; grid-row:1; }
.supmap__row--team .supmap__acts{ grid-column:4; grid-row:1; }

/* A category row is three cells, not four: the name is editable so it is an
   input rather than a `who`, and there is no dropdown to file it under. DOM
   order and visual order are the same here, so nothing needs pinning - unlike
   the team row above, whose order deliberately differs. */
.supmap__row--cat{ grid-template-columns:minmax(180px,1.6fr) 170px auto; }

/* A staff row is four editable cells: name, the email they sign in with
   (D-075), the role, and the controls.

   **The last track is a fixed width, not `auto`, and that is the whole reason
   these rows line up.** Every row here is its own `<form>` and therefore its
   own grid container, so `auto` sizes the action cell to that row's own
   buttons - and the rows do not carry the same buttons. Your own row has no
   `Desactivar`, a switched-off row says `Reactivar`, and the add row has only
   `Adicionar`. With `auto` that produced four different column layouts in five
   rows, with the name and email fields jagging left and right down the card.

   The supplier map above never showed this because every one of its rows has
   exactly one button, so `auto` resolved to the same width every time. Found by
   looking at the screen, which is the only way it could have been found: every
   test passes either way. */
/* 390px, not 316px: the widest this cell ever gets is the row waiting for a
   second press, which carries `Guardar`, `Palavra-passe`, `Cancelar` and
   `Confirmar`. Sized for three buttons, the fourth spilled left and crushed the
   role dropdown of that one row - visible immediately on screen, invisible to
   every test. Size the track for the widest state, not the common one. */
/* The action track sizes to its buttons and may wrap (R-034, F33): at 390px
   fixed, three grown buttons clipped through the card edge and dragged a
   horizontal scrollbar with them. */
.supmap__row--staff{ grid-template-columns:minmax(110px,1fr) minmax(170px,1.3fr) minmax(130px,150px) minmax(0,auto); }
.supmap__row--staff .supmap__acts{ justify-content:flex-end; flex-wrap:wrap; row-gap:var(--s2); min-width:0; }

/* A switched-off colleague renders as a ghost (R-034, F28) - the amber wash
   read as a warning, not as "set aside". This supersedes the earlier call
   against dimming: the whole cell dims and desaturates together (box,
   border and value), which reads as a disabled control rather than an empty
   one - the confusion the old comment feared came from dimming the value
   alone. The one guard kept: the action cell stays at full strength,
   because a person you cannot see is a person you cannot bring back. */
.supmap__row--off > *:not(.supmap__acts){ opacity:.45; filter:grayscale(1); }

/* `Acessos dos clientes` (D-078). Three cells, not four: who, what state their
   access is in, and the one control.

   **The action track is fixed for the reason `--staff` states at length above.**
   Every row is its own form and therefore its own grid container, so `auto`
   sizes that cell to whichever buttons that row happens to carry - and these
   rows do not carry the same ones. A row says `Convidar` or `Novo convite`
   normally, and the one waiting for a second press says `Cancelar` and
   `Confirmar`. 200px is sized for that widest state rather than the common one,
   which is the mistake that had to be corrected twice on the staff row. */
.supmap__row--access{ grid-template-columns:minmax(180px,1.6fr) 150px 200px; }
.supmap__row--access .supmap__acts{ justify-content:flex-end; }
/* The state chip sits directly in a grid cell rather than inside a flex
   container the way the team row's tags do, and a grid item defaults to
   `justify-self:stretch` - so `Activo` rendered as a 150px slab with the word
   floating at its left edge, reading as an empty input rather than as a chip.
   Measured on the local page: 150px here against 84px for the identical
   `.tag` on the Equipa card one section above. Found by looking at it. */
.supmap__row--access .tag{ justify-self:start; }

/* Each assignee is its own remove control, so the target is never inferred
   from a dropdown. It reads as the tag it replaced and clicks as a button. */
.supmap__drop{ border:0; cursor:pointer; font-family:inherit; }
.supmap__drop:hover{ box-shadow:var(--ring); }
.supmap__x{ margin-left:5px; opacity:.55; font-weight:700; }

@media (max-width: 860px){
  .supmap__row{ grid-template-columns:1fr; }
  /* Named explicitly. `.supmap__row--cat` is a single class like
     `.supmap__row`, so it would win on source order alone and keep three
     columns on a phone. */
  .supmap__row--cat{ grid-template-columns:1fr; }
  /* Same trap, same fix: a single class beating `.supmap__row` on source
     order would keep four columns on a phone. */
  .supmap__row--staff{ grid-template-columns:1fr; }
  /* Same trap again: a single class beating `.supmap__row` on source order
     would keep three columns on a phone. */
  .supmap__row--access{ grid-template-columns:1fr; }
  /* Stacked, the explicit columns above would fight the single track, so the
     team row falls back to source order corrected by `order`. */
  .supmap__row--team > *{ grid-column:1; grid-row:auto; }
  .supmap__row--team .supmap__who{ order:1; }
  .supmap__row--team .supmap__tags{ order:2; }
  .supmap__row--team .select{ order:3; }
  .supmap__row--team .supmap__acts{ order:4; }
}

/* === A2 ON A NARROW VIEWPORT =============================================
   `.clist` got this treatment at 760 and `.recon` never did, so the
   reconciliation grid kept all five columns at every width and the page
   scrolled sideways: measured at **224px of overflow in a 500px viewport**,
   with `Fornecedor` - the column she reads first - scrolled off the left.

   Nothing caught it because nothing looked at it. Gate 5k walked A1, the held
   queue, settings and the whole portal at 390 and listed A2 only at 1440, so
   the one screen with the widest grid is the one narrow width never saw.

   Same technique as `.crow`: switch to flex, which makes every grid-column and
   grid-row rule above inert, then order the cells by what she needs first. */
@media (max-width:760px){
  .recon__table{overflow-x:visible;}
  .recon__head{display:none;}

  .drow{
    display:flex; flex-wrap:wrap; align-items:center;
    gap:var(--s2) var(--s3);
    min-width:0; min-height:0;
    padding:var(--s4) var(--s4) var(--s4) var(--s5);
  }
  .drow__sup{order:0; flex:1 1 100%; white-space:normal;}
  .drow__supname{white-space:normal;}
  .drow__doc{order:1; flex:1 1 auto;}
  .drow__date{order:2; flex:none;}
  /* Total and status share the last line, and the status is the control that
     opens the trail - it must not be the thing that wraps out of reach. */
  .drow__total{order:3; flex:none; text-align:left;}
  .drow__status{order:4; flex:none; margin-left:auto;}
}

/* `.settings` is auto-fit, so a bare save row becomes a third column beside
   the cards it saves. It spans instead. */
.settings__save{ grid-column:1 / -1; }

/* The login screen. Not in the prototype, which had no accounts at all.
   Built from existing tokens so a practice's accent re-skins it too. */
.authpage{
  min-height:100dvh;
  display:flex; align-items:center; justify-content:center;
  padding:var(--s5);
}
.authcard{
  width:100%; max-width:26rem;
  display:flex; flex-direction:column; gap:var(--s4);
  padding:var(--s6);
  background:var(--paper);
  border-radius:var(--r-card);
  box-shadow:var(--ring), var(--lift);
}
.authcard .page__title{ margin:0; }
.authcard .page__sub{ margin:0; }
.authform{ display:flex; flex-direction:column; gap:var(--s4); margin-top:var(--s2); }
.authfield{ display:flex; flex-direction:column; gap:var(--s2); }
.authfield__label{
  font-size:var(--t-sm); font-weight:600; color:var(--slate);
}
.authfield__input{
  font:inherit; font-size:var(--t-base);
  padding:10px 14px;
  border:0; border-radius:var(--r-ctl);
  background:var(--field);
  box-shadow:var(--ring);
  width:100%;
}
.authfield__input:focus-visible{
  outline:none; box-shadow:var(--ring), var(--focus-pad);
}
.authnote{
  margin:0; font-size:var(--t-sm); color:var(--slate); text-align:center;
}
/* Sign in with Google (GS.4, D-150), `_google_button.html`. An "ou" rule
   between the password form and Google's own, then Google's standard button
   in its light theme, as its branding guidelines give it and nothing of
   ours over it: white fill, a 1px #747775 border, #1F1F1F text, a medium
   14px label, 12px of padding left and right and 10px between the G and
   the label, the four-colour G at 20px, 40px tall. Roboto when the device
   has it (Google's face), else the page's own. Full width, like Entrar. */
.authsep{
  display:flex; align-items:center; gap:var(--s3);
  margin:var(--s1) 0 0;
  font-size:var(--t-sm); color:var(--slate);
}
.authsep::before,.authsep::after{ content:""; flex:1; height:1px; background:var(--line); }
.authgoogle{ margin:0; }
.gbtn{
  display:flex; align-items:center; justify-content:center; gap:10px;
  width:100%; min-height:40px; padding:0 12px;
  background:#FFFFFF; color:#1F1F1F;
  border:1px solid #747775; border-radius:var(--r-ctl);
  font-family:Roboto, var(--f-body); font-weight:500; font-size:14px;
  line-height:20px; letter-spacing:.25px; white-space:nowrap;
  cursor:pointer;
  -webkit-appearance:none; appearance:none;
  transition:background .18s ease, box-shadow .18s ease;
}
.gbtn:hover{
  background:#F7F8F8;
  box-shadow:0 1px 2px rgba(60,64,67,.30), 0 1px 3px 1px rgba(60,64,67,.15);
}
.gbtn__logo{ display:block; width:20px; height:20px; flex:none; }
.gbtn__label{ min-width:0; overflow:hidden; text-overflow:ellipsis; }
/* The docolect logo on the auth card (D-115): 28px on top when no practice
   holds the card, 18px at its foot when one does (set-password, mockup b). */
.auth__logo{ display:block; width:113px; height:28px; }
.auth__foot{ margin:var(--s2) 0 0; display:flex; justify-content:center; line-height:0; }
.auth__footlogo{ display:block; width:73px; height:18px; }

/* The split login (F57). From 1024px up the three auth screens - both logins
   and set-password - become the reference layout: form centered in the left
   half of a paper page, one rounded gradient panel filling the right half
   with a decorative checklist floating on it. Below 1024px the panel does
   not exist and the centered card ships exactly as before; the split is
   desktop only on purpose.

   The gradient is mixed from --accent so a practice accent re-skins it like
   everything else (SPEC 9). The anonymous logins have no practice and show
   the product cyan; the set-password page already passes the accent on a
   valid link. The @supports gate is not decoration: the stops go through
   var(), so a browser without color-mix would resolve the declaration
   invalid at computed-value time and paint NO background rather than fall
   back to an earlier one - the gate keeps such a browser on the flat wash. */
.authsplit__panel{ display:none; }
@media (min-width:1024px){
  .authpage--split{
    display:grid; grid-template-columns:1fr 1fr; gap:var(--s4);
    /* .authpage centers its flex children; on the grid that would collapse
       the empty panel to nothing. The card re-centers itself with
       place-self. */
    align-items:stretch;
    padding:var(--s4); background:var(--paper);
  }
  .authpage--split .authcard{
    background:transparent; box-shadow:none; place-self:center;
  }
  .authsplit__panel{
    display:block;
    border-radius:var(--r-soft); overflow:hidden;
    background:var(--wash);
  }
  @supports (background: color-mix(in oklch, red 50%, white)){
    .authsplit__panel{
      background:
        radial-gradient(at 15% 12%, rgba(255,255,255,.85) 0, transparent 45%),
        radial-gradient(at 85% 8%,
          color-mix(in oklch, var(--accent) 18%, var(--paper)) 0, transparent 55%),
        radial-gradient(at 88% 82%,
          color-mix(in oklch, var(--accent) 65%, var(--paper)) 0, transparent 60%),
        linear-gradient(160deg,
          color-mix(in oklch, var(--accent) 12%, var(--paper)) 0%,
          color-mix(in oklch, var(--accent) 45%, var(--paper)) 55%,
          /* darker AND greyer than any accent+white mix can reach, so the
             pastel is pulled toward slate for the last stop */
          color-mix(in oklch,
            color-mix(in oklch, var(--accent) 55%, var(--paper)) 78%,
            var(--slate)) 100%);
    }
  }
  /* The background video (LOGIN.VIDEO) sits on the gradient and fills the
     panel the grid already sized, so nothing shifts when it arrives. With no
     poster and no src it paints nothing and the gradient shows; the poster
     covers the panel the same way the clip does. */
  .authsplit__panel{ position:relative; }
  .authsplit__video{
    position:absolute; inset:0; width:100%; height:100%;
    object-fit:cover; display:block;
  }
}

/* The date picker (F40). A text field first, with the calendar as the second
   way in - see static/js/datepicker.js for why that order. Sized off the
   shadcn calendar: square cells, one weight of type, whitespace instead of
   internal borders. */
.dpick{position:relative; display:flex; align-items:center;}
.dpick__input{padding-right:38px;}
.dpick__btn{
  position:absolute; right:4px;
  display:grid; place-items:center;
  width:30px; height:30px;
  border:0; border-radius:var(--r-ctl);
  background:transparent; color:var(--slate); cursor:pointer;
}
.dpick__btn:hover{background:var(--wash); color:var(--accent-deep);}
.dpick__pop{
  position:absolute; top:calc(100% + 4px); left:0; z-index:40;
  padding:var(--s4);
  background:var(--paper); border-radius:var(--r-card);
  box-shadow:var(--ring), var(--lift-lg);
}
.dpick__head{
  display:flex; align-items:center; justify-content:space-between;
  gap:var(--s2); margin-bottom:var(--s3);
}
/* `capitalize` would give "Setembro De 2026" - it hits every word. The month
   is a sentence, so only its first letter is. */
.dpick__month{font-size:var(--t-sm); font-weight:600; text-transform:lowercase;}
.dpick__month::first-letter{text-transform:uppercase;}
.dpick__chev{
  display:grid; place-items:center;
  width:26px; height:26px; flex:none;
  border:0; border-radius:var(--r-ctl);
  background:transparent; color:var(--slate); cursor:pointer;
  box-shadow:var(--ring);
}
.dpick__chev:hover{color:var(--accent-deep); background:var(--wash);}
.dpick__grid{border-collapse:collapse;}
.dpick__wd{
  width:32px; padding:2px 0 6px;
  font-size:var(--t-xs); font-weight:600; color:var(--slate);
}
.dpick__pad{width:32px; height:32px;}
.dpick__day{
  width:32px; height:32px;
  display:grid; place-items:center;
  border:0; border-radius:var(--r-ctl);
  background:transparent; color:var(--ink);
  font:inherit; font-size:var(--t-sm); cursor:pointer;
  font-variant-numeric:tabular-nums;
}
.dpick__day:hover{background:var(--wash);}
.dpick__day--today{box-shadow:inset 0 0 0 1.5px var(--accent); font-weight:600;}
.dpick__day--sel{background:var(--accent-deep); color:var(--paper); font-weight:600;}
.dpick__day--sel:hover{background:var(--accent-deep);}

/* On a phone the popover would hang off the edge of a field near the bottom
   of the form, so it becomes a sheet across the width instead. */
@media (max-width:640px){
  .dpick__pop{
    position:fixed; inset:auto 0 0 0; top:auto; z-index:60;
    display:flex; justify-content:center;
    border-radius:var(--r-soft) var(--r-soft) 0 0;
    padding:var(--s5) var(--s4) var(--s6);
    box-shadow:var(--lift-lg);
  }
}

/* The period's two deadlines, edited where they are read (F58). A details
   element rather than a script: the toolbar is server-rendered and this has
   no state worth a listener. */
.pdl{position:relative;}
.pdl > summary{cursor:pointer; list-style:none;}
.pdl > summary::-webkit-details-marker{display:none;}
.pdl__pop{
  position:absolute; top:calc(100% + 6px); left:0; z-index:30;
  width:min(19rem, 80vw);
  display:flex; flex-direction:column; gap:var(--s4);
  padding:var(--s5);
  background:var(--paper); border-radius:var(--r-card);
  box-shadow:var(--ring), var(--lift-lg);
}
.pdl__field{display:flex; flex-direction:column; gap:var(--s2);}
.pdl__foot{
  display:flex; align-items:center; justify-content:space-between;
  gap:var(--s3); flex-wrap:wrap;
}
.chip--btn u{text-decoration-style:dotted; text-underline-offset:3px;}

/* The buffer, written as a sentence with two controls in it (F58). */
.bufrow{display:flex; align-items:center; gap:var(--s2); flex-wrap:wrap;}
.bufrow__n{width:5rem; flex:none;}
.bufrow__u{width:auto; flex:none;}
.bufrow__tail{font-size:var(--t-base); color:var(--ink);}

/* The client's deadline chip (D-083). One control, three states, no
   countdown: units never go below a day and nothing animates, because a
   ticking number is urgency the product invented rather than urgency the
   practice chose. The calendar glyph is here so the state is never carried by
   colour alone. */
.dchip{
  /* The header is a stretching flex column, so an inline-flex chip still
     filled the width and read as a banner. It hugs its own text: this is one
     chip, not a bar across the page. */
  display:inline-flex; align-self:flex-start; align-items:center; gap:var(--s3);
  padding:10px 20px;
  border-radius:var(--r-pill);
  /* Bigger than the body text since D-084: three or four words have to carry
     the whole state, so they are read at a glance rather than scanned. */
  font-size:var(--t-lg); font-weight:600;
  background:var(--ok-wash); color:var(--ok);
}
.dchip__cal{
  flex:none; position:relative;
  width:17px; height:16px;
  border-radius:3px;
  box-shadow:inset 0 0 0 1.6px currentColor;
}
.dchip__cal::after{
  content:""; position:absolute; top:4px; left:0; right:0;
  height:1.6px; background:currentColor;
}
.dchip--soon{background:var(--soon-wash); color:var(--soon);}
.dchip--late{background:var(--late-wash); color:var(--late);}
/* The seconds change every second, and proportional digits would make the
   whole chip twitch as they do. */
.dchip--late [data-countdown]{font-variant-numeric:tabular-nums;}

/* The address that becomes a client's login, asked for on the row that
   creates it (F46). Sized so it does not shove the state and the button off
   the end of a narrow row. */
.supmap__email{max-width:15rem; font-size:var(--t-sm); padding:7px 10px;}

/* ---- the portal language toggle (D-092, D-094) --------------------------
   Two literal labels in one quiet pill, beside the portal's own links. A form
   of two submit buttons rather than links, because the invite page must not
   change state on a GET (D-079). */
.langtoggle{
  display:inline-flex; gap:2px; padding:2px;
  border-radius:var(--r-pill);
  background:var(--paper); box-shadow:var(--ring);
}
.langtoggle__opt{
  font:inherit; font-size:var(--t-xs); font-weight:700; letter-spacing:.06em;
  padding:4px 10px; border:0; border-radius:var(--r-pill);
  background:transparent; color:var(--slate); cursor:pointer;
}
.langtoggle__opt:hover{ color:var(--accent-deep); }
.langtoggle__opt.is-on{ background:var(--wash); color:var(--accent-deep); cursor:default; }
.langtoggle__opt:focus-visible{ outline:2px solid var(--accent-deep); outline-offset:2px; }
.authcard .langtoggle{ align-self:flex-end; }
/* The staff rail's toggle (SE.3): at the top of the rail foot, left, its own
   width; in the 900px top bar it sits in the foot's row beside "Sair". */
.rail__foot .langtoggle{ align-self:flex-start; flex:none; }
@media (max-width:900px){
  .rail__foot .langtoggle{ align-self:center; }
}
/* Brand left, PT/EN right, on one centre line (was two stacked rows, with the
   logo sitting above the toggle). */
.authcard__top{
  display:flex; align-items:center; justify-content:space-between;
  gap:var(--s3); min-width:0;
}
.authcard__top .langtoggle{ align-self:center; flex:none; }
/* The passwordless login page (LP.3, D-151): the docolect logo alone and
   centred at the top of the card, a quarter bigger than the 28px brand line
   (35px), with room under it; then "Entrar" and the PT/EN toggle on one row,
   the toggle at its right end, both on its centre line. */
.authlogo{ display:flex; justify-content:center; line-height:0; margin:0 0 var(--s4); }
.authlogo__img{ display:block; width:141px; height:35px; }
.authcard__title{
  display:flex; align-items:center; justify-content:space-between;
  gap:var(--s3); min-width:0;
}
.authcard__title .langtoggle{ align-self:center; flex:none; }
.authform--email{ margin-top:0; }
/* The invite page's one tap (LP.4): a form holding only "Entrar". */
.authform--enter{ margin-top:var(--s2); }

/* ---- the first sign-in onboarding modal (D-118, docs/ONBOARDING-PLAN.md) --
   Ported from Hugo's approved PT mockup (docolect-onboarding-modal-PT.html):
   the .ob, .intro, .imp, .em and .sp rules and their keyframes (the
   "Brevemente" badge's rule went with the badge, OB2.2; the deadline step's
   .tl, .tlv, .wk and .obs went with the step, DR.5, D-145; the reminders
   step's .rmc went with its copied-on-every-email line, OB2.8),
   unchanged except that .ob is now a native <dialog> (the backdrop
   is ::backdrop) and the art sits in an .ob__stage scaled by onboarding.js on
   narrow screens (M2). The mockup's skip, dots, replay button and unused
   helpers are left out. Tokens only; the rgba() shadows are --ink's own. */
dialog.ob{position:fixed; inset:0; width:100%; height:100%; max-width:none; max-height:none; margin:0;
  padding:var(--s4); border:0; background:transparent; overflow:auto; overscroll-behavior:contain;}
dialog.ob[open]{display:grid; place-items:center;}
dialog.ob::backdrop{background:rgba(34,66,81,.42); backdrop-filter:blur(2px);}
html:has(dialog.ob[open]){overflow:hidden;}
.ob__card{position:relative; width:min(720px,100%); background:var(--paper); border-radius:var(--r-soft);
  box-shadow:var(--lift-lg); overflow:visible;}
.ob__slide{display:none;}
.ob__slide.is-on{display:block; min-height:320px; animation:obIn .25s var(--ease-out);}
@keyframes obIn{from{opacity:0; transform:translateX(8px);}to{opacity:1; transform:none;}}
.ob__art{border-radius:var(--r-soft) var(--r-soft) 0 0; height:200px; display:grid; place-items:center; align-content:center; gap:var(--s4);
  background:var(--wash); padding:var(--s5);}
.ob__step{display:block; margin:var(--s5) var(--s6) 4px; font-size:var(--t-xs); font-weight:700; letter-spacing:.08em; text-transform:uppercase; color:var(--accent-deep);}
.ob__t{margin:var(--s5) var(--s6) var(--s2); font-family:var(--f-display); font-size:26px; font-weight:650; letter-spacing:-.02em; line-height:1.15; color:var(--ink);}
.ob__p{margin:0 var(--s6); font-size:17px; font-weight:500; line-height:1.45; color:var(--slate); max-width:none; text-wrap:pretty;}
.ob__foot .btn[disabled]{opacity:.35;}
.ob__foot{display:flex; align-items:center; gap:var(--s2); padding:var(--s6) var(--s6) var(--s5);}

/* Step 1 art: a TOConline export is dragged into the upload card, loops.
   One 4.8s timeline shared by every part. The slide is display:none when off,
   so the animation restarts from 0 each time the slide is shown. */
.imp{--imp-t:4.8s; --imp-d:0s; position:relative; width:560px; height:160px;}
.imp *{animation-duration:var(--imp-t); animation-delay:var(--imp-d); animation-iteration-count:infinite; animation-fill-mode:both;}
.imp__card{position:absolute; left:280px; top:10px; width:240px; height:140px; border-radius:var(--r-card);
  background:var(--paper); animation-name:impCard;}
.imp__card::before,.imp__card::after{content:""; position:absolute; inset:0; border-radius:inherit; pointer-events:none;}
.imp__card::before{border:1.5px dashed var(--accent-deep); animation:impDash var(--imp-t) var(--imp-d) infinite both;}
.imp__card::after{border:1.5px solid var(--accent); opacity:0; animation:impSolid var(--imp-t) var(--imp-d) infinite both;}
.imp__wash{position:absolute; inset:0; border-radius:inherit; background:var(--wash); opacity:0; animation-name:impSolid;}
.imp__st{position:absolute; inset:0; display:flex; flex-direction:column; align-items:center; justify-content:center; gap:var(--s3);
  padding:0 var(--s4); text-align:center;}
.imp__st--idle{animation-name:impIdle;}
.imp__st--done{gap:6px; opacity:0; animation-name:impDone;}
.imp__mark{width:44px; height:44px; display:grid; place-items:center; border-radius:var(--r-pill);
  background:var(--wash); color:var(--accent-deep); animation-name:impMark;}
.imp__mark svg{width:20px; height:20px;}
.imp__title{font-size:var(--t-md); font-weight:600; line-height:1.35; color:var(--ink);}
.imp__file{display:flex; align-items:center; gap:6px; white-space:nowrap; font-size:var(--t-sm); font-weight:600; color:var(--ink);}
.imp__file svg{width:18px; height:22px; flex:none;}
.imp__ok{width:20px; height:20px; flex:none; display:grid; place-items:center; border-radius:var(--r-pill);
  background:var(--ok); color:var(--paper); animation-name:impOk;}
.imp__ok svg{width:12px; height:12px;}
.imp__rows{font-size:var(--t-sm); font-weight:500; color:var(--ok);}
.imp__mx{position:absolute; left:80px; top:30px; width:64px; animation-name:impX;}
.imp__my{animation-name:impY;}
.imp__doc{display:flex; flex-direction:column; align-items:center; gap:6px; transform-origin:50% 40%;
  animation-name:impDoc;}
.imp__doc svg{width:44px; height:54px; display:block; filter:drop-shadow(0 0 0 rgba(34,66,81,0)); animation:impShadow var(--imp-t) var(--imp-d) infinite both;}
.imp__doc span{font-size:var(--t-xs); font-weight:600; color:var(--ink); white-space:nowrap; text-align:center; line-height:1.25;}
.imp__cur{position:absolute; left:30px; top:24px; width:20px; height:24px; transform-origin:4px 3px; animation-name:impCur;}
.imp__cur svg{width:20px; height:24px; display:block; filter:drop-shadow(0 1px 1.5px rgba(34,66,81,.28));}

@keyframes impX{
  0%,25%{transform:translateX(0); animation-timing-function:cubic-bezier(.45,.05,.25,1);}
  60%,89%{transform:translateX(288px);}
  90%,100%{transform:translateX(0);}
}
@keyframes impY{
  0%,25%{transform:translateY(0); animation-timing-function:cubic-bezier(.3,.1,.4,1);}
  42%{transform:translateY(-30px); animation-timing-function:cubic-bezier(.5,0,.3,1);}
  60%,89%{transform:translateY(10px);}
  90%,100%{transform:translateY(0);}
}
@keyframes impDoc{
  0%,19%{transform:none; opacity:1; animation-timing-function:cubic-bezier(.4,0,.2,1);}
  22%{transform:scale(.96); animation-timing-function:cubic-bezier(.22,1,.36,1);}
  30%{transform:translateY(-4px) rotate(-4deg) scale(1.02); animation-timing-function:ease-in-out;}
  52%{transform:translateY(-4px) rotate(-3deg) scale(1.02); animation-timing-function:cubic-bezier(.22,1,.36,1);}
  62%{transform:translateY(-2px) rotate(0deg) scale(1); animation-timing-function:cubic-bezier(.55,0,.75,.2);}
  64%{transform:translateY(-2px) scale(1); opacity:1; animation-timing-function:cubic-bezier(.5,0,.75,0);}
  69%,90%{transform:translateY(8px) scale(.3); opacity:0;}
  91%{transform:translateY(4px) scale(.92); opacity:0; animation-timing-function:cubic-bezier(.22,1,.36,1);}
  99%,100%{transform:none; opacity:1;}
}
@keyframes impShadow{
  0%,20%{filter:drop-shadow(0 0 0 rgba(34,66,81,0)); animation-timing-function:cubic-bezier(.22,1,.36,1);}
  30%,58%{filter:drop-shadow(0 10px 12px rgba(34,66,81,.20)); animation-timing-function:ease-in-out;}
  66%,100%{filter:drop-shadow(0 0 0 rgba(34,66,81,0));}
}
@keyframes impCur{
  0%,5%{transform:translate(96px,74px); opacity:0; animation-timing-function:cubic-bezier(.22,1,.36,1);}
  10%{opacity:1;}
  19%{transform:translate(0,0); opacity:1; animation-timing-function:cubic-bezier(.4,0,.2,1);}
  22%{transform:scale(.86); animation-timing-function:cubic-bezier(.22,1,.36,1);}
  26%,64%{transform:scale(.92); opacity:1; animation-timing-function:cubic-bezier(.22,1,.36,1);}
  68%{transform:scale(1); opacity:1; animation-timing-function:cubic-bezier(.4,0,.2,1);}
  80%,100%{transform:translate(70px,56px); opacity:0;}
}
@keyframes impCard{
  0%,50%{transform:none; animation-timing-function:cubic-bezier(.22,1,.36,1);}
  58%,64%{transform:scale(1.03); animation-timing-function:cubic-bezier(.4,0,.6,1);}
  69%{transform:scale(.985); animation-timing-function:cubic-bezier(.4,0,.6,1);}
  74%{transform:scale(1.006); animation-timing-function:ease-in-out;}
  78%,100%{transform:none;}
}
@keyframes impDash{
  0%,50%{opacity:1; animation-timing-function:ease-out;}
  56%,90%{opacity:0; animation-timing-function:ease-in-out;}
  98%,100%{opacity:1;}
}
@keyframes impSolid{
  0%,50%{opacity:0; animation-timing-function:ease-out;}
  56%,90%{opacity:1; animation-timing-function:ease-in-out;}
  98%,100%{opacity:0;}
}
@keyframes impMark{
  0%,50%{transform:none; background:var(--wash); animation-timing-function:cubic-bezier(.22,1,.36,1);}
  58%,64%{transform:translateY(-3px) scale(1.06); background:var(--paper);}
  68%,100%{transform:none; background:var(--wash);}
}
@keyframes impIdle{
  0%,49%{opacity:1; transform:none; animation-timing-function:cubic-bezier(.4,0,.6,1);}
  57%,92%{opacity:0; transform:scale(.97); animation-timing-function:cubic-bezier(.22,1,.36,1);}
  94%{opacity:0; transform:translateY(6px); animation-timing-function:cubic-bezier(.22,1,.36,1);}
  100%{opacity:1; transform:none;}
}
@keyframes impDone{
  0%,69%{opacity:0; transform:translateY(8px); animation-timing-function:cubic-bezier(.22,1,.36,1);}
  76%,90%{opacity:1; transform:none; animation-timing-function:cubic-bezier(.4,0,.6,1);}
  94%,100%{opacity:0; transform:translateY(-4px);}
}
@keyframes impOk{
  0%,72%{transform:scale(.4); animation-timing-function:cubic-bezier(.34,1.56,.64,1);}
  78%,100%{transform:scale(1);}
}
/* Step 3 art: the invite lands in the client's inbox, loops.
   One 6s timeline. Card arrives, body wipes in line by line, the link
   appears, a cursor settles on it and presses, then everything eases out. */
.ob__art--em{overflow:hidden;}
.ob__art--rm{gap:var(--s3);}
.em.em--rm{height:146px;}
.em__av--d .material-symbols-rounded{font-family:"Material Symbols Rounded"; font-size:18px; font-variation-settings:"FILL" 1; line-height:1; color:var(--paper); font-weight:normal; letter-spacing:0;}
@keyframes rmIn{from{opacity:0; transform:translateY(6px);} to{opacity:1; transform:none;}}

.em{--em-t:1.5s; position:relative; width:400px; height:150px; text-align:left;}
.em *,.em *::before,.em *::after{animation-duration:var(--em-t); animation-iteration-count:1; animation-fill-mode:both;}
.em__card{position:absolute; left:0; right:0; top:0; background:var(--paper); border-radius:var(--r-card);
  box-shadow:var(--ring), var(--lift); transform-origin:50% 0; animation-name:emCard;}
.em__head{display:flex; align-items:center; gap:10px; padding:12px 14px 11px;
  border-bottom:1px solid color-mix(in srgb, var(--line) 55%, transparent);}
.em__av{flex:none; width:30px; height:30px; display:grid; place-items:center; border-radius:var(--r-pill);
  background:var(--accent-deep); color:var(--paper); font-size:var(--t-xs); font-weight:700; letter-spacing:.02em;}
.em__meta{flex:1; min-width:0; display:flex; flex-direction:column; gap:1px;}
.em__from{font-size:13px; font-weight:650; line-height:1.3; color:var(--ink);}
.em__sub{font-size:var(--t-sm); font-weight:500; line-height:1.3; color:var(--slate);}
.em__when{align-self:flex-start; margin-top:2px; display:flex; align-items:center; gap:6px;
  font-size:var(--t-xs); font-weight:600; color:var(--slate);}
.em__pill{flex:none; align-self:center; padding:3px 9px; border-radius:var(--r-pill); background:var(--wash); color:var(--accent-deep); font-size:var(--t-xs); font-weight:650; animation-name:emPill;}
.em__dot{width:7px; height:7px; border-radius:var(--r-pill); background:var(--accent); animation-name:emDot;}
.em__body{padding:10px 14px 13px; font-size:13px; font-weight:500; line-height:1.5; color:var(--ink);}
.em__l{display:block; width:fit-content;}
.em__link{position:relative; display:inline-block; margin:5px 0 0 -6px; padding:2px 6px; border-radius:6px;
  font-weight:600; color:var(--accent-deep); text-decoration:underline; text-decoration-thickness:1px; text-underline-offset:2px;
}

@keyframes emCard{
  0%,8%{opacity:0; transform:translateY(150px); animation-timing-function:cubic-bezier(.22,1,.36,1);}
  70%,100%{opacity:1; transform:none;}
}
@keyframes emPill{
  0%,60%{opacity:0; transform:scale(.6); animation-timing-function:cubic-bezier(.34,1.56,.64,1);}
  100%{opacity:1; transform:none;}
}

/* Step 4 art: split screen, the client's upload page on the left and the
   accountant's list on the right. Three PDFs are dropped one at a time; each
   one leaves the client's list and turns its row green on the accountant's.
   One 7.5s timeline. The three drags share one set of keyframes, offset by
   --d, so their 22% active window lands at 4%, 26% and 48%. */
.ob__art--tall{height:260px;}
.sp{--sp-t:7.5s; position:relative; width:560px; height:216px; display:grid; grid-template-columns:1fr 1fr; gap:16px; text-align:left;}
.sp *,.sp *::before,.sp *::after{animation-duration:var(--sp-t); animation-iteration-count:infinite; animation-fill-mode:both;}
.sp__col{display:flex; flex-direction:column; gap:6px; min-width:0;}
.sp__lab{padding-left:2px; font-size:var(--t-xs); font-weight:700; letter-spacing:.08em; text-transform:uppercase; line-height:13px; color:var(--accent-deep);}
.sp__win{flex:1; display:flex; flex-direction:column; background:var(--paper); border-radius:var(--r-card); box-shadow:var(--ring), var(--lift); overflow:hidden;}
.sp__bar{flex:none; height:22px; display:flex; align-items:center; gap:4px; padding:0 10px; background:var(--canvas);}
.sp__bar i{width:6px; height:6px; border-radius:var(--r-pill); background:var(--line);}
.sp__body{flex:1; display:flex; flex-direction:column; padding:10px;}
.sp__h{margin:0 4px 6px; font-size:var(--t-xs); font-weight:700; letter-spacing:.06em; text-transform:uppercase; line-height:13px; color:var(--slate);}
.sp__rows{position:relative; height:90px;}
.sp__h--c{animation-name:spHead;}
.sp__r{display:flex; align-items:center; justify-content:space-between; gap:8px; height:30px; padding:0 8px; border-radius:6px;
  font-size:var(--t-sm); font-weight:600; color:var(--ink); white-space:nowrap; overflow:hidden;}
.sp__r small{font-size:var(--t-xs); font-weight:500; color:var(--slate);}
.sp__r--c{box-shadow:inset 0 -1px 0 var(--canvas);}
.sp__r--c1{animation-name:spGo1;} .sp__r--c2{animation-name:spGo2;} .sp__r--c3{animation-name:spGo3;}
.sp__r--a1{animation-name:spOk1;} .sp__r--a2{animation-name:spOk2;} .sp__r--a3{animation-name:spOk3;}
.sp__done{position:absolute; inset:0; display:flex; align-items:center; justify-content:center; gap:8px;
  font-size:14px; font-weight:650; color:var(--ok); animation-name:spDone;}
.sp__ck{flex:none; width:20px; height:20px; display:grid; place-items:center; border-radius:var(--r-pill); background:var(--ok); color:var(--paper);}
.sp__ck svg{width:12px; height:12px;}
.sp__zone{position:relative; margin-top:auto; height:44px; display:flex; align-items:center; justify-content:center; gap:6px;
  border-radius:8px; background:var(--paper); font-size:var(--t-sm); font-weight:600; color:var(--accent-deep); animation-name:spZone;}
.sp__zone::before,.sp__zone::after{content:""; position:absolute; inset:0; border-radius:inherit; pointer-events:none;}
.sp__zone::before{border:1.5px dashed var(--accent-deep); animation-name:spDash;}
.sp__zone::after{border:1.5px solid var(--accent); opacity:0; animation-name:spSolid;}
.sp__zone svg{width:15px; height:15px;}
.sp__pl{position:relative; flex:none; width:86px; height:22px;}
.sp__pm,.sp__po{position:absolute; right:0; top:1px; height:20px; display:flex; align-items:center; gap:4px; padding:0 8px;
  border-radius:var(--r-pill); font-size:var(--t-xs); font-weight:650; transform-origin:100% 50%;}
.sp__pm{background:var(--canvas); color:var(--slate); box-shadow:inset 0 0 0 1px var(--line);}
.sp__po{padding-left:6px; background:var(--ok); color:var(--paper);}
.sp__po svg{width:11px; height:11px;}
.sp__r--a1 .sp__pm{animation-name:spPm1;} .sp__r--a2 .sp__pm{animation-name:spPm2;} .sp__r--a3 .sp__pm{animation-name:spPm3;}
.sp__r--a1 .sp__po{animation-name:spPo1;} .sp__r--a2 .sp__po{animation-name:spPo2;} .sp__r--a3 .sp__po{animation-name:spPo3;}
.sp__prog{margin-top:auto; padding:0 4px 3px; display:flex; flex-direction:column; gap:7px;}
.sp__pt{display:flex; align-items:center; gap:4px; font-size:var(--t-sm); font-weight:600; color:var(--slate);}
.sp__n{position:relative; display:inline-block; width:8px; height:16px; overflow:hidden; color:var(--ink);}
.sp__n span{position:absolute; left:0; top:0; display:flex; flex-direction:column; line-height:16px; animation-name:spNum;}
.sp__track{height:5px; border-radius:var(--r-pill); background:color-mix(in srgb, var(--line) 40%, transparent); overflow:hidden;}
.sp__fill{height:100%; border-radius:inherit; background:var(--ok); transform-origin:0 50%; animation-name:spFill;}
/* the drag: one carrier per file, shared keyframes, offset by --d */
.sp__c{--d:.3s; position:absolute; left:-12px; top:100px; width:112px; animation-name:spX;}
.sp__c,.sp__c *{animation-delay:var(--d);}
.sp__c--2{--d:1.95s;} .sp__c--3{--d:3.6s;}
.sp__c > div{animation-name:spY;}
.sp__f{display:flex; align-items:center; gap:6px; width:fit-content; height:34px; padding:0 10px 0 6px; border-radius:8px; background:var(--paper);
  box-shadow:var(--ring), 0 8px 16px rgba(34,66,81,.16); transform-origin:50% 50%; animation-name:spF;}
.sp__f svg{width:20px; height:26px; flex:none; display:block;}
.sp__f span{font-size:var(--t-xs); font-weight:600; color:var(--ink); white-space:nowrap;}
.sp__cur{position:absolute; left:64px; top:17px; width:20px; height:24px; transform-origin:4px 3px; animation-name:spCur;}
.sp__cur svg{width:20px; height:24px; display:block; filter:drop-shadow(0 1px 1.5px rgba(34,66,81,.28));}

/* carrier, local time: 0-4% in, 4-14% drag, 14-15% press, 15-19% drop, then parked */
@keyframes spX{
  0%{transform:translateX(0); animation-timing-function:cubic-bezier(.45,.05,.25,1);}
  14%,99.9%{transform:translateX(92px);}
  100%{transform:translateX(0);}
}
@keyframes spY{
  0%{transform:translateY(0); animation-timing-function:cubic-bezier(.3,.1,.4,1);}
  6%{transform:translateY(-8px); animation-timing-function:cubic-bezier(.5,0,.3,1);}
  14%,99.9%{transform:translateY(67px);}
  100%{transform:translateY(0);}
}
@keyframes spF{
  0%{opacity:0; transform:scale(.9); animation-timing-function:cubic-bezier(.22,1,.36,1);}
  4%{opacity:1; transform:rotate(-4deg) scale(1.02); animation-timing-function:ease-in-out;}
  13%{opacity:1; transform:rotate(-2deg) scale(1.02); animation-timing-function:cubic-bezier(.22,1,.36,1);}
  15%{opacity:1; transform:rotate(0) scale(1); animation-timing-function:cubic-bezier(.5,0,.75,0);}
  19%,100%{opacity:0; transform:translateY(4px) scale(.5);}
}
@keyframes spCur{
  0%{opacity:0; transform:translate(-6px,8px); animation-timing-function:cubic-bezier(.22,1,.36,1);}
  4%{opacity:1; transform:scale(.92); animation-timing-function:cubic-bezier(.22,1,.36,1);}
  14%{opacity:1; transform:scale(.92); animation-timing-function:cubic-bezier(.22,1,.36,1);}
  16%{opacity:1; transform:scale(1); animation-timing-function:cubic-bezier(.4,0,.2,1);}
  21%,100%{opacity:0; transform:translate(22px,20px);}
}
/* global time: drops land at 18%, 40%, 62% */
@keyframes spZone{
  0%,12%{background:var(--paper); animation-timing-function:ease-out;}
  15%,19%{background:var(--wash); animation-timing-function:ease-in-out;}
  24%,34%{background:var(--paper); animation-timing-function:ease-out;}
  37%,41%{background:var(--wash); animation-timing-function:ease-in-out;}
  46%,56%{background:var(--paper); animation-timing-function:ease-out;}
  59%,63%{background:var(--wash); animation-timing-function:ease-in-out;}
  68%,100%{background:var(--paper);}
}
@keyframes spSolid{
  0%,12%{opacity:0; animation-timing-function:ease-out;}
  15%,19%{opacity:1; animation-timing-function:ease-in-out;}
  24%,34%{opacity:0; animation-timing-function:ease-out;}
  37%,41%{opacity:1; animation-timing-function:ease-in-out;}
  46%,56%{opacity:0; animation-timing-function:ease-out;}
  59%,63%{opacity:1; animation-timing-function:ease-in-out;}
  68%,100%{opacity:0;}
}
@keyframes spDash{
  0%,12%{opacity:1; animation-timing-function:ease-out;}
  15%,19%{opacity:0; animation-timing-function:ease-in-out;}
  24%,34%{opacity:1; animation-timing-function:ease-out;}
  37%,41%{opacity:0; animation-timing-function:ease-in-out;}
  46%,56%{opacity:1; animation-timing-function:ease-out;}
  59%,63%{opacity:0; animation-timing-function:ease-in-out;}
  68%,100%{opacity:1;}
}
@keyframes spGo1{
  0%,18%{height:30px; opacity:1; transform:none; animation-timing-function:cubic-bezier(.4,0,.2,1);}
  21%{height:30px; opacity:0; transform:translateX(10px); animation-timing-function:cubic-bezier(.65,0,.35,1);}
  27%,87%{height:0; opacity:0; transform:translateX(10px); animation-timing-function:cubic-bezier(.65,0,.35,1);}
  92%{height:30px; opacity:0; transform:translateY(4px); animation-timing-function:cubic-bezier(.22,1,.36,1);}
  97%,100%{height:30px; opacity:1; transform:none;}
}
@keyframes spGo2{
  0%,40%{height:30px; opacity:1; transform:none; animation-timing-function:cubic-bezier(.4,0,.2,1);}
  43%{height:30px; opacity:0; transform:translateX(10px); animation-timing-function:cubic-bezier(.65,0,.35,1);}
  49%,88%{height:0; opacity:0; transform:translateX(10px); animation-timing-function:cubic-bezier(.65,0,.35,1);}
  93%{height:30px; opacity:0; transform:translateY(4px); animation-timing-function:cubic-bezier(.22,1,.36,1);}
  100%{height:30px; opacity:1; transform:none;}
}
@keyframes spGo3{
  0%,62%{height:30px; opacity:1; transform:none; animation-timing-function:cubic-bezier(.4,0,.2,1);}
  65%{height:30px; opacity:0; transform:translateX(10px); animation-timing-function:cubic-bezier(.65,0,.35,1);}
  71%,89%{height:0; opacity:0; transform:translateX(10px); animation-timing-function:cubic-bezier(.65,0,.35,1);}
  94%{height:30px; opacity:0; transform:translateY(4px); animation-timing-function:cubic-bezier(.22,1,.36,1);}
  100%{height:30px; opacity:1; transform:none;}
}
@keyframes spDone{
  0%,70%{opacity:0; transform:translateY(6px); animation-timing-function:cubic-bezier(.22,1,.36,1);}
  76%,84%{opacity:1; transform:none; animation-timing-function:cubic-bezier(.4,0,.6,1);}
  88%,100%{opacity:0; transform:translateY(-4px);}
}
@keyframes spHead{
  0%,68%{opacity:1; animation-timing-function:cubic-bezier(.4,0,.6,1);}
  73%,86%{opacity:0; animation-timing-function:cubic-bezier(.4,0,.6,1);}
  92%,100%{opacity:1;}
}
@keyframes spOk1{
  0%,18%{background:transparent; animation-timing-function:cubic-bezier(.22,1,.36,1);}
  22%,88%{background:var(--ok-wash); animation-timing-function:cubic-bezier(.4,0,.6,1);}
  95%,100%{background:transparent;}
}
@keyframes spOk2{
  0%,40%{background:transparent; animation-timing-function:cubic-bezier(.22,1,.36,1);}
  44%,88%{background:var(--ok-wash); animation-timing-function:cubic-bezier(.4,0,.6,1);}
  95%,100%{background:transparent;}
}
@keyframes spOk3{
  0%,62%{background:transparent; animation-timing-function:cubic-bezier(.22,1,.36,1);}
  66%,88%{background:var(--ok-wash); animation-timing-function:cubic-bezier(.4,0,.6,1);}
  95%,100%{background:transparent;}
}
@keyframes spPm1{
  0%,18%{opacity:1; transform:none; animation-timing-function:cubic-bezier(.4,0,.2,1);}
  21%,90%{opacity:0; transform:scale(.8); animation-timing-function:cubic-bezier(.22,1,.36,1);}
  96%,100%{opacity:1; transform:none;}
}
@keyframes spPm2{
  0%,40%{opacity:1; transform:none; animation-timing-function:cubic-bezier(.4,0,.2,1);}
  43%,90%{opacity:0; transform:scale(.8); animation-timing-function:cubic-bezier(.22,1,.36,1);}
  96%,100%{opacity:1; transform:none;}
}
@keyframes spPm3{
  0%,62%{opacity:1; transform:none; animation-timing-function:cubic-bezier(.4,0,.2,1);}
  65%,90%{opacity:0; transform:scale(.8); animation-timing-function:cubic-bezier(.22,1,.36,1);}
  96%,100%{opacity:1; transform:none;}
}
@keyframes spPo1{
  0%,19%{opacity:0; transform:scale(.6); animation-timing-function:cubic-bezier(.34,1.56,.64,1);}
  24%,88%{opacity:1; transform:none; animation-timing-function:cubic-bezier(.4,0,.2,1);}
  92%,100%{opacity:0; transform:scale(.85);}
}
@keyframes spPo2{
  0%,41%{opacity:0; transform:scale(.6); animation-timing-function:cubic-bezier(.34,1.56,.64,1);}
  46%,88%{opacity:1; transform:none; animation-timing-function:cubic-bezier(.4,0,.2,1);}
  92%,100%{opacity:0; transform:scale(.85);}
}
@keyframes spPo3{
  0%,63%{opacity:0; transform:scale(.6); animation-timing-function:cubic-bezier(.34,1.56,.64,1);}
  68%,88%{opacity:1; transform:none; animation-timing-function:cubic-bezier(.4,0,.2,1);}
  92%,100%{opacity:0; transform:scale(.85);}
}
@keyframes spNum{
  0%,18%{transform:translateY(0); opacity:1; animation-timing-function:cubic-bezier(.22,1,.36,1);}
  22%,40%{transform:translateY(-16px); animation-timing-function:cubic-bezier(.22,1,.36,1);}
  44%,62%{transform:translateY(-32px); animation-timing-function:cubic-bezier(.22,1,.36,1);}
  66%,88%{transform:translateY(-48px); opacity:1; animation-timing-function:cubic-bezier(.4,0,.6,1);}
  91%{transform:translateY(-48px); opacity:0;}
  92%{transform:translateY(0); opacity:0; animation-timing-function:cubic-bezier(.4,0,.6,1);}
  96%,100%{transform:translateY(0); opacity:1;}
}
@keyframes spFill{
  0%,18%{transform:scaleX(0); animation-timing-function:cubic-bezier(.22,1,.36,1);}
  24%,40%{transform:scaleX(.3333); animation-timing-function:cubic-bezier(.22,1,.36,1);}
  46%,62%{transform:scaleX(.6667); animation-timing-function:cubic-bezier(.22,1,.36,1);}
  68%,88%{transform:scaleX(1); animation-timing-function:cubic-bezier(.65,0,.35,1);}
  96%,100%{transform:scaleX(0);}
}
.ob__slide--intro.is-on{min-height:0;}
.intro{padding:var(--s7) var(--s6) 0;}
.intro__logo{height:30px; width:auto; display:block; margin-bottom:var(--s6);}
.intro__t{margin:0 0 var(--s6); font-family:var(--f-display); font-size:var(--t-2xl); font-weight:650; letter-spacing:-.02em; line-height:1.1; color:var(--ink);}
.intro__sub{margin:0 auto var(--s6); font-size:17px; font-weight:500; line-height:1.45; color:var(--slate); max-width:none;}
.intro__t,.intro__sub{text-align:center;}
.intro__list div > span{display:block; text-wrap:balance;}
.ob.is-intro .ob__foot{justify-content:center; padding-top:var(--s6);}
.ob.is-intro .ob__foot [data-act=back]{display:none;}
.intro__list{list-style:none; max-width:470px; margin:0 auto; padding:0; display:flex; flex-direction:column; gap:var(--s4);}
.intro__list li{display:flex; gap:var(--s4); align-items:center; padding:var(--s4); border-radius:var(--r-card); background:var(--wash);}
.intro__list b{display:block; font-size:var(--t-lg); font-weight:650; color:var(--ink); margin-bottom:0;}
.intro__list span{font-size:17px; font-weight:500; line-height:1.45; color:var(--slate);}
.intro__i{flex:none; width:40px; display:grid; place-items:center; color:var(--accent-deep);}
.intro__i .material-symbols-rounded{font-family:"Material Symbols Rounded"; font-size:40px; line-height:1; font-variation-settings:'FILL' 1; color:var(--accent-deep); font-weight:normal; font-style:normal; letter-spacing:normal; text-transform:none; white-space:nowrap; -webkit-font-feature-settings:"liga"; font-feature-settings:"liga";}
.intro__list li span.intro__i{color:var(--accent-deep);}
.ob__foot .btn{font-family:var(--f-body); font-size:var(--t-lg); font-weight:650; padding:12px 22px;}
.ob__foot .btn--primary{background:transparent; color:var(--accent-deep); box-shadow:inset 0 0 0 1.5px var(--accent-deep);}
.ob__foot .btn--primary:hover{background:var(--wash); color:var(--accent-deep); filter:none;}
.ob__foot [data-act=back]{margin-right:auto; padding-left:0; background:transparent !important; box-shadow:none !important; outline:none !important; border:0;}
.ob__foot [data-act=back]:hover{color:var(--accent-deep);}
.ob__slide .ob__art{height:260px;}
.ob .ob__slide.is-on{height:388px; min-height:0;}
.ob .ob__slide--intro.is-on{display:flex; flex-direction:column; justify-content:center;}

/* The stage (M2): the animations keep their design width and are scaled as
   one picture, so nothing reflows on a phone. --k is set by onboarding.js. */
.ob__art{position:relative; overflow:hidden;}
.ob__stage{--k:1; position:absolute; left:50%; top:50%; width:max-content; display:grid; place-items:center;
  transform:translate(-50%,-50%) scale(var(--k)); transform-origin:50% 50%;}
.ob__err{flex:none; margin:4px var(--s6) var(--s2); text-align:center;}
.em__from,.em__sub{display:block; white-space:nowrap; overflow:hidden; text-overflow:ellipsis;}

/* ---- the onboarding on phones (D-119, docs/ONBOARDING-MOBILE-PLAN.md) -----
   One template, one mode: the query below is the phone layout, and it sets
   --ob-m:1 on the dialog, which onboarding.js reads, so CSS and the script
   never disagree. Outside it (desktop, tablets) the two phone-only blocks are
   display:none and the track is display:contents: no box, so the desktop
   modal renders exactly as before (Gate ONBM, CR1). Ported from Hugo's
   approved mobile mockup (docolect-onboarding-mobile-PT.html).
   The phone heading (.ob__tm, "Passo N" as a small label above the title) is
   its own element rather than spans inside the desktop .ob__t: measured in
   ONBM.2, spans in the desktop heading shift its glyphs by subpixels. */
.ob__track{display:contents;}
.ob__tm{display:none;}
@media (max-width:640px), (max-height:480px) and (orientation:landscape){
  .ob__n{display:block; margin-bottom:6px; font-family:var(--f-body); font-size:12px; font-weight:700; letter-spacing:.09em;
    line-height:1.5; text-transform:uppercase; color:var(--accent-deep);}
}

/* The phone shell (ONBM.3): full screen, no sheet and no backdrop; the steps
   side by side in the track, moved by onboarding.js and by the finger; the
   art on a rounded blue panel that takes the free height; Back as plain
   text and an outlined Next filling the row, above the home bar. Only the
   step in view animates (the others are rendered beside it now). */
@media (max-width:640px), (max-height:480px) and (orientation:landscape){
  dialog.ob{--ob-m:1; padding:0; overflow:hidden; overscroll-behavior:contain;}
  dialog.ob[open]{display:block;}
  dialog.ob::backdrop{background:transparent; backdrop-filter:none;}
  dialog.ob .ob__card{width:100%; height:100vh; height:100dvh; border-radius:0; box-shadow:none; display:flex; flex-direction:column;
    overflow:hidden; overflow:clip; overscroll-behavior:contain;
    padding:env(safe-area-inset-top) env(safe-area-inset-right) 0 env(safe-area-inset-left);}
  dialog.ob .ob__track{position:relative; flex:1 1 auto; min-height:0; display:flex; transition:transform .5s var(--ease-out);
    will-change:transform; touch-action:pan-y; user-select:none; -webkit-user-select:none;}
  dialog.ob .ob__slide,dialog.ob .ob__slide.is-on{display:flex; flex-direction:column; flex:0 0 100%; min-width:0; height:100%; min-height:0;
    animation:none; overflow:hidden;}
  dialog.ob .ob__slide:not(.is-on) *,dialog.ob .ob__slide:not(.is-on) *::before,dialog.ob .ob__slide:not(.is-on) *::after{animation:none;}

  /* the art panel; the stage is drawn at its natural size (fit() scales it
     down only when the panel is smaller than the art) */
  dialog.ob .ob__slide .ob__art{position:relative; flex:1 1 auto; min-height:0; height:auto; margin:8px 16px 0; padding:0; gap:0;
    border-radius:26px; background:var(--wash); overflow:hidden; display:flex; align-items:center; justify-content:center; container-type:size;}
  dialog.ob .ob__stage{position:relative; left:auto; top:auto; flex:none; width:auto; display:grid; place-items:center;
    transform:scale(var(--k)); transform-origin:50% 50%;}

  /* the text under it */
  .ob__t{display:none;}
  .ob__tm{display:block; flex:none; margin:22px 24px 0; font-family:var(--f-display); font-size:25px; font-weight:650; letter-spacing:-.02em;
    line-height:1.14; color:var(--ink); text-wrap:balance;}
  .ob__p{flex:none; margin:8px 24px 0; font-size:17px; font-weight:500; line-height:1.42;}

  /* the error line under a step (the language step's) */
  .ob__err{margin:10px 24px 0; text-align:left;}

  /* the welcome: three large cards, rising in one after the other */
  dialog.ob .ob__slide--intro,dialog.ob .ob__slide--intro.is-on{justify-content:flex-start;}
  .intro{margin:auto 0; padding:0 24px 8px;}
  .intro__t{margin:0 0 28px; font-size:32px; line-height:1.1;}
  .intro__list{max-width:none; gap:12px;}
  .intro__list li{gap:16px; padding:18px; border-radius:18px;}
  .intro__i{width:auto;}
  .intro__i .material-symbols-rounded{font-size:36px;}
  .intro__list b{font-size:17px; font-weight:650; line-height:1.35;}
  dialog.ob .ob__slide--intro.is-on .intro__list li{animation:obmLiIn .6s var(--ease-out) both;}
  dialog.ob .ob__slide--intro.is-on .intro__list li:nth-child(2){animation-delay:.08s;}
  dialog.ob .ob__slide--intro.is-on .intro__list li:nth-child(3){animation-delay:.16s;}

  /* the footer, in the thumb zone, above the home bar */
  .ob__foot{flex:none; gap:12px; padding:16px 24px max(16px, calc(env(safe-area-inset-bottom) + 10px));}
  .ob.is-intro .ob__foot{justify-content:flex-start; padding-top:16px;}
  .ob__foot .btn{height:54px; font-size:18px; font-weight:650; line-height:1.2; -webkit-tap-highlight-color:transparent;}
  .ob__foot [data-act=back]{flex:none; margin-right:0; padding:0 14px 0 0;}
  .ob__foot [data-act=next]{flex:1 1 auto; min-width:0; padding:0 14px; border-radius:14px; white-space:nowrap;}
  .ob__foot .btn--primary:hover{background:transparent;}
  .ob__foot .btn--primary:active{background:var(--wash);}
}
/* The end label at 380px wide or less (the mockup's container query). */
@media (max-width:380px){
  .ob.is-last .ob__foot [data-act=next]{font-size:16px; padding:0 10px;}
  .ob.is-last .ob__foot [data-act=back]{padding-right:6px;}
}
/* Landscape phones: one column that scrolls inside the step, the art at its
   natural size, the footer pinned; the sheet's chips in one row. */
@media (max-height:480px) and (orientation:landscape){
  dialog.ob .ob__slide,dialog.ob .ob__slide.is-on{overflow-x:hidden; overflow-y:auto; overscroll-behavior:contain; padding-bottom:16px;}
  dialog.ob .ob__slide .ob__art{flex:none; container-type:normal; padding:12px 0;}
  .ob__foot{padding-top:10px; padding-bottom:max(10px, env(safe-area-inset-bottom));}
  .intro{padding-top:12px;}
  .intro__t{margin-bottom:14px; font-size:28px;}
  .intro__list li{padding:12px 18px;}
}
@keyframes obmLiIn{from{opacity:0; transform:translateY(12px);} to{opacity:1; transform:none;}}

/* The phone art (ONBM.4), the mockup's sizes. Where the phone moves differ
   from desktop's, its keyframes are its own (obm...), bound only to the step
   in view; the rest reuse desktop's unchanged keyframes. Parts that only
   appear mid-loop start invisible, so a step seen mid-swipe is at rest. */
@media (max-width:640px), (max-height:480px) and (orientation:landscape){
  /* step 1: the export falls from the top into the drop card */
  .imp{flex:none; width:300px; height:300px;}
  @container (min-height:460px){ .imp{transform:scale(1.08);} }
  .imp__card{left:20px; top:156px; width:260px; height:136px; border-radius:16px;}
  .imp__st{padding:0 28px;}
  .imp__file,.imp__rows{font-size:13px;}
  .imp__mx{left:44px; top:14px; width:72px;}
  .imp__doc svg{width:48px; height:60px;}
  .imp__cur{left:34px; top:28px; opacity:0;}
  dialog.ob .ob__slide.is-on .imp__mx{animation-name:obmImpX;}
  dialog.ob .ob__slide.is-on .imp__my{animation-name:obmImpY;}
  dialog.ob .ob__slide.is-on .imp__cur{animation-name:obmImpCur;}


  /* steps 3 and 5: the email card at 326px, natural size */
  .em,.em.em--rm{flex:none; width:326px; height:186px;}
  .em__card{border-radius:16px;}
  .em__head{padding:14px 14px 12px;}
  .em__av{width:36px; height:36px; font-size:12px;}
  .em__av .material-symbols-rounded,.em__av--d .material-symbols-rounded{font-size:20px;}
  /* the sender (and step 5's "em nome de ...") wrap as in the mockup, and a
     long name still ends in an ellipsis, on the second line (E-M11) */
  .em__from{font-size:14px;}
  .em__from,.em__sub{white-space:normal; display:-webkit-box; -webkit-box-orient:vertical; -webkit-line-clamp:2; line-clamp:2;}
  .em__pill{padding:4px 10px;}
  .em__body{padding:12px 14px 16px; font-size:14px;}
  .em__l{width:auto;}
  .em__link{margin:6px 0 0; padding:0; border-radius:0; font-size:13px; word-break:break-all;}
  dialog.ob .ob__slide.is-on .em__card{animation-name:obmEmCard;}

  /* step 4: the client's window stacked above the practice's, each label in
     its window bar */
  .sp{flex:none; width:318px; height:366px; display:flex; flex-direction:column; gap:10px;}
  .sp__col{position:relative; display:block;}
  .sp__lab{position:absolute; z-index:1; top:0; right:10px; height:24px; display:flex; align-items:center; padding:0; line-height:1;}
  .sp__win{flex:none; border-radius:14px;}
  .sp__bar{height:24px;}
  .sp__body{flex:none; padding:8px;}
  .sp__h{margin:0 6px 6px;}
  .sp__rows{height:78px;}
  .sp__r{height:26px; border-radius:7px;}
  .sp__done,.sp__po,.sp__f,.sp__cur{opacity:0;}
  .sp__zone{margin-top:8px; height:38px; border-radius:9px;}
  .sp__prog{margin-top:8px; padding:0 6px 3px;}
  .sp__c{left:190px; top:40px; width:auto; opacity:0;}
  .sp__f{border-radius:9px;}
  .sp__cur{left:62px;}
  dialog.ob .ob__slide.is-on .sp__c{opacity:1; animation-name:obmSpX;}
  dialog.ob .ob__slide.is-on .sp__c > div{animation-name:obmSpY;}
  dialog.ob .ob__slide.is-on .sp__r--c1{animation-name:obmSpGo1;}
  dialog.ob .ob__slide.is-on .sp__r--c2{animation-name:obmSpGo2;}
  dialog.ob .ob__slide.is-on .sp__r--c3{animation-name:obmSpGo3;}
}
@media (max-height:480px) and (orientation:landscape){
  /* step 1 in landscape: the same art 50px shorter (card higher, a shorter
     fall), so the panel, 282px with its margin, fits above the footer */
  .imp{height:250px;}
  .imp__card{top:106px;}
  dialog.ob .ob__slide.is-on .imp__my{animation-name:obmImpYL;}
}
@keyframes obmImpX{
  0%,25%{transform:translateX(0); animation-timing-function:cubic-bezier(.45,.05,.25,1);}
  60%,89%{transform:translateX(70px);}
  90%,100%{transform:translateX(0);}
}
@keyframes obmImpY{
  0%,25%{transform:translateY(0); animation-timing-function:cubic-bezier(.3,.1,.4,1);}
  40%{transform:translateY(-10px); animation-timing-function:cubic-bezier(.5,0,.3,1);}
  60%,89%{transform:translateY(178px);}
  90%,100%{transform:translateY(0);}
}
@keyframes obmImpYL{
  0%,25%{transform:translateY(0); animation-timing-function:cubic-bezier(.3,.1,.4,1);}
  40%{transform:translateY(-10px); animation-timing-function:cubic-bezier(.5,0,.3,1);}
  60%,89%{transform:translateY(128px);}
  90%,100%{transform:translateY(0);}
}
@keyframes obmImpCur{
  0%,5%{transform:translate(90px,70px); opacity:0; animation-timing-function:cubic-bezier(.22,1,.36,1);}
  10%{opacity:1;}
  19%{transform:translate(0,0); opacity:1; animation-timing-function:cubic-bezier(.4,0,.2,1);}
  22%{transform:scale(.86); animation-timing-function:cubic-bezier(.22,1,.36,1);}
  26%,64%{transform:scale(.92); opacity:1; animation-timing-function:cubic-bezier(.22,1,.36,1);}
  68%{transform:scale(1); opacity:1; animation-timing-function:cubic-bezier(.4,0,.2,1);}
  80%,100%{transform:translate(60px,46px); opacity:0;}
}
@keyframes obmEmCard{
  0%,8%{opacity:0; transform:translateY(170px); animation-timing-function:cubic-bezier(.22,1,.36,1);}
  70%,100%{opacity:1; transform:none;}
}
@keyframes obmSpX{
  0%{transform:translateX(0); animation-timing-function:cubic-bezier(.45,.05,.25,1);}
  14%,99.9%{transform:translateX(-82px);}
  100%{transform:translateX(0);}
}
@keyframes obmSpY{
  0%{transform:translateY(0); animation-timing-function:cubic-bezier(.3,.1,.4,1);}
  6%{transform:translateY(-10px); animation-timing-function:cubic-bezier(.5,0,.3,1);}
  14%,99.9%{transform:translateY(99px);}
  100%{transform:translateY(0);}
}
@keyframes obmSpGo1{
  0%,18%{height:26px; opacity:1; transform:none; animation-timing-function:cubic-bezier(.4,0,.2,1);}
  21%{height:26px; opacity:0; transform:translateX(10px); animation-timing-function:cubic-bezier(.65,0,.35,1);}
  27%,87%{height:0; opacity:0; transform:translateX(10px); animation-timing-function:cubic-bezier(.65,0,.35,1);}
  92%{height:26px; opacity:0; transform:translateY(4px); animation-timing-function:cubic-bezier(.22,1,.36,1);}
  97%,100%{height:26px; opacity:1; transform:none;}
}
@keyframes obmSpGo2{
  0%,40%{height:26px; opacity:1; transform:none; animation-timing-function:cubic-bezier(.4,0,.2,1);}
  43%{height:26px; opacity:0; transform:translateX(10px); animation-timing-function:cubic-bezier(.65,0,.35,1);}
  49%,88%{height:0; opacity:0; transform:translateX(10px); animation-timing-function:cubic-bezier(.65,0,.35,1);}
  93%{height:26px; opacity:0; transform:translateY(4px); animation-timing-function:cubic-bezier(.22,1,.36,1);}
  100%{height:26px; opacity:1; transform:none;}
}
@keyframes obmSpGo3{
  0%,62%{height:26px; opacity:1; transform:none; animation-timing-function:cubic-bezier(.4,0,.2,1);}
  65%{height:26px; opacity:0; transform:translateX(10px); animation-timing-function:cubic-bezier(.65,0,.35,1);}
  71%,89%{height:0; opacity:0; transform:translateX(10px); animation-timing-function:cubic-bezier(.65,0,.35,1);}
  94%{height:26px; opacity:0; transform:translateY(4px); animation-timing-function:cubic-bezier(.22,1,.36,1);}
  100%{height:26px; opacity:1; transform:none;}
}

/* ---- onboarding v2 (OB2.3, D-144, docs/ONBOARDING-V2-PLAN.md) --------------
   The language step's two tiles (PT and EN, never translated, as the rail's
   toggle; the check pops in on the account's language) and the import art's
   "32 ja recebidas", in after "50 linhas lidas" on the same 4.8s loop. As in
   the mockup's "onboarding v2" block, without its draggable marker (OB2.4b,
   dropped). After the phone section so its phone rules come last. */
.lg{display:flex; gap:20px;}
.lg__opt{position:relative; width:190px; height:150px; display:flex; flex-direction:column; align-items:center; justify-content:center; gap:8px;
  padding:0; border:0; border-radius:var(--r-card); background:var(--paper); box-shadow:var(--ring); cursor:pointer; color:var(--ink);
  font-family:var(--f-body); -webkit-tap-highlight-color:transparent; transition:box-shadow .2s, transform .15s, background .2s;}
.lg__opt:hover{box-shadow:0 0 0 1.5px var(--accent);}
.lg__opt:active{transform:scale(.98);}
.lg__opt:focus{outline:none;}
.lg__code{font-family:var(--f-display); font-size:44px; font-weight:650; letter-spacing:-.02em; line-height:1;}
.lg__name{font-size:var(--t-lg); font-weight:500; color:var(--slate);}
.lg__opt.is-on,.lg__opt.is-on:hover{box-shadow:inset 0 0 0 2px var(--accent-deep);}
.lg__opt.is-on .lg__code{color:var(--accent-deep);}
.lg__ck{position:absolute; top:12px; right:12px; width:24px; height:24px; display:grid; place-items:center; border-radius:var(--r-pill);
  background:var(--accent-deep); color:var(--paper); opacity:0; transform:scale(.4); transition:opacity .2s, transform .35s cubic-bezier(.34,1.56,.64,1);}
.lg__ck svg{width:14px; height:14px;}
.lg__opt.is-on .lg__ck{opacity:1; transform:none;}
.ob__slide--lang .ob__err{margin:6px var(--s6) 0;}
.imp__got{display:flex; align-items:center; gap:6px; font-size:var(--t-sm); font-weight:600; color:var(--accent-deep); opacity:0; animation-name:impGot;}
.imp__got svg{width:14px; height:14px;}
@keyframes impGot{
  0%,74%{opacity:0; transform:translateY(6px); animation-timing-function:cubic-bezier(.22,1,.36,1);}
  80%,90%{opacity:1; transform:none;}
  94%,100%{opacity:0;}
}
@media (max-width:640px), (max-height:480px) and (orientation:landscape){
  .lg{gap:12px;}
  .lg__opt{width:128px; height:164px; border-radius:18px;}
  .lg__code{font-size:46px;}
  .ob__slide--lang .ob__err{margin:10px 24px 0;}
  .imp__got{font-size:13px;}
}

/* ---- Textos dos lembretes (S7 EM.8f, D-130) -------------------------------
   One card per step; the preview is the email's own content partial, framed
   like the email's white card, under a <details>. The link to the page sits
   under the Lembretes card, outside its form (forms do not nest). */
.texts__step{margin-bottom:var(--s5);}
.texts__preview{border-top:1px solid var(--line);}
.texts__preview summary{cursor:pointer; font-weight:600; color:var(--accent-deep);}
.texts__mail{margin-top:var(--s3);}
.texts__link{margin:calc(-1 * var(--s3)) 0 var(--s5) 0; font-size:var(--t-sm); color:var(--slate);}
.texts__link a{font-weight:600;}
