/* ============================================================================
   LUMINA — APPLICATION STYLES
   Every value here comes from tokens.css. No literal colour, spacing, font
   size or radius appears below this line. CLAUDE.md, UI Rules.
   Enforced by core/tests/test_stylesheet.py, not by inspection.

   Two literals are unavoidable and are the only ones permitted:
     - the 1px clip box in .visually-hidden, which is the accessibility idiom
     - the media query width, because CSS cannot read var() in a @media
       condition. It must be kept equal to --bp-tablet by hand; the test
       checks that it is.

   Layout uses logical properties so --direction is the only change if the
   product ever flips to RTL. DESIGN-SYSTEM.md section 6.
   ========================================================================= */

*,*::before,*::after{box-sizing:border-box}
html{-webkit-text-size-adjust:100%}
body{
  margin:0;background:var(--c-bg);color:var(--c-ink);
  font:var(--fw-n) var(--fs-md)/var(--lh-body) var(--f-ui);
  -webkit-font-smoothing:antialiased;
}
h1,h2,h3{margin:0;line-height:var(--lh-tight);font-weight:var(--fw-x);letter-spacing:var(--ls-tight)}
a{color:var(--c-accent)}
/* Tabular figures, and never broken across lines. `thead th` already sets
   nowrap; a body cell splitting a date across two lines at phone width was
   the inconsistency, found on screen in P4. Safe because .table-wrap scrolls.
   (No dimension written here on purpose -- the guard below reads comments
   too, and keeping it blunt is worth more than a number in prose.) */
.num{font-variant-numeric:tabular-nums;white-space:nowrap}
.r{text-align:end}
[hidden]{display:none!important}
.visually-hidden{position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap}

/* ---- icons -------------------------------------------------------------- *
 * Inline SVG, stroked in currentColor so an icon inherits whatever the
 * surrounding control is already doing -- hover, active section, disabled.
 * Icons are decorative here: every one sits next to, or inside, a control
 * that already carries an accessible name. DESIGN-SYSTEM.md §5.15.
 * ------------------------------------------------------------------------ */
.icon{
  inline-size:var(--icon);block-size:var(--icon);
  display:block;flex:none;
  fill:none;stroke:currentColor;
  stroke-width:var(--bw-2);stroke-linecap:round;stroke-linejoin:round;
}
.icon--lg{inline-size:var(--icon-lg);block-size:var(--icon-lg)}

/* ---- shell ------------------------------------------------------------- */
.app{display:flex;min-height:100vh}
/* The rail stays put while the page scrolls -- asked for by the client on
   2026-09-19, looking at a long customer list: it scrolled away with the
   rows. Sticky rather than fixed, so it stays in the flex row and the shell
   needs no offset. `align-self:flex-start` stops the row stretching it to
   the page's height, which would leave nothing to stick. A screen shorter
   than the rail's icons scrolls the rail on its own, so Sign out is never
   cut off. */
.rail{
  inline-size:var(--rail-w);flex:none;
  background:linear-gradient(180deg,var(--brand-800),var(--brand-900));
  display:flex;flex-direction:column;align-items:center;
  padding-block:var(--s-5);gap:var(--s-5);
  position:sticky;inset-block-start:0;align-self:flex-start;
  block-size:100vh;overflow-y:auto;z-index:var(--z-rail);
}
/* The brand mark, not a letter. It was a typographic "L" in a --brand-500
   badge while the only logo asset was a raster; the client supplied a
   monochrome vector on 2026-08-11.

   No badge behind it, for two reasons. Measured: the mark in its own colours
   reads 2.59:1 against --brand-800 at the top of the rail gradient, and white
   on a --brand-500 badge is 2.78:1 -- both the teal-on-teal trap that made
   the P2 buttons invisible. White straight onto the rail is 7.20:1 rising to
   11.30:1. Stated: DESIGN-SYSTEM.md 2.2 gives --brand-500 as "marks on dark
   surfaces", which is the mark's colour, not a surface to put one on. */
.rail__mark{
  inline-size:var(--rail-mark);block-size:var(--rail-mark);
  display:grid;place-items:center;
}
.rail__mark img{inline-size:100%;block-size:100%}
/* S-05's unread mark -- D122. A dot, not a number: the rail item is a touch
   target and a digit on it is unreadable on a phone. The count is on the
   screen itself. `.rail__link` is already `position:relative`, which is what
   this anchors to. */
.rail__dot{
  position:absolute;inset-block-start:var(--s-2);inset-inline-end:var(--s-2);
  inline-size:var(--rail-dot);block-size:var(--rail-dot);
  border-radius:var(--r-pill);background:var(--c-warn);
}

.rail__link{
  position:relative;inline-size:var(--rail-item);block-size:var(--rail-item);
  border-radius:var(--r-2);
  display:grid;place-items:center;color:var(--c-onbrand-2);
  text-decoration:none;font-size:var(--fs-lg);
  transition:background var(--dur) var(--ease),color var(--dur) var(--ease);
}
/* Sign out is a form submit, not a link, so it needs the button chrome reset
   rather than an inline style attribute. */
.rail__link--button{border:0;background:transparent;cursor:pointer;font:inherit}
.rail__link:hover{color:var(--c-onbrand)}
.rail__link[aria-current="page"]{background:var(--c-onbrand-wash);color:var(--c-onbrand)}
/* The logo orange, used as a non-text accent only. DESIGN-SYSTEM.md 2.2b */
.rail__link[aria-current="page"]::before{
  content:"";position:absolute;inset-inline-start:calc(var(--s-2) * -1);
  inset-block:var(--s-2);inline-size:var(--bw-3);
  border-radius:var(--r-pill);background:var(--accent-orange);
}
.rail__spacer{margin-block-start:auto}
.rail__avatar{
  inline-size:var(--rail-avatar);block-size:var(--rail-avatar);border-radius:var(--r-pill);
  background:var(--brand-500);color:var(--brand-900);
  display:grid;place-items:center;font-weight:var(--fw-x);font-size:var(--fs-xs);
}
/* Found on the client's own screenshot, 2026-09-19: on a window shorter than
   the rail, flex items shrink to their content, and the avatar -- a circle,
   and now a photograph -- was squeezed into a pill. The mark and the avatar
   keep their size; the links still give up height first, so every control
   stays on a short screen without the rail having to scroll. */
.rail__mark,.rail__avatar{flex:none}
/* The phone menu -- DESIGN-SYSTEM.md 5.14b. On a desktop the toggle and its
   button do not exist and the sections are the rail's own items, as before. */
.rail__toggle{display:none}
.rail__burger{display:none}
.rail__sections{display:contents}
/* BR-139a -- a person's own photograph fills the avatar's circle. */
.rail__avatar{overflow:hidden}
.shell{flex:1;min-inline-size:0;display:flex;flex-direction:column}

/* ---- band -------------------------------------------------------------- */
.band{
  background:linear-gradient(135deg,var(--brand-700),var(--brand-800) 62%,var(--brand-900));
  color:var(--c-onbrand);
  padding-inline:var(--s-7);
  padding-block-start:var(--band-pad-block);
  padding-block-end:var(--band-pad-block-end);
}
.band__eyebrow{
  font-size:var(--fs-xs);letter-spacing:var(--ls-wider);text-transform:uppercase;
  color:var(--c-onbrand-2);margin-block-end:var(--s-3);font-weight:var(--fw-b);
}
.band h1{font-size:var(--fs-2xl);margin-block-end:var(--s-2)}
/* --c-onbrand, not --c-onbrand-2. The dim token measures 2.42:1 at the light
   end of this gradient -- below --c-ink-4, which DESIGN-SYSTEM.md 2.2a bans
   for text. Nothing between the two passes either, so hierarchy here comes
   from size, not from dimming. */
.band__sub{margin:0;color:var(--c-onbrand);font-size:var(--fs-sm)}
.band__stats{display:flex;gap:var(--band-stats-gap);margin-block-start:var(--s-6);flex-wrap:wrap}
.band__stat .lab{font-size:var(--fs-xs);color:var(--c-onbrand-2);margin-block-end:var(--s-1)}
.band__stat .val{font-size:var(--fs-xl);font-weight:var(--fw-x);font-variant-numeric:tabular-nums}
.band__actions{display:flex;gap:var(--s-3);margin-block-start:var(--s-5);flex-wrap:wrap}

/* The on-band button variants live with the other .btn modifiers, further
   down. They MUST be declared after .btn -- see the note there. */

/* Sub-navigation inside a section. Without it there is no route between the
   screens of one section except typing the URL. */
.subnav{display:flex;gap:var(--s-5);flex-wrap:wrap;margin-block-start:var(--s-6);
  border-block-start:var(--bw) solid var(--c-onbrand-rule);padding-block-start:var(--s-4)}
.subnav a{color:var(--c-onbrand-2);text-decoration:none;font-size:var(--fs-sm);
  font-weight:var(--fw-b);padding-block-end:var(--s-2);
  border-block-end:var(--bw-3) solid transparent;transition:color var(--dur) var(--ease)}
.subnav a:hover{color:var(--c-onbrand)}
.subnav a[aria-current="page"]{color:var(--c-onbrand);border-block-end-color:var(--accent-orange)}

main{padding-inline:var(--s-7);padding-block-end:var(--s-9);margin-block-start:calc(var(--card-overlap) * -1);flex:1}
.stack > * + *{margin-block-start:var(--s-6)}

/* ---- card -------------------------------------------------------------- */
.card{
  background:var(--c-surface);border-radius:var(--r-3);
  box-shadow:var(--shadow-2);overflow:hidden;
}
.card__head{
  padding:var(--s-5) var(--s-6);background:var(--brand-050);
  border-block-end:var(--bw-2) solid var(--brand-100);
  display:flex;align-items:center;gap:var(--s-4);flex-wrap:wrap;
}
[data-theme="dark"] .card__head{background:var(--c-surface-2);border-block-end-color:var(--c-line)}
/* Form cards were carrying four different hard-coded widths as inline styles.
   One measure, so a form does not change shape as the manager moves between
   screens. */
.card--form{max-inline-size:var(--measure-form)}
.card--form-sm{max-inline-size:var(--measure-form-sm)}
.card__head h2{font-size:var(--fs-md);color:var(--c-accent)}
.card__head .spacer{margin-inline-start:auto}
.card__body{padding:var(--s-6)}

/* ---- controls ---------------------------------------------------------- */
.field{display:flex;flex-direction:column;gap:var(--s-2);margin-block-end:var(--s-5)}
.field > label{font-size:var(--fs-xs);font-weight:var(--fw-b);color:var(--c-ink-2)}
/* D185 -- one hairline, the control height alone, and a chevron that sits
   inside the box. The padding was --s-3 above and below the line, so the
   control rendered six pixels taller than --ctl-h said and read taller than
   it was wide; the height now comes from min-block-size only. --c-line stays at 3.24:1 (WCAG 1.4.11):
   it is the width that was heavy, not the colour, and reps read outdoors. */
.input,select.input,textarea.input{
  font:var(--fw-n) var(--fs-sm)/1.4 var(--f-ui);
  padding:0 var(--s-3);min-block-size:var(--ctl-h);
  border:var(--bw) solid var(--c-line);border-radius:var(--r-1);
  background:var(--c-surface);color:var(--c-ink);inline-size:100%;
  transition:border-color var(--dur) var(--ease);
}
textarea.input{padding-block:var(--s-2)}
.input:hover{border-color:var(--c-ink-3)}
/* The native arrow sat against the right edge and drew differently in every
   browser. Two gradients draw the two strokes of a chevron in the ink colour,
   so it follows the theme without a literal colour in an SVG. */
select.input:not([multiple]){
  appearance:none;-webkit-appearance:none;
  padding-inline-end:calc(var(--chev-inset) * 2 + var(--chev) * 2);
  background-image:
    linear-gradient(45deg, transparent calc(50% - var(--bw)), var(--c-ink-2) calc(50% - var(--bw)),
      var(--c-ink-2) calc(50% + var(--bw)), transparent calc(50% + var(--bw))),
    linear-gradient(135deg, transparent calc(50% - var(--bw)), var(--c-ink-2) calc(50% - var(--bw)),
      var(--c-ink-2) calc(50% + var(--bw)), transparent calc(50% + var(--bw)));
  background-position:
    calc(100% - var(--chev-inset) - var(--chev)) 50%,
    calc(100% - var(--chev-inset)) 50%;
  background-size:var(--chev) var(--chev);
  background-repeat:no-repeat;
}
/* A filter control sits in a row and sizes to its content. Without this an
   .input fills its container, and a four-filter row becomes a four-deep
   stack that pushes the table off the screen.

   The element-qualified selectors are not decoration. The base rule above is
   `.input,select.input,textarea.input`, and `select.input` (0,1,1) outranks a
   bare `.input--auto` (0,1,0) -- the modifier loses on specificity exactly the
   way .btn--onband lost on source order. A modifier must be able to reach
   every selector its base was written with. */
.input--auto,select.input--auto,textarea.input--auto{inline-size:auto}

/* D185 -- Title Case on every label the product writes: headings, buttons,
   navigation, field labels, the terms of a definition list, and a select's
   shown value. The client's developer, 2026-10-08: "بحب ان اول حرف ف كل كلمه
   يبقي كابتل", every word, small ones included. **Sentences stay as written**
   -- hints, banners, empty-state bodies and subheadings are prose, and a
   capital on every word of a sentence reads as shouting. Done here rather
   than in ~150 templates and every model's verbose_name (a migration each),
   so one rule says it and nothing drifts. */
h1,h2,h3,h4,legend,dt,.btn,.subnav a,.field > label,select.input,
.input::placeholder,.tile__title{text-transform:capitalize}
.input:focus{outline:0;border-color:var(--c-accent);box-shadow:var(--focus-ring)}
.input[aria-invalid="true"]{border-color:var(--c-danger);background:var(--c-danger-soft)}
.input:disabled{background:var(--c-surface-2);color:var(--c-ink-4);cursor:not-allowed}
/* BR-127b -- the specialty filter is a Clinic's. Drawn disabled the moment
   another type is picked, before the form is sent; the server ignores it. */
form:has(select[name="type"] option:checked:not([value=""]):not([value="doctor"])) select.input[name="specialty"]{
  background-color:var(--c-surface-2);color:var(--c-ink-4);pointer-events:none}
/* BR-34a -- a doctor is never sold to, so the credit limit is not offered.
   Driven entirely by the current <select> value: :checked on an <option>
   tracks the live selection, so the field disappears the moment the manager
   picks Doctor. No JavaScript, which keeps this a server-rendered app with
   one place to look when the rule changes.

   If a browser lacks :has(), the field stays visible and the server discards
   the value anyway -- the rule degrades to invisible, never to wrong. */
form:has(#id_type option[value="doctor"]:checked) .field--sellable-only{display:none}
/* BR-129, D139 -- a control appears for the types whose own tab carries it.
   Four rules rather than a class per absence: `field--typed` says "this one
   is type-specific", and `field--for-<type>` says which. A control with no
   matching class for the chosen type is hidden.

   Same degradation as the rule above: without :has() every control stays
   visible and the server still stores only what was sent. Invisible, never
   wrong. */
form:has(#id_type option[value="doctor"]:checked) .field--typed:not(.field--for-doctor){display:none}
form:has(#id_type option[value="pharmacy"]:checked) .field--typed:not(.field--for-pharmacy){display:none}
form:has(#id_type option[value="medical_centre"]:checked) .field--typed:not(.field--for-medical_centre){display:none}
form:has(#id_type option[value="hospital"]:checked) .field--typed:not(.field--for-hospital){display:none}
form:has(#id_type option[value="institutional_hospital"]:checked) .field--typed:not(.field--for-institutional_hospital){display:none}
/* BR-137a, D152 -- on a Clinic the name field is the doctor's name, with the
   clinic's own name beside it, so its label says so. Without :has() the
   label stays "Name", which is true for every type. The Clinic half also
   carries `hidden` in the markup, so a stale copy of this file shows "Name"
   too -- which is why the rule that reveals it needs !important: it has to
   beat `[hidden]{display:none!important}` at the top of this file, and does
   on specificity. Measured in a browser: without it the label went blank. */
.label--for-doctor{display:none}
.label--not-doctor{display:inline}
form:has(#id_type option[value="doctor"]:checked) .label--for-doctor[hidden]{display:inline!important}
form:has(#id_type option[value="doctor"]:checked) .label--not-doctor{display:none}
.hint{font-size:var(--fs-xs);color:var(--c-ink-3)}
.error{font-size:var(--fs-xs);color:var(--c-danger);font-weight:var(--fw-b)}
/* ---- pick list: a choice that carries a follow-up ----------------------- *
 * DESIGN-SYSTEM.md 5.20, BR-113, D123.
 *
 * The follow-up belongs to the choice and appears only when it is made.
 * S-104 offered a stage select for every product including the unticked ones,
 * and E5.21 refuses a stage for a product that was not discussed -- so four
 * of five controls offered an answer the system would reject.
 *
 * ⚠️ **CSS, never a round trip.** HTMX is this project's tool for a control
 * that depends on another field, and it is the wrong one here: a request on
 * every tick, on a screen a rep opens several times a day on mobile data
 * (R2, R3). BR-113a.
 *
 * ⚠️ **It fails safe.** If this stylesheet does not load, `display:none`
 * never applies and every follow-up is visible -- the previous behaviour,
 * not a form missing its fields.
 * ------------------------------------------------------------------------ */
.picks{list-style:none;margin:0;padding:0}
.pick + .pick{margin-block-start:var(--s-3)}
.pick__stage{display:none;margin-inline-start:var(--s-6);margin-block-start:var(--s-2)}
.pick:has(input:checked) .pick__stage{display:block}
.pick__stage label{display:block;font-size:var(--fs-xs);color:var(--c-ink-3);margin-block-end:var(--s-1)}
/* The label of a field whose control is a list rather than one input. A
   <label for> would point at nothing. */
.field__label{display:block;font-size:var(--fs-sm);font-weight:var(--fw-b);margin-block-end:var(--s-2)}

/* BR-154, D172 -- one doctor's half of an arrival. A venue visit is one
   block per doctor and each becomes its own visit, so the blocks need to
   read as separate answers rather than as one long form. A rule and a
   title, nothing else: the fields inside are the same fields the screen
   already had. DESIGN-SYSTEM.md 5.24. */
.meeting + .meeting{margin-block-start:var(--s-5);padding-block-start:var(--s-5);border-block-start:var(--bw) solid var(--c-line-2)}
.meeting__head{display:flex;align-items:center;gap:var(--s-3);margin-block-end:var(--s-3)}
.meeting__title{margin:0;font-size:var(--fs-sm);font-weight:var(--fw-b);color:var(--c-ink-2)}

.checkbox{display:flex;align-items:center;gap:var(--s-3);font-size:var(--fs-sm)}
/* On the element, not on a wrapper -- found on screen in P14, present since
   P13. `.checkbox input` only reaches a checkbox somebody remembered to wrap,
   and Django's CheckboxSelectMultiple and RadioSelect build their own markup
   (input inside label inside div) which no template wraps. Those rendered at
   the browser default size, in the OS accent -- blue, beside a product whose
   every other control is teal, and a smaller tap target than the one §5.2
   already recorded as a defect and fixed. A rule that depends on being
   remembered is a rule that is eventually forgotten at one call site.
   (Measurements are in the test and the checkpoint. Quoting them here breaks
   the no-literal-dimension guard -- which is how the deleted logo token broke
   collectstatic a second time, from inside the comment explaining it.) */
input[type=checkbox],input[type=radio]{
  inline-size:var(--ctl-box);block-size:var(--ctl-box);accent-color:var(--c-accent)
}

.btn{
  font:var(--fw-b) var(--fs-sm)/1 var(--f-ui);
  padding-inline:var(--s-5);min-block-size:var(--ctl-h);
  display:inline-flex;align-items:center;justify-content:center;gap:var(--s-2);
  border:var(--bw-2) solid var(--c-accent);border-radius:var(--r-1);
  background:transparent;color:var(--c-accent);cursor:pointer;text-decoration:none;
  transition:background var(--dur) var(--ease),color var(--dur) var(--ease);
}
.btn:hover{background:var(--c-accent-soft)}
.btn:focus-visible{outline:0;box-shadow:var(--focus-ring)}
.btn--primary{background:var(--c-accent);color:var(--c-accent-ink)}
.btn--primary:hover{background:var(--brand-800);color:var(--c-accent-ink)}
.btn--ghost{border-color:transparent}
.btn--danger{background:var(--c-danger);border-color:var(--c-danger);color:var(--c-danger-ink)}
.btn:disabled{opacity:.42;cursor:not-allowed}

/* ---- buttons on the brand band ----------------------------------------- *
 * EVERY modifier below must stay AFTER .btn. Two reasons, and the first one
 * shipped invisible buttons on eight screens once already:
 *   1. .btn uses the `border` SHORTHAND, which resets border-color. A
 *      modifier that only sets border-color and is declared earlier loses.
 *   2. .btn and .btn--x have identical specificity, so source order decides.
 * The failure is silent: the button renders in --c-accent, which IS
 * --brand-700, which is exactly where the band gradient starts -- 1.00:1.
 * DESIGN-SYSTEM.md §5.1b.
 * ------------------------------------------------------------------------ */

/* The one action the band exists for. Solid, because it is competing with a
   saturated gradient and an outline is not enough. */
.btn--onband{
  background:var(--c-onbrand-fill);
  border-color:var(--c-onbrand-fill);
  color:var(--c-onbrand-fill-ink);
}
.btn--onband:hover{
  background:var(--c-onbrand-fill);
  border-color:var(--c-onbrand-fill);
  color:var(--brand-900);
}

/* Secondary routes off the band -- "back to", cancel. Outlined, so it cannot
   be mistaken for the primary action sitting next to it. */
.btn--onband-quiet{
  background:transparent;
  border-color:var(--c-onbrand-line);
  color:var(--c-onbrand);
}
.btn--onband-quiet:hover{
  background:var(--c-onbrand-wash);
  border-color:var(--c-onbrand);
  color:var(--c-onbrand);
}
.btn-row{display:flex;gap:var(--s-3);flex-wrap:wrap;align-items:center}
/* D184 -- controls swapped together, laid out as if unwrapped (DESIGN-SYSTEM 5.17) */
.btn-row__group{display:contents}

/* ---- badges ------------------------------------------------------------ */
/* 🔴 A dot and the word, no fill -- 2026-10-09, DESIGN-SYSTEM 5.3. The
   client's developer on the old pill: "الخط بولد اوي متعب للعين و مش
   احترافي", then chose this of three. Every badge in the product: the
   colour is the dot's, the word is ordinary ink at a normal weight, and the
   case is the source's own. On the band the word takes the band's ink. */
.badge{
  display:inline-flex;align-items:center;gap:var(--s-2);white-space:nowrap;
  font-size:var(--fs-xs);font-weight:var(--fw-m);color:var(--c-ink-2);
}
.badge::before{
  content:"";flex:none;inline-size:var(--s-2);block-size:var(--s-2);
  border-radius:var(--r-pill);background:var(--badge-dot,var(--c-mute));
}
.band .badge{color:inherit}
.badge--ok{--badge-dot:var(--c-ok)}
.badge--warn{--badge-dot:var(--c-warn)}
.badge--danger{--badge-dot:var(--c-danger)}
.badge--info{--badge-dot:var(--c-info)}
.badge--mute{--badge-dot:var(--c-mute)}
.badge--brand{--badge-dot:var(--c-accent)}

/* D190, BR-167 -- the customer's colour for the month, DESIGN-SYSTEM 5.25.
   The badge's dot in the client's colour with the number beside it, a
   stripe down the row's leading edge and the row tinted. The stripe sits on the first cell, never on the
   tr -- a border on a row is dropped by the table's own layout. */
.visits{margin-inline-start:var(--s-3);font-weight:var(--fw-b)}
.visits--0{--badge-dot:var(--c-visits-0)}
.visits--1{--badge-dot:var(--c-visits-1)}
.visits--2{--badge-dot:var(--c-visits-2)}
.visits--3{--badge-dot:var(--c-visits-3)}
.visits--4{--badge-dot:var(--c-visits-4)}
/* The whole row tinted, pale, over the zebra -- the client's developer on
   seeing the stripe alone: "الصف كله يبقي واخد اللون بس الوان باهته شويه".
   One step more specific than the zebra and the hover below, which would
   otherwise paint every other row back to grey. */
table tbody tr.row-visits--0{background:var(--c-visits-0-soft)}
table tbody tr.row-visits--1{background:var(--c-visits-1-soft)}
table tbody tr.row-visits--2{background:var(--c-visits-2-soft)}
table tbody tr.row-visits--3{background:var(--c-visits-3-soft)}
table tbody tr.row-visits--4{background:var(--c-visits-4-soft)}
tbody tr[class*="row-visits--"]:hover{filter:brightness(0.97)}
tr.row-visits--0 > td:first-child{border-inline-start:var(--bw-4) solid var(--c-visits-0)}
tr.row-visits--1 > td:first-child{border-inline-start:var(--bw-4) solid var(--c-visits-1)}
tr.row-visits--2 > td:first-child{border-inline-start:var(--bw-4) solid var(--c-visits-2)}
tr.row-visits--3 > td:first-child{border-inline-start:var(--bw-4) solid var(--c-visits-3)}
tr.row-visits--4 > td:first-child{border-inline-start:var(--bw-4) solid var(--c-visits-4)}

/* ---- chips ------------------------------------------------------------- */
/* DESIGN-SYSTEM.md 5.12 -- soft brand fill, accent text, pill, tabular
   figures. For compact key-value pairs: a rep stock summary, or the three
   figures AC-1.3 puts in front of a rep before the first invoice line.

   **Specified since the design system was written and implemented in P7.**
   Until then the only thing shaped like it was `.band__stat`, which belongs
   to the band -- it is coloured for the dark gradient (`--c-onbrand-2`) and
   its children are `.lab` and `.val`. Used inside a card it was the wrong
   colour for the surface and, because the child classes did not match, was
   not laid out at all: label and value rendered touching, with no gap
   whatever between them on a phone.

   Same soft-fill/matching-text pairing as `.badge`, so the two read as one
   family rather than as two people's ideas. */
.chips{display:flex;gap:var(--s-3);flex-wrap:wrap;margin:0;padding:0;list-style:none}
.chip{
  display:inline-flex;align-items:baseline;gap:var(--s-2);
  padding:var(--s-2) var(--s-4);border-radius:var(--r-pill);
  background:var(--c-accent-soft);color:var(--c-accent-on-soft);
  font-family:var(--f-num);font-variant-numeric:tabular-nums;
}
.chip__lab{font-size:var(--fs-xs);font-weight:var(--fw-m)}
.chip__val{font-size:var(--fs-md);font-weight:var(--fw-b)}

/* ---- table ------------------------------------------------------------- */
.table-wrap{overflow-x:auto}
table{border-collapse:collapse;inline-size:100%;font-size:var(--fs-sm)}
thead th{
  background:var(--brand-700);color:var(--c-onbrand);
  padding:var(--s-3) var(--cell-x);text-align:start;
  font-size:var(--fs-xs);font-weight:var(--fw-b);
  text-transform:uppercase;letter-spacing:var(--ls-wide);white-space:nowrap;
}
[data-theme="dark"] thead th{background:var(--c-surface-3);color:var(--c-ink)}
tbody td{
  padding:var(--cell-y) var(--cell-x);block-size:var(--row-h);
  border-block-end:var(--bw) solid var(--c-line-2);line-height:var(--lh-data);
}
/* AMENDMENT 1 -- table bodies are neutral. Colour lives in the chrome. */
tbody tr:nth-child(odd){background:var(--c-row-alt)}
tbody tr:hover{background:var(--c-row-hover)}
.cell-stack{display:flex;flex-direction:column;gap:var(--s-1)}
/* BR-139a, D155 -- a person's photograph, or their initials, beside their
   name. DESIGN-SYSTEM.md 5.14a. Neutral on a neutral surface; the rail's
   avatar keeps its own brand colours. */
.person{display:flex;align-items:center;gap:var(--s-3)}
.person--lg{gap:var(--s-5);flex-wrap:wrap}
.avatar{
  flex:none;display:grid;place-items:center;overflow:hidden;
  border-radius:var(--r-pill);background:var(--c-surface-2);color:var(--c-ink-3);
  font-weight:var(--fw-b);
}
.avatar--sm{inline-size:var(--avatar-sm);block-size:var(--avatar-sm);font-size:var(--fs-xs)}
.avatar--lg{inline-size:var(--avatar-lg);block-size:var(--avatar-lg);font-size:var(--fs-xl)}
.avatar__img{inline-size:100%;block-size:100%;object-fit:cover;display:block}
.sub{color:var(--c-ink-3);font-size:var(--fs-xs)}
.strong{font-weight:var(--fw-b)}

/* ---- tile -- DESIGN-SYSTEM.md 5.18 -------------------------------------
   One thing that wants attention: a decision, a blocked sale, a destination.
   A row rather than a table, because the field half of the product is 11
   reps on phones and a three-column table is a horizontal scroll there.
   Neutral body per Amendment 1 -- the status colour is the badge's job,
   never the row's fill.                                                  */
/* Chart -- DESIGN-SYSTEM.md 5.19. Horizontal bars, one series, no axis.
   Server-rendered markup: there is no charting library and no JavaScript,
   which is the point of it (BR-57g, D98).

   **Boxes and widths rather than SVG**, and that was a correction: inside an
   SVG viewBox the coordinate system is unitless, so a font-size from this
   file resolves to user units and the labels rendered several times the
   height of the drawing. A bar is a box with a width; HTML does that
   responsively and with real text. */
.chart{margin:0;display:block}
.chart__rows{display:flex;flex-direction:column;gap:var(--s-5);margin:0;padding:0;list-style:none}
.chart__row{display:grid;grid-template-columns:minmax(0,var(--chart-label-w)) 1fr auto;align-items:center;gap:var(--s-4)}
/* D156 -- slim, spaced, rounded. The client, on the first dashboard mockup:
   "الخطوط دي لزقه ف بعضها". A thick bar in a tight stack reads as one block;
   a slim one with room around it reads as a list. One component, so the
   reports took the same shape -- the client was told before approving. */
.chart__label{color:var(--c-ink-2);font-size:var(--fs-sm);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.chart__track{background:var(--c-row-alt);border-radius:var(--r-pill);block-size:var(--s-3);inline-size:100%}
/* --c-accent is brand: DESIGN-SYSTEM.md 2.4 reserves it for counts and
   neutral emphasis, never status. A magnitude is neutral emphasis. */
.chart__bar{display:block;background:var(--c-accent);border-radius:var(--r-pill);block-size:100%}
/* BR-140c -- goods and field work are orange: the bar only, never the text
   beside it. DESIGN-SYSTEM.md 2.2b's fifth place. */
.chart--goods .chart__bar{background:var(--accent-orange)}
.chart__value{color:var(--c-ink);font-size:var(--fs-sm);font-weight:var(--fw-b);font-variant-numeric:tabular-nums;white-space:nowrap}
.chart__more{color:var(--c-ink-3);font-size:var(--fs-xs);margin-block-start:var(--s-3)}
/* On a phone the label takes its own line above the bar, the same move
   .tile makes for its value -- three columns on a narrow screen leaves the
   track no room at all. Width kept equal to --bp-tablet by hand; CSS cannot
   read var() in a @media condition, and a guard test asserts they match. */
@media (max-width:900px){
  .chart__row{grid-template-columns:1fr auto;row-gap:var(--s-1)}
  .chart__label{grid-column:1/-1}
}

.tiles{display:flex;flex-direction:column;gap:var(--s-2);margin:0;padding:0;list-style:none}

/* ---- the Company Manager's home -- BR-140, D156, DESIGN-SYSTEM.md 5.21 --- */
/* The band's four figures, larger than a report's, each a link to its report,
   with BR-140c's orange rule over the label -- the one mark the band carries,
   and it carries no text. */
.band__stats--hero{gap:var(--s-8)}
.band__stats--hero .band__stat{color:inherit;text-decoration:none}
.band__stats--hero .band__stat::before{
  content:"";display:block;inline-size:var(--s-6);block-size:var(--bw-3);
  border-radius:var(--r-pill);background:var(--accent-orange);margin-block-end:var(--s-3);
}
.band__lab{font-size:var(--fs-xs);color:var(--c-onbrand);margin-block-end:var(--s-1)}
.band__val{font-size:var(--fs-3xl);font-weight:var(--fw-x);font-variant-numeric:tabular-nums}
.band__note{font-size:var(--fs-xs);color:var(--c-onbrand);margin-block-start:var(--s-1)}
.dash{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:var(--s-6)}
.dash + .dash__title,.reps + .dash__title,.reps + .colour-key,.dash + .colour-key{margin-block-start:var(--s-7)}
.dash__wide{grid-column:span 2}
.dash__link{color:inherit;text-decoration:none;transition:box-shadow var(--dur) var(--ease)}
.dash__link:hover{box-shadow:var(--shadow-3)}
.dash__link:focus-visible{outline:var(--focus-ring);outline-offset:var(--bw)}
.dash__tile{display:flex;flex-direction:column;gap:var(--s-2);block-size:100%}
.dash__tile--centre{align-items:center;text-align:center}
.dash__lab{font-size:var(--fs-xs);text-transform:uppercase;letter-spacing:var(--ls-wide);color:var(--c-ink-3);font-weight:var(--fw-b)}
.dash__val{font-size:var(--fs-xl);font-weight:var(--fw-x);color:var(--c-ink)}
.dash__danger{color:var(--c-danger)}
.dash__sub{font-size:var(--fs-xs);text-transform:uppercase;letter-spacing:var(--ls-wide);color:var(--c-ink-3);margin-block:var(--s-5) var(--s-3)}
.dash__title{font-size:var(--fs-md);color:var(--c-ink);display:flex;gap:var(--s-3);align-items:baseline;margin-block-end:var(--s-4)}
/* BR-140a, D88 -- money owed in its two parts, never in age bands. */
.split{display:flex;gap:var(--bw-2);block-size:var(--s-4);border-radius:var(--r-pill);overflow:hidden;margin-block:var(--s-2) var(--s-5)}
.split__part{background:var(--c-accent)}
.split__part--over{background:var(--c-danger)}
.legend{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:var(--s-3)}
.legend li{display:grid;grid-template-columns:auto 1fr auto auto;gap:var(--s-3);align-items:center;font-size:var(--fs-sm)}
.legend__key{inline-size:var(--s-3);block-size:var(--s-3);border-radius:var(--r-pill);background:var(--c-accent)}
.legend__key--over{background:var(--c-danger)}
.late{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:var(--s-3)}
.late li{display:grid;grid-template-columns:1fr auto auto;gap:var(--s-3);align-items:center;font-size:var(--fs-sm)}
.late a{color:inherit;text-decoration:none}
.late a:hover .strong{color:var(--c-accent)}
.avstack{display:flex;margin-block:var(--s-2)}
.avstack .avatar{margin-inline-end:calc(var(--s-2) * -1);border:var(--bw-2) solid var(--c-surface)}
.avatar--md{inline-size:var(--rail-avatar);block-size:var(--rail-avatar);font-size:var(--fs-xs)}
/* DESIGN-SYSTEM.md 5.19a -- a share as a ring. Orange: visits are field work. */
.ring{position:relative;display:inline-block;inline-size:var(--ring);aspect-ratio:1}
.ring--big{inline-size:var(--ring-lg)}
.ring svg{inline-size:100%;block-size:100%;transform:rotate(-90deg)}
.ring__track{fill:none;stroke:var(--accent-orange-soft);stroke-width:10}
.ring__val{fill:none;stroke:var(--accent-orange);stroke-width:10;stroke-linecap:round}
.ring__pct{position:absolute;inset:0;display:grid;place-items:center;font-weight:var(--fw-x);font-size:var(--fs-md);color:var(--c-ink)}
.ring--big .ring__pct{font-size:var(--fs-xl)}
.reps{display:grid;gap:var(--s-5)}
.reps--4{grid-template-columns:repeat(auto-fit,minmax(min(100%,var(--rep-card-min)),1fr))}
.reps--5{grid-template-columns:repeat(auto-fit,minmax(min(100%,var(--rep-card-min-sm)),1fr))}
.rep-card .card__body{display:flex;flex-direction:column;gap:var(--s-2)}
.rep-card--centre .card__body{align-items:center;text-align:center}
.rep-card--centre .person{align-self:stretch;text-align:start}
.rep-card__big{font-size:var(--fs-xl);font-weight:var(--fw-x);color:var(--c-ink);margin-block-start:var(--s-4)}
.meter{display:block;block-size:var(--s-2);border-radius:var(--r-pill);background:var(--c-row-alt);overflow:hidden;margin-block:var(--s-2) var(--s-3)}
.meter i{display:block;block-size:100%;background:var(--c-accent);border-radius:inherit}
.facts{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:var(--s-3);margin:0}
.facts dt{font-size:var(--fs-xs);color:var(--c-ink-3)}
.facts dd{margin:0;font-weight:var(--fw-b);font-size:var(--fs-sm)}
/* BR-145 -- a sales rep's day as an agenda: time, customer, state. Tiles
   wrap to four lines on a phone, and five visits were a screen and a half. */
.agenda{list-style:none;margin:0;padding:0;display:flex;flex-direction:column}
.agenda li{display:grid;grid-template-columns:auto minmax(0,1fr) auto;gap:var(--s-3);align-items:center;
  padding-block:var(--s-3);border-block-end:var(--bw) solid var(--c-line)}
.agenda li:last-child{border-block-end:0}
.agenda a{color:inherit;text-decoration:none}
.agenda a:hover .strong{color:var(--c-accent)}
.dash__ring-row{display:flex;align-items:center;gap:var(--s-6);flex-wrap:wrap}
.facts--stack{grid-template-columns:minmax(0,1fr);gap:var(--s-2)}
.dot{display:inline-block;inline-size:var(--s-2);block-size:var(--s-2);border-radius:var(--r-pill);margin-inline-end:var(--s-1);vertical-align:middle}
.dot--goods{background:var(--accent-orange)}
.colour-key{display:flex;gap:var(--s-6);flex-wrap:wrap;font-size:var(--fs-xs);color:var(--c-ink-3);margin:0}
.colour-key span{display:inline-flex;align-items:center;gap:var(--s-2)}
.swatch{inline-size:var(--s-4);block-size:var(--s-2);border-radius:var(--r-pill);display:inline-block;background:var(--c-accent)}
.swatch--goods{background:var(--accent-orange)}
.swatch--over{background:var(--c-danger)}
.tile{
  display:flex;align-items:center;gap:var(--s-4);
  padding:var(--s-4);min-block-size:var(--ctl-h-touch);
  border:var(--bw) solid var(--c-line);border-radius:var(--r-2);
  background:var(--c-surface);color:inherit;text-decoration:none;
  transition:background var(--dur-fast) var(--ease),
             border-color var(--dur-fast) var(--ease);
}
.tile__body{display:flex;flex-direction:column;gap:var(--s-1);min-inline-size:0;flex:1}
.tile__title{font-weight:var(--fw-b);line-height:var(--lh-tight)}
.tile__meta{color:var(--c-ink-3);font-size:var(--fs-xs)}
/* Tabular figures, for the same reason every money column carries them. */
.tile__value{
  font-family:var(--f-num);font-variant-numeric:tabular-nums;
  font-weight:var(--fw-b);white-space:nowrap;
}
/* The affordance that says the row goes somewhere. Hidden from the reader
   because the title is already the link text. */
.tile__go{color:var(--c-ink-3);flex:none}
a.tile:hover{background:var(--c-row-hover);border-color:var(--c-line-strong)}
a.tile:focus-visible{outline:var(--focus-ring);outline-offset:var(--bw)}
/* A tile that is not a link -- a fact rather than a destination. */
.tile--flat{background:var(--c-surface-2)}

/* ---- messages / banners ------------------------------------------------ */
.banner{
  padding:var(--s-4) var(--s-5);border-radius:var(--r-2);
  border-inline-start:var(--bw-3) solid;font-size:var(--fs-sm);
}
.banner--ok{background:var(--c-ok-soft);color:var(--c-ok);border-color:var(--c-ok)}
.banner--warn{background:var(--c-warn-soft);color:var(--c-warn);border-color:var(--c-warn)}
.banner--danger{background:var(--c-danger-soft);color:var(--c-danger);border-color:var(--c-danger)}
.banner--info{background:var(--c-info-soft);color:var(--c-info);border-color:var(--c-info)}
.banner strong{display:block;margin-block-end:var(--s-1)}

/* ---- states ------------------------------------------------------------ */
.empty{text-align:center;padding:var(--s-8) var(--s-6)}
.empty__icon{
  inline-size:var(--empty-icon);block-size:var(--empty-icon);border-radius:var(--r-pill);
  background:var(--c-accent);color:var(--c-accent-ink);
  margin:0 auto var(--s-5);display:grid;place-items:center;
  font-size:var(--fs-xl);font-weight:var(--fw-x);
}
.empty h3{margin-block-end:var(--s-2);font-size:var(--fs-lg)}
.empty p{margin:0 0 var(--s-5);color:var(--c-ink-3);font-size:var(--fs-sm)}
.skeleton{
  block-size:var(--row-h);border-radius:var(--r-1);margin-block-end:var(--s-2);
  background:linear-gradient(90deg,var(--c-surface-2) 25%,var(--brand-100) 50%,var(--c-surface-2) 75%);
  background-size:200% 100%;animation:shimmer var(--dur-slow) linear infinite;
}
@keyframes shimmer{to{background-position:-200% 0}}
/* A skeleton standing in for a form control rather than a table row.
   DESIGN-SYSTEM.md 5.11 -- at the height of the real content, so nothing
   reflows when the answer arrives. */
.skeleton--field{block-size:var(--ctl-h)}
/* The only swapped region in the product: the rep pickers, refetched when the
   city changes (ADR-021). The stale reps are hidden while it loads because
   they are the wrong city's -- which is the whole point of ADR-021 -- not
   because a spinner should ever cover a rep's screen. */
.swap-region .skeleton-group{display:none}
.swap-region.htmx-request .skeleton-group{display:block}
.swap-region.htmx-request .swap-target{display:none}

/* ---- auth -------------------------------------------------------------- */
/* ---- sign-in ------------------------------------------------------------ *
   Two panels: the identity on one side, the work on the other. The screen a
   client opens first, and the only one in the product where the brand is the
   subject rather than the chrome around data. DESIGN-SYSTEM.md 6.2.

   It was a single card centred on a flat teal field. The card was fine; the
   field around it said nothing.                                             */
.auth{
  min-block-size:100vh;display:grid;
  grid-template-columns:
    clamp(var(--auth-brand-min), var(--auth-brand-share), var(--auth-brand-max)) 1fr;
  /* Not redundant, and leaving it out cost the heading. `.auth` re-declares
     the dark tokens (tokens.css), but `color` inherits as a COMPUTED value:
     <body> had already resolved var(--c-ink) against the light palette and
     passed that dark ink down. Re-declaring the variable underneath does not
     re-resolve a colour that was inherited from above it. So "Sign in"
     rendered near-black on a near-black panel -- in the DOM, styled, and
     invisible. */
  color:var(--c-ink);
}

/* The unlit ground. Everything above it is light being cast. */
.auth__brand{
  position:relative;overflow:hidden;isolation:isolate;
  background:var(--auth-base);
  color:var(--c-onbrand);
  display:grid;align-content:center;
  padding:var(--s-9) var(--s-8);
}
/* Three casts, warm over cool, all soft-edged and all far larger than the
   panel. Two hard colour fields would be a flag; light that falls off is a
   light source. The orange sits low and the teal high, so the eye reads one
   source rather than two swatches. */
/* `screen`, because light adds and paint does not. Layered normally, the
   orange sat over a dark teal ground and turned brown -- the exact muddiness
   that made the blurred version fail. Screened, each pool keeps its own hue
   and the overlap brightens instead of dulling. */
.auth__brand::before{
  content:"";position:absolute;inset:0;z-index:-3;
  mix-blend-mode:screen;
  background:
    radial-gradient(70% 50% at 78% 10%, var(--auth-glow-teal),   transparent 70%),
    radial-gradient(80% 58% at 62% 96%, var(--auth-glow-orange), transparent 70%),
    radial-gradient(60% 48% at 102% 46%, var(--auth-glow-lift),  transparent 68%);
}
/* Sharp, oversized, and quiet -- a form standing in the light rather than a
   logo printed on it. White, because the coloured version competes with the
   glows it is supposed to be lit by. */
.auth__mark{
  position:absolute;pointer-events:none;z-index:-2;
  inline-size:var(--auth-mark-size);block-size:auto;
  opacity:var(--auth-mark-opacity);
  inset-inline-end:calc(var(--auth-mark-size) / -2.4);
  inset-block-start:calc(var(--auth-mark-size) / -3.2);
}
/* The legibility scrim, and the reason the light sits on the right: the copy
   lives on the left, so the pools are placed where no words are and the
   scrim only has to hold the near edge. An earlier pass put the teal behind
   the heading and then darkened it back down -- protecting the text by
   deleting the thing it was supposed to sit in front of. */
.auth__brand::after{
  content:"";position:absolute;inset:0;z-index:-1;pointer-events:none;
  background:linear-gradient(96deg,
    var(--auth-scrim) 0%, var(--auth-scrim-mid) 30%, transparent 58%);
}
.auth__brandinner{position:relative}
/* The lockup carries the company name on its own. A "Lumina Pharma" heading
   used to sit beneath it, which was the wordmark set twice. */
.auth__logo{display:block;block-size:var(--logo-h-auth);inline-size:auto;margin-block-end:var(--s-7)}
/* The company's own line, and now the only line -- so it is sized as a
   statement rather than a caption. --c-onbrand, not the dim token: that one
   measures 2.94:1 here. See the note on .band__sub. */
.auth__blurb{
  color:var(--c-onbrand);font-size:var(--auth-tagline);
  line-height:var(--lh-tight);max-inline-size:var(--measure-form-sm);
}

/* Near the brand panel, not opposite it. `--c-surface` resolves against the
   dark palette here (see the `.auth` selector in tokens.css), so this is a
   lifted step off the same ground rather than a white slab bolted to it. */
.auth__panel{
  background:var(--c-surface);
  display:grid;place-items:center;padding:var(--s-9) var(--s-8);
}
/* --measure-form-sm, not --measure-card. The card measure centred in the
   space a wide display leaves reads as an island; that width was written for
   a card sitting on a background, and this is a panel. */
.auth__form{inline-size:100%;max-inline-size:var(--measure-form-sm)}
.auth__form h1{font-size:var(--fs-2xl);line-height:var(--lh-tight);margin-block-end:var(--s-2)}
.auth__lede{color:var(--c-ink-3);font-size:var(--fs-md);margin-block-end:var(--s-8)}
/* The refusal needs air under it or it reads as a caption on the first
   field. It carried an inline style until the panel rewrite, which is why it
   was easy to drop. */
.auth__form .banner{margin-block-end:var(--s-6)}

/* Sign-in fields are the touch size on every screen, not just phones. The
   desktop --ctl-h is right in a dense table filter row; on a panel holding
   two fields it reads as a form nobody finished. This is also the one form a
   client judges the product by before seeing anything else. */
.auth__form .input{min-block-size:var(--ctl-h-touch);font-size:var(--fs-md)}
.auth__form .field{gap:var(--s-3);margin-block-end:var(--s-6)}
.auth__form .field > label{font-size:var(--fs-sm)}
.auth__submit{min-block-size:var(--ctl-h-touch);font-size:var(--fs-md)}
.auth__submit{inline-size:100%;margin-block-start:var(--s-2)}
.auth__help{margin-block-start:var(--s-6)}

/* ---- the reports index -- S-45, D166, DESIGN-SYSTEM.md 5.23 -------------
   The client, 2026-09-25, choosing from five drawings: a card per report,
   grouped, each carrying the figure it would open on. The card is a link:
   the whole of it, so a thumb has the target the tile gave it. */
.rgrid{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:var(--s-5)}
.rgrid + .dash__title{margin-block-start:var(--s-7)}
.rcard{text-decoration:none;color:inherit;transition:box-shadow var(--dur) var(--ease)}
.rcard:hover{box-shadow:var(--shadow-3)}
.rcard:focus-visible{outline:var(--focus-ring);outline-offset:var(--bw)}
.rcard .card__body{display:flex;flex-direction:column;gap:var(--s-2);block-size:100%}
.rcard__top{display:flex;align-items:flex-start;justify-content:space-between;gap:var(--s-4);margin-block-end:var(--s-2)}
/* The rail's own mark for the section this report belongs to: the index and
   the navigation then say the same thing about where a report lives. */
.rcard__icon{inline-size:var(--ctl-h);block-size:var(--ctl-h);border-radius:var(--r-2);
  display:grid;place-items:center;flex:none;
  background:var(--c-accent-soft);color:var(--c-accent-on-soft)}
.rcard__figure{text-align:end;display:flex;flex-direction:column;gap:var(--s-1);min-inline-size:0}
.rcard__value{font-size:var(--fs-xl);font-weight:var(--fw-x);color:var(--c-ink);line-height:1.1}
.rcard__note{font-size:var(--fs-xs);color:var(--c-ink-3)}
.rcard__title{font-weight:var(--fw-b);font-size:var(--fs-md)}
.rcard__desc{color:var(--c-ink-3);font-size:var(--fs-sm);flex:1}
.rcard__go{color:var(--c-accent);font-size:var(--fs-sm);font-weight:var(--fw-b);margin-block-start:var(--s-3)}
.rcard:hover .rcard__go{color:var(--brand-800)}
/* The whole-company export: words on one side, the date and the button on
   the other, rather than a button above a paragraph it belongs to. */
.export{display:flex;gap:var(--s-6);align-items:center;flex-wrap:wrap}
.export__words{flex:1;min-inline-size:0}
.export__title{font-size:var(--fs-md);color:var(--c-accent);margin:0 0 var(--s-1)}
.export .sub{margin:0}
.export__form{flex:none}

/* ---- the profile -- S-04, D160, DESIGN-SYSTEM.md 5.14c ------------------
   The client, 2026-09-19: the first version was "not professional at all" --
   a table of four rows, a bare browser file control and three primary
   buttons. The person on the start side, what they keep current beside it;
   one column on a phone, the person first. */
.profile{display:grid;grid-template-columns:var(--profile-aside) minmax(0,1fr);gap:var(--s-6);align-items:start}
.profile__id{
  padding:var(--s-7) var(--s-6) var(--s-6);
  background:linear-gradient(var(--brand-050),var(--brand-050)) top / 100% var(--profile-cover) no-repeat,var(--c-surface);
}
.profile__name{font-size:var(--fs-lg);color:var(--c-ink);margin:0}
.profile__fields{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));column-gap:var(--s-5)}
.profile__save{justify-content:flex-end}
/* BR-139 -- the photograph and its controls. */
.photo{position:relative;display:flex;flex-direction:column;align-items:center;gap:var(--s-3);text-align:center}
.photo p{margin:0}
.photo__frame{position:relative;margin-block-end:var(--s-2)}
.photo__frame .avatar{border:var(--bw-3) solid var(--c-surface);box-shadow:var(--shadow-1);
  background:var(--c-accent-soft);color:var(--c-accent-on-soft);font-size:var(--fs-2xl)}
.photo__cam{
  position:absolute;inset-block-end:0;inset-inline-end:0;
  inline-size:var(--avatar-action);block-size:var(--avatar-action);
  display:grid;place-items:center;border-radius:var(--r-pill);cursor:pointer;
  background:var(--c-accent);color:var(--c-accent-ink);border:var(--bw-3) solid var(--c-surface);
  transition:background var(--dur) var(--ease);
}
.photo__cam:hover{background:var(--brand-800)}
.photo__actions{justify-content:center;margin-block-start:var(--s-2)}
/* ---- the file control -- DESIGN-SYSTEM.md 5.2a, D160, D161 --------------
   A browser's own file input is the one control in this product nothing can
   style, and the client said so twice. The input is visually hidden,
   focusable and named; the button that labels it is what the rep sees, and
   the form's own button appears once a file is chosen.

   **No script.** The input is `required`, so it matches `:valid` exactly
   when a file has been chosen -- the same position 5.16 takes on the theme
   toggle: it changes state, so it must work with scripts off.

   ⚠️ **`.file__input` must stay after `.input`.** Every form gives its
   widgets `.input` (core/forms.py), whose `inline-size:100%` would otherwise
   win on source order and leave an absolutely placed box the width of the
   screen -- clipped, but still widening a phone's page. */
.file{position:relative}
.file-control{position:relative;display:block}
.file__input{position:absolute;inline-size:var(--bw);block-size:var(--bw);min-block-size:0;padding:0;border:0;overflow:hidden;clip-path:inset(50%)}
/* A label drawn as a button. It steps back once a file is chosen, below. */
.file__choose{transition:border-color var(--dur) var(--ease),background var(--dur) var(--ease)}
/* Focus lands on the hidden input; the button that labels it shows it. */
.file-control:has(.file__input:focus-visible) .file__choose{box-shadow:var(--focus-ring)}
.file__go,.file__note{display:none}
.file__again{display:none}
.file__first,.file__again{white-space:nowrap}
.file:has(.file__input:valid) .file__go{display:inline-flex;order:-1}
.file-control:has(.file__input:valid) .file__note{display:block}
/* ...and the button that chose it steps back, so sending is the one outlined. */
.file-control:has(.file__input:valid) .file__choose{border-color:transparent}
.file-control:has(.file__input:valid) .file__first{display:none}
.file-control:has(.file__input:valid) .file__again{display:inline}
.file__note{font-size:var(--fs-xs);font-weight:var(--fw-b);color:var(--c-ok);margin-block-start:var(--s-2)}
.file__actions{margin-block-start:var(--s-4)}
/* The four facts the manager keeps: as many to a row as fit, two on a phone. */
.facts--account{grid-template-columns:repeat(auto-fit,minmax(min(100%,var(--fact-min)),1fr));gap:var(--s-5)}
.facts--account dt{text-transform:uppercase;letter-spacing:var(--ls-wide);font-weight:var(--fw-b);margin-block-end:var(--s-1)}
.facts--account dd{font-size:var(--fs-md);overflow-wrap:break-word}
.profile__note{margin:var(--s-5) 0 0;padding-block-start:var(--s-4);border-block-start:var(--bw) solid var(--c-line-2)}

/* ---- error pages ------------------------------------------------------- */
.errpage{max-inline-size:var(--measure-prose);margin:0 auto;padding:var(--s-9) var(--s-5);text-align:center}
.errpage__code{font-size:var(--fs-3xl);font-weight:var(--fw-x);color:var(--c-danger)}
.errpage h1{font-size:var(--fs-xl);margin-block:var(--s-4) var(--s-3)}
.errpage p{color:var(--c-ink-3);font-size:var(--fs-sm);margin-block-end:var(--s-6)}

/* ---- responsive -------------------------------------------------------- */
@media (max-width:900px){
  .app{flex-direction:column}
  /* ⚠️ **The row had no overflow handling at all**, so an icon added later
     pushed Sign out off the screen -- found on a phone, on the distribution
     rep, who carries the most. The bell that D122 added was the one that did
     it: the rail measured wider than the viewport, with part of the sign-out
     control past the edge and unreachable. (Measurements are in the
     checkpoint. Quoting them here breaks the no-literal-dimension guard --
     the same trap the checkbox note fell into, and the deleted logo token
     before it.)

     Two changes, and the second matters more than the first. The tighter gap
     makes today's set fit; `overflow-x:auto` means the *next* addition
     scrolls rather than clips. A nav that silently drops its last control is
     the "right with no route" shape arriving through CSS. */
  /* On a phone the rail is the bar across the top, and it scrolls away with
     the page as it always has -- the sticky rail was asked for on a laptop,
     and a pinned bar costs a phone a row of its screen. */
  .rail{
    inline-size:100%;block-size:var(--rail-h-mobile);
    flex-direction:row;padding-inline:var(--s-3);padding-block:var(--s-3);gap:var(--s-2);
    overflow-x:auto;overscroll-behavior-inline:contain;
    position:static;align-self:stretch;
  }
  .rail::-webkit-scrollbar{display:none}
  .rail{scrollbar-width:none}
  .rail__spacer{margin-block-start:0;margin-inline-start:auto}
  /* Match the touch-height control the skeleton stands in for, so the swap
     does not resize the form on a phone. */
  /* ⚠️ **A checkbox row was half the height of every other control**, on the
     product whose own §3.1 says 11 of 15 users are on phones. Text inputs
     and selects take --ctl-h-touch here; a `.checkbox` took its text's
     height and nothing else, so five stacked rows were a column of narrow
     strips to hit with a thumb.

     **Present since the first commit** and found only when somebody opened
     the screens at this width -- DESIGN-SYSTEM.md 5.2 excludes checkboxes
     from `.input` so the *box* is not stretched, which is right and says
     nothing about the *row*.

     The label carries the height, not the box: the box stays --ctl-box and
     the row becomes tappable along its whole width. */
  .checkbox{min-block-size:var(--ctl-h-touch)}
  .checkbox label{display:flex;align-items:center;min-block-size:var(--ctl-h-touch);flex:1}
  /* ⚠️ **And the same row, when nobody wrapped it.** The rule above needs a
     `.checkbox`, and Django's CheckboxSelectMultiple and RadioSelect build
     `div > label > input` that no template classes -- the very shape the
     element-selector note further up this file warns about, for the box.
     **The box was fixed on the element and the row was not**, so the row
     stayed forgettable and was duly forgotten at the first RadioSelect in
     the product (`S-138`, D125).

     Matching the label by what it contains removes the dependency on
     remembering. Harmless where a wrapper exists: the same declarations. */
  label:has(> input[type=checkbox]),label:has(> input[type=radio]){
    display:flex;align-items:center;min-block-size:var(--ctl-h-touch);gap:var(--s-3)
  }
  .skeleton--field{block-size:var(--ctl-h-touch)}
  /* DESIGN-SYSTEM.md 5.18 -- the title takes the whole line and the value,
     badge and chevron share the one under it. Wrapping only the value left
     the badge floating half-way down a tall meta line, centred against text
     rather than reading as the row's status. */
  .tile{flex-wrap:wrap;gap:var(--s-2) var(--s-3)}
  /* `flex-basis`, not `inline-size`: the base rule sets `flex:1`, whose
     basis of 0% wins over a width in flex layout -- so the width was
     accepted and had no effect. */
  .tile__body{flex:1 0 100%}
  /* AMENDMENT 2 again -- the brand collapses to a strip so the rep's thumb
     lands on the username field, not on a headline. The blurb is the first
     thing cut: it is orientation for a client on a laptop, and a rep signing
     in for the ninth time this week does not need orienting. */
  .auth{grid-template-columns:1fr;grid-template-rows:auto 1fr}
  .auth__brand{padding:var(--s-6) var(--s-5);align-content:start}
  .auth__logo{block-size:var(--logo-h);margin-block-end:var(--s-3)}
  /* Kept on the phone, unlike the sentence it replaced. That one was three
     lines of description a rep signing in for the ninth time this week did
     not need; this is the company's own line and it fits on one. */
  .auth__blurb{font-size:var(--fs-sm)}
  .auth__mark{inline-size:calc(var(--auth-mark-size) / 2)}
  .auth__panel{padding:var(--s-7) var(--s-5);place-items:start center}
  .rail__link[aria-current="page"]::before{
    inset-inline:var(--s-2);inset-block-start:auto;inset-block-end:calc(var(--s-2) * -1);
    inline-size:auto;block-size:var(--bw-3);
  }
  /* DESIGN-SYSTEM.md 5.14b -- the sections fold into one button at the end
     of the bar; notifications, the theme, the profile and sign out stay. The
     client, 2026-09-19: the icons ran off the bar and it looked wrong. */
  .rail__toggle{display:block}
  .rail__burger{
    display:grid;place-items:center;flex:none;order:1;
    inline-size:var(--rail-item);block-size:var(--rail-item);border-radius:var(--r-2);
    color:var(--c-onbrand-2);cursor:pointer;
  }
  .rail__toggle:focus-visible + .rail__burger{outline:var(--focus-ring);outline-offset:var(--bw)}
  .rail__toggle:checked + .rail__burger{background:var(--c-onbrand-wash);color:var(--c-onbrand)}
  .rail__sections{display:none}
  .rail__toggle:checked ~ .rail__sections{
    display:flex;flex-direction:column;gap:var(--s-1);
    position:absolute;inset-inline:0;inset-block-start:var(--rail-h-mobile);z-index:var(--z-overlay);
    padding:var(--s-3);box-shadow:var(--shadow-3);
    background:linear-gradient(180deg,var(--brand-800),var(--brand-900));
  }
  .rail__sections .rail__link{
    inline-size:100%;block-size:var(--ctl-h-touch);
    display:flex;align-items:center;gap:var(--s-4);padding-inline:var(--s-4);
  }
  /* The label the desktop keeps for screen readers is the row's text here. */
  .rail__sections .visually-hidden{
    position:static;width:auto;height:auto;overflow:visible;clip-path:none;
    font-size:var(--fs-sm);font-weight:var(--fw-b);
  }
  .rail__sections .rail__link[aria-current="page"]::before{
    inset-inline:0 auto;inset-block:var(--s-2);inline-size:var(--bw-3);block-size:auto;
  }
  /* BR-140 -- the manager's home is one column on a phone. Here, after the
     rules it overrides: in the chart's query further up it came first in the
     file and lost to them, and the phone kept four columns. */
  .dash{grid-template-columns:minmax(0,1fr)}
  .dash__wide{grid-column:auto}
  /* D166 -- one report card to a row on a phone. */
  .rgrid{grid-template-columns:minmax(0,1fr)}
  .export__form{inline-size:100%}
  /* D160 -- the profile stacks, the person first. */
  .profile,.profile__fields{grid-template-columns:minmax(0,1fr)}
  .band__stats--hero{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:var(--s-5) var(--s-4)}
  .band__val{font-size:var(--fs-xl)}
  /* AMENDMENT 2 -- the band collapses so the work starts above the fold. */
  .band{
    padding-inline:var(--s-4);
    padding-block-start:var(--band-pad-block-mobile);
    padding-block-end:var(--band-pad-block-end-mobile);
  }
  .band__stats{gap:var(--band-stats-gap-mobile)}
  main{padding-inline:var(--s-4);padding-block-end:var(--s-8);
    margin-block-start:calc(var(--card-overlap-mobile) * -1)}
  .btn{min-block-size:var(--ctl-h-touch)}
  .input{min-block-size:var(--ctl-h-touch)}
}

/* ============================================================================
   LIGHTBOX -- DESIGN-SYSTEM.md 5.21, BR-116, D128
   A photograph over the page it was opened from. Not a new tab: 01 section 3.1
   puts 11 of 15 users on phones, where a tab means losing the invoice you were
   reading. `target="_blank"` was never decided -- it was what a link to a file
   does, and D128 reversed it.
   ============================================================================ */
/* The trigger. A button rather than a link, because it goes nowhere -- D128
   replaced an anchor with `target="_blank"` and the pointer is the only thing
   that has to survive that change. */
.lightbox-open{cursor:pointer}

.lightbox{
  position:fixed;inset:0;z-index:var(--z-overlay);
  display:flex;align-items:center;justify-content:center;
  padding:var(--s-4);
  background:var(--scrim-overlay);
}
.lightbox[hidden]{display:none}

/* The page behind does not scroll while it is open, and is where it was on
   close -- `position:fixed` on the body would jump it to the top. */
body.has-lightbox{overflow:hidden}

.lightbox__close{
  position:absolute;inset-block-start:var(--s-4);inset-inline-end:var(--s-4);
  inline-size:var(--ctl-h-touch);block-size:var(--ctl-h-touch);
  display:flex;align-items:center;justify-content:center;
  font-size:var(--fs-xl);line-height:1;
  color:var(--on-scrim);background:transparent;
  border:var(--bw-2) solid transparent;border-radius:var(--r-pill);
  cursor:pointer;
}
.lightbox__close:hover{background:color-mix(in srgb, var(--on-scrim) 16%, transparent)}
.lightbox__close:focus-visible{border-color:var(--on-scrim);outline:none}

.lightbox__frame{margin:0;display:flex;flex-direction:column;align-items:center;gap:var(--s-3)}

/* `contain`, never `cover`: a 2-8 MB phone photograph is portrait and proves a
   paper invoice (BR-17b). Cropping it to fit is cropping the evidence. */
.lightbox__img{
  max-inline-size:92vw;max-block-size:88vh;object-fit:contain;
  border-radius:var(--r-2);box-shadow:var(--shadow-3);
}
.lightbox__cap{color:var(--on-scrim);font-size:var(--fs-sm);text-align:center}

/* Loading and error keep the frame. An empty box reads as "there is no
   photograph", which is a different and worse statement than "it did not
   load" -- R2 and R3 make the slow case ordinary, not exceptional. */
.lightbox.is-loading .lightbox__img{min-inline-size:var(--lightbox-min-w);min-block-size:var(--lightbox-min-h);background:color-mix(in srgb, var(--on-scrim) 12%, transparent)}
.lightbox.is-error .lightbox__img{display:none}
