
/* ==========================================================================
   FONTS. Self-hosted on purpose. IS runs behind a tunnel on an internal
   network, so an external font host would make the interface depend on the
   internet and would tell a third party every time someone opens the system.
   Two weights, nothing else. If the files are absent the system stack is used
   and the layout does not move, because the token names do not change.
   ========================================================================== */
/* NO BRAND FONT HERE. The system is not one website's back office - there
   are several sites and several brands, and it cannot inherit the face of
   any one of them. The interface uses whatever the machine already has.
   A brand font belongs to what leaves the building: mail templates, where
   the message should look like the site it came from. */

/* ==========================================================================
   shell.css - THE ONLY STYLESHEET
   15 colours - 4 type sizes - 2 weights - 6 spacing steps - radius 0
   ========================================================================== */
:root{
  --c-desk:#c3c8ce; --c-panel:#e6e8eb; --c-caption:#3d4f5d; --c-caption-fg:#ffffff;
  --c-surface:#ffffff; --c-alt:#f4f5f6;
  --c-text:#16181b; --c-text-2:#5d646c;
  --c-line:#b9bfc6; --c-line-2:#7f878f;
  --c-accent:#1c5d99;
  --c-link:#0000ee;
  --c-chrome:#1d262d; --c-chrome-2:#2f3d48; --c-chrome-line:#55636e; --c-chrome-line-2:#8a9aa6;
  --s-ok:#1e7a46; --s-warn:#9a5a00; --s-err:#a80000; --s-off:#9aa1a8;
  /* AN ORDER'S STATE IN THE COLOURS IMS v4 GAVE IT, Plane ODDIA-1105. Jan,
     19 October 2026: *"i really liked the original colour we had for
     certain statuses before. So please, check the IMSv4 and apply that."*
     v4 `theme-98.css`, `[data-state-id]`: paid bright green, sent and
     closed dark green, in preparation cyan, waiting for money orange,
     cancelled dark red, anything else navy. Drawn by `[data-hue]` below. */
  --o-paid:#00ff00; --o-sent:#008000; --o-making:#00ffff; --o-waiting:#ff8000;
  --o-cancelled:#800000; --o-other:#000080;
  /* A PARCEL CANCELLED IS BLACK, Plane ODDIA-1093 (Jan, 21 October: *"For
     canceled, we use black"*). And a signed figure that fills its cell:
     light green under dark green, light red under bright red. */
  --o-void:#000000;
  /* DELIVERED IS WINE AND WITH THE CARRIER LIGHT GREEN, Jan, 21 October
     12:45: *"Doručeno was always VINE colour ... Green was always reserved
     for Payed statuses. Předáno dopravci ... light green label"*. The wine
     is the old shop's `Vínová` swatch (`1202`, 740124). */
  --o-delivered:#740124; --o-handed:#c6efce; --o-handed-ink:#006100;
  --s-ok-fill:#d9f2dc; --s-ok-ink:#006400; --s-err-fill:#ffdcdc; --s-err-ink:#ff0000;
  /* THE WORK TABLE'S ROWS BY STATE, Plane ODDIA-1786, v4's `row-*` fills
     (`styles.css`): made orange, put aside grey, being worked blue. Red
     and green are the two fills above. */
  --s-warn-fill:#fbe3bf; --s-off-fill:#dfe1e4; --s-info-fill:#d8e0ee;
  --f-ui:system-ui,Arial,sans-serif;
/* ONE FONT. `--f-data` existed so columns of digits would line up under
   each other - and the body already sets font-variant-numeric:tabular-nums,
   which does that in ANY font. So the token stays, because 35 rules point
   at it and none of them names a font, and it resolves to the interface
   font. If a scanning screen ever needs character-by-character mono, that
   is one deliberate line for that one screen. Ruled by the operator,
   27 August. */
  --f-data:system-ui,Arial,sans-serif;
  --t-meta:11px; --t-base:13px; --t-sec:15px;
  /* THE ONE FIGURE A PANEL IS ABOUT. `TASKS.md` I004: *"The minimum
     selling price displayed BIG, so we know what is the minimum we can
     afford to sell this for and not go into red numbers."* A fourth size
     and not a literal, so the three rules that want it agree by token. */
  --t-big:20px;
  /* THE SIZE A LONG TEXT IS READ AND WRITTEN AT, Plane ODDIA-514, Jan: *"bigger
     letters in the fields and only here ... i dont wanna wear glasses"*. */
  --t-read:18px;
  --sp1:4px; --sp2:8px; --sp3:12px; --sp4:16px; --sp5:24px;
  --row:28px; --nav:180px; --top:32px; --draw:328px; --dock:400px;
  --ctl:24px; --ctl-sm:20px;
  /* THE RECORD FOOTER'S HEIGHT, DECLARED ONCE. `.acts` is a control plus
     its padding plus its top border, and the selection strip has to stop
     exactly where the footer starts or it lies across Uložit and Zavřít.
     Two rules needing the same number is a token, not a number typed
     twice. */
  --acts:calc(var(--ctl) + var(--sp2) * 2 + 1px);
  --band:var(--ctl);
}
*{box-sizing:border-box;scrollbar-width:thin;scrollbar-color:var(--c-line-2) var(--c-panel)}
::-webkit-scrollbar{width:10px;height:10px}
::-webkit-scrollbar-track{background:var(--c-panel);border-left:1px solid var(--c-line)}
::-webkit-scrollbar-thumb{background:var(--c-line-2);border:2px solid var(--c-panel)}
::-webkit-scrollbar-thumb:hover{background:var(--c-text-2)}
::-webkit-scrollbar-corner{background:var(--c-panel)}
html,body{margin:0;padding:0;height:100%;overflow:hidden}
body{font-family:var(--f-ui);font-size:var(--t-base);line-height:1.35;
  color:var(--c-text);background:var(--c-desk);font-variant-numeric:tabular-nums}
/* THE ONLY LINK RULE.
   Classic browser blue, underlined by the browser. A link must be spottable at
   a glance and this is the colour every human already reads as "link".
   --c-link is the 16th colour and the single documented exception to the
   fifteen-colour palette. Visited is the same colour on purpose: in a system
   you open the same record hundreds of times and a purple trail is noise.
   Components that merely use an <a> tag (buttons, nav rows, tabs) set their own
   colour and switch the underline off, because they are not links. */
a,a:visited{color:var(--c-link)}
:focus-visible{outline:2px solid var(--c-accent);outline-offset:0}
.app{display:flex;height:calc(100% - var(--top))}
/* THE STATION. `TASKS.md` ZL13, of the Výrobní terminál: "IT IS NOT A DESK
   SCREEN. Big targets, few words", at a touch screen in the workshop, read
   standing up. spec-is-visual 8 said one density "until you tell me a touch
   screen exists"; he has. A nav row declares `station` and the shell puts
   `data-station` on the body; this redefines the size tokens under it and
   nothing else, so every rule that already reads them - rows, controls -
   grows together and no rule is written twice.
   ZQ18, 26 September: TARGETS, NOT TYPE, AND ONLY WHERE THE WORK IS. This was
   set on `body` and redefined the four type sizes too, so the top bar, the
   menu, `Zamknout stanici` and every cell went to 17 px while headings,
   captions and the record's box titles stayed at desk size - "all the fonts
   are bigger". v4's terminal ran at the workshop PC at the desk's own type
   for months and was tuned there; what a bench needs is a row and a button
   big enough to hit, not bigger letters. So the type tokens are not touched,
   and the sizes are redefined on the list, the piece card and its windows,
   never on the chrome around them.
   AND NOT ON ITS WINDOWS, Plane ODDIA-1820. Jan, 2 November 2026: the
   terminal's `Problém` and its other windows at normal size, *"not the
   station's huge tokens"*. The console's keep theirs, below. */
body[data-station] .work,body[data-station] .rec{
  --row:48px;--ctl:44px;--ctl-sm:36px;--sp2:10px;--sp3:14px}
/* AND AT A STATION A CELL WRAPS. What to make and in what variant is the
   longest text on the row and the one the bench makes from; an ellipsis
   there hides the size or the colour. A desk list keeps its one line.
   AND A NUMBER WITH NO SPACE IN IT BREAKS RATHER THAN HIDES, `TASKS.md` ON04,
   plan 5.1 point 7: the station's wider padding leaves a 60 px id column 32 px
   of text, a five-digit id needs 34, and the card drew `143...`. Nothing a
   bench reads is cut short; an order's ten-digit reference fits its column
   and is unaffected. */
body[data-station] tbody td{white-space:normal;overflow-wrap:anywhere}
/* AND THE ID COLUMN IS WIDER THERE, Plane ODDIA-1583. Pieces passed 55 000
   on 31 October 2026 and every id on the terminal and the packing table
   broke after its fourth digit, "5594" over "8". The station's padding
   takes 28 of the desk's 60; 72 holds six digits on one line. Only the
   first heading the shell sized at `ModuleService::ID_WIDTH`. */
body[data-station] table[data-sized]>thead th[width="60"]:first-child{width:72px}
/* THE CONSOLE, Plane ODDIA-60: a station at the packing table, mouse today
   and a touch screen later (Jan, 13 October: "over time he will get the
   touching monitor so big buttons are what I would do here"). TARGETS, NOT
   TYPE, as ZQ18 above: the first console grew its letters to 20 and 34 px
   and Jan, the same day: "making all the font on the page bigger ... is
   not the way to go. This looks ridiculous ... this needs to be control
   panel." So the type is the desk's and what grows is the button. The
   presses are a panel, `[data-pad]` below. */
body[data-station="console"] .work,body[data-station="console"] .conf{
  --row:40px;--ctl:40px;--ctl-sm:32px;--sp2:10px;--sp3:14px}

/* ---------- TOP BAR ---------- */
.top{height:var(--top);background:var(--c-chrome);color:var(--c-caption-fg);
  display:flex;align-items:center;gap:var(--sp3);padding:0 var(--sp2);position:relative;z-index:16}
.top .clock{font-family:var(--f-data);font-size:var(--t-meta);color:var(--c-desk);
  margin-right:var(--sp2)}
.top .sp{flex:1}
.top input.gs{height:var(--ctl);width:340px;background:var(--c-chrome-2);border:1px solid var(--c-chrome-line);
  color:var(--c-caption-fg);font-family:var(--f-ui);font-size:var(--t-base);padding:0 var(--sp2)}
.top a.tb{color:var(--c-caption-fg);text-decoration:none;font-size:var(--t-base);padding:0 var(--sp2);
  height:var(--ctl);display:flex;align-items:center;gap:var(--sp1);border:1px solid transparent}
.top a.tb.on{background:var(--c-chrome-2);border-color:var(--c-chrome-line)}
.top a.tb b{font-family:var(--f-data);font-size:var(--t-meta)}
/* the clock is a link to the company calendar (ODDIA-647) and keeps the clock's face */
.top a.tb.clock{font-family:var(--f-data);font-size:var(--t-meta);color:var(--c-desk)}

/* unread badge, the one attention pattern in the system */
.badge{display:inline-flex;align-items:center;justify-content:center;
  min-width:16px;height:15px;padding:0 4px;
  background:var(--s-err);color:var(--c-caption-fg);font-family:var(--f-data);font-size:var(--t-meta);
  font-weight:700;line-height:1}
.tb.seen .badge{background:var(--c-text-2)}
.tb.has-new{animation:nudge 2s ease-in-out 3}
@keyframes nudge{
  0%,88%,100%{background:transparent}
  92%{background:var(--c-chrome-2)}
}
@media (prefers-reduced-motion:reduce){ .tb.has-new{animation:none} }
.top .dot{width:8px;height:8px;background:var(--s-ok);flex:none}
body.pause .top .dot{background:var(--s-warn)}

/* ---------- NOTIFICATION PANEL ---------- */
.notif{position:fixed;top:var(--top);right:var(--sp2);width:420px;max-height:66%;
  background:var(--c-surface);border:1px solid var(--c-line-2);display:none;
  flex-direction:column;z-index:15}
.notif.open{display:flex}
.notif .body{max-height:none}
/* EVERY ROW IS AN `<a>`, and the two properties that makes necessary
   are added here rather than in a second selector. They are the same
   pair `.umenu a` already carries, for the same reason: a link inside a
   chrome panel is the whole row, so it takes the row's colour and draws
   no underline. Rows used to be `div` when the notification had no
   `action_url`; since A7 they all point at `/notification?id=`, which
   marks the row read and then forwards. */
.notif .row{display:flex;gap:var(--sp2);align-items:center;padding:var(--sp1) var(--sp2);
  border-bottom:1px solid var(--c-line);cursor:pointer;
  color:var(--c-text);text-decoration:none}
.notif .row .t{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
/* READ, AND STILL THERE. The panel shows dismissed-or-not, never
   read-or-not, so a row that has been clicked would otherwise look
   exactly like one that has not. `seen` is the word `.tb.seen` already
   uses for this one control along; the chip keeps its severity colour,
   because what it was is not changed by having been read. */
.notif .row.seen{color:var(--c-text-2)}

/* ---------- USER MENU ---------- */
/* THE SAME MENU, UNDER A FIELD. `.umenu` is the mockup's popup list; a
   pick-and-type field opens it beneath itself rather than at the top
   right. Same component, one position. Operator, 27 August. */
.fld{position:relative}
/* AND IT OPENS AGAINST THE BOX, NOT AGAINST THE CELL THE BOX IS IN.
   `top:100%` was 100% of `.fld`, which is the LABEL, the box AND
   whatever is drawn under it - and since `0330` a picker may carry a
   link at its foot, `Chybí skupina? Založte ji ve Skupinách`. So the
   list opened 53 measured pixels below the box, with the link sitting
   in the gap: the operator, `TASKS.md` Y001, *THE DROPDOWN FLOATS,
   DISCONNECTED FROM ITS FIELD.*
   `top:auto` IS THE STATIC POSITION, which is where the element would
   have been in flow - directly under the input it is written after.
   A picker with nothing under it is unmoved, which is every other one. */
.fld .umenu{position:absolute;top:auto;left:0;right:auto;width:100%;
  max-height:240px;overflow-y:auto;z-index:20}
.fld .umenu a[hidden]{display:none}
.umenu{position:fixed;top:var(--top);right:var(--sp2);width:230px;
  background:var(--c-surface);border:1px solid var(--c-line-2);display:none;
  flex-direction:column;z-index:15}
.umenu.open{display:flex}
.umenu a{display:block;padding:var(--sp1) var(--sp2);color:var(--c-text);text-decoration:none;
  border-bottom:1px solid var(--c-line)}
.umenu a.last{border-bottom:0}
.umenu a.sep{border-top:2px solid var(--c-line-2)}
/* THE ROW THE KEYBOARD IS ON, in a pick-and-type list. Down and up walk the
   list and Enter takes this one, so it has to be visible from across the
   desk - the hover tint further down is a 7% wash and reads as nothing at
   all when the mouse is not the thing moving. `on` is the word `.nav-body a`,
   `.tabs a` and `.tree a` already use for *the one you are at*, and this is
   the same idea in a fourth place rather than a fifth name for it. */
.umenu a.on{background:var(--c-caption);color:var(--c-caption-fg)}

/* THE SAME MENU, UNDER A BUTTON IN A BAR. Plane ODDIA-1105, Jan:
   *"Zavolat should be also a button that have small dropdown with two
   options"*, and the order's state with the presses that move it.
   `details` opens it with no script (`Shell::barItem()`); `data-menu`
   and not a class, because `asset_check` holds invented classes at zero.
   The fold's own heading look (`details > summary` below) is undone: this
   summary is a `.btn`. */
details[data-menu]{position:relative;flex:none}
details[data-menu] > summary{list-style:none;font-size:var(--t-base);font-weight:400;padding:0 var(--sp2);
  border-bottom:1px solid var(--c-line-2);margin:0;gap:var(--sp1)}
details[data-menu][open] > summary{margin:0}
details[data-menu] > summary::-webkit-details-marker{display:none}
details[data-menu] > summary::after{content:'▾'}
details[data-menu] > .umenu{position:absolute;top:100%;left:0;right:auto;width:auto;min-width:100%;
  display:flex;z-index:30;margin-top:2px}
details[data-menu="end"] > .umenu{left:auto;right:0}
details[data-menu] .umenu .btn{width:100%;justify-content:flex-start;border:0;
  border-bottom:1px solid var(--c-line)}
details[data-menu] .umenu > span:not([hidden]){display:block}

/* THE ORDER'S STATE, COLOURED. Bold as in v4; the control's own frame takes
   the fill. Two classes deep, because `.btn` and its hover are declared
   further down and would repaint a lighter selector. */
/* AND SERIOUSLY VISIBLE, Plane ODDIA-1106, Jan, 20 October: *"make it more
   WIDE so its seriously visible."* 170px, its word in the middle: wider
   would push `Zavolat` off the bar at 1440 on an order with two joined.
   A LIST WITH ITS CONFIRM PRESS BESIDE IT, 21 October (`data-choose`): the
   chosen word coloured, the list it opens in the page's own colours; a
   fixed width, or the select grows to its longest status. 230px since
   ODDIA-1096: Jan's names (`Platba byla přijata`, `Čeká na platbu
   převodem`, 189px bold) were cut at 170. */
form[data-choose]{display:inline-flex;align-items:center;gap:var(--sp1)}
/* LEFT ALIGNED, Jan on ODDIA-1785: the state reads from the left edge of
   its box like every other control on the card, not centred in it. */
form[data-choose] > select.btn[data-hue]{font-weight:700;width:230px;text-align:left}
form[data-choose] > select.btn[data-hue] option{background:var(--c-surface);color:var(--c-text);font-weight:400}
.tb[data-hue]{display:inline-flex;align-items:center;justify-content:flex-start;min-width:170px;height:var(--ctl);padding:0 var(--sp2);font-weight:700}
.tb[data-hue="paid"],.btn[data-hue="paid"]{background:var(--o-paid);border-color:var(--o-paid);color:var(--c-text)}
.tb[data-hue="sent"],.btn[data-hue="sent"]{background:var(--o-sent);border-color:var(--o-sent);color:var(--c-caption-fg)}
.tb[data-hue="making"],.btn[data-hue="making"]{background:var(--o-making);border-color:var(--o-making);color:var(--c-text)}
.tb[data-hue="waiting"],.btn[data-hue="waiting"]{background:var(--o-waiting);border-color:var(--o-waiting);color:var(--c-text)}
.tb[data-hue="cancelled"],.btn[data-hue="cancelled"]{background:var(--o-cancelled);border-color:var(--o-cancelled);color:var(--c-caption-fg)}
.tb[data-hue="other"],.btn[data-hue="other"]{background:var(--o-other);border-color:var(--o-other);color:var(--c-caption-fg)}

.stack{flex:1;min-width:0;display:flex;flex-direction:column;gap:var(--sp2)}

/* ---------- COMMENT THREAD ---------- */
.thread{border:1px solid var(--c-line)}
.thread .c{padding:var(--sp2);border-bottom:1px solid var(--c-line)}
.thread .c.sys{background:var(--c-alt)}
.thread .c .h{font-size:var(--t-meta);color:var(--c-text-2);font-family:var(--f-data);
  display:flex;gap:var(--sp2);align-items:center;margin-bottom:2px}
.thread .c .h .sp{flex:1}
.thread .c .h a{font-size:var(--t-meta)}
.thread .c.reply{padding-left:var(--sp5)}
.thread .new{padding:var(--sp2);background:var(--c-panel);display:flex;gap:var(--sp2);align-items:flex-start}
/* THE SAME THREAD AS A CHAT. Plane ISV5-46, Jan, of a ticket: *"needs to
   be done in whatsall like format ... message in buuble and the secondary is
   second bubble in different colour."* `Shell::blockThread()`. Theirs on the
   left on the surface, ours on the right in the caption's colour, a change of
   state a quiet line in the middle. The flow scrolls; the answer box under it
   stays where the hand is. `data-flag` is a message the shield flagged. */
.thread[data-chat]{background:var(--c-alt)}
.thread[data-chat] [data-flow]{display:flex;flex-direction:column;gap:var(--sp2);padding:var(--sp3);
  overflow-y:auto;min-height:calc(var(--row) * 8);max-height:calc(100vh - var(--top) - 300px)}
.thread[data-chat] .c{max-width:78%;align-self:flex-start;border:1px solid var(--c-line);
  background:var(--c-surface);padding:var(--sp2) var(--sp3)}
.thread[data-chat] .c.reply{align-self:flex-end;background:var(--c-caption);
  border-color:var(--c-caption);color:var(--c-caption-fg)}
.thread[data-chat] .c.reply .h,.thread[data-chat] .c.reply .h a{color:var(--c-caption-fg)}
.thread[data-chat] .c.sys{align-self:center;max-width:none;background:none;border:0;padding:0}
.thread[data-chat] .c.sys .h{margin:0}
.thread[data-chat] .c[data-flag]{border-color:var(--s-err);border-left-width:var(--sp1)}
.thread[data-chat] .c p{margin:0;white-space:pre-wrap;overflow-wrap:anywhere}
.thread[data-chat] .new{border-top:1px solid var(--c-line)}
.thread[data-chat] .new > form{flex:1;min-width:0}
.thread[data-chat] .new textarea{width:100%;min-height:calc(var(--row) * 3)}
.thread[data-chat] .gate-act{flex-wrap:wrap}
/* A CALL'S OR A VOICE MESSAGE'S SOUND ON IS'S OWN BUTTONS, ODDIA-759 (Jan,
   24 October 2026: "not rounded edges ... normal buttons"). `blockThread()`
   draws it, `shell.js` `data-player` plays it. The bar is square: a line
   with the part heard filled, pressed anywhere to go there. As wide as the
   message it plays (Jan, 24 October 2026: "Make it 100% of the message"). */
.thread [data-player]{display:flex;align-items:center;gap:var(--sp1);margin-top:var(--sp2);width:100%}
.thread [data-player-bar]{flex:1;height:var(--sp1);min-width:80px;border:1px solid var(--c-line-2);
  background:var(--c-surface);cursor:pointer;position:relative}
.thread [data-player-done]{position:absolute;left:0;top:0;bottom:0;width:0;background:var(--c-text-2)}
.thread [data-player-time]{font-family:var(--f-data);font-size:var(--t-meta);white-space:nowrap}
/* A VOICE MESSAGE'S WORDS AND SOUND BEHIND A PRESS, ODDIA-1515: while the
   voice reader is on, the summary stands first and the transcript and the
   player wait under one button, as a mail's words under the Mail filter. */
.thread details[data-behind]{margin-top:var(--sp1)}
.thread details[data-behind]>summary{display:inline-flex;list-style:none;cursor:pointer}
.thread details[data-behind]>summary::-webkit-details-marker{display:none}
.thread details[data-behind][open]>summary{margin-bottom:var(--sp1)}
.thread[data-chat] .c.reply [data-player-bar]{border-color:var(--c-caption-fg)}
.thread[data-chat] .c.reply [data-player-done]{background:var(--c-caption-fg)}
.thread .c [data-meta]{font-size:var(--t-meta);color:var(--c-text-2);margin:0 0 2px}
[data-puts]{flex-wrap:wrap;align-items:center;margin-top:var(--sp2);
  font-size:var(--t-meta);color:var(--c-text-2)}

/* ---------- MENU ---------- */
.nav{width:var(--nav);flex:none;display:flex;flex-direction:column;
  background:var(--c-desk)}
.nav-foot{flex:none;padding:var(--sp2)}

/* pause blocker */
.pausewrap{position:fixed;inset:0;background:rgba(0,0,0,0.55);display:none;z-index:40;
  align-items:center;justify-content:center}
body.pause .pausewrap{display:flex}
.pausewrap .win{width:420px}

/* the work area's windows have a 1px frame; the menu has none, so it borrows
   one in transparent to keep the first row level with the first caption */
.nav-body{flex:1;overflow-y:auto;padding:var(--sp2) 0 0;font-size:var(--t-base);
  border-top:1px solid transparent}

/* folder tree */
.nav-body .grp{display:flex;align-items:center;gap:6px;height:var(--ctl);
  padding:0 var(--sp2);cursor:pointer;color:var(--c-text);font-weight:700;user-select:none}
.nav-body .grp b{flex:1;font-weight:700}
/* AND THE SAME BOX IN A SPLIT RECORD'S LIST, Plane ISV5-561: the fold in
   front of `Landing` and the box in front of each landing under it. */
.nav-body .tw,.tree .tw{width:11px;height:11px;flex:none;border:1px solid var(--c-line-2);
  background:var(--c-surface);font-family:var(--f-data);font-style:normal;
  font-size:11px;line-height:9px;font-weight:700;
  text-align:center;color:var(--c-text)}

.nav-body a{display:flex;align-items:center;gap:6px;height:var(--ctl);text-decoration:none;
  padding:0 var(--sp2) 0 27px;color:var(--c-text);position:relative}
.nav-body a::before{content:"";position:absolute;left:13px;top:0;bottom:0;
  border-left:1px solid var(--c-line-2)}
.nav-body a.last::before{bottom:12px}
.nav-body a::after{content:"";position:absolute;left:13px;top:12px;width:8px;
  border-top:1px solid var(--c-line-2)}
.nav-body a.on{background:var(--c-caption);color:var(--c-caption-fg)}
.nav-body a.on::before,.nav-body a.on::after{border-color:var(--c-chrome-line-2)}
.nav-body a .lb{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.nav-body a.on .n{color:#c3c8ce}
.nav-body a.hid{display:none}
.nav-body a.root{padding-left:var(--sp2)}
.nav-body a.root::before,.nav-body a.root::after{display:none}
.tw--none{border-color:transparent;background:transparent}
body.flatmenu .nav-body a{padding-left:var(--sp2)}
body.flatmenu .nav-body a::before,body.flatmenu .nav-body a::after{display:none}
body.flatmenu .nav-body .tw{display:none}





/* ---------- MODULE AREA ---------- */
.work{flex:1;min-width:0;display:flex;flex-direction:row;padding:var(--sp2);gap:var(--sp2)}
/* `position:relative` SO THE SELECTION STRIP CAN SIT INSIDE THIS WINDOW
   rather than across the whole screen. Nothing else changes: a flex
   column with static children lays out identically either way. */
.win{display:flex;flex-direction:column;min-height:0;background:var(--c-surface);
  border:1px solid var(--c-line-2);position:relative}
.win.grow{flex:1}
.cap{flex:none;height:var(--band);background:var(--c-caption);color:var(--c-caption-fg);
  font-size:var(--t-meta);text-transform:uppercase;letter-spacing:0.7px;font-weight:700;
  padding:0 var(--sp2);display:flex;align-items:center;justify-content:space-between;
  gap:var(--sp3)}
/* THE TITLE TAKES THE SLACK AND THE LINKS STAY TOGETHER AT THE END.
   `space-between` spreads its children evenly, which is right for two and
   wrong for three: the moment `Dokumentace` joined `Nastavení`, the
   settings link was pushed into the middle of the band. The title is the
   only child that may grow, so it takes the gap and every link after it
   sits where the one link used to. A caption with one link draws exactly
   as before. */
.cap > span:first-child{flex:1;min-width:0}
/* WHAT COMES AFTER THE TITLE IS SET ONE WAY.
   The breadcrumb after the separator and the settings link are the same
   kind of thing - secondary text on the caption band - and were set in
   two rules with the same values written twice. One rule, so they cannot
   drift apart, and adding a third such link means adding a selector here
   rather than copying four properties. Operator, 2 September. */
.cap em,
.cap a.tb{font-style:normal;font-weight:400;font-family:var(--f-data);
  color:#c3c8ce;text-transform:none;letter-spacing:0}
/* THE COG READS ON THE CAPTION, not against it. The caption band is dark
   and a link is blue, so it was drawn in the one colour that disappears
   there. It takes the caption's own foreground, like the text beside it. */
/* AND IT IS A LINK, so it looks like something you click rather than
   like a second title. The type comes from the rule above, shared with
   the breadcrumb. What is left is the underline and the box, and it is
   written against `.cap a` rather than `.cap a.tb` on purpose: declaring
   the same selector twice is the fault the checker exists to catch, and
   a caption has one kind of link. */
.cap a{text-decoration:underline;
  display:flex;align-items:center;height:var(--band)}
.cap a.tb:hover{color:#c3c8ce}
.bar{flex:none;background:var(--c-panel);border-bottom:1px solid var(--c-line);
  padding:var(--sp1) var(--sp2);display:flex;gap:var(--sp2);align-items:center}
/* A BAR CARRYING TWO BANDS WRAPS, BECAUSE IT CANNOT BE MADE TO FIT.
   `TASKS.md` Q012: the competitor map is nine region buttons and, inside
   Evropa EU, seventeen country ones. At 1600px fourteen of the countries
   were off the right edge - and a flex row does not scroll, so they were
   not merely out of sight, they could not be reached at all. M018's
   answer on the gallery bar was to tighten the gaps, which bought 100px;
   this needs 1100 and there is no amount of tightening that finds it.
   ONE BAR STILL. His words, 22 September - *"we have main bar and the
   tabs under, you made double bar, that we never ever had"* - refuse a
   second `.bar` ELEMENT under the first, and this is not one: same
   background, same border, as tall as what it holds. It is also the only
   bar in the system that says so - the attribute is written by
   `Shell::toolbar()` where a list declares more than one band, so every
   other screen keeps its single line and its clipping exactly as it was.
   NO NEW CLASS, the same road `data-gallery` took for the same reason. */
.bar[data-bands]{flex-wrap:wrap}
/* AND A BAR WITH A SENTENCE OF ITS OWN WRAPS TOO, ODDIA-1029: what a tab is,
   in one row at the foot of the same bar, under the tabs, and the figure
   beside `Vypočítat` with it. Eight tabs on /costs left the sentences a
   column a word wide. ONE BAR STILL, as above. */
.bar:has(> .tb[data-line]){flex-wrap:wrap}
.bar > .tb[data-line]{flex:0 0 100%;order:1}
.bar .sp{flex:1}
/* A CHIP AND A LINKED NUMBER IN A BAR KEEP THEIR LINE: the order's
   `Společná zásilka` and the orders it names, ODDIA-1105. */
.bar > .chip,.bar > a.tb{flex:none;white-space:nowrap}

/* WHAT ACTS ON A SELECTION, AT THE FOOT AND ALWAYS IN VIEW.
   Pinned to the bottom of the WINDOW it belongs to, not to the screen:
   it acts on the rows in this list, so it sits inside the frame that
   holds them, just above the count. Operator's placement, 31 August. It
   still does not scroll away - on a list of four hundred the rows you
   ticked are above the fold and the action was below it. Dark like the
   header because it has to be unmistakable - it holds the destructive
   controls, and it appears only while something is ticked, which is
   itself the warning. The strip carries `data-mass`, so the script that
   already watches the ticks reveals it and no new behaviour is added.
   Operator, 30 August. */
.selbar{position:absolute;left:0;right:0;bottom:var(--row);z-index:40;
  background:var(--c-chrome);border-top:1px solid var(--c-chrome-line);
  display:flex;align-items:center;gap:var(--sp2);
  padding:var(--sp1) var(--sp2)}
.selbar .sp{flex:1}
.selbar .tb{color:var(--c-caption-fg);font-size:var(--t-meta)}
.selbar[hidden]{display:none}

/* AND IT GOES WHILE A RECORD IS OPEN. The strip acts on rows ticked in
   the list behind the overlay, and offering to delete them from on top
   of an unsaved record is an invitation to lose two things at once.
   A record that itself holds a list will have its own strip, inside it.
   Operator, 1 September. */
/* The list's own strip goes while a record is open - it acts on rows
   behind the overlay. A strip drawn INSIDE the record is the record's
   and stays. */
body:has(.rec) .win .selbar{display:none}
/* AND IT STOPS WHERE THE FOOTER STARTS. At `bottom:0` it lay across
   Uložit and Zavřít, so the strip that appears when you tick a row hid
   the two controls you need afterwards. */
.rec .selbar{position:absolute;left:0;right:0;bottom:var(--acts)}

/* ON A SPLIT SCREEN IT STARTS WHERE THE LIST STARTS. The menu beside the
   list is not what the selection acts on, and a strip lying across it
   hides the thing you would use to leave. Same width as the panel, which
   is the same token the panel uses. Operator, 1 September. */
.win[data-split] .selbar{left:var(--nav)}
.bar .div{width:1px;height:var(--ctl-sm);background:var(--c-line)}
.scope{flex:none;background:var(--c-panel);border-bottom:1px solid var(--c-line-2);
  padding:var(--sp1) var(--sp2) 0;display:flex;gap:2px}
.scope a{font-size:var(--t-meta);padding:var(--sp1) var(--sp2);color:var(--c-text);text-decoration:none;
  border-bottom:2px solid transparent;text-transform:uppercase;letter-spacing:0.5px}
/* NO WEIGHT ON THE SELECTED TAB. Bold changes the width of the label,
   so every tab after it moves and the whole strip jumps on each click.
   The underline already says which one is open. The mockup carries the
   weight and this is an agreed edit against it, ruled by the operator. */
.scope a.on{border-bottom-color:var(--c-accent)}
.scope .n,.btn .n{font-family:var(--f-data);color:var(--c-text-2);padding-left:var(--sp1)}
/* A TAB STRIP INSIDE A BOX KEEPS THE FIELDS UNDER IT OFF ITS LINE.
   ODDIA-1306, Jan, 27 October 2026, on a kind of mail's Text: "Předmět
   label is glued to the tabs completely, there is missing padding". */
.box > .scope{margin-bottom:var(--sp2)}
.btn--selected .n{color:inherit}
.body{flex:1;min-height:0;overflow:auto}
.body.pad{padding:var(--sp3)}
/* THE THREE TYPE PROPERTIES COME FROM THE SHARED META RULE, which names
   `.stat` in its own list. They were restated here with the same values,
   so the strip was styled twice and either copy could drift. */
.stat{flex:none;background:var(--c-panel);border-top:1px solid var(--c-line);
  padding:var(--sp1) var(--sp2);display:flex;align-items:center;gap:var(--sp2)}
.stat .sp{flex:1}
/* THE TWO NUMBERS ARE THE MOCKUP'S OWN STATUS CLASSES, `ok` and `err`,
   the same pair `.chip` uses for the same meaning. `act`/`ina` were a
   pair invented here, and asset_check reported `ina` on the first run
   as drift, which is what it was. */
.stat .ok{color:var(--s-ok)}
.stat .err{color:var(--s-err)}
.stat .div{color:var(--c-line-2)}
.stat select{height:var(--ctl-sm);border:1px solid var(--c-line);background:var(--c-surface);
  font-family:var(--f-data);font-size:var(--t-meta);color:var(--c-text)}
.stat .pg{display:flex;gap:2px}
.stat .pg .btn{min-width:var(--ctl-sm)}
.stat .pg a.on{background:var(--c-caption);color:var(--c-caption-fg);border-color:var(--c-caption)}

/* ---------- CONTROLS ---------- */
/* ============ BUTTON, one definition, no overrides anywhere ============ */
.btn{
  display:inline-flex; align-items:center; justify-content:center;
  height:var(--ctl); box-sizing:border-box;
  padding:0 var(--sp2);
  border:1px solid var(--c-line-2);
  background:var(--c-surface); color:var(--c-text);
  font-family:var(--f-ui); font-size:var(--t-base); font-weight:400;
  line-height:1; white-space:nowrap; text-decoration:none; cursor:pointer;
}
/* hover is defined ONCE, below, for every interactive surface */

/* A DISABLED BUTTON IS THE SAME BUTTON, FADED. NOT A DIFFERENT ONE.
   It repainted `color` and `background`, which produced a grey fill under
   text that stayed red - `a.btn--danger` outranks `.btn.is-disabled` on
   specificity, so the colour override never even landed on the one button
   that most needed it. A refused Smazat therefore looked like a third kind
   of control beside the two live ones: same frame, grey box, red word.
   Reported by the operator, 7 September: *the button we usually have, but
   greyed out, not different.*
   OPACITY DOES THE WHOLE JOB and cannot fight a more specific rule,
   because it is not a colour. Every disabled control in the system is now
   its own live self at 45%, which is the one thing that reads as "this is
   the button, and it is off". */
.btn:disabled,.btn.is-disabled{opacity:.45;cursor:default}
/* a:visited outranks .btn on specificity, so a button drawn as a link went
   blue once it had been followed. The comment on --c-link already says a
   component using an <a> sets its own colour; this is that sentence enforced. */
a.btn,a.btn:visited{color:var(--c-text)}
a.btn--danger,a.btn--danger:visited{color:var(--s-err)}
a.btn--selected,a.btn--selected:visited{color:var(--c-caption-fg)}

/* intent */
.btn--default{font-weight:700}
/* THE CALLING BOARD'S BUTTONS, Jan, 14 October 2026: "big buttons to act on
   it". One and a half times a control and a bold word, so a call is acted
   on at a glance; three side by side still fit the board at 1600 px. A submit column that says `big` (Plane ISV5-1039); an
   attribute, as `data-drift` below, not a class of its own. */
.btn[data-big]{height:calc(var(--ctl) * 1.5);padding:0 var(--sp3);font-weight:700}
.btn--danger{border-color:var(--s-err);color:var(--s-err)}
/* A BUTTON WHOSE FILL IS A STATE. `TASKS.md` SD01, plan 3.25, the operator on
   2 October of `Synchronizovat s webem`: "green when the shop matches IS, red
   when it does not ... Should be red inside, more than the one we now have -
   this is a critical button." The outline CS01 gave it could not say which,
   because it was red always. Filled, in the two colours a window's verdict
   already uses (`.conf .cap.ok`, `.conf .cap.err`), so the button and the
   first line of the window it opens are the same answer in the same colour.
   AN ATTRIBUTE AND NOT A CLASS: it is the same button in a state, and the
   `a.btn--danger` rule above outranks a class on a link anyway. */
.btn[data-drift="ok"],a.btn[data-drift="ok"],a.btn[data-drift="ok"]:visited{
  background-color:var(--s-ok);border-color:var(--s-ok);color:var(--c-caption-fg)}
.btn[data-drift="err"],a.btn[data-drift="err"],a.btn[data-drift="err"]:visited{
  background-color:var(--s-err);border-color:var(--s-err);color:var(--c-caption-fg)}
/* A BUTTON THAT WAS PRESSED AND IS BEING ANSWERED. The same round: "a press
   or a window that takes more than a second shows that it is working, from
   the moment of the press." `shell.js` sets `aria-busy` on a `data-working`
   button when it is pressed and writes the console's word on it; the stripe
   is the console's own, `@keyframes jobrun`, which is what this system
   already means by something running. It takes no second press while it
   works. The colour is put back to the quiet one so the word reads on the
   stripe, whatever the button's fill was. */
.btn[aria-busy="true"],a.btn[aria-busy="true"],a.btn[aria-busy="true"]:visited{
  pointer-events:none;color:var(--c-text);border-color:var(--c-accent);
  background-color:var(--c-alt);
  background-image:repeating-linear-gradient(90deg,var(--c-alt) 0,var(--c-alt) 16px,var(--c-line) 16px,var(--c-line) 32px);
  background-size:32px 100%;animation:jobrun .7s linear infinite}
@media (prefers-reduced-motion:reduce){
  .btn[aria-busy]{animation:none}
}
/* No weight change on selection. Bold reflows the label, so the whole row
   moves every time somebody clicks one. The filled caption block already
   says which is chosen, louder than weight ever did. */
.btn--selected{background:var(--c-caption);border-color:var(--c-caption);
  color:var(--c-caption-fg)}

/* size */
.btn--mini{height:var(--ctl-sm);padding:0 var(--sp1);font-size:var(--t-meta)}

/* quiet mono meta text: counts, codes, timestamps. Used wherever a value is
   secondary to the row it sits in. Declared once. */
.meta,.notif .row .w,.nav-body .grp .k,.nav-body .n,.btn--tall .r,
.tree a span,.ph .n,.wh .list a .ex,.stat,.card .m,.bcol .h span
  {font-family:var(--f-data);font-size:var(--t-meta);color:var(--c-text-2)}

/* width */
.btn--block{width:100%}
.btn--tall{height:calc(var(--ctl) * 2);justify-content:space-between}
.btn--tall .l{text-align:left}

/* alignment of label inside the button */

/* ============ THE ONLY HOVER RULE IN THE SYSTEM ============
   A translucent black wash laid over the element. It darkens whatever is behind
   it, so it works identically on a white button, a dark selected row, and a row
   with no background of its own. filter:brightness was tried first and fails on
   transparent elements, because it can only darken what the element itself
   paints. Never restate a hover appearance anywhere else. */
.btn:hover,
.nav-body a:hover,
.nav-body .grp:hover,
.scope a:hover,
.tabs a:hover,

.notif .row:hover,
.umenu a:hover,
.tree a:hover,
.card:hover,
.top a.tb:hover,
.wh .list a:hover,
.ph:hover .im,
tbody tr:hover td,
[data-mails] > a:hover,
.cal .e:hover{
  background-image:linear-gradient(rgba(0,0,0,0.07),rgba(0,0,0,0.07));
  text-decoration:none;
}
.btn:active,.nav-body a:active,.umenu a:active,.notif .row:active{
  background-image:linear-gradient(rgba(0,0,0,0.14),rgba(0,0,0,0.14));
}

/* rows of buttons */
.btnrow{display:flex;gap:var(--sp1)}

input.f,select.f,textarea.f{height:var(--ctl);box-sizing:border-box;
  border:1px solid var(--c-line-2);background:var(--c-surface);
  font-family:var(--f-ui);font-size:var(--t-base);line-height:1;
  padding:0 var(--sp2);color:var(--c-text)}
textarea.f{height:auto;padding:var(--sp1)}
input.f[readonly]{background:var(--c-alt);border-color:var(--c-line)}
/* A BOX NOBODY MAY FILL IN LOOKS LIKE ONE.
   The operator, 9 September, about IČO on a foreign supplier: *"should be
   greyed out so its clear you cant input in it."* It carried `disabled`
   and nothing said so, because only `[readonly]` above was ever styled
   and the record uses `disabled` - a readonly input still posts and a
   disabled one does not, and every locked box in this system needs the
   second. The same tint and the same border as readonly, plus the muted
   ink and the cursor `.sw:disabled` already uses.
   THE CELLS ARE UNAFFECTED: `td > .fld > .f` further down strips the
   border and the background off a box inside a table, and it wins by
   coming later. */
input.f:disabled,select.f:disabled,textarea.f:disabled,
input.f[data-off-by][readonly],select.f[data-off-by][data-shut]{
  background:var(--c-alt);border-color:var(--c-line);
  color:var(--c-text-2);cursor:not-allowed}
/* A NUMBER BOX HAS NO ARROWS ON IT, ANYWHERE.

   THE OPERATOR, `TASKS.md` Z003 fault 6: *"there will literally never be a
   moment where we will use it, its always written manually."*

   EVERY NUMBER INPUT IN THE SYSTEM AND NOT A LIST OF THEM, which is what
   he asked for twice - `Z005` fault 4 names seven more fields than `Z003`
   did and ends *"Remove the spinner from EVERY number input in the system,
   not from a list."* A rule on the element type cannot be short of one.

   NO EXCEPTION IS KEPT AND THERE WAS ONE CANDIDATE. The position box in
   reorder mode - `td.act input[type="number"]` further down - is 56px
   wide and is the one place a spinner would be stepping through small
   whole numbers. It loses them too: *always written manually* is a
   sentence about the person, not about the field, and a control that
   behaves differently on one screen is the fault `00_MASTER_RULES.md` §3
   names as lookalikes kept in step by hand.

   BOTH HALVES ARE NEEDED. Firefox answers `appearance:textfield`; Chrome
   and Safari paint the spinner as a shadow pseudo-element and only
   `-webkit-appearance:none` on it takes it off. The `margin:0` is the
   gap WebKit reserves beside the buttons, which stays behind without it.

   THE FIELD IS STILL `type="number"`. What goes is the widget, not the
   input mode, the keyboard on a tablet, or the step validation the
   browser does on the typed value. */
input[type="number"]{appearance:textfield;-moz-appearance:textfield}
input[type="number"]::-webkit-outer-spin-button,
input[type="number"]::-webkit-inner-spin-button{-webkit-appearance:none;margin:0}
.lbl{font-size:var(--t-meta);color:var(--c-text-2);text-transform:uppercase;
  letter-spacing:0.5px;display:flex;align-items:center;gap:4px}
.w-xs{width:80px}.w-s{width:140px}.w-m{width:240px}.w-l{width:400px}.w-full{width:100%}

/* ---------- TABLE ---------- */
/* THE TABLE HAS AN OUTSIDE. The column rule further down draws lines
   BETWEEN columns, so the first column had no left edge and the last no
   right one. On a list `.win` lent it a border and it looked finished by
   accident; a table in a record or in a panel ended in mid air. Declared
   here, in the rule the table already has, because a second `table{}`
   rule is the exact fault the checker was written to catch. */
table{width:100%;border-collapse:collapse;font-size:var(--t-base);
  border:1px solid var(--c-line)}
/* A SIZED TABLE OBEYS ITS OWN WIDTHS. `TASKS.md` Y001 fault 4:
   "graphically it's broken, settings for the widths of the tables are
   missing." They were not missing - in the default `auto` layout a width
   is a suggestion the browser overrules from the content, so a Reference
   column set to 170 drew at 620 and pushed half the movements table past
   the edge of the record. Fixed layout is the only thing that makes a
   declared width a width.
   THE ATTRIBUTE IS NOT DECORATION AND IT IS NOT ON EVERY TABLE.
   `Shell::tableSizing()` adds it only where every column carries a number
   or is the flex one, and the comment there says why a derived list must
   keep sizing itself from its content, and why this is an attribute
   rather than a class - the same answer `.win[data-split]` gives. The
   `min-width` beside it is that table's own minimum, so the flex column
   cannot be squeezed to nothing and the table scrolls in `.body` instead
   of losing columns. */
table[data-sized]{table-layout:fixed}
/* A CHECKLIST GROUP STAYS IN ITS COLUMN, Plane ISV5-908. `thead th` does
   not wrap, so a long group name ("Objednávka nespolehlivého plátce DPH je
   pozastavena") widened its table past the `.cols` track and over the next
   column, its last box cut off under it. Fixed layout holds the table to
   the track and the name wraps onto a second line instead. */
table[data-checklist]{table-layout:fixed}
table[data-checklist] thead th{white-space:normal;overflow-wrap:anywhere;
  line-height:1.3;padding-top:4px;padding-bottom:4px}
/* And a long choice wraps in its third rather than being cut: `tbody td`
   is nowrap with an ellipsis, and fixed layout no longer lets the cell grow
   ("Příprava materiálu · Latex 01" in the new-order window). */
table[data-checklist] td{white-space:normal;overflow-wrap:anywhere}
/* And three columns only where three fit. At 800 a third was 187 px and
   "Matrix" broke in the middle; a column under 240 px moves to the next
   row instead. */
.cols:has(> div > table[data-checklist]){grid-auto-flow:row;
  grid-template-columns:repeat(auto-fit,minmax(min(240px,100%),1fr))}
/* A LANGUAGE BOX IN A SIZED TABLE FILLS ITS COLUMN. Only a box that says
   `lang_flex` sizes a table holding them; the box is 20 characters
   otherwise and stood over the next column. Plane ODDIA-348. */
table[data-sized] td > input.f[data-cell$="translate"]{width:100%}
/* The scroller is .body.pad, so top:0 left a 12px band above the sticky
   header through which rows scrolled and appeared over it. Sticking at
   the padded edge closes the band. */
/* A HEADING IS ONE ROW HIGH, LIKE A ROW. It was sized by its padding and
   came out shorter than the rows under it, so the band above a table
   never lined up with the band above any other. `--row` is the same
   token the rows use, so they cannot disagree. Operator, five times. */
/* THE HEADING SAYS NOTHING ABOUT ITS OWN BOX. Type, colour, the line
   under it and where it sticks - the height and the padding come from
   the one cell rule further down, like every other cell. */
thead th{position:sticky;top:calc(var(--sp3) * -1);z-index:3;font-size:var(--t-meta);text-transform:uppercase;
  letter-spacing:0.5px;color:var(--c-text-2);font-weight:700;
  border-bottom:1px solid var(--c-line);
  background:var(--c-panel);white-space:nowrap}
/* No hand cursor on a column name. The mockup drew sortable headers and
   sorting is not built, so the cursor was promising something that does not
   happen. It comes back with sorting, not before. */
thead th.r{text-align:right}
/* The filter row is part of the header, not a white strip under it. No class:
   the only td inside a thead is this row, so the selector is the structure.
   No top border and no top padding - the header's own padding already
   separates them. */
/* AND THE FILTER ROW STICKS DIRECTLY UNDER IT. The offset was 21px,
   which was the heading's height before it had one. It is `--row` now,
   so changing the row height moves both together instead of leaving a
   band the rows scroll through. */
thead td{position:sticky;top:calc(var(--sp3) * -1 + var(--row));z-index:3;
  background:var(--c-panel);border-bottom:1px solid var(--c-line)}
/* One line under the header, not two. Where a filter row follows, the column
   names hand their underline to it: the band is one object. */
thead:has(td) th{border-bottom:none}
/* AND ITS BOXES LOOK LIKE BOXES. `td > .f` below strips the border,
   background and height from an input sitting straight in a cell - which
   is right for an editable cell, where the box IS the cell, and wrong
   here, where it is a box you type a filter into. The filter row matched
   that rule and drew seven invisible inputs: the heading grew, and
   nothing appeared in it. Reported by the operator, 3 September. Same
   shape as any other field, restated for the one place the cell rule
   should not reach. */
thead td > .f{height:var(--ctl-sm);border:1px solid var(--c-line-2);
  background:var(--c-surface);padding:0 var(--sp1)}
thead td > .f:focus{outline:0;background:var(--c-surface)}
/* AND A FILTER THAT SUGGESTS IS WRAPPED, SO THE SAME TWO RULES HAVE TO
   REACH ONE LAYER DEEPER. `TASKS.md` A002 fault 1: the pick-and-type
   filter is inside a `.fld` now, because that wrapper is the whole of how
   `.umenu` knows where to open - `position:relative` on it, `top:auto` on
   the list. Without these two lines `td > .fld > .f` further down strips
   the border, the background and the height off it, which is the box that
   grew invisible on 3 September, arriving a second time by a different
   door. Same values as the pair above, and they are the same box. */
thead td .fld > .f{height:var(--ctl-sm);border:1px solid var(--c-line-2);
  background:var(--c-surface);padding:0 var(--sp1)}
thead td .fld > .f:focus{outline:0;background:var(--c-surface)}
/* AND THE OPEN LIST IS PAINTED ABOVE THE CELLS BESIDE IT.
   The same argument as `tbody tr:has(.umenu.open)` further down, one band
   up, and it is the fault `00_SHELL_RULES.md` 7.7a records about tooltips:
   every `thead td` is `position:sticky` with `z-index:3`, so the filter row
   is a row of stacking contexts at ONE level, and a list opened in any of
   them is painted over by every cell after it in the document. The cell
   holding an open list is lifted for as long as it is open, and nothing
   else on the page moves. */
thead td:has(.umenu.open){z-index:4}
/* A ROW CARRIES ITS OWN WHITE. It used to inherit it from whatever was behind
   it, so a table inside a grey box went grey-on-grey against its own header.
   A table now works on any background instead of depending on one. */
tbody td{background:var(--c-surface);
  border-bottom:1px solid var(--c-line);
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
/* A CELL THAT SPANS THE ROW HOLDS A SENTENCE, NOT A VALUE.
   `tbody td` above is nowrap with an ellipsis, which is right for a
   column - a name that does not fit is read from the record - and wrong
   for a line of prose that has no column to be read out of. The price
   ladder's rate warning is `Chybí kurz, cena není přepočtena.` across
   `I`, `J` and the action cell: measured at 1800x950 the sentence is
   343px in a 344px cell, so ONE character of a retranslated phrase cuts
   it mid-word. It wraps and the row grows instead.
   The only other spanning cells in a `tbody` hold `Přidat řádek` and
   `Přidat` - a button wraps at no width it is ever given. */
tbody td[colspan]{white-space:normal}
/* A CELL OF SEVERAL CHIPS WRAPS THEM, it never cuts one. Plane ODDIA-1096:
   `/orders`' `Stav` with an order's `Probíhá výměna`, `oprava`, `reklamace`
   beside its state cut the second chip at 1440. The chips go onto a second
   line and the row grows, each chip whole on its line; a cell of one chip
   is as before. */
tbody td:has(> .chip + .chip){white-space:normal;text-overflow:clip;
  padding-top:2px;padding-bottom:2px;line-height:19px}
tbody td:has(> .chip + .chip) > .chip{white-space:nowrap}
tbody td.grow{max-width:0}
tbody td.fit{width:1%;overflow:visible;text-overflow:clip}
/* A LINK OUT OF THE ROW STANDS AT THE RIGHT EDGE OF ITS CELL, and the
   address beside it is a line of its own that ellipses in the space left.
   TASKS.md R009: the button used to trail the text, so on an address longer
   than the column the ellipsis cut the button off with it. The button is
   drawn first and floated; the line is a block with its own overflow, so it
   narrows beside the float instead of running under it.
   Shell::openControl() and the list cell loop in Shell::blockTable().
   AND THE COPY BUTTON OF THE SAME ADDRESS STANDS BESIDE IT, the same shape
   floated the same way. TASKS.md DM05: on the Odkazy window one trailed the
   text and the other sat at the edge. Shell::copyControl(), pinned. */
tbody td > a.btn[data-opens],
tbody td > button.btn[data-copy]{float:right;margin-left:var(--sp1)}
tbody td > a[data-opens]:not(.btn){display:block;overflow:hidden;
  text-overflow:ellipsis}
/* A TEXT CUT TO ONE LINE, THE WHOLE ON POINTING. ODDIA-1194: a supplier's
   `VS/REF`, where a long payment message made its row the tallest. A cell
   of a table sized by its content grows, so the cut is on a block inside. */
tbody td > span[data-whole]{display:block;max-width:96px;overflow:hidden;
  text-overflow:ellipsis;white-space:nowrap}
/* AND A BOX BESIDE THE BUTTON NARROWS TO THE ROOM LEFT. ODDIA-1194: the
   item's `Odkaz` box. flow-root and not overflow, so a picker menu under a
   box is never clipped. */
tbody td > a.btn[data-opens] + .fld{display:flow-root}
/* THE WAY INTO THE ROW DRAWN AS A BUTTON, AS WIDE AS ITS COLUMN. Plane
   ODDIA-1104, Jan, 20 October: the ID on /orders becomes "a action buttons
   that are wide as theis column allows". `way_in` 'button', Shell's way-in
   loop. AND A COLUMN THAT IS THE ROW'S FIGURE IS BOLD, `strong`: CZK. */
/* AND THE COLUMN IS NEVER UNDER 80, NUMBER CENTRED. ODDIA-1019, Jan, 22
   October: "100% of the width of the column ... I'm going to fix that to 80
   pixels". On a list the browser sizes from its content a declared 80 was
   squeezed to 55 (/scans) and the button to the width of its digits; the
   button's own floor (80 less the cell's 2 x 8px) holds the column. */
tbody td > a.btn[data-way]{display:flex;width:100%;min-width:64px;text-align:center}
tbody td[data-strong]{font-weight:700}
/* MONEY IN AND MONEY OUT, READ AT A GLANCE. The operator, TASKS.md Y007:
   "If I can I would go with green for + and red with - values, possibly make
   an exception here and make them bold also." A column asks for it with
   'sign' => true and the renderer puts one of these two on the cell from the
   value's own sign; zero gets neither, because zero is not a direction.
   `ok` and `bad` are the mockup's own tone names - see the note in
   Shell::cellClass() on why a purpose-named class is a later pass. */
tbody td.ok{color:var(--s-ok);font-weight:700}
tbody td.bad{color:var(--s-err);font-weight:700}
/* AND AMBER FOR WHAT IS COMING. A column's `mark` (Plane OMV-29): a domain's
   Platnost do from 14 days before its date. `warn` is the mockup's own tone
   name, as `.chip.warn` uses it. */
tbody td.warn{color:var(--s-warn);font-weight:700}
tbody td.ok[data-fill]{background:var(--s-ok-fill);color:var(--s-ok-ink)}
tbody td.bad[data-fill]{background:var(--s-err-fill);color:var(--s-err-ink)}
/* AND A LINK IN A TONED CELL READS IN ITS TONE. Plane ODDIA-1106: a
   gateway's payout late in Platby is red and leads to its movement; the
   link's own blue hid the red. */
tbody td.ok a,tbody td.bad a,tbody td.warn a{color:inherit}
/* AND A BOX IN A CONTENT-WIDTH COLUMN KEEPS ITS OWN WIDTH.
   Every box the renderer draws in a grid carries a `size` computed from
   the widest value in its column, and `width:100%` further down was
   throwing that away - so a typed column could not be content width and
   `Obsah` silently meant "share the leftover" there. It means the same
   thing in every column now. */
td.fit > .f,
td.fit > .fld > .f{width:auto}
/* THE HEADER SIZES THE COLUMN TOO. `.fit` was written for cells only, so
   a column set to Obsah kept the width its header claimed and did not
   shrink. Operator, 27 August. */
th.fit{width:1%;white-space:nowrap}
/* ONE SIZE OF TICK. A checkbox with no size takes the font size of what
   it sits in, and the header is set in the smaller meta type - so the
   select-all was visibly smaller than the ticks beneath it. Sized here,
   once, for both. Operator, 27 August. */
/* ONE TICK, ONE RULE, ONE SELECTOR.
   A checkbox with no size takes the font size of what it sits in, and a
   heading is set in the smaller meta type - so a select-all in a `th` and
   a tick in a `td` came out different sizes. It was written as two
   selectors sharing one block, which is still two places for a later rule
   to reach one of and not the other. It is `table` now: every tick in
   every table, in one place.

   `min-` AS WELL AS `width`, and `flex:none`, because the box sits in a
   label that centres it and a flex item shrinks to its container by
   default. A size anything else can override is not a size. */
/* AND ITS OWN FONT SIZE, WHICH IS THE PART THAT WAS STILL DIFFERENT.
   A checkbox is drawn by the browser, and the box it occupies is not the
   only thing the font size decides: the weight of the frame it draws
   inside that box scales with it too. The heading is set in the 11px
   meta type and a row in the 13px base type, so the same 13px square
   came out with a heavier frame in one and a lighter one in the other -
   the same size and visibly not the same tick. Fixing the box was not
   enough because the box was never the whole of it. */
/* AND THE ARITHMETIC, WHICH IS THE REST OF IT.
     row            28   --row
     tick           14
     above/below    (28 - 14) / 2 = 7      a whole pixel
   At 13 the same sum is 7.5, and half a pixel is not a position: the
   browser rounds it, and which way it rounds depends on where the cell
   happens to start. On a 1080p screen the rounding went the same way in
   both cells and they matched; on a 4K screen at 100% it did not, so the
   heading's tick sat half a pixel high and drew its frame across two
   device rows instead of one - which reads as smaller and blurrier
   rather than as misplaced. Zooming to 110% or 125% moved everything
   back onto whole device pixels, which is why it looked correct there
   and only there.
   14 IS NOT A PREFERENCE. It is the nearest size to 13 that leaves an
   even gap inside a 28px row. Changing `--row` to an odd number breaks
   this again, and the checker says so. */
table input[type="checkbox"]{width:14px;height:14px;
  min-width:14px;min-height:14px;font-size:var(--t-base);
  flex:none;margin:0;vertical-align:middle}
/* A GRID HAS LINES BOTH WAYS.
   The table already draws a line under every row; a spreadsheet draws one
   between every column too, and without them a row of boxes is a row of
   boxes rather than cells. The same colour and the same weight as the
   horizontal ones - nothing new, just the other direction. Operator, 28
   August, having asked twice. */
/* MADE GLOBAL, 30 AUGUST. It was scoped to `.rec` and the operator asked
   for the third time, looking at a list: a table needs lines in both
   directions wherever it is, and a rule that is right inside a record is
   not wrong outside one. Same colour, same weight, one direction added.
   Nothing else about a table changes. */
table th + th,
table td + td{border-left:1px solid var(--c-line)}
/* A TICK COLUMN IS AS WIDE AS A TICK. The renderer used to declare 40
   pixels for it; a checkbox is 13 and a cell is left aligned, so 27
   pixels of slack sat on one side of every tick in the system. No width
   is declared any more and the column takes its content's width, with
   the tick in the middle of it.

   BOTH CELLS, AND THIS IS WHY THEY DIFFERED. The heading holds a bare
   box and a row holds one wrapped in a `label`, because the word rides
   with the box where a row carries its own. Written as a direct child
   the rule matched the heading and not the rows, so the select-all sat
   in a narrow centred cell and the ticks under it did not. Each is
   named for what it actually is. */
/* ONE SELECTOR NOW, because the heading's tick and a row's tick are the
   same markup: a box inside a label inside the cell. They were two
   shapes needing two rules, which is how they came out different
   sizes in the same column. */
th:has(> label > input[type="checkbox"]),
td:has(> label > input[type="checkbox"]){width:1%;text-align:center}
/* THE LABEL FILLS THE CELL AND CENTRES THE TICK IN IT. As an inline
   box it was placed on the text baseline, which is a different offset
   in a heading (11px type) and in a row (13px type) - so the tick sat
   at two different heights in the same column. Filling the cell makes
   the centring arithmetic above the only thing deciding where it sits. */
th:has(> label > input[type="checkbox"]) > label,
td:has(> label > input[type="checkbox"]) > label{display:flex;height:100%;
  align-items:center;justify-content:center;line-height:0;gap:6px}
/* A TICK WITH ITS WORD HAS ROOM BETWEEN THEM, AND A LIVE ONE SAYS IT IS
   PRESSED. Plane ODDIA-1113, Jan, 20 October, on the rights grid:
   *"Checkbox next to číst etc, needs a little space"* and *"Cell triggers
   it, that is good, but hovering does not show the cursor"*. The label
   fills the cell, so the pointer is over the whole cell; a locked tick
   keeps the arrow. The room is `gap` in the rule above. */
td > label:has(> input[type="checkbox"]:not(:disabled)){cursor:pointer}
/* A checklist's choice may wrap onto a second line (above), and at
   line-height 0 the lines of "Příprava materiálu · Latex 01" lay on top
   of each other. ISV5-908. */
table[data-checklist] td:has(> label > input[type="checkbox"]) > label{line-height:1.3;gap:4px}
/* AND SO MAY A TICK IN A COLUMN THAT WRAPS (`wrap`): a right's own name in
   the rights grid, Plane ODDIA-1113. The row grows to hold it. */
tbody td[data-wrap]:has(> label > input[type="checkbox"]) > label{line-height:1.3;gap:6px;
  text-align:left;padding:4px 0}

/* A CHILD TABLE THAT IS WRITTEN IN IS A GRID, NOT A FORM.
   The boxes were fields dropped into cells - each with its own margin,
   its own border and space around it, so the table read as a stack of
   floating inputs with lines between them. In a cell the box IS the cell:
   no margin, full width, and the table's own grid draws the borders.
   Operator, 28 August. */
/* A BOX IN A CELL FILLS THE CELL, WHEREVER IT IS.
   The rule further up answers a box wrapped in `.fld`, which is what a
   record's child table draws. A list draws the box straight into the
   cell, so it kept its own border and its own width and floated in a
   wide column with white either side. Same rule, one selector wider.
   Operator, 30 August. */
td > .f{width:100%;margin:0;border:0;background:transparent;height:auto;
  padding:2px var(--sp1)}
td > .f:focus{outline:0;background:var(--c-alt)}
/* A LANGUAGE BOX IS WIDE ENOUGH TO READ A NAME IN. A language column
   carries no width on purpose (`ModuleService::languageColumns()`), so
   its table is sized from content, and a box that fills its cell has no
   content width of its own: on Metody doručení each name box drew 17px
   and showed one letter, on Platební metody 24px. The floor is on the
   box, so every table that draws `_lang_` gets it, and a table too wide
   for the window scrolls sideways in `.body`. Operator, ISV5-429.
   THE HANDLER'S NAME IS MATCHED IN TWO HALVES, `record` and `translate`,
   because a dot in the selector reads to checks/asset_check.py as a new
   class `.translate`. */
td > input.f[data-cell^="record"][data-cell$="translate"]{min-width:120px}
/* NARROWER SINCE ODDIA-348. Five languages at 220px pushed the countries list on Seznamy
   past the edge and the row actions out of sight (Jan, 8 October); 120px
   still shows a name. A sized table gives the box its column instead: the
   width chosen for the language block is the width. */
table[data-sized] td > input.f[data-cell^="record"][data-cell$="translate"]{min-width:0}
/* A command's output in a cell keeps its lines and wraps inside the cell. OPS-167. */
td > pre{margin:0;font:inherit;white-space:pre-wrap;overflow-wrap:anywhere}

/* A LONG TEXT SHOWS FOUR ROWS OF ITSELF AND HIDES THE REST.
   THE OPERATOR, `TASKS.md` L001: *"we dont need to see all of it, but
   some part of it yes, so make the field display like 3 - 4 rows in this
   particular display, then overflow hidden."*
   FOUR ROWS IS `rows="4"` IN THE MARKUP, not a height here. A height in
   pixels would have to be recomputed every time the interface font or
   --t-base moved, and would be a fifth number nobody could tie to the
   four rows he asked for. What this rule does is the other half: cut off
   what does not fit, and take away the drag handle, which on a cell
   resizes the box out of the column.
   THE LINES NEED ROOM TO BE FOUR LINES. Every control in this system is
   line-height:1 because every other one of them is a single line; four
   of them stacked touch, and the descenders of one sit on the next.
   NOTHING IS UNREACHABLE WITH THE SCRIPT ABSENT. A focused box scrolls -
   it is only the growing that is the script's, and what it grows is
   `style.height`, which is why nothing here may declare one.
   AND CLICKING INTO IT MOVES NOTHING SIDEWAYS. THE OPERATOR, `TASKS.md`
   O002: *"It must not move. It may grow downward. It never shifts
   sideways."* The table is `auto` layout, so every column is sized from
   what its boxes ask for, and a textarea asks for a scrollbar's worth
   more the moment `overflow` stops being `hidden`. The focused column
   took ~3px from the columns beside it and the box slid left. Measured
   on /items?id=100&off=1&sec=texts at 1600x950.
   `width:0` takes the box out of the sizing, `min-width:100%` fills the
   cell it is given. The columns are then what they were before focus,
   to the tenth of a pixel, and stay so while it is focused. */
td > textarea.f{line-height:1.35;overflow:hidden;resize:none;
  vertical-align:top;width:0;min-width:100%}
td > textarea.f:focus{overflow:auto}
/* AND A QUESTION BOX ABOVE IT, IN THE SAME CELL. Plane ODDIA-460, Jan on
   the product's questions: *"two cells in that row under each other ...
   one smaller for the question, one bigger for the answer."* Both boxes
   stand on their own line, the cell's width, with the textarea's rule above
   for the same reason: they leave the column sizing alone. */
td > input.f:has(+ textarea.f){display:block;width:0;min-width:100%;margin-bottom:var(--sp1)}
td > input.f + textarea.f{display:block}
/* AND THE ROW'S NAME STANDS AT THE TOP OF IT, NOT IN THE MIDDLE. The
   operator, 22 September: *"The Pole column aligns TOP. Not vertically
   centred."* A four-row box beside it made the cell 74px tall and the
   browser's own `middle` floated `Pole` halfway down. Keyed on the box
   in the row, for the same reason as the rule above: only Texty draws
   one, so *only on Texty* is true in the markup. */
tbody tr:has(> td > textarea.f) > td:first-child{vertical-align:top}
/* AND NO BOX OF IT TOUCHES THE LINE ABOVE. `TASKS.md` CS01, plan 3.21, his
   words: *"every field touches the top row of its cell. Fix the padding on
   the whole grid, one rule, not per field."* A cell is padded left and
   right and not at all above or below, which is right for a row of one
   line of text and wrong for a row of bordered boxes: the box's own border
   sat on the row's. Keyed on the box in the row, as the two rules above
   are, so it is every cell of every row of every Texty grid and nothing
   else in the system moves. */
tbody tr:has(> td > textarea.f) > td{padding-top:var(--sp1);padding-bottom:var(--sp1)}

/* THE RED DOT AT THE END OF A BOX HE CAN SPEAK INTO. `TASKS.md` O006, his
   words: *"A red dot, with a subtle animation while recording so he can
   see it works."*
   IN THE BOX'S TOP RIGHT CORNER, OVER IT, so the box keeps its width and
   the dot stays put while the box grows downward - O002's rule. The box
   gives up the dot's width on its right so no line of text runs under it.
   The cell is the positioning frame and stays a table cell: `relative`
   is all it is given, never `display` - `a-td-with-display-flex`.
   ON THE ATTRIBUTE AND NOT A CLASS: `data-voice` is written by
   `Shell::fieldCell()` and says what the button DOES, the reason
   `data-unit` and `data-branch` are attributes. No new class, no new
   colour: the dot is `--s-err`, the grey of a refused microphone is
   `--s-off`.
   `aria-pressed="true"` IS RECORDING: the dot beats, slowly, and a ring
   the same red stands round the button. `disabled` is the recording in
   the air to the vendor, and `.btn:disabled` already fades it. */
td:has(> button[data-voice]){position:relative}
td:has(> button[data-voice]) > textarea.f{padding-right:calc(var(--ctl-sm) + var(--sp2))}
button[data-voice]{position:absolute;top:calc(var(--sp1) + var(--sp1));right:calc(var(--sp1) + var(--sp2));
  width:var(--ctl-sm);padding:0;border-radius:50%;display:flex;align-items:center;justify-content:center}
button[data-voice]::before{content:"";width:var(--sp2);height:var(--sp2);border-radius:50%;
  background:var(--s-err)}
button[data-voice][aria-pressed="true"]{border-color:var(--s-err);box-shadow:0 0 0 1px var(--s-err)}
button[data-voice][aria-pressed="true"]::before{animation:voicebeat 1.2s ease-in-out infinite}
button[data-voice][data-refused]::before{background:var(--s-off)}
@keyframes voicebeat{ 0%,100%{transform:scale(1);opacity:1} 50%{transform:scale(1.4);opacity:.55} }

/* A BOX'S EMPTYING BUTTON, `data-clears` (`Shell::field()`), Plane ODDIA-514:
   at the right end of the label's line, so it stands at the end of the box
   it empties and moves nothing. The label is given the button's height so
   the two share one line. On the attribute, like `data-voice`. */
/* AND RED, FILLED, Jan on the card, 9 October: *"make buttons to delete the
   field RED for me"*. The fill `[data-drift="err"]` uses, so it reads as the
   one press that throws text away. */
.fld > button[data-clears]{position:absolute;top:0;right:0;
  background-color:var(--s-err);border-color:var(--s-err);color:var(--c-caption-fg)}
.fld:has(> button[data-clears]) > .lbl{min-height:var(--ctl-sm);padding-right:calc(var(--ctl-sm) * 4)}

/* AND A SET OF ACTS IN A CELL STANDS ON TOP OF ITSELF INSTEAD OF BESIDE.
   THE OPERATOR, `TASKS.md` L002: *"THREE BUTTONS PER TEXT FIELD:
   Generovat, Prelozit, Smazat ... One set per field row, in a last
   column."*
   WIDTH IS WHAT THIS BUYS AND IT IS THE WHOLE ARGUMENT, MEASURED AT
   1600x950 ON THE SCREEN IT SHIPS ON. The record gives this table 941px;
   `Pole` takes 260 and the five language columns share what is left
   EQUALLY, which was 136 each. `.btnrow` is a ROW, so three buttons side
   by side are 266px of that and every language column drops to 83 - on a
   grid whose only purpose is reading paragraphs. Stacked, the column is
   declared 90, draws 103, and the languages keep 115.
   AND THE HEIGHT IS ALREADY PAID FOR. Three `.btn` at 24px with two 4px
   gaps is 80; the four-row text box beside it is 74, so the row goes
   from 74 to 81 and no other table in the system moves at all.
   `td > .btnrow` IS AS NARROW AS THE FEATURE. A `.btnrow` is drawn in
   exactly two places - `Shell::blockActs()`, which puts one under a box
   and never in a table, and `Shell::submitCell()` when a row carries
   more than one act, which today is this grid and nothing else. The same
   reasoning as `td > textarea.f` above: *only on Texty* is true in the
   markup rather than in a rule naming a screen.
   NOTHING IS LOST WITHOUT THIS RULE. The buttons are still three buttons
   in the last cell; they sit in a line and the column is too narrow for
   them, which is a layout this corrects rather than a control it
   creates. */
td > .btnrow{flex-direction:column}
/* AND A ROW'S FEW PRESSES STAY IN A ROW, Plane ISV5-638, Jan on the
   landings: "The actions sit NEXT to each other in the row." The column
   says so, `inline`, `Shell::submitCell()`. */
td > .btnrow[data-row]{flex-direction:row}

/* A PART OF A SECTION IS NAMED AS A FIELD IS, Plane ISV5-638: `sub` on a
   section block, `Shell::blockSection()`. Air above it, as `.secline` has,
   at the step a field's label sits at. */
p.lbl[data-sub]{margin:var(--sp4) 0 var(--sp2)}
p.lbl[data-sub]:first-child{margin-top:0}

/* A FAMILY IS ONE BAND, AND THE ROW IT HANGS OFF IS BOLD.
   Striping by row and colouring by hierarchy fight for the same pixels;
   one device does both. A root and its children share a shade, the next
   family takes the other, and the containment survives a cell that is a
   box - which an indent does not.

   ON AN ATTRIBUTE, NOT A CLASS. The first version used `band` and `root`
   because both names are in the mockup, which kept `asset_check` quiet.
   Both are already styled for other things - `.band` is `flex:none`
   with padding - so putting them on a `<tr>` took the row out of the
   table's layout and the browser split the table in two. A name being in
   the mockup means the checker will not complain, not that the name is
   free to use. Operator caught it, 30 August. */
/* THE BRANCH IS PART OF THE FRAME, NOT PART OF THE NAME. It is a hint
   now that the shading carries the hierarchy, so it takes the colour of
   the rules around it and stops competing with the text. */
[data-branch]{color:var(--c-line-2)}
/* THE TWISTY. It is the one glyph on the row that says "there is more
   under this", so it takes the link colour while the elbow beside it
   stays part of the frame - and the whole cell is the link now, so the
   two have to be told apart by colour rather than by which is clickable.
   `[data-branch]` above keeps the elbow and the leaf's dot grey. */
[data-twist]{color:var(--c-link)}

/* AND THE FOLD LINK ITSELF CARRIES THE ROW'S OWN TEXT COLOUR, because it
   wraps the name: a whole tariff of blue names would read as a list of
   links rather than as a tree with a control in it. Underlined on hover
   like any other link, so it still says it is one. */
a[data-fold]{color:var(--c-text);text-decoration:none}
a[data-fold]:hover{text-decoration:underline}

/* HOW FAR IN A ROW OF A TREE SITS. `data-tier` is the row's own depth,
   set from `tree_depth` and not worked out from its neighbours, so the
   indent is right on the first row of a page as well as the last.

   THE STOREYS OF THE CELNI SAZEBNIK ARE FIVE and the tariff has had that
   shape since 1988, so the ladder is written out rather than generated:
   five rules is shorter than the machinery to avoid writing five rules,
   and a sixth is a change to the Harmonised System.

   THE STEP IS THE PANEL'S - `.tree .l1` and `.l2` are 12px then 24px -
   because they are the same idea twice and a reader moving between them
   should not have to relearn the distance. It is written out in the
   tokens that exist: `--sp6` does not, which is why `.tree .l3` has been
   indenting by nothing since it was written, and this ladder does not
   join it.

   THE WHOLE CELL MOVES, glyph and name together, because the glyph is
   the row's position and the name hangs off it. Indenting only the text
   would leave a column of twisties in a straight line saying nothing. */
[data-tier]{display:inline-block}
[data-tier="1"]{padding-left:var(--sp3)}
[data-tier="2"]{padding-left:var(--sp5)}
[data-tier="3"]{padding-left:calc(var(--sp5) + var(--sp3))}
[data-tier="4"]{padding-left:calc(var(--sp5) + var(--sp5))}

/* A SWITCH THAT CANNOT BE THROWN LOOKS LIKE ONE. It carries `disabled`
   and a title saying why; without a visual difference it reads as a
   control that is broken rather than one that is refused. The cursor
   says so on the way in and the colour says so at a glance. Operator, 1
   September. */
.sw:disabled{opacity:0.45;cursor:not-allowed}

tbody tr[data-band] > td{background:var(--c-alt)}
tbody tr[data-root] > td{font-weight:700}
tbody tr[data-root] > td > .f{font-weight:700}

td > .fld{margin:0;padding:0}
td > .fld > .f{width:100%;border:0;background:transparent;height:auto;
  padding:2px var(--sp1)}
/* A CELL BEING TYPED IN IS TINTED, NOT OUTLINED. An outline inside a
   grid draws a second border a pixel in from the first and reads as a
   box floating in the cell - which is what the operator has been
   objecting to. The cell simply lights up. */
td > .fld > .f:focus{outline:0;background:var(--c-alt)}
/* AND A CELL YOU CAN TYPE IN LOOKS LIKE A FIELD, ONE YOU CANNOT DOES NOT.
   THE OPERATOR, `TASKS.md` ZE02, on the channel's Měny: *"I didn't notice
   the field with the percentage was editable on the table ... make the
   cell with the number visibly a field that you can edit."* The two rules
   above strip the border off every box in a cell, so a typed-in cell and
   a printed one were the same white rectangle with the same number in it.
   The border comes back on the box, the box's own field border, still
   filling the cell with no margin - the grid of 28 August stays a grid.
   A disabled or readonly box keeps no border and stays text; a switch is
   a button and a tick is its own shape, so neither is matched. For every
   table body in the system, which is every editable child. */
tbody td > input.f:not(:disabled):not([readonly]):not([type="checkbox"]):not([type="radio"]):not([type="hidden"]),
tbody td > select.f:not(:disabled),
tbody td > textarea.f:not(:disabled):not([readonly]),
tbody td > .fld > input.f:not(:disabled):not([readonly]):not([type="checkbox"]):not([type="radio"]):not([type="hidden"]),
tbody td > .fld > select.f:not(:disabled),
tbody td > .fld > textarea.f:not(:disabled):not([readonly]){
  border:1px solid var(--c-line-2)}
/* A CELL THE SYSTEM WRITES, in a table where every other cell is typed
   in. `.freeze` is the mockup's own word for a thing held still; the
   cursor says it before anybody clicks. */
td.freeze{cursor:not-allowed;color:var(--c-text-2)}
/* A ROW THAT BELONGS TO THE ROW ABOVE IT.
   The glyph in the first cell says so - `├─` and `└─`, which everybody
   reads without being told. NO TINT: the operator asked for the colours
   back and he was right, a background on top of a shape that already
   reads is noise. */
/* THE GLYPH SITS AT THE LEFT OF THE NAME COLUMN, not centred in it - a
   tree only reads as a tree when its stems line up. */
tbody td.freeze{text-align:left;padding-left:var(--sp2);white-space:nowrap}
/* A column that wraps breaks its text onto lines and keeps its width
   (`wrap` on the column, ODDIA-348: a method's countries), and so does
   its heading (ODDIA-1106). A word longer than the column breaks inside
   itself: `GLS ShopDeliveryService` in a 99px Vlastnosti at 1440. */
tbody td[data-wrap],thead th[data-wrap]{white-space:normal;overflow-wrap:break-word}
/* A HEADING CAPPED BY ITS BOX (`widths_max`) ENDS IN AN ELLIPSIS and is
   whole in its title. Plane ODDIA-348: `ID u dopravce` at 96px. */
thead th[data-cut]{overflow:hidden;text-overflow:ellipsis}

/* WHAT A CELL IS, DECLARED ONCE FOR EVERY CELL IN EVERY TABLE.
   The height and the padding were written three times - once on the
   heading, once on the filter row and once on the body - so a heading and
   a row were the same height only for as long as three numbers agreed,
   and they did not. Any rule that changes a cell's box belongs here or it
   is a second answer to the same question.

   EVERY CELL IS LEFT-ALIGNED UNLESS IT SAYS OTHERWISE. Two rows of the
   same table must line up on the same edge or the eye has to re-find the
   start of every line. Numbers that mean quantities stay right; ids and
   codes are labels, not quantities. Operator, 28 August. */
table th, table td{height:var(--row);padding:0 var(--sp2);text-align:left}
table th.r, table td.r{text-align:right}

/* A GROUP OF ROWS IS SEPARATED FROM THE ONE BEFORE IT.
   A rate and its periods are one thing; the heavier line above the next
   rate says where one ends. NO NEW CLASS AND NO REUSED ONE - `.grp` is
   the nav's word for a group and would be two meanings in one file. The
   row after a tree row is a new group by definition, which CSS can see
   for itself. */
/* THE LINE GOES ABOVE THE NEXT RATE, NOT ABOVE EVERY PERIOD.
   A row with a tree glyph belongs to the row above it; a row WITHOUT one
   that follows a row WITH one is the start of the next group. That is the
   only place the heavier line belongs - it was landing between periods,
   which cut apart the very thing it was meant to hold together. */
/* THE GLYPH IS THE `freeze` CELL RIGHT AFTER THE ID, NOT ANY `freeze`.
   A weight band a delivery method does not price is `freeze` too, and
   `Metody v zemi` drew the line under every such row. Plane ODDIA-482. */
tbody tr:has(> td.m + td.freeze) + tr:not(:has(> td.m + td.freeze)) > td{
  border-top:2px solid var(--c-line-2)}
td > .fld > .lbl{display:none}
/* A CELL THAT SAYS WHAT ITS NUMBER IS COUNTED IN, AND STILL LETS IT BE
   TYPED. `TASKS.md` X002: "Every quantity carries its unit as a prefix:
   bm 1,2, pcs 1, min 30." The prefix is a <span> inside the `.fld`, in
   front of the control, and `.fld` is a block - so without this the unit
   sits on a line of its own and the row is twice as tall. Flex puts the
   two on one line and `min-width:0` lets the box shrink to what is left
   of the column rather than pushing the unit out of it.

   THE FLEX IS ON THE `.fld` AND NOT ON THE `<td>`, WHICH IS `TASKS.md`
   X003 AND IS NOT A PREFERENCE. This rule read `td[data-unit]{display:
   flex}` - and a `<td>` that is not `display:table-cell` is not a cell.
   The table wraps CONSECUTIVE non-cell children of one row in ONE
   anonymous cell, so two prefixed columns beside each other collapsed
   into a single column, stacked, and shifted every cell after them one
   place left. It survived X002 because only one cell per row had a
   prefix; X003 put `GBP` next to `BM` on `Nákupní cena` and it showed.
   The cell is a cell again and the attribute is only the hook. */
td[data-unit] > .fld{display:flex;align-items:center;gap:var(--sp1)}
td[data-unit] > .fld > *{flex:1 1 auto;min-width:0}
td[data-unit] > .fld > span{flex:0 0 auto}
/* TWO BOXES IN ONE CELL, SIDE BY SIDE: one figure held in two columns,
   `with` in `fields.config.php`. Plane ODDIA-282, the channel's `Poplatek
   Kč/%`. The two boxes are the same width whatever their units, and
   `Shell::blockTable()` sizes the column for both; the cell stays a cell. */
td[data-pair] > .fld{display:inline-flex;width:auto;vertical-align:middle}
td[data-pair] > .fld > .f{flex:0 0 72px;width:72px}
td[data-pair] > .fld + .fld{margin-left:var(--sp1)}
tr[hidden]{display:none}
tbody tr.sel td{background:var(--c-alt)}
tbody tr{cursor:default}
td.m,th.m{font-family:var(--f-data)}
td.r,th.r{text-align:right}
td.q{color:var(--c-text-2)}
/* A SECOND LINE IN A TEXT CELL, `Shell::secondLine()`, ODDIA-348: what
   the customer reads in the zone, under the method's own name. */
td > span[data-second]{display:block;font-size:var(--t-meta);color:var(--c-text-2)}
/* AND IT IS CUT LIKE ITS CELL, where the cell may narrow (`grow`): on
   `/equipment` what a disk hangs off ran 500 px wide (ODDIA-1194). */
tbody td > span[data-second]{overflow:hidden;text-overflow:ellipsis}
td.act{overflow:visible}
/* AND A FOUNDING ROW CARRIES TWO CONTROLS WHERE EVERY OTHER ROW CARRIES ONE.
   TASKS.md A003 fault 2 put Uložit beside Smazat on the line being added,
   and the action column is sized for ONE control: `Shell::ACT_WIDTH` is 76
   because `a.btn.btn--mini.btn--danger` measures 51.3 and the cell pays 16
   of padding - RULINGS.md R998, and the 76 is load-bearing, because the ten
   letters of a gridded tab plus this cell add up to the 1 065 a record
   gives a table. Measured at 1800x950: Uložit 41.0 and Smazat 51.3 side by
   side with the space between them need 112.3, so the red frame drew past
   the edge of the card.
   SO THEY STACK, AND ONLY ON THAT ONE ROW. The cell's widest content is
   then 51.3 + 16 = 67.3 and no column moves on any table, gridded or not.
   The line being added is about 52px tall while it is being typed into and
   every other row is untouched - which is the trade `tbody td[colspan]`
   above already makes in as many words: it wraps and the row grows instead.
   Widening the column was the other answer and was refused: 36px of empty
   cell on every row of every calculation table, permanently, to fit a row
   that is on the screen only while somebody is adding one. */
tbody td.act[data-two]{white-space:normal;padding-top:var(--sp1);padding-bottom:var(--sp1)}
tbody td.act[data-two] .btn{display:flex;width:100%}
tbody td.act[data-two] .btn + .btn{margin-top:var(--sp1)}
tfoot td{background:var(--c-panel);font-weight:700;border-top:1px solid var(--c-line-2);
  padding:var(--sp1) var(--sp2)}
/* AND A FOOT MAY CARRY THE ONE FIGURE THE WHOLE SEGMENT IS FOR.
   `TASKS.md` I004: "The minimum selling price displayed BIG, so we know
   what is the minimum we can afford to sell this for and not go into red
   numbers." The price ladder is a table since Y003 and its answer is its
   `<tfoot>`; `Shell::keyValueRow()` wraps that one cell and the block
   says which row it is, so no declaration carries markup.
   NOT WRAPPED, BECAUSE THE COLUMN IS 136px. `CZK 1 973,09` fits that at
   the data size and is ~12px over it at --t-big, so the answer broke
   after `CZK`. It overflows to the right instead, into the action column
   that a ladder never draws anything in, and `tbody td`'s ellipsis does
   not reach a foot. */
tfoot td b{font-size:var(--t-big);white-space:nowrap}

/* ---------- CHIP ---------- */
.chip{display:inline-block;font-family:var(--f-data);font-size:var(--t-meta);line-height:15px;
  padding:0 var(--sp1);color:var(--c-caption-fg);background:var(--c-text-2);text-transform:uppercase;letter-spacing:0.4px}
/* HIDDEN MEANS HIDDEN. `.chip` sets display:inline-block, and a class
   beats the browser's own `[hidden]{display:none}`, so the NEULOŽENO
   marker was visible from the moment a record opened - on every record,
   whether anything had been typed or not. Reported by the operator on
   Jazyky, 27 August. */
.chip[hidden]{display:none}
/* A PARCEL'S STATE, Plane ODDIA-1093: delivered wine, with the carrier
   light green, between orange, cancelled black. Shell::chip(). */
.chip[data-hue="delivered"]{background:var(--o-delivered);color:var(--c-caption-fg)}
.chip[data-hue="handed"]{background:var(--o-handed);color:var(--o-handed-ink)}
.chip[data-hue="waiting"]{background:var(--o-waiting);color:var(--c-text)}
.chip[data-hue="void"]{background:var(--o-void);color:var(--c-caption-fg)}
.chip.ok{background:var(--s-ok)}
.chip.warn{background:var(--s-warn)}
.chip.err{background:var(--s-err)}
.chip.off{background:var(--s-off)}
/* THE DOT THAT STANDS FOR A WORD, its word in the tooltip: HLAVNÍ. ODDIA-348. */
.chip[data-dot]{width:8px;height:8px;padding:0;border-radius:50%;vertical-align:middle;cursor:help}
/* WHAT A PICKER THAT TAKES SEVERAL HAS CHOSEN, `TASKS.md` M004: a chip
   each, under the box, and `×` on each takes it off. `Shell::fieldPickMany()`.
   No class of its own - `.fld` and `.chip` together are the place.

   AS WIDE AS THE BOX ABOVE IT, AND THE `×` IN ONE PLACE. `TASKS.md` Q005,
   his words: *"the boxxes added are correct, cross on them is not really
   visible, maybe make them always to be wide as the field and the delete
   make little more clickable without destroying the layout."*

   IT WAS `inline-block` WITH NO WIDTH, so a chip was as wide as its own
   word: a short one ended mid-cell, a long one filled the cell and wrapped,
   and the `×` therefore landed somewhere different on every line of a
   column of them. That is what made it hard to hit, more than its size.
   `display:flex` at `width:100%` gives every chip the field's own width -
   the field is `w-full` inside its cell, so the two now match at any span -
   with the word on the left and the `×` always at the right edge.

   THE TARGET IS A SQUARE OF `--ctl-sm` rather than one character on a 15px
   line, in `--c-text` so it reads as a control against the chip's own
   `--c-text-2`, and `--s-err` under the pointer because it removes
   something. It does not change the layout: the chip was already the width
   of the cell whenever its word was long, and it is `--ctl-sm` tall, which
   is the height a one-line chip already had with its padding.

   THE WORD STILL WRAPS. `min-width:0` is what lets it: a flex item's floor
   is its content, so without that line a long name pushes the `×` out past
   the right edge of the field instead of wrapping under itself. */
.fld .chip{display:flex;align-items:center;width:100%;min-height:var(--ctl-sm);
  margin:var(--sp1) 0 0;padding:var(--sp1)}
.fld .chip b{font-weight:inherit;flex:1 1 auto;min-width:0}
.fld .chip a{color:inherit;text-decoration:none;flex:0 0 auto;margin-left:var(--sp1);
  width:var(--ctl-sm);height:var(--ctl-sm);line-height:var(--ctl-sm);text-align:center;
  font-size:var(--t-base);background:var(--c-text)}
.fld .chip a:hover{background:var(--s-err)}

/* THE THREE-WAY WAS THREE CHIP BUTTONS HERE AND IT IS NOT ANY MORE.
   `TASKS.md` U002, the fifth asking: "TOGGLE is the toggle. We need a
   toggle - the one we have, that just have the MIDDLE position."

   FIVE RULES STOOD HERE - `button.chip`, the gap between two of them,
   the disabled cursor, the faded contrast and the focus ring - and every
   one of them existed for that control and for nothing else. `.chip` is
   a LABEL again, a `<span>` on every screen that draws one, which is
   what it was before H003 and what `Shell::chip()` has always emitted.

   WHERE IT WENT: `.sw.f[data-at]`, with the switch, below. Same three
   tones, same order, same gate - drawn on the track that was always the
   right asset. Deleted rather than left standing because a dead rule for
   a refused control is the next person's starting point. U002. */

/* ---------- KEY VALUE / SECTIONS ---------- */
/* A table cell carries padding:4px 8px inside the panel's own padding, so
   table text sits 8px further in than a bare list would. The same inset here
   keeps a facts panel and a table on one line as each other. */
.kv{display:grid;grid-template-columns:130px 1fr;gap:2px var(--sp3);margin:0;
  padding:var(--sp1) var(--sp2)}
/* AND A PANEL THAT IS TYPED INTO HAS A COLUMN FOR THE CONTROL.
   `TASKS.md` W001 fault 3: "The labels, the inputs and the amounts each need
   one left edge down the whole block." Three columns, in that order, and
   `Shell::blockKeyValue()` emits the middle one on every row whether or not
   that row carries a control - an empty cell is what holds the edge.
   `max-content` is the widest control in the block and nothing else: the
   80px percentage boxes and the 240px rate select share one edge, and the
   column is exactly as wide as the select rather than a number written here
   that would be wrong the day a field changes width. */
.kv[data-typed]{grid-template-columns:130px max-content 1fr}
/* A PANEL THAT STANDS ON THE TAB'S COLUMN GRID IS NOT A `.kv` ANY MORE.
   FOUR RULES STOOD HERE - the gap, the inset on `dt` and `dd`, the
   right-aligned label and the unwrapped big figure - and every one of
   them was a `<dl>` being persuaded to look like a table cell.
   `TASKS.md` Y003, his sixth asking: "Kalkulace ceny box does not follow
   the colspan as it should, it floats ... Make it a table, make there
   rows as I suggested and make it finally what it should be."
   The pixels those rules bought were correct to within the table's own
   1px border - measured on /items?id=18 before they were deleted - and
   he was never describing an offset. A `<dl>` has no cell frames, no row
   lines and no cell backgrounds, and that is what floats.
   WHERE IT WENT: `Shell::keyValueTable()`, which emits a `<table>` on the
   same four columns every other segment of the tab ends on, and the
   answer in its `<tfoot>`. Nothing here replaces them, because a table
   already carries all four. Deleted rather than left standing: a dead
   rule for a refused control is the next person's starting point. */
.kv dt{font-size:var(--t-meta);color:var(--c-text-2);text-transform:uppercase;letter-spacing:0.5px;padding-top:3px}
.kv dd{margin:0;font-family:var(--f-data)}
/* THE ONE ROW OF A PANEL THAT IS THE ANSWER, AND THERE IS AT MOST ONE.
   `Shell::blockKeyValue()` wraps it, the panel decides which, and no
   declaration carries markup. */
.kv dd b{font-size:var(--t-big)}
/* THE LEAD OF A STATION'S PANEL, `Shell::blockKeyValue()`, Plane ODDIA-60:
   whose it is in bold, then what to do, at the desk's own sizes, filled by
   the tone so the colour says it first - green go on, amber wait, red stop
   and look, blue done. Idle is the panel's own grey. AN ATTRIBUTE AND NOT
   A CLASS, as `data-leaves` says: the tone is the value. */
[data-lead]{margin:0 0 var(--sp3);padding:var(--sp4) var(--sp5);border:1px solid var(--c-line-2);
  background:var(--c-panel);color:var(--c-text)}
[data-lead] b{display:block;font-size:var(--t-big);line-height:1.2}
[data-lead] p{margin:var(--sp1) 0 0;font-size:var(--t-sec);line-height:1.35}
[data-lead="go"]{background:var(--s-ok);border-color:var(--s-ok);color:var(--c-caption-fg)}
[data-lead="wait"]{background:var(--s-warn);border-color:var(--s-warn);color:var(--c-caption-fg)}
[data-lead="stop"]{background:var(--s-err);border-color:var(--s-err);color:var(--c-caption-fg)}
[data-lead="done"]{background:var(--c-accent);border-color:var(--c-accent);color:var(--c-caption-fg)}
/* A PANEL OF PRESSES, `Shell::blockSection()` `pad`, Plane ODDIA-60: two
   columns of equal buttons, each press in the place it always has; one
   that does not apply now is drawn greyed (`form[data-off]`), not taken
   away, so the hand learns the panel. A press with fields of its own
   (`data-wide`) takes the whole row. A form here is only its button. */
[data-pad]{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:var(--sp2)}
[data-pad]>form{margin:0;display:flex;flex-direction:column;justify-content:flex-end}
[data-pad]>form[data-wide]{grid-column:1 / -1;padding:var(--sp2);border:1px solid var(--c-line)}
[data-pad]>form .gate-act{margin-top:0;min-width:0}
[data-pad]>form[data-wide] .gate-act{margin-top:var(--sp2)}
/* AND A LONG LABEL WRAPS INSIDE ITS OWN BUTTON, Plane ISV5-1031. `.btn`
   keeps one line, and a column `minmax(0,1fr)` does not grow for it, so at
   1440 px "Znovu vytisknout štítek dopravce" ran on under the press beside
   it. Here the button is at least its 56 px and as tall as its lines; a
   row of the grid takes its tallest and the shorter press fills it, so the
   pair stays level. */
[data-pad]>form:not([data-wide]) .gate-act{flex:1;align-items:stretch}
[data-pad]>form .gate-act .btn{margin-left:0;flex:1;min-width:0;height:auto;min-height:56px;
  padding:var(--sp1) var(--sp2);font-weight:700;white-space:normal;overflow-wrap:break-word;
  line-height:1.25;text-align:center}
[data-pad]>p:empty{display:none}
/* THE PRESS THE PANEL IS FOR, `go`: grey until it can be pressed, then
   green. Jan, 13 October: "the button send needs to be greyed out until it
   can be hit and it needs to go bright green". */
[data-pad]>form[data-go]:not([data-off]) .gate-act .btn{background:var(--s-ok);border-color:var(--s-ok);
  color:var(--c-caption-fg)}
/* A FORM SENT BY ENTER ALONE, `enter`: the scan box, which a scanner ends
   with Enter. Its button stays in the form, unseen, so Enter presses it. */
form[data-enter] .gate-act{display:none}
/* THE CONSOLE IS ONE SCREEN HIGH, Plane ODDIA-60. Jan, having scanned the
   test sheet: "something that will hold this on the screen and makes it
   100% of the screen, so it doesn't spill out and the person that is
   working with it will not need to scroll". The lead line keeps one height
   whatever it says, the scan box its row, and the two columns share the
   rest, each scrolling inside itself where it holds more. */
body[data-station="console"] .body.pad{display:flex;flex-direction:column;overflow:hidden}
body[data-station="console"] .body.pad>[data-lead]{flex:none;height:96px;box-sizing:border-box;overflow:auto}
body[data-station="console"] .body.pad>form{flex:none}
body[data-station="console"] .body.pad>.cols{flex:1;min-height:0}
body[data-station="console"] .body.pad>.cols>div{min-height:0;overflow:auto}
/* AND A FORM THAT IS ONE BOX AND ITS BUTTON, ON ONE ROW: the scan box and
   OK beside it, where the eye already is. */
form[data-inline]{display:flex;align-items:flex-end;gap:var(--sp2)}
form[data-inline]>div:not(.gate-act){flex:1;margin-bottom:0}
form[data-inline] .fld{margin-bottom:0}
form[data-inline] .gate-act .btn{min-width:120px}
form[data-inline] .gate-act{margin-top:0}
/* A ROW OF EQUAL SLOTS, AND GRID IS THE ONLY WAY IT IS EQUAL.
   TASKS.md Y002 fault 5, his words: "Celni deklarace on Zasobovani is wider
   than the blocks around it ... check if the proper CSS is used and classes
   are not made up or duplicities of something else, I see no way this should
   happen."
   NO CLASS IS INVENTED AND NONE IS DUPLICATED. There is one .cols, one .box,
   and Shell::boxes() already fills a short row with an empty slot so that
   "a row is always perRow slots wide, filled or not". The markup was right.
   THE FLEXBOX WAS WRONG, AND IT WAS WRONG BY EXACTLY THE BOX'S OWN PADDING.
   .cols>* was flex:1, which is flex-basis:0% - and with box-sizing:border-box
   a border-box basis of zero cannot be smaller than the element's own padding
   and border. A .box carries 12px each side and a 1px frame, so it entered
   the share-out 26px ahead of the empty slot beside it. Measured at 1800x950
   on /items?id=101&sec=supply: the two boxes of "Rezim zasobovani | Zasoba"
   came out 541.5 each, and "Celni deklarace | <empty>" came out 554.5 and
   528.5 - the box 13px wider than the identical box in the row above it.
   A GRID TRACK HAS NO SUCH FLOOR. minmax(0,1fr) is the same width whatever
   stands in it, and the `auto` minimum that would have reintroduced content
   sizing is written out. The stylesheet already makes this argument three
   rules down for .fldrow: "Grid rather than flex, because grid subtracts its
   own gaps. The same row in flex needs calc() on every fraction and gets it
   wrong at the edges."
   .cols>* below keeps min-width:0 and its flex properties are now inert on a
   grid item. They are left where they are: removing them is a second change
   to a rule this one already answers. RULINGS.md R1000. */
.cols{display:grid;grid-auto-flow:column;grid-auto-columns:minmax(0,1fr);
  gap:var(--sp2);min-height:0}
/* A THIRD AND TWO THIRDS, Plane ISV5-309, Jan's mail client: the list in one
   column (1/3) and the open message in the other (2/3). Same grid, the same
   minmax(0, ...) floor, two tracks named instead of derived. */
/* A BOX DRAWN BY HALVES PUTS THEM ONE ABOVE THE OTHER WHERE IT IS TOO NARROW
   FOR TWO, Plane ODDIA-1106: the order's `Adresy a doručení` is half a row
   (Jan, 20 October: *"address is a REALLY small thing"*), which at 1440 leaves
   each address 200px and cuts the mail, the town and the country. Side by
   side where the box has room, at 1920. */
.box:has(> .cols[data-halves]){container-type:inline-size}
@container (max-width:520px){.cols[data-halves]{grid-auto-flow:row}}
.cols[data-narrow-first]{grid-template-columns:minmax(0,1fr) minmax(0,2fr)}
/* AND THE OTHER WAY ROUND, Plane ISV5-46: a ticket's conversation in two
   thirds on the left, what we know about the customer in the third beside it. */
.cols[data-narrow-last]{grid-template-columns:minmax(0,2fr) minmax(0,1fr)}
/* AND A STRIP OF PICTURES BEFORE THE WORK, Plane ODDIA-514: a product's two
   pictures side by side with the texts, one above the other. The
   picture fills its card's width and keeps its own height. */
.cols[data-pictures-first]{grid-template-columns:300px minmax(0,900px)}
.cols[data-pictures-first] .card img{width:100%;max-height:none}
/* AND READABLE, Jan on the card, 9 October: *"Make pictures little bigger
   ... So the fields im writing are not across the whole monitor ... bigger
   letters in the fields and only here"*. Pictures 300px, the texts at most
   900px wide (about 95 characters a line at the size below, the 90 the
   description needs), the rest of a wide monitor left empty. The boxes
   own letters 18px, on this layout only: `pictures_first` is declared by
   `/product-copy` alone. A one-line box grows with its letters. */
.cols[data-pictures-first] :is(input.f,textarea.f){font-size:var(--t-read);line-height:1.45}
.cols[data-pictures-first] input.f{height:auto;padding:var(--sp1) var(--sp2)}
/* THE TWELVE-COLUMN ROW, ruled by the operator 20 August.

   Fixed pixel widths inside a box whose width changes are incoherent: 240px is
   a third of one box and all of another. A field is now a FRACTION OF ITS BOX.

   Twelve because it divides by 2, 3, 4 and 6, so 100 / 50 / 33 / 25 all land
   without a remainder - which is exactly what an address needs:
     Ulice 6 · c.p. 3 · c.o. 3      Mesto 4 · ctvrt 4 · PSC 4

   Grid rather than flex, because grid subtracts its own gaps. The same row in
   flex needs calc() on every fraction and gets it wrong at the edges. */
.fldrow{display:grid;grid-template-columns:repeat(12,1fr);gap:0 var(--sp2)}
/* A ROW WAITING ON ANOTHER CONTROL, `shown_by` - TASKS.md R007. `display:grid`
   above beats the browser's own `[hidden]`, the same way `.chip[hidden]` does. */
.fldrow[hidden]{display:none}
.c1{grid-column:span 1}.c2{grid-column:span 2}.c3{grid-column:span 3}
.c4{grid-column:span 4}.c5{grid-column:span 5}.c6{grid-column:span 6}
.c7{grid-column:span 7}.c8{grid-column:span 8}.c9{grid-column:span 9}
.c10{grid-column:span 10}.c11{grid-column:span 11}.c12{grid-column:span 12}
/* A button standing in a row is a cell like any other. It sits on the baseline
   the inputs sit on, under where a label would have been. */
/* A FIELD THAT ENDS IN A BUTTON. Operator, 20 August: the button sits on the
   box edge and the two together fill the width. Twelve equal columns cannot do
   that - a button is as wide as its word, so `ICO 8 / ARES 4` left a dead strip
   between the two. The field takes what is left and the button takes what it
   needs, which is what 1fr auto means. */
.fldrow--act{display:grid;grid-template-columns:1fr auto;gap:0 var(--sp2)}
.fld--act{display:flex;align-items:flex-end}
/* AND A ROW OF SEVERAL FIELDS THAT ENDS IN A BUTTON, Plane ISV5-315: the
   mailbox's server, port, security and `Změnit heslo pro příjem`. The
   fields share what the button leaves in the proportions of their
   twelfths, and the button is as wide as its word, inside the box. */
.fldrow[data-ends]{display:flex;gap:0 var(--sp2)}
.fldrow[data-ends]>.fld{min-width:0}
.fldrow[data-ends]>.c1{flex:1 1 0}.fldrow[data-ends]>.c2{flex:2 1 0}.fldrow[data-ends]>.c3{flex:3 1 0}
.fldrow[data-ends]>.c4{flex:4 1 0}.fldrow[data-ends]>.c5{flex:5 1 0}.fldrow[data-ends]>.c6{flex:6 1 0}
.fldrow[data-ends]>.c7{flex:7 1 0}.fldrow[data-ends]>.c8{flex:8 1 0}.fldrow[data-ends]>.c9{flex:9 1 0}
.fldrow[data-ends]>.fld--act{flex:none}
/* ODDIA-1438: the newsletter form uses the workbench's normal panel inset. */
.cols[data-campaign-editor] > div:first-child{padding:var(--sp4)}
.cols>*{flex:1;min-width:0}
.grid3{display:grid;grid-template-columns:1fr 1fr 1fr;gap:var(--sp2);flex:1;min-height:0}
.fld{margin-bottom:var(--sp3)}
.fld .lbl{display:block;margin-bottom:2px}
/* AND THE NOTE IS GLUED TO THE FIELD IT BELONGS TO.

   THE OPERATOR, `TASKS.md` Z003 fault 4: *"THE NOTES UNDER FIELDS FLOAT
   TOO FAR BELOW THEM. 'Chybi skupina, zalozte ji ve Skupinach' and 'Najit
   kod v celnim sazebniku' leave an impossible gap. Glue each to its field.
   Its gap must not differ from the gap between fields."*

   `margin:0` IS THE WHOLE FIX AND THE CAUSE WAS WRITTEN DOWN ALREADY.
   `Shell::fieldAfter()` draws the note as a `<p>`, and nothing in this
   stylesheet resets the browser's own `p{margin:1em 0}` - which is the
   sentence `Shell::tableSettingsLink()` carries verbatim, where a round
   worked round it by emitting a `<div>` instead of a `<p>`. So the
   mechanism's own comment below - *the label frames the top left, this
   frames the bottom right* - described an intention the UA sheet had
   already defeated.

   MEASURED ON `/items?id=18` AT 1800x950, BEFORE AND AFTER:

     field to its note      11px  ->   1px    (`.hint.r`'s own padding)
     note to the next row   23px  ->  12px    (`.fld`'s margin, as declared)
     field to field         12px  ->  12px    (unchanged)

   `--t-meta` is 11px, so the browser's `1em` was 11px above the note and
   11px below it, and the 11 below stacked on `.fld`'s own 12. That is
   both halves of his sentence: the note stood off its field, and the gap
   under it was twice the gap between two fields.

   IT IS THE `.hint` RULE AND NOT A NEW SELECTOR, because every `<p
   class="hint">` in the system has the same margin for the same reason -
   the note under a field, the button under one, `Tato operace je
   nevratná.` in a delete window. Each of those follows a `<p>` whose own
   bottom margin still separates them. */
.hint{font-size:var(--t-meta);color:var(--c-text-2);padding-top:2px;margin:0}
/* THE COUNTRY'S POSTCODE SHAPE, under the PSČ box and as wide as the row.
   Plane ISV5-456, the operator: in the label of a three-twelfths PSČ the
   six shapes of Spojené království took eight lines and pushed the box
   below Město and Kraj. PSČ is the first cell of its row on every form
   that has one, so the note may run on under the cells beside it: a row is
   four three-twelfths cells and the three gaps between them. A PSČ of the
   whole row needs nothing. */
.c3 > .hint[data-postcode-of]{width:calc(400% + 3 * var(--sp2))}

/* A STATE STANDING IN A FIELD'S LABEL. `IČO | Načteno, 08. 09. 2026`.
   Two colours because a field has one good answer and several bad ones,
   and "nobody asked yet" is one of the bad ones - the operator's own
   ruling: an unverified counterparty is not a neutral state.
   NOT `.hint`, which inside a label is the 14px ringed question mark
   further down and crushed the sentence into a circle. */
/* AND THE THIRD, FOR A STATE THAT IS NEITHER GOOD NOR BAD. `DIČ u
   subjektů mimo EU nelze ověřit` is not a verdict and not a failure -
   there is no register connected that answers it, and nothing anybody
   can press. The accent, which is the system's own blue and already the
   colour of a thing that is simply true.
   A CHIP SINCE 26 SEPTEMBER. The three were coloured text, and `uloženo`
   green on the grey ground of a box was too quiet for the operator to
   see. The same three colours as grounds, under the chip's own light
   letters - the component a list's state column already wears. */
.chip.info{background:var(--c-accent)}
.lbl .chip{display:inline-block;width:auto;min-height:0;margin:0;padding:0 var(--sp1);
  letter-spacing:0.4px;font-weight:700}

/* A LINK OUT OF A FIELD, under the box and hard against its right edge -
   the label frames the top left, this frames the bottom right. */
.hint.r{text-align:right;padding-top:1px}
/* THE LENGTH COUNT UNDER A FIELD. `TASKS.md` O005: green inside the
   field's range, red outside it. Same two tokens as a label state chip. */
.hint.r.ok{color:var(--s-ok)}
.hint.r.err{color:var(--s-err)}

/* THE MARK BESIDE A LABEL. Same class, drawn inline: a small ringed
   question mark that says there is something to hover and nothing more.
   `help` is the cursor browsers use for exactly this, so the pointer
   answers before the tooltip does. */
/* AND NEVER BESIDE A COLUMN HEADING. `thead th > .hint` stood in this
   selector and a rule under it flipped the heading's box downwards,
   because upwards was painted outside the scroller and clipped. Both are
   gone, 13 September, `TASKS.md` H002.

   DOWNWARDS DID NOT WORK EITHER, AND THIS IS THE PART THAT WAS NEVER
   LOOKED AT. A heading is `position:sticky` with a `z-index`, which makes
   it a STACKING CONTEXT: the box's own `z-index:60` is spent inside the
   heading and counts for nothing outside it. The filter row under it is
   the next sticky cell at the same `z-index:3`, later in the document, so
   it painted over the box every single time. What the operator got was a
   black strip with the sentence behind the filter boxes -
   `docs/claudecode/screens/tooltip.png`. Seventeen of them, six screens,
   and the markup was correct in all seventeen.

   The explanation is in `docs/00_PAGE_DOCS.md` now, one row per label,
   which is where a paragraph about a column belongs. `RULINGS.md` R617. */
/* AND BESIDE A HEADING AGAIN, BUT ONLY WHERE JAN ASKED FOR ONE BY NAME
   (`heading_hint`, Plane ODDIA-1029: `Obaly` on the packaging average).
   Both faults above are answered here: the heading under the pointer is
   lifted over the filter row (z-index 5 against its 3), and the box opens
   DOWNWARDS, inside the scroller, over the filter row and the first rows. */
thead th:has(> .hint){overflow:visible}
thead th:has(.hint:hover){z-index:5}
thead th > .hint{vertical-align:middle}
thead th > .hint[data-tip]:hover::after{top:calc(100% + 6px);bottom:auto}
/* AND BESIDE A PRICE BOX, Plane ODDIA-348: the same mark after the box it is
   about (`Shell::fieldCell()`, `{key}_tip`), `tbody` so it outweighs the
   wrapper rule further down that draws no ring. */
.lbl > .hint,
thead th > .hint[data-tip],
tbody td > .hint[data-mark]{display:inline-flex;align-items:center;justify-content:center;
  width:14px;height:14px;padding:0;border-radius:50%;
  border:1px solid var(--c-line-2);line-height:1;cursor:help;
  font-size:10px;position:relative;top:-1px}

/* THE LABEL CENTRES WHAT IS IN IT. The mark sat low because a label is a
   line of text and an inline-flex circle sits on the baseline, which is
   under the middle of uppercase letters. Making the label itself a flex
   row lines them up from the same centre instead of guessing an offset.
   Operator, twice. */


/* THE TOOLTIP IS DRAWN HERE, NOT BY THE BROWSER.
   `title` waits about a second, cannot be styled, and is not shown at all
   on a disabled control - three things the operator asked for and none of
   them in our gift. Drawn from `data-tip` it appears on hover with no
   delay, in the system's own colours, and works on a wrapper around a
   dead switch. No script: hover is a CSS state.
   Operator, 1 September, having asked five times. */
/* A CELL THAT HOLDS A TOOLTIP MUST NOT CLIP IT.
   `tbody td` is `overflow:hidden` so long text ellipses, which also cut
   the tooltip off at the cell edge the instant it was painted - the
   markup was right, the wrapper was there, the box was drawn and then
   clipped away. Two hours of the operator asking why nothing appeared.
   Only cells that actually carry one are opened up, so nothing else
   loses its ellipsis. */
tbody td:has(> [data-tip]){overflow:visible}

/* AND NEITHER MAY IT CLIP THE LIST A PICKER OPENS.
   THE SAME FAULT, THE SAME CELL, A DIFFERENT CONTROL, AND IT IS THE ONE
   THE OPERATOR REPORTED ON 13 SEPTEMBER, TASKS.md H004:
   *"The dropdown lists nothing, it's hidden under the listing, there are
   graphical glitches, probably the same problem as in Zarazeni."*

   IT LISTED EVERYTHING. The 69 attribute groups were all in the markup,
   the menu took `.open`, `display` computed to `flex`, and its box was
   measured 118 x 240 at the right place. `tbody td` is `overflow:hidden`
   for the ellipsis, the cell is 19px tall, so 240px of menu was painted
   and 238 of them were cut off inside the cell - which reads exactly like
   a dropdown that lists nothing. Three tabs draw a picker inside a child
   row - Zarazeni, Atributy and Slozeni - and it was broken on all three.

   ONLY THE CELLS THAT CARRY ONE, like the rule above, so every other cell
   keeps its ellipsis. `.fld` is the wrapper the shell puts round a field
   and `.umenu` is the list; a cell holding one is a cell a person opens
   something out of. */
tbody td:has(.fld > .umenu){overflow:visible}

/* AND THE ROW IT STANDS ON IS PAINTED BEFORE THE ROWS BELOW IT.
   `tbody td` carries its own background - so with the cell opened up, the
   menu hung over the NEXT row's cells and those cells painted over it,
   because both are unpositioned and the later one in the document wins.
   The row holding an open list is lifted out of that order for as long as
   the list is open, and nothing else on the page moves. */
tbody tr:has(.umenu.open){position:relative;z-index:2}

/* AND IT IS AS WIDE AS WHAT IT OFFERS, NOT AS WIDE AS THE CELL.
   `.fld .umenu` is `width:100%`, which is right under a field in a box
   and wrong inside a 118px column: `Typ analniho koliku` drew as
   `Typ analniho koli`. It grows to its content, never past 340px - the
   same ceiling the tooltip above it uses - and never narrower than the
   cell it hangs off. */
td .fld .umenu{width:max-content;min-width:100%;max-width:340px}

/* AND IT OPENS INWARDS FROM A CELL AT THE RIGHT-HAND EDGE.
   `left:0` is the whole rule below, so the box always grows rightwards -
   and every tooltip left in a table body is in one of the last two
   columns, which is where the shell puts the row switch and `Upravit /
   Smazat`. A 340px box growing rightwards out of a 100px cell hard
   against the right edge runs past `.body`, which is `overflow:auto`, and
   the far end is cut off. On Položky that ate two thirds of the sentence
   and left `Položka s...` readable. Found on 13 September by hovering it,
   H002, which is the thing that had not been done: the markup was right,
   the box was drawn, and nobody had looked at the screen.
   `:nth-last-child` is the structure saying it, not a guess - it counts
   from the right, which is the side that overflows. */
tbody td.act [data-tip]::after,
tbody td:nth-last-child(-n+2) [data-tip]::after{left:auto;right:0}

/* AND A CONTROL'S ANSWER IS THE SAME BUBBLE, HELD OPEN FOR A MOMENT.
   Operator, 12 September, on the copy button: *"I'm not a fan of toasts
   whatsoever, you usually don't see it until it's late anyway, just
   response from the button, tooltip like would be enough."* So the answer
   is not a second mechanism in the corner of the screen: it is this box,
   at the control, with the confirmation in `data-tip` for as long as
   `data-shown` is on it. One selector list and one box, because a
   tooltip and an answer that looked alike but were built twice is the
   fault Z002 was raised for.

   NOT A HOVER, SO IT IS NOT A `:hover` RULE. The pointer is usually on
   the button when it is pressed and then the two agree anyway; a button
   reached by keyboard has no pointer on it at all and still has to
   answer. `shell.js` takes the attribute off again. */
[data-tip]{position:relative}
[data-tip]:hover::after,
[data-tip][data-shown]::after{content:attr(data-tip);position:absolute;z-index:60;
  left:0;bottom:calc(100% + 6px);min-width:180px;max-width:340px;
  background:var(--c-chrome);color:var(--c-caption-fg);
  border:1px solid var(--c-chrome-line);border-radius:4px;
  padding:var(--sp1) var(--sp2);font-size:var(--t-meta);font-weight:400;
  line-height:1.35;text-transform:none;letter-spacing:0;white-space:normal;
  pointer-events:none}

/* A WRAPPER AROUND A REFUSED SWITCH IS NOT A QUESTION MARK. It carries
   the tooltip and nothing else, so it draws no ring and takes no space. */
/* The wrapper carries the tooltip and nothing else - no ring, no space.
   The cursor stays on `.sw:disabled` above, where it belongs: a wrapper
   around a switch that CAN be thrown must not say it cannot. */
td .hint[data-tip]{display:inline-block;width:auto;height:auto;border:0;
  padding:0;margin:0}
.err-t{font-size:var(--t-meta);color:var(--s-err);padding-top:2px}
/* A FILE BOX'S LINE, WHEN THE SELECTION IS OVER WHAT ONE REQUEST TAKES.
   `TASKS.md` BU02: `shell.js` marks it `data-refused` and the press does not
   send; the refusal's own colour, on the line that already says the count. */
[data-files][data-refused]{color:var(--s-err)}
/* A READONLY BOX WHOSE VALUE HAS A DIRECTION. TASKS.md Z006: a
   movement's amount is green going in and red going out on the record,
   which is what tbody td.ok and td.bad already do on the list, so the
   tab and the record cannot read as two different figures. Same two
   tone words, same two tokens, applied to the box instead of the cell.
   `bad` ALSO MARKS A STALE TRANSLATION, TASKS.md U003: a language box
   whose words were made from Czech since rewritten. Those boxes are
   textareas on Texty, so the rule covers both. */
input.f.ok{color:var(--s-ok);font-weight:700}
input.f.bad,textarea.f.bad{color:var(--s-err);font-weight:700}
/* AND THE PRICE A BAD BOX ONLY FOLLOWS, Plane ISV5-726. An empty cell on
   the channel's Doprava shows the price it inherits as its placeholder,
   and `bad` there means that inherited price is under the minimum. The
   browser's grey made it read as fine beside the red typed boxes, so the
   placeholder takes the same red, lighter, still marked as carried. */
input.f.bad::placeholder,textarea.f.bad::placeholder{color:var(--s-err);opacity:.6}
.secline{font-size:var(--t-sec);font-weight:700;border-bottom:1px solid var(--c-line-2);
  padding-bottom:var(--sp1);margin:var(--sp5) 0 var(--sp2)}
.secline:first-child,.body.pad > .cols:first-child .secline:first-child{margin-top:0}

/* ---------- A SECTION THAT FOLDS ---------- */
/* `TASKS.md` I004: *"ONE TAB CALLED KALKULACE. Collapsible sections inside
   it, as v4 had."* Five subjects on one tab, four of which a person reads
   once a month, is a tab nobody scrolls to the bottom of.

   NO NEW CLASS, AND THAT IS DELIBERATE. `details` and `summary` are the
   elements the browser already has for this, they work with no script and
   with no JavaScript at all, and the fold survives a form post because
   nothing about it is state anybody has to keep. The heading reads exactly
   like `.secline` above, because it IS the same heading. */
/* AND IT CARRIES NO MARGIN OF ITS OWN. `TASKS.md` X001: *"THE <details>
   ELEMENTS CARRY AN ENORMOUS MARGIN. Remove it."* It had `--sp5` above
   and `--sp2` below, copied from `.secline` - which is a heading that
   sits among other things inside one box and needs air above it. A
   folding section is the ONLY thing in its box, and the box already pays
   `--sp3` on every side plus `--sp2` to the box under it, so the 24px
   was added to 12 that were already there and 8 more below: 56px of
   nothing between one table and the next heading, five times down
   `Kalkulace`. The gap between two sections belongs to the boxes, like
   every other gap in a record. */
details{margin:0}
details > summary{font-size:var(--t-sec);font-weight:700;cursor:pointer;
  border-bottom:1px solid var(--c-line-2);padding-bottom:var(--sp1);
  list-style-position:inside}
details > summary:hover{color:var(--c-accent)}
details[open] > summary{margin-bottom:var(--sp2)}

/* ---------- TABS ---------- */
.tabs{flex:none;display:flex;background:var(--c-panel);border-bottom:1px solid var(--c-line-2);padding:0 var(--sp2)}
.tabs a{font-size:var(--t-base);padding:var(--sp1) var(--sp3);color:var(--c-text-2);text-decoration:none;
  border-bottom:2px solid transparent}
.tabs a.on{color:var(--c-text);font-weight:700;border-bottom-color:var(--c-accent);background:var(--c-surface)}

/* ---------- BANDS ---------- */
.band{flex:none;padding:var(--sp1) var(--sp2);font-size:var(--t-base);
  background:var(--c-alt);border:1px solid var(--c-line);
  display:flex;flex-wrap:wrap;gap:var(--sp2);align-items:baseline}
.band b{font-family:var(--f-data)}
/* THE MOVING HALF OF IT, `TASKS.md` M019: the crop dialog's second band
   says which sizes the box ON SCREEN will not have, and there is nothing
   to say while it can have them all. `display:flex` beats the browser's
   own `[hidden]`, the same way `.chip[hidden]` does. It wraps because the
   list is as long as the box is small. */
.band[hidden]{display:none}
.empty{padding:var(--sp5);text-align:center;color:var(--c-text-2)}

/* ---------- RECORD OVERLAY ---------- */
.scrim{position:fixed;top:var(--top);left:0;right:0;bottom:0;
  background:rgba(0,0,0,0.40);display:none;z-index:10}
.scrim.open{display:block}
/* THE THREE LAYERS, RULED BY THE OPERATOR 20 AUGUST.

   Handoff 18 put the grey on the record body and the white on the box. That
   moved the white-on-white one level in: the box was white and the field was
   white, so a field still had no edge. Third time the same grey has been moved.

   Inverted, and this time it is a rule rather than a place:

     record body  --c-surface   the same white as .win, so a record body and a
                                page body finally agree
     box          --c-panel     furniture, the same grey as .bar, .acts, thead
     field        --c-surface   data

   FURNITURE IS GREY. DATA IS WHITE. That is section 3 of spec-is-visual said
   as a rule, and it makes "data is the white thing" stronger rather than
   weaker - which is the argument that killed tinting the inputs. */
.rec{position:fixed;top:var(--top);right:0;bottom:0;
  width:62%;min-width:720px;max-width:calc(100% - 260px);display:none;z-index:11;
  flex-direction:column;background:var(--c-surface);border-left:1px solid var(--c-line-2)}
/* AND A RECORD SHOWING A TABLE IS AS WIDE AS IT IS ALLOWED TO BE.
   `TASKS.md` Y001 fault 4, and it is the half of it he offered first:
   "Either the record is wider for a table like this, which he has already
   permitted, or the table scrolls." Both. This is the first.
   62% of 1600px is 991px and the movements tab asks for 1332, so on the
   tab that holds a table the record takes every pixel it is allowed.
   THE TEST IS THE SECTION ON SCREEN AND NOT THE RECORD. A first draft
   widened any record that had tabs at all, and the person record then
   drew thirteen single-line boxes 1290px wide - a form does not want the
   width and a ten-column table cannot do without it, and both live in
   the same record. `[data-sec]:not([hidden])` is the section the tab band
   has opened, so Záznam stays 62% and Pohyby is wide, in the same
   record, with nothing declared anywhere.
   AND IT IS A `data-wide` TABLE AND NOT ANY TABLE.
   `Shell::tableSizing()` marks only a table whose own columns ask for
   more than an ordinary record can give them, and the comment there says
   where the number comes from. Two drafts got this wrong in the same way
   and both are worth knowing: on `table` alone, and then on any sized
   table, the person record's Přehled qualified on a 482px table and drew
   thirteen single-line boxes 1290px wide. A record is widened by a table
   that needs it, never by a table that merely exists.
   `max-width` is unchanged and still leaves 260px of the list showing,
   which is what stops a record reading as a page. */
/* AND A RECORD WHOSE SECTIONS ARE A LIST DOWN THE LEFT, `TASKS.md` ZE06, has
   its body one level down, inside the pages' `.split`, beside the `.tree`
   that lists them. Same test, same width. And the card is wider by the
   list's own width, `--nav`, so a section keeps the width it had under a
   strip; without it `Přirážky` lost 180px and its switches went off the
   edge. `max-width` is the same limit, so the list behind still shows.
   A TICKET'S CONVERSATION IS THE THIRD, Plane ISV5-46: Jan, *"Odpověďet
   should be a window"*, a chat on the left and the customer on the right,
   which an ordinary record's 62% cannot hold side by side. */
.rec:has(> .split){width:calc(62% + var(--nav))}
.rec:has(> .body > div[data-sec]:not([hidden]) table[data-wide]),
.rec:has(> .split > .body > div[data-sec]:not([hidden]) table[data-wide]),
.rec:has(> .body > div[data-sec]:not([hidden]) .thread[data-chat]){width:calc(100% - 260px)}
/* AND A RECORD THAT DECLARES `record_wide` IS THAT WIDTH ON EVERY TAB. Plane
   ODDIA-282, Jan: *"each menu item shows differtn width of the record,
   please, lets set on one. I have n problem with the wider solution."* The
   test above moved the channel record when a section held a wide table, and
   when switching a payment method off narrowed a matrix under 1065px. */
.rec[data-broad]{width:calc(100% - 260px)}
.rec[data-sub][data-broad]{width:calc(100% - 260px - var(--sp3) * 2)}
.rec .box,.win .box{background:var(--c-panel);border:1px solid var(--c-line);
  padding:var(--sp3)}
/* THE GAP BELONGS TO THE ROW, NOT TO THE BOX.
   It was on the box, with :last-child clearing it - which inside a .cols row
   meant the LAST box had no bottom margin and its siblings did. Flex stretches
   all of them to the same outer height, so the ones carrying the margin came
   out 8px shorter and a row of boxes ended at two different heights. Moving it
   to the row makes every box in a row exactly as tall as the tallest. */
.rec .box,.win .box{margin-bottom:var(--sp2)}
/* AND A BOX WHOSE TABLE IS WIDER THAN IT SCROLLS SIDEWAYS INSIDE ITS OWN
   BORDER, so nothing stands out of the frame. Plane ODDIA-348, operator:
   the carrier's methods at 1366. `shell.js` says `data-scroll` only while
   the content is wider: a box that scrolls is the scroller its table's
   heading sticks to, and on every box that fits the heading keeps
   sticking to the record. */
.rec .box[data-scroll],.win .box[data-scroll]{overflow-x:auto}
.rec .cols > .box,.win .cols > .box{margin-bottom:0}
.rec .cols,.win .cols{margin-bottom:var(--sp2)}
/* AND A COLUMN OF THE ROW MAY HOLD BOXES ONE UNDER ANOTHER, `Shell::boxes()`
   `stack`, in the `.stack` the work area already is (a column with the row's
   gap). Plane ODDIA-282: the channel's `Obecné` on the left, the two delay
   boxes on the right; the last one reaches the foot of the row so both
   columns end level. */
.rec .cols > .stack > .box{margin-bottom:0}
.rec .cols > .stack > .box:last-child{flex:1 1 auto}
/* AND A SWITCH THAT IS UNLOCKED BEFORE IT MOVES, `Shell::fieldCheckbox()`
   `unlock`, ODDIA-282: faded and deaf to the pointer until the button beside
   it is pressed. Attributes, not classes: the state is the script's. */
[data-swlock]{display:inline-flex;align-items:center;gap:var(--sp2)}
.sw[data-lock]{opacity:.55;cursor:not-allowed}
.rec.open{display:flex}

/* ---------- A RECORD STANDING ON A RECORD ---------- */

/* `TASKS.md` Z005, and the sentence is his:
     "the detail opens another record modal over the first one and has two tabs,
      making unclear how you can go back ... it needs to be visible it's opened
      above the record and can be independently closed, now it covers the
      original record modal 1:1 ... the secondary record can be little smaller,
      shifted."

   NO NEW CLASS, AND THAT IS NOT A DODGE. `.rec` is the record card and a
   sub-record IS a record card - what is new is that one of them may be standing
   on another, which is a STATE of the card rather than a second component.
   `data-sub` is the mechanism the file already uses for a state the stylesheet
   has to see: `table[data-wide]` and `div[data-sec]` three rules above are the
   same thing. One element, one set of rules, nothing to keep in step.

   THE INSET IS THE SAME NUMBER ON ALL FOUR EDGES, so twelve pixels of the card
   underneath show on the left, on the right and at the foot, and its caption
   band shows above. A card inset on one edge only reads as misaligned rather
   than as stacked. The shadow is what says which of the two is on top. */
.rec[data-sub]{top:calc(var(--top) + var(--band) + var(--sp2));
  right:var(--sp3);bottom:var(--sp3);
  width:calc(62% - var(--sp3) * 2);min-width:calc(720px - var(--sp3) * 2);
  max-width:calc(100% - 260px - var(--sp3) * 2);
  border:1px solid var(--c-line-2);
  box-shadow:0 var(--sp1) var(--sp4) rgba(0,0,0,0.45)}

/* AND THE TOP EDGE OF THE CARD UNDERNEATH IS THE SAME CAPTION BAND. It holds
   the identity of the record this one was opened from and the way back to it at
   its right end, which is where a list's cogwheel stands - a caption has one
   kind of link and this is it. Drawn at the footprint the record underneath
   would have, so the card on top sits inside it on every edge.
   NOT A SECOND RECORD. Only one `.rec` is ever drawn; rendering the account
   again to put a picture of it behind the movement would fetch 4 634 rows
   nobody can see. What is behind this band is the list, which is what was
   behind the account record too. */
.cap[data-under]{position:fixed;top:var(--top);right:0;width:62%;
  min-width:720px;max-width:calc(100% - 260px);z-index:11;
  border-left:1px solid var(--c-line-2)}

/* AND WHERE THE STACKED CARD IS THE WIDE ONE, SO IS THE BAND OVER IT. The rule
   above widens any record whose open tab holds a table its own columns
   overflow, and a card wider than the band it is standing on reads as two
   unrelated things. Both selectors carry `[data-sub]`, so a record that is not
   stacked is untouched by either. */
.rec[data-sub]:has(> .body > div[data-sec]:not([hidden]) table[data-wide]),
.rec[data-sub]:has(> .split > .body > div[data-sec]:not([hidden]) table[data-wide]){
  width:calc(100% - 260px - var(--sp3) * 2)}
body:has(.rec[data-sub] > .body > div[data-sec]:not([hidden]) table[data-wide]) .cap[data-under],
body:has(.rec[data-sub] > .split > .body > div[data-sec]:not([hidden]) table[data-wide]) .cap[data-under]{
  width:calc(100% - 260px)}
/* THE FOOTER HOLDS A MARKER ON THE LEFT AS WELL AS BUTTONS ON THE RIGHT.
   Operator's ruling of 20 August, restating v4: "Neuložené změny" sits
   left-aligned in the record footer. .sp is the same spacer .top, .bar,
   .stat and .gate-act already use; this is its fifth context, not a new
   idea. align-items stops the chip stretching to the full height of the
   band, which is what a flex item does by default. */
.acts{flex:none;height:var(--acts);background:var(--c-panel);
  border-top:1px solid var(--c-line-2);
  padding:0 var(--sp2);display:flex;justify-content:flex-end;gap:var(--sp2);
  align-items:center}
.acts .sp{flex:1}

/* ---------- CONFIRM ---------- */
.conf{position:fixed;inset:0;background:rgba(0,0,0,0.40);display:none;z-index:20;
  align-items:center;justify-content:center}
.conf.open{display:flex}
/* The result of an action, said in the same box that asks the questions.
   An error under a field was two lines of small red text at the edge of a
   window somebody had stopped looking at - reported unseen twice. The bar
   carries the verdict; the colours are the existing status tokens and the
   classes are the ones .stat already uses. */
.conf .cap.ok{background:var(--s-ok)}
.conf .cap.err{background:var(--s-err)}
/* AND A DIALOG DRAWN INSIDE A RECORD IS NOT ONE OF THE RECORD'S BOXES:
   no padding, no margin. Plane ODDIA-306, Jan on `Stav obchodu`'s warning:
   *"unnecessary padings, its have huge whit outline"*. The window is written
   beside its field, so `.rec .box` above gave its own .box the panel's 12px
   of padding and 8px under it, a white frame round the band, the text and
   the buttons. */
.conf .box{width:460px;background:var(--c-surface);border:1px solid var(--c-line-2);display:flex;flex-direction:column;
  padding:0;margin-bottom:0}
/* A LIST THAT OPENS INSIDE A DIALOG NEEDS ROOM TO OPEN INTO.
   `.body` scrolls, so the menu was clipped to whatever height the fields
   happened to need - one visible line out of 516, with the dialog's own
   scrollbar over it. The dialog now gives its body room for about six
   entries and lets the menu spill out of it rather than scroll inside.
   Operator, 27 August. */
.conf .box .body{overflow:visible}
/* AND ITS TEXT IS NOT SPACED BY THE BROWSER.
   TASKS.md A003 fault 4, his words: "THE DELETE MODAL HAS ABSURD PADDING.
   He has reported this before. Fix the modal component, not this one
   instance."
   THE PADDING WAS NEVER THE FAULT. A dialog's body is .body.pad - 12px on
   all four sides, which is --sp3 and the step every panel in the system
   already stands on. What nobody had ever set is the paragraph inside it,
   so the browser's own 1em margins were added to that 12 and doubled it.
   Measured on ?confirm=childdel at 1800x950: the body drew 84.4px around
   34.4px of words - 12 + 13 + 17.5 + 13 + 16.8 + 12 - and the box 151.4.
   Zeroed, with one spacing step between two paragraphs, the same body is
   58.4 and the box 125.4: 12px of air on every edge, like every other box.
   IN THE COMPONENT AND NOT ON THE QUESTION. `.conf` is every dialog in the
   system - the delete, the unlock, the unsaved-changes warning, the verdict
   of an action, the create windows - so they now read the same, which is
   what "fix the modal component" asks for. A dialog holding FIELDS is
   untouched: a .fld carries its own spacing and is not a <p>. */
.conf .box .body > p{margin:0}
.conf .box .body > p + p{margin-top:var(--sp2)}
.conf .box:has(.umenu) .body{min-height:264px}
/* AND IT STOPS ABOVE THE FOOTER. The body is 264 tall, the label and the
   input take about 48 of it, so 200 of list ends where the buttons
   begin. A dropdown lying over Založit is a dropdown you cannot get out
   of the way. */
.conf .box .fld .umenu{max-height:200px}
/* AND A DIALOG HOLDING FACTS KEEPS THEM INSIDE ITS OWN EDGE.
   TASKS.md CT01: `Synchronizovat s webem` on /categories says what a press
   would send before it sends, as a .kv. `1fr` is `minmax(auto,1fr)`, so one
   value with no space in it - a picture's code, an address - set the column
   to its own length and every line ran out through the right-hand border.
   The column may now shrink and such a value breaks where it must; 640 and
   not 460 because a fact is a label and a sentence, where a question is one
   line. No class: it is the same .box, holding a .kv. */
.conf .box:has(.kv){width:640px}
.conf .box .kv{grid-template-columns:130px minmax(0,1fr)}
.conf .box .kv dd{overflow-wrap:anywhere}
/* The label breaks in its own 130px too: a file's name is the label of
   its `Otevřít soubor` in the accountant's letter, and ran under it
   (ODDIA-1223). */
.conf .box .kv dt{overflow-wrap:anywhere}
.leave-kv{grid-template-columns:150px 1fr}
.reveal dd a{font-family:var(--f-data)}
.leave-thanks{display:none}
.leave-kv dt.leave-thanks{font-weight:700;color:var(--c-text)}
#leaveBoxInner.is-going .leave-thanks{display:block}
#leaveBoxInner.is-going .acts{display:none}

/* ---------- TOAST ---------- */
.toasts{position:fixed;right:var(--sp3);bottom:var(--sp3);display:flex;flex-direction:column;gap:var(--sp1);z-index:30}
.toast{background:var(--c-caption);color:var(--c-caption-fg);border:1px solid var(--c-caption);
  padding:var(--sp1) var(--sp2);font-size:var(--t-base);min-width:260px;
  display:flex;justify-content:space-between;gap:var(--sp2)}
.toast a{color:#c3c8ce}

/* ---------- swatches (reference screen only) ---------- */
/* ---------- LOCK / LOGIN ---------- */

/* ---------- PICKER ---------- */

/* ---------- PHOTOS ---------- */
.photos{display:flex;flex-wrap:wrap;gap:var(--sp2);padding:var(--sp3)}
.ph{width:132px}
.ph .im{height:132px;border:1px solid var(--c-line-2);background:var(--c-alt);
  display:flex;align-items:center;justify-content:center;
  font-family:var(--f-data);font-size:var(--t-meta);color:var(--c-text-2)}
.ph.on .im{border:2px solid var(--c-accent)}
.ph .n{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;padding-top:2px;
  display:flex;justify-content:space-between;gap:var(--sp1)}
.ph .k{padding-top:2px}

/* ---------- PRINT PREVIEW ---------- */
.paper{background:var(--c-surface);border:1px solid var(--c-line-2);
  width:420px;padding:var(--sp4);font-size:var(--t-meta);flex:none}
.paper h3{font-size:var(--t-sec);margin:0 0 var(--sp1)}
.paper .ph-head{display:flex;justify-content:space-between;
  border-bottom:1px solid var(--c-text);padding-bottom:var(--sp1);margin-bottom:var(--sp2)}
.paper table{font-size:var(--t-meta)}
.paper th{background:transparent;border-bottom:1px solid var(--c-text);position:static;color:var(--c-text)}
.paper td{height:auto;border-bottom:1px solid var(--c-line)}
.paper .foot{border-top:1px solid var(--c-text);margin-top:var(--sp3);padding-top:var(--sp1);
  display:flex;justify-content:space-between;color:var(--c-text-2);font-family:var(--f-data)}
.paper .pchip{border:1px solid var(--c-text);padding:0 3px;font-family:var(--f-data);font-size:10px}
.label{background:var(--c-surface);border:1px solid var(--c-line-2);width:240px;
  padding:var(--sp2);font-family:var(--f-data);font-size:var(--t-meta);flex:none}
.bc{display:flex;align-items:flex-end;height:44px;gap:1px;margin:var(--sp2) 0}
.bc i{background:var(--c-text);height:100%}
.bc i.w1{width:1px}.bc i.w2{width:2px}.bc i.w3{width:4px}
.bc i.g{background:transparent}

/* ---------- CHART ---------- */
.chart{padding:var(--sp3)}
.chart .rowc{display:flex;align-items:center;gap:var(--sp2);height:var(--ctl)}
.chart .rowc .lb{width:110px;font-size:var(--t-meta);color:var(--c-text-2);text-align:right}
/* A bar may be in parts, side by side (ODDIA-691: the staff in the
   accent, the rest grey), and a month still to come is faded. */
.chart .rowc .tr{flex:1;display:flex;background:var(--c-alt);height:14px;border:1px solid var(--c-line)}
.chart .rowc .tr i{display:block;height:100%;background:var(--c-line-2)}
.chart .rowc .tr i.hi{background:var(--c-accent)}
.chart .rowc[data-ahead] .tr i{opacity:.45}
.chart .rowc .vl{width:90px;font-family:var(--f-data);font-size:var(--t-meta);text-align:right}
.chart .axis{display:flex;justify-content:space-between;font-family:var(--f-data);
  font-size:var(--t-meta);color:var(--c-text-2);border-top:1px solid var(--c-line);
  margin:var(--sp1) 98px 0 118px;padding-top:2px}
/* Upright (ODDIA-691, Jan: months read left to right, bars grow up):
   a column a month, its parts stacked from the foot, the value over it,
   the label (and the year where it changes) under it, the axis at the
   left. Too many months for the width scroll sideways. */
.chart[data-up]{display:flex;gap:var(--sp2);align-items:flex-start}
.chart[data-up] .axis{flex:none;display:flex;flex-direction:column;justify-content:space-between;
  align-items:flex-end;height:220px;margin:18px 0 0;padding:0;border:0;white-space:nowrap}
.chart[data-up] .axis span{line-height:1;transform:translateY(-50%)}
.chart[data-up] .axis span:last-child{transform:translateY(50%)}
.chart[data-up] .cols{flex:1;min-width:0;display:grid;grid-auto-flow:column;
  grid-auto-columns:minmax(56px,1fr);gap:2px;overflow-x:auto}
.chart[data-up] .rowc{display:flex;flex-direction:column;align-items:stretch;height:auto;gap:0}
.chart[data-up] .rowc .vl{width:auto;height:18px;line-height:18px;text-align:center;white-space:nowrap}
.chart[data-up] .rowc .tr{flex:none;height:220px;flex-direction:column-reverse;
  background:linear-gradient(var(--c-line) 1px,transparent 1px) 0 0/100% 50% var(--c-alt);
  border:0;border-bottom:1px solid var(--c-line-2)}
.chart[data-up] .rowc .tr i{width:auto;margin:0 6px}
.chart[data-up] .rowc .lb{width:auto;height:32px;padding-top:2px;text-align:center;white-space:nowrap}
.chart[data-up] .rowc .lb b{display:block;font-weight:400}
.cell-bar{position:relative}
.cell-bar i{position:absolute;left:0;top:50%;transform:translateY(-50%);
  height:12px;background:var(--c-line);z-index:0}
.cell-bar span{position:relative;z-index:1}

/* ---------- FROZEN COLUMN GRID ---------- */
.freeze td:first-child,.freeze th:first-child{position:sticky;left:0;z-index:2;
  background:var(--c-surface);border-right:1px solid var(--c-line-2)}
.freeze th:first-child{background:var(--c-panel);z-index:3}
/* ---------- CAPACITY ---------- */
.cap-table th{text-align:center;font-weight:400}
.cap-table td{text-align:center;height:auto;padding:var(--sp1)}
.cap-table th.off,.cap-table td.off{background:var(--c-alt)}
.cap-table th.today,.cap-table td.today{outline:2px solid var(--c-accent);outline-offset:-2px}
.capbar{height:8px;background:var(--c-alt);border:1px solid var(--c-line)}
.capbar i{display:block;height:100%;background:var(--c-line-2)}
.capnum{font-size:var(--t-meta);color:var(--c-text-2);padding-top:2px}

/* ---------- CALENDAR ---------- */
.cal{display:grid;grid-template-columns:repeat(7,minmax(0,1fr));border-left:1px solid var(--c-line);
  border-top:1px solid var(--c-line);flex:1;min-height:0;overflow:auto;
  grid-template-rows:auto;grid-auto-rows:minmax(96px,1fr)}
.cal .dow{background:var(--c-panel);border-right:1px solid var(--c-line);
  border-bottom:1px solid var(--c-line-2);padding:var(--sp1) var(--sp2);
  font-size:var(--t-meta);text-transform:uppercase;letter-spacing:0.5px;
  color:var(--c-text-2);font-weight:700;position:sticky;top:0;z-index:1}
.cal .day{background:var(--c-surface);border-right:1px solid var(--c-line);
  border-bottom:1px solid var(--c-line);padding:3px;min-height:0;overflow:hidden;
  display:flex;flex-direction:column;gap:2px}
.cal .day.off{background:var(--c-alt)}
.cal .day.out .n{color:var(--c-line-2)}
/* THE DAYS OF THE MONTHS BESIDE IT, FADED, ODDIA-647. Jan, 10 October:
   "the days that doesn't belong to the month ... should be kind of faded
   out or something so we know exactly which is where". */
.cal .day.out{opacity:0.45}
.cal .day.today{outline:2px solid var(--c-accent);outline-offset:-2px}
.cal .n{font-family:var(--f-data);font-size:var(--t-meta);color:var(--c-text-2);
  padding:0 var(--sp1) 2px;display:flex;justify-content:space-between}
.cal .day.today .n{color:var(--c-text);font-weight:700}
.cal .e{display:flex;gap:var(--sp1);align-items:center;padding:0 var(--sp1);
  font-size:var(--t-meta);white-space:nowrap;overflow:hidden;color:var(--c-text);cursor:default}
.cal .e .m{font-family:var(--f-data);color:var(--c-text-2);flex:none;width:86px}
.cal .e .t{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis}
/* WHAT THE DAY IS, ON THE BAR'S RIGHT, ODDIA-688, Jan 11 October: "on
   the right part, there is written what actually is happening. That can
   be a label ... a little bit more saturated, in the color that is used".
   The bar's own colour (`--k`, set per kind below), deeper; as wide as
   its word, cut only where the day is narrow. */
.cal .e .chip{flex:0 1 auto;min-width:0;max-width:55%;margin-left:auto;overflow:hidden;
  text-overflow:ellipsis;padding:0 var(--sp1);border-radius:2px;color:var(--c-text);
  background:color-mix(in srgb,var(--k,var(--c-text-2)) 42%,var(--c-surface))}
.cal .e.is-empty .m{color:var(--c-line-2)}
/* A DAY THE COMPANY CLOSES, hatched over the shading of a day off.
   Plane ODDIA-647. Its kind stands in the header beside the number, on
   one line, in the kind's colour, its name on hover (ODDIA-1055, Jan
   22 October: "we don't need to write it too long"). */
.cal .day[data-shut]{background-image:repeating-linear-gradient(135deg,transparent 0 6px,var(--c-line) 6px 7px)}
.cal .n .chip{font-family:var(--f-ui);font-weight:700;white-space:nowrap;overflow:hidden;
  text-overflow:ellipsis;min-width:0;margin-left:var(--sp1);padding:0 var(--sp1);border-radius:2px;
  color:var(--c-text);text-decoration:none;background-color:var(--c-panel)}
.cal .n .chip[data-k="co"]{background-color:var(--c-caption);color:var(--c-caption-fg)}
.cal .n .chip[data-k="sick"]{background-color:color-mix(in srgb,var(--s-err) 26%,var(--c-surface))}
.cal .n a.chip{cursor:pointer}
.cal a.e{color:var(--c-text);text-decoration:none;cursor:pointer}
/* WHAT A DAY HOLDS IS A BAR, ODDIA-647, Jan 10 October: "the anything
   that will be added to a day should be like a bar", as IMS v4 drew its
   shifts. Its edge says its kind: a shift, an absence, one's own entry,
   another's shared one; the company's closed day is the caption's bar.
   The colour is set as background-color, never the shorthand, so the
   hover's shade above still lays over it (ODDIA-688: "when I hover the
   certain shift, that should also get some effect"). */
.cal .e[data-k]{background-color:var(--c-panel);border-left:3px solid var(--c-line-2);
  border-radius:2px;padding:1px var(--sp1);min-height:var(--ctl-sm)}
/* A PERSON'S DAY IN ITS COLOUR, Plane ODDIA-688, Jan 11 October: "green is
   when it works normally", yellow "if the shift happens but it's
   different", "light blue ... if they have free day", "really visual red
   can be sickness or problem", "different blue for the vacation". Tints of
   the palette's own state colours, so no sixteenth colour. */
.cal .e[data-k="shift"]{--k:var(--s-ok);border-left-color:var(--s-ok);background-color:color-mix(in srgb,var(--s-ok) 16%,var(--c-surface))}
.cal .e[data-k="cut"]{--k:var(--s-warn);border-left-color:var(--s-warn);background-color:color-mix(in srgb,var(--s-warn) 28%,var(--c-surface))}
.cal .e[data-k="free"]{--k:color-mix(in srgb,var(--c-accent) 45%,var(--c-surface));border-left-color:var(--c-chrome-line-2);background-color:color-mix(in srgb,var(--c-accent) 8%,var(--c-surface));color:var(--c-text-2)}
.cal .e[data-k="vac"]{--k:var(--c-accent);border-left-color:var(--c-accent);background-color:color-mix(in srgb,var(--c-accent) 26%,var(--c-surface))}
.cal .e[data-k="sick"]{--k:var(--s-err);border-left-color:var(--s-err);background-color:color-mix(in srgb,var(--s-err) 26%,var(--c-surface));font-weight:700}
/* A shift expected from the usual hours (ISV5-868) has no row yet: the
   shift's edge, dashed, on a fainter green. */
.cal .e[data-k="plan"]{--k:var(--s-ok);border-left:3px dashed var(--s-ok);background-color:color-mix(in srgb,var(--s-ok) 8%,var(--c-surface))}
.cal .e[data-k="away"]{border-left-color:var(--s-off);background-color:var(--c-alt)}
.cal .e[data-k="own"]{border-left-color:var(--s-ok)}
/* A shift request still waiting for its answer (ODDIA-688): the
   absence's edge, dotted, since nothing is decided yet. */
.cal .e[data-k="ask"]{--k:var(--s-warn);border-left:3px dotted var(--s-warn)}
/* What the company must pay on the day (ISV5-875), for finance.read
   only: open with the warning edge, overdue in the error colour, paid
   with the done edge. */
.cal .e[data-k="pay"]{border-left-color:var(--s-warn);font-weight:600}
.cal .e[data-k="late"]{border-left-color:var(--s-err);font-weight:600}
.cal .e[data-k="paid"]{border-left-color:var(--s-ok)}
.cal .e[data-k="co"],.cal a.e[data-k="co"]{background-color:var(--c-caption);color:var(--c-caption-fg);
  border-left-color:var(--c-caption)}
.cal .e[data-k="co"] .m{color:var(--c-caption-fg)}
.cal .e[data-k] .m{width:auto}
/* ANOTHER'S SHIFT, ODDIA-1059: faded, and blocked to the pointer (Jan,
   22 October 2026: "yours needs to be the main"). */
.cal .e[data-lock]{opacity:0.5;cursor:not-allowed}
.cal .e[data-lock]:hover{background-image:none}
/* THE MONTH FILLS THE PAGE, its rows sharing the height ("stretch it a
   little, so the columns are a little bigger"), between the months
   above it and the picked day's bar under it. A day is picked by a press
   (`data-pick`, `shell.js`) and shown by its frame. */
[data-month]{display:flex;flex-direction:column;height:100%;min-height:480px}
/* A person's month of attendance says its totals after the picker and
   wraps where they do not fit (ODDIA-700). */
.bar[data-month-nav]{border:1px solid var(--c-line);border-bottom:0;gap:var(--sp1);flex-wrap:wrap}
.bar[data-month-nav] select{width:auto}
.bar[data-month-nav] > span{margin-right:var(--sp2)}
.bar[data-month-day]{border:1px solid var(--c-line);border-top:0;gap:var(--sp2)}
.cal .day[data-pick]{cursor:pointer;position:relative}
/* THE FRAME GOES WITH THE POINTER, ODDIA-688: "I would like the day to be
   hovered before I click it". Lighter than the picked day's frame. The
   frames are drawn over the day (`::after`), inside its padding, so the
   bars stay inside the frame and never cover it (Jan, 06:58: "the bars
   with the shifts are kinda leaking out into it"). */
.cal .day[data-pick]::after{content:"";position:absolute;inset:0;pointer-events:none}
.cal .day[data-pick]:not([data-picked]):hover::after{box-shadow:inset 0 0 0 2px var(--c-line-2)}
/* ONE OF A FEW AS BUTTONS, `Shell::fieldChoice()`, ODDIA-688: the radio
   stays in the button, unseen, so the button is what is pressed. */
[data-choice]{display:flex;flex-wrap:wrap;gap:var(--sp1)}
[data-choice] input[type="radio"],[data-choice] input[type="checkbox"]{position:absolute;opacity:0;width:1px;height:1px;pointer-events:none}
[data-choice] label.btn{cursor:pointer;position:relative}
/* A CHOICE THAT CANNOT BE PICKED NOW is greyed with its press, ODDIA-60. */
[data-choice] label.btn:has(input:disabled){opacity:.45;cursor:default}
[data-choice] label.btn:focus-within{outline:2px solid var(--c-accent);outline-offset:1px}
.cal .day[data-picked]::after{box-shadow:inset 0 0 0 2px var(--c-text-2)}
.cal .day.today[data-picked]::after{box-shadow:inset 0 0 0 3px var(--c-accent)}

/* ---------- PRODUCTION WEEK ---------- */
.wk{display:flex;flex:1;min-height:0;overflow:auto;border-top:1px solid var(--c-line)}
.wk-gut{width:52px;flex:none;border-right:1px solid var(--c-line);background:var(--c-panel)}
.wk-gut .h{height:60px;border-bottom:1px solid var(--c-line);font-family:var(--f-data);
  font-size:var(--t-meta);color:var(--c-text-2);padding:0 var(--sp1)}
.wk-col{flex:1;min-width:120px;border-right:1px solid var(--c-line);position:relative}
.wk-col.off{background:var(--c-alt)}
.wk-col.today{outline:2px solid var(--c-accent);outline-offset:-2px}
.wk-head{height:var(--ctl);background:var(--c-panel);border-bottom:1px solid var(--c-line-2);
  font-size:var(--t-meta);text-transform:uppercase;letter-spacing:0.5px;color:var(--c-text-2);
  font-weight:700;padding:0 var(--sp2);display:flex;align-items:center;justify-content:space-between;
  position:sticky;top:0;z-index:2}
.wk-head .k{font-family:var(--f-data);font-weight:400}
.wk-body{position:relative}
.wk-body .hr{position:absolute;left:0;right:0;border-top:1px solid var(--c-line)}
.blk{position:absolute;left:2px;right:2px;background:var(--c-surface);
  border:1px solid var(--c-line-2);border-left:3px solid var(--c-caption);
  padding:1px var(--sp1);overflow:hidden;font-size:var(--t-meta);cursor:default}
.blk:hover{background-image:linear-gradient(rgba(0,0,0,0.07),rgba(0,0,0,0.07))}
.blk .nm{font-size:var(--t-base);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.blk .mt{font-family:var(--f-data);color:var(--c-text-2);white-space:nowrap}
.blk.cut{border-left-color:var(--s-warn)}
.blk.pause{background:var(--c-alt);border-left-color:var(--c-line-2);border-style:dashed}

/* ---------- QUEUE ORDER ---------- */
.qpanel{display:none;flex:none;width:320px;border-left:1px solid var(--c-line-2);
  flex-direction:column;min-height:0}
body.qopen .qpanel{display:flex}
body.qopen #calBody{border-right:0}
.qlist{flex:1;overflow-y:auto;background:var(--c-surface)}
.qrow{display:flex;align-items:center;gap:var(--sp1);height:var(--row);
  padding:0 var(--sp2);border-bottom:1px solid var(--c-line);cursor:grab}
.qrow:hover{background-image:linear-gradient(rgba(0,0,0,0.07),rgba(0,0,0,0.07))}
.qrow.drag{opacity:0.4}
.qrow.over{border-top:2px solid var(--c-accent)}
.qrow .gp{font-family:var(--f-data);color:var(--c-line-2);flex:none}
.qrow .ix{font-family:var(--f-data);font-size:var(--t-meta);color:var(--c-text-2);
  width:20px;flex:none;text-align:right}
.qrow .nm{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.qrow .mn{font-family:var(--f-data);font-size:var(--t-meta);color:var(--c-text-2);flex:none}
.qrow .ix.auto{color:var(--c-line-2)}
.qrow.pin .gp{color:var(--c-accent)}
.cal .more{padding:0 var(--sp1);font-family:var(--f-data);font-size:var(--t-meta);
  color:var(--c-text-2)}

/* ---------- TASK BOARD ---------- */
.board{display:flex;gap:var(--sp2);padding:var(--sp2);align-items:stretch;
  overflow-x:auto;min-height:0;flex:1}
.bcol{width:250px;flex:none;background:var(--c-desk);border:1px solid var(--c-line);
  display:flex;flex-direction:column;min-height:0}
.bcol .h{background:var(--c-panel);border-bottom:1px solid var(--c-line-2);
  padding:var(--sp1) var(--sp2);font-size:var(--t-meta);text-transform:uppercase;
  letter-spacing:0.6px;font-weight:700;display:flex;justify-content:space-between}
.bcol .h span{font-weight:400}
.board-host{display:flex;padding:0}
.bcol .cards{padding:var(--sp1);overflow-y:auto;flex:1;min-height:60px}
.card{background:var(--c-surface);border:1px solid var(--c-line);
  padding:var(--sp1) var(--sp2);margin-bottom:var(--sp1);cursor:default}
.card .t{font-size:var(--t-base);margin-bottom:2px}
/* A LINE THAT IS A WARNING. A blocked telephone on an address card reads
   in the same ink as the street unless it says otherwise, and the one
   thing worth knowing about a number before you ring it is that it is
   blocked. */
.card .t.err{color:var(--s-err)}
/* A CARD THAT IS A PICTURE. `C008`, the reference images beside the
   note thread, and the first <img> drawn on any screen here.

   IT DECLARES NO CLASS AND THAT IS THE POINT. `asset_check` counts class
   NAMES against the mockup, so a rule that needs none raises neither
   INVENTED nor UNDECLARED. The same argument `.rec .cols > .box` makes
   for `wide`.

   `max-width:100%` IS THE WHOLE OF THE LAYOUT. A card is already a
   column of `.cards`, which is already half the record, so the picture
   is told what it may not exceed and never what it is. A photograph off
   a phone is 4 000px wide and would otherwise push the thread off the
   screen; a 200px swatch stays 200px rather than being stretched to fill
   a box, because a reference image scaled UP is a reference nobody can
   read.

   `max-height` IS IN THE SAME UNITS THE PANE IS. A portrait photograph
   with only a width has no bound at all - it fits the box and runs three
   screens down - and a gallery whose rows are as tall as the tallest
   picture in it cannot be scanned. 240px is four rows of the `.kv` grid
   beside it.

   NO COLOUR AND NO FONT SIZE, so neither ratchet moves. */
.card img{display:block;max-width:100%;max-height:240px;
  width:auto;height:auto;margin-bottom:var(--sp1)}
/* AND A THUMBNAIL IS A SQUARE. `TASKS.md` J009, of the channel's
   `Nahled fotek a videi`: *"Thumbnails 1:1."*

   IT IS A FLAG AND NOT THE RULE ABOVE, because the rule above is
   right for the OTHER gallery. `C008`'s reference photographs are
   whatever shape somebody photographed and a 200px swatch blown up to
   fill a tile is a reference nobody can read. A shop window is the
   opposite question: the four pictures are the same thing four times
   and the row is read by scanning it, so every tile is the same
   square and the pictures line up down their own edges.

   AN ATTRIBUTE AND NOT A CLASS, the same answer `table[data-sized]`
   and `.win[data-split]` give: `asset_check` compares class NAMES
   against the mockup, so a hook that needs none raises neither
   INVENTED nor UNDECLARED.

   `aspect-ratio` NEEDS THE OTHER THREE OR IT DOES NOTHING. `width`
   has to be a width - `auto` above means the file's own pixels and
   the ratio never binds. `max-height` has to go, or a tile wider than
   240px is capped in one direction only and comes out a rectangle.
   `object-fit:cover` is what makes the rule true of a picture that is
   NOT square: it fills the tile and loses the overhang, rather than
   being squashed. All 717 assets linked to an offer on this bed
   resolve to a square derivative today - measured this round - so it
   crops nothing now; it is what keeps this a grid on the day the DAM
   stores something else.

   NO COLOUR AND NO FONT SIZE, so neither ratchet moves. */
.cards[data-tiles] .card img{width:100%;max-height:none;
  aspect-ratio:1;object-fit:cover}
/* AND A GRAPHIC IS DRAWN WHOLE. Plane ISV5-433: a wide logo cut to the
   square above read `sk`, `ásil` and `i dp` in the carriers' folder.
   `data-fit` is on the picture of an SVG, PDF or AI
   (`MediaThumbService::whole()`): inside the square, on white, nothing
   lost. A photograph keeps its square. A folder's picture is already
   `contain`; a graphic there stands on white rather than the well. */
.cards[data-tiles] .card img[data-fit],
.cards[data-gallery] .card[data-folder] i img[data-fit]{object-fit:contain;
  background:var(--c-surface)}
/* AND A CUT IS DRAWN IN THE SHAPE IT IS. `TASKS.md` M020, the `Výřezy`
   tab: *"the main point was to show the pictures in the aspect ratio they
   actually are in some sensible size."*

   NEITHER OF THE TWO RULES ABOVE IS THAT. `data-tiles` makes every picture
   a square and throws the overhang away, which is the one thing a box of
   cuts must never do - a 21:9 cut and a 9:16 cut came out the same square
   and both were shown with their ends missing. The plain `.card img` rule
   keeps the shape but caps the height at 240px, which is right beside a
   thread of text and leaves a 9:16 cut 135px wide on the tab that exists
   to show it.

   SO THE HEIGHT CAP GOES. `width:auto` and `height:auto` are `.card
   img`'s and are not declared again: the file is 350px on its long side,
   so it draws at 350 and is never stretched past it.

   AND THE BOX ROUND THE PICTURE IS THE PICTURE'S OWN SHAPE, `TASKS.md`
   N010. The operator, looking at the tab M020 had already been called
   done on: *"Orezy is showing the image in the box that does not reflect
   the ratio. I mean the images need to be there in THEIR RATIO. If box is
   needed, it needs to reflect that."*

   WHAT HE WAS LOOKING AT. M020 gave the PICTURE its shape and left the
   CARD a share of the row - `.cols` is a grid of `minmax(0,1fr)` columns,
   so every card was the box divided by however many went across and the
   picture sat in the corner of it. A 350px square in a 500px card is
   150px of surface beside the photograph, and the wider the screen the
   more of it there is: the thing the tab exists to show was drawn inside
   a rectangle that said something else. Two shapes, one of them wrong, is
   worse than the square was.

   SO THE CARD IS AS WIDE AS ITS PICTURE AND NO WIDER. `width:min-content`
   is the picture's own pixels: an `<img>` contributes its intrinsic width
   and the chips and the state line are shorter than any of them, so the
   box comes out 368 wide round a 350px 2:1 cut and 215 round a 197px 9:16
   one - the picture and the card's own padding, measured, nothing else -
   and the border is drawn on the picture's edge in both. `max-width:100%`
   is what happens on a box too narrow for that, which the record pane is
   not today: the smallest it gets is 669px.

   AND `max-width` ON THE PICTURE IS 350px AND NOT `100%`, WHICH IS THE
   ONE LINE HERE THAT IS NOT OBVIOUS. A percentage max-width on a replaced
   element resolves against a containing block that is itself being sized
   from its contents, so the browser takes the picture's contribution as
   ZERO and `min-content` comes out the longest WORD in the state line -
   a 55px card with the photograph squeezed into it, which is what this
   rule drew before the number was written in. 350 is `MediaCutsService::
   THUMB` and `MediaUploadService::PREVIEW`, the long side every picture
   drawn in one of these boxes is made at, so it binds on nothing the
   system produces today and is there for the day a box draws a frame off
   a larger rung.

   AND HOW MANY GO ACROSS IS NOT DECLARED ANY MORE, it is what fits. The
   cards are a wrapping flex row, so a section of 9:16 cuts puts four in
   the width that takes two 2:1 ones and six on a wider screen, and
   `MediaCutsService::perRow()` - which existed only to hand a row's width
   to a shape - is gone. `align-items:flex-start` is what stops a short
   card being stretched to the tallest one beside it, which would put the
   surface back underneath instead of beside.

   NO COLOUR AND NO FONT SIZE, so neither ratchet moves. */
.cards[data-shapes]{display:flex;flex-wrap:wrap;align-items:flex-start;gap:var(--sp2)}
.cards[data-shapes] .card{width:min-content;max-width:100%;margin-bottom:0}
.cards[data-shapes] .card img{max-width:350px;max-height:none}
/* A SHAPE SHOWN SMALL, Plane ISV5-562: a landing block's three display boxes side by side, each its cut 160px tall, so a 16:9 one fits its column. */
.cards[data-shapes][data-small] .card img{height:160px;width:auto;max-width:none;max-height:none}
/* AND A SHAPE SHOWN AS LARGE AS ITS COLUMN, `data-fill`, Plane ODDIA-1591.
   Jan, of the landing's boxes: "small though the record has room: use the
   width, as large as the panel allows". The card is the column's width and
   the picture as wide as the card, its own shape, never taller than 70% of
   the window, so a 9:16 phone cut and its buttons stay on the screen. What
   is drawn is a sized preview, `MediaLinkService::pane()` `large`. */
.cards[data-shapes][data-fill] .card{width:100%}
.cards[data-shapes][data-fill] .card > img, .cards[data-shapes][data-fill] .card > figure > img{
  width:auto;max-width:100%;max-height:70vh}
.cards[data-shapes][data-fill] [data-cutout]{width:min(100%, calc(70vh * var(--ar)))}
/* AND THE PICTURE ON ITS OWN RECORD IS AS LARGE AS THE BOX LETS IT BE.
   `TASKS.md` M007, the editor an upload opens into. The whole frame,
   centred, never taller than 45% of the window, so the flags and the
   models under it are still on the screen. `Shell::cropStill()`.

   NO COLOUR AND NO FONT SIZE, so neither ratchet moves. */
.card[data-still]{margin-bottom:var(--sp2);background:var(--c-desk)}
.card[data-still] img{max-height:45vh;margin:0 auto}
/* THE FOCAL POINT ON IT, `TASKS.md` ZA19. The picture is the control: a
   click puts the point where the subject is, and the dot shows where it
   stands. The script places the dot in pixels over the centred picture, as
   the crop dialog places its box. `.btn` is inline-flex, which beats the
   browser's own `[hidden]`, so the button that takes the point off says so. */
.card[data-focal]{position:relative}
.card[data-focal] img{cursor:crosshair}
.card[data-focal] [data-focal-dot]{position:absolute;width:var(--sp3);height:var(--sp3);
  margin:calc(var(--sp3) / -2) 0 0 calc(var(--sp3) / -2);box-sizing:border-box;
  border:2px solid var(--c-surface);border-radius:50%;background:var(--c-accent);
  pointer-events:none}
.card[data-focal] [data-focal-dot][hidden],
.btn[data-focal-clear][hidden]{display:none}
/* AND A LIST MAY BE DRAWN AS THOSE TILES. `TASKS.md` M006, /media: *"I
   imagined thumbnails that I can select and filter by the top menu"*.
   `Shell::blockTiles()`.

   AS MANY COLUMNS AS FIT, NONE NARROWER THAN 200px. The preview is 350px
   on its long side, so a 200px square is as large as it is useful before
   it blurs, and five fit across a laptop's list window.

   A TICKED TILE IS OUTLINED IN THE ACCENT, the same ink as the selected
   tab, so fifty ticked pictures can be counted by eye before the strip
   is pressed.

   THE FILTERS OVER THEM ARE IN THE PAGE'S ONE BAR, 22 September: *"we
   have main bar and the tabs under, you made double bar, that we never
   ever had."* That bar is outside the scrolling body, so it stays put
   without being pinned. It never wraps - a second line is a second bar -
   so the filter boxes give up width instead, down to 48px, and the
   buttons and the select keep theirs. A box's list does not shrink with
   it: 320px, the width a category path needs to be read.

   AN ATTRIBUTE AND NOT A CLASS, the argument of `data-tiles` above. */
.cards[data-gallery]{display:grid;grid-template-columns:repeat(auto-fill,minmax(200px,1fr));
  gap:var(--sp2)}
.cards[data-gallery] .card{margin-bottom:0;padding:var(--sp1)}
.cards[data-gallery] .card:has(input:checked){border-color:var(--c-accent);
  box-shadow:0 0 0 1px var(--c-accent)}
.cards[data-gallery] .card img{margin:var(--sp1) 0 0}
/* A picker's search tile names its folder under the picture, Plane ISV5-802. */
.cards[data-gallery] .card > label + .m{margin-top:var(--sp1)}
/* AND A FOLDER LOOKS LIKE A FOLDER. `TASKS.md` N007, the operator of
   `/media`: *"We need some icon of a folder, for the folder in the media
   gallery, or we need to separate folders from images by a separator, so
   its obvious. Not ist touching."*

   WHAT HE WAS LOOKING AT. A folder tile was the word `Složka` in meta
   grey and its name as a link - two short lines, no picture - standing in
   a row of 200px photographs and touching them on both sides. Nothing on
   it said *place* rather than *picture*, and because it was half their
   height the grid read as a row of pictures with a gap in it.

   THE ICON IS A BAND, NOT A SQUARE. `<i>` is the well the folder is
   drawn in - `::before` the tab, `::after` the body, the way
   `.nav-body a::before` draws the tree lines and `.sw.f i::after` draws
   the knob. It is 96px tall and the folder on it is a fixed 88 by 52
   centred, so it is the same folder in a 200px column on `/media`, in a
   160px one and in whatever width the picker's window gives it. **A
   folder does NOT get a photograph's 200px square**: the separator below
   already puts folders on their own rows, so nothing has to line up with
   a picture, and twenty folders in a square each would be four screens of
   grey before the first photograph.

   IT PAINTS WITH TOKENS. `--c-alt` is the well the shape sits in, the
   same ink the striped row uses; `--c-caption` is the shape, the darkest
   ink on white in the palette, because the one thing he asked for is that
   it be OBVIOUS. No literal colour, no font size, so neither ratchet
   moves. The name keeps the link colour - it IS a link, and section 0's
   only link rule is not restated for one tile.

   AND THE SEPARATOR IS THE OTHER HALF OF HIS SENTENCE. `.div` is the
   divider every bar already draws; here it spans the grid, which in CSS
   grid means it always starts a new row, so the pictures begin on a clean
   line under the folders whether there are two folders or eleven. Both
   halves and not one: he asked for either, and the line alone would still
   have left a folder looking like a picture that failed to load.

   NO CLASS IS INVENTED. `cards`, `card`, `div` and `t` are all declared
   already; `data-folder` is an attribute, the argument `data-tiles` above
   makes. */
.cards[data-gallery] .card[data-folder] > a{display:block;margin-top:auto}
.cards[data-gallery] .card[data-folder] i{display:block;position:relative;
  height:96px;margin:var(--sp1) 0;background:var(--c-alt)}
.cards[data-gallery] .card[data-folder] i::before{content:"";position:absolute;
  left:50%;top:22px;margin-left:-44px;width:32px;height:6px;background:var(--c-caption)}
.cards[data-gallery] .card[data-folder] i::after{content:"";position:absolute;
  left:50%;top:28px;margin-left:-44px;width:88px;height:46px;background:var(--c-caption)}
.cards[data-gallery] .card[data-folder] .t{font-weight:700;margin-bottom:0;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
/* AND EVERY FOLDER IN A ROW STANDS ON ONE LINE. Plane ISV5-410, his words:
   *"the Hlavní úroveň folder that leads back is differently aligned than
   title on folder i make"*. The tile out of a folder has no tick, so its
   corner line was 15px where a folder's, with its box, was 21, and its
   folder and name sat 6px higher. The anchor now stands at the foot of
   the tile (`margin-top:auto`, on the anchor's rule above), and a grid row gives its tiles one height, so the wells and
   the names line up whatever the corner holds. No number is written.

   AND A FOLDER MAY SHOW A PICTURE, `cover`, in the well where the drawn
   folder stood: the whole picture, not cut, on the well's own ground. */
.cards[data-gallery] .card[data-folder]{display:flex;flex-direction:column}
.cards[data-gallery] .card[data-folder] i img{display:block;width:100%;height:100%;
  margin:0;object-fit:contain}
.cards[data-gallery] .card[data-folder] i:has(img)::before,
.cards[data-gallery] .card[data-folder] i:has(img)::after{content:none}
/* AND ITS PICTURE IS SET AND TAKEN OFF AT THE FAR END OF ITS CORNER,
   Plane ISV5-410: `Obrázek složky`, or the square `×` once it has one. */
.cards[data-gallery] .card[data-folder] .m [data-cover]{margin-left:auto}
.cards[data-gallery] > .div{grid-column:1/-1;height:1px;background:var(--c-line-2);
  margin:var(--sp2) 0}
/* AND ONE FOLDER GOES THE OTHER WAY. `TASKS.md` Q004, the operator of
   `/media`: *"We need to add that one folder icon to left, that will take
   you one level up back where you were, if you in the depth tho."*

   IT IS THE SAME FOLDER WITH AN ARROW CUT OUT OF IT. Every rule above
   matches on `[data-folder]` being PRESENT, so the tile picks up the
   well, the tab, the body and the name unchanged, and the only thing
   this adds is the arrow - drawn in `--c-alt`, the colour of the well
   behind the folder, so it reads as a hole in the shape rather than a
   second object sitting on it.

   THE ARROW IS ON THE ANCHOR because the well's two pseudo-elements are
   the folder itself. `<a>` has its own two and neither is used: `::after`
   is the head, `::before` the shaft. Both are measured from the anchor,
   which the `<i>` starts `--sp1` down from, so the body's 28..74 inside
   the well is 32..78 here and the arrow's 42..70 sits inside it.

   NO CLASS IS INVENTED AND NO LITERAL COLOUR IS WRITTEN. `data-folder`
   is the attribute N007 added, this reads one value of it, and the ink is
   the same token the well already paints with - neither ratchet moves. */
.cards[data-gallery] .card[data-folder="up"] > a{position:relative}
.cards[data-gallery] .card[data-folder="up"] > a::after{content:"";position:absolute;
  left:50%;top:calc(var(--sp1) + 38px);margin-left:-12px;width:0;height:0;
  border-left:12px solid transparent;border-right:12px solid transparent;
  border-bottom:14px solid var(--c-alt)}
.cards[data-gallery] .card[data-folder="up"] > a::before{content:"";position:absolute;
  left:50%;top:calc(var(--sp1) + 52px);margin-left:-4px;width:8px;height:14px;
  background:var(--c-alt)}
/* AND A FOLDER SAYS SO WHILE A SELECTION IS HELD OVER IT. `TASKS.md`
   N008, the operator of `/media`: *"I seriously need drag and drop here
   ... so i can select and move, similarily to google drive behaviour."*

   A DROP TARGET HAS TO ANSWER BEFORE THE FINGER COMES OFF THE BUTTON.
   Without it the only feedback is the pictures disappearing afterwards,
   which is a move you find out about rather than one you made - and with
   twenty folders on a row, the one under the cursor is a guess.

   THE SAME INK AS A SELECTED TILE AND TWICE THE RING, so the two states
   are read as one family and still told apart at a glance: a chosen
   picture is `0 0 0 1px --c-accent` four rules up, and this is 2px. The
   folder is not selectable itself - it has no tick - so nothing can
   carry both.

   `data-over` IS WRITTEN BY `shell.js` AND EXISTS ONLY WHILE A DRAG IS
   IN THE AIR; `data-into` is the renderer's and says the tile is a place
   a selection can go at all. No class is invented and no literal colour
   is written, the argument `data-folder` above makes. */
.cards[data-gallery] .card[data-into][data-over]{border-color:var(--c-accent);
  box-shadow:0 0 0 2px var(--c-accent)}
/* AND A STEP OF THE PATH HELD OVER LIGHTS THE SAME WAY, Plane ISV5-573. */
a[data-into][data-over]{box-shadow:0 0 0 2px var(--c-accent);border-radius:2px}
/* AND A PRODUCT'S OWN SHOP WINDOW IS SMALLER THAN THE LIBRARY'S.
   `TASKS.md` M021: *"Thumbnails here about 150 x 150."*

   THE LIBRARY IS A SCREEN AND THIS IS A BOX ON A RECORD. 200px is right
   for a wall of three thousand pictures you are searching; a product has
   six to eight and they sit above the price, the categories and the texts,
   so the box has to be read in one glance rather than scrolled past.

   AN ATTRIBUTE AND NOT A CLASS, the argument `data-tiles` above makes:
   `asset_check` compares class NAMES against the mockup, so a hook that
   needs none raises neither INVENTED nor UNDECLARED. And it is one
   instruction about the same grid, not a second kind of grid.

   THE COLUMN IS 160px AND THE PICTURE IN IT IS 150. A tile is its border
   and its padding either side - 1px and `--sp1` - so a column that flexed
   to fill the row would draw a thumbnail of whatever was left over: 140px
   in a box of six across, 176px in a box of five. A fixed column is the
   only one that keeps his number, and what it costs is a little slack at
   the right-hand edge of the box.

   NO COLOUR AND NO FONT SIZE, so neither ratchet moves. */
.cards[data-gallery][data-small]{grid-template-columns:repeat(auto-fill,160px)}
/* AND A PICKER OPEN IN A SHAPE DRAWS EVERY PICTURE IN IT. Plane ISV5-563,
   Jan: *"draw each thumbnail in that aspect ratio, so he sees the picture
   as it will appear and not a cropped square."* `data-shape` is on the
   grid of a picker opened for a place drawn in one shape, and `--shape`
   beside it is that shape's ratio (`Shell::blockTiles()`), so the square
   of `data-tiles` becomes the shape and `cover` loses nothing: the
   picture is that cut. A wide shape (16:9 and wider) gets wider columns,
   or a 64:27 cut is a strip 84px high. A folder keeps its square.

   NO COLOUR AND NO FONT SIZE, so neither ratchet moves. */
.cards[data-tiles][data-shape] .card:not([data-folder]) img{aspect-ratio:var(--shape)}
.cards[data-gallery][data-shape="wide"]{grid-template-columns:repeat(auto-fill,minmax(320px,1fr))}
/* AND A PICTURE THAT IS STILL ON ITS WAY IS A TILE TOO. `TASKS.md` Q002,
   the operator of `Nahrát obrázky` on a product: *"nahrat obrazky at
   product works, but its slow. When you select, them and confirm, there
   is no upload nothing. There should be some info, like empty picture
   with animated strip that this is being uploaded or something?"*

   WHAT HE WAS LOOKING AT. The dialog closes, the request goes, and the
   box does not change until the answer comes back - five to fifteen
   seconds with a 7 MB photograph on a slow line. The strip is exactly
   what it was before the press, which is indistinguishable from a button
   that did nothing.

   SO THE TILE IS DRAWN AT ONCE, ONE PER FILE, AND IT IS A REAL TILE. It
   stands in the same grid, in the same column, in the order the files
   were chosen, so the box grows by what he handed it the moment he hands
   it over and nothing under it jumps when the real thumbnails arrive.
   `shell.js` section 2c writes it and takes it away again; the server
   never draws one, because at the moment the server answers there is a
   picture and this is the absence of one.

   THE WELL IS THE FOLDER'S WELL AND THE STRIPE IS THE JOB CONSOLE'S,
   which is the whole of the design. `--c-alt` is the ink an empty
   surface is painted in here - `.card[data-folder] i` above - and
   `@keyframes jobrun` at the top bar's own 32px pitch is what this
   system already means by *something is running*. One animation, not
   two, and a person who has seen the console knows this stripe.

   `width:100%` WITH A FLOOR, so one rule serves all three galleries: a
   200px column, `data-small`'s 160px column, and a `shapes` row whose
   card is `min-content` wide and would otherwise collapse to nothing.
   `aspect-ratio:1` is `data-tiles`' own rule said about a tile with no
   picture in it, so the square the photograph will occupy is already
   the square being held.

   NO CLASS IS INVENTED AND NO COLOUR IS WRITTEN. `card` and `t` are
   declared; `data-waiting` is an attribute, the argument `data-folder`
   and `data-tiles` above both make, and every value here is a token.

   AND THE REDUCED-MOTION RULE IS A DIFFERENT SELECTOR ON PURPOSE - the
   same care `.jobs .jobs-p` takes four hundred lines down. The same
   selector written twice, even inside a media query, is a duplicate the
   asset ratchet counts. */
.card[data-waiting] i{display:block;position:relative;width:100%;min-width:150px;
  aspect-ratio:1;background:var(--c-alt);margin:var(--sp1) 0 0;overflow:hidden}
.card[data-waiting] i::after{content:"";position:absolute;left:0;right:0;bottom:0;height:6px;
  background-image:repeating-linear-gradient(135deg,var(--c-accent) 0,var(--c-accent) 8px,var(--c-chrome-line) 8px,var(--c-chrome-line) 16px);
  background-size:32px 6px;animation:jobrun .5s linear infinite}
.card[data-waiting] .t{color:var(--c-text-2);overflow:hidden;text-overflow:ellipsis;
  white-space:nowrap;margin-bottom:0}
@media (prefers-reduced-motion:reduce){ .cards .card[data-waiting] i::after{animation:none} }
/* AND THE GALLERY'S BAR CARRIES MORE THAN ANY OTHER, SO ITS CONTROLS SIT
   CLOSER TOGETHER. `TASKS.md` M018, answering M017's open finding: with
   `Razeni` added the strip needed 1 404px of a 1 302px bar at 1 500px
   wide, and `Oznacit vse` stood off the right edge where nobody could
   press it. Twenty-five gaps at 8px are 200px of the overflow; at 4px
   they are 100. Nothing is removed and no class is invented - only this
   one bar is tighter, and every other bar in the system is untouched. */
.bar[data-gallery]{white-space:nowrap;gap:var(--sp1)}
.bar[data-gallery] .fld,.bar[data-gallery] > input{flex:0 1 140px;min-width:48px;margin:0}
.bar[data-gallery] > select{flex:none}
.bar[data-gallery] .fld > input{width:100%;display:block}
.bar[data-gallery] .fld .umenu{white-space:normal;min-width:320px}
[data-gallery] + a[data-more]{margin-top:var(--sp3)}
/* AND A PICTURE IN A TABLE ROW IS A 48px SQUARE. `TASKS.md` N009, the
   library at /media: *"Every uploaded picture with a thumbnail."*

   `contain` AND NOT `cover`, the opposite of the tiles above and for the
   opposite reason. A tile is a shop window of one shape; a row in the
   library is one picture among six shapes, and a 21:9 banner cropped to
   a square is a picture of its middle. The whole frame, smaller, says
   which picture it is and what shape it is at the same glance.

   THE CELL IS NOT SIZED HERE. Height and padding are `table th, table
   td`'s, once; this sizes the picture inside it and the row grows to
   hold it. No class, the same argument as `.card img`. */
td > img, td > a > img{display:block;width:48px;height:48px;object-fit:contain}
/* AND A BIG ONE FILLS ITS CELL, WHOLE. Plane ISV5-788, `/alt-texts`: the
   column is a quarter of the row and the picture is described from it, so
   it is as wide as the cell, never cropped, and at most 480px tall so a
   tall photograph does not take the whole screen. */
td img[data-big]{width:100%;height:auto;max-height:480px}
/* A RECTANGLE OF A FRAME, CUT BY THE BROWSER, `data-cutout`. Plane
   ODDIA-1555: the picture record's cuts are drawn out of the frame already
   in the cache rather than out of a file made for each, `Shell::cutOut()`.
   The box is the rectangle's shape and its width comes with it; the frame
   inside is placed by the three numbers it carries. Never wider than its
   cell or card. */
[data-cutout]{display:block;position:relative;overflow:hidden;aspect-ratio:var(--ar);max-width:100%}
.card [data-cutout]{margin-bottom:var(--sp1)}
[data-cutout] > img, .cards[data-shapes] .card [data-cutout] > img{position:absolute;left:var(--il);top:var(--it);
  width:var(--iw);height:auto;max-width:none;max-height:none;margin:0}
.card .m{display:flex;gap:var(--sp1);align-items:center;flex-wrap:wrap}
/* AND A PICTURE TILE WEARS ITS NAME ON THE PICTURE. `TASKS.md` PIC01, his
   design of a product's picture on a channel: *"ID -> white, floating on the
   image, bottom-left corner. OUT of the card header."* and *"Odebrat -> a
   square with a centred X. No word."*

   ELEMENTS AND AN ATTRIBUTE, NO CLASS, the argument `.card img` makes above.
   `<figure>` is the picture and `<figcaption>` the name on it; the ink is
   the caption's own white and the edge under it the text colour, so it
   reads on a pale photograph and on a dark one. `data-mark` is a button
   that is a glyph: as wide as it is tall.

   NO NEW COLOUR AND NO NEW SIZE, so neither ratchet moves. */
.card figure{position:relative;margin:0}
.card figcaption{position:absolute;left:var(--sp1);bottom:var(--sp1);pointer-events:none;
  color:var(--c-caption-fg);font-family:var(--f-data);font-size:var(--t-meta);font-weight:700;
  line-height:1;text-shadow:0 0 2px var(--c-text),0 0 2px var(--c-text),0 0 3px var(--c-text)}
.card figure + .m{margin-top:var(--sp1)}
.btn[data-mark]{width:var(--ctl-sm);padding:0}
.card .m .sp{flex:1}
.card .m .due.late{color:var(--s-err);font-weight:700}
.bcol .add{border-top:1px solid var(--c-line);padding:var(--sp1);
  font-size:var(--t-meta);color:var(--c-text-2);text-align:center}

/* ---------- PLACE TREE ---------- */
.split{display:flex;flex:1;min-height:0}
/* THE PANEL SITS ON `--c-panel`, the bar's token, and carries the
   navigation's text colour, row height, selected style and hover. The
   main navigation's own rule is not touched by any of this. Both are the
   stylesheet's own tokens; nothing here is a colour chosen for this
   panel, so nothing here can drift on its own. Ruled by the operator,
   27 August.
   NO TOP PADDING, so its rows land on the same 24px grid as the shell's
   navigation. That nav starts 40px down; the panel starts under the
   caption and the bar, and four pixels of padding on top of that put
   every row five pixels below the one beside it. */
/* THE PANEL IS THE NAVIGATION'S WIDTH, `--nav`, not a number of its
   own. Two panels side by side at different widths read as two
   different kinds of thing; they are the same kind of thing one level
   in. Long names ellipsis until they are shortened. Operator, 30 August. */
.tree{width:var(--nav);flex:none;border-right:1px solid var(--c-line);
  overflow-y:auto;background:var(--c-panel);padding:0}
.tree a{display:flex;align-items:center;justify-content:space-between;gap:6px;
  height:var(--ctl);padding:0 var(--sp2);text-decoration:none;
  color:var(--c-text);font-size:var(--t-base)}
.tree a .lb{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
/* THE NAMES ARE IN A SPAN, AND `.tree a span` IS CAUGHT BY THE SHARED
   META RULE that sets --c-text-2. So every name in this panel rendered
   grey however `.tree a` was written, and changing the panel's colour
   could never fix it. Text is --c-text, the navigation's own, and the
   count is the size of the row it sits on. Operator, 27 August. */
.tree a span{font-size:var(--t-base);color:var(--c-text)}
.tree a.on{background:var(--c-caption);color:var(--c-caption-fg)}
.tree a.on span{color:#c3c8ce}
/* A ROW IN TROUBLE CARRIES ITS CHIP, Plane ISV5-309: a mailbox that cannot
   send or receive. `.tree a span` above sizes and colours every span in a
   row, so the chip takes back its own size and its own ink, and it keeps
   them on the selected row. */
.tree a span.chip{font-size:var(--t-meta);color:var(--c-caption-fg)}
/* AND IT CARRIES IT UNDER THE NAME, Plane ISV5-315: beside it a mailbox
   address kept ten letters. Two lines, the name the whole width. */
.tree a[data-chip]{flex-wrap:wrap;justify-content:flex-start;height:auto;
  min-height:var(--ctl);padding-top:4px;padding-bottom:4px;row-gap:2px}
.tree a[data-chip] .lb{flex:1 0 100%}
.tree .tw[data-folds]{cursor:pointer}
/* A NODE AND ITS ENTRIES ARE THE MAIN MENU'S, Plane ISV5-638. Jan, of the
   landings under `Landing`: "not visibly its children. Use the same indent
   and the same look as the main menu." The node is bold as `.grp` is, and
   each entry hangs off it by `.nav-body a`'s two lines at its 27px; the
   last one's line stops at it, as `a.last` does there. */
.tree a:has(> .tw[data-folds]) .lb{font-weight:700}
.tree [data-entries] a{position:relative;padding-left:27px}
.tree [data-entries] a::before{content:"";position:absolute;left:13px;top:0;bottom:0;
  border-left:1px solid var(--c-line-2)}
.tree [data-entries] a:last-child::before{bottom:12px}
.tree [data-entries] a::after{content:"";position:absolute;left:13px;top:12px;width:8px;
  border-top:1px solid var(--c-line-2)}
.tree [data-entries] a.on::before,.tree [data-entries] a.on::after{border-color:var(--c-chrome-line-2)}
.tree .l1{padding-left:var(--sp3)}
.tree .l2{padding-left:var(--sp5)}
.tree .l3{padding-left:var(--sp6)}
/* THE GROUP HEADING IS THE NAVIGATION'S GROUP HEADING. Same weight, same
   colour, same height as `.nav-body .grp` - it is the same thing one
   level in, and it was drawn small, grey and uppercase, which read as a
   caption rather than as a heading. Operator, 27 August. */
.tree .cap2{display:flex;align-items:center;min-height:var(--ctl);
  padding:0 var(--sp2);color:var(--c-text);font-weight:700;overflow-wrap:anywhere}
/* A GROUP THAT FOLDS, ODDIA-1194 (`/equipment`'s kinds, `folds` on the
   panel): the heading is a `summary`, a triangle before it says open or
   shut, and its rows stand in under it. */
.tree details > summary.cap2{cursor:pointer;list-style:none}
.tree details > summary.cap2::-webkit-details-marker{display:none}
.tree details > summary.cap2::before{content:"\25B8";display:inline-block;width:14px;flex:none}
.tree details[open] > summary.cap2::before{content:"\25BE"}
.tree details > a{padding-left:calc(var(--sp2) + 14px)}
/* A HEADING LONGER THAN THE PANEL WRAPS, Plane ISV5-919: a mailbox's address
   is its heading on `/mail`, and `no-reply@weareoddia.com` ran under the row
   below it at a fixed height. */

/* ---------- WHISPERER ---------- */
.wh{position:relative;display:inline-block}
.wh .list{position:absolute;left:0;top:24px;width:100%;z-index:4;
  background:var(--c-surface);border:1px solid var(--c-line-2);border-top:0}
.wh .list a{display:flex;gap:var(--sp2);align-items:baseline;text-decoration:none;
  padding:var(--sp1) var(--sp2);color:var(--c-text);border-bottom:1px solid var(--c-line);
  font-size:var(--t-base)}
.wh .list a.on{background:var(--c-alt)}
.wh .list a .nm{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.wh .list a .ex{white-space:nowrap}
.wh .list .more{padding:var(--sp1) var(--sp2);font-size:var(--t-meta);color:var(--c-text-2);
  font-family:var(--f-data);background:var(--c-panel);display:flex;justify-content:space-between;gap:var(--sp2)}

/* ---------- LIST WINDOW (escape hatch) ---------- */
.pick{position:fixed;inset:0;background:rgba(0,0,0,0.40);display:none;z-index:22;
  align-items:center;justify-content:center}
.pick.open{display:flex}
.pick .win{width:860px;height:540px}
/* A LARGE PICKER, `data-large`. `TASKS.md` N011: `Přiřadit fotky` is "a
   LARGE picker over the library", and a picture is chosen by eye - so the
   window takes the screen less a margin, and its thumbnail is twice the
   list's 48px. Same window, same table; one attribute. */
.pick .win[data-large]{width:calc(100vw - 96px);height:calc(100vh - 96px)}
.win[data-large] td > img{width:96px;height:96px}

/* ---------- CROP DIALOG ----------
   `TASKS.md` N013, M001 3.1 to 3.3. The four names were agreed on their own
   in N003. `.crop` is the layer, `.pick`'s shape, holding the ordinary
   `.win` at the screen less a margin: the floor of 3.3 is held in pixels he
   can see. `.crop-s` is the stage - the picture scaled to fit, centred, and
   the frame the box is measured against. `.crop-b` is the box; the rest of
   the picture is shaded by its own shadow, clipped by the stage, not by four
   mask elements. `.crop-h` is a corner, which one is `data-h`. Corners only,
   because the ratio is locked and an edge would have to move its neighbour. */
.crop{position:fixed;inset:0;background:rgba(0,0,0,0.40);display:none;z-index:22;
  align-items:center;justify-content:center}
.crop.open{display:flex}
.crop .win{width:calc(100vw - 96px);height:calc(100vh - 96px)}
.crop .win > .body{flex:1;min-height:0;display:flex;flex-direction:column;gap:var(--sp2)}
.crop-s{position:relative;flex:1;min-height:0;overflow:hidden;background:var(--c-desk);
  touch-action:none;user-select:none}
.crop-s > img{position:absolute;inset:0;margin:auto;max-width:100%;max-height:100%;display:block}
.crop-b{position:absolute;outline:1px solid var(--c-surface);cursor:move;
  box-shadow:0 0 0 9999px rgba(0,0,0,0.55)}
.crop-b[hidden]{display:none}
.crop-h{position:absolute;width:12px;height:12px;background:var(--c-surface);
  border:1px solid var(--c-caption)}
/* INSIDE THE CORNER, NOT ASTRIDE IT: a box as tall as the picture reaches
   the stage's edge, and the stage clips, so a handle half outside is half
   gone - measured on a 16:9 upload in the 1:1 step. */
.crop-h[data-h="nw"]{left:0;top:0;cursor:nwse-resize}
.crop-h[data-h="ne"]{right:0;top:0;cursor:nesw-resize}
.crop-h[data-h="sw"]{left:0;bottom:0;cursor:nesw-resize}
.crop-h[data-h="se"]{right:0;bottom:0;cursor:nwse-resize}

/* login page: shown after logout and after locking. Same page, one element. */
.gatewrap{position:fixed;inset:0;background:var(--c-desk);display:none;z-index:45;
  padding:var(--sp5)}
.gatewrap.is-open{display:block}
.gatebox{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%)}
.gate-main{width:380px}
.whobox{position:absolute;top:var(--sp5);right:var(--sp5);width:230px}
.chipwait{text-align:center;padding:var(--sp4) 0 var(--sp3)}
.chipbox{display:inline-flex;align-items:center;justify-content:center;
  min-height:calc(var(--ctl) * 2);padding:var(--sp3) var(--sp5);
  border:1px solid var(--c-line-2);background:var(--c-alt);
  font-size:var(--t-sec);font-weight:700;line-height:1;
  animation:chipwait 1.4s steps(1) infinite}
@keyframes chipwait{ 0%,50%{background:var(--c-alt)} 51%,100%{background:var(--c-surface)} }
@media (prefers-reduced-motion:reduce){ .chipbox{animation:none} }
/* The chip box: focused for the reader, never seen (Jan, 22 Oct 2026). */
.pin-hidden{position:absolute;width:1px;height:1px;padding:0;border:0;opacity:0;
  overflow:hidden;clip:rect(0 0 0 0);caret-color:transparent}

/* THE SAME WAIT, ON A BUTTON THAT IS CALLING A REGISTER.

   The operator, 8 September, on the ten seconds VIES takes: "there is no
   animation to be displayed while this is happening. Animation minimal,
   systemic, profi, technical. Nothing fancy."

   `.chipbox` is the mockup's own waiting state and it is already exactly
   that - a one-step background flip rather than a spinner, and it stops
   dead for anyone who has asked for reduced motion. This only takes its
   panel metrics off so it fits a control: the animation is inherited and
   nothing about it is redrawn here. Both class names are the mockup's. */
.btn.chipbox{min-height:0;height:var(--ctl);padding:0 var(--sp2);
  font-size:var(--t-base);font-weight:400;cursor:progress}
.whobox tbody td{border-bottom:0;height:var(--ctl)}
.orline{position:relative;text-align:center;color:var(--c-text-2);font-size:var(--t-meta);
  border-bottom:1px solid var(--c-line);line-height:0.1em;margin:var(--sp4) 0}
.fld--tight{margin-bottom:var(--sp2)}
.forgot{margin:2px 0 0;font-size:var(--t-meta);text-align:right}
.gate-act{display:flex;align-items:center;gap:var(--sp2);margin-top:var(--sp3)}
.gate-act .sp{flex:1}
.gate-act .btn{margin-left:auto}
.gate-reset,.gate-sent,.cap-reset{display:none}
.gate-main.is-reset .gate-login,
.gate-main.is-reset .gate-chip,
.gate-main.is-reset .cap-login{display:none}
.gate-main.is-reset .gate-reset{display:block}
.gate-main.is-reset .cap-reset{display:inline}
.gate-main.is-sent .gate-sent{display:flex;margin-top:var(--sp3)}


/* THE SWITCH IN A RECORD. `.sw` is the mockup's own control; these three
   rules give it its two states so a yes/no field reads as a switch
   rather than as a tick box. The input inside is still a checkbox, so
   with no script and no styles it is exactly what it always was.
   Operator's ruling, 27 August. */
.sw input{position:absolute;opacity:0;width:0;height:0}
.sw.f{width:auto;margin:0}
/* A SWITCH IN A COLUMN THAT DECLARES THE RIGHT EDGE STANDS THERE. `.sw` is
   a flex box as wide as its cell, so `td.r`'s text-align never moved it.
   Plane ODDIA-348, the carriers' `Ověření věku` and `Dobírka` on Doprava. */
td.r > .sw.f{justify-content:flex-end}
/* ONE SHAPE, TWO PLACES, AND THE ARITHMETIC WRITTEN DOWN.

     box            44 x 20, border-box
     border          1 all round      ->  inner 42 x 18
     padding         2 all round      ->  track 38 x 14
     knob           14 x 14           ->  2px of border all four sides
     travel     38 - 14 = 24          ->  margin-left 24 at the far end

   The travel was 22 while the track was 38 and the knob 14, so the knob
   stopped 2px short of the right and the border looked even on the left
   and wrong on the right. Every number here follows from the two above
   it; changing the height means redoing the line below it.
   In pixels rather than tokens because an even border on four sides is
   arithmetic between height, border and knob, which no token expresses. */
.sw.f i{display:inline-flex;align-items:center;box-sizing:border-box;
  width:44px;height:20px;border:1px solid var(--c-line);
  background:var(--s-err);cursor:pointer;padding:2px;
  transition:background 0.1s;vertical-align:middle}
.sw.f i::after{content:"";width:14px;height:14px;background:var(--c-surface);
  transition:margin-left 0.1s}
/* TWO KINDS OF SWITCH, AND THEY LOOK DIFFERENT ON PURPOSE.

   ON/OFF says a thing is or is not - active, published, enabled. One of
   those states is good and the other is not, so it is green and red, and
   the colour is the message.

   SWITCH chooses between two equal answers - the symbol before or after
   the amount. Neither is good, so colour would be a lie; the knob's
   position is the whole message and the track stays quiet.

   A SETTING DECLARES ITSELF by naming what each side means, which is what
   `data-on` carries. So the stylesheet asks the markup rather than being
   told twice. Operator, 28 August. */
/* The red track and the white knob are set once, in the shape rule
   above. They were restated here with identical values, which is how a
   switch ends up with two rules that must be kept in step by hand. Only
   what CHANGES a state is declared from here down. */
.sw.f input:checked + i{background:var(--s-ok);border-color:var(--s-ok)}

.sw.f:has(span.lb[data-on]) i{background:var(--c-panel);border-color:var(--c-line-2)}
.sw.f:has(span.lb[data-on]) i::after{background:var(--c-caption)}
.sw.f:has(span.lb[data-on]) input:checked + i{background:var(--c-panel);border-color:var(--c-line-2)}
/* THE SWITCH IN A LIST is a button rather than a checkbox, so it carries
   its state as a class. Green for on, red for off - the two states of a
   row, readable down a column without reading a word. Slightly shorter
   than the row, so it sits in it rather than filling it. */
/* REORDER MODE MUST NOT CHANGE THE ROW HEIGHT. The handle and the number
   sit on one line at the row's own height, and the number box is the
   height of every other control - so turning the mode on rearranges
   nothing but what is in the first column. Operator, 27 August. */
td.act .tb{cursor:grab;padding:0 var(--sp1);user-select:none}
/* A PLACE BOX WITH ITS HANDLE IN A PLAIN CELL, ISV5-66 (Shell::fieldCell(), a
   `field` column that declares `drag`): `td > .f` fills the cell, so beside
   the glyph the box was pushed out of it. The box takes what the glyph leaves. */
td > .tb[data-drag] + .f{width:calc(100% - 2.5em)}
/* A BOX WITH ITS MARK BESIDE IT, Plane ODDIA-348: a price wide, whatever
   the cell, so a box spanning three columns is not three columns of box,
   and a price column (`align` `r`) puts box and mark at its right. */
td > .f:has(+ .hint[data-mark]){width:4.8em}
td:has(> .hint[data-mark]){white-space:nowrap}
/* AND IN A RIGHT-ALIGNED COLUMN THE MARK STANDS AT THE LEFT EDGE AND THE BOX
   FILLS THE REST, Plane ODDIA-348. Jan, 8 October, on the delivery prices:
   "this is gord of cells, like excel here ... The question mark can stay,
   should be the oposite of the alingment in each of the field." */
/* AND HALF THE PADDING, cell and box, Plane ODDIA-348, operator 9 October:
   a zone of nine bands ran 45px past `Metody v zemi`'s box at 1920. 8px a
   column off the frame and none off the price: a 77px column holds a bold
   `457.44` beside its mark, as 88 did. */
td.r:has(> .hint[data-mark]){padding-left:calc(var(--sp1) / 2);padding-right:calc(var(--sp1) / 2)}
td.r > .f:has(+ .hint[data-mark]){width:calc(100% - 16px);padding:0 calc(var(--sp1) / 2)}
td.r > .hint[data-mark]{float:left;margin-top:5px}
td.r > input.f{text-align:right}
/* A ROW SWITCHED OFF IN ITS FIRST CELL IS GREYED BEHIND IT, and a cell with
   nothing to hold in its row is grey ground (`mark` `freeze`), Plane
   ODDIA-348: a delivery method not active in a country, a weight band it
   does not price. A first cell with no switch in it is greyed too: a zone's
   prices, whose rows are switched per country under it (ISV5-688). */
tbody tr[data-dim] > td:not(:first-child),
tbody tr[data-dim] > td:first-child:not(:has(input)){opacity:0.45}
/* AND A RULE OVER A ROW THAT BEGINS A SECOND PART OF THE LIST, Plane
   ODDIA-700: on Lidé, Pracovníci, who worked here under who works here now.
   `_data` `rule` on the row. */
tbody tr[data-rule] > td{border-top:2px solid var(--c-text-2)}
/* A DAY OF A PERSON'S DOCHÁZKA IN THE COLOUR /shifts GIVES IT, Plane
   ODDIA-700, Jan 11 October: "the coloring that we are using on the shifts
   to be transferred here". The tints of `.cal .e[data-k]` above, kind for
   kind (`CalendarViewService::kind()`), on the row's cells; and the month's
   total in bold over a rule ("bolded or marked as something more
   important"). */
tbody tr[data-k="shift"] > td{background-color:color-mix(in srgb,var(--s-ok) 16%,var(--c-surface))}
tbody tr[data-k="cut"] > td{background-color:color-mix(in srgb,var(--s-warn) 28%,var(--c-surface))}
tbody tr[data-k="free"] > td{background-color:color-mix(in srgb,var(--c-accent) 8%,var(--c-surface))}
tbody tr[data-k="vac"] > td{background-color:color-mix(in srgb,var(--c-accent) 26%,var(--c-surface))}
tbody tr[data-k="sick"] > td{background-color:color-mix(in srgb,var(--s-err) 26%,var(--c-surface))}
tbody tr[data-k="plan"] > td{background-color:color-mix(in srgb,var(--s-ok) 8%,var(--c-surface))}
tbody tr[data-k="away"] > td{background-color:var(--c-alt)}
/* The caption's dark, which a code book row may name too (ISV5-958). */
tbody tr[data-k="co"] > td{background-color:var(--c-caption);color:var(--c-caption-fg)}
tbody tr[data-sum] > td{font-weight:700;border-top:2px solid var(--c-text-2);background-color:var(--c-panel)}
/* AN ITEM A PERSON OWNS, NOT THE COMPANY, Plane ODDIA-1413, Jan 25 October:
   "We can color the row." `_data` `owner` on a row of `/items`
   (`ItemOwnerService::decorate()`); the owner's tag stands in `Owner`. */
tbody tr[data-owner] > td{background-color:color-mix(in srgb,var(--s-warn) 22%,var(--c-surface))}
/* THE ITEM WHOSE PRICES ARE DRAWN UNDER THE TABLE, Plane ODDIA-1654, Jan 1
   November: "the row whose price is shown stays highlighted". `_data`
   `priced` on a row of a product's `Položky` (`ProductItemService::items()`). */
tbody tr[data-priced] > td{background-color:color-mix(in srgb,var(--c-accent) 26%,var(--c-surface))}
tbody tr[data-priced] > td:first-child{box-shadow:inset 4px 0 0 var(--c-accent)}
/* And its headings may take a second line, so the added `Druh dne` keeps
   the table within the 1 065 a record gives it: `Dřívější odchod` is the
   one sized under its heading (`records/people.php`). */
table[data-ns="attendance_days"] thead th{white-space:normal;line-height:1.3;vertical-align:bottom}
/* And the absence types' on Seznamy, Plane ISV5-963: five switches sized by
   their one-line headings (`Counts toward hours` 184px) drew the table 1 792
   in a 1 542 box at 1920, so Vzhled, Pořadí and the row buttons scrolled.
   Wrapped they still need 1 536 of the form's 1 518, so the five names keep
   104 of the 120 a name box is given below: 1 518 at 1920, ID and Pořadí
   at their 60 and 90. */
table[data-ns="ref_absence_type"] thead th{white-space:normal;line-height:1.3;vertical-align:bottom}
table[data-ns="ref_absence_type"] td > input.f[data-cell^="record"][data-cell$="translate"]{min-width:104px}
tbody td.freeze:empty{background:var(--c-panel)}
/* AND BESIDE THE RECORD BOX'S `Pořadí`, NOT OVER IT, Plane ODDIA-282. Jan on a
   channel's `Platby`: "the grag icon tho to change order should be next to the
   numbered field so its dragable this was hard to actually drag, since the icon
   is microscopic an wrongly placed above." `Shell::blockTable()` draws the glyph
   and then the box in its `.fld`, a block, so the box went under the glyph. Both
   are inline here, the glyph is drawn at the section heading's size and its
   whole line height is the handle. The box is two digits wide, so both fit the
   72 px the column is drawn at: a
   percentage of a cell sized by its content resolves to nothing. And the cell
   does not wrap, or a narrow column puts the box back on a second line, which
   the row's height then cut off. */
td > .tb[data-drag]{cursor:grab;user-select:none;display:inline-block;vertical-align:middle;
  font-size:var(--t-big);line-height:var(--ctl-sm);padding:0 1px}
td > .tb[data-drag] + .fld{display:inline-block;vertical-align:middle;margin-bottom:0;width:36px}
td:has(> .tb[data-drag] + .fld){white-space:nowrap}
td.act input[type="number"]{height:var(--ctl-sm);width:56px;padding:0 var(--sp1);
  vertical-align:middle}
button.sw.f{padding:0;border:0;background:none;cursor:pointer;line-height:0}
button.sw.f.on i{background:var(--s-ok);border-color:var(--s-ok)}
button.sw.f.on i::after{margin-left:24px}
/* A LOCKED SWITCH STILL SAYS WHAT IT IS.
   Grey said "no state at all", which is wrong: the main currency is very
   much active and cannot be otherwise. It keeps its colour and loses its
   contrast, so the state reads and the lock reads too. Operator, 28
   August. */
.sw.f.off i{opacity:0.55}
/* THE LOCK IS ON TOP OF THE STATE, NOT INSTEAD OF IT.
   An older rule painted a locked switch grey and, sitting further down
   the file, beat the rule above that keeps its colour - so the main
   currency read as neither on nor off when it is emphatically on. The
   lock is the faded contrast and the cursor; the colour still says which
   way it is. Operator, 28 August, twice. */
.sw.f.off,
button.sw.f.off{cursor:not-allowed}
.sw.f input:checked + i::after{margin-left:24px}
.sw.f input:focus-visible + i{outline:2px solid var(--c-accent)}

/* ==========================================================================
   THE SAME SWITCH, WITH A STOP IN THE MIDDLE.

   THE OPERATOR, `TASKS.md` U002, and he counted the askings himself:
   *"TOGGLE is the toggle. We need a toggle - the one we have, that just
   have the MIDDLE position. So its three way. Otherwise the same asset,
   middle is yellow."*

   NO NEW CLASS AND NO NEW SHAPE. It is `.sw.f` - the same 44 x 20 track,
   the same 14px knob, the same border, the same two transitions. What is
   added is one attribute, `data-at`, which says WHICH STOP the knob is
   at, and the arithmetic the shape rule already wrote down finishes
   itself:

     track    38 wide, knob 14   ->  travel 24
     stop 0   margin-left  0     ->  red, --s-err, the shape rule's own
     stop 1   margin-left 12     ->  yellow, --s-warn, and 12 is 24 / 2
     stop 2   margin-left 24     ->  green, --s-ok, where `on` already is

   AN ATTRIBUTE AND NOT A CLASS, for two reasons and both are rules of
   this house. `asset_check.py`'s INVENTED gate is at zero and a class
   this file adds is a decision recorded in `AGREED` - which is a checker
   edit, and a checker is not edited by the work it would have judged
   (`CLAUDE.md`, R348). And `off` is already taken: on a switch it means
   LOCKED, not `stop 0`. `data-on` on the setting switch above is the
   same idea and the same precedent - the stylesheet asks the markup.

   THREE BUTTONS OVER ONE TRACK, NOT THREE BUTTONS IN A ROW. He looked at
   three squares four times and said no four times. The control is the
   track; each third of it is a submit button with nothing drawn in it, so
   pressing a stop goes straight to that stop - nothing wraps round,
   nothing is pressed twice, and it works with script off exactly as the
   two-state switch beside it does.

   `--s-warn` IS THE PALETTE'S YELLOW AND THERE IS NO OTHER. Fifteen
   colours, one warm tone. OVERRULABLE: a brighter yellow is a sixteenth
   colour and a change to `:root`, which is his to make and not one
   control's to take.
   ========================================================================== */
.sw.f[data-at]{position:relative;display:inline-block;width:44px;height:20px}
.sw.f[data-at] i{position:absolute;top:0;left:0}
/* NO STOP AT ALL. The row holds a value that is none of the three, so the
   track says so rather than reading as the first one. `--s-off` is the
   mockup's own grey for a position nothing is on, which is exactly this. */
.sw.f[data-at=""] i{background:var(--s-off);border-color:var(--s-off)}
.sw.f[data-at="1"] i{background:var(--s-warn);border-color:var(--s-warn)}
.sw.f[data-at="1"] i::after{margin-left:12px}
.sw.f[data-at="2"] i{background:var(--s-ok);border-color:var(--s-ok)}
.sw.f[data-at="2"] i::after{margin-left:24px}
/* A THIRD OF THE TRACK EACH, AND NOTHING PAINTED. The knob is the state;
   a button that drew anything would be a second thing saying it. */
.sw.f[data-at] button{position:absolute;top:0;left:0;height:100%;width:33.34%;
  padding:0;border:0;background:none;cursor:pointer}
.sw.f[data-at] button:nth-of-type(2){left:33.33%}
.sw.f[data-at] button:nth-of-type(3){left:66.66%}
/* THE STOP IT IS ALREADY AT IS NOT A CONTROL, and neither is any of them
   while the row is switched off. Pressing the current one would write the
   value it already holds - `00_SHELL_RULES.md` 7.1, the dead control. */
.sw.f[data-at] button:disabled{cursor:default}
.sw.f[data-at].off button{cursor:not-allowed}
.sw.f[data-at] button:focus-visible{outline:2px solid var(--c-accent)}

.sw{width:110px;display:inline-block;margin:0 var(--sp2) var(--sp2) 0;vertical-align:top}
.sw i{display:block;height:32px;border:1px solid var(--c-line)}
.sw b{display:block;font-family:var(--f-data);font-size:var(--t-meta);font-weight:400;color:var(--c-text-2)}

.view{display:none;flex:1;min-height:0;flex-direction:column;gap:var(--sp2)}
.view.on{display:flex}

/* ---------- JOB CONSOLE, IN THE TOP BAR ----------
   IT LIVED IN THE NAV AND POINTED AT COLOURS THAT DO NOT EXIST.
   --c-bg-2, --c-bg-3, --c-muted, --c-ok and --c-bad are not in :root and
   never have been, so every declaration naming one was thrown away by the
   browser: the console had no background, the running stripe never drew,
   and `.f.lit` - the highlight that says a cell was just filled by a
   translation - was invisible for as long as it has existed. That is why
   the strip looked dead while the queue was draining.
   The fifteen-colour palette is the whole register. --c-alt is the quiet
   background, --c-text-2 the quiet text, --s-ok / --s-err the two states. */

.jobsw{position:relative}
.top button.jobs{background:var(--c-chrome-2);border:1px solid var(--c-chrome-line);
  color:var(--c-caption-fg);font-family:var(--f-ui);font-size:var(--t-meta);
  height:var(--ctl);min-width:132px;padding:0 var(--sp2);cursor:pointer;
  display:flex;align-items:center;gap:var(--sp2);position:relative;overflow:hidden}
.top button.jobs .l{flex:1;text-align:left;white-space:nowrap;position:relative}
.top button.jobs .n{font-family:var(--f-data);min-width:18px;text-align:center;
  background:var(--c-chrome-line);color:var(--c-caption-fg);padding:0 var(--sp1);position:relative}
.top button.jobs:hover{border-color:var(--c-chrome-line-2)}

/* THE ANIMATION IS THE INDICATOR. A number that changes every few seconds
   is not visible movement; a stripe crossing the block is. It runs only
   while there is work, so a quiet system is a still one. */
@keyframes jobrun{ from{background-position:0 0} to{background-position:32px 0} }
/* AND IT IS THE WHOLE BLOCK, NOT A LINE UNDER IT. `TASKS.md` TL01, the
   operator on 2 October: "I kinda think the animation there needs to be
   more visible." It was three pixels along the bottom edge of a control in
   the corner. The same stripe, the same movement, the height of the block;
   the word and the count stand on it. Upright bars, because a slanted one
   only repeats cleanly at one height. */
.jobs-p{position:absolute;left:0;right:0;bottom:0;height:100%;display:none}
.jobs.run .jobs-p{display:block;
  background-image:repeating-linear-gradient(90deg,var(--c-accent) 0,var(--c-accent) 16px,var(--c-chrome-2) 16px,var(--c-chrome-2) 32px);
  background-size:32px 100%;animation:jobrun .5s linear infinite}
.jobs.run{border-color:var(--c-caption-fg)}
.jobs.run .n{background:var(--s-ok)}
.jobs.fail .n{background:var(--s-err)}

/* THE PHONE BLOCK SHOWS ITS STATE, ISV5-1037; NO SWITCH since ODDIA-1068.
   Signed in here, ringing or talking: a green block, a white dot and the
   handset. Before it signs in: grey, a hollow dot, the words quiet. A
   fault of the line: amber (`shell.js` marks it `data-taking`). */
.top button.jobs[data-calls]::before{content:"";width:8px;height:8px;border-radius:50%;flex:none;
  position:relative;border:2px solid currentColor;box-sizing:border-box}
.top button.jobs[data-calls] svg{flex:none;position:relative}
.top button.jobs[data-calls][data-taking="0"]{color:var(--s-off)}
.top button.jobs[data-calls][data-taking="1"]{background:var(--s-ok);
  border-color:var(--s-ok);color:var(--c-caption-fg);font-weight:700}
.top button.jobs[data-calls][data-taking="1"]::before{background:currentColor}
.top button.jobs[data-calls][data-taking="1"][data-phone-fault]{background:var(--s-warn);border-color:var(--s-warn)}
.top button.jobs[data-calls][data-taking="1"]:hover{border-color:var(--c-caption-fg)}
/* The selector is deliberately not `.jobs.run .jobs-p`: the same selector
   written twice, even in a media query, is a duplicate the asset ratchet
   counts and the next edit collides with. `.jobs .jobs-p` says the same
   thing here, because the stripe is only ever animated when `.run` is on. */
@media (prefers-reduced-motion:reduce){ .jobs .jobs-p{animation:none} }

/* THE PANEL IS THE SAME SHAPE AS THE NOTIFICATION PANEL, because it is
   the same thing: chrome hanging off the top bar, opened by a control in
   it, closed by clicking away. One pattern, not two. */
.jobsp{position:absolute;top:100%;left:0;width:460px;max-height:60vh;
  background:var(--c-surface);border:1px solid var(--c-line-2);display:none;
  flex-direction:column;z-index:1}
.jobsp.open{display:flex}
.jobsp .cap{background:var(--c-caption);color:var(--c-caption-fg);
  padding:var(--sp1) var(--sp2);font-size:var(--t-meta)}
.jobs-b{overflow:auto;flex:1;min-height:0}
/* AND A LINE SAYS WHAT IT IS ABOUT, IN A COLOUR THAT SHOWS. `TASKS.md` TL01,
   the operator on 2 October: "The note was not added to console." It was.
   The panel hangs inside the top bar and took the top bar's text colour,
   which is the light one, on the panel's white: the first line of every
   row - the Czech being translated and the language - was drawn and could
   not be read, and what was left was `Hotovo` and a sentence in German. */
.jobs-r{display:block;padding:var(--sp1) var(--sp2);font-size:var(--t-meta);
  border-bottom:1px solid var(--c-line);color:var(--c-text)}
.jobs-r b{display:block;font-weight:400;color:var(--c-text-2)}
.jobs-r.s-running{background:var(--c-alt)}
.jobs-r.s-running b{color:var(--c-text)}
.jobs-r.s-failed b{color:var(--s-err)}
.jobs-e{padding:var(--sp2);font-size:var(--t-meta);color:var(--c-text-2)}
.jobs-f{border-top:1px solid var(--c-line);padding:var(--sp1) var(--sp2);font-size:var(--t-meta)}

/* A CELL A JOB JUST FILLED. The class is added by the console when a
   finished job carries its answer back, and it is what proves the console
   did something. It stays until the next page load, which is right: the
   fact it reports is "this arrived while you were looking", and the next
   load is when that stops being news. */
.f.lit{background:var(--c-alt)}

/* A BOX WHOSE TRANSLATION IS BEING MADE. `TASKS.md` TL01: "while it works,
   something he cannot miss says so." The console sets `aria-busy` on the
   box that holds the text a job is translating and takes it off when the
   job ends, so the sign is where he is looking and not only in the corner.
   The console's own stripe in the page's quiet colours, an accent edge,
   and the word the top bar uses in the empty box - the script writes that.
   An attribute and not a class, because it is a state the markup already
   has a name for; `data-tr` is what a box or a cell holding a translation
   carries, and the offer names list has cells. The buttons of that row wait with it: a second press
   while four jobs are in the air queues nothing and reads as a fault. */
[data-tr][aria-busy="true"]{outline:2px solid var(--c-accent);outline-offset:-2px;
  background-image:repeating-linear-gradient(90deg,var(--c-alt) 0,var(--c-alt) 16px,var(--c-line) 16px,var(--c-line) 32px);
  background-size:32px 100%;animation:jobrun .7s linear infinite}
tr:has([data-tr][aria-busy="true"]) .btn{pointer-events:none;color:var(--c-text-2);
  background-image:repeating-linear-gradient(90deg,var(--c-alt) 0,var(--c-alt) 16px,var(--c-line) 16px,var(--c-line) 32px);
  background-size:32px 100%;animation:jobrun .7s linear infinite}
@media (prefers-reduced-motion:reduce){
  [data-tr][aria-busy]{animation:none}
  tr:has([data-tr][aria-busy]) .btn{animation:none}
}

/* ISV5-731: fit surcharge controls and wrap headings within the carrier record. */
table:has(input[name^="rows[service_surcharges]"]){min-width:1065px!important}
table:has(input[name^="rows[service_surcharges]"]) td{padding-left:2px;padding-right:2px}
table:has(input[name^="rows[service_surcharges]"]) th{padding-left:2px;padding-right:2px;white-space:normal;overflow:visible;text-overflow:clip;line-height:1.2;height:36px}
table:has(input[name^="rows[service_surcharges]"]) th:nth-child(1){width:44px!important}
table:has(input[name^="rows[service_surcharges]"]) th:nth-child(2){width:157px!important}
table:has(input[name^="rows[service_surcharges]"]) th:nth-child(3){width:56px!important}
table:has(input[name^="rows[service_surcharges]"]) th:nth-child(4){width:48px!important}
table:has(input[name^="rows[service_surcharges]"]) th:nth-child(5){width:65px!important}
table:has(input[name^="rows[service_surcharges]"]) th:nth-child(6){width:96px!important}
table:has(input[name^="rows[service_surcharges]"]) th:nth-child(7){width:60px!important}
table:has(input[name^="rows[service_surcharges]"]) th:nth-child(8){width:122px!important}
table:has(input[name^="rows[service_surcharges]"]) th:nth-child(9){width:122px!important}
table:has(input[name^="rows[service_surcharges]"]) th:nth-child(10){width:254px!important}
table:has(input[name^="rows[service_surcharges]"]) th:nth-child(11){width:55px!important}
table:has(input[name^="rows[service_surcharges]"]) textarea.f{height:36px;min-height:36px;padding:2px;resize:vertical}

/* ==========================================================================
   THE MAIL CLIENT. Plane ISV5-919, Jan, 11 October, of `/mail`: "left panel
   shows the mails, the middle panel shows the messages, right panel shows the
   content as every other mail client in the world." The panel is the split's
   own `.tree`; the middle is `mails`, the right `letter` (`shell.php`).
   Attributes and not classes, for the reason `.win[data-split]` gives.
   ========================================================================== */
/* A LINE UNDER A MAILBOX THAT SAYS WHAT IS WRONG AND WHAT TO DO: red, and
   wrapped rather than cut. `quiet` is a state that is no fault, in grey. */
.tree a[data-fault]{height:auto;min-height:var(--ctl);padding-top:var(--sp1);padding-bottom:var(--sp1)}
.tree a[data-fault] .lb{white-space:normal;font-size:var(--t-meta);color:var(--s-err)}
.tree a[data-fault="quiet"] .lb{color:var(--c-text-2)}
/* EACH PANE SCROLLS ON ITS OWN, as in any mail client, and fills the
   height the split gives it. */
.body.pad:has(> .cols[data-mail]){padding:0;overflow:hidden}
.cols[data-mail]{height:100%;gap:0}
.cols[data-mail] > div{overflow-y:auto;min-height:0;background:var(--c-surface)}
.cols[data-mail] > div:first-child{border-right:1px solid var(--c-line)}
.cols[data-mail] > div:last-child{padding:var(--sp3)}
/* THE MIDDLE: the search over it, then one row a letter. */
[data-mails] form{position:sticky;top:0;display:flex;gap:var(--sp1);padding:var(--sp2);
  background:var(--c-panel);border-bottom:1px solid var(--c-line)}
[data-mails] form input.f{flex:1;min-width:0}
/* THE KINDS OF MAIL under the search, wrapping in the narrow pane. ODDIA-1513. */
[data-mails] [data-kinds]{flex-wrap:wrap;padding:0 var(--sp1)}
[data-mails] [data-kinds] a{padding:var(--sp1) var(--sp2)}
[data-mails] > a{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:2px var(--sp2);
  padding:var(--sp2) var(--sp3);border-bottom:1px solid var(--c-line);
  color:var(--c-text);text-decoration:none}
[data-mails] > a[aria-current]{background:var(--c-alt);box-shadow:inset 3px 0 0 var(--c-accent)}
[data-mails] > a > span{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
[data-mails] [data-when]{font-size:var(--t-meta);color:var(--c-text-2);text-align:right}
[data-mails] [data-subject]{grid-column:1 / -1}
[data-mails] [data-first]{grid-column:1 / -1;font-size:var(--t-meta);color:var(--c-text-2)}
/* BOLD WHILE UNREAD, the one thing every client agrees on. */
[data-mails] > a[data-unread] [data-who],
[data-mails] > a[data-unread] [data-subject]{font-weight:700}
[data-mails] [data-pages]{display:flex;justify-content:space-between;align-items:center;
  padding:var(--sp2) var(--sp3);font-size:var(--t-meta);color:var(--c-text-2)}
[data-mails] .empty{padding:var(--sp3)}
/* A PAPERCLIP, where a letter carried files. */
i[data-clip]::before{content:"\1F4CE";font-style:normal;margin-right:var(--sp1)}
/* THE RIGHT: what may be done, the subject, the head, the files, the letter. */
[data-letter]{display:flex;flex-direction:column;gap:var(--sp2);min-height:100%}
[data-letter] [data-bar]{display:flex;flex-wrap:wrap;gap:var(--sp1);align-items:flex-end}
[data-letter] [data-bar] form{display:flex;flex-wrap:wrap;gap:var(--sp1);align-items:flex-end;margin:0}
[data-letter] [data-bar] form .gate-act{display:contents}
[data-letter] [data-bar] form .gate-act .sp{display:none}
[data-letter] h2{margin:0;font-size:var(--t-sec)}
[data-letter] .kv{grid-template-columns:70px 1fr}
[data-letter] [data-files]{display:flex;flex-wrap:wrap;gap:var(--sp1) var(--sp3);align-items:center;
  padding:var(--sp2) 0;border-top:1px solid var(--c-line);border-bottom:1px solid var(--c-line)}
[data-letter] [data-file]{display:inline-flex;gap:var(--sp1);align-items:center}
[data-letter] [data-file] > span{font-size:var(--t-meta);color:var(--c-text-2)}
[data-letter] [data-file] form{margin:0}
[data-letter] [data-file] form .gate-act{display:contents}
[data-letter] [data-file] form .gate-act .sp{display:none}
[data-letter] [data-file] form .btn{height:var(--ctl-sm);padding:0 var(--sp1);font-size:var(--t-meta)}
[data-letter] [data-pictures]{margin:0}
[data-letter] iframe{flex:1;width:100%;min-height:60vh;border:1px solid var(--c-line);background:var(--c-surface)}

/* ONE DOCUMENT, SHOWN INSIDE IS. Plane ODDIA-704, Jan, 11 October 2026:
   "I should not leave IS to see the document." `Shell::blockDocument()`:
   its lines, its buttons in a row, and the file as tall as the window
   allows, so a page of an invoice reads without scrolling inside it. */
[data-document] [data-links]{display:flex;flex-wrap:wrap;gap:var(--sp1);margin:var(--sp2) 0}
[data-document] iframe{display:block;width:100%;height:80vh;border:1px solid var(--c-line);background:var(--c-surface)}
[data-document] img{display:block;max-width:100%;height:auto;border:1px solid var(--c-line)}
/* A RECORD THAT PINS A BOX: the body scrolls on the left, the pinned box
   holds the right side to the foot of the record, a little over half
   so a page reads (ODDIA-1019, 07:33). Plane ODDIA-1019, Jan,
   22 October, on a paper's card: *"the right column of this should be
   always the document, and only the left part is scrollable"*. The frame
   takes the whole height, so a PDF of more pages scrolls inside it. */
[data-pinned]{display:flex;flex:1;min-height:0}
[data-pinned] > .body{flex:1 1 44%;min-width:0}
[data-pin]{flex:1 1 56%;min-width:0;display:flex;flex-direction:column;
  padding:var(--sp3) var(--sp3) var(--sp3) 0}
[data-pin] [data-document]{flex:1;min-height:0;display:flex;flex-direction:column}
[data-pin] [data-document] [data-links]{margin-top:0}
[data-pin] [data-document] iframe{flex:1;height:auto;min-height:0}
[data-pin] [data-document] img{overflow:auto}
/* A PAPER'S PAGES, one under another at the column's width, scrolling as
   one in the pinned half (`Shell::blockDocument()`, `pages`, ODDIA-1019). */
[data-document] [data-pages]{display:flex;flex-direction:column;gap:var(--sp2);overflow:auto;
  border:1px solid var(--c-line);background:var(--c-line);padding:var(--sp2)}
[data-pin] [data-document] [data-pages]{flex:1;min-height:0}
[data-document] [data-pages] a{flex:none;display:block}
[data-document] [data-pages] img{display:block;width:100%;max-width:none;height:auto;border:0;background:var(--c-surface)}
/* THE WRITING WINDOW IS WIDE, `data-wide` (`modal()`). */
.conf .box[data-wide]{width:min(820px,calc(100vw - 96px))}
/* A LARGE DIALOG, `data-large` (`modal()`), Plane ODDIA-1107: wide enough
   for a choice beside a list, never the whole screen (Jan, 21 October: *"Why
   is this so monstrously wide?"*), its body scrolling where its rows
   outgrow it. */
.conf .box[data-large]{width:min(1040px,calc(100vw - 96px));max-height:calc(100vh - 48px)}
.conf .box[data-large] .body{overflow:auto}
/* AND ITS TWO HALVES ARE TWO PANELS, edge to edge: the item chosen on white,
   the list it goes onto beside it in the panel colour behind a line, and the
   left half's own press as wide as its boxes. Jan, 21 October, of `Přidat do
   objednávky`: *"Make the modal that have split view with the split at the
   right, different colour of background or something. Where the items go."* */
.conf .box[data-large] .body.pad:has(> .cols[data-narrow-first]){padding:0}
.conf .box[data-large] .body > .cols[data-narrow-first]{gap:0;min-height:min(520px,calc(100vh - 200px))}
.conf .box[data-large] .body > .cols[data-narrow-first] > div{padding:var(--sp4)}
.conf .box[data-large] .body > .cols[data-narrow-first] > div:last-child{background:var(--c-panel);
  border-left:1px solid var(--c-line-2)}
.conf .box[data-large] .cols[data-narrow-first] .fld--act{margin-top:var(--sp3)}
.conf .box[data-large] .cols[data-narrow-first] .fld--act .btn{flex:1;height:auto;padding:var(--sp2) var(--sp3)}
/* ==========================================================================
   A WORKBENCH, Plane ODDIA-916, Jan, 14 October, of the product texts: "I
   have my monitor. I need big buttons ... everything else can be
   automatic." And his drawing of the same day: the way round the records
   at the top, four panels across (the record's pictures and its texts the
   left half, two tools the right half, side by side and never nested),
   one row of presses across the whole page at the foot. The page itself
   never scrolls: each panel scrolls on its own and the description box
   takes the height its panel has left. Every panel a box with its heading,
   16 px inside, 24 px between panels, 16 px between fields: nothing
   touches anything. `bench` on a `columns` block (`shell.php`).
   ========================================================================== */
.body.pad:has(> .cols[data-bench]){display:flex;flex-direction:column;overflow:hidden;padding:var(--sp4)}
.body.pad:has(> .cols[data-bench]) > form{flex:none;margin:0 0 var(--sp4)}
.body.pad:has(> .cols[data-bench]) > form .fld{margin-bottom:0}
.body.pad > .cols[data-bench]{flex:1 1 auto;min-height:0;margin:0;display:grid;gap:var(--sp5);
  grid-template-columns:minmax(0,12fr) minmax(0,38fr) minmax(0,25fr) minmax(0,25fr)}
.cols[data-bench] > div{min-height:0;overflow-y:auto;background:var(--c-surface);
  border:1px solid var(--c-line);padding:var(--sp4)}
.cols[data-bench] > div > .secline:first-child{margin:0 0 var(--sp4)}
/* THE PICTURES: as wide as their panel, one under the other. */
.cols[data-bench] > div:first-child .card{padding:0;border:0;margin:0 0 var(--sp4)}
.cols[data-bench] > div:first-child .card img{width:100%;max-height:none;margin:0}
.cols[data-bench] > div:first-child .card .m{display:none}
/* THE TEXTS: the name, its chips, the three fields; the description is the
   rest of the panel's height. */
.cols[data-bench] > div:nth-child(2){display:flex;flex-direction:column;overflow:hidden}
.cols[data-bench] > div:nth-child(2) > p:not(.secline){flex:none;margin:0 0 var(--sp2)}
.cols[data-bench] > div:nth-child(2) > p.secline + p{font-weight:700;font-size:var(--t-sec)}
.cols[data-bench] > div:nth-child(2) > details{flex:none;max-height:30vh;overflow-y:auto;margin:0 0 var(--sp4)}
.cols[data-bench] > div:nth-child(2) > form{flex:1 1 auto;min-height:0;display:flex;flex-direction:column;margin:var(--sp2) 0 0}
.cols[data-bench] > div:nth-child(2) > form > .fldrow{flex:none;margin:0 0 var(--sp4)}
.cols[data-bench] > div:nth-child(2) > form > .fldrow:last-child{flex:1 1 auto;min-height:0;margin:0;display:flex}
.cols[data-bench] > div:nth-child(2) > form > .fldrow:last-child > .fld{flex:1 1 auto;min-width:0;display:flex;flex-direction:column;margin:0}
.cols[data-bench] > div:nth-child(2) > form > .fldrow > .fld{margin:0}
.cols[data-bench] textarea[name="description"]{flex:1 1 auto;min-height:6em;resize:none}
/* AND THE BOXES' OWN LETTERS 18px, Jan, 9 October, ODDIA-514: "bigger
   letters in the fields and only here". A one-line box grows with them. */
.cols[data-bench] textarea.f{font-size:var(--t-read);line-height:1.45}
.cols[data-bench] input.f{font-size:var(--t-read);line-height:1.45;height:auto;padding:var(--sp1) var(--sp2)}
/* VYMAZAT, a small quiet link beside the field's label and still one
   press, Jan's drawing of 14 October. Only here: elsewhere it keeps the
   red fill he asked for on 9 October (`button[data-clears]` above). */
.cols[data-bench] .fld > button[data-clears]{height:auto;padding:0;border:0;background:none;
  color:var(--c-text-2);font-size:var(--t-meta);text-decoration:underline}
.cols[data-bench] .fld:has(> button[data-clears]) > .lbl{min-height:0;padding-right:calc(var(--ctl-sm) * 3);margin-bottom:var(--sp1)}
/* THE ROW OF PRESSES: across the page, one height, one family; Schválit
   green and wider, the main press. */
[data-bench-bar]{flex:none;display:flex;align-items:center;gap:var(--sp2);margin-top:var(--sp4)}
[data-bench-bar] .btnrow{gap:var(--sp2)}
[data-bench-bar] .sp{flex:1}
[data-bench-bar] .btn{min-width:160px;height:calc(var(--ctl) * 2);font-size:var(--t-sec)}
[data-bench-bar] .btn[data-go]{min-width:320px;font-size:var(--t-big);
  background:var(--s-ok);border-color:var(--s-ok);color:var(--c-caption-fg)}
/* THE TOOLS: one row each, its name and its control. */
[data-snippet]{display:flex;align-items:center;gap:var(--sp2);padding:var(--sp2) 0;border-top:1px solid var(--c-line)}
[data-snippet]:first-child{border-top:0;padding-top:0}
[data-snippet] > .lb{flex:1 1 auto;min-width:0;overflow-wrap:anywhere}
[data-snippet][data-off] > .lb{color:var(--c-text-2);font-style:italic}
[data-snippet] .sw{flex:none}
[data-snippet] .btn{flex:none;height:var(--ctl-sm);min-width:90px}
[data-snippet-new]{margin:var(--sp4) 0 0}
/* Not the picture mark's square button (`.btn[data-mark]`): its words in full. */
[data-snippet] .btn[data-mark]{width:auto;padding:0 var(--sp1)}

/* ==========================================================================
   THE CALL PANEL, Plane ISV5-1037. Jan, 14 October 2026: on an incoming
   call a large panel, who it is, which brand's number they called, their
   orders and open tickets, and Answer / Decline big on it.
   `Shell::callPanel()`. By its attributes, as `[data-document]` above.
   DOCKED BESIDE THE PAGE, ODDIA-1068 (Jan, 19 October 2026: the panel over
   IS stopped him working during a call). A column at the right edge under
   the top bar; while it is open the page gives it its width
   (`body[data-phone-dock]`), so nothing is covered. An INTERFACE, not a
   page: the state in a coloured band, the caller in big type, the lists
   scroll inside their own part, and the presses are tall full-width
   buttons at the foot that never move. Ringing, the band is green and
   beats; talking, it is the caption's colour; a fault, red.
   ========================================================================== */
[data-call-panel]{position:fixed;top:var(--top);right:0;bottom:0;width:var(--dock);z-index:15;display:none;
  flex-direction:column;background:var(--c-surface);border-left:1px solid var(--c-line-2);
  box-shadow:-2px 0 8px rgba(0,0,0,0.18)}
[data-call-panel].open{display:flex}
body[data-phone-dock] .app{margin-right:var(--dock)}
/* AND SO DOES EVERYTHING ELSE PINNED TO THE RIGHT EDGE (ODDIA-1068, Jan,
   19 October 2026: a person opened from the panel came up in a record
   under it, "the page should operate like the right panel is not
   accessible at all"). The record and the band over it, their grey, the
   bell and user menus, the toasts and the dialogs stand left of the dock;
   the pause and the gate still cover everything. */
body[data-phone-dock] .rec,
body[data-phone-dock] .cap[data-under]{right:var(--dock);max-width:calc(100% - 260px - var(--dock))}
body[data-phone-dock] .scrim,
body[data-phone-dock] .conf,
body[data-phone-dock] .pick,
body[data-phone-dock] .crop{right:var(--dock)}
body[data-phone-dock] .notif,
body[data-phone-dock] .umenu{right:calc(var(--dock) + var(--sp2))}
body[data-phone-dock] .toasts{right:calc(var(--dock) + var(--sp3))}
[data-call-panel] .cap{height:auto;min-height:44px;font-size:var(--t-sec);padding:var(--sp2) var(--sp3);white-space:normal}
[data-call-panel][data-state="ringing"] .cap{background:var(--s-ok);animation:dockbeat 1s ease-in-out infinite}
[data-call-panel][data-state="arriving"] .cap{background:var(--s-warn)}
[data-call-panel][data-state="fault"] .cap{background:var(--s-err)}
@keyframes dockbeat{ 0%,100%{opacity:1} 50%{opacity:.72} }
[data-call-body]{flex:1 1 auto;min-height:0;overflow-y:auto;padding:var(--sp3)}
[data-call-head]{margin-bottom:var(--sp2)}
[data-call-panel] [data-call-number]{font-size:var(--t-big);font-family:var(--f-data);font-weight:700;overflow-wrap:anywhere}
[data-call-panel] [data-call-who]{font-size:var(--t-sec);margin-top:2px;overflow-wrap:anywhere}
[data-call-panel] .kv{grid-template-columns:90px minmax(0,1fr)}
[data-call-panel] .kv dd{overflow-wrap:anywhere}
[data-call-panel] h4{margin:var(--sp3) 0 var(--sp1);font-size:var(--t-meta);color:var(--c-text-2);
  text-transform:uppercase;letter-spacing:0.5px;font-weight:normal}
[data-call-lines]{margin-top:var(--sp2);color:var(--c-text-2)}
[data-call-lines] div + div{margin-top:2px}
[data-call-lines] [data-call-warn]{color:var(--s-err);font-weight:700}
/* A call's presses stand one per line, each as wide as the column; Call back
   sits in its dial box and is stretched to match (ODDIA-1068). */
table[data-ns=phone_calls] td > .btnrow [data-phone-dial-box] > .btn{width:100%}
/* Its headings wrap rather than cut, and a state chip wraps inside its cell. */
table[data-ns=phone_calls] thead th{white-space:normal;line-height:1.3;vertical-align:bottom}
/* A robot line's presses stand at the right end of its row, in one order,
   and the line's form below keeps a gap from the table (Jan, 19 October
   2026: "glued to it", "left aligned for some reason"). ODDIA-1068. */
table[data-ns=phone_voices] td > .btnrow[data-row]{justify-content:flex-end}
#phone-upload{margin-top:var(--sp4)}
/* Each order, ticket and earlier call is two short lines in the dock's
   width: what it is first, then how it stands. */
[data-call-orders] > div,[data-call-tickets] > div,[data-call-calls] > div{display:flex;flex-wrap:wrap;
  column-gap:var(--sp2);padding:4px 0;border-bottom:1px solid var(--c-line);font-family:var(--f-data)}
[data-call-orders] > div > span:first-child,[data-call-tickets] > div > span:first-child,
[data-call-calls] > div > span:first-child{font-weight:700}
[data-call-orders] > div > span,[data-call-tickets] > div > span,[data-call-calls] > div > span{overflow-wrap:anywhere}
[data-call-orders] > p,[data-call-tickets] > p,[data-call-calls] > p{margin:0;color:var(--c-text-2)}
[data-call-panel] [data-phone-said]{margin:0;color:var(--s-err)}
[data-call-card]:not([hidden]) + [data-phone-said]{margin-top:var(--sp3)}
[data-call-panel] .acts{flex:none;height:auto;display:flex;flex-direction:column;align-items:stretch;gap:var(--sp2);padding:var(--sp3);
  border-top:1px solid var(--c-line-2);background:var(--c-panel)}
[data-phone-ringing],[data-phone-talking],[data-phone-confirm]{display:flex;flex-direction:column;gap:var(--sp2)}
[data-phone-ringing][hidden],[data-phone-talking][hidden],[data-phone-confirm][hidden],[data-call-close][hidden]{display:none}
/* A call out is asked once in the panel before it is made (ODDIA-1068,
   Jan, 20 October 2026: "we need safety net ... the side panel should
   open on call and lets you click CALL there"). */
[data-call-panel][data-state="confirm"] .cap{background:var(--s-warn)}
[data-call-panel] [data-phone-confirm] .btn[data-phone-confirm="call"]{height:72px;font-size:var(--t-big)}
[data-call-panel] .acts .btn{width:100%;height:56px;font-size:var(--t-sec);font-weight:700}
[data-call-panel] [data-phone-ringing] .btn[data-phone-do="answer"]{height:72px;font-size:var(--t-big)}

/* THE TAGGING BOARD, Plane ODDIA-1559 (`Shell::blockTagboard()`). The grid
   scrolls beside a panel that stays; every tile is one height and as wide
   as its picture's own shape, so nothing is cropped. A chosen tile is
   outlined in the accent, the one the keys stand on dashed. */
[data-tagboard]{display:flex;flex-direction:column;gap:var(--sp2);height:calc(100vh - var(--top) - 120px);min-height:420px}
[data-tb-filters]{display:flex;flex-wrap:wrap;gap:var(--sp3);align-items:flex-end}
[data-tb-filters] label{display:flex;flex-direction:column;gap:2px;font-size:var(--t-meta);color:var(--c-text-2)}
[data-tb-filters] input,[data-tb-filters] select{height:var(--ctl);font:inherit;font-size:var(--t-base);color:var(--c-text)}
[data-tb-body]{flex:1;min-height:0;display:flex;gap:var(--sp3)}
[data-tb-scroll]{flex:1;min-width:0;overflow:auto;position:relative;background:var(--c-surface);border:1px solid var(--c-line);padding:var(--sp2);user-select:none}
[data-tb-grid]{display:flex;flex-wrap:wrap;gap:var(--sp2);align-content:flex-start;outline:none}
[data-tb-tile]{position:relative;height:132px;flex:none;background:var(--c-alt);outline:1px solid var(--c-line);cursor:pointer}
[data-tb-tile] img{display:block;height:100%;width:auto;pointer-events:none}
[data-tb-tile][aria-selected="true"]{outline:4px solid var(--c-accent);outline-offset:1px}
[data-tb-tile][aria-selected="true"] img{opacity:0.7}
[data-tb-tile][aria-selected="true"]::after{content:"\2713";position:absolute;top:0;right:0;width:var(--ctl);height:var(--ctl);
  display:flex;align-items:center;justify-content:center;background:var(--c-accent);color:var(--c-caption-fg);font-size:var(--t-sec);font-weight:700}
[data-tb-tile][data-at]{box-shadow:0 0 0 2px var(--c-surface),0 0 0 4px var(--c-text)}
[data-tb-tile] [data-tb-marks]{position:absolute;left:2px;bottom:2px;display:flex;gap:2px}
[data-tb-tile] [data-tb-name]{display:block;height:100%;padding:var(--sp1);font-size:var(--t-meta);color:var(--c-text-2);overflow:hidden;width:132px}
[data-tb-band]{position:absolute;border:1px solid var(--c-accent);background:color-mix(in srgb,var(--c-accent) 14%,transparent);pointer-events:none}
[data-tb-foot]{margin:var(--sp3) 0 0;display:flex;gap:var(--sp2);align-items:center}
[data-tb-panel]{width:var(--draw);flex:none;overflow:auto;background:var(--c-surface);border:1px solid var(--c-line);padding:var(--sp3);display:flex;flex-direction:column;gap:var(--sp3)}
[data-tb-panel] p{margin:0}
[data-tb-head]{font-size:var(--t-sec)}
[data-tb-acts]{display:flex;gap:var(--sp2)}
[data-tb-kind] [data-tb-on]{display:flex;flex-wrap:wrap;gap:var(--sp1);margin:var(--sp2) 0}
[data-tb-kind] [data-tb-on] .chip{display:inline-flex;align-items:center;gap:var(--sp1);background:var(--c-caption);cursor:default;
  text-transform:none;letter-spacing:0;font-size:var(--t-base);line-height:1.4;padding:1px var(--sp1)}
[data-tb-kind] [data-tb-on] .chip[data-some]{background:var(--c-line-2)}
[data-tb-kind] [data-tb-on] .chip button{border:0;background:none;color:inherit;font:inherit;cursor:pointer;padding:0 2px}
[data-tb-box]{position:relative;display:block}
[data-tb-box] input{width:100%;height:var(--ctl);font:inherit;font-size:var(--t-base)}
[data-tb-menu]{position:absolute;left:0;right:0;top:100%;z-index:30;max-height:260px;overflow:auto;background:var(--c-surface);border:1px solid var(--c-line-2)}
[data-tb-menu] [role="option"]{display:block;padding:var(--sp1) var(--sp2);font-size:var(--t-base);color:var(--c-text);cursor:pointer;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
[data-tb-menu] [role="option"][aria-selected="true"]{background:var(--c-accent);color:var(--c-caption-fg)}
[data-tb-said]{font-size:var(--t-meta);color:var(--c-text-2)}
[data-tb-said][data-bad]{color:var(--s-err)}

/* ---------- THE WORK TABLE, Plane ODDIA-1786 ----------
   The production terminal as v4's `Fronta výroby`, Jan's spec of 1
   November 2026: compact rows so the whole queue is in sight, the bar one
   line of buttons that WRAPS rather than clips (1024 px, his point 5), the
   tabs with their counts, every row's presses in fixed places. The
   station's tokens (`body[data-station]`, 48 px rows for a touch screen)
   are set back to the desk's here: Jan called the big band "weirdly huge"
   and asked for the queue whole. `Shell::blockWorktable()` draws it. */
[data-wt]{--row:26px;--ctl:26px;--ctl-sm:22px;display:flex;flex-direction:column;gap:var(--sp1);min-height:0}
/* THE ICONS, `Shell::icon()`: v4's 16 px pictures, one rule each. */
[data-icon]{display:inline-block;flex:none;width:16px;height:16px;background:center / 16px 16px no-repeat}
[data-icon="accept"]{background-image:url("/assets/icons/accept.png")}
[data-icon="arrow_rotate_clockwise"]{background-image:url("/assets/icons/arrow_rotate_clockwise.png")}
[data-icon="book"]{background-image:url("/assets/icons/book.png")}
[data-icon="exclamation"]{background-image:url("/assets/icons/exclamation.png")}
[data-icon="filter"]{background-image:url("/assets/icons/filter.png")}
[data-icon="folder"]{background-image:url("/assets/icons/folder.png")}
[data-icon="package"]{background-image:url("/assets/icons/package.png")}
[data-icon="print"]{background-image:url("/assets/icons/print.png")}
[data-icon="refresh"]{background-image:url("/assets/icons/refresh.png")}
[data-icon="scissors"]{background-image:url("/assets/icons/scissors.png")}
[data-icon="settings"]{background-image:url("/assets/icons/settings.png")}
[data-icon="stop_sign"]{background-image:url("/assets/icons/stop_sign.png")}
[data-icon="text_line_numbers"]{background-image:url("/assets/icons/text_line_numbers.png")}
[data-icon="write"]{background-image:url("/assets/icons/write.png")}
[data-wt-scroll]{overflow:auto;min-height:0}
[data-wt-table]{width:100%;table-layout:auto}
[data-wt-table] tbody td{padding:2px var(--sp1);height:var(--row);vertical-align:middle;
  white-space:nowrap;border-bottom:1px solid var(--c-line)}
/* THE HEADING IS EVERY LIST'S (`thead th`), the filter row's search box
   too; it sticks at the top of the table's own scroller. */
[data-wt-table] thead th{top:0}
[data-wt-table] thead td{top:var(--row)}
[data-wt-table] td[data-wt-col="options"]{white-space:normal;overflow-wrap:break-word;min-width:140px}
[data-wt-table] td[data-wt-col="product"]{white-space:normal;overflow-wrap:break-word;min-width:140px}
[data-wt-table] td[data-wt-col="gpsr"] > span{white-space:nowrap}
/* THE STATE COLUMN HOLDS ITS WIDTH, Plane ODDIA-1820: a press changes the
   state in place, and the longest state with who has it must not widen
   the table under the hand: its width is a number (`TerminalService`),
   and the words wrap inside it. */
[data-wt-table] td[data-wt-col="status"]{white-space:normal;overflow-wrap:normal}
[data-wt-table] td[data-wt-col="product"] > [data-wt-btns]{float:right;margin-left:var(--sp1)}
[data-wt-table] td[data-wt-col="minutes"]{text-align:right}
[data-wt-table] tr[data-tone="err"] > td{background:var(--s-err-fill)}
[data-wt-table] tr[data-tone="ok"] > td{background:var(--s-ok-fill)}
[data-wt-table] tr[data-tone="warn"] > td{background:var(--s-warn-fill)}
[data-wt-table] tr[data-tone="off"] > td{background:var(--s-off-fill);color:var(--c-text-2)}
[data-wt-table] tr[data-tone="info"] > td{background:var(--s-info-fill)}
[data-wt-table] select{height:var(--ctl-sm);font-family:var(--f-ui);font-size:var(--t-meta);max-width:110px}
[data-wt-presses],[data-wt-btns]{display:inline-flex;gap:2px;flex:none}
[data-wt-press]{display:inline-flex;align-items:center;justify-content:center;width:24px;height:22px;padding:0;
  border:1px solid var(--c-line-2);background:var(--c-surface);cursor:pointer}
[data-wt-press][aria-disabled]{opacity:.3;cursor:default}
[data-wt-press][data-wt-lit]{border-color:var(--s-err);box-shadow:inset 0 0 0 1px var(--s-err)}
[data-wt] [hidden]{display:none}
@media (max-width:1279px){
  [data-wt] [data-wt-table] td{padding:2px}
  [data-wt] td[data-wt-col="deadline"],[data-wt] td[data-wt-col="gpsr"],[data-wt] td[data-wt-col="status"],
  [data-wt] td[data-wt-col="gpsr"] > span{font-size:var(--t-meta);letter-spacing:0;white-space:normal;overflow-wrap:normal}
  [data-wt] td[data-wt-col="options"],[data-wt] td[data-wt-col="product"]{min-width:92px}
  [data-wt] td[data-wt-col="product"] > [data-wt-btns]{float:none;display:flex;margin:2px 0 0}
  [data-wt] [data-wt-table] select{max-width:76px}
}

/* Jan, 2 Nov 2026: the benches in the page's own bar, a date never wraps, a record opens over the page. */
[data-wt-table] td[data-wt-col="deadline"],[data-wt-table] th[data-wt-col="deadline"]{white-space:nowrap;min-width:84px}
[data-wt-table] td[data-wt-col="order"]{white-space:nowrap}
/* AS WIDE AS A RECORD (`.rec`), under the top bar, Plane ODDIA-1820. */
[data-wt-over]{position:fixed;top:var(--top);right:0;bottom:0;width:62%;min-width:720px;max-width:calc(100% - 260px);
  z-index:900;background:var(--c-surface);border-left:1px solid var(--c-line-2)}
[data-wt-over] iframe{width:100%;height:100%;border:0}
html[data-framed] .top,html[data-framed] .app{display:none}
html[data-framed] .rec{top:0;left:0;right:0;width:100%;max-width:none}

/* Jan, 2 Nov 2026: the count of a bench is a small square of the button's opposite colour. */
.win:has([data-wt]) > .bar .btn .n{background:var(--c-accent);color:var(--c-surface);padding:0 var(--sp1);margin-left:var(--sp1)}
.win:has([data-wt]) > .bar .btn--selected .n{background:var(--c-surface);color:var(--c-accent)}
