/* The two faces, served from this machine.

   They came from Google's CDN until 2026-09-01, behind two preconnects and a
   render-blocking stylesheet on a third-party origin - on a cheap phone on 3G
   that is a DNS lookup, a TLS handshake and a round trip before the first
   letter is drawn, on every screen. The comment in base.html had claimed they
   were self-hosted since the day it was written; now they are.

   Latin only. Assamese and Hindi need their own scripts rather than the
   latin-ext subset, and will bring their own files when they arrive.
   00-common 5a                                                              */

@font-face {
  font-family: 'IBM Plex Sans'; font-style: normal; font-weight: 400;
  font-display: swap;
  src: url('/static/fonts/ibm-plex-sans-400-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6,
                 U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F,
                 U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF,
                 U+FFFD;
}
@font-face {
  font-family: 'IBM Plex Sans'; font-style: normal; font-weight: 500;
  font-display: swap;
  src: url('/static/fonts/ibm-plex-sans-500-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6,
                 U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F,
                 U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF,
                 U+FFFD;
}
@font-face {
  font-family: 'IBM Plex Sans'; font-style: normal; font-weight: 600;
  font-display: swap;
  src: url('/static/fonts/ibm-plex-sans-600-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6,
                 U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F,
                 U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF,
                 U+FFFD;
}
@font-face {
  font-family: 'Outfit'; font-style: normal; font-weight: 500;
  font-display: swap;
  src: url('/static/fonts/outfit-500-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6,
                 U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F,
                 U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF,
                 U+FFFD;
}

/* evedeck design tokens - 00-common.md 5a.
   Nothing here is hand-set in a screen. If a value is needed that is not on
   this page, it is a decision, not a nudge. */

:root {
  /* Grey is the container, white is what sits inside or behind it.
     Sidebar and cards are grey; the header, the content ground and the
     nested stat boxes are white. */
  --white:    #FFFFFF;
  /* **The ground is neutral, and it is the only thing on the screen that is.**
     `[CHANGED 2026-09-07]` It was `#F4F6FA`, and so were the two lines - all
     three blue-tinted greys sitting under a blue brand. Measured across all
     forty-two screens: **one hue carried 96% of the colour in the product**,
     and 81% of every pixel was white or that one grey.

     A warm ground was tried first and rejected by the founder the same hour:
     it put the neutral at 45 degrees, **thirteen from `--soon` at 32**, so the
     amber that means *this needs you* came out as brown on cream. A signal
     cannot share a hue with the ground it sits on. Saturation 0.11 instead of
     0.37 keeps the page quiet without the whole app being one temperature.

     Every contrast ratio was re-measured and holds; the worst is `--text-4` at
     4.70:1 against 4.74 before.   founder 2026-09-07 */
  --panel:    #F5F5F7;
  --line:     #E3E3E8;
  --line-2:   #ECECF0;
  --text:     #141821;
  --text-2:   #525A6B;
  /* **Both of these were unreadable and one of them was invisible.**
     `--text-3` came out at 3.17:1 on a panel and `--text-4` at 1.89:1 - the
     second is the colour the sidebar's group headings and every small key
     label were drawn in, on a screen this product promises will be used in
     daylight by a man in his fifties. 4.5:1 is the bar for text this size and
     both now clear it against the panel, which is the harder ground of the
     two.

     They are still the two quiet greys: the hierarchy is unchanged, the
     numbers are just true now.   AUDIT-2026-09-02 §16
       text-3   3.43 / 3.17  ->  5.62 / 5.19
       text-4   2.04 / 1.89  ->  5.13 / 4.74  */
  --text-3:   #5D6676;
  --text-4:   #656E80;
  --brand:    #1B5FBF;
  --brand-bg: #E6EEFB;

  /* Three steps, so hover and active read as different states. They follow the
     ground above: a blue-grey hover on a neutral sidebar reads as a smudge. */
  --nav-hover:  #EDEDF1;
  --nav-active: #E4E4EA;

  /* Status colour never overlaps brand colour. Blue is identity;
     red, amber and green are signal. */
  --late:    #B83729;  --late-bg: #FDEFEE;
  /* 4.39:1 on a panel at 10px, which is the badge that says a member is
     overdue - the one colour on the screen carrying an instruction.
     AUDIT-2026-09-02 §16 */
  --soon:    #8A5210;  --soon-bg: #FCF3E6;
  --ok:      #0D7345;  --ok-bg:   #E8F4EE;

  /* What a thing *is*, as against how it is going. Five families, taken from
     the way he already talks about his business. They sit on the icon chip and
     nowhere else: sixteen chips in the same blue made the strongest element on
     the screen into wallpaper.   founder 2026-09-01
     Signal still beats family - amber for a job, green for settled - which is
     what stops a screen becoming a colour chart. */
  --money:     #0F6E6E;  --money-bg:     #E3F0F0;
  --people:    #3D4EAB;  --people-bg:    #E9EBF9;
  --customers: #6B46A8;  --customers-bg: #F0EAF9;
  --front:     #1B5FBF;  --front-bg:     #E6EEFB;
  /* What has to be kept running, and who he buys it from. A sixth family
     because a treadmill and a trade licence were sitting under money, where he
     would never look for them.   AUDIT-2026-09-02 §8 */
  --upkeep:    #8A5A1F;  --upkeep-bg:    #F6EEE2;
  /* A seventh: his own day. The calendar and the to-do list were under money,
     on the reasoning that money is what he opens the app for - which is a fact
     about him and not about the module. Founder, 2026-09-08: *"should calendar
     and to do come under money?"*   founder 2026-09-08

     **Picked by measuring, not by naming.** The six sat at 33, 153, 180, 215,
     220, 231 and 263 degrees, leaving two real gaps: one around 93 and one
     around 314. The 93 is yellow-green and would read as `--ok`, which means
     *settled* on every screen in the product - so a family colour there would
     be a signal saying nothing. 314 is 51 degrees off `--customers` one way
     and 52 off `--late` the other, and it renders where it says it does. */
  --day:       #8D3479;  --day-bg:       #F8E8F4;
  --admin:     #4E5666;  --admin-bg:     #EDF0F5;

  /* **And one of its own for every module.** `[ADDED 2026-09-07]` Six family
     colours over sixteen modules put four blue cards, three indigo and three
     purple beside each other on one dashboard - the founder: *"you have
     assigned very limited color set to the cards. so many blues many rust."*

     **The hue is set by where the card sits, not by its family.** They were
     grouped by family at first and the founder saw the result: *"the pinkish
     are all together, bluish all together"* - because the cards are ordered
     by family, so were the colours. Sixteen hues evenly round the wheel, then
     walked in steps of seven - seven and sixteen share no factor, so the walk
     lands on every one of them and each card is a third of the wheel from the
     one before it. Closest two cards side by side: 140 degrees. Closest two
     anywhere: 14. The family is still the grouping - it orders the cards and
     names the sidebar - it is simply not the colour.

     Placed clear of the three signal hues wherever the wheel allowed - `--soon`
     32, `--late` 6, `--ok` 153. `--m-expenses` at 160 is the one that could
     not be: the money family is teal by definition and `--ok` lives in it. It
     meets that green only on a settled expenses card, where the figure is a
     nought and there is nothing to read wrongly.
  /* ink, then the tint the card is drawn on. */
  --m-website:    #215E7D;  --m-website-chip:    #CFEFFF;
  --m-gallery:    #7D217D;  --m-gallery-chip:    #FFCFFF;
  --m-enquiries:  #7D7C21;  --m-enquiries-chip:  #FFFFCF;
  --m-listings:   #217D40;  --m-listings-chip:   #CFFFDF;
  --m-calendar:   #21217D;  --m-calendar-chip:   #CFCFFF;
  --m-todo:       #7D2140;  --m-todo-chip:       #FFCFDF;
  --m-sales:      #517D21;  --m-sales-chip:      #E8FFCF;
  --m-expenses:   #217D7D;  --m-expenses-chip:   #CFFFFF;
  --m-employees:  #5E217D;  --m-employees-chip:  #EFCFFF;
  --m-attendance: #7D6421;  --m-attendance-chip: #FFF2CF;
  --m-salary:     #217D21;  --m-salary-chip:     #CFFFCF;
  --m-clients:    #21407D;  --m-clients-chip:    #CFDFFF;
  --m-contacts:   #7D215E;  --m-contacts-chip:   #FFCFEF;
  --m-membership: #687D21;  --m-membership-chip: #F4FFCF;
  --m-vendors:    #217D65;  --m-vendors-chip:    #CFFFF2;
  --m-assets:     #40217D;  --m-assets-chip:     #DFCFFF;
  --m-renewals:   #7D3D21;  --m-renewals-chip:   #FFDDCF;
  --m-users:      #485F3F;  --m-users-chip:      #E3EFDF;

  /* Tints for a breakdown that carries no signal at all. Nobody's gender is a
     problem to be flagged, so it is never drawn in red, amber or green.
     founder 2026-08-27 */
  --tone-1:  #1B5FBF;
  --tone-2:  #7FA6DE;
  --tone-3:  #B9A0DC;

  /* WhatsApp's own green. Used on the icon of a control that opens it, and
     nowhere else - it is somebody else's mark, not a status colour. */
  --wa: #128C7E;

  --r:   4px;   /* containers - cards, panels, menus */
  --r-s: 3px;   /* controls - chips, buttons, stat boxes, nav rows */

  /* Sign-in only. The brand blue is a link colour; a whole panel of it is
     glare on a phone held in a lit shop. Same hue, two steps down. */
  --brand-deep: #123A72;

  --font: "IBM Plex Sans", -apple-system, "Segoe UI", Roboto, sans-serif;
  --font-wordmark: "Outfit", var(--font);
}

* { box-sizing: border-box; }

/* 17px, 1.7 stroke, round caps and joins. Inline symbols, never a library:
   on a cheap Android on 3G an icon request is a visible delay.  00-common 5a */
.icon {
  width: 17px; height: 17px; flex: 0 0 auto;
  stroke: currentColor; fill: none;
  stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round;
  /* One rest colour for every icon, set here and nowhere else. A shade darker
     than the group labels: at --text-4 an icon reads as decoration, and the
     point of one is to be found at a glance. */
  color: var(--text-3);
}

body {
  margin: 0;
  font-family: var(--font);
  font-size: 14px;
  color: var(--text);
  background: var(--white);
  -webkit-font-smoothing: antialiased;
}

/* Every column of digits lines up or the list stops being scannable. */
.num, td.num, .big-number { font-variant-numeric: tabular-nums; }

a { color: var(--brand); text-decoration: none; }
a:hover { text-decoration: underline; }

.wordmark {
  font-family: var(--font-wordmark);
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: .2em;
  margin-right: -.2em;
}

/* ------------------------------------------------------------ shell */

.shell { display: flex; min-height: 100vh; }

.sidebar {
  width: 208px;
  flex: 0 0 208px;
  background: var(--panel);
  border-right: 1px solid var(--line);
  padding: 14px 10px;
  display: flex; flex-direction: column;
}
.sidebar nav { flex: 1; }

.side-brand {
  display: flex; align-items: center; gap: 8px;
  padding: 2px 6px 16px;
  font-size: 16px; color: var(--text);
}

.nav-group {
  font-size: 9.5px; font-weight: 600; text-transform: uppercase;
  letter-spacing: .12em; color: var(--text-4);
  padding: 14px 10px 6px;
}

/* One row per module. The icon carries the recognition, the word carries the
   meaning, and the row stays a single flat line either way.   00-common 6 */
.nav-link {
  display: flex; align-items: center; gap: 11px; position: relative;
  padding: 8px 10px; border-radius: var(--r-s);
  font-size: 13.5px; font-weight: 500; line-height: 1.2; color: var(--text-2);
}
.nav-link em { flex: 1; min-width: 0; font-style: normal;
               overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* **A thumb, not a mouse pointer.** These rows were 33px and the hamburger 29,
   against the 44 every phone guideline asks for, and this is a product whose
   buyer is holding the phone in one hand at a counter while talking to a
   customer. On a desktop 33px keeps sixteen modules in one sidebar without
   scrolling, so the height is raised only where the thumb is.
   AUDIT-2026-09-02 §16 */
@media (max-width: 620px) {
  .nav-link, .hamburger, .rail-toggle, .cake, .say,
  .card.unstarted .mod { min-height: 44px; }
  .hamburger, .rail-toggle, .cake, .say { min-width: 44px;
                                          display: inline-flex;
                                          align-items: center;
                                          justify-content: center; }
}


/* The number beside the word. Amber when it is a job rather than news — a
   figure he has to act on looks different from one he only reads. 00-common 5 */
.nav-count { font-size: 11px; font-weight: 600; color: var(--text-3);
             font-variant-numeric: tabular-nums; }
.nav-count.urgent { color: var(--soon); }

/* A module he does not have. Named so he knows it exists, marked so he is not
   surprised when it asks him to talk to us.   founder 2026-08-26 */
.nav-tag { font-size: 9px; font-weight: 600; text-transform: uppercase;
           letter-spacing: .06em; color: var(--text-4); }
.nav-link.active .nav-count { color: var(--text-2); }

.nav-link:hover        { background: var(--nav-hover); color: var(--text); text-decoration: none; }
.nav-link:hover .icon  { color: var(--text-2); }

/* Three steps of grey, then the icon turns brand colour and a bar appears at
   the edge. Hover and active have to read as different states. */
.nav-link.active       { background: var(--nav-active); color: var(--text); font-weight: 600; }
.nav-link.active .icon { color: var(--brand); }
.nav-link.active::before {
  content: ""; position: absolute; left: -10px; top: 7px; bottom: 7px;
  width: 3px; border-radius: 0 2px 2px 0; background: var(--brand);
}
.nav-link:focus-visible { outline: 2px solid var(--brand); outline-offset: -2px; }

.main { flex: 1; min-width: 0; display: flex; flex-direction: column; }

.header {
  height: 56px; display: flex; align-items: center; gap: 14px;
  padding: 0 18px; background: var(--white); border-bottom: 1px solid var(--line);
}

/* The screen's own icon, the same one as its sidebar row. Small: it names the
   screen, it does not announce it.   00-common 5a */
.page-chip {
  flex: 0 0 auto; width: 26px; height: 26px; border-radius: var(--r-s);
  background: var(--brand-bg); display: flex; align-items: center;
  justify-content: center;
}
.page-chip .icon { width: 15px; height: 15px; color: var(--brand); }

/* The name of the screen he is on. Without it every page looks the same at a
   glance and the sidebar is the only clue. */
/* Said aloud, never drawn. The app needed its own: the published sites got one
   in batch 2 and this stylesheet had none, so a screen with no visible heading
   had no heading at all.   AUDIT-2026-09-02 §16 */
.sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
           overflow: hidden; clip-path: inset(50%); white-space: nowrap; border: 0; }

.page-title { flex: 1; min-width: 0; margin: 0; font-size: 15px; font-weight: 600;
              white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

/* Which business he is looking at. Always visible, because everything on the
   screen is scoped to it.   00-common 1a */
.shop-switch {
  display: flex; align-items: center; gap: 7px; margin: 0; flex: 0 0 auto;
  padding: 5px 10px; border: 1px solid var(--line); border-radius: var(--r-s);
  background: var(--panel); font-size: 13px; font-weight: 500; color: var(--text);
  max-width: 40vw; white-space: nowrap; overflow: hidden;
}
.shop-switch .icon { color: var(--brand); }
.shop-switch select {
  font: inherit; font-size: 13px; font-weight: 500; color: var(--text);
  border: 0; padding: 0; background: none; max-width: 30vw;
}

/* account -----------------------------------------------------------------

   A details element: the menu opens with no script at all, and the browser
   already closes it on Escape and on a click outside.                       */

.account { position: relative; flex: 0 0 auto; }
.account summary {
  display: flex; align-items: center; gap: 8px; padding: 5px 8px;
  border-radius: var(--r-s); cursor: pointer; list-style: none;
}
.account summary::marker { content: ""; }
.account summary:hover { background: var(--nav-hover); }
.account[open] summary { background: var(--nav-active); }

/* A person icon, not initials. Initials of a name nobody typed carefully read
   as a mistake, and two letters carry no information he did not already have -
   his own name is written beside it. */
.avatar {
  flex: 0 0 auto; width: 28px; height: 28px; border-radius: var(--r-s);
  background: var(--brand-bg); color: var(--brand);
  display: flex; align-items: center; justify-content: center;
}
.avatar .icon { width: 16px; height: 16px; color: var(--brand); }
.user-name { display: flex; flex-direction: column; line-height: 1.2; }
.user-name b { font-size: 12.5px; font-weight: 600; }
.user-name small { font-size: 10.5px; color: var(--text-3); }
.account .chev { width: 14px; height: 14px; color: var(--text-4); }

.account .menu {
  position: absolute; right: 0; top: calc(100% + 6px); z-index: 60;
  min-width: 210px; padding: 5px; background: var(--white);
  border: 1px solid var(--line); border-radius: var(--r);
  box-shadow: 0 8px 28px rgba(20, 24, 33, .12);
}
.menu-head { display: flex; align-items: center; gap: 9px; padding: 8px;
             border-bottom: 1px solid var(--line-2); margin-bottom: 4px; }
.menu-head b { display: block; font-size: 12.5px; }
.menu-head small { font-size: 11px; color: var(--text-3); }

/* The install button is a `button` and every other row is an `a`, because one
   of them asks the browser something and the rest go somewhere. They have to
   read as one list.   founder 2026-09-05 */
.account .menu a,
.account .menu > button {
  display: flex; align-items: center; gap: 9px; padding: 8px 9px; width: 100%;
  border: 0; background: none; margin: 0; text-align: left; cursor: pointer;
  border-radius: var(--r-s); font-size: 13px; font-family: inherit;
  color: var(--text-2);
}
.account .menu a:hover,
.account .menu > button:hover { background: var(--nav-hover); color: var(--text);
                                text-decoration: none; }
.account .menu a.danger { color: var(--late); }
.account .menu a.danger .icon { color: var(--late); }
.account .menu a.danger:hover { background: var(--late-bg); }

/* **White, all the way through** - founder, 2026-09-08: *"i am not liking the
   dashboard so many colors. can we just try white cards with light grey bg?"*
   then *"make the content area also white."*

   A grey ground was tried in between and it was the wrong half of the idea:
   the tiles are what carried too much colour, not the page under them. So the
   page, the cards and the panels are all white, and what separates them is a
   hairline. The colour on the screen is the icon chip and the signals -
   nothing else.   See the note over the module palette. */
.content { padding: 20px; background: var(--white); flex: 1; }


/* ------------------------------------------------------------ cards */

.card-grid {
  display: grid; gap: 10px;
  grid-template-columns: repeat(auto-fill, minmax(196px, 1fr));
}

.card {
  /* A column, so the figure sits on the same line in every card whatever the
     label above it wrapped to, and the note hangs off the figure rather than
     floating at the bottom of the tile.   founder 2026-09-01 */
  display: flex; flex-direction: column; min-height: 118px;
  background: var(--white); border: 1px solid var(--line);
  border-radius: var(--r); padding: 13px 14px 15px; color: inherit;
}
.card:hover { border-color: var(--text-4); text-decoration: none; }

/* The tinted icon chip. The strongest single element on the screen, and what
   makes it look finished rather than assembled.   00-common 5a */
.card-head { display: flex; align-items: center; gap: 9px; }
.chip {
  flex: 0 0 auto; width: 30px; height: 30px; border-radius: var(--r-s);
  display: flex; align-items: center; justify-content: center;
  background: var(--brand-bg); color: var(--brand);
}
/* The chip sets the colour and the icon has to take it. `.icon` names a colour
   on the svg itself, and a rule on the element beats one inherited from its
   parent - so every chip on the home screen and on the website tiles was
   drawing its icon in the default grey inside a blue box. It also carries the
   amber on a card that needs action, which was being lost the same way.
   founder 2026-08-26 */
.chip .icon { color: inherit; }
.card .label { flex: 1; font-size: 12.5px; font-weight: 500; color: var(--text-2); }
.card .arrow { width: 14px; height: 14px; color: var(--text-4); }
.card:hover .arrow { color: var(--text-2); }

/* The figure. It is pushed to the foot of the card by the margin above it, so
   a two-line label does not drag the number up with it. */
.big-number { font-size: 26px; font-weight: 600; letter-spacing: -.028em;
              margin-top: auto; padding-top: 11px; line-height: 1.15;
              /* A figure longer than its card breaks the line rather than the
                 page. Nothing here should ever be long enough to need it - a
                 phone number was, and that is fixed at the source - but a card
                 is not worth a horizontal scrollbar on the whole screen.
                 AUDIT-2026-09-02 §3·B-15 */
              min-width: 0; overflow-wrap: anywhere; }

/* Renewals name the thing instead of counting it - *Trade licence* - and a name
   is a line of text, not a number. Two lines, then it is cut: a card is a
   glance.   05-renewals 5 */
.card-phrase {
  margin-top: auto; padding-top: 11px;
  font-size: 15px; font-weight: 600; letter-spacing: -.012em; line-height: 1.3;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden;
}

/* The words under the figure. Small, quiet, and never the headline: "Nothing
   outstanding" set at 26px was the loudest thing on a screen full of real
   numbers.   founder 2026-09-01 */
.card-note {
  margin-top: 4px; font-size: 11.5px; font-weight: 500; color: var(--text-3);
  line-height: 1.35;
}

/* A card that needs action looks different from one that informs. "8 due this
   week" is a job; "1,240 visits" is news.   00-common 5

   **The figure says it, not the chip.**  `[CHANGED 2026-09-07]` The chip used
   to be repainted amber too, and on a real dashboard five of ten cards have
   something to do - so half the screen went amber, the six family colours were
   switched off exactly when a module had something to say, and amber stopped
   meaning *this one*. Measured over all forty-two screens: one hue carried 96%
   of the colour in the product and the family teal, indigo, purple, brown and
   slate were drawn on 0.000% of it.

   The chip says **what the module is**; the number and the words under it say
   **how it is going**. The number is the largest thing on the card, so an
   urgent card is still the loudest one on the screen.   founder 2026-09-07 */
.card.needs-action .big-number { color: var(--soon); }
.card.needs-action .card-phrase { color: var(--soon); }
.card.needs-action .card-note { color: var(--soon); }

/* Settled: the module has an answer and the answer is nought. The colour is on
   the words and the nought itself is grey - a green 0 reads as a figure to be
   pleased about rather than as nothing to do. The chip keeps its family for the
   same reason the amber one does. */
.card.settled .big-number { color: var(--text-3); font-weight: 500; }
.card.settled .card-note { color: var(--ok); }

/* Nothing to say yet, which is not the same as nothing to do. A dash, and the
   line under it says why.   00-common 5 */
.card.empty .big-number { color: var(--text-4); font-weight: 500; }

/* The last tile: a way to the rest, not a number. Dashed and unfilled so it
   reads as an opening in the grid rather than another card competing with
   the ones that carry real figures.   00-common 4, 5 */
.card.add {
  background: none; border-style: dashed; border-color: var(--line);
  display: flex; flex-direction: column; justify-content: space-between;
}
.card.add:hover { background: var(--panel); border-color: var(--text-4); }
.card.add .chip { background: var(--white); border: 1px solid var(--line-2); color: var(--text-3); }
.card.add:hover .chip { background: var(--brand-bg); color: var(--brand); border-color: transparent; }
.add-sub { margin-top: auto; padding-top: 11px; font-size: 12px; color: var(--text-4); }

/* The website screen's tiles. The same card as the home screen, because two
   grids that look different are two things to learn. A count shows as a
   number; everything else shows the thing itself, since "Forest" says more
   than "1 skin" ever could.   founder 2026-08-26                           */
.card .tile-text {
  margin-top: 11px; font-size: 15px; font-weight: 600; letter-spacing: -.012em;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.card .tile-sub { margin-top: 5px; font-size: 12px; color: var(--text-4); line-height: 1.35;
                  white-space: normal; }

/* --------------------------------------------- a number, and what it is made of

   A figure that has to answer "made up of what" carries a legend or a row of
   bars, drawn in CSS and nothing else - no chart library, no canvas, no
   script, and it reads the same on a cheap Android as on a laptop.
   00-common 5a                                                              */

.legend { display: flex; flex-wrap: wrap; gap: 4px 14px; margin-top: 12px; }
.legend div { display: flex; align-items: center; gap: 6px; font-size: 12px; color: var(--text-2); }
.legend i { width: 7px; height: 7px; border-radius: 2px; flex: 0 0 auto; }
.legend b { font-weight: 600; color: var(--text); font-variant-numeric: tabular-nums; }
/* Nothing recorded is drawn back, not drawn out. */
.legend .none { color: var(--text-4); }
.legend .none b { color: var(--text-3); }

/* Colour by what it means, never by where it sits in a list. */
.t-female  { background: var(--tone-1); }
.t-male    { background: var(--tone-2); }
.t-other   { background: var(--tone-3); }
.t-unknown { background: var(--line); }
/* An amount is recorded, or it is not. Neither is good or bad news, so it is
   brand colour and ground rather than green and red. */
.t-set     { background: var(--brand); }
.t-paid    { background: var(--ok); }
.t-part    { background: var(--soon); }
.t-unpaid  { background: var(--text-4); }
.t-present { background: var(--ok); }
.t-half    { background: var(--soon); }
.t-absent  { background: var(--late); }
.t-leave   { background: var(--brand); }

/* --------------------------------------------------------- a module dashboard

   A column per subject, and the figure and the detail behind it are one box.
   The version before this put the figures in a row and the detail in a second
   row underneath, and nothing said which panel belonged to which number - the
   founder read "Still to be paid" sitting under "Total employees" and could
   not tell it was about salary.

   Designed first in docs/mockups/employee-dashboard.html, direction C, chosen
   by the founder 2026-08-27. Design before code for any screen he has not
   already seen.   00-common 5b                                              */

/* As many columns as the module has subjects, not three because three is what
   the first one had. Founder, 2026-08-29: *"it is not mandatory to have 3
   cards in every dashboard. give as required. all dashboard need not be same.
   it should be easy and helpful."*   00-common 5b */
.dash-grid { display: grid; gap: 12px; grid-template-columns: repeat(3, minmax(0, 1fr)); }
.dash-grid.two { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.dash-grid.one { grid-template-columns: minmax(0, 720px); }

.dash-col {
  position: relative; display: flex; flex-direction: column;
  background: var(--white); border: 1px solid var(--line); border-radius: var(--r);
}
/* The head is tinted and the body is white, so the figure and its detail read
   as one box with two parts rather than as two boxes. */
.dash-col-top {
  padding: 15px 17px 16px; background: var(--panel);
  border-bottom: 1px solid var(--line); border-radius: var(--r) var(--r) 0 0;
}
/* Room kept on the right for the button, which is lifted out of the flow so
   that on a phone it can drop to the foot of the card without the markup being
   written twice.   00-common 5a */
.dash-col-head { display: flex; align-items: center; gap: 10px; margin-bottom: 13px;
                 padding-right: 104px; }
.dash-col-head .chip { width: 32px; height: 32px; }
/* The heading names its subject - "Salary - August", never "Still to be paid".
   founder 2026-08-27 */
.dash-col-head h2 { flex: 1; min-width: 0; margin: 0; font-size: 13.5px; font-weight: 600;
                    color: var(--text-2); letter-spacing: -.006em;
                    overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* The way in sits beside the heading, not at the foot of the column. It is the
   first thing he looks for and the columns are not the same height, so a link
   at the bottom of each one lands somewhere different every time.

   A small quiet button rather than a link: the head is tinted, and blue text on
   a grey band reads as part of the heading instead of as something to press.
   The same white-on-line control as button.quiet, one size down.
   founder 2026-08-27 */
.dash-col > .more {
  position: absolute; top: 15px; right: 17px;
  padding: 5px 10px; font-size: 12px; font-weight: 500;
  color: var(--text-2); background: var(--white);
  border: 1px solid var(--line); border-radius: var(--r-s);
  white-space: nowrap; text-decoration: none;
}
.dash-col > .more:hover {
  color: var(--text); border-color: var(--text-4); text-decoration: none;
}

.dash-figure { font-size: 33px; font-weight: 600; letter-spacing: -.03em; line-height: 1.02;
               font-variant-numeric: tabular-nums; }
.dash-figure small { font-size: 19px; font-weight: 500; color: var(--text-3);
                     letter-spacing: -.01em; }
.dash-key  { margin-top: 6px; font-size: 12.5px; font-weight: 500; color: var(--text-2); }
.dash-note { margin-top: 3px; font-size: 12px; color: var(--text-4); }

/* A figure that is a job looks different from one that informs. Salary is the
   only thing on this screen that is a job, so it is the only amber.
   00-common 5 */
.dash-col.needs-action .dash-figure,
.dash-col.needs-action .dash-key { color: var(--soon); }
.dash-col.needs-action .chip { background: var(--soon-bg); color: var(--soon); }
.dash-col.settled .dash-key { color: var(--ok); }
.dash-col.settled .chip { background: var(--ok-bg); color: var(--ok); }
/* Nothing to report is a real answer and reads as one. Never an em dash: that
   looks like the screen failed.   founder 2026-08-27 */
.dash-col.quiet .dash-figure { color: var(--text-4); }

.dash-col-body { flex: 1; padding: 10px 17px 14px; }

/* Label, bar, count. A breakdown reads as a measurement rather than as numbers
   he has to compare in his head. */
.bar-row {
  display: grid; grid-template-columns: 98px 1fr 32px; gap: 12px; align-items: center;
  padding: 7px 0; font-size: 13.5px;
}
.bar-track { height: 8px; background: var(--line-2); border-radius: 4px; overflow: hidden; }
.bar-fill { display: block; height: 100%; border-radius: 4px; }
.bar-row .n { text-align: right; font-weight: 600; font-variant-numeric: tabular-nums;
                min-width: 0; overflow: hidden; text-overflow: ellipsis; }
.bar-row > span:first-child { min-width: 0; overflow: hidden; text-overflow: ellipsis;
                              white-space: nowrap; }
/* A row whose figure is money rather than a count. ₹17,40,000 does not fit in
   32px, and a track that cannot fit its content grows the grid instead.

   Every column may shrink to nothing and the figure is sized to itself. Fixed
   at 84/1fr/88 the row could not fit a dashboard card on a 360px phone, so it
   grew the card instead and the ₹ column was drawn off the side of the screen.
   AUDIT-2026-09-02 §3·B-14 */
/* The label on a money row is a month - "Aug" - not a plan name, so it does
   not need the 84px the counting rows use. The figure gets the difference,
   which is what stops ₹11,400 ending in an ellipsis. */
.bar-row.cash { grid-template-columns: minmax(0, 52px) minmax(0, 1fr) auto; }
/* On a phone the dashboard is still two cards to a row, so a money row has
   about 165px to hold a month, a bar and a figure - and ₹11,400 lost its last
   digits to an ellipsis. The bar is the part worth losing: forty pixels of it
   in a card that narrow says nothing the figure beside it does not.
   AUDIT-2026-09-02 §3·B-14 */
@media (max-width: 620px) {
  .bar-row.cash { grid-template-columns: minmax(0, 1fr) auto; }
  .bar-row.cash .bar-track { display: none; }
}
.bar-row.zero, .bar-row.zero .n { color: var(--text-4); }
/* The track is already the palest line on the page, so a fill drawn in it
   would be invisible. Inside a bar it is grey, not ground. */
.bar-fill.t-unknown { background: var(--text-4); }
/* A trade carries no meaning worth colouring, so every role bar is the same
   one. Colour is for what the colour says.   00-common 5a */
.bar-fill.t-role { background: var(--tone-2); }

/* A small label over a group of bars, where one column answers two questions.
   Each block carries its own rule, so the two never run together into one long
   list of numbers.   founder 2026-08-27 */
.block-head {
  margin: 13px 0 2px; padding-top: 13px; border-top: 1px solid var(--line-2);
  font-size: 12px; font-weight: 500; color: var(--text-2);
}
/* The first block sits straight under the figure, which already has the head's
   own border above it. */
.dash-col-body > .block-head:first-child { margin-top: 2px; padding-top: 0; border-top: 0; }
.bar-row .none { color: var(--text-4); }

.dash-empty { padding: 12px 0 18px; font-size: 13.5px; color: var(--text-3); }

/* Six months of what left his hand. Six divs and a height each - no chart
   library, no canvas, no script, and it draws before the fonts land.
   00-common 5a, 5b                                                          */
.spark-head { display: flex; align-items: baseline; justify-content: space-between;
              gap: 10px; font-size: 12px; font-weight: 500; color: var(--text-2); }
.spark-head .up-to { font-weight: 400; color: var(--text-4);
                     font-variant-numeric: tabular-nums; }
.spark { display: flex; align-items: flex-end; gap: 7px; height: 56px; margin-top: 10px; }
/* A fortnight is fourteen bars in the width six had, so they close up. */
.spark.days { gap: 3px; height: 46px; }
.spark-col { flex: 1; height: 100%; display: flex; align-items: flex-end; }
.spark-bar { display: block; width: 100%; min-height: 3px;
             border-radius: 2px 2px 0 0; background: var(--tone-2); }
/* The month in progress is hollow. Filled like the five behind it, a month
   three days old reads as a collapse in wages.   founder 2026-08-27 */
.spark-bar.now { background: var(--brand-bg); box-shadow: inset 0 0 0 1px var(--tone-2); }
.spark-x { display: flex; gap: 7px; margin-top: 6px; }
.spark-x span { flex: 1; text-align: center; font-size: 11px; color: var(--text-4); }
.spark-x span.now { color: var(--text-2); font-weight: 600; }

/* A fortnight, redrawn 2026-08-29. Founder, on the old one: "what is this?"
   Fourteen bars, no axis, and the days nobody had marked drawn as 3px stubs
   that read as "almost nobody came" - the opposite of "no record".   07-employees 16 */

/* Fourteen letters in the width six month names had, so they close up and the
   weekday is one character. Sunday is greyed: a shop shut on Sunday should be
   readable as a shut Sunday and not as a bad day.                          */
.spark-x.days { gap: 3px; }
.spark-x.days span { font-size: 10px; letter-spacing: 0; }
.spark-x.days span.rest { color: var(--text-5, var(--text-4)); opacity: .55; }

/* No record is a full-height empty box. Anything shorter is a claim about how
   many people came in, and the whole point is that nobody said.            */
.spark-bar.unmarked {
  height: 100%; background: transparent;
  box-shadow: inset 0 0 0 1px var(--line-2);
  border-radius: 2px;
}

/* The two ends of the window, and the one thing the drawing cannot say about
   itself. A swatch, not a sentence under the chart.                        */
.spark-ends { display: flex; align-items: center; justify-content: space-between;
              gap: 8px; margin-top: 7px; font-size: 11px; color: var(--text-4); }
.spark-ends .legend { display: inline-flex; align-items: center; gap: 5px; }
.spark-ends .swatch { width: 9px; height: 11px; border-radius: 2px;
                      box-shadow: inset 0 0 0 1px var(--line-2); }

/* Three columns is a wide-screen shape. Narrower, they stack in the order they
   were written - the team, today, then the money - and that order is the same
   at every width, so there is nothing to re-learn.   founder 2026-08-27 */
@media (max-width: 1000px) { .dash-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }

@media (max-width: 680px) {
  .dash-grid { grid-template-columns: minmax(0, 1fr); }
  /* **Including the pair.** `.dash-grid.two` is a selector deeper than the
     line above and kept two columns on a 360px screen - a hundred and sixty
     pixels each, which turned "Jitu Baruah / Member since" into "Ji... / Mem"
     and "stopped coming - ask them back" into three lines.
     founder 2026-09-07 */
  .dash-grid.two { grid-template-columns: minmax(0, 1fr); }

  /* A 29px button is not a thumb target. Apple asks for 44px and Android for
     48dp, so on a phone the button leaves the corner, drops to the foot of the
     card it belongs to, and takes the full width.   founder 2026-08-27 */
  .dash-col-head { padding-right: 0; }
  .dash-col > .more {
    position: static; display: block; width: 100%; min-height: 46px;
    padding: 13px 10px; font-size: 13.5px; text-align: center;
    color: var(--brand); background: var(--white);
    border: 0; border-top: 1px solid var(--line-2);
    border-radius: 0 0 var(--r) var(--r);
  }
  .dash-col > .more:hover { background: var(--panel); color: var(--brand); }
  .dash-col-body { padding-bottom: 4px; }
}

/* A heading with its own controls beside it - the month arrows belong to the
   salary list, not to the whole screen, now that the screen opens with figures
   that are not about one month.   founder 2026-08-27 */
.head-row {
  display: flex; align-items: center; justify-content: space-between;
  gap: 12px; flex-wrap: wrap; margin: 24px 0 4px;
}
.head-row .section-head { margin: 0; }

/* ------------------------------------------------------------ forms */

.page-narrow { max-width: 340px; margin: 60px auto; padding: 0 20px; }

label { display: block; font-size: 12.5px; font-weight: 500; color: var(--text-2); margin: 14px 0 5px; }
/* Beside the label, not under the box: the number is there to be pressed and
   the box is there to be corrected.   founder 2026-08-27 */
a.ring { margin-left: 8px; font-size: 11.5px; font-weight: 600; letter-spacing: .04em;
         text-transform: uppercase; }

/* Every kind of box he types in, named by what it is not rather than by a
   list: `type=tel` was added for the phone field and fell straight out of a
   list of three, so the box lost its width, its padding and its border and
   nobody could see why.   founder 2026-08-27 */
input:not([type=checkbox]):not([type=radio]):not([type=file]):not([type=submit]):not([type=button]):not([type=hidden]),
select, textarea {
  width: 100%; padding: 8px 10px; font: inherit; font-size: 14px;
  border: 1px solid var(--line); border-radius: var(--r-s); background: var(--white);
}
input:focus, select:focus, textarea:focus {
  outline: 2px solid var(--brand-bg); border-color: var(--brand);
}

/* Wrong as he types, not only when he presses the button. `:placeholder-shown`
   holds it back until there is something in the box, so an empty field is not
   red before he has touched it — which is why every field with a pattern also
   carries a placeholder. Pure CSS: the pattern does the deciding and no script
   is involved.   00-common 5a, founder 2026-08-27 */
input[pattern]:not(:placeholder-shown):invalid,
input[pattern]:not(:placeholder-shown):invalid:focus {
  border-color: var(--late); outline-color: var(--late-bg); background: var(--late-bg);
}

/* Flex, not inline-block. An svg on a text baseline sits low, so every button
   with an icon had the drawing and the word on different lines — and only the
   two places that set `inline-flex` for themselves looked right.
   founder 2026-08-27 */
button, .button {
  display: inline-flex; align-items: center; justify-content: center; gap: 7px;
  margin-top: 18px; padding: 9px 16px;
  font: inherit; font-size: 13.5px; font-weight: 500;
  color: #fff; background: var(--brand);
  border: 0; border-radius: var(--r-s); cursor: pointer;
}
/* The quiet control, wherever it is and whatever tag it is. It had a rule for
   `<button>` and two scoped ones for `.confirm-actions` and `.list-tools`, so
   every `<a class="button quiet">` outside those two — the month arrows, the
   panel headers, the head actions — rendered as a filled blue primary.
   founder 2026-08-27 */
button.quiet, .button.quiet, a[role=button].quiet {
  background: var(--white); color: var(--text-2); border: 1px solid var(--line);
}
button.quiet:hover, .button.quiet:hover, a[role=button].quiet:hover {
  background: var(--panel); color: var(--text); text-decoration: none;
}

/* An icon inside a button takes the button's own colour. `.icon` names a
   colour on the svg itself and a rule on the element beats one inherited from
   its parent, so every icon in a filled blue button was drawing in the default
   grey — the same fault as the chips, in a second place.
   00-common 5a, founder 2026-08-27 */
button .icon, .button .icon, a[role=button] .icon { color: inherit; }

/* A button with nowhere to go is turned off, not taken away. Removing it moves
   every button after it, so no two rows line up down the screen.
   founder 2026-08-26 */
/* pointer-events rather than a hover rule per place: every screen styles its
   own buttons on hover, and a turned-off one must not light up in any of
   them. */
button[disabled] { opacity: .3; cursor: default; pointer-events: none; }

.error { margin-top: 14px; padding: 9px 11px; border-radius: var(--r-s);
         background: var(--late-bg); color: var(--late); font-size: 13px; }
.notice { margin-top: 14px; padding: 9px 11px; border-radius: var(--r-s);
          background: var(--soon-bg); color: var(--soon); font-size: 13px; }

/* ------------------------------------------------------------ tables */

/* A real table: an outer rule, a headed top row, a line between every column
   and every row, and numbers that line up down the column. A list of two
   hundred businesses is read by scanning, and scanning needs edges. */

.table-scroll {
  overflow-x: auto;
  border: 1px solid var(--line);
  border-radius: var(--r);
  background: var(--white);
}

table { width: 100%; border-collapse: collapse; font-size: 13.5px; }

thead th {
  background: var(--panel);
  border-bottom: 1px solid var(--line);
  text-align: left; vertical-align: bottom;
  font-size: 9.5px; font-weight: 600; text-transform: uppercase;
  letter-spacing: .085em; color: var(--text-3);
  padding: 9px 12px; white-space: nowrap;
}

tbody td { padding: 10px 12px; border-top: 1px solid var(--line-2); vertical-align: middle; }
tbody tr:first-child td { border-top: 0; }
tbody tr:hover { background: #FAFBFD; }

/* One line between columns, from the header all the way down. */
th + th, td + td { border-left: 1px solid var(--line-2); }

/* Numbers, money and dates line up on the right or the column stops being
   scannable.   00-common 5a */
th.num, td.num { text-align: right; font-variant-numeric: tabular-nums; }

/* The serial number. It is a row's position in what is on screen, not an id,
   so it is quiet: narrow, muted, and it never competes with the name. */
th.sn, td.sn {
  width: 1%; text-align: right; white-space: nowrap;
  color: var(--text-3); font-variant-numeric: tabular-nums;
  padding-right: 10px;
}
td .sub { margin-top: 2px; }

/* A free-text column wraps inside a set width instead of stretching the table
   and pushing the columns after it off the screen. */
td.wrap-cell { max-width: 260px; white-space: normal; }

.tag { font-size: 9.5px; font-weight: 600; text-transform: uppercase; letter-spacing: .085em; }
.tag.draft  { color: var(--text-3); }
.tag.active { color: var(--ok); }
.tag.grace  { color: var(--soon); }
.tag.parked { color: var(--late); }

/* ------------------------------------------------------------ admin screens

   Everything below happens standing in a shop, not at a desk. The admin is
   built to the same constraint as the owner's app, which is easy to forget on
   a screen called admin.   10-admin 4                                       */

.page-head {
  display: flex; align-items: flex-start; justify-content: space-between;
  gap: 14px; flex-wrap: wrap; margin-bottom: 18px;
}
.page-head h1 { font-size: 18px; font-weight: 600; margin: 0; }

/* The way back out of a part, on the screen rather than in the browser chrome.
   He arrived by tapping a tile, so the way back has to be where he is
   looking.   founder 2026-08-26                                            */
.back { display: inline-block; margin-bottom: 3px; font-size: 12.5px;
        color: var(--text-3); text-decoration: none; }
.back:hover { color: var(--text-1); text-decoration: none; }
/* Wrapping, because three buttons do not fit 360px and the third one used to
   hang off the right edge of the screen - Download on sales and expenses.
   AUDIT-2026-09-02 §3·B-16 */
.head-actions { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }

/* His web address, and the way to look at it. A bordered pill rather than a
   link in grey text: the address is what he reads out across the counter and
   what he sends on WhatsApp, so it is shown rather than hidden behind the
   words "See it".   founder 2026-08-26                                      */
/* It is drawn in brand colour, not in grey. Grey text in a bordered box is
   what every screen in the world uses for a control that cannot be pressed, so
   the one link he most wants to press read as switched off.
   founder 2026-08-26 */
.site-pill {
  display: inline-flex; align-items: center; gap: 8px; max-width: 100%;
  padding: 7px 11px; border: 1px solid var(--line); border-radius: var(--r);
  background: var(--white); color: var(--brand); font-weight: 500;
  font-size: 12.5px; text-decoration: none;
}
.site-pill:hover {
  background: var(--brand-bg); border-color: var(--brand); color: var(--brand);
  text-decoration: none;
}
.site-pill .icon { width: 14px; height: 14px; flex: none; color: inherit; }
.site-pill .go { margin-left: auto; }
.site-address {
  font-weight: 500; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

/* On a phone the header stacks, so the pill takes the width rather than
   sitting squeezed beside a title.                                          */
@media (max-width: 560px) {
  .page-head .head-actions { width: 100%; }
  .site-pill { width: 100%; }
}
.head-actions form { margin: 0; }
.head-actions button, .page-head .button { margin-top: 0; }

.quiet-link { font-size: 13px; color: var(--text-2); }

/* A footnote to something full width - a table, a list. It runs the full width
   too, because a short measure under a wide box reads as a broken layout, not
   as a considered line length. */
.note-under { margin-top: 14px; font-size: 12.5px; color: var(--text-3); }

/* Standing prose that is NOT under a wide box - an explanation above a narrow
   form. Here a measure is right, because nothing beside it sets the edge. */
.prose { max-width: 52ch; font-size: 12.5px; color: var(--text-3); }
.sub  { font-size: 12.5px; color: var(--text-3); }
.empty { font-size: 13.5px; color: var(--text-2); }
.section-head { font-size: 14.5px; font-weight: 600; margin: 22px 0 10px; }
.sub-head { font-size: 12.5px; font-weight: 600; color: var(--text-2); margin: 18px 0 6px; }

.ok-note {
  margin-bottom: 16px; padding: 9px 11px; border-radius: var(--r-s);
  background: var(--ok-bg); color: var(--ok); font-size: 13px;
}

textarea {
  width: 100%; padding: 8px 10px; font: inherit; font-size: 13.5px; line-height: 1.5;
  border: 1px solid var(--line); border-radius: var(--r-s); background: var(--white);
  resize: vertical;
}
select {
  width: 100%; padding: 8px 10px; font: inherit; font-size: 14px;
  border: 1px solid var(--line); border-radius: var(--r-s); background: var(--white);
}

/* list ------------------------------------------------------------------ */

.filters { display: flex; gap: 8px; margin-bottom: 16px; flex-wrap: wrap; }
.filters input { flex: 1 1 220px; }
.filters select { flex: 0 0 150px; width: auto; }
.filters button { margin-top: 0; }

/* A table is the right shape for this list and the wrong shape for a phone,
   so it scrolls inside its own box rather than pushing the page sideways. */
.table-scroll table { min-width: 620px; }

/* new shop -------------------------------------------------------------- */

.one-field { max-width: 460px; }
.one-field button { margin-top: 12px; }

.cand-list { display: flex; flex-direction: column; gap: 8px; }
.cand {
  display: flex; align-items: center; justify-content: space-between; gap: 14px;
  padding: 12px 14px; background: var(--panel);
  border: 1px solid var(--line); border-radius: var(--r);
}
.cand form { margin: 0; }
.cand button { margin-top: 0; }
.cand-name { font-size: 14px; font-weight: 600; }
.cand-meta { display: flex; gap: 10px; flex-wrap: wrap; margin-top: 4px;
             font-size: 11.5px; color: var(--text-3); }
.flag { color: var(--soon); font-weight: 600; }

/* one shop -------------------------------------------------------------- */

.sections { display: grid; gap: 14px; grid-template-columns: 1fr; max-width: 760px; }

/* 760px is the width of a column of form fields, which is the right width for
   words and the wrong one for photographs. His photo screen takes everything
   the screen has, and the tiles grow with it - a row of fixed 96px strips in a
   wide column reads as a filmstrip rather than as his shopfront.
   founder 2026-08-26                                                        */
.sections.wide { max-width: none; }
/* A logo, a banner and a gallery, each in its own place. The banner used to be
   whichever photograph happened to be first, so reordering the gallery changed
   the top of his website by accident.   founder 2026-08-26                   */
.slots {
  display: grid; gap: 12px; margin: 14px 0 22px;
  grid-template-columns: repeat(auto-fit, minmax(210px, 1fr));
}
.slot {
  display: flex; flex-direction: column; gap: 9px;
  padding: 12px; border: 1px solid var(--line); border-radius: var(--r);
  background: var(--panel);
}
.slot .shot { background: var(--white); }
/* One box, the same on both, whatever shape the file is. A preview that
   changes size when he swaps the picture makes the screen jump under his
   thumb.   founder 2026-08-26 */
.slot .shot { width: 100%; }
.slot .shot img { width: 100%; height: 104px; background: var(--white); }
/* A logo is judged whole, so it sits inside its box. A banner is judged as the
   wide strip it becomes, so it fills one. Neither is cropped on the way in and
   there is no crop tool.   09-public-extras 2 */
.slot-logo .shot img { object-fit: contain; }
.slot-banner .shot img,
.slot-gallery .shot img { object-fit: cover; object-position: center; }

/* The gallery is the same card as the two above it, across the full width.
   Three places for photographs that behave alike should look alike.
   founder 2026-08-26 */
.slot-gallery { margin-bottom: 4px; }
.slot-gallery .photo-grid { margin-top: 0; }
.slot form { margin: 0; }
.slot .button { margin-top: 0; }
/* Full width in the two single slots, where the button is the only thing on
   the row. The gallery's sits beside its count. */
.slots .slot .button { width: 100%; text-align: center; }

.slot-head { display: flex; flex-direction: column; gap: 2px; }
.slot-head b { font-size: 13px; font-weight: 600; }
.slot-head .sub { margin: 0; font-size: 11.5px; line-height: 1.4; }
/* The gallery's head is a row: the label on the left and the button hard right,
   where the eye ends up after reading the label rather than under it. It wraps
   on a phone rather than squeezing.   founder 2026-08-26 */
/* A heading with its control beside it. `display` was never set, so used on
   its own it stacked and the button dropped under the words — it only worked
   where `.slot-head` was on the same element.   founder 2026-08-27 */
.slot-head-row {
  display: flex; flex-direction: row; align-items: flex-start;
  justify-content: space-between; gap: 12px; flex-wrap: wrap;
  margin-bottom: 12px;
}
/* The words stack exactly as they do in the two slots above - name, then what
   it is, then the count. The row only puts the button beside them. */
.slot-head-text { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.slot-head-row form { flex: 0 0 auto; }
.slot-head-row .button, .slot-head-row a[role=button] { flex: 0 0 auto; margin-top: 0; }
.slot-head-row .slot-head-text .sub { margin-top: 2px; }

.slot-empty {
  display: flex; align-items: center; justify-content: center;
  height: 104px; border: 1px dashed var(--line);
  border-radius: var(--r); background: var(--white);
  font-size: 12px; color: var(--text-4);
}

/* Wider columns when the screen has the room. Every tile keeps the same
   height in all three places, on every width: one that changes height when he
   swaps the picture makes the page jump under his thumb.
   founder 2026-08-26 */
.sections.wide .photo-grid {
  grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
}

.panel {
  background: var(--white); border: 1px solid var(--line);
  border-radius: var(--r); padding: 16px;
}
.panel h2 { font-size: 14.5px; font-weight: 600; margin: 0 0 4px; }
.panel > form { margin-top: 6px; }

.pair { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
@media (max-width: 560px) { .pair { grid-template-columns: 1fr; } }

.checks { display: grid; grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); gap: 6px; }
.check {
  display: flex; align-items: center; gap: 9px; margin: 0;
  padding: 8px 10px; border-radius: var(--r-s); background: var(--white);
  border: 1px solid var(--line-2); font-size: 13.5px; font-weight: 400; color: var(--text);
}
.check input { margin: 0; accent-color: var(--brand); }
.from-plan {
  font-style: normal; font-size: 9.5px; font-weight: 600; text-transform: uppercase;
  letter-spacing: .085em; color: var(--text-4); margin-left: auto;
}

.limits { display: grid; grid-template-columns: repeat(auto-fill, minmax(210px, 1fr)); gap: 10px; }
.limit { background: var(--white); border: 1px solid var(--line-2);
         border-radius: var(--r-s); padding: 9px 11px; }
.limit label { margin: 0 0 4px; font-size: 11.5px; }
.limit input { font-size: 13.5px; padding: 5px 8px; }
.limit .unit { font-size: 10px; color: var(--text-4); text-transform: uppercase;
               letter-spacing: .085em; }

.skins { display: flex; gap: 8px; flex-wrap: wrap; }
.skin-pick {
  display: flex; align-items: center; gap: 7px; margin: 0;
  padding: 7px 11px; border-radius: var(--r-s); background: var(--white);
  border: 1px solid var(--line-2); font-size: 12.5px; font-weight: 400; color: var(--text);
  cursor: pointer;
}
.skin-pick input { margin: 0; accent-color: var(--brand); }
.swatch { width: 13px; height: 13px; border-radius: 2px; display: inline-block; }

.plain { list-style: none; margin: 8px 0 0; padding: 0; font-size: 13.5px; }
.plain li { padding: 5px 0; border-top: 1px solid var(--line-2); }
.plain li:first-child { border-top: 0; }

.log { list-style: none; margin: 8px 0 0; padding: 0; font-size: 13px; }
.log li { display: flex; gap: 10px; align-items: baseline;
          padding: 6px 0; border-top: 1px solid var(--line-2); }
.log li:first-child { border-top: 0; }
.log-when { flex: 0 0 62px; color: var(--text-3); font-size: 11.5px; }
.log-what { flex: 1; }

/* ------------------------------------------------------------ on a phone

   The sidebar becomes a drawer. Same list, same order, nothing nested - it is
   the same navigation, not a reduced one.   00-common 6, 7                  */

.hamburger { display: none; align-items: center; background: none; border: 0;
             padding: 6px; margin: 0; color: var(--text-2); cursor: pointer; }
.scrim { display: none; }

@media (max-width: 760px) {
  .hamburger { display: inline-flex; }
  /* **It scrolls.** The drawer is pinned to the height of the screen, and on a
     full plan its rows come to about a thousand pixels - so on a 568px phone
     the bottom of the list simply was not reachable. The last module in it was
     off the screen with nothing to say so.   founder 2026-09-05 */
  .sidebar {
    position: fixed; inset: 0 auto 0 0; z-index: 50; width: 250px;
    transform: translateX(-100%); transition: transform .18s ease;
    box-shadow: 0 0 40px rgba(20, 24, 33, .2);
    overflow-y: auto; -webkit-overflow-scrolling: touch;
    /* The page behind must not scroll instead when the list reaches its end. */
    overscroll-behavior: contain;
  }
  /* A thin one, and only while it is being used: a scrollbar down the middle
     of a 250px drawer is a fifth of the word next to it. */
  .sidebar { scrollbar-width: thin; scrollbar-color: var(--line) transparent; }
  .sidebar::-webkit-scrollbar { width: 4px; }
  .sidebar::-webkit-scrollbar-thumb { background: var(--line); border-radius: 4px; }
  body.nav-open .sidebar { transform: none; }
  body.nav-open .scrim {
    display: block; position: fixed; inset: 0; z-index: 40;
    background: rgba(20, 24, 33, .35);
  }
  /* And the screen under it stays where it was. Without this the drawer
     scrolls, reaches its end, and hands the rest of the gesture to the page
     behind - so he closes it and finds himself somewhere else. */
  body.nav-open { overflow: hidden; }
  .header { padding: 0 12px; gap: 8px; }
  /* The screen name and the person's name both go; which business he is on
     and the way out both stay, because those are the two he can be wrong
     about. */
  .page-title { display: none; }
  .page-chip { display: none; }
  .user-name { display: none; }
  .account .chev { display: none; }
  .shop-switch { flex: 1; max-width: none; }
  .content { padding: 16px 14px; }
  .card-grid { grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); }
}

/* A destructive action looks destructive, and only once you are next to it.
   The panel is quiet in the page and loud at the button.   10-admin 6 */
.panel.danger { border-color: var(--late); }
.panel.danger h2 { color: var(--late); }
/* The trigger that opens a delete question is a link, not a button, so this
   cannot be button-only or the most dangerous control on the screen would be
   the one that is not red.   founder 2026-08-26 */
.destructive { background: var(--late); color: #fff; }
.destructive:hover { background: #A32E22; color: #fff; text-decoration: none; }

/* One question before anything is deleted.   founder 2026-08-26

   The browser's own dialog element, styled to match. No library: a yes-or-no
   box is not worth 45 KB on a 3G phone, and everything here has to work with
   no script running at all. `:target` is what opens it without one - the
   trigger is a link to the dialog's id. The script upgrades that to a real
   modal and nothing more.   00-common 5a                                    */
.confirm {
  display: none;
  position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%);
  width: min(370px, calc(100vw - 32px));
  margin: 0; padding: 20px 20px 18px;
  border: 1px solid var(--line); border-radius: var(--r);
  background: var(--white); color: var(--text);
  box-shadow: 0 18px 44px rgba(20, 24, 33, .18);
  z-index: 60;

  /* **A popup taller than the phone scrolls itself.** Founder, 2026-09-08:
     *"the popups are not scrollable. the page behind it scrolls."* A day with
     eight things on it and a form under them is taller than a 360x640 screen,
     and `position: fixed` with no height on it just runs off both ends: the
     add form could not be reached and the thumb found the page underneath
     instead.

     `dvh` is the viewport *without* the address bar, which is the one the
     content has to fit in on a phone. `vh` first for anything that does not
     know it - it is the same number on a desktop and merely generous on a
     phone.   founder 2026-09-08 */
  max-height: calc(100vh - 32px);
  max-height: calc(100dvh - 32px);
  overflow-y: auto;
  overscroll-behavior: contain;   /* the scroll stops here, not on the page */
}
.confirm[open], .confirm:target { display: block; }

/* **And the page behind it holds still.** `showModal()` locks the page for
   free, but the day popup is opened by the *server* - the `open` attribute,
   drawn because the address carries `?on=` - and an `open` dialog is an
   ordinary element that the page scrolls right past.

   `:has()` is what locks it with no script. A browser too old for `:has()`
   ignores the rule and behaves exactly as it does today, which is the right
   way round for an enhancement.   00-common 5a */
html:has(dialog.confirm[open]),
html:has(dialog.confirm:target) { overflow: hidden; }

/* The modal path gets the browser's backdrop; the script-free path paints its
   own, inside the dialog's own stacking context so it sits over the page and
   under the box.                                                            */
.confirm::backdrop { background: rgba(20, 24, 33, .45); }

/* **The script-free scrim is a shadow, not a box.** It was a `::before` at
   `inset: 0` with `z-index: -1`, meant to sit under the dialog and over the
   page. It could do neither: `.confirm` is `position: fixed` with a `transform`
   and a `z-index`, so it is a stacking context *and* the containing block for
   its own fixed children - which put the grey square exactly over the dialog
   and nowhere else. The question came out greyed and unreadable, and only with
   no script running, which is why nobody saw it.

   A shadow is painted outside the border box and cannot be trapped inside it.
   `100vmax` covers the page whichever way round it is held, and the page is
   already `overflow: hidden` while a dialog is open, so it makes no
   scrollbars.   founder 2026-09-08 */
.confirm:target {
  box-shadow: 0 0 0 100vmax rgba(20, 24, 33, .45),
              0 18px 44px rgba(20, 24, 33, .18);
}
/* The day popup and the suggestion box are opened by the *server* - the `open`
   attribute - so they are never native modals and never get `::backdrop`. They
   need the same scrim. `:not(:modal)` keeps it off the ones the script did open
   properly, which would otherwise be darkened twice; it is a rule of its own so
   that a browser which does not know `:modal` drops only this one and keeps the
   `:target` scrim above.   founder 2026-09-08 */
.confirm[open]:not(:modal) {
  box-shadow: 0 0 0 100vmax rgba(20, 24, 33, .45),
              0 18px 44px rgba(20, 24, 33, .18);
}

.confirm h2 { font-size: 15px; font-weight: 600; margin: 0 0 6px; }
.confirm p { font-size: 12.5px; line-height: 1.5; color: var(--text-2); margin: 0; }
/* One of the two is a link and the other is a button in a form, so nothing
   about their geometry can be left to the defaults: the link carries a border
   and a top margin the button does not, and two pixels is enough to see. Both
   are given the same box here and nowhere else.                              */
.confirm-actions {
  display: flex; align-items: stretch; justify-content: flex-end; gap: 10px;
  margin-top: 18px;
}
.confirm-actions form { display: flex; margin: 0; }
.confirm-actions .button,
.confirm-actions button {
  margin: 0; padding: 8px 16px;
  font-size: 13.5px; font-weight: 500; line-height: 1.25;
  border: 1px solid transparent; border-radius: var(--r-s);
  text-decoration: none;
}
.confirm-actions .button.quiet {
  background: var(--white); color: var(--text-2); border-color: var(--line);
}
.confirm-actions .button.quiet:hover { background: var(--panel); text-decoration: none; }

/* The same box, widened for a form and given a way out of its own. A man who
   opened it to look has to be able to leave without answering.   00-common 5a */
.sheet { width: min(620px, calc(100vw - 32px)); padding: 18px 20px 20px; }
.sheet-head { display: flex; align-items: flex-start; gap: 14px; margin-bottom: 4px; }
.sheet-head > div { flex: 1; min-width: 0; }
/* **A fingertip on a phone.** It was 28px in every dialog in the product -
   the account menu, the birthday sheet, the suggestion box, and now the day on
   the calendar. Found by measuring the calendar and true of all of them.
   founder 2026-09-08, 00-common 6 */
@media (max-width: 760px) {
  .sheet-close { width: 44px; height: 44px; margin: -8px -8px 0 0; }
}
.sheet-close {
  flex: 0 0 auto; width: 28px; height: 28px; margin: -4px -4px 0 0;
  display: flex; align-items: center; justify-content: center;
  font-size: 20px; line-height: 1; color: var(--text-3);
  border: 1px solid transparent; border-radius: var(--r-s); text-decoration: none;
}
.sheet-close:hover { color: var(--text); background: var(--panel);
                     border-color: var(--line); text-decoration: none; }
.sheet .pair { display: grid; gap: 0 14px; grid-template-columns: 1fr 1fr; }
.sheet label:first-of-type { margin-top: 10px; }
.sheet .sheet-do { display: flex; justify-content: flex-end; gap: 10px; margin-top: 18px; }
.sheet .sheet-do .button, .sheet .sheet-do button { margin-top: 0; }
@media (max-width: 560px) { .sheet .pair { grid-template-columns: 1fr; } }

/* people ------------------------------------------------------------------

   A month of staff, and a day of them. One row per person, read left to right:
   who, what they cost, what was marked, where it stands, what to do about it.
   07-employees 4, 06-attendance 3                                           */

.people { gap: 0; margin-top: 10px; }
.person {
  display: grid; gap: 4px 14px; align-items: center;
  padding: 14px 0; border-bottom: 1px solid var(--line-2);
  grid-template-columns: 1fr auto;
}
.person:last-child { border-bottom: 0; }
@media (min-width: 760px) {
  .person { grid-template-columns: 1.5fr .9fr 1.1fr auto auto; padding: 12px 0; }
  .person.mark { grid-template-columns: 1.4fr 1.1fr auto; }
}
.person-who { min-width: 0; display: block; color: inherit; }
.person-who b { display: block; font-size: 15px; font-weight: 600; }
.person-who .sub { margin: 0; }
a.person-who:hover b { color: var(--brand); }
.person-pay { display: flex; flex-direction: column; }
.person-pay .sub { margin: 0; }
.person-days { min-width: 0; }
.person-do form { margin: 0; }
.person-do button { margin-top: 0; padding: 7px 12px; font-size: 13px; }
/* Somebody who has left keeps his place in the months he was paid in.
   07-employees E-3 */
.person.gone { opacity: .62; }

/* Where the month stands. Amber is a job, green is news.   00-common 5 */
.tag-unpaid, .tag-part, .tag-paid {
  font-size: 10.5px; font-weight: 600; letter-spacing: .07em; text-transform: uppercase;
  padding: 4px 8px; border-radius: var(--r-s); white-space: nowrap;
}
.tag-unpaid { background: var(--soon-bg); color: var(--soon); }
.tag-part   { background: var(--soon-bg); color: var(--soon); }
.tag-paid   { background: var(--ok-bg); color: var(--ok); }

/* One tap per person, four states, no save button.   06-attendance 3 */
.marks { display: flex; gap: 5px; flex-wrap: wrap; }
.marks form { margin: 0; }
.mark-btn {
  margin: 0; padding: 8px 12px; font-size: 12.5px; font-weight: 500;
  background: var(--white); color: var(--text-2);
  border: 1px solid var(--line); border-radius: var(--r-s);
}
.mark-btn:hover { background: var(--panel); color: var(--text); }
/* Present is the prominent one because most people come most days - but
   nothing is written until he taps it.   06-attendance 9 */
.mark-btn.lead { border-color: var(--line); color: var(--text); font-weight: 600; }
.mark-present.on { background: var(--ok-bg); color: var(--ok); border-color: var(--ok); }
.mark-absent.on  { background: var(--late-bg); color: var(--late); border-color: var(--late); }
.mark-half.on    { background: var(--soon-bg); color: var(--soon); border-color: var(--soon); }
.mark-leave.on   { background: var(--brand-bg); color: var(--brand); border-color: var(--brand); }

/* Moving between months and days. A step that leads nowhere is turned off
   rather than taken away, so the pair never shifts.   founder 2026-08-26 */
.months { gap: 6px; }
.months .button { margin-top: 0; padding: 8px 14px; }
.months .button.off { opacity: .3; pointer-events: none; }

.line-row { grid-template-columns: 1.4fr .8fr auto; align-items: baseline; }
.line-row .cell-name { font-size: 15px; }

/* The same figures the dashboard opens with, in one line above the list he is
   standing on. He asked for the collective detail here too, and a strip rather
   than three more cards: this screen's job is the list underneath it.
   founder 2026-08-27                                                        */
.stat-strip {
  display: flex; margin-bottom: 12px; overflow: hidden;
  border: 1px solid var(--line); border-radius: var(--r); background: var(--panel);
}
.stat-strip .cell { flex: 1; padding: 13px 17px; border-right: 1px solid var(--line); }
.stat-strip .cell:last-child { border-right: 0; }
.stat-strip .v { font-size: 24px; font-weight: 600; letter-spacing: -.025em;
                 line-height: 1.05; font-variant-numeric: tabular-nums; }
.stat-strip .k { margin-top: 4px; font-size: 12px; font-weight: 500; color: var(--text-2); }
.stat-strip .cell.none .v { color: var(--text-4); }
@media (max-width: 720px) {
  .stat-strip { flex-wrap: wrap; }
  .stat-strip .cell { flex: 1 1 50%; border-bottom: 1px solid var(--line); }
  .stat-strip .cell:nth-child(2n) { border-right: 0; }
}

/* The counts, spelled out beside their heading. Chips rather than the bars the
   dashboard draws: the same figures said twice in the same shape would read as
   the same screen twice, and this one's job is the list.   founder 2026-08-27 */
.count-rows { margin-bottom: 4px; }
.count-row { display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap;
             padding: 7px 0; font-size: 13px; }
.count-row > .k { flex: 0 0 68px; font-size: 12px; font-weight: 500; color: var(--text-3); }
.count-chip {
  display: inline-flex; align-items: baseline; gap: 6px; padding: 4px 10px;
  font-size: 12.5px; color: var(--text-2);
  background: var(--white); border: 1px solid var(--line); border-radius: 20px;
}
.count-chip b { font-weight: 600; color: var(--text); font-variant-numeric: tabular-nums; }
.count-chip.zero { color: var(--text-4); background: none; }
.count-chip.zero b { color: var(--text-4); }
/* A chip he can press is a filter; the one that is on says so.
   founder 2026-08-27 */
a.count-chip { text-decoration: none; }
a.count-chip:hover { border-color: var(--text-4); color: var(--text); text-decoration: none; }
.count-chip.on, a.count-chip.on:hover {
  background: var(--brand); border-color: var(--brand); color: #fff;
}
.count-chip.on b { color: #fff; }

/* Search, and the way to take the whole list away with him. */
.list-tools { display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
              margin-bottom: 12px; }
.list-tools form { display: flex; gap: 8px; margin: 0; flex: 1 1 260px; }
.list-tools input[type=search] {
  flex: 1; min-width: 0; padding: 8px 11px; font: inherit; font-size: 13.5px;
  border: 1px solid var(--line); border-radius: var(--r-s); background: var(--white);
}
.list-tools input:focus { outline: 2px solid var(--brand-bg); border-color: var(--brand); }
.list-tools button, .list-tools .button { margin-top: 0; padding: 8px 14px; font-size: 13px; }
.list-tools .button.quiet:hover .icon { color: var(--text); }
.showing { font-size: 12.5px; color: var(--text-3); }

/* a list of people -------------------------------------------------------

   A table with a header rather than a stack of rows: eight names with a role,
   a number and a salary each is a table, and pretending otherwise costs him
   the column headings that say which figure is which.
   Designed in docs/mockups/employee-screens.html, founder 2026-08-27        */

/* Eight columns will not fit a phone. The table scrolls sideways inside its
   own box rather than being cut off by it, and the box carries the border so
   the scroll never looks like a broken edge.   00-common 5a, founder 2026-08-27 */
.table-scroll { overflow-x: auto; border: 1px solid var(--line); border-radius: var(--r);
                background: var(--white); }
.table-scroll .list-table { border: 0; border-radius: 0; min-width: 720px; }
.fold .table-scroll { border: 0; border-top: 1px solid var(--line-2); border-radius: 0; }
.fold .table-scroll .list-table { min-width: 420px; }

.list-table { border: 1px solid var(--line); border-radius: var(--r); overflow: hidden;
              background: var(--white); }
.list-head, .list-row { display: grid; align-items: stretch; }

/* Ruled both ways. With only horizontal lines he has to carry his eye across
   open space to work out which column a figure is in.   founder 2026-08-27 */
.list-head > span, .list-row > * { display: flex; align-items: center;
                                   padding: 11px 14px; min-width: 0; }
.list-head > span + span, .list-row > * + * { border-left: 1px solid var(--line-2); }

.list-head {
  background: var(--panel); border-bottom: 1px solid var(--line);
  font-size: 11px; font-weight: 600; letter-spacing: .07em; text-transform: uppercase;
  color: var(--text-3);
}
.list-row { border-bottom: 1px solid var(--line-2); }
.list-row:last-child { border-bottom: 0; }
.list-row:hover { background: #FAFBFD; }
/* The serial and the chevron are markers, not readings. */
.list-head > span.no, .list-row > .no, .list-row > .go { justify-content: center; }

/* Two letters and a tint. evedeck holds no photograph of anybody who works in
   a shop, so this is the face.   00-common 11 */
.who-chip {
  flex: 0 0 auto; width: 34px; height: 34px; border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  font-size: 12.5px; font-weight: 600; background: var(--brand-bg); color: var(--brand);
}
.who-chip.t2 { background: #F0E9F8; color: #7B54AE; }
.who-chip.t3 { background: var(--ok-bg); color: var(--ok); }
.who-chip.t4 { background: var(--soon-bg); color: var(--soon); }
.list-row.gone .who-chip { background: var(--line-2); color: var(--text-3); }

/* The face belongs with the name, in one cell. Two cells put a rule between a
   man and his own initials.   founder 2026-08-27 */
.list-row > .who { gap: 11px; color: inherit; text-decoration: none; }
.list-row .who .name { min-width: 0; }
.list-row .who b { display: block; font-size: 14.5px; font-weight: 600; letter-spacing: -.008em;
                   overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.list-row .who small { display: block; margin-top: 1px; font-size: 12px; color: var(--text-3);
                       overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
a.who:hover b { color: var(--brand); }
a.who:hover { text-decoration: none; }
.list-row .cell { font-size: 13px; color: var(--text-2);
                  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.list-row .cell.none { color: var(--text-4); }

/* ---------------------------------------------- the same list, on a phone

   **A table is a desktop shape.** Every list in this product was a 720px grid
   inside a box that scrolled sideways, so on a 330px column more than half of
   every row was always hidden and the owner had to drag a table under his
   thumb to reach the thing the module exists for - the expiry date in members,
   the message in enquiries. Nothing was lost, but nothing was readable either,
   and the CSS said what the product had become: twenty-two of its twenty-three
   media queries are `max-width`, which is a desktop layout with a phone
   fallback rather than the other way round.   AUDIT-2026-09-02 §2·05

   So under 620px a row stops being a row. The name owns the first line, what
   the row does sits at the right of it, and every other column runs underneath
   as one quiet line that wraps. No sideways scroll anywhere in the product.

   **The grid is not overridden, it is switched off.** Each row carries its
   columns as an inline `style`, which no stylesheet rule can beat without
   `!important`; changing `display` makes the whole declaration inert instead.
   That is why this block sets `display` and never `grid-template-columns`. */
@media (max-width: 620px) {
  /* Only where the rows become cards. The attendance register is a real
     matrix - every person against every day of a month - and there is no
     card that is a grid of thirty ticks, so that one goes on scrolling
     sideways inside its own box, which is the right answer for it. */
  .table-scroll:has(.list-table) { overflow-x: visible; }
  .table-scroll .list-table,
  .fold .table-scroll .list-table { min-width: 0; }

  /* Column headings are the one thing a card cannot carry. A date, a plan
     name and a figure say what they are; that is why the cells that do not
     are given a word of their own in the markup rather than a heading. */
  .list-table .list-head { display: none; }

  .list-row { display: flex; flex-wrap: wrap; align-items: baseline;
              gap: 1px 9px; padding: 12px 14px; position: relative; }
  .list-row > * { padding: 0; border-left: 0; min-width: 0; }

  /* The name, and the room for whatever is pinned beside it. */
  .list-row > .who { flex: 1 1 100%; padding-right: 34px; }
  .list-row .who b { white-space: normal; }

  /* Everything else, in a line under it. Space rather than punctuation: a
     dot between values is drawn before the first item of a wrapped line as
     well as between them, and a line beginning "· 1 Apr" reads as a mistake.
     The values separate themselves - a badge has its own ground, money has a
     sign, a date looks like a date. */
  .list-row > .cell { flex: 0 1 auto; font-size: 12.5px; white-space: normal; }
  .list-row { gap: 3px 16px; }

  /* What the row *does* belongs beside the name, not on the line of readings
     under it. The action is not a direct child - it is a link or a form
     inside the last cell - so the cell holding one is what moves. */
  /* Split from the `:has()` rule below on purpose: a browser that does not
     know `:has()` throws away the whole selector list it appears in, and
     these two would go with it. Everything here degrades to the action
     sitting inline at the end of the card, which is tidy rather than broken. */
  .list-row > .go, .list-row > .pick {
    position: absolute; top: 9px; right: 10px; padding: 0;
  }
  .list-row > .cell:has(> form),
  .list-row > .cell:has(> a.go),
  .list-row > .cell:has(> a.button) {
    position: absolute; top: 9px; right: 10px; padding: 0;
  }
  .list-row > .cell > form { margin: 0; }
  .list-row > .no { display: none; }          /* a serial is a table's habit */

  /* An empty row has one thing to say and the whole width to say it in. */
  .list-row.empty > .cell { flex: 1 1 100%; }
  .list-row.empty > .cell::before { content: none; }
}
/* One person's month, day by day. The day carries its weekday under it, so a
   run of blanks reads as Sundays rather than as neglect. */
.list-row .day small { margin-left: 7px; font-size: 12px; color: var(--text-4);
                       text-transform: uppercase; letter-spacing: .04em; }
.list-row.unmarked { background: #FCFDFE; }
.mark-tag {
  font-size: 11px; font-weight: 600; letter-spacing: .05em; text-transform: uppercase;
  padding: 4px 9px; border-radius: var(--r-s); white-space: nowrap;
}
.mark-tag.mark-present { background: var(--ok-bg);   color: var(--ok); }
.mark-tag.mark-half    { background: var(--soon-bg); color: var(--soon); }
.mark-tag.mark-absent  { background: var(--late-bg); color: var(--late); }
.mark-tag.mark-leave   { background: var(--brand-bg);color: var(--brand); }
/* A column heading he can press to sort by it. */
.sortable { color: inherit; text-decoration: none; }
.sortable:hover { color: var(--text); text-decoration: none; }
.sortable.on { color: var(--brand); }
.sortable.on::after { content: " ↓"; }

/* Picking a group, and what to do with them. */
.list-row > .pick, .list-head > span.pick { padding-left: 12px; padding-right: 6px; }
.list-row .pick input, .list-head .pick input {
  width: 16px; height: 16px; margin: 0; accent-color: var(--brand); cursor: pointer;
}
/* Not red. Red is signal - a problem, or something that cannot be undone -
   and taking somebody off the list is neither: nothing is deleted, he keeps
   every month he was paid in, and Reinstate is one tap away. It is the same
   quiet button as Download beside it.   00-common 5a, 07-employees E-3

   Nothing picked, nothing to remove. Turned off rather than taken away, so the
   row of controls never shifts.   founder 2026-08-26 */
.list-tools .button.pick-do:hover { border-color: var(--text-4); color: var(--text);
                                    text-decoration: none; }
.list-tools .button.pick-do.off { color: var(--text-4); pointer-events: none; opacity: .55; }
.list-foot { display: flex; align-items: center; gap: 14px; flex-wrap: wrap; margin-top: 12px; }
.list-foot .button, .list-foot > a[role=button] { margin-top: 0; }
.list-foot .sub { margin: 0; }
.pager { display: flex; align-items: center; gap: 10px; margin-left: auto; }
.pager .button { margin-top: 0; padding: 7px 13px; font-size: 13px; }
.pager .button.off { opacity: .3; pointer-events: none; }
.pager .sub { margin: 0; white-space: nowrap; }

/* Two men can share a name. Two rows that are one man entered twice look
   exactly like it, so this flags and never blocks.   founder 2026-08-27 */
.list-row .name small.warn {
  display: inline-block; margin-top: 2px; padding: 1px 6px;
  font-size: 10.5px; font-weight: 600; letter-spacing: .04em; text-transform: uppercase;
  background: var(--soon-bg); color: var(--soon); border-radius: 2px;
}

/* His own order, numbered. He asked for a serial rather than the up and down
   arrows: the numbers are what he reads them out by.   founder 2026-08-27 */
.list-row .no, .list-head .no { font-size: 12.5px; color: var(--text-4);
                                font-variant-numeric: tabular-nums; }
.list-row .moves { display: flex; gap: 4px; }
.list-row .moves form { margin: 0; }
.list-row .moves button { margin: 0; padding: 5px 9px; font-size: 12px; }
.list-row .go { color: var(--text-4); }
.list-row .go:hover { color: var(--brand); }
.list-row.gone { opacity: .72; }

/* Somebody who left is kept and is never in the way. A details element, so it
   opens with no script at all.   00-common 5a, 07-employees E-3 */
.fold { border: 1px solid var(--line); border-radius: var(--r); background: var(--white);
        margin-top: 12px; }
.fold > summary { padding: 12px 16px; font-size: 13.5px; font-weight: 500;
                  color: var(--text-2); cursor: pointer; list-style: none; }
.fold > summary::-webkit-details-marker { display: none; }
.fold > summary:hover { color: var(--text); }
.fold > summary .sub { display: inline; margin-left: 6px; color: var(--text-4); }
.fold .list-table { border: 0; border-top: 1px solid var(--line-2); border-radius: 0; }

/* A cake, on the day and never otherwise. Not a figure and not a job, so not
   a card: it sits beside the title and is gone tomorrow.
   founder 2026-08-27                                                        */
.cake {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 7px 11px; border-radius: var(--r-s);
  background: var(--soon-bg); border: 1px solid transparent; text-decoration: none;
}
.cake .icon { width: 19px; height: 19px; color: var(--soon); }
.cake:hover { border-color: var(--soon); text-decoration: none; }
.cake .many { font-size: 13px; font-weight: 600; color: var(--soon);
              font-variant-numeric: tabular-nums; }
/* In the header it sits between the page title and the business, so it is
   compact and never pushes either off a narrow screen.   founder 2026-08-27 */
.header .cake { flex: 0 0 auto; margin-left: auto; padding: 6px 10px; }
.header .cake .many.today { font-size: 12px; letter-spacing: .02em; }
@media (max-width: 560px) { .header .cake .many { display: none; } }
/* The date, and how far off it is. Two facts in one cell, so the date leads
   and the distance follows it.   founder 2026-08-27 */
.list-row > .cell.when {
  flex-direction: column; align-items: flex-start; gap: 3px;
  white-space: nowrap; font-variant-numeric: tabular-nums;
}
.cell.when .line { display: flex; align-items: center; gap: 8px; }
.cell.when .wish { font-size: 12px; font-weight: 500; }
/* Every row is its own grid, so a column sized `auto` measures that row's own
   content and the columns drift apart between rows. Fixed widths or fractions
   only.   founder 2026-08-27 */
.list-row .cell.when small { font-size: 12px; color: var(--text-4); }

/* A panel he came to read, not to fill in. Label over value, and the form
   behind a button.   founder 2026-08-27                                     */
.facts { display: grid; gap: 12px 26px; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); }
.facts > div { min-width: 0; }
.facts .k { display: block; font-size: 12px; font-weight: 500; color: var(--text-3); }
.facts .v { display: block; margin-top: 3px; font-size: 14.5px; }
.facts .v.big { font-size: 19px; font-weight: 600; letter-spacing: -.02em;
                font-variant-numeric: tabular-nums; }
.facts .v.none { font-size: 13.5px; color: var(--text-4); }
.facts .wish { display: inline-flex; align-items: center; gap: 5px; margin-top: 5px;
               font-size: 12.5px; font-weight: 500; }
.facts .wish .icon { width: 14px; height: 14px; color: var(--brand); }

/* what happened to somebody ----------------------------------------------

   "History" was six rows of past months with no heading. The word promises a
   record of what happened to a person and it delivered a payment list.
   07-employees 12, founder 2026-08-27                                       */

.timeline { position: relative; margin: 6px 0 2px; padding-left: 24px; }
.timeline::before {
  content: ""; position: absolute; left: 6px; top: 8px; bottom: 8px;
  width: 2px; background: var(--line);
}
.tl-item { position: relative; padding: 9px 0; }
.tl-item::before {
  content: ""; position: absolute; left: -22px; top: 15px;
  width: 10px; height: 10px; border-radius: 50%;
  background: var(--white); border: 2px solid var(--text-4);
}
/* Colour by what happened. A raise and a man leaving are not the same news. */
.tl-item.tl-salary::before, .tl-item.tl-role::before { border-color: var(--ok); }
.tl-item.tl-joined::before, .tl-item.tl-reinstated::before { border-color: var(--brand); }
.tl-item.tl-removed::before { border-color: var(--late); }
.tl-when { font-size: 12px; color: var(--text-4); }
.tl-what { margin-top: 1px; font-size: 13.5px; }

/* Three across, where a pair is two. The emergency contact is a name, a
   relationship and a number, and splitting it over two rows loses that they
   are one answer.   07-employees 12 */
.trio { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 0 14px; }
@media (max-width: 640px) { .trio { grid-template-columns: 1fr; } }

/* plans ------------------------------------------------------------------ */

.plan-head { display: flex; align-items: baseline; justify-content: space-between;
             gap: 12px; flex-wrap: wrap; margin-bottom: 2px; }
.plan-head h2 { margin: 0; }

.flags { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 14px; }
.flags .check { font-size: 13px; }

.plan-actions { margin-top: 4px; }
.plan-delete { margin-top: 10px; padding-top: 10px; border-top: 1px solid var(--line-2); }
.plan-delete button { margin-top: 0; font-size: 12.5px; padding: 6px 11px; color: var(--late); }
.plan-delete button:hover { border-color: var(--late); }

/* A count and its unit read as one control, because they are one answer. */
.period { display: flex; gap: 6px; }
.period input { flex: 0 0 72px; }
.period select { flex: 1; }

/* photos -----------------------------------------------------------------

   A grid of what was just shot, big enough to tell one from another on a phone
   held at arm's length in a shop.   10-admin 4                              */

.photo-grid {
  display: grid; gap: 8px; margin-top: 14px;
  grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
}

.shot { position: relative; margin: 0; border: 1px solid var(--line);
        border-radius: var(--r); overflow: hidden; background: var(--white); }
.shot img { width: 100%; height: 96px; object-fit: cover; display: block; }

/* Google's are the preview. Marked, so a live site is never left showing them. */
.shot.is-google { border-color: var(--soon); }
.shot.is-google::after {
  content: "Google"; position: absolute; top: 5px; right: 5px;
  font-size: 9px; font-weight: 600; letter-spacing: .06em; text-transform: uppercase;
  background: var(--soon-bg); color: var(--soon); padding: 2px 5px; border-radius: 2px;
}

.hero-tag {
  position: absolute; top: 5px; left: 5px;
  font-size: 9px; font-weight: 600; letter-spacing: .06em; text-transform: uppercase;
  background: var(--brand); color: #fff; padding: 2px 5px; border-radius: 2px;
}

.shot figcaption { display: flex; gap: 2px; padding: 4px; border-top: 1px solid var(--line-2); }
.shot figcaption form { margin: 0; flex: 1; }
/* The delete control is a link rather than a button, because it opens the
   question instead of doing the thing. It has to look identical to the three
   beside it.   founder 2026-08-26 */
.shot figcaption button, .shot figcaption a.drop {
  display: block; box-sizing: border-box; flex: 1; text-align: center;
  width: 100%; margin: 0; padding: 4px 0; font-size: 11px; font-weight: 500;
  background: var(--white); color: var(--text-2); border: 1px solid var(--line-2);
  border-radius: 2px; text-decoration: none; cursor: pointer;
}
.shot figcaption button:hover, .shot figcaption a.drop:hover {
  background: var(--panel); color: var(--text); text-decoration: none;
}
.shot figcaption button.drop:hover, .shot figcaption a.drop:hover {
  background: var(--late-bg); color: var(--late); border-color: var(--late);
}

/* A file input is unstyleable, so the label is the button and the input hides
   behind it. On a phone this opens the camera.   10-admin 4 */
label.button { margin-top: 0; color: #fff; cursor: pointer; }

/* tabs --------------------------------------------------------------------

   A business screen has nine sections and a plan has six. Stacked, that is a
   long scroll on a desk and a very long one on a phone held in a shop, where
   the founder wants one of them and knows which.   10-admin 4

   Every panel is rendered; the tab only decides which is shown. So a ?tab=
   link works with no script at all, and the script only saves the round trip.
*/

.tabs {
  display: flex; gap: 2px; overflow-x: auto; margin-bottom: 14px;
  border-bottom: 1px solid var(--line);
}
.tabs a {
  flex: 0 0 auto; padding: 9px 13px; border-radius: var(--r-s) var(--r-s) 0 0;
  font-size: 13px; font-weight: 500; color: var(--text-2);
  border: 1px solid transparent; border-bottom: 0;
  margin-bottom: -1px; white-space: nowrap;
}
.tabs a:hover { background: var(--nav-hover); color: var(--text); text-decoration: none; }

.tabs a.on {
  background: var(--white); color: var(--text); font-weight: 600;
  border-color: var(--line); border-bottom: 1px solid var(--white);
}
.tabs a .count {
  margin-left: 6px; font-size: 11px; font-weight: 600; color: var(--text-4);
  font-variant-numeric: tabular-nums;
}
.tabs a.on .count { color: var(--brand); }
.tabs a.warn .count, .tabs a.warn { color: var(--soon); }

.tab-panel[hidden] { display: none; }

/* editable rows -----------------------------------------------------------

   Four fields on one line, and on a phone they stack. Every row is its own
   form, so saving one never touches another and there is no Save All to
   forget.   01-website 5                                                    */

.rows-edit { display: flex; flex-direction: column; gap: 6px; margin: 12px 0; }

.row-edit {
  display: grid; gap: 6px; margin: 0; align-items: center;
  grid-template-columns: 1fr 1.6fr .9fr 1.2fr auto auto;
}
.row-edit input { font-size: 13.5px; padding: 6px 9px; }
.row-edit button { margin-top: 0; padding: 6px 10px; font-size: 12.5px; }
.row-edit .cell-price { font-variant-numeric: tabular-nums; }

.row-moves { display: flex; gap: 3px; align-items: stretch; }
.row-moves button { padding: 6px 8px; }
.row-moves a.drop {
  display: inline-flex; align-items: center; padding: 6px 10px;
  font-size: 12.5px; font-weight: 500; text-decoration: none;
  background: var(--white); color: var(--text-2);
  border: 1px solid var(--line); border-radius: var(--r-s);
}
.row-moves button.drop:hover, .row-moves a.drop:hover {
  background: var(--late-bg); color: var(--late); border-color: var(--late);
  text-decoration: none;
}

.row-add { padding-top: 10px; border-top: 1px solid var(--line-2); }

@media (max-width: 620px) {
  .row-edit { grid-template-columns: 1fr 1fr; }
  .row-edit .cell-name { grid-column: 1 / -1; }
  .row-moves { justify-content: flex-end; }
}

/* a module he has and does not use --------------------------------------- */

.start-module { max-width: 420px; margin: 40px auto; text-align: center; }
.start-module h2 { font-size: 17px; font-weight: 600; margin: 14px 0 6px; }
.start-module .prose { margin: 0 auto; text-align: center; font-size: 13.5px;
                       color: var(--text-2); }
.start-module button { margin-top: 18px; }
/* The starters on an empty renewals module. Chips rather than a list: he is
   picking, not reading.   AUDIT-2026-09-02 §10A */
.starters { display: flex; flex-wrap: wrap; gap: 8px; justify-content: center;
            margin: 18px 0 10px; }
.starters .mod { display: inline-flex; align-items: center; gap: 7px;
                 min-height: 40px; padding: 8px 12px; background: var(--white);
                 border: 1px solid var(--line); border-radius: var(--r-s);
                 font-size: 13px; color: var(--text-2); text-decoration: none; }
.starters .mod:hover { border-color: var(--brand); color: var(--brand); }
.starters .mod .icon { width: 14px; height: 14px; color: var(--text-4); }
.starters .mod:hover .icon { color: var(--brand); }
.start-module .sub { margin-top: 10px; }

.chip.big { width: 46px; height: 46px; margin: 0 auto; border-radius: var(--r); }
.chip.big .icon { width: 24px; height: 24px; }

/* Stop using it. A quiet line at the foot of the module, never a button
   competing with the work.   founder 2026-08-26 */
.module-foot { margin-top: 26px; padding-top: 14px; border-top: 1px solid var(--line-2); }
.module-foot button { margin: 0; padding: 0; background: none; border: 0;
                      color: var(--text-3); font-size: 12.5px; text-decoration: underline; }
.module-foot button:hover { color: var(--late); }
/* **Not on a module he has not used yet.** "Stop using this" under an empty
   screen invites him to switch off something he has not tried - the panel
   above it is asking him to start. `.start-module` is on exactly the screens
   that have nothing in them, and `/modules` is the proper place for the
   decision anyway.   AUDIT-2026-09-02 §9 */
.start-module ~ .module-foot { display: none; }

/* The lapsed members, offered from the dashboard rather than only counted
   there.   AUDIT-2026-09-02 §10B */
.win-back { display: flex; align-items: center; gap: 10px; padding: 11px 16px;
            min-height: 44px; border-top: 1px solid var(--line-2);
            color: var(--text); text-decoration: none; font-size: 13.5px; }
.win-back:hover { background: var(--panel); }
.win-back .n { font-weight: 600; color: var(--soon);
               font-variant-numeric: tabular-nums; }
.win-back .w { flex: 1; min-width: 0; color: var(--text-2); }
.win-back .arrow { width: 15px; height: 15px; color: var(--text-4); flex: none; }
.win-back:hover .arrow { color: var(--brand); }

/* choose what you use -----------------------------------------------------

   Every module, a switch each, one Save. Rows big enough for a thumb, because
   this is done standing up on a phone.   00-common 7                        */

.chooser { max-width: 760px; }
.chooser-group { margin-bottom: 20px; }
.chooser-group h3 {
  font-size: 9.5px; font-weight: 600; text-transform: uppercase;
  letter-spacing: .12em; color: var(--text-4); margin: 0 0 8px;
}

/* The family inside a group - Money, Your day, Your people. Quieter than the
   group above it and louder than a switch, because it is the middle of three
   levels and a heading that matches either one of its neighbours reads as
   belonging to it.   founder 2026-09-08, 00-common 6 */
.chooser-family {
  font-size: 12px; font-weight: 600; color: var(--text-2);
  margin: 14px 0 7px;
}
.chooser-family:first-of-type { margin-top: 0; }

/* **Three across on a desk**, two on a narrow window, one on a phone. It was
   two everywhere, which left seventeen modules running down two long columns
   with a family heading every second row - founder, 2026-09-08: *"the chooser
   page can have 3 cards in one row."* Three fits the widest family exactly and
   turns the screen back into something read at a glance.   00-common 7 */
.switches { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 6px; }

@media (max-width: 980px) {
  .switches { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 620px) {
  .switches { grid-template-columns: 1fr; }
}

.switch-row {
  display: flex; align-items: center; gap: 11px; margin: 0;
  padding: 10px 12px; background: var(--panel);
  border: 1px solid var(--line); border-radius: var(--r);
  font-size: 14px; font-weight: 400; color: var(--text); cursor: pointer;
}
.switch-row:hover { border-color: var(--text-4); }
.switch-row.locked { cursor: default; opacity: .8; }
.switch-row.locked:hover { border-color: var(--line); }

.switch-text { flex: 1; display: flex; flex-direction: column; line-height: 1.3; }
.switch-text b { font-size: 13.5px; font-weight: 500; }
.switch-text small { font-size: 11.5px; color: var(--text-3); }

/* A real checkbox, scaled up: nothing to learn and nothing to script. */
.switch-row input[type=checkbox] {
  flex: 0 0 auto; width: 20px; height: 20px; margin: 0; accent-color: var(--brand);
}

.chooser button { margin-top: 4px; }

/* collapsed to a rail ------------------------------------------------------

   Eleven rows on a full plan, and a laptop carried into a shop is not a wide
   screen. Collapsed it is icons only, and the choice is remembered.

   A convenience: with the script blocked the sidebar simply stays open, which
   is the working state.   00-common 7                                       */

.rail-toggle {
  display: flex; align-items: center; gap: 11px; width: 100%;
  margin: 8px 0 0; padding: 8px 10px; border: 0; border-radius: var(--r-s);
  background: none; color: var(--text-3);
  font: inherit; font-size: 12.5px; font-weight: 500; cursor: pointer;
}
.rail-toggle:hover { background: var(--nav-hover); color: var(--text-2); }
.rail-toggle em { font-style: normal; }
.rail-toggle .icon { transform: rotate(180deg); transition: transform .15s ease; }

@media (min-width: 761px) {
  body.rail .sidebar { width: 58px; flex: 0 0 58px; padding: 14px 8px; }

  /* The words go; the icons and the marks that carry meaning stay. */
  body.rail .nav-link em,
  body.rail .rail-toggle em,
  body.rail .nav-tag,
  body.rail .wordmark { display: none; }

  body.rail .nav-link,
  body.rail .rail-toggle { justify-content: center; padding-left: 0; padding-right: 0; }
  body.rail .side-brand { justify-content: center; padding-left: 0; padding-right: 0; }
  body.rail .rail-toggle .icon { transform: none; }

  /* A count still shows, because it is the reason to look. */
  body.rail .nav-count {
    position: absolute; top: 3px; right: 6px; font-size: 9.5px;
  }
  body.rail .nav-group {
    padding: 12px 0 5px; text-align: center; font-size: 8px; letter-spacing: .06em;
    overflow: hidden; white-space: nowrap;
  }
  body.rail .nav-link.active::before { left: -8px; }
}

/* clients, vendors and contacts ------------------------------------------
   One module drawn three times, so everything here is shared. Only the words
   change between them.   12-parties 3                                      */

/* Two bars a month: what was raised, and what changed hands. A grouped bar
   rather than two charts, because the gap between the pair IS the answer. */
.spark-col.pair { gap: 2px; }
.spark-bar.billed  { background: var(--tone-3, #c7d2fe); }
.spark-bar.settled { background: var(--tone-2); }

/* Who is waiting for money, biggest first. A row, not a table: five names on a
   dashboard is a glance, and the list screen is one tap away. */
.owe-row { display: grid; grid-template-columns: 26px 1fr auto; align-items: center;
           gap: 9px; padding: 7px 0; text-decoration: none; color: inherit;
           border-top: 1px solid var(--line-2, var(--line)); }
.owe-row:first-of-type { border-top: 0; }
/* **A row without an avatar has two columns, not three.** The template is
   `26px 1fr auto` for chip, name, figure - and the rows that carry no chip put
   the name in the twenty-six pixels meant for one. "Jitu Baruah / Member
   since 12 Aug 2025" came out as "Ji... / Mem".   founder 2026-09-07 */
.owe-row:not(:has(.who-chip)) { grid-template-columns: 1fr auto; }
.owe-row:hover .owe-name { text-decoration: underline; }
.owe-row .who-chip { width: 26px; height: 26px; font-size: 10.5px; }
.owe-name { min-width: 0; font-size: 13.5px; font-weight: 500; line-height: 1.25;
            overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.owe-name small { display: block; font-size: 11.5px; font-weight: 400;
                  color: var(--text-4); }
.owe-amount { font-size: 13.5px; font-weight: 600; font-variant-numeric: tabular-nums;
              color: var(--warn-ink, var(--text-1)); }

/* A row on the money tab: what it was, what it came to, and where the balance
   stood after it. The running figure is the point of the table. */
.entry-amount.bill    { color: var(--text-1); }
.entry-amount.payment { color: var(--ok-ink, var(--text-1)); }
.entry-balance { font-variant-numeric: tabular-nums; color: var(--text-3); }

/* The row a panel's own Save sits on. The sheet has `.sheet-do`; a panel that
   is a form on the page had nothing, and the button sat flush against the last
   field.   founder 2026-08-28                                              */
.panel-do { display: flex; justify-content: flex-end; gap: 10px; margin-top: 16px; }
.panel-do button, .panel-do .button { margin-top: 0; }

/* the attendance register ------------------------------------------------
   People down the side, days across, one square a day. The register a shop
   already recognises, and the answer to "nothing to see".
   06-attendance 11, founder 2026-08-29                                     */

table.register { border-collapse: separate; border-spacing: 0; width: 100%;
                 font-size: 13px; background: var(--white); }
table.register th, table.register td { padding: 0; white-space: nowrap;
                                       border-bottom: 1px solid var(--line-2); }
table.register thead th {
  position: sticky; top: 0; z-index: 2; padding: 7px 0; text-align: center;
  background: var(--panel); border-bottom: 1px solid var(--line);
  font-size: 10.5px; font-weight: 600; color: var(--text-3);
}
table.register thead th small { display: block; font-size: 9.5px; font-weight: 500;
                                opacity: .7; }
/* The name column stays put while the days scroll past it. Without it the row
   he is reading loses the man it belongs to.   00-common 5a */
table.register th.who, table.register td.who {
  position: sticky; left: 0; z-index: 3; text-align: left; padding: 0 14px;
  min-width: 178px; background: var(--white);
  border-right: 1px solid var(--line-2);
}
table.register thead th.who { background: var(--panel); }
table.register td.who a { font-weight: 500; color: var(--text); text-decoration: none; }
table.register td.who a:hover { text-decoration: underline; }
table.register td.who small { display: block; font-size: 11.5px; color: var(--text-4); }
table.register td { height: 38px; text-align: center; }
table.register tbody tr:hover td { background: #FBFCFE; }

/* Sunday shaded, so a shop shut on Sunday reads as a shut Sunday and not as a
   week of absences.   06-attendance A-2 */
table.register th.rest, table.register td.rest { background: #FAFBFD; }
table.register th.now { color: var(--text-2); background: var(--brand-bg); }

table.register td.tot { padding: 0 14px; font-weight: 600; text-align: right;
                        font-variant-numeric: tabular-nums;
                        border-left: 1px solid var(--line-2); }
table.register td.tot small { font-weight: 400; color: var(--text-4); }
table.register th.tot { padding: 7px 14px; text-align: right;
                        border-left: 1px solid var(--line-2); }

table.register.wide td { padding: 0 12px; font-variant-numeric: tabular-nums; }
table.register.wide td a { color: var(--text); text-decoration: none; }
table.register.wide td a:hover { text-decoration: underline; }
table.register.wide td small { color: var(--text-4); margin-left: 2px; }
table.register.wide td.none { color: var(--text-4); }

.cellmark {
  display: inline-flex; width: 21px; height: 21px; border-radius: 3px;
  align-items: center; justify-content: center;
  font-size: 10px; font-weight: 700; color: var(--white); text-decoration: none;
}
.cellmark:hover { outline: 2px solid var(--brand); outline-offset: 1px; }
.cellmark.m-present { background: var(--ok); }
.cellmark.m-half    { background: var(--soon); }
.cellmark.m-absent  { background: var(--late); }
.cellmark.m-leave   { background: var(--brand); }
/* A day with no record is empty, never a colour. It is not an absence.  A-2 */
.cellmark.none { background: transparent; box-shadow: inset 0 0 0 1px var(--line-2); }

/* A mark made after the fact carries a dot. The record still settles the
   argument it exists for - it now says when it was written.   06-attendance O-1 */
.cellmark.late { position: relative; }
.cellmark.late::after {
  content: ""; position: absolute; top: 1.5px; right: 1.5px;
  width: 4px; height: 4px; border-radius: 50%;
  background: var(--white); box-shadow: 0 0 0 1px rgba(0,0,0,.18);
}

/* The legend sits above the table it explains — he reads down — so it needs
   room under it, not over it.   founder 2026-08-29 */
.mark-legend { display: flex; gap: 16px; flex-wrap: wrap;
               margin: 14px 0 12px; font-size: 12px; color: var(--text-3); }
.mark-legend span { display: inline-flex; align-items: center; gap: 6px; }
.mark-legend i { width: 12px; height: 12px; border-radius: 3px; display: inline-block;
                 position: relative; }
.mark-legend i.m-present { background: var(--ok); }
.mark-legend i.m-half    { background: var(--soon); }
.mark-legend i.m-absent  { background: var(--late); }
.mark-legend i.m-leave   { background: var(--brand); }
.mark-legend i.none { box-shadow: inset 0 0 0 1px var(--line-2); }
.mark-legend i.late::after { content: ""; position: absolute; top: 1px; right: 1px;
                             width: 4px; height: 4px; border-radius: 50%;
                             background: var(--white); }

/* A form sitting on a chip row — the two dates, or the three pickers.
   **One rule, and every control in it is the same height.** There were three:
   a date input came out at 38px from the base input rule, a select at 32 and a
   button at 30, so the two rows did not line up with each other or with the
   chips beside them.   founder 2026-08-29 */
.count-row form { display: inline-flex; align-items: center; gap: 7px;
                  flex-wrap: wrap; margin-left: 4px; }
/* The `:not` chain is copied from the base input rule above, because that rule
   is six `:not()`s deep — specificity (0,6,1) — and a plain `.count-row form
   input` at (0,1,1) never touched it. The date boxes stayed 100% wide and 38px
   tall, so the row broke into three lines and the Show button sat under them
   looking like a different button.   founder 2026-08-29 */
.count-row form input:not([type=checkbox]):not([type=radio]):not([type=file]):not([type=submit]):not([type=button]):not([type=hidden]),
.count-row form select,
.count-row form button,
.count-row form .button {
  flex: 0 0 auto; margin: 0; width: auto;
  height: 32px; padding: 0 11px; font-size: 12.5px; line-height: 1;
}
.count-row form input[type=date] { width: 152px; }
.count-row form input[type=search] { width: 200px; }

/* Show is filled light grey, so it reads as the thing that acts on the boxes
   beside it rather than as one more empty box on the row.   founder 2026-08-29 */
.count-row form button[type=submit] {
  background: var(--panel); border-color: var(--line); color: var(--text-2);
  font-weight: 600;
}
.count-row form button[type=submit]:hover { background: var(--line-2); color: var(--text); }
.count-row form select { padding-right: 26px; max-width: 210px; }
.count-row form .button { display: inline-flex; align-items: center; gap: 5px; }

/* The four buttons, against a person who has nothing marked yet, on the
   dashboard. The daily habit is done from the front page.   06-attendance 11 */
.dash-col-body .mark-row { display: grid; grid-template-columns: 1fr auto; gap: 10px;
                           align-items: center; padding: 8px 0;
                           border-top: 1px solid var(--line-2); }
.dash-col-body .mark-row:first-of-type { border-top: 0; }
.dash-col-body .mark-row .who { display: flex; align-items: center; gap: 9px;
                                min-width: 0; text-decoration: none; color: inherit; }
.dash-col-body .mark-row .who-chip { width: 26px; height: 26px; font-size: 10.5px; }
.dash-col-body .mark-row .name { min-width: 0; font-size: 13.5px; font-weight: 500;
                                 line-height: 1.2; overflow: hidden;
                                 text-overflow: ellipsis; white-space: nowrap; }
.dash-col-body .mark-row .name small { display: block; font-size: 11.5px;
                                       font-weight: 400; color: var(--text-4); }
.dash-col-body .mark-row .marks { display: flex; gap: 4px; }
.dash-col-body .mark-row .marks form { margin: 0; }
.mk { padding: 5px 9px; font-size: 12px; font-weight: 600; border-radius: var(--r-s);
      border: 1px solid var(--line); background: var(--white); color: var(--text-3); }
.mk:hover { border-color: var(--brand); color: var(--brand); }
.mk.go { border-color: var(--ok); color: var(--ok); }
.mk.go:hover { background: var(--ok); color: var(--white); }

/* A late entry, said beside the row it belongs to on the marking screen. */
.person .sub.late { color: var(--soon); }

/* One tap for the shop where everybody came, at the foot of the column that
   lists who is still unmarked.   06-attendance 11 */
.col-do { padding: 11px 17px 0; }
.col-do form { margin: 0; }
.col-do button { margin: 0; width: 100%; }

/* Four states, four columns, so every row wraps the same way. Ragged wrapping
   - two and two, then three and one - reads as a misprint on a phone.
   founder 2026-08-29 */
@media (max-width: 640px) {
  .person .marks { display: grid; grid-template-columns: repeat(4, 1fr); gap: 5px; }
  .person .marks .mark-btn { width: 100%; padding: 9px 2px; font-size: 11.5px;
                             white-space: nowrap; }
}

/* Four group buttons above the day's table. Twenty labourers all came in, or a
   whole shift did not, and marking them one at a time is twenty taps for one
   fact.   06-attendance 11a */
.group-do { display: flex; align-items: center; gap: 7px; flex-wrap: wrap;
            margin: 0 0 10px; }
.group-do .k { font-size: 11.5px; font-weight: 600; letter-spacing: .055em;
               text-transform: uppercase; color: var(--text-3); margin-right: 3px; }
.group-do .mark-btn { margin: 0; }
/* Quiet until something is ticked. With the script blocked they stay live, so
   picking rows by hand still works.   00-common 5a */
.group-do .mark-btn.off { opacity: .45; }

/* The four buttons inside a row of the table. */
.list-row .marks { display: flex; gap: 4px; justify-content: flex-end;
                   flex-wrap: wrap; }
.list-row .marks .mark-btn { margin: 0; padding: 5px 9px; font-size: 11.5px;
                             font-weight: 600; }
/* `.list-row > *` is a flex row, so a `display: block` child of it is still a
   flex item sitting on the same line — the late note ran alongside the tag and
   was clipped by the column. The cell that carries both stacks them.
   founder 2026-08-29 */
.list-row > .cell.status { flex-direction: column; align-items: flex-start;
                           justify-content: center; gap: 3px; }
.list-row .cell small.late { font-size: 11px; line-height: 1.25; color: var(--soon);
                             white-space: normal; }

/* Today's mark, in the status column of the day table. The four states each
   have a colour everywhere else on the screen; a plain word here was the one
   place they did not.   06-attendance 11a */
.tag-present, .tag-half, .tag-absent, .tag-leave {
  display: inline-block; font-size: 10.5px; font-weight: 600; letter-spacing: .07em;
  text-transform: uppercase; padding: 4px 8px; border-radius: var(--r-s);
  white-space: nowrap;
}
.tag-present { background: var(--ok-bg);    color: var(--ok); }
.tag-half    { background: var(--soon-bg);  color: var(--soon); }
.tag-absent  { background: var(--late-bg);  color: var(--late); }
.tag-leave   { background: var(--brand-bg); color: var(--brand); }

/* Three pickers on the register, in one GET form. Chips a head was four rows
   of them before the table started. Sizing is the shared rule above.
   06-attendance 11c */

/* A day the shop was shut. It belongs to the shop and not to a person, so it
   is a line about the day and never a fifth button on every row.
   06-attendance 11d */
.shut-row { display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
            padding: 10px 14px; margin-bottom: 12px; border-radius: var(--r);
            border: 1px solid var(--line-2); background: var(--white);
            font-size: 13.5px; }
.shut-row.on { background: var(--soon-bg); border-color: #F0DEC2; }
.shut-row form { display: inline-flex; align-items: center; gap: 7px; margin: 0; }
.shut-row form input:not([type=checkbox]):not([type=radio]):not([type=file]):not([type=submit]):not([type=button]):not([type=hidden]) {
  margin: 0; height: 32px; width: 190px; padding: 0 11px; font-size: 12.5px;
}
.shut-row form button { margin: 0; height: 32px; padding: 0 12px; font-size: 12.5px; }
.shut-row .sub { margin: 0; }
.tag-shut { font-size: 10.5px; font-weight: 600; letter-spacing: .07em;
            text-transform: uppercase; padding: 4px 8px; border-radius: var(--r-s);
            background: var(--soon); color: var(--white); white-space: nowrap; }

table.register th.shut, table.register td.shut { background: var(--soon-bg); }
table.register th.shut { color: var(--soon); }
.mark-legend i.shut { background: var(--soon-bg);
                      box-shadow: inset 0 0 0 1px #F0DEC2; }

/* Reasons a shop is shut, one tap each. They are submit buttons carrying their
   own value, not chips that fill a box: a chip that types into an input needs
   a script and nothing on an owner screen may.   00-common 5a */
.reason-chips { display: flex; flex-wrap: wrap; gap: 7px; margin: 2px 0 4px; }
.reason-chips button {
  margin: 0; height: 32px; padding: 0 13px; font-size: 12.5px; font-weight: 500;
  border-radius: 100px; border: 1px solid var(--line);
  background: var(--white); color: var(--text-2);
}
.reason-chips button:hover { border-color: var(--brand); color: var(--brand);
                             background: var(--brand-bg); }

/* A month of days inside a tab. Thirty-one rows would push everything under it
   off the screen, so the box scrolls and the panel keeps its shape. */
.table-scroll.tall { max-height: 420px; overflow-y: auto; }

/* A day the shop was shut, in the fortnight chart. It was drawn as an empty
   box — the same as a day he forgot — which is the one thing the holiday
   record exists to stop.   06-attendance 11d */
.spark-col.shut { background: var(--soon-bg); border-radius: 2px; }
.spark-bar.closed { height: 100%; background: transparent; border-radius: 2px;
                    box-shadow: inset 0 0 0 1px #E4C89A; }
.spark-ends .swatch.shut { background: var(--soon-bg);
                           box-shadow: inset 0 0 0 1px #E4C89A; }

/* The head-actions row carries a form now — a month box on salary, a year box
   on the year screen — so its controls take the same height as the buttons
   beside them.   founder 2026-08-29 */
.head-actions form.picks { display: inline-flex; align-items: center; gap: 7px;
                           margin: 0; }
.head-actions form.picks input:not([type=checkbox]):not([type=radio]):not([type=file]):not([type=submit]):not([type=button]):not([type=hidden]) {
  margin: 0; height: 34px; width: auto; padding: 0 10px; font-size: 12.5px;
}
.head-actions form.picks button { margin: 0; height: 34px; padding: 0 12px;
                                  font-size: 12.5px; background: var(--panel);
                                  border: 1px solid var(--line); color: var(--text-2);
                                  font-weight: 600; }
table.register tfoot td { background: var(--panel); font-weight: 600;
                          border-top: 1px solid var(--line); border-bottom: 0; }
table.register tfoot td.who { background: var(--panel); }

/* ------------------------------------------------------------ sign-in

   The only screens a person sees before they have signed in, and the only ones
   that have to sell rather than serve. Two columns on a laptop, one on a
   phone. Drawn first in docs/mockups/login.html.   00-common 5a, 5b        */

.auth { display: flex; min-height: 100vh; background: var(--white); }

/* The left half is the only place in the product that says what evedeck is.
   An owner reads it before he has bought anything and a staff member reads it
   on his first morning, so it is the panel and not a strip of colour. */
.auth-side {
  flex: 0 0 42%; max-width: 460px; padding: 38px 40px;
  display: flex; flex-direction: column; justify-content: space-between;
  color: #fff; background: var(--brand-deep);
  /* One wash of light, no photograph: an image is a request, and this screen
     loads on 3G before anything of ours is cached. */
  background-image: radial-gradient(120% 90% at 8% 0%, rgba(255,255,255,.14), transparent 55%);
}
.auth-side-top { display: flex; align-items: center; gap: 9px; font-size: 17px; }
.auth-side h2 { margin: 0 0 14px; font-size: 26px; line-height: 1.28;
                font-weight: 600; letter-spacing: -.015em; }
.auth-side p  { margin: 0 0 26px; font-size: 14px; line-height: 1.6;
                color: rgba(255,255,255,.72); }
.auth-points { list-style: none; margin: 0; padding: 0; }
.auth-points li { display: flex; gap: 11px; align-items: flex-start; margin: 11px 0;
                  font-size: 13.5px; line-height: 1.5; color: rgba(255,255,255,.9); }
.auth-points .icon { color: rgba(255,255,255,.62); margin-top: 1px; }
.auth-side-foot { font-size: 12px; color: rgba(255,255,255,.5); }

/* The phone version of the same panel: the mark and the name, nothing else.
   Three selling lines above the fold on a five-inch screen push the button
   below it, and the button is what he came for. */
.auth-strip {
  display: none; align-items: center; gap: 9px;
  padding: 20px 22px; color: #fff; background: var(--brand-deep); font-size: 16px;
}

.auth-main {
  flex: 1; min-width: 0; display: flex; flex-direction: column; justify-content: center;
  padding: 38px 40px calc(38px + env(safe-area-inset-bottom));
}
.auth-box { width: 100%; max-width: 352px; margin: 0 auto; }
.auth-box h1 { margin: 0; font-size: 22px; font-weight: 600; letter-spacing: -.01em; }
.auth-box .sub { margin: 7px 0 0; font-size: 13.5px; line-height: 1.55; color: var(--text-2); }

/* The two banners take an icon here, which the ones inside the app do not: a
   man who has just been refused entry is reading faster than he reads any
   screen after it. */
.auth .error, .auth .notice { display: flex; gap: 9px; align-items: flex-start;
                              line-height: 1.45; }
.auth .error .icon { color: var(--late); margin-top: 1px; }
.auth .notice .icon { color: var(--soon); margin-top: 1px; }
/* Signed out is not a warning. Amber for it tells him something went wrong on
   the way out, which is the opposite of what happened. */
.auth .notice.good { background: var(--ok-bg); color: var(--ok); }
.auth .notice.good .icon { color: var(--ok); }

/* 16px, not the 14px every other box on the site uses. Below 16px Safari on an
   iPhone zooms the page the moment the box is focused, and he is left
   scrolling sideways to find the button he was about to press. */
.auth input:not([type=checkbox]):not([type=radio]):not([type=file]):not([type=submit]):not([type=button]):not([type=hidden]),
.auth select {
  height: 46px; padding: 0 12px; font-size: 16px;
}
/* The form is a block under the sentence that introduces it, not the next
   line of it. Without this the first label sits tight against the sub. */
.auth form { margin-top: 22px; }
.auth label { margin: 16px 0 6px; }
.auth form > label:first-child { margin-top: 0; }

.with-eye { position: relative; }
.with-eye input { padding-right: 46px; }
/* 44px square. A control smaller than a fingertip, on the one screen a man
   uses before he trusts the product, is the wrong place to save space. */
.eye {
  position: absolute; right: 1px; top: 1px; width: 44px; height: 44px;
  margin: 0; padding: 0; border: 0; background: none; border-radius: var(--r-s);
  display: flex; align-items: center; justify-content: center; cursor: pointer;
}
.eye:hover { background: var(--panel); }
.eye .icon { color: var(--text-3); }

/* Full width, and taller than a button inside the app. It is the only thing on
   the screen to press. */
.auth .go {
  display: flex; align-items: center; justify-content: center; gap: 9px;
  width: 100%; height: 46px; margin-top: 22px; padding: 0;
  font-size: 14.5px; font-weight: 500;
}
.auth .go:hover { background: #17539F; text-decoration: none; }
.auth .go:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; }

/* Google's own button, to their guidelines: white ground, their mark at 18px,
   their wording. A blue button with a G in it is the thing people have been
   taught to distrust. */
.auth .go-google { background: var(--white); color: var(--text); border: 1px solid #CDD3DE; }
.auth .go-google:hover { background: var(--panel); border-color: var(--text-4); }

.auth-foot { margin-top: 26px; padding-top: 18px; border-top: 1px solid var(--line-2);
             font-size: 12.5px; line-height: 1.6; color: var(--text-3); }
.auth-help { margin-top: 10px; font-size: 12.5px; color: var(--text-3); }

/* 820px, not the 760px the shell uses: this layout is two columns of text and
   it crowds sooner than a sidebar beside a table does. */
@media (max-width: 820px) {
  .auth { flex-direction: column; }
  .auth-side { display: none; }
  .auth-strip { display: flex; }
  .auth-main { padding: 30px 22px calc(34px + env(safe-area-inset-bottom));
               justify-content: flex-start; }
}

/* The line between the two ways in. Google above it, the password below.
   Without it the Google button reads as the label on the form under it.
   migration 026 */
.auth .or {
  display: flex; align-items: center; gap: 12px; margin: 22px 0 0;
  font-size: 11.5px; font-weight: 600; letter-spacing: .08em;
  text-transform: uppercase; color: var(--text-4);
}
.auth .or::before, .auth .or::after {
  content: ""; flex: 1; height: 1px; background: var(--line);
}

/* ------------------------------------------------------------ users

   The logins the owner hands out. The list is the module — 00-common 5b — so
   what a dashboard would have drawn is three counts in one strip and nothing
   more. Drawn first in docs/mockups/users.html.                            */

.users-strip { display: flex; gap: 10px; flex-wrap: wrap; margin-bottom: 20px; }
.u-tile { flex: 1 1 180px; background: var(--white); border: 1px solid var(--line);
          border-radius: var(--r); padding: 12px 14px; }
.u-tile .k { font-size: 9.5px; font-weight: 600; letter-spacing: .12em;
             text-transform: uppercase; color: var(--text-4); }
.u-tile .v { font-size: 22px; font-weight: 600; margin-top: 3px; letter-spacing: -.02em; }
.u-tile .s { font-size: 12px; color: var(--text-3); margin-top: 5px; line-height: 1.45; }
.u-tile.warn { background: var(--soon-bg); border-color: #F0DFC2; }
.u-tile.warn .v { color: var(--soon); }
.u-tile.full { background: var(--late-bg); border-color: #F3D5D2; }
.u-tile.full .v { color: var(--late); }
.u-tile .of { font-size: 14px; font-weight: 400; color: var(--text-3); }

/* The cap drawn rather than stated: five boxes, filled ones in use. He sees he
   is one away from the ceiling without reading a number, and the ceiling is
   the cleanest upgrade conversation in the product.   00-common 4 */
.pips { display: flex; gap: 3px; margin-top: 8px; flex-wrap: wrap; }
.pip  { width: 14px; height: 6px; border-radius: 1px; background: var(--line); }
.pip.on    { background: var(--brand); }
.pip.owner { background: var(--text-4); }   /* his own login, always the first */


/* one row per login ------------------------------------------------------ */

/* The list is a `.list-table`, the same shape as Employees and Parties, so
   this section only adds what is peculiar to logins.   founder 2026-08-30 */

.list-row.is-owner { background: var(--panel); }
.list-row.empty .cell { color: var(--text-3); justify-content: center; width: 100%; }

/* What they may open, as icons rather than a sentence: six words wrap inside a
   cell and six icons do not, and he recognises them from his own sidebar. */
.list-row .cell.mods { gap: 5px; }
.list-row .cell.mods .m { width: 24px; height: 24px; border-radius: var(--r-s);
                          background: var(--brand-bg); flex: 0 0 auto;
                          display: flex; align-items: center; justify-content: center; }
.list-row .cell.mods .m .icon { width: 14px; height: 14px; color: var(--brand); }
.list-row .cell.mods .more { font-size: 11.5px; color: var(--text-3); font-weight: 600; }
.list-row .cell.mods .quiet-more { font-weight: 400; color: var(--text-4); }
.list-row.gone .cell.mods .m { background: var(--line-2); }
.list-row.gone .cell.mods .m .icon { color: var(--text-3); }

/* The column that earns this screen a second visit. A login handed over and
   never once opened is a job he believes is finished and is not. */
.list-row .cell.seen.never { color: var(--soon); font-weight: 600; }
.list-row .cell.seen.old { color: var(--text-3); }

.tag.owner { display: inline-flex; align-items: center; padding: 2px 8px;
             border-radius: 999px; background: var(--brand-bg); color: var(--brand);
             font-size: 9.5px; font-weight: 600; text-transform: uppercase;
             letter-spacing: .085em; }

/* one tick per module ---------------------------------------------------- */

/* A pressable tile rather than a checkbox in a list: this is the screen he
   uses standing next to the person, on a phone, in a shop.

   `[NOTE]` **It is `pick-tile`, not `pick`.** It was `pick`, which is also the
   name of the narrow first column in every list table - and the rule that
   hides the native box so this tile can draw its own was hiding the real
   checkboxes in Employees, Attendance and Clients as well. They were there,
   sized, and completely transparent. Founder, 2026-09-01: *"the checkbox in
   table are not getting displayed."* Two components, two names.   00-common 5a */
.picks { display: grid; grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
         gap: 8px; margin-top: 8px; }
.pick-tile { display: flex; align-items: center; gap: 9px; padding: 10px 11px;
             border: 1px solid var(--line); border-radius: var(--r-s);
             background: var(--white); cursor: pointer; font-size: 13.5px; }
.pick-tile input { position: absolute; opacity: 0; width: 0; height: 0; }
.pick-tile em { font-style: normal; min-width: 0; overflow: hidden;
                text-overflow: ellipsis; white-space: nowrap; }
.pick-tile .box { width: 16px; height: 16px; border: 1.5px solid var(--line);
                  border-radius: 2px; flex: 0 0 auto; display: flex;
                  align-items: center; justify-content: center; }
.pick-tile .box svg { width: 11px; height: 11px; stroke: #fff; stroke-width: 2.6;
                      fill: none; opacity: 0; }
.pick-tile:has(input:checked) { border-color: var(--brand); background: var(--brand-bg);
                                font-weight: 600; }
.pick-tile:has(input:checked) .box { background: var(--brand); border-color: var(--brand); }
.pick-tile:has(input:checked) .box svg { opacity: 1; }
.pick-tile:has(input:checked) .icon { color: var(--brand); }
.pick-tile:has(input:focus-visible) { outline: 2px solid var(--brand); outline-offset: 2px; }

/* the password, shown once ----------------------------------------------- */

.pw-shout { display: flex; align-items: center; gap: 18px; flex-wrap: wrap;
            border-left: 3px solid var(--ok); }
.pw-k { font-size: 12px; font-weight: 600; letter-spacing: .06em;
        text-transform: uppercase; color: var(--text-3); }
.pw-v { display: inline-block; margin-top: 5px; font-size: 22px; font-weight: 600;
        letter-spacing: .06em; font-family: ui-monospace, Menlo, Consolas, monospace; }
.pw-shout .sub { margin: 0; flex: 1 1 220px; }

/* the hand-over ---------------------------------------------------------- */

/* He is standing next to a man with a cheap Android. Reading out an address
   and a password is the moment the hand-over fails; a camera does not
   mistype.   founder 2026-08-30 */
.hand { display: flex; gap: 18px; flex-wrap: wrap; align-items: flex-start; }
.hand .qr { flex: 0 0 auto; border: 1px solid var(--line); border-radius: var(--r-s);
            background: #fff; padding: 4px; }
.hand .hand-txt { flex: 1 1 260px; min-width: 0; }
.hand h3 { margin: 0 0 5px; font-size: 14px; font-weight: 600; }
.hand .sub { margin: 0 0 11px; }
.linkbox { display: flex; align-items: center; gap: 8px; border: 1px solid var(--line);
           border-radius: var(--r-s); background: var(--panel); padding: 7px 8px 7px 11px;
           font-size: 13px; }
.linkbox span { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis;
                white-space: nowrap; }
.linkbox .button { margin-top: 0; padding: 6px 11px; font-size: 12.5px; }

@media (max-width: 560px) {
  .rows .row { flex-wrap: wrap; row-gap: 8px; }
  .rows .who { flex: 1 1 100%; }
  .rows .seen { flex: 1 1 auto; text-align: right; }
  .rows .chev { display: none; }
}


/* ------------------------------------------------------------ enquiries

   What people send from his website. The list is the module — 00-common 5b —
   so this section adds only what is peculiar to an enquiry: the two states,
   and the message riding in the row. Drawn first in
   docs/mockups/enquiries.html, direction A.                                */

.tag-new, .tag-done {
  display: inline-block; padding: 2px 8px; border-radius: 999px;
  font-size: 10.5px; font-weight: 600; letter-spacing: .04em; white-space: nowrap;
}
.tag-new  { background: var(--brand-bg); color: var(--brand); }
.tag-done { background: var(--ok-bg);    color: var(--ok); }

/* The message itself, one line of it, because the row is where he decides
   whether to ring back. Truncated rather than wrapped: four rows of three
   lines each is a screen he scrolls instead of scans. */
.list-row .cell .msg { display: block; min-width: 0; overflow: hidden;
                       text-overflow: ellipsis; white-space: nowrap; }
/* On a phone the row is a card and the message is the reason he opened the
   module, so it is read rather than trimmed. Two lines, then it is cut - a
   list is still a list, and the whole of it is one tap away.
   AUDIT-2026-09-02 §2·06 */
@media (max-width: 620px) {
  .list-row .cell .msg {
    white-space: normal; text-overflow: clip;
    display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2;
  }
}
.list-row .cell .msg.none { color: var(--text-4); }

/* What will go out on WhatsApp, shown before he taps, because it leaves from
   his own number and he is the one saying it.   00-common 8 */
.draft { margin-top: 16px; border: 1px solid var(--line); border-radius: var(--r);
         overflow: hidden; background: var(--white); }
.draft .draft-h { display: flex; align-items: center; gap: 7px; padding: 8px 12px;
                  background: var(--ok-bg); color: var(--wa);
                  font-size: 12px; font-weight: 600; }
.draft .draft-h .icon { width: 15px; height: 15px; color: var(--wa); }
.draft p { margin: 0; padding: 12px; font-size: 13.5px; line-height: 1.6;
           color: var(--text-2); }

/* His words, as he typed them. A quotation, not a form field. */
.said { border: 1px solid var(--line); border-radius: var(--r); background: var(--panel);
        padding: 15px 16px; font-size: 14.5px; line-height: 1.65; color: var(--text);
        white-space: pre-wrap; overflow-wrap: anywhere; }
.said.none { color: var(--text-3); font-size: 13.5px; }
.said-meta { display: flex; gap: 22px; flex-wrap: wrap; margin-top: 14px; }
.said-meta > div { min-width: 118px; }
.said-meta .k { font-size: 9.5px; font-weight: 600; letter-spacing: .12em;
                text-transform: uppercase; color: var(--text-4); }
.said-meta .v { font-size: 14px; margin-top: 3px; }
.said-acts { display: flex; gap: 9px; flex-wrap: wrap; margin-top: 18px; }
.said-acts form { margin: 0; }
.said-acts .button, .said-acts button { margin-top: 0; }

/* A tighter row, for a list he scans rather than studies. An enquiry is a name,
   a line and a time - none of it is a figure he compares against the row above,
   so the air between them is only scrolling.   founder 2026-08-31 */
.list-table.tight .list-head > span { padding: 6px 10px; font-size: 10.5px; }
.list-table.tight .list-row > *     { padding: 6px 10px; }
.list-table.tight .list-row .who    { gap: 0; }
.list-table.tight .list-row .who b     { font-size: 13px; line-height: 1.25; }
.list-table.tight .list-row .who small { margin-top: 0; font-size: 11px; line-height: 1.3; }
.list-table.tight .list-row .cell   { font-size: 12px; }
/* A tag inside a tight row is a label, not a badge to be seen from across the
   shop. It sits on one line with the figures beside it. */
.list-table.tight [class^="tag-"] { padding: 1px 7px; font-size: 10px; }
.list-table.tight .list-row .cell > .button.small { padding: 4px 8px; font-size: 12px; }


/* ------------------------------------------------------------ members

   Who is due to renew. The module a gym owner opens every morning, so the two
   states have to be readable at arm's length and tell each other apart at a
   glance: red is late, amber is soon, and the brand colour is neither.
   03-membership 1, docs/mockups/membership.html                            */

.bar-fill.t-late { background: var(--late); }
.bar-fill.t-soon { background: var(--soon); }

.tag-late, .tag-soon, .tag-later, .tag-lapsed {
  display: inline-block; padding: 2px 8px; border-radius: 999px;
  font-size: 10.5px; font-weight: 600; letter-spacing: .04em; white-space: nowrap;
}
.tag-late   { background: var(--late-bg); color: var(--late); }
.tag-soon   { background: var(--soon-bg); color: var(--soon); }
.tag-later  { background: var(--ok-bg);   color: var(--ok); }
.tag-lapsed { background: var(--panel);   color: var(--text-3); }

/* The row itself carries the state, not only the tag inside it. A pile he
   scans on a phone is read down the left edge.   00-common 5a */
.list-row.overdue { box-shadow: inset 3px 0 0 var(--late); }
.list-row.due     { box-shadow: inset 3px 0 0 var(--soon); }
.list-row.lapsed  { background: #FCFCFD; }
.list-row.lapsed .who b { color: var(--text-2); }

/* What will be sent, before he taps. It leaves from his own number and he is
   the one saying it, so the words are on the screen first.   00-common 8 */
.member-ends { margin-top: 14px; background: var(--brand-bg); border-radius: var(--r);
               padding: 11px 13px; font-size: 13.5px; color: var(--brand); font-weight: 600; }
.member-hist { margin-top: 8px; border-top: 1px solid var(--line-2); }
.member-hist .line { display: flex; gap: 12px; align-items: baseline; padding: 8px 0;
                     border-bottom: 1px solid var(--line-2); font-size: 13px; }
.member-hist .line:last-child { border-bottom: 0; }
.member-hist .d { flex: 0 0 118px; color: var(--text-3); font-variant-numeric: tabular-nums; }
.member-hist .m { margin-left: auto; font-weight: 600; font-variant-numeric: tabular-nums; }


/* A button inside a list cell. The cell clips (nowrap, ellipsis), so a button
   wider than its column is a button hanging outside the table rather than one
   that shrinks. It keeps its own padding and loses the label's overflow.
   founder 2026-08-31 */
/* `button, .button` carries margin-top: 18px, which inside a list row drops
   the control below the line it belongs to.   founder 2026-08-31 */
.list-row .cell > .button { margin: 0; min-width: 0; max-width: 100%;
                            white-space: nowrap; }
.list-row .cell > .button.small { padding: 5px 9px; }
/* The Ask and Reply buttons in a row. On a desktop table they are 27px and
   that is right - the pointer is exact and the row is 44px tall. On a phone
   they are the most-tapped things in the product and the thumb is not.
   AUDIT-2026-09-02 §16 */
@media (max-width: 620px) {
  .list-row .cell > .button.small,
  .list-table.tight .list-row .cell > .button.small {
    min-height: 40px; padding: 8px 14px; font-size: 13px;
  }
  .list-row .cell > form > .button.small,
  .list-row .cell > form > button.small { min-height: 40px; padding: 8px 14px; }
}
/* The icon in WhatsApp's own green, so the row says which app the tap opens
   without a word for it.   founder 2026-08-31 */
.icon.wa { color: var(--wa); }
.button:hover .icon.wa { color: var(--wa); }


/* Renewed against lost, two bars a month. "Renewed nine, lost two" is the
   shape of a month and neither figure means much alone, so they are drawn in
   the same column and never as two charts.   03-membership 1 */
.spark-bar.lost { background: var(--late-bg); box-shadow: inset 0 0 0 1px #F3D5D2; }
.spark-ends .swatch.t-paid { background: var(--tone-2); }
.spark-ends .swatch.t-unknown { background: var(--late-bg); box-shadow: inset 0 0 0 1px #F3D5D2; }

/* The waiting-longest row and the ones he has already asked. Same component as
   the parties dashboard uses for who owes him, so it is one thing to learn. */
.owe-amount.late { color: var(--late); font-weight: 600; }


/* A heading inside a list, so overdue and due-soon are one table and not two.
   The split is drawn here, as rows he can press, and nowhere else on the
   screen.   founder 2026-08-31, 00-common 5b */
.list-group {
  grid-column: 1 / -1; padding: 6px 10px; background: var(--panel);
  border-bottom: 1px solid var(--line-2);
  font-size: 10.5px; font-weight: 600; letter-spacing: .07em;
  text-transform: uppercase; color: var(--text-3);
}
.list-group .n { color: var(--text-2); }

/* The pager under a list sits with the page count between the two moves, and
   the list already carries its own margin above it. */
.list-table + .pager, .table-scroll + .pager { margin: 14px 0 0; }
/* A button inside a list cell may be the only child of a form. The form must
   not add a line of its own.   founder 2026-08-31 */
.list-row .cell > form { margin: 0; }
.list-row .cell > form > button { margin: 0; white-space: nowrap; }


/* ------------------------------------------------------------ sales

   One number a day, and the picture of it. The box is the first thing on the
   screen and has to be enterable one-handed while pulling the shutter down, so
   it is big, it is above everything, and nothing stands between it and the
   keyboard.   08-sales 4, docs/mockups/sales.html                          */

.sale-enter {
  display: flex; gap: 20px; flex-wrap: wrap; align-items: flex-end;
  background: var(--panel); border: 1px solid var(--line); border-radius: var(--r);
  padding: 16px 18px; margin-bottom: 16px;
}
.sale-enter form { margin: 0; }
.sale-enter .when { font-size: 11.5px; font-weight: 600; letter-spacing: .1em;
                    text-transform: uppercase; color: var(--text-4); margin-bottom: 8px; }
.sale-enter .box { display: flex; align-items: center; gap: 9px; }
.sale-enter .cur { font-size: 24px; font-weight: 600; color: var(--text-3); }
.sale-enter input {
  width: 210px; font: inherit; font-size: 28px; font-weight: 600; padding: 5px 12px;
  border: 1.5px solid var(--line); border-radius: var(--r); background: var(--white);
  font-variant-numeric: tabular-nums;
}
.sale-enter input:read-only { background: var(--line-2); color: var(--text-2); }
.sale-enter button { margin-top: 0; }
/* Yesterday's figure, which catches 42,000 typed for 4,200 more cheaply than
   any validation rule.   08-sales 4 */
.sale-enter .under { margin-top: 8px; font-size: 12.5px; color: var(--text-3); }
.sale-enter .side { margin-left: auto; }
.sale-enter .side .button { margin-top: 0; }
@media (max-width: 560px) {
  .sale-enter { flex-direction: column; align-items: stretch; }
  .sale-enter input { width: 100%; }
  .sale-enter .side { margin-left: 0; }
  .sale-enter .side .button { width: 100%; }
}

/* the chart: one component, four groupings   founder 2026-08-31 */
.chart-card { background: var(--white); border: 1px solid var(--line);
              border-radius: var(--r); padding: 15px 17px 17px; margin-bottom: 16px; }
.chart-head { display: flex; align-items: baseline; gap: 12px; flex-wrap: wrap; }
.chart-head h2 { flex: 1; margin: 0; font-size: 14.5px; font-weight: 600; }
.chart-sub { margin: 4px 0 14px; font-size: 12.5px; color: var(--text-3); }
.tabs { display: flex; border: 1px solid var(--line); border-radius: var(--r-s);
        overflow: hidden; }
.tabs a { padding: 6px 13px; font-size: 12.5px; color: var(--text-2);
          border-right: 1px solid var(--line); background: var(--white); }
.tabs a:last-child { border-right: 0; }
.tabs a:hover { background: var(--panel); text-decoration: none; }
.tabs a.on { background: var(--brand); color: #fff; font-weight: 600; }

.bars.big { display: flex; align-items: flex-end; gap: 5px; height: 148px; }
.bars.big .col { flex: 1; min-width: 0; height: 100%; position: relative;
                 display: flex; flex-direction: column; justify-content: flex-end; }
.bars.big .col .fill { display: block; background: var(--brand-bg);
                       border-top: 2px solid var(--brand); border-radius: 2px 2px 0 0; }
.bars.big .col.best .fill { background: var(--brand); }
/* A day he skipped is hatched, not zero, and never joined up with a line: a
   gap is a gap.   00-common 9, 08-sales 4 */
.bars.big .col.none .fill {
  background: repeating-linear-gradient(45deg, #F4F6FA, #F4F6FA 3px, #EAEEF5 3px, #EAEEF5 6px);
  border-top: 1px dashed var(--line);
}
/* The same month a year before, drawn behind. His own two figures side by
   side, never a projection.   08-sales 6 */
.bars.big .col .ghost { position: absolute; left: 14%; right: 14%; bottom: 0;
                        border-top: 2px dashed var(--brand-soft, #BFD5EF); }
/* The axis under the bars. Most of its columns are deliberately blank - thirty
   labels on a 360px chart is six pixels each - so a label is placed over the
   middle of its own bar and left free to spill across the empty ones either
   side. It used to be text inside a six-pixel box with `overflow: hidden`,
   which cut "7 Aug" down to "7" and was the whole of the complaint.
   founder 2026-09-05 */
.bars-x { display: flex; gap: 5px; margin-top: 7px; height: 25px; }
.bars-x span { flex: 1; min-width: 0; position: relative; }
.bars-x b { position: absolute; top: 0; left: 50%; transform: translateX(-50%);
            font-size: 10.5px; font-weight: 400; color: var(--text-4);
            white-space: nowrap; }
/* The two ends spill into the card's own padding rather than being pinned to
   the edges - pinned, they leaned towards their neighbour and the first and
   last pair touched at 320px. */
.bars-x span:first-child b { transform: translateX(-30%); }
.bars-x span:last-child b { transform: translateX(-70%); }
.bars-x span.now b { color: var(--brand); font-weight: 600; }
/* Where one month becomes the next, on its own line - beside the number it is
   wider than the bar it belongs to.   founder 2026-09-05 */
.bars-x i { display: block; font-size: 9px; font-style: normal; line-height: 1.35; }
/* Twelve months on a 320px screen is twenty-two pixels a bar, and "Aug" and
   "Sep" were touching. */
@media (max-width: 380px) { .bars-x b { font-size: 9.5px; } }
.chart-foot { display: flex; justify-content: space-between; align-items: center;
              gap: 10px; flex-wrap: wrap; margin-top: 12px; padding-top: 11px;
              border-top: 1px solid var(--line-2); font-size: 12.5px; color: var(--text-3); }
.chart-foot .legend { display: inline-flex; align-items: center; gap: 6px; margin-right: 12px; }
.chart-foot .swatch { width: 11px; height: 11px; border-radius: 2px;
                      background: var(--brand-bg); border-top: 2px solid var(--brand); }
.chart-foot .swatch.none {
  background: repeating-linear-gradient(45deg, #F4F6FA, #F4F6FA 3px, #EAEEF5 3px, #EAEEF5 6px);
  border-top: 1px dashed var(--line);
}
.chart-foot .swatch.ghost { width: 14px; height: 2px; background: none;
                            border: 0; border-top: 2px dashed var(--brand-soft, #BFD5EF); }

.sale-cols { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 16px; }
@media (max-width: 900px) { .sale-cols { grid-template-columns: minmax(0, 1fr); } }
.sale-cols .panel { background: var(--white); }
.sale-cols .list-table { margin-top: 4px; }

/* what sold, ranked. A bar under the row rather than beside it, so a long item
   name never squeezes the figure.   08-sales 4 */
.rank { display: grid; gap: 11px; margin-top: 6px; }
.rank-row { display: grid; grid-template-columns: 1fr auto; gap: 10px; align-items: baseline;
            font-size: 13.5px; }
.rank-row .what { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.rank-row .m { font-weight: 600; font-variant-numeric: tabular-nums; }
.rank-row .track { grid-column: 1 / -1; height: 6px; background: var(--line-2);
                   border-radius: 3px; overflow: hidden; margin-top: -5px; }
.rank-row .track i { display: block; height: 100%; background: var(--brand-soft, #BFD5EF); }

/* a day nobody entered, in the fortnight list */
.list-row.skipped { background: #FCFCFD; }
.list-row.skipped .cell { color: var(--text-4); }
.list-row .cell.money { justify-content: flex-end; font-weight: 600;
                        font-variant-numeric: tabular-nums; }
.list-row.is-total { background: var(--brand-bg); font-weight: 600; }

/* adding an item line: one row, and it stays one row on a phone */
.line-add { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 12px; }
.line-add input { flex: 1 1 130px; min-width: 0; margin: 0; }
/* **By name, not by position.** These were `:nth-child(2)` and `(3)`, and the
   counter's own screen carries a hidden `day` field as the first child - so
   every rule landed one box to the right. *What sold*, the widest thing on the
   row, was given the 90px meant for the quantity and read "What sc" on a
   phone, while the amount took the space.   AUDIT-2026-09-02 §3·B-27 */
.line-add input[name="qty"] { flex: 0 1 90px; }
.line-add input[name="amount"] { flex: 0 1 120px; }
.line-add button { margin-top: 0; }

.up { color: var(--ok); font-weight: 600; }
.down { color: var(--late); font-weight: 600; }


/* A filter row is a grid of controls, and it only packs several to a line if
   it is given the width to do it. As a flex child with no grow it sized to its
   content, so every select sat on a line of its own — a column of dropdowns
   down the left of the screen.   founder 2026-08-31 */
.count-row > .picks, .count-row > form.picks { flex: 1 1 auto; min-width: 0; margin-top: 0; }
.count-row > .picks > * { min-width: 0; }
.count-row > .picks button, .count-row > .picks .button { margin-top: 0; }
/* A date box is a fixed width in every browser, so it does not need a whole
   grid cell of its own. */
.count-row > .picks input[type=date] { max-width: 100%; }


/* ------------------------------------------------- logging a sale

   A shop boy between two customers has one hand and four seconds. Tap what
   sold, or type one line — and the page comes back to the same box with the
   list under it. No second page and nothing to save afterwards.
   founder 2026-08-31                                                        */

.panel.quick { background: var(--white); }
.panel.quick .head-row { align-items: baseline; gap: 10px; }
.panel.quick .head-row .sub { margin: 0; }

/* The things he sells most often, at the price he last charged. One tap is one
   sale; tapping twice is two. */
.quick-taps { display: flex; flex-wrap: wrap; gap: 8px; margin: 10px 0 4px; }
.quick-taps form { margin: 0; }
.quick-taps button {
  margin: 0; display: flex; flex-direction: column; align-items: flex-start; gap: 1px;
  padding: 8px 13px; min-height: 46px; line-height: 1.25; text-align: left;
}
.quick-taps button b { font-size: 13.5px; font-weight: 600; color: var(--text); }
.quick-taps button span { font-size: 12px; color: var(--text-3);
                          font-variant-numeric: tabular-nums; }
.quick-taps button:hover { border-color: var(--brand); }
.quick-taps button:hover b { color: var(--brand); }

/* Today's list, right under the box that made it. */
.quick-list { border: 1px solid var(--line); border-radius: var(--r); overflow: hidden;
              margin: 6px 0 8px; }
.quick-row { display: flex; align-items: center; gap: 10px; padding: 7px 11px;
             border-bottom: 1px solid var(--line-2); font-size: 13.5px; }
.quick-row:last-child { border-bottom: 0; }
.quick-row .what { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis;
                   white-space: nowrap; }
.quick-row .what small { color: var(--text-3); font-size: 12px; }
.quick-row .m { font-weight: 600; font-variant-numeric: tabular-nums; }
.quick-row form { margin: 0; }
.quick-row form button { margin: 0; padding: 4px 8px; }

@media (max-width: 560px) {
  /* On a phone the tap targets take the width in twos: 44px is Apple's floor
     and 48dp is Android's, and a counter hand is not aiming carefully. */
  .quick-taps { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .quick-taps button { width: 100%; min-height: 52px; }
  .line-add input { flex: 1 1 100%; }
  .line-add button { width: 100%; }
}

/* The counter's running total. Big enough to read across a counter, and the
   first thing on the screen he opens forty times a day.   founder 2026-08-31 */
.log-total { background: var(--brand-bg); border: 1px solid #C9DCF5; border-radius: var(--r);
             padding: 16px 18px; margin-bottom: 16px; }
.log-total .v { font-size: 34px; font-weight: 600; letter-spacing: -.03em; line-height: 1.05;
                color: var(--brand); font-variant-numeric: tabular-nums; }
.log-total .k { margin-top: 4px; font-size: 13px; color: var(--text-2); }
.log-total .s { margin-top: 2px; font-size: 12px; color: var(--text-3); }

/* Which day he is entering. Yesterday's takings get typed the next morning, so
   the screen moves rather than sending him elsewhere to fix it.
   founder 2026-08-31 */
.day-strip { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-bottom: 14px; }
.day-strip form { display: flex; gap: 8px; margin: 0; }
.day-strip .button, .day-strip button { margin-top: 0; }
.day-strip input[type=date] { margin: 0; }


/* ------------------------------------------------------------ the listing

   What is on his website this week. A row is a thumbnail, what it is, the
   price, and the controls — because the screen *is* the page his customers
   see, and he reads it the way they do.   09-public-extras 6              */

.lot-rows { display: grid; gap: 10px; margin-top: 6px; }
.lot-row {
  display: flex; align-items: center; gap: 14px; flex-wrap: wrap;
  background: var(--white); border: 1px solid var(--line); border-radius: var(--r);
  padding: 11px 13px;
}
.lot-row.gone { background: #FCFCFD; }
.lot-row.gone .lot-what b { color: var(--text-2); }
.lot-thumb { flex: 0 0 auto; width: 96px; height: 72px; object-fit: cover;
             border-radius: var(--r-s); background: var(--panel); }
.lot-thumb.none { display: flex; align-items: center; justify-content: center; }
.lot-row.gone .lot-thumb { filter: grayscale(1); opacity: .7; }
.lot-what { flex: 1 1 220px; min-width: 0; }
.lot-what b { display: block; font-size: 14.5px; font-weight: 600; }
.lot-what small { display: block; margin-top: 2px; font-size: 12.5px; color: var(--text-3); }
/* His own pairs, read as a row of small facts rather than a table nobody
   asked for.   09-public-extras 6 */
.lot-pairs { display: flex; flex-wrap: wrap; gap: 4px 12px; margin-top: 6px; }
.lot-pairs span { font-size: 12px; color: var(--text-3); }
.lot-pairs b { font-weight: 600; color: var(--text-2); }
.lot-money { flex: 0 0 auto; font-size: 15px; font-weight: 600;
             font-variant-numeric: tabular-nums; }
.lot-money small { display: block; font-size: 11.5px; font-weight: 400; color: var(--text-3); }
.lot-money .none { font-size: 13px; font-weight: 400; color: var(--text-4); }
.lot-acts { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.lot-acts form { margin: 0; }
.lot-acts button, .lot-acts .button { margin-top: 0; }

@media (max-width: 620px) {
  .lot-thumb { width: 100%; height: 150px; }
  .lot-money { width: 100%; }
  .lot-acts { width: 100%; }
}


/* A wrong address, or a screen that broke. Quiet, in the product's own hand,
   and never a stack trace.   00-common 5a, founder 2026-08-31 */
.error-page { text-align: center; max-width: 460px; }
.error-code { font-size: 13px; font-weight: 600; letter-spacing: .16em; color: var(--text-4); }
.error-page h1 { margin: 10px 0 8px; font-size: 21px; font-weight: 600; }
.error-page p { margin: 0; font-size: 14px; color: var(--text-2); line-height: 1.6; }
.error-acts { display: flex; gap: 10px; justify-content: center; flex-wrap: wrap; margin-top: 22px; }
.error-acts .button { margin-top: 0; }

/* Getting paid. The square is printed and taped to a counter, so it is given
   a white ground of its own whatever the panel behind it is doing, and it does
   not shrink below the size a phone camera finds quickly.   app/upi.py */
.pay-qr { display: flex; gap: 18px; align-items: flex-start; flex-wrap: wrap;
          margin-top: 18px; padding-top: 16px; border-top: 1px solid var(--line-2); }
.pay-qr > div { flex: 1 1 240px; }
.pay-qr h3 { margin: 0 0 4px; font-size: 14px; font-weight: 600; }
.pay-qr .qr { flex: 0 0 auto; width: 180px; height: 180px; background: var(--white);
              border: 1px solid var(--line); border-radius: var(--r); padding: 10px; }
@media print {
  /* One square, big, and nothing else on the paper. */
  body * { visibility: hidden; }
  .pay-qr, .pay-qr * { visibility: visible; }
  .pay-qr { position: absolute; inset: 0; border: 0; }
  .pay-qr .qr { width: 320px; height: 320px; }
}

/* ------------------------------------------------ the day, above the cards */

/* He opens this at closing time. The strip says what day it is and what
   happened in it, which is the thing he is holding in his head - the cards
   underneath are the detail of that sentence.   founder 2026-09-01 */
.day {
  display: flex; flex-wrap: wrap; margin-bottom: 14px;
  background: var(--panel); border: 1px solid var(--line);
  border-radius: var(--r); overflow: hidden;
}
.day-when {
  padding: 13px 16px; min-width: 180px; background: var(--white);
  border-right: 1px solid var(--line);
  display: flex; flex-direction: column; justify-content: center; gap: 1px;
}
.day-when .k { font-size: 10.5px; font-weight: 600; letter-spacing: .1em;
               text-transform: uppercase; color: var(--text-3); }
.day-when .v { font-family: var(--font-wordmark); font-size: 18px; font-weight: 500;
               letter-spacing: -.01em; }
.day-figs { display: flex; flex: 1; flex-wrap: wrap; }
.day-fig { flex: 1 1 150px; padding: 11px 16px; display: flex; flex-direction: column;
           gap: 1px; border-right: 1px solid var(--line); }
.day-fig:last-child { border-right: 0; }
.day-fig .k { font-size: 11.5px; font-weight: 500; color: var(--text-2); }
.day-fig .v { font-family: var(--font-wordmark); font-size: 29px; font-weight: 500;
              letter-spacing: -.025em; line-height: 1.12;
              font-variant-numeric: tabular-nums; }
.day-fig .s { font-size: 11.5px; color: var(--text-3); }
/* They are links now - the two modules they stand for have no card of their
   own on this screen any more. */
a.day-fig { color: inherit; text-decoration: none; }
a.day-fig:hover { background: var(--white); }
a.day-fig:hover .k { color: var(--brand); }
/* A figure he has to do something about looks different from one he reads. */
.day-fig.act .v, .day-fig.act .s { color: var(--soon); }

/* What wants him tonight, one line each. It replaced a count - "Pending 5" -
   which is a number he cannot act on standing over four things he cannot see.
   A whole line is the tap target, because he is holding the phone in one hand.
   AUDIT-2026-09-02 §11 */
.day-needs { flex: 1 1 100%; border-top: 1px solid var(--line); background: var(--white); }
.day-needs > .k { padding: 10px 16px 2px; font-size: 11px; font-weight: 600;
                  letter-spacing: .07em; text-transform: uppercase; color: var(--text-3); }
.need { display: flex; align-items: baseline; gap: 10px; padding: 9px 16px;
        min-height: 44px; color: var(--text); text-decoration: none;
        border-top: 1px solid var(--line-2); }
.day-needs > .k + .need { border-top: 0; }
.need:hover { background: var(--panel); }
.need .n { font-weight: 600; font-variant-numeric: tabular-nums; color: var(--soon);
           flex: 0 0 auto; min-width: 34px; }
/* Renewals names a thing where the others count one, and a title is not a
   figure: it gets the room a name needs rather than the 34px a number does.
   05-renewals 5 */
.need.names .n { min-width: 0; }
.need.names .w { flex: 0 1 auto; margin-left: auto; padding-left: 10px;
                 text-align: right; }
.need .w { flex: 1; min-width: 0; font-size: 13.5px; color: var(--text-2); }
.need .arrow { width: 15px; height: 15px; color: var(--text-4); flex: none;
               align-self: center; }
.need:hover .arrow { color: var(--brand); }

/* The line that makes opening it at eight worth doing on a quiet day. Without
   it, a screen with nothing on it reads the same as one that has not loaded. */
/* Finish your website. On the screen only while something is outstanding, and
   gone the moment it is not.   AUDIT-2026-09-02 §10A */
/* The date controls on the counter's screen, folded away while he is on today.
   AUDIT-2026-09-02 §4 */
.day-strip-fold > summary { display: inline-flex; align-items: center; gap: 7px;
                            min-height: 40px; padding: 6px 2px; cursor: pointer;
                            font-size: 13px; color: var(--text-3); }
.day-strip-fold > summary:hover { color: var(--brand); }
.day-strip-fold[open] > summary { color: var(--text-2); }

/* His own page, on the screen that edits it.   AUDIT-2026-09-02 §10A */
.site-peek { display: block; position: relative; margin-bottom: 16px;
             border: 1px solid var(--line); border-radius: var(--r);
             overflow: hidden; background: var(--panel); text-decoration: none; }
.site-peek img { display: block; width: 100%; height: 150px; object-fit: cover; }
.site-peek-bar { display: flex; align-items: center; gap: 9px; padding: 10px 14px;
                 min-height: 44px; background: var(--white);
                 border-top: 1px solid var(--line); font-size: 13px;
                 font-weight: 500; color: var(--brand); }
.site-peek-bar .icon { width: 15px; height: 15px; color: var(--brand); flex: none; }
.site-peek:hover { border-color: var(--brand); }

.setup { border: 1px solid var(--brand-line, var(--line)); border-radius: var(--r);
         background: var(--white); margin-bottom: 16px; overflow: hidden; }
.setup-head { padding: 13px 16px 4px; }
.setup-head h2 { font-size: 15px; font-weight: 600; margin: 0; }
.setup-head .sub { margin: 3px 0 9px; font-size: 12.5px; color: var(--text-3); }
.setup-steps { border-top: 1px solid var(--line-2); }
.step { display: flex; align-items: center; gap: 11px; padding: 10px 16px;
        min-height: 44px; border-bottom: 1px solid var(--line-2);
        color: var(--text); text-decoration: none; font-size: 13.5px; }
.step:last-child { border-bottom: 0; }
.step:hover { background: var(--panel); }
.step .tick { flex: 0 0 auto; width: 20px; height: 20px; border-radius: 50%;
              border: 1.5px solid var(--line); display: flex;
              align-items: center; justify-content: center; }
.step .tick .icon { width: 12px; height: 12px; color: #fff; }
.step.is-done .tick { background: var(--ok); border-color: var(--ok); }
.step.is-done .w { color: var(--text-3); text-decoration: line-through; }
.step .w { flex: 1; min-width: 0; }
.step .arrow { width: 15px; height: 15px; color: var(--text-4); flex: none; }
.step:hover .arrow { color: var(--brand); }
.setup-see { display: flex; align-items: center; gap: 9px; padding: 11px 16px;
             min-height: 44px; border-top: 1px solid var(--line);
             background: var(--panel); font-size: 13px; font-weight: 500;
             color: var(--brand); text-decoration: none; }
.setup-see .icon { width: 15px; height: 15px; }
.setup-see .addr { margin-left: auto; font-size: 12px; font-weight: 400;
                   color: var(--text-3); overflow: hidden;
                   text-overflow: ellipsis; white-space: nowrap; }

.day-clear { flex: 1 1 100%; margin: 0; padding: 9px 16px 11px;
             border-top: 1px solid var(--line); background: var(--white);
             font-size: 12.5px; color: var(--text-3); }

/* ---------------------------------------------------- what wants him tonight

   One line on the dashboard and a bell in the header. It was the whole list
   five or six rows tall, and with the setup panel under it not one module card
   was above the fold - which is the opposite of what a card grid is for.
   founder 2026-09-05 */
.day-needs { flex: 1 1 100%; border-top: 1px solid var(--line);
             background: var(--white); }
.day-needs > summary { display: flex; align-items: center; gap: 10px;
                       padding: 10px 16px; min-height: 44px; cursor: pointer;
                       font-size: 13px; color: var(--text-2); list-style: none; }
.day-needs > summary::-webkit-details-marker { display: none; }
.day-needs > summary:hover { background: var(--panel); }
.day-needs > summary > .icon { width: 16px; height: 16px; color: var(--soon);
                               flex: none; }
.day-needs .lede { flex: 1; min-width: 0; }
.day-needs .lede b { color: var(--soon); font-variant-numeric: tabular-nums; }
.day-needs > summary > .chev { width: 15px; height: 15px; color: var(--text-4);
                               flex: none; transition: transform .15s; }
.day-needs[open] > summary > .chev { transform: rotate(180deg); }
.needs-all { border-top: 1px solid var(--line-2); }

/* Six things will not fit a 360px header, and the one that was being squeezed
   to nothing was the shop's own name. Suggest moves into the account menu on a
   phone rather than going away.   founder 2026-09-05 */
.account .menu a.on-phone { display: none; }
@media (max-width: 620px) {
  .header > .say { display: none; }
  .account .menu a.on-phone { display: flex; }
  /* And the name gets enough room to be a name - but only as much as the
     screen can spare. A fixed floor pushed the account menu off the edge of a
     320px phone. */
  .shop-switch { min-width: clamp(64px, 20vw, 140px); }
}

/* The bell. The same `details` as the account menu, so it opens with no script
   and closes the same way.   00-common 5a */
.account.bell > summary { gap: 7px; }
.account.bell .avatar { background: var(--soon-bg); color: var(--soon); }
.account.bell .n { font-size: 13px; font-weight: 600; color: var(--soon);
                   font-variant-numeric: tabular-nums; padding-right: 3px; }
.account.bell .menu a { gap: 10px; }
.account.bell .need-n { font-weight: 600; color: var(--soon); flex: 0 0 auto;
                        min-width: 26px; font-variant-numeric: tabular-nums; }
.account.bell .need-w { flex: 1; min-width: 0; }

/* **Neither of these may eat the screen.** On a phone everything scrolls and a
   tall block costs nothing; on a desktop the needs list and the setup panel
   between them filled the whole fold, so not one module card was visible
   without scrolling - which is the opposite of what a card grid is for.
   00-common 5

   Two columns each from 900px up, and both blocks come in at about half the
   height. Below 900 they stack, which is right: one column of full-width rows
   is what a thumb wants.

   `[FIXED 2026-09-07]` **The grid goes on the list, not on the `details`.** It
   was written on 2026-09-04 when `.day-needs` was a plain div full of rows; on
   the 5th it became a `<details>` and the rule stayed - so the **summary** took
   the first column and came out half the width of the strip, with the chevron
   in the middle of the row and a hand's width of nothing after it. The rows it
   was meant for live in `.needs-all`.   founder 2026-09-07 */
@media (min-width: 900px) {
  .needs-all { display: grid; grid-template-columns: 1fr 1fr; }
  /* The rule between them, so two columns do not read as one list. */
  .needs-all > .need:nth-child(even) { border-left: 1px solid var(--line-2); }

  .setup-steps { display: grid; grid-template-columns: 1fr 1fr; }
  .setup-steps > .step:nth-child(even) { border-left: 1px solid var(--line-2); }
}

/* The first evening. An empty strip is where a dashboard either invites him in
   or teaches him there is nothing here.   00-common 5 */
.day-first { flex: 1; display: flex; align-items: center; gap: 12px;
             flex-wrap: wrap; padding: 12px 16px; }
.day-first p { margin: 0; flex: 1 1 280px; font-size: 14px; color: var(--text-2); }
.day-first b { color: var(--text); font-weight: 600; }
.day-first .button { margin-top: 0; }

.section-head { margin: 0 0 9px; font-size: 11px; font-weight: 600;
                letter-spacing: .1em; text-transform: uppercase; color: var(--text-3); }

/* ------------------------------------------------------ what a card is about

   `[CHANGED 2026-09-08]` **The card is white and only the chip is coloured.**
   Founder, looking at seventeen tinted tiles: *"i am not liking the dashboard
   so many colors. can we just try white cards with light grey bg?"*

   Every card carried its module's tint across its whole face, which at
   seventeen modules is seventeen washes of colour on one screen - and the
   figure, the one thing he opens the dashboard to read, was sitting on a
   different colour in every tile. The colour is still there and still tells
   one module from another; it is just back down to the thirty-pixel chip it
   started on, on a white card, on a grey ground.

   `[NOTE]` **Grey chips were tried in the same hour and rejected.** With the
   chip neutral the tiles are identical and he has to read every label to find
   the one he wants, which is the thing the colour was for. */
.card.m-money .chip     { background: var(--money-bg);     color: var(--money); }
.card.m-people .chip    { background: var(--people-bg);    color: var(--people); }
.card.m-customers .chip { background: var(--customers-bg); color: var(--customers); }
.card.m-front .chip     { background: var(--front-bg);     color: var(--front); }
.card.m-upkeep .chip    { background: var(--upkeep-bg);    color: var(--upkeep); }
.card.m-day .chip       { background: var(--day-bg);       color: var(--day); }
.card.m-admin .chip     { background: var(--admin-bg);     color: var(--admin); }

/* **And the card takes the family, at 70% of the tint.**
   `[CHANGED 2026-09-07]` The chip alone is thirty-two pixels: measured over all
   forty-two screens it carried 0.004% of the product's pixels, which is a
   decision nobody can see. The card is the whole body of the dashboard, so the
   tint on the card is what makes the grid read as six families rather than as
   ten grey tiles.

   **Seventy per cent and not a hundred, and the number is not a taste.** At
   full strength `--text-4` falls to 4.33:1 on these grounds and `--ok` to 4.43
   - under the 4.5 the audit's §16 work exists to hold, on the screens of the
   man this product is sold to. 77% is the most that holds; 70 leaves a margin
   for a tint somebody adds later.

   The chip goes white and keeps the family in its ink, or a chip the same
   colour as the card it sits on is not a chip.   founder 2026-09-07 */

.card.mod-website     .chip { background: var(--m-website-chip); color: var(--m-website); }
.card.mod-gallery     .chip { background: var(--m-gallery-chip); color: var(--m-gallery); }
.card.mod-enquiries   .chip { background: var(--m-enquiries-chip); color: var(--m-enquiries); }
.card.mod-listings    .chip { background: var(--m-listings-chip); color: var(--m-listings); }
.card.mod-calendar    .chip { background: var(--m-calendar-chip); color: var(--m-calendar); }
.card.mod-todo        .chip { background: var(--m-todo-chip); color: var(--m-todo); }
.card.mod-sales       .chip { background: var(--m-sales-chip); color: var(--m-sales); }
.card.mod-expenses    .chip { background: var(--m-expenses-chip); color: var(--m-expenses); }
.card.mod-employees   .chip { background: var(--m-employees-chip); color: var(--m-employees); }
.card.mod-attendance  .chip { background: var(--m-attendance-chip); color: var(--m-attendance); }
.card.mod-salary      .chip { background: var(--m-salary-chip); color: var(--m-salary); }
.card.mod-clients     .chip { background: var(--m-clients-chip); color: var(--m-clients); }
.card.mod-contacts    .chip { background: var(--m-contacts-chip); color: var(--m-contacts); }
.card.mod-membership  .chip { background: var(--m-membership-chip); color: var(--m-membership); }
.card.mod-vendors     .chip { background: var(--m-vendors-chip); color: var(--m-vendors); }
.card.mod-assets      .chip { background: var(--m-assets-chip); color: var(--m-assets); }
.card.mod-renewals    .chip { background: var(--m-renewals-chip); color: var(--m-renewals); }
.card.mod-users       .chip { background: var(--m-users-chip); color: var(--m-users); }
/* `[CHANGED 2026-09-07]` These two used to be repeated here to beat the family
   rules above. They are gone: a chip that changes colour when the module has
   work in it is a chip that stops naming the module, and it was taking the
   whole family palette off the screen with it. The state is on the figure. */

/* Switched on and never used, in one tile instead of ten identical dashes. */
.card.unstarted {
  grid-column: 1 / -1; min-height: 0; padding: 14px;
  background: none; border-style: dashed;
  display: flex; flex-direction: column; gap: 10px;
}
.card.unstarted .head { display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap; }
.card.unstarted h3 { margin: 0; font-size: 13px; font-weight: 600; }
.card.unstarted .why { font-size: 12.5px; color: var(--text-3); }
.card.unstarted .chips { display: flex; flex-wrap: wrap; gap: 7px; }
.card.unstarted .mod {
  display: inline-flex; align-items: center; gap: 7px; padding: 7px 10px;
  background: var(--white); border: 1px solid var(--line);
  border-radius: var(--r-s); font-size: 12.5px; color: var(--text-2);
}
.card.unstarted .mod:hover { border-color: var(--text-4); color: var(--text);
                             text-decoration: none; }
.card.unstarted .mod .icon { width: 15px; height: 15px; }
.mod.f-money .icon     { color: var(--money); }
.mod.f-people .icon    { color: var(--people); }
.mod.f-customers .icon { color: var(--customers); }
.mod.f-front .icon     { color: var(--front); }
.mod.f-upkeep .icon    { color: var(--upkeep); }
.mod.f-admin .icon     { color: var(--admin); }

@media (max-width: 620px) {
  /* On a phone the date is a line of its own above the figures, and the
     figures stack two up. */
  .day-when { width: 100%; border-right: 0; border-bottom: 1px solid var(--line); }
  .day-fig { flex-basis: 50%; }
}

/* ------------------------------------------------------- the suggestion box */

/* A button in the header, quiet, beside his own name. It is not a module and
   it does not carry a number: it is the way to say something.   00-common 8c */
.say { display: inline-flex; align-items: center; gap: 7px; padding: 6px 10px;
       border: 1px solid var(--line); border-radius: var(--r-s);
       font-size: 13px; color: var(--text-2); background: var(--white); }
.say:hover { border-color: var(--text-4); color: var(--text); text-decoration: none; }
.say em { font-style: normal; }
@media (max-width: 720px) { .say em { display: none; } }

.say-box textarea { width: 100%; min-height: 110px; resize: vertical; }
.say-box label { display: block; margin-bottom: 5px; font-size: 12.5px;
                 font-weight: 500; color: var(--text-2); }
.say-do { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.say-do .button { margin-top: 0; }

.say-head { margin: 18px 0 8px; padding-top: 14px; border-top: 1px solid var(--line-2);
            font-size: 11px; font-weight: 600; letter-spacing: .09em;
            text-transform: uppercase; color: var(--text-3); }
.say-list { display: flex; flex-direction: column; gap: 6px; }
.say-row { display: flex; align-items: baseline; gap: 10px; padding: 9px 11px;
           background: var(--panel); border: 1px solid var(--line-2);
           border-radius: var(--r-s); color: var(--text-2); font-size: 13.5px; }
.say-row:hover { border-color: var(--text-4); color: var(--text); text-decoration: none; }
.say-row.read { cursor: default; }
.say-row .body { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis;
                 white-space: nowrap; }
.say-row .who { flex: 0 0 auto; font-size: 11.5px; color: var(--text-3); }
.say-row:hover .who { color: var(--brand); }
.say-row.read:hover .who { color: var(--text-3); }
.say-all { display: inline-block; margin-top: 14px; font-size: 13px; color: var(--brand); }

/* The page behind it. Five rows in a dialog is a reminder; a page is a record. */
.say-page { display: flex; flex-direction: column; gap: 10px; }
.say-item { padding: 13px 15px; background: var(--panel);
            border: 1px solid var(--line); border-radius: var(--r); }
.say-item p { margin: 0; white-space: pre-wrap; }
.say-item .meta { display: flex; gap: 12px; flex-wrap: wrap; margin-top: 7px;
                  font-size: 12px; color: var(--text-3); }

/* What his offer strip will look like, in the shop's own brand colour, before
   he saves it. A coloured band across a page is the one thing he cannot
   picture from a text box.   09-public-extras 8 */
.offer-preview {
  margin-top: 14px; padding: 10px 14px; border-radius: var(--r-s);
  background: var(--brand); color: var(--white);
  font-size: 14px; font-weight: 500; text-align: center;
}

/* The two printed things. The code sits beside the words on a laptop and under
   them on a phone.   09-public-extras 7 */
.qr-lay { display: flex; gap: 22px; align-items: flex-start; flex-wrap: wrap; }
.qr-lay > div { flex: 1 1 260px; }
.qr-lay h2 { margin: 0 0 4px; font-size: 15px; font-weight: 600; }
.qr-lay .qr { flex: 0 0 auto; width: 190px; height: 190px; background: var(--white);
              border: 1px solid var(--line); border-radius: var(--r); padding: 10px; }
.qr-do { display: flex; gap: 8px; flex-wrap: wrap; margin: 14px 0 8px; }
.qr-do .button { margin-top: 0; }

/* The four looks, picked as tiles rather than as a dropdown: he is choosing
   the shape of his own page and a list of four words says nothing about
   which.   01-website 3 */
.looks { display: grid; gap: 8px; margin-top: 10px;
         grid-template-columns: repeat(auto-fill, minmax(210px, 1fr)); }
.look-pick { display: flex; flex-direction: column; gap: 3px; cursor: pointer;
             padding: 12px 14px; background: var(--white);
             border: 1px solid var(--line); border-radius: var(--r-s); }
.look-pick input { position: absolute; opacity: 0; width: 0; height: 0; }
.look-name { font-size: 14px; font-weight: 600; }
.look-why { font-size: 12.5px; color: var(--text-3); line-height: 1.4; }
.look-pick:hover { border-color: var(--text-4); }
.look-pick:has(input:checked) { border-color: var(--brand); background: var(--brand-bg); }
.look-pick:has(input:checked) .look-name { color: var(--brand); }
.look-pick:has(input:focus-visible) { outline: 2px solid var(--brand); outline-offset: 2px; }

/* A row he can read and remove, on the sections screen. The words on the left,
   the one control on the right, and the line between them doing the work of a
   table without being one. */
.row-line { display: flex; align-items: flex-start; gap: 14px; padding: 12px 0;
            border-bottom: 1px solid var(--line-2); }
.row-line > div { flex: 1; min-width: 0; }
.row-line b { font-size: 14.5px; font-weight: 600; }
.row-line form { margin: 0; }
.row-line .drop { margin: 0; }
.stack { display: flex; flex-direction: column; gap: 4px; margin-top: 16px; }
.stack label { font-size: 12.5px; font-weight: 500; color: var(--text-2); margin-top: 8px; }
.stack button { align-self: flex-start; }


/* ==========================================================================
   ONE SIZE FOR A CONTROL ON A PHONE

   Measured across forty-two owner screens at 360px on 2026-09-07. The same
   thing - a control you press - came out at:

       36px  Add member            38px  Download          40px  Ask, Reply
       32px  a filter select       32px  Show              26px  a count chip
       24px  Move to gallery       21px  a register cell   20px  a row arrow
       17px  a name in a row       16px  the way back

   Eleven heights. The founder, looking at the app on his phone: *"I can see no
   uniformity anywhere."* He is right, and this is the whole of it - not colour,
   not spacing, but that nothing his thumb lands on is the same size as anything
   else.

   Forty-four is the number. It is a fingertip, it is what `AUDIT-2026-09-02`
   §4 already asked for on the home screen, and applying it everywhere is what
   makes a page stop reading as a website with small blue words on it.
   founder 2026-09-07
   ========================================================================== */

@media (max-width: 760px) {

  /* Anything he presses, and anything he types into. */
  button,
  .button,
  a.button,
  select,
  input[type="text"], input[type="tel"], input[type="date"], input[type="url"],
  input[type="email"], input[type="password"], input[type="search"],
  input[type="number"], input[type="month"], input[type="time"],
  input[type="file"],
  input[type="submit"] {
    min-height: 44px;
  }
  /* `time` was missing until 2026-09-08 - no screen had one until the calendar,
     and a list of types is a list somebody has to remember to add to. It came
     out at 39px, which is what measuring every screen is for.

     `file` was missing since the day photographs were added, and nobody noticed
     for the same reason. The list is now checked against what the templates
     actually use - `test_every_kind_of_field_a_template_uses_is_thumb_sized` -
     so it cannot fall behind again.   LESSONS 1 */

  /* A row of filter pills, and the tabs over a chart. Chips were 26px and
     tabs 30px - both are the control the screen is steered with. */
  .count-chip { min-height: 44px; padding: 10px 14px; }
  .tabs a, .tabs button { min-height: 44px; display: inline-flex;
                          align-items: center; justify-content: center; }
  /* The starter chips on Renewals, and the module chips on the home screen.
     `.starters .mod` sets 40 and is two selectors deep, so this says which. */
  a.mod, .chips a, .starters .mod { min-height: 44px; }

  /* The way back off a screen was sixteen pixels of grey text and the most
     pressed control in the whole app after the cards. The negative margin
     keeps the heading where it was: it grows a tap area, not a gap. */
  .back {
    display: inline-flex; align-items: center;
    min-height: 44px; margin: -10px 0 -6px; padding-right: 8px;
    font-size: 13.5px;
  }

  /* A name inside a list row, and the arrow at the end of one. Both were the
     size of the text and nothing more - seventeen pixels on the salary year,
     which is a name he is meant to press. `inline-flex` and not `inline`,
     because a min-height does nothing at all to an inline box. */
  .list-table a, .list-row a, td a {
    display: inline-flex; align-items: center; min-height: 44px;
  }
  /* The whole row read as one link. It came out at 43 - one pixel short of a
     fingertip, which is the sort of thing only a measurement finds.
     `.list-row > .who` is the rule that shapes it, so this matches it. */
  .list-row > .who, a.who { min-height: 44px; }
  .list-row .go, a.go { min-width: 44px; min-height: 44px;
                        display: inline-flex; align-items: center;
                        justify-content: center; }

  /* His web address in the page head - the way to look at his own site, and
     the thing he shows a customer. It was 32px. */
  .site-pill { min-height: 44px; }

  /* One cell of the attendance register. Height only - forty-four wide would
     turn a month into a mile of sideways scrolling. */
  .cellmark { min-height: 44px; }

  /* The small button in a row - Ask, Reply, Move to gallery, the × on a photo.
     `.small` earned its name on a desktop; on a phone there is no such thing
     as a small control. */
  .button.small, button.small,
  .gallery button, .gallery .drop, .photos button, .photos .drop {
    min-height: 44px;
  }

  /* And the same again, more loudly, because `.list-row .cell > form > button`
     is three levels deep and beats the rule above it. Ask and Reply are the
     two most pressed buttons in the product and both were 40px. */
  .list-row .cell > .button.small,
  .list-row .cell > form > .button.small,
  .list-row .cell > form > button.small,
  .list-table.tight .list-row .cell > .button.small {
    min-height: 44px;
  }
}


/* ==========================================================================
   AND IT SHOULD READ AS AN APP, NOT A PAGE

   Founder, 2026-09-07: *"the page should feel like mobile app."* Three things
   were making it read as a website that had been squeezed:

   1. **His own business name was cut in half.** The switcher is a `select` in
      a pill, and the pill was capped at 30vw - 64px at 360 - so "Demo Fitness"
      rendered as "Demo Fitn" with the dropdown arrow sitting on top of the
      last letters.
   2. **The action he came to press was the third button down**, left-aligned
      and the same size as Download next to it. An app puts the one thing
      first and makes it the width of the thumb.
   3. **Six controls before any data.** Three dropdowns, Show, a search box and
      Search - half a screen of chrome above the first member.
   ========================================================================== */

@media (max-width: 760px) {

  /* 1. His business, readable. The cake goes into the account menu with
        Suggest, which is fifty-two pixels back for the name. */
  .header > .cake { display: none; }
  .shop-switch {
    flex: 1 1 auto; max-width: none;
    min-width: clamp(96px, 38vw, 220px);
  }

  .shop-switch select {
    flex: 1; min-width: 0; max-width: none;
    text-overflow: ellipsis; padding-right: 2px;
  }

  /* 2. The primary action first, and the width of a thumb. The secondary ones
        share the row under it rather than pushing it down the screen.
        `wrap` matters: without it a screen with two actions kept them on one
        line and a screen with three did not, so the same button was full width
        on one screen and half of one on the next. */
  .page-head { flex-direction: column; align-items: stretch; gap: 12px; }
  .head-actions { width: 100%; gap: 8px; flex-wrap: wrap; }
  .head-actions > * { flex: 1 1 0; margin-top: 0; justify-content: center; }
  .head-actions > .button:not(.quiet) { flex-basis: 100%; order: -1; }

  /* 3. The filter row as a grid rather than a wrapping line, and Show across
        the bottom of it. `height: 32px` a thousand lines up is why these were
        fingernails; `auto` lets the 44 above do its work. */
  .count-row { align-items: stretch; }
  .count-row > .k { flex-basis: 100%; }
  .count-row form,
  .count-row .picks {
    display: grid; grid-template-columns: 1fr 1fr; gap: 8px;
    width: 100%; margin-left: 0;
  }
  .count-row form input:not([type=checkbox]):not([type=radio]):not([type=file]):not([type=submit]):not([type=button]):not([type=hidden]),
  .count-row form select,
  .count-row form button,
  .count-row form .button {
    height: auto; width: 100%; padding: 10px 12px; font-size: 13.5px;
  }
  .count-row form button,
  .count-row form .button { grid-column: 1 / -1; }

  /* The label beside a little bar. Ninety-eight fixed pixels fits "Members"
     and cuts "Employee records" and "Nothing recorded"; on a phone the label
     takes what it needs and the bar takes the rest. */
  .bar-row { grid-template-columns: minmax(0, 1.25fr) 1fr 32px; }

  /* A chart's own heading, above its tabs rather than beside them. Sharing
     the line turned "The last 30 days" into three words on three lines with a
     row of tabs pressed against them. */
  .chart-head { flex-direction: column; align-items: stretch; gap: 10px; }
  .chart-head h2 { flex: none; }
  .chart-head .tabs { width: 100%; }
  .chart-head .tabs a, .chart-head .tabs button { flex: 1; }

  /* The search box gets its own line and the button sits beside it, because a
     search box two thumbs wide is the one control on the screen he types in. */
  .list-tools { gap: 8px; }
  .list-tools form { flex: 1 1 100%; gap: 8px; }
  .list-tools input[type=search] { flex: 1 1 auto; padding: 11px; }
  .list-tools button, .list-tools .button { margin-top: 0; padding: 10px 14px; }
}


/* On the narrowest phone still sold, the shop icon inside the pill is twenty
   pixels the name needs more. The pill is already a shop; the icon says it
   twice. Kept apart from the block above deliberately: an `@media` opened
   inside another one closes it, and for an hour on 2026-09-07 every rule after
   it silently applied below 340px only. */
@media (max-width: 340px) {
  .shop-switch > .icon { display: none; }
}


/* ==========================================================================
   THE BAR UNDER HIS THUMB

   A hamburger is not how a phone is navigated. Until 2026-09-07 the only route
   to anything on a phone was a drawer behind a menu button - two taps to reach
   a screen, and nothing on the screen saying what else there was. Founder:
   *"the page should feel like mobile app."*

   Phones only. On a laptop the sidebar is already on the screen and a second
   navigation would be a second thing to keep in step.
   ========================================================================== */

.thumb-bar { display: none; }

@media (max-width: 760px) {
  .thumb-bar {
    position: fixed; left: 0; right: 0; bottom: 0; z-index: 45;
    display: flex; align-items: stretch;
    background: var(--white);
    border-top: 1px solid var(--line);
    /* The home indicator on an iPhone sits over the bottom of the screen, and
       in a standalone app there is no browser chrome to keep it clear. */
    padding-bottom: env(safe-area-inset-bottom, 0);
  }
  .thumb-bar a {
    flex: 1 1 0; min-width: 0;
    display: flex; flex-direction: column; align-items: center;
    justify-content: center; gap: 3px;
    min-height: 56px; padding: 7px 2px 8px;
    color: var(--text-3); text-decoration: none;
  }
  .thumb-bar a:hover { text-decoration: none; }
  .thumb-bar .icon { width: 21px; height: 21px; }
  .thumb-bar .w {
    max-width: 100%; font-size: 10.5px; font-weight: 500; line-height: 1.1;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  }

  /* Where he is. Colour and weight, not a filled block: five filled blocks is
     a toolbar, and one lit word is a place. */
  .thumb-bar a.on { color: var(--brand); }
  .thumb-bar a.on .w { font-weight: 600; }

  /* The same dot the sidebar row carries, so a module that wants him says so
     without being opened. */
  .thumb-bar .spot { position: relative; display: inline-flex; }
  .thumb-bar .dot {
    position: absolute; top: -1px; right: -4px;
    width: 7px; height: 7px; border-radius: 50%;
    background: var(--late, #C0392B);
  }

  /* Room for it. Without this the last row of every list sits under the bar
     and he cannot reach the thing he scrolled to. */
  .content { padding-bottom: calc(76px + env(safe-area-inset-bottom, 0)); }

  /* The drawer and its scrim go over the bar, not under it: a half-covered
     bar behind a dimmed screen reads as a broken one. */
  .sidebar { z-index: 60; }
  body.nav-open .scrim { z-index: 55; }
}


/* ---------------------------------------------- how far into a module he may go

   Two-level staff permission. The tick says he may open Sales; the two buttons
   under it say whether he may only add to it, or change what is already there
   and download the book.   AUDIT-2026-09-02 §2·03, 14-users

   **The two only appear once the module is ticked**, with `:has()` and no
   script - fourteen modules each showing a choice he has not made is a wall,
   and a choice that means nothing until the tick is on should not be asked
   for.   00-common 5a */
/* `[RENAMED 2026-09-09]` It was `.pick`, which is also the narrow first column
   of every list table - the exact collision `test_the_checkbox_in_a_list_is_a_
   visible_checkbox` exists to forbid, walked into a second time because that
   test's own pattern had been corrupted and matched nothing.   LESSONS 1b */
.may { display: grid; gap: 0; }
.may > .two { display: none; }
.may:has(> .pick-tile input:checked) > .two {
  display: flex; gap: 6px; padding: 0 4px 4px;
}
.may > .two label {
  flex: 1; display: inline-flex; align-items: center; justify-content: center;
  gap: 6px; min-height: 40px; padding: 6px 9px; margin: 0;
  background: var(--white); border: 1px solid var(--line);
  border-radius: var(--r-s); font-size: 12px; color: var(--text-2);
  cursor: pointer; text-align: center;
}
.may > .two input { margin: 0; flex: none; }
.may > .two label:has(input:checked) {
  border-color: var(--brand); background: var(--brand-bg); color: var(--brand);
  font-weight: 500;
}

@media (max-width: 760px) {
  .may > .two label { min-height: 44px; }
}


/* ------------------------------------------------- the data promise, and the

   A list of plain sentences - what we do with his business and what we do not.
   Never a bullet of features: these are statements he is being asked to
   believe, and they read as sentences.   16-your-data, founder 2026-09-08 */
.plain-list { margin: 10px 0 0; padding: 0; list-style: none; }
.plain-list li {
  position: relative; padding: 7px 0 7px 22px;
  font-size: 13.5px; line-height: 1.5; color: var(--text-2);
  border-top: 1px solid var(--line-2);
}
.plain-list li:first-child { border-top: 0; }
.plain-list li::before {
  content: ""; position: absolute; left: 4px; top: 15px;
  width: 6px; height: 6px; border-radius: 50%; background: var(--text-4);
}

/* The one control in this product that destroys something and cannot be
   undone. It does not look like the others. */
button.danger, .button.danger, a[role=button].danger {
  background: var(--late); color: var(--white); border-color: var(--late);
}
button.danger:hover, .button.danger:hover, a[role=button].danger:hover {
  background: #A33024; color: var(--white); text-decoration: none;
}
.panel.danger .plain-list li { color: var(--text-2); }


/* ------------------------------------------- the calendar and the to-do list

   One line of his own day: a tick, what it is, and a way to remove it. Not a
   list row - a list row is a record with fields, and this is a sentence.
   15-calendar */
.dnote { display: flex; align-items: center; gap: 10px; padding: 9px 0;
         border-top: 1px solid var(--line-2); }
.dnote:first-of-type { border-top: 0; }
.dnote .what { flex: 1; min-width: 0; }
.dnote .what b { display: block; font-size: 14px; font-weight: 500;
                 line-height: 1.35; }
.dnote .what small { display: block; margin-top: 1px; font-size: 12px;
                     color: var(--text-3); }
.dnote form { margin: 0; }

/* The tick. Big enough for a thumb, quiet enough not to be the loudest thing
   on a screen full of them. */
.dnote .tick button {
  width: 26px; height: 26px; min-height: 26px; padding: 0; margin: 0;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--white); border: 1.5px solid var(--line);
  border-radius: 50%; cursor: pointer;
}
.dnote .tick svg { width: 14px; height: 14px; fill: none; stroke: var(--text-4);
                   stroke-width: 2.6; stroke-linecap: round; stroke-linejoin: round; }
.dnote .tick button:hover { border-color: var(--ok); }
.dnote .tick button:hover svg { stroke: var(--ok); }

.dnote .drop { background: none; border: 0; margin: 0; padding: 0 4px;
               min-height: 0; font-size: 19px; line-height: 1;
               color: var(--text-4); cursor: pointer; }
.dnote .drop:hover { color: var(--late); background: none; }

/* Finished, and **still in its place**. Founder, 2026-09-08: *"why do you remove
   if ticked. let it stay green filled tick circle."* The circle fills solid
   green with a white tick - a pale wash read as a hover state, not as done -
   and the line stays readable. He is checking he did it, not reading a
   gravestone.   15-calendar 4b */
.dnote.gone .what b { color: var(--text-3); text-decoration: line-through; }
.dnote.gone .tick button { border-color: var(--ok); background: var(--ok); }
.dnote.gone .tick svg { stroke: var(--white); stroke-width: 3; }
.dnote.gone .tick button:hover { background: var(--ok-bg); }
.dnote.gone .tick button:hover svg { stroke: var(--ok); }

/* The note under a line. *Meeting postponed.* Folded away behind a pencil,
   because six open text boxes on one day is not a day he can read. Open by
   itself when there is already a note in it.   founder 2026-09-08 */
.dnote { flex-wrap: wrap; }
.dnote .what .jotted {
  display: block; margin-top: 2px; font-size: 12px; font-style: normal;
  color: var(--text-2);
}
.dnote.gone .what .jotted { color: var(--text-3); }

.jot { flex: none; }
.jot > summary {
  display: inline-flex; align-items: center; justify-content: center;
  width: 28px; height: 28px; padding: 0;
  border-radius: var(--r-s); color: var(--text-4); cursor: pointer;
  list-style: none;                       /* no disclosure triangle beside it */
}
.jot > summary::-webkit-details-marker { display: none; }
.jot > summary:hover { color: var(--brand); background: var(--panel); }
.jot > summary .icon { width: 15px; height: 15px; }
/* Open, it needs the whole width - so it leaves the row and takes one of its
   own underneath. `order` puts it after the delete button rather than between
   the words and it. */
/* Open, it needs the whole width, so it leaves the row and takes one of its own
   underneath - indented to line up with the words it belongs to, not with the
   tick. It is **closed by default even when there is a note**: the note is
   already read on the line above it, and a row that opens a form by itself is
   a row that shows the same words twice. */
/* Indented with padding, not margin. `flex-basis: 100%` is the whole row, and
   a left margin on top of it overflows by exactly the indent - which put the
   Save button past the right edge on a wide screen. */
.jot[open] { flex: 1 0 100%; order: 9; margin: 0 0 8px;
             padding-left: 36px; box-sizing: border-box; }
.jot[open] > summary { color: var(--brand); }
.jot-form label { display: block; margin: 0 0 5px; font-size: 12px;
                  color: var(--text-3); }
/* One line: the box and a small Save. Not `.add-line` - that is the *add*
   control at the foot of a screen and is sized to be the thing he came to
   press. This one sits inside a row and must not shout over it. */
.jot-line { display: flex; gap: 6px; align-items: stretch; }
.jot-line input { flex: 1 1 auto; min-width: 0; margin: 0; }
.jot-line button { flex: none; margin: 0; padding: 0 12px; font-size: 13px; }

@media (max-width: 760px) {
  /* One size for a control on a phone - `00-common` §6. */
  .jot > summary { width: 44px; height: 44px; }
  /* Full width on a phone. Indenting it to line up with the words costs
     fifty-four of three hundred and sixty pixels, and the Save button was
     pushed off the edge and cut in half. The label ties it to its row. */
  .jot[open] { padding-left: 0; }
  /* The tick is the thing he presses on this screen, so it gets a fingertip
     like everything else. `00-common` §6 - one size for a control on a phone. */
  .dnote { padding: 6px 0; }
  .dnote .tick button { width: 44px; height: 44px; min-height: 44px; }
  .dnote .tick svg { width: 17px; height: 17px; }
  .dnote .drop { min-width: 44px; min-height: 44px; }
}


/* ------------------------------------------------------------- the month

   Seven columns and six rows, drawn as a table on the server. A calendar
   library is a blank box without a script and two hundred kilobytes before it
   draws a day; `00-common` §5a and `16-your-data` both rule it out.
   15-calendar, founder 2026-09-08

   **Sized for a 320px phone first.** Seven columns of a 288px content box is
   forty-one pixels each, which holds two digits and three dots and nothing
   else - so the day's entries are read underneath, never inside the cell. */
.month-head { display: flex; align-items: center; gap: 8px; margin-bottom: 10px; }
.month-head h2 { flex: 1; margin: 0; text-align: center; font-size: 15px; }
.month-head .step {
  display: inline-flex; align-items: center; justify-content: center;
  width: 44px; height: 44px; flex: none;
  border: 1px solid var(--line); border-radius: var(--r-s);
  background: var(--white); color: var(--text-2);
}
.month-head .step:hover { border-color: var(--brand); color: var(--brand); }
/* One chevron, turned round for the month before. */
.month-head .step:first-child .icon { transform: rotate(180deg); }

.month {
  width: 100%; table-layout: fixed;
  border-collapse: separate; border-spacing: 0;
  border: 1px solid var(--line); border-radius: var(--r-s); overflow: hidden;
}
.month th {
  padding: 8px 0; font-size: 11px; font-weight: 600;
  letter-spacing: .04em; text-align: center; color: var(--text-3);
  background: var(--panel); border-bottom: 1px solid var(--line);
}
.month th abbr { text-decoration: none; border: 0; }

/* Real grid lines, the way a wall calendar has them. Only right and bottom, so
   the outer border is not doubled. */
.month td {
  padding: 0; vertical-align: top;
  border-right: 1px solid var(--line-2);
  border-bottom: 1px solid var(--line-2);
}
.month td:last-child { border-right: 0; }
.month tr:last-child td { border-bottom: 0; }

.month td > a {
  display: flex; flex-direction: column; align-items: center; gap: 4px;
  /* The whole cell is the target, not the number in it. */
  min-height: 46px; padding: 7px 0 6px;
  color: var(--text); text-decoration: none;
}
.month td > a:hover { background: var(--nav-hover); text-decoration: none; }
.month td b { font-size: 13.5px; font-weight: 500; font-variant-numeric: tabular-nums; }

/* **Sunday, mildly.** A shop is open on a Sunday in this market, so it is a
   different day and not a closed one - a tint, not a colour.
   founder 2026-09-08 */
.month th.sun { color: var(--late); }
.month td.sun { background: var(--late-bg); }
.month td.sun > a:hover { background: #FBE6E3; }

/* A day outside this month is drawn, not blanked: a grid with holes in it
   reads as broken, and he can still tap into the month either side. */
.month td.out { background: var(--panel); }
.month td.out > a { color: var(--text-4); }
.month td.out b { font-weight: 400; }
.month td.out.sun { background: var(--late-bg); }

/* **Today, mildly too.** The number is the mark - a filled circle, the way a
   wall calendar is ringed - and the cell behind it only tints. */
.month td.now { background: var(--brand-bg); }
.month td.now b {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 24px; height: 24px; padding: 0 5px;
  background: var(--brand); color: var(--white);
  border-radius: 12px; font-weight: 600;
}
.month td.now > a:hover { background: #DCE8FA; }

.month .dots { display: flex; align-items: center; gap: 3px; height: 7px; }
.month .dots i { width: 5px; height: 5px; border-radius: 50%; background: var(--brand); }
.month .dots em { font-size: 9px; font-style: normal; color: var(--text-4);
                  line-height: 1; }
/* A day gone by with something still on it. The one thing worth its own
   colour: it is what he missed. */
.month td.late .dots i { background: var(--late); }

/* Everything on it ticked off. The opposite of missed, and the reason for
   ticking at all - a month he can read at a glance for the days he cleared.
   founder 2026-09-08 */
.month td.allok .dots i { background: var(--ok); }

/* The day, as a popup. Wider than a confirmation because it holds a list and a
   form, and it is the screen he came to the calendar for. */
.day-box { width: min(520px, calc(100vw - 28px)); }
.day-box .dnote:first-of-type { border-top: 1px solid var(--line-2); }
.day-box .add-line { margin-top: 16px; }

/* One line and a button, for adding without leaving the day he is looking at */
.add-line { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 14px; }
.add-line input[type=text] { flex: 1 1 160px; min-width: 0; margin: 0; }
.add-line input[type=time] { flex: 0 0 auto; margin: 0; }
.add-line button { margin: 0; flex: none; }

@media (max-width: 760px) {
  /* The whole row of controls goes full width, one under the other: at 320px
     a text box, a clock and a button on one line leaves nothing readable. */
  .add-line input[type=text],
  .add-line input[type=time],
  .add-line button { flex: 1 1 100%; }
  .month td > a { min-height: 52px; }
}


/* A twelve-hour clock, in three parts. `input type=time` follows the browser's
   locale and not the page's, so one phone shows 16:30 and another 4:30 PM;
   three selects are the same everywhere and need no script.
   founder 2026-09-08, 15-calendar */
/* **Each part keeps its own width.** `flex: 1 1 auto` with `min-width: 0` let
   the hour and the minute collapse to their dropdown arrows inside the row,
   which is what a flex child does when it is allowed to.   founder 2026-09-08 */
.clock { display: flex; align-items: center; gap: 5px; flex: 0 0 auto; }
/* Fixed, not `auto`. A `select` sized by its content takes the width of its
   longest option - so the hour, which carries "No time", grew until it pushed
   the minute and the half out of the dialog.   founder 2026-09-08 */
.clock select { flex: 0 0 auto; width: 74px; margin: 0; padding-right: 6px; }
.clock select:last-child { width: 78px; }
.clock b { flex: none; font-weight: 600; color: var(--text-4); }

@media (max-width: 760px) {
  /* The clock takes its own line under the text box and shares it out, rather
     than three dropdowns squeezed beside a sentence. */
  .clock { flex: 1 1 100%; }
  .clock select, .clock select:last-child { flex: 1 1 0; width: auto; min-width: 0; }
}


/* The form inside the day, and the one on the to-do list. Labelled fields
   stacked, not a compact row of unlabelled boxes: a placeholder is an example
   and it goes the moment he types.   founder 2026-09-08, AUDIT-2026-09-02 §16 */
.day-add { margin-top: 16px; padding-top: 14px; border-top: 1px solid var(--line-2); }
/* On the to-do list it is the first thing on the panel, so it has nothing to
   be separated from. */
.panel > .day-add:first-child { margin-top: 0; padding-top: 0; border-top: 0; }
.day-add label { display: block; margin: 0 0 5px; font-size: 12.5px;
                 font-weight: 500; color: var(--text-2); }
.day-add label + input,
.day-add label + .clock { margin-bottom: 12px; }
.day-add input[type=text] { width: 100%; margin: 0; }
.day-add .clock { display: flex; }
.day-add .hint { margin: -6px 0 12px; }
.day-add button { margin: 0; }
